]> git.tokkee.org Git - rrdtool-all.git/commitdiff

Code

branched from trunk
authoroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Mon, 22 Mar 2010 18:58:49 +0000 (18:58 +0000)
committeroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Mon, 22 Mar 2010 18:58:49 +0000 (18:58 +0000)
git-svn-id: svn://svn.oetiker.ch/rrdtool/branches/1.4@2047 a5681a0c-68f1-0310-ab6d-d61299d08faa

202 files changed:
program/.indent.pro [new file with mode: 0644]
program/00README [new file with mode: 0644]
program/CHANGES [new file with mode: 0644]
program/CONTRIBUTORS [new file with mode: 0644]
program/COPYING [new file with mode: 0644]
program/COPYRIGHT [new file with mode: 0644]
program/MakeMakefile [new file with mode: 0755]
program/Makefile.am [new file with mode: 0644]
program/NEWS [new file with mode: 0644]
program/README [new file with mode: 0644]
program/THREADS [new file with mode: 0644]
program/TODO [new file with mode: 0644]
program/WIN32-BUILD-TIPS.txt [new file with mode: 0644]
program/autogen.sh [new file with mode: 0755]
program/bindings/Makefile.am [new file with mode: 0644]
program/bindings/lua/Makefile.am [new file with mode: 0644]
program/bindings/lua/README [new file with mode: 0644]
program/bindings/lua/compat-5.1r5/compat-5.1.c [new file with mode: 0644]
program/bindings/lua/compat-5.1r5/compat-5.1.h [new file with mode: 0644]
program/bindings/lua/compat-5.1r5/compat-5.1.lua [new file with mode: 0644]
program/bindings/lua/rrdlua.c [new file with mode: 0644]
program/bindings/lua/test.lua.bottom [new file with mode: 0644]
program/bindings/perl-piped/MANIFEST [new file with mode: 0644]
program/bindings/perl-piped/Makefile.PL [new file with mode: 0644]
program/bindings/perl-piped/README [new file with mode: 0644]
program/bindings/perl-piped/RRDp.pm [new file with mode: 0644]
program/bindings/perl-piped/leaktest.pl [new file with mode: 0644]
program/bindings/perl-piped/rrdpl.dsp [new file with mode: 0644]
program/bindings/perl-piped/rrdpl.dsw [new file with mode: 0644]
program/bindings/perl-piped/t/base.t [new file with mode: 0755]
program/bindings/perl-shared/MANIFEST [new file with mode: 0644]
program/bindings/perl-shared/Makefile.PL [new file with mode: 0644]
program/bindings/perl-shared/README [new file with mode: 0644]
program/bindings/perl-shared/RRDs.pm [new file with mode: 0644]
program/bindings/perl-shared/RRDs.ppd [new file with mode: 0755]
program/bindings/perl-shared/RRDs.xs [new file with mode: 0644]
program/bindings/perl-shared/ntmake-build [new file with mode: 0644]
program/bindings/perl-shared/t/base.t [new file with mode: 0755]
program/bindings/python/ACKNOWLEDGEMENT [new file with mode: 0644]
program/bindings/python/AUTHORS [new file with mode: 0644]
program/bindings/python/COPYING [new file with mode: 0644]
program/bindings/python/README [new file with mode: 0644]
program/bindings/python/rrdtoolmodule.c [new file with mode: 0644]
program/bindings/python/setup.py [new file with mode: 0644]
program/bindings/ruby/CHANGES [new file with mode: 0644]
program/bindings/ruby/README [new file with mode: 0644]
program/bindings/ruby/extconf.rb [new file with mode: 0644]
program/bindings/ruby/main.c [new file with mode: 0644]
program/bindings/ruby/test.rb [new file with mode: 0755]
program/bindings/tcl/Makefile.am [new file with mode: 0644]
program/bindings/tcl/README [new file with mode: 0644]
program/bindings/tcl/ifOctets.tcl.in [new file with mode: 0644]
program/bindings/tcl/tclrrd.c [new file with mode: 0644]
program/configure.ac [new file with mode: 0644]
program/debian/README [new file with mode: 0644]
program/doc/Makefile.am [new file with mode: 0644]
program/doc/RRDp.pod [new symlink]
program/doc/RRDs.pod [new symlink]
program/doc/bin_dec_hex.pod [new file with mode: 0644]
program/doc/cdeftutorial.pod [new file with mode: 0644]
program/doc/librrd.pod [new file with mode: 0644]
program/doc/name.inc [new file with mode: 0644]
program/doc/rpntutorial.pod [new file with mode: 0644]
program/doc/rrd-beginners.pod [new file with mode: 0644]
program/doc/rrdbuild.pod [new file with mode: 0644]
program/doc/rrdcached.pod [new file with mode: 0644]
program/doc/rrdcgi.pod [new file with mode: 0644]
program/doc/rrdcreate.pod [new file with mode: 0644]
program/doc/rrddump.pod [new file with mode: 0644]
program/doc/rrdfetch.pod [new file with mode: 0644]
program/doc/rrdfirst.pod [new file with mode: 0644]
program/doc/rrdflushcached.pod [new file with mode: 0644]
program/doc/rrdgraph-old.pod [new file with mode: 0644]
program/doc/rrdgraph.pod [new file with mode: 0644]
program/doc/rrdgraph_data.pod [new file with mode: 0644]
program/doc/rrdgraph_examples.pod [new file with mode: 0644]
program/doc/rrdgraph_graph.pod [new file with mode: 0644]
program/doc/rrdgraph_libdbi.pod [new file with mode: 0644]
program/doc/rrdgraph_rpn.pod [new file with mode: 0644]
program/doc/rrdinfo.pod [new file with mode: 0644]
program/doc/rrdlast.pod [new file with mode: 0644]
program/doc/rrdlastupdate.pod [new file with mode: 0644]
program/doc/rrdlua.pod [new file with mode: 0644]
program/doc/rrdpython.pod [new file with mode: 0644]
program/doc/rrdresize.pod [new file with mode: 0644]
program/doc/rrdrestore.pod [new file with mode: 0644]
program/doc/rrdruby.pod [new file with mode: 0644]
program/doc/rrdthreads.pod [new file with mode: 0644]
program/doc/rrdtool-dump.dtd [new file with mode: 0644]
program/doc/rrdtool-dump.xsd [new file with mode: 0644]
program/doc/rrdtool-xport.dtd [new file with mode: 0644]
program/doc/rrdtool.pod [new file with mode: 0644]
program/doc/rrdtune.pod [new file with mode: 0644]
program/doc/rrdtutorial.es.pod [new file with mode: 0644]
program/doc/rrdtutorial.pod [new file with mode: 0644]
program/doc/rrdupdate.pod [new file with mode: 0644]
program/doc/rrdxport.pod [new file with mode: 0644]
program/etc/rrdcached-default [new file with mode: 0644]
program/etc/rrdcached-init [new file with mode: 0644]
program/examples/4charts.pl.in [new file with mode: 0755]
program/examples/Makefile.am [new file with mode: 0644]
program/examples/bigtops.pl.in [new file with mode: 0755]
program/examples/cgi-demo.cgi.in [new file with mode: 0755]
program/examples/minmax.pl.in [new file with mode: 0755]
program/examples/perftest.pl.in [new file with mode: 0755]
program/examples/piped-demo.pl.in [new file with mode: 0755]
program/examples/rrdcached/Makefile.am [new file with mode: 0644]
program/examples/rrdcached/RRDCached.pm [new file with mode: 0644]
program/examples/rrdcached/rrdcached-size.pl [new file with mode: 0644]
program/examples/shared-demo.pl.in [new file with mode: 0755]
program/examples/stripes.pl.in [new file with mode: 0755]
program/favicon.ico [new file with mode: 0644]
program/m4/acinclude.m4 [new file with mode: 0644]
program/netware/Makefile [new file with mode: 0644]
program/po/ChangeLog [new file with mode: 0644]
program/po/LINGUAS [new file with mode: 0644]
program/po/Makevars [new file with mode: 0644]
program/po/POTFILES.in [new file with mode: 0644]
program/rrdtool-1.2-release [new file with mode: 0755]
program/rrdtool-1.4-release [new file with mode: 0755]
program/rrdtool.spec [new file with mode: 0644]
program/src/Makefile.am [new file with mode: 0644]
program/src/compile_afm.pl [new file with mode: 0644]
program/src/fnv.h [new file with mode: 0644]
program/src/get_ver.awk [new file with mode: 0644]
program/src/gettext.h [new file with mode: 0644]
program/src/hash_32.c [new file with mode: 0644]
program/src/librrd.pc.in [new file with mode: 0644]
program/src/librrd.sym.in.in [new file with mode: 0644]
program/src/plbasename.c [new file with mode: 0644]
program/src/plbasename.h [new file with mode: 0644]
program/src/pngsize.c [new file with mode: 0644]
program/src/rrd.h [new file with mode: 0644]
program/src/rrd_cgi.c [new file with mode: 0644]
program/src/rrd_client.c [new file with mode: 0644]
program/src/rrd_client.h [new file with mode: 0644]
program/src/rrd_config_bottom.h [new file with mode: 0644]
program/src/rrd_create.c [new file with mode: 0644]
program/src/rrd_daemon.c [new file with mode: 0644]
program/src/rrd_datalang.c [new file with mode: 0644]
program/src/rrd_diff.c [new file with mode: 0644]
program/src/rrd_dump.c [new file with mode: 0644]
program/src/rrd_error.c [new file with mode: 0644]
program/src/rrd_fetch.c [new file with mode: 0644]
program/src/rrd_fetch_libdbi.c [new file with mode: 0644]
program/src/rrd_first.c [new file with mode: 0644]
program/src/rrd_flushcached.c [new file with mode: 0644]
program/src/rrd_format.c [new file with mode: 0644]
program/src/rrd_format.h [new file with mode: 0644]
program/src/rrd_getopt.c [new file with mode: 0644]
program/src/rrd_getopt.h [new file with mode: 0644]
program/src/rrd_getopt1.c [new file with mode: 0644]
program/src/rrd_gfx.c [new file with mode: 0644]
program/src/rrd_graph.c [new file with mode: 0644]
program/src/rrd_graph.h [new file with mode: 0644]
program/src/rrd_graph_helper.c [new file with mode: 0644]
program/src/rrd_hw.c [new file with mode: 0644]
program/src/rrd_hw.h [new file with mode: 0644]
program/src/rrd_hw_math.c [new file with mode: 0644]
program/src/rrd_hw_math.h [new file with mode: 0644]
program/src/rrd_hw_update.c [new file with mode: 0644]
program/src/rrd_hw_update.h [new file with mode: 0644]
program/src/rrd_i18n.h [new file with mode: 0644]
program/src/rrd_info.c [new file with mode: 0644]
program/src/rrd_is_thread_safe.h [new file with mode: 0644]
program/src/rrd_last.c [new file with mode: 0644]
program/src/rrd_lastupdate.c [new file with mode: 0644]
program/src/rrd_nan_inf.c [new file with mode: 0644]
program/src/rrd_not_thread_safe.c [new file with mode: 0644]
program/src/rrd_open.c [new file with mode: 0644]
program/src/rrd_parsetime.c [new file with mode: 0644]
program/src/rrd_parsetime.h [new file with mode: 0644]
program/src/rrd_resize.c [new file with mode: 0644]
program/src/rrd_restore.c [new file with mode: 0644]
program/src/rrd_rpncalc.c [new file with mode: 0644]
program/src/rrd_rpncalc.h [new file with mode: 0644]
program/src/rrd_thread_safe.c [new file with mode: 0644]
program/src/rrd_thread_safe_nt.c [new file with mode: 0644]
program/src/rrd_tool.c [new file with mode: 0644]
program/src/rrd_tool.h [new file with mode: 0644]
program/src/rrd_tune.c [new file with mode: 0644]
program/src/rrd_update.c [new file with mode: 0644]
program/src/rrd_utils.c [new file with mode: 0644]
program/src/rrd_version.c [new file with mode: 0644]
program/src/rrd_xport.c [new file with mode: 0644]
program/src/rrd_xport.h [new file with mode: 0644]
program/src/rrdupdate.c [new file with mode: 0644]
program/src/strftime.c [new file with mode: 0644]
program/src/strftime.h [new file with mode: 0644]
program/src/unused.h [new file with mode: 0644]
program/src/win32comp.c [new file with mode: 0644]
program/svn2cl.xsl [new file with mode: 0644]
program/win32/Makefile [new file with mode: 0644]
program/win32/config.h [new file with mode: 0644]
program/win32/rrd.dsp [new file with mode: 0644]
program/win32/rrd.sln [new file with mode: 0644]
program/win32/rrd.vcproj [new file with mode: 0644]
program/win32/rrd_config.h.msvc [new file with mode: 0644]
program/win32/rrdlib.vcproj [new file with mode: 0644]
program/win32/rrdtool.dsp [new file with mode: 0644]
program/win32/rrdtool.dsw [new file with mode: 0644]
program/win32/rrdtool.vcproj [new file with mode: 0644]

diff --git a/program/.indent.pro b/program/.indent.pro
new file mode 100644 (file)
index 0000000..73401ef
--- /dev/null
@@ -0,0 +1,88 @@
+--blank-lines-after-declarations
+--blank-lines-after-procedures
+--break-before-boolean-operator
+--break-function-decl-args
+--no-blank-lines-after-commas
+--braces-on-if-line
+--braces-on-struct-decl-line
+--comment-indentation25
+--declaration-comment-column25
+--no-comment-delimiters-on-blank-lines
+--cuddle-else
+--continuation-indentation4
+--case-indentation0
+--else-endif-column33
+--space-after-cast
+--line-comments-indentation0
+--declaration-indentation10
+--dont-format-first-column-comments
+--dont-format-comments
+--honour-newlines
+--indent-level4
+/* changed from 0 to 4 */
+--parameter-indentation4
+--line-length78 /* changed from 75 */
+--continue-at-parentheses
+--no-space-after-function-call-names
+--dont-break-procedure-type
+--dont-star-comments
+--leave-optional-blank-lines
+--dont-space-special-semicolon
+--tab-size4
+/* additions by Mark */
+--case-brace-indentation0
+--leave-preprocessor-space
+--no-tabs
+-TFIFOqueue
+-TFnv32_t
+-TTcl_Interp
+-T_ArtRgbaSVPAlphaData
+-Tafm_fontinfo
+-Tafm_sint16
+-Tafm_sint8
+-Tafm_uint16
+-Tafm_uint8
+-Tafm_unicode
+-Tcdp_prep_t
+-Tcgi_s
+-Tds_def_t
+-Teps_font
+-Teps_state
+-Tgfx_canvas_t
+-Tgfx_char_s
+-Tgfx_color_t
+-Tgfx_node_t
+-Tgfx_string_s
+-Tgraph_desc_t
+-Timage_desc_t
+-Tinfo_t
+-Tinfoval
+-Tlive_head_t
+-Toff_t
+-Told_afm_fontinfo
+-Tpdf_buffer
+-Tpdf_font
+-Tpdf_point
+-Tpdf_state
+-Tpdp_prep_t
+-Trpn_cdefds_t
+-Trpnp_t
+-Trpnstack_t
+-Trra_def_t
+-Trra_ptr_t
+-Trrd_file_t
+-Trrd_t
+-Trrd_value_t
+-Tstat_head_t
+-Tstring_arr_t
+-Tsvg_dash
+-Ttext_prop_t
+-Ttime_t
+-Ttm
+-Tunival
+-Tva_list
+-Tvar_s
+-Tvdef_t
+-Txlab_t
+-Tygrid_scale_t
+-Tylab_t
diff --git a/program/00README b/program/00README
new file mode 100644 (file)
index 0000000..8a0389f
--- /dev/null
@@ -0,0 +1,6 @@
+Title: RRDtool
+Date: 2010-03-22
+Owner: Tobias Oetiker <tobi@oetiker.ch>
+Group: Software
+
+Round Robin Database Tool
diff --git a/program/CHANGES b/program/CHANGES
new file mode 100644 (file)
index 0000000..e2a8674
--- /dev/null
@@ -0,0 +1,7454 @@
+RRDtool 1.4.3 - 2010-03-22
+==========================
+New Features
+
+* rrdcached: Log to stderr (in addition to syslog) when running in foreground.
+  -- Sebastian Harl
+
+* rrdcached: Added -m command line option. This option may be used to specify
+  the file permissions of a UNIX socket. The option affects the following
+  sockets only, i.e., it's possible to specify different modes for different
+  sockets. -- Sebastian Harl
+
+* rrdcached: Added -s option so set group permissions of the UNIX domain
+  socket. Obviously this will only work if rrdcached is running as root which in
+  my case it has to be (as upstart can't currently monitor non-root
+  daemons). As you may have RRD data sources (data loggers) and RRD
+  consumers (e.g. CGI scripts) that both need to talk to the cache
+  daemon one solution is to set the group permissions of the socket.
+
+  This offer some flexibility over trying to get every RRD user/daemon
+  running under the same user context. -- Alex Bennee  ajb pitcairn.cambridgebroadband.com
+
+* rrdcached: Let -s affect the following sockets only. This way, it's possible
+  to specify different groups for different sockets. -- Sebastian Harl
+
+* rrd_create: added --no-overwrite option. It prevents rrdtool from
+  clobbering existing rrd files. -- Chris - LINX
+
+* ruby bindings: rb_rrd_xport function  -- Pavel Pachkovskij pavel.pachkovskij azati.com
+
+* use locale settings and _NL_TIME_WEEK_1STDAY to determine the first
+  day of the week. Works on Linux at least -- Tobi Oetiker
+
+* rrd_graph: \u to backup one line for special legend placement tricks 
+  -- tobi oetiker
+
+* rrd_update: new (better) checker for incoming COUNTER or DERIVED data. -- Florian
+
+Bug Fixes
+
+* rrdcached: Do not put any code in assert(). -- Sebastian Harl
+
+* add 'm' as a shortcut for 'zoom' in rrd_graph as it was in 1.3 
+  -- Sebastian Harl
+  
+* rrd_update: fix for max calculation ... the MAX value of the current
+  consolidation interval was pre-initialized with the MAX value from the
+  previous interval (an over optimization). -- Tobi Oetiker
+
+* build: additional dependencies on Makefile to make sure bindings makefiles
+  get rebuild after reconfig -- Tobi Oetiker
+
+* ruby bindings: reset rrd_state for grapv in ruby bindings -- Sven Engelhardt
+
+* rrd_cgi: make sure that (argv-1)[0] points to something sensible ... 
+  hack to undo the damage caused by a senseless optimization. see bug #251
+  -- tobi oetiker
+
+* rrd_graph: fix right aligned text -- tobi oetiker
+
+* python bindings: pass DESTDIR to setup.py as --root=<DESTDIR> iff a DESTDIR
+  was given. --- Bernhard Reutner-Fischer
+
+* rrd_graph: backward compatibility fix - only draw the new type of marker
+  in the graph legend when called with --dynamic-labels -- tobi oetiker
+
+* rrd_restore: portability fix - sizeof(time_t) is 4 byte not 32 bit -- tobi oetiker
+
+* rrd_xport: The end of the export range should not be adjusted DOWN but UP to hit the
+  next matching step interval or the response will be one row short. -- tobi oetiker
+
+* rrd_graph: the end time itself is not part of a time range ... this fixes the segfault
+  reported in #248 -- tobi oetiker
+
+* rrd_restore: teach rrd_restore to be able to read the #text value of <a></a> ... this
+  fixes problems with blank last_ds entries ... as reported in #247. -- tobi oetiker
+
+* rrd_update: be more diligent in checking the input arguments for rrdtool  update and
+  complain if they are wrong. fix for #243 (and more) -- tobi oetiker
+
+----- Older Changelog --------------------
+
+2009-11-15 11:54  oetiker
+
+       * bindings/perl-piped/RRDp.pm, bindings/perl-shared/RRDs.pm,
+         configure.ac, doc/rrdbuild.pod, rrdtool.spec, src/pngsize.c,
+         src/rrd.h, src/rrd_cgi.c, src/rrd_create.c, src/rrd_datalang.c,
+         src/rrd_diff.c, src/rrd_dump.c, src/rrd_error.c, src/rrd_fetch.c,
+         src/rrd_first.c, src/rrd_format.c, src/rrd_format.h,
+         src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph_helper.c,
+         src/rrd_hw.c, src/rrd_hw.h, src/rrd_i18n.h, src/rrd_info.c,
+         src/rrd_is_thread_safe.h, src/rrd_last.c, src/rrd_lastupdate.c,
+         src/rrd_not_thread_safe.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_restore.c, src/rrd_rpncalc.c, src/rrd_rpncalc.h,
+         src/rrd_thread_safe.c, src/rrd_thread_safe_nt.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_tune.c, src/rrd_update.c,
+         src/rrd_version.c, src/rrd_xport.c, src/rrd_xport.h,
+         src/rrdupdate.c: prepare for the release of rrdtool-1.4.2
+
+2009-11-15 11:53  oetiker
+
+       * doc/librrd.pod, doc/rrdcached.pod: fix pod syntax
+
+2009-11-15 10:51  oetiker
+
+       * branches/1.3/program/src/rrd_tool.c, src/rrd_tool.c: Switch
+         LC_NUMERIC to C prior to creating rrd_xport output. This makes
+         sure that numbers are always output with a . and not with a ,
+
+2009-10-30 22:49  oetiker
+
+       * src/rrd_daemon.c: clarify rrd_daemon code with JOURNAL_REPLAY
+         macro -- kevin
+
+2009-10-28 05:15  oetiker
+
+       * bindings/perl-piped/RRDp.pm, bindings/perl-shared/RRDs.pm,
+         doc/rrdbuild.pod, rrdtool.spec, src/pngsize.c, src/rrd.h,
+         src/rrd_cgi.c, src/rrd_create.c, src/rrd_datalang.c,
+         src/rrd_diff.c, src/rrd_dump.c, src/rrd_error.c, src/rrd_fetch.c,
+         src/rrd_first.c, src/rrd_format.c, src/rrd_format.h,
+         src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph_helper.c,
+         src/rrd_hw.c, src/rrd_hw.h, src/rrd_i18n.h, src/rrd_info.c,
+         src/rrd_is_thread_safe.h, src/rrd_last.c, src/rrd_lastupdate.c,
+         src/rrd_not_thread_safe.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_restore.c, src/rrd_rpncalc.c, src/rrd_rpncalc.h,
+         src/rrd_thread_safe.c, src/rrd_thread_safe_nt.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_tune.c, src/rrd_update.c,
+         src/rrd_version.c, src/rrd_xport.c, src/rrd_xport.h,
+         src/rrdupdate.c: prepare for the release of rrdtool-1.4.1
+
+2009-10-28 05:15  oetiker
+
+       * configure.ac: updated versions to account for rrd cached fix
+
+2009-10-28 05:14  oetiker
+
+       * src/rrd_daemon.c: Avoid unnecessary string handling for UPDATE
+         commands in rrd_deaemon for 20x speed increasse. Only copy as many
+         bytes as necessary. During journal replay, avoid unnecessary copy
+         (string is discarded). Bug reported by Thorsten von Eicken Patched
+         by kevin brintnall
+
+2009-10-27 21:44  oetiker
+
+       * src/rrd_tool.c: we have 2009 now
+
+2009-10-27 21:40  oetiker
+
+       * NEWS: added links to documentation
+
+2009-10-27 17:55  oetiker
+
+       * bindings/perl-piped/RRDp.pm, bindings/perl-shared/RRDs.pm,
+         configure.ac, doc/rrdbuild.pod, rrdtool.spec, src/pngsize.c,
+         src/rrd.h, src/rrd_cgi.c, src/rrd_create.c, src/rrd_datalang.c,
+         src/rrd_diff.c, src/rrd_dump.c, src/rrd_error.c, src/rrd_fetch.c,
+         src/rrd_first.c, src/rrd_format.c, src/rrd_format.h,
+         src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph_helper.c,
+         src/rrd_hw.c, src/rrd_hw.h, src/rrd_i18n.h, src/rrd_info.c,
+         src/rrd_is_thread_safe.h, src/rrd_last.c, src/rrd_lastupdate.c,
+         src/rrd_not_thread_safe.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_restore.c, src/rrd_rpncalc.c, src/rrd_rpncalc.h,
+         src/rrd_thread_safe.c, src/rrd_thread_safe_nt.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_tune.c, src/rrd_update.c,
+         src/rrd_version.c, src/rrd_xport.c, src/rrd_xport.h,
+         src/rrdupdate.c: set version to 1.4.0 for all files
+
+2009-10-27 17:51  oetiker
+
+       * rrdtool-1.3-release, rrdtool-1.4-release: create build script for
+         1.4 release
+
+2009-10-27 17:49  oetiker
+
+       * Makefile.am, configure.ac, src/rrd_client.c, src/rrd_graph.h,
+         src/rrd_restore.c: * remove intl from build process ... someone
+         who does actually need it should fix this ... * make the code work
+         on solaris 10 (tested on opencsw server farm. thanks!)
+
+2009-10-27 17:04  oetiker
+
+       * src/rrd_client.c, src/rrd_graph.h, src/rrd_restore.c: more solaris
+         10 portability changes ....
+
+2009-10-27 16:18  oetiker
+
+       * configure.ac, src/rrd_dump.c, src/rrd_graph.c, src/rrd_restore.c,
+         src/rrd_tool.c: remove HAVE_LOCALE_H and HAVE_TIME_H and
+         HAVE_SETLOCALE since they have not been used consistantly anyway
+
+2009-10-27 16:12  oetiker
+
+       * configure.ac: if we use HAVE_TIME_H we should have a check for
+         this in configure ...
+
+2009-10-27 13:57  oetiker
+
+       * NEWS: updated for 1.4 release
+
+2009-10-27 13:54  oetiker
+
+       * src/rrd_graph.c: simplify rrd_graph LC_NUMERIC fix so that all
+         call paramters are affected by it.
+
+2009-10-27 13:13  oetiker
+
+       * doc/rrdgraph.pod, src/rrd_graph.c: allow the grid on:off pattern
+         to be configured
+
+2009-10-27 13:07  oetiker
+
+       * src/rrd_restore.c: needs locale.h to compile
+
+2009-10-23 15:58  oetiker
+
+       * configure.ac, src: optional software is expected to live in /opt,
+         so lets follow this custom ... /usr/local is really old school.
+
+2009-10-22 23:22  oetiker
+
+       * src/rrd_tool.c: fix memory handling when argument separation in
+         pipe mode fails. fix for #230
+
+2009-10-20 19:23  oetiker
+
+       * src/rrd_restore.c: set locale to C while restoring
+
+2009-10-15 08:42  oetiker
+
+       * doc/rrdtutorial.pod: another typo fixed by Justin T Pryzby
+
+2009-10-15 04:47  oetiker
+
+       * src/Makefile.am: Removed left-over occurrence of LIB_LIBINTL --
+         sebastian
+
+2009-10-14 08:04  oetiker
+
+       * bindings/perl-piped/RRDp.pm, doc/rpntutorial.pod,
+         doc/rrd-beginners.pod, doc/rrdbuild.pod, doc/rrdcached.pod,
+         doc/rrdcreate.pod, doc/rrddump.pod, doc/rrdflushcached.pod,
+         doc/rrdgraph-old.pod, doc/rrdgraph.pod, doc/rrdgraph_data.pod,
+         doc/rrdgraph_graph.pod, doc/rrdgraph_libdbi.pod,
+         doc/rrdgraph_rpn.pod, doc/rrdpython.pod, doc/rrdruby.pod,
+         doc/rrdtool.pod, doc/rrdtutorial.pod: Fixed various typos in the
+         manpages. Debian bug #550919. -- Justin T. Pryzby
+
+2009-10-11 16:00  oetiker
+
+       * src/rrd_client.c, src/rrd_client.h: load rrd_config.h before
+         rrd_client.h since its content is expected to be present --
+         Florian Forster
+
+2009-10-04 21:16  oetiker
+
+       * src/rrd_flushcached.c: Fix potential memory leak in
+         rrd_flushcached during failure. -- kevin
+
+2009-10-04 12:18  oetiker
+
+       * CONTRIBUTORS: sebastian was missing from the contributors list
+
+2009-10-04 12:00  oetiker
+
+       * src/rrd_graph.c: Do not mess with the locale settings. The main
+         program takes care of that (rrd_tool.c calls setlocale(LC_ALL,"")
+         already. We do call setlocale on LC_NUMERIC a few times to get
+         predictable number formats when parsing strings into floats, but
+         apart from that the code is written in a portable manner and can
+         take whatever the local system supports.
+
+2009-10-04 11:48  oetiker
+
+       * src/rrd_tool.c: * comment on the reson for calling setlocale * to
+         not force the message encoding ... messages should be encoded in
+         the current locale
+
+2009-10-04 11:37  oetiker
+
+       * src/rrd_client.c, src/rrd_daemon.c: rrd_client, rrd_daemon: Do not
+         require hostnames to contain a dot. Non-FQDN (e.g. "localhost")
+         are now supported as well. Anything that does not start with '['
+         should be (and is now) treated as <name>[:<port>]. -- Sebastian
+         Harl
+
+2009-10-04 11:36  oetiker
+
+       * src/rrd_client.c: rrd_client: Improved error handling / reporting.
+         * Report getaddrinfo() errors using gai_strerror(). * Replaced
+         "Internal error" with a more verbose error message, if available.
+         -- Sebastian Harl
+
+2009-10-04 11:35  oetiker
+
+       * src/rrd_client.c: rrd_client: Do not rewrite path names when
+         accessing remote daemons. When talking to a local daemon (thru a
+         UNIX socket), relative path names are resolved to absolute path
+         names to allow for transparent integration into existing solutions
+         (as requested by Tobi). However, when talking to a remote daemon,
+         absolute path names are not allowed, since path name translation
+         is done by the server (relative to the base directory). --
+         Sebastian Harl
+
+2009-10-04 11:34  oetiker
+
+       * src/rrd_flushcached.c: rrdflushcached: Do not free 'opt_daemon'
+         before checking the connection. -- Sebastian Harl
+
+2009-10-04 11:33  oetiker
+
+       * src/rrd_fetch.c: rrdfetch: Print a usage summary if not enough
+         parameters have been given. -- Sebastian Harl
+
+2009-10-04 11:32  oetiker
+
+       * src/rrd_daemon.c: rrdcached: Handle journal replay in
+         socket_permission_check(). -- Sebastian Harl
+
+2009-10-04 11:30  oetiker
+
+       * src/rrd_daemon.c: rrdcached: Create the base directory on startup.
+         -- Sebastian Harl
+
+2009-10-04 11:29  oetiker
+
+       * src/rrd_daemon.c: rrdcached: Improved the "base-dir symlink
+         check". The daemon checks if the specified base directory contains
+         symlinks by comparing the canonicalized path name (by realpath())
+         with the path name specified on the command line. The GNU libc's
+         implementation of realpath() removed trailing slashes ('/') from
+         the pathname. Thus, specifying a base directory with a trailing
+         slash results in rrdcached aborting, complaining about an invalid
+         base directory, which is quite annoying imho. Now, trailing
+         slashes are removed before comparing the two path names. --
+         Sebastian Harl
+
+2009-10-04 11:28  oetiker
+
+       * src/rrd_daemon.c: rrdcached: Improved an error message. --
+         Sebastian Harl
+
+2009-10-04 11:27  oetiker
+
+       * src/rrd_daemon.c: rrdcached: Create the pidfile and (UNIX) socket
+         directories as well. Those files may be located in a subdirectory
+         of, e.g., /var/run/. To avoid the need to manually create (and
+         recreate, e.g. in case /var/run/ is on a tmpfs) that subdirectory,
+         let the daemon handle the creation of those directories. --
+         Sebastian Harl
+
+2009-10-01 18:54  oetiker
+
+       * src/rrd_fetch_libdbi.c, src/rrd_tool.h: rrd_fetch_libdbi: Fixed
+         some compiler warnings. While at it, declared some private helper
+         functions as static. -- Sebastian Harl
+
+2009-09-28 16:21  oetiker
+
+       * doc/librrd.pod: Moved rrd_dump_cb_r() to section "CORE FUNCTIONS"
+         since this is not a utility function but rather part of RRDtool's
+         core. -- Sebastian Harl
+
+2009-09-28 16:16  oetiker
+
+       * doc/Makefile.am: Moved the 'librrd' manpage to section 3 --
+         Sebastian Harl
+
+2009-09-28 16:16  oetiker
+
+       * doc/librrd.pod: documentation for rrd_mkdir_p added -- Sebastian
+         Harl
+
+2009-09-28 16:16  oetiker
+
+       * src/rrd_daemon.c: free config_pid_file after using it for the last
+         time ... Else, the daemon might either segfault or leave the PID
+         file around. -- Sebastian Harl
+
+2009-09-28 16:15  oetiker
+
+       * src/rrd_daemon.c: try to create missing journal directories rather
+         than abort with an error. -- Sebastian Harl
+
+2009-09-28 16:13  oetiker
+
+       * src/librrd.sym.in.in, src/rrd.h, src/rrd_utils.c: added
+         rrd_mkdir_p - This function may be used to recursively create some
+         directory, similar to "mkdir -p" on the command-line. -- Sebastian
+         Harl
+
+2009-09-24 19:21  oetiker
+
+       * src/rrd_restore.c: adjusted copyright wording according to
+         sebastians suggestions
+
+2009-09-15 12:21  oetiker
+
+       * trunk/tutorial/lisa2009/rrd-by-example/body.tex: adapted for 2009
+
+2009-09-15 12:20  oetiker
+
+       * trunk/tutorial/lisa2009/abstract-rrdexample.txt,
+         trunk/tutorial/lisa2009/abstract-rrdtut.txt: initial
+
+2009-09-15 12:17  oetiker
+
+       * trunk/tutorial/lisa2009, trunk/tutorial/lisa2009/rrd-intro.odp:
+         initial
+
+2009-09-01 08:49  oetiker
+
+       * NEWS, src/rrd_graph.c: show element type in legend
+
+2009-08-25 09:01  oetiker
+
+       * configure.ac, src/rrd_client.h, src/rrd_daemon.c: detect if
+         stdint.h is available. if not, use inttypes. this should fix #239
+         and make compilation on solaris 9 work.
+
+2009-08-24 12:04  oetiker
+
+       * debian/README, debian/README.Debian, debian/build_freetype.sh,
+         debian/changelog, debian/control, debian/copyright,
+         debian/librrd0-dev.files, debian/librrd0.files,
+         debian/librrd0.postinst, debian/librrd0.postrm,
+         debian/librrd0.shlibs, debian/librrdp-perl.files,
+         debian/librrds-perl.files, debian/rrdtool-tcl.files,
+         debian/rrdtool.files, debian/rules, debian/watch: replae with
+         pointer
+
+2009-08-24 12:02  oetiker
+
+       * src/rrd_config_bottom.h: call fpclass only once when replacing
+         isnan for solaris
+
+2009-08-22 22:03  oetiker
+
+       * MakeMakefile: remove misspellt keyword ...
+
+2009-08-21 05:03  oetiker
+
+       * m4/acinclude.m4, src/rrd_config_bottom.h: make sure all elements
+         required in the definition of isinf are defined in their turn
+
+2009-08-20 22:02  oetiker
+
+       * src/rrd_graph.c, src/rrd_tool.c: Trim trailing whitespace, reoder
+         getopt_long per shortopt char; Fix -D while at it -- Bernhard
+         Reutner-Fischer
+
+2009-08-19 15:24  oetiker
+
+       * NEWS, doc/rrdgraph.pod, src, src/rrd_graph.c, src/rrd_graph.h,
+         src/rrd_tool.c: The attached patch allows to $ rrdtool graph
+         --border=0 to disable the 3d border around the image. -- Bernhard
+         Reutner-Fischer rep.dot.nop gmail.com
+
+2009-08-19 07:44  oetiker
+
+       * src/rrd_graph.c: yes this should not matter, but I think it is the
+         right thing todo ...
+
+2009-08-17 21:34  oetiker
+
+       * src/rrd_dump.c, src/rrd_tune.c: added missing prototypes
+
+2009-08-17 21:31  oetiker
+
+       * CONTRIBUTORS, doc/librrd.pod, src/librrd.sym.in.in, src/rrd.h,
+         src/rrd_dump.c: Added a call back interface to rrd_dump to be able
+         to call the function from c. See librrd.pod for documentation. --
+         Benny Baumann BenBE geshi.org
+
+2009-08-16 22:22  oetiker
+
+       * doc/rrdcached.pod, src/rrd_daemon.c: From: Florian Forster
+         <octo@leeloo.lan.home.verplant.org> This patch introduces
+         per-socket, per-command permissions to the RRD caching daemon,
+         RRDCacheD. This allows administrators to limit access to the
+         daemon in a fine-grained manner. The patch removes the command
+         line option `-L' and introduces `-P' instead. The option must
+         appear *before* the `-l' option and the given permissions are set
+         for all following sockets or another `-P' option. For example:
+         rrdcached -P FLUSH,PENDING -l 10.2.3.4 -P UPDATE -l
+         unix:/some/path Allow `FLUSH' and `PENDING' commands to clients
+         connected via IPv4 networking and `UPDATE' to clients connected
+         via a UNIX domain socket only. There are a couple of exceptions: -
+         The commands `HELP' and `QUIT' are always allowed. - If the
+         command `BATCH' is allowed, the command `.' is automatically
+         allowed, too. By default, i. e. if no `-P' option is specified,
+         all commands will be allowed. As a shortcut to reset the behavior
+         to the default behavior, you can use the slightly hackish `-P ""'
+         syntax. Signed-off-by: Florian Forster
+         <octo@leeloo.lan.home.verplant.org>
+
+2009-08-16 22:05  oetiker
+
+       * configure.ac: with my Debian Testing box I had to apply the
+         following small changes to the `configure.ac' file in order to
+         make `autoconf' happy. Don't know what the problem precisely is ?
+         m4 and its square parenthesis is too high for me ;) -- Florian
+
+2009-08-16 22:00  oetiker
+
+       * autogen.sh: there is no config directory ... so lets not search
+         for it in autogen.sh
+
+2009-08-11 14:00  oetiker
+
+       * branches/1.3/program/src/rrd_restore.c, src/rrd_restore.c: fix
+         freeing order when generating error messages. found by Dmitry V.
+         Krivenok
+
+2009-08-10 11:47  oetiker
+
+       * m4/acinclude.m4: include math.h when testing IEEE
+
+2009-08-08 14:06  oetiker
+
+       * branches/1.3/program/doc/rrd-beginners.pod, doc/rrd-beginners.pod:
+         remove extra \ in perl example
+
+2009-08-08 09:38  oetiker
+
+       * src/rrd_daemon.c: The journal files are time-stamped and replayed
+         in order. This allows systems with 32-bit signed off_t to write
+         more than 2GB of journal entries per flush interval. --kevin
+
+2009-08-08 09:36  oetiker
+
+       * doc/rrdgraph.pod: add some shell proggramming advice ... Bernhard
+         Reutner-Fischer
+
+2009-08-08 09:34  oetiker
+
+       * src/rrd_daemon.c: Remove PID file later in the cleanup process
+         --kevin
+
+2009-08-08 09:29  oetiker
+
+       * src/rrd_daemon.c: Increment the DataSetsWritten counter before
+         freeing the RRD values. --kevin
+
+2009-08-08 09:28  oetiker
+
+       * src/rrd_daemon.c: The current method may have caused flushes (and
+         journal rotations) more often than necessary. The problem was
+         introduced in r1600. --kevin
+
+2009-08-08 09:27  oetiker
+
+       * src/rrd_daemon.c: Two-phase shutdown for rrdcached ensures that
+         values are flushed. Previously, it was possible for the queue
+         threads to exit before the flush thread completed queueing values.
+         If running with -F, rrdcached may have crashed due to assertion
+         failure before writing all values. -- kevin
+
+2009-08-07 06:04  oetiker
+
+       * branches/1.3/program/bindings/python/rrdtoolmodule.c,
+         bindings/python/rrdtoolmodule.c: include PACKAGE_VERSION as
+         version number for python module to fix #236
+
+2009-08-03 18:51  oetiker
+
+       * branches/1.3/program/src/rrd_info.c, src/rrd_info.c: in rrd > 1.3
+         it's called rrd_info_push ... and not just info_push ...
+
+2009-08-03 18:45  oetiker
+
+       * branches/1.2/program/src/rrd_info.c,
+         branches/1.3/program/src/rrd_info.c, src/rrd_info.c: Show the
+         index of each data source in the rrd_info output. Patch for #231
+         provided by Glenn Herteg, GroundWork Open Source, Inc.
+
+2009-07-30 07:57  oetiker
+
+       * NEWS: new news
+
+2009-07-30 07:56  oetiker
+
+       * src/rrd_tune.c: rrd_tune HW Update Allow to set the smoothing
+         window (alpha, beta, gamma) to zero. I'm using rrdtool 1.3.8 with
+         abberant behavior detection from the Brutlag method. I need to use
+         a week seasonality and no smoothing for the SEASONAL and
+         DEVSEASONAL. So I use rrdtune in order to set the smoothing window
+         to 0.0. This calls the set_hwarg(). But this method is also used
+         to tune the alpha, beta and gamma that can't be set to 0.0 So when
+         I tune my smoothing window size to 0.0, it returns -1 due to the
+         if(window<=0.0) then do nothing. I join here a small correction
+         with a differnent method used to set only the smoothing window
+         size and allowing it to be zero. -- sylvain.luiset gmail.com
+
+2009-07-28 16:49  oetiker
+
+       * configure.ac: update conditional silent-rules
+
+2009-07-14 12:00  oetiker
+
+       * src/rrd_daemon.c: Handle race condition for "UPDATE" with new
+         files. Problem found by Sebastian Harl. Patch by Kevin.
+
+2009-07-12 06:28  oetiker
+
+       * src/rrd_daemon.c: Do not trust cache tree pointers after releasing
+         the cache lock. It's possible that a "FORGET" was issued in the
+         mean time. -- kevin
+
+2009-07-10 05:52  oetiker
+
+       * src/rrd_daemon.c: destroy flush condition variable
+
+2009-07-10 05:51  oetiker
+
+       * src/rrd_graph.c: Move <libgen.h> include so we know whether we
+         HAVE_LIBGEN_H -- kevin
+
+2009-07-09 20:14  oetiker
+
+       * doc/Makefile.am, src/rrd_thread_safe.c: Silence automake
+         portability warnings caused by multiple inference rules. -- kevin
+         brintnall
+
+2009-07-05 08:41  oetiker
+
+       * configure.ac, src/rrd_restore.c, win32/rrd_config.h.msvc: check
+         for the size of time_t and behave accordingly
+
+2009-07-04 14:51  oetiker
+
+       * src/rrd_restore.c: be more careful when converting to string to
+         time_t ... test if time_t is long long
+
+2009-06-10 19:05  oetiker
+
+       * bindings/lua/compat-5.1r5/compat-5.1.c,
+         bindings/lua/compat-5.1r5/compat-5.1.h,
+         bindings/lua/compat-5.1r5/compat-5.1.lua: add the real license ...
+
+2009-06-10 12:17  oetiker
+
+       * branches/1.3/program/doc/bin_dec_hex.pod,
+         branches/1.3/program/doc/rrdtutorial.es.pod, doc/bin_dec_hex.pod,
+         doc/rrdtutorial.es.pod: fix bad pdf links
+
+2009-06-09 19:22  oetiker
+
+       * bindings/lua/compat-5.1r5/compat-5.1.c,
+         bindings/lua/compat-5.1r5/compat-5.1.h,
+         bindings/lua/compat-5.1r5/compat-5.1.lua: added compat 5.1 license
+         copied from website
+
+2009-06-08 05:12  oetiker
+
+       * branches/1.3/program/configure.ac, configure.ac: make config.h a
+         symlink to rrd_config.h so that intl/Makefile is happy
+
+2009-06-08 04:56  oetiker
+
+       * branches/1.3/program/MakeMakefile,
+         branches/1.3/program/configure.ac, MakeMakefile, configure.ac:
+         Relax automake requirements and make 1.11 features optional --
+         kevin brintnall
+
+2009-06-07 20:48  oetiker
+
+       * bindings/perl-shared/RRDs.xs, bindings/python/rrdtoolmodule.c,
+         bindings/ruby/main.c: fix some missing rrd_flushcached instances
+         -- kevin
+
+2009-06-07 14:46  oetiker
+
+       * bindings/tcl/tclrrd.c: switch to the rrd_flushcached interface
+
+2009-06-07 14:45  oetiker
+
+       * src/rrd_cgi.c: consider fgets return value
+
+2009-06-07 14:33  oetiker
+
+       * intl, po/Makefile.in.in: do not try to mess with autopoints files
+
+2009-06-07 14:27  oetiker
+
+       * src/rrd_open.c: check return value from write
+
+2009-06-07 14:19  oetiker
+
+       * src/rrd_daemon.c: check return values of dup and ftruncate
+
+2009-06-07 14:12  oetiker
+
+       * src/rrd_fetch.c, src/rrd_flushcached.c, src/rrd_info.c: fix some
+         warnings
+
+2009-06-07 13:57  oetiker
+
+       * intl, intl/Makefile.in: added a copy to svn since it is changed
+         from the original
+
+2009-06-07 13:53  oetiker
+
+       * po/Makefile.in.in: changed from original version, so include it
+         ...
+
+2009-06-07 13:42  oetiker
+
+       * Makefile.am, autogen.sh: include libtool with the distro
+
+2009-06-07 11:36  oetiker
+
+       * configure.ac: another ID tag
+
+2009-06-07 11:35  oetiker
+
+       * Makefile.am, README: more id tags added
+
+2009-06-07 11:34  oetiker
+
+       * COPYRIGHT: updated for release
+
+2009-06-07 11:34  oetiker
+
+       * COPYRIGHT, TODO: updated for release
+
+2009-06-07 11:20  oetiker
+
+       * CONTRIBUTORS: added $Id:$
+
+2009-06-07 11:16  oetiker
+
+       * NEWS: initial news
+
+2009-06-02 13:55  oetiker
+
+       * branches/1.3/program/doc/rrdupdate.pod, doc/rrdupdate.pod: Mention
+         that negative time values should be separated by '--' in
+         rrdupdate.pod -- Sebastian Harl
+
+2009-06-02 05:55  oetiker
+
+       * trunk/talks/op5-09, trunk/talks/op5-09/3days-solar-graph.pdf,
+         trunk/talks/op5-09/active-acache-management.pdf,
+         trunk/talks/op5-09/active-acache-management.xar,
+         trunk/talks/op5-09/beamercolorthemetobi.sty,
+         trunk/talks/op5-09/body.tex, trunk/talks/op5-09/cache-flow.pdf,
+         trunk/talks/op5-09/cache-flow.xar,
+         trunk/talks/op5-09/cache-importance.pdf,
+         trunk/talks/op5-09/cache-importance.xar,
+         trunk/talks/op5-09/charles.png,
+         trunk/talks/op5-09/energy_graph.png,
+         trunk/talks/op5-09/handouts.tex,
+         trunk/talks/op5-09/n20e-daily.png,
+         trunk/talks/op5-09/nmn09_200.jpg, trunk/talks/op5-09/pings.png,
+         trunk/talks/op5-09/presentation.tex,
+         trunk/talks/op5-09/readahead-fix.pdf,
+         trunk/talks/op5-09/readahead-fix.xar,
+         trunk/talks/op5-09/systembelastung.png,
+         trunk/talks/op5-09/update-schematics.pdf,
+         trunk/talks/op5-09/update-schematics.xar: initial
+
+2009-06-01 14:01  oetiker
+
+       * branches/1.3/program/m4/acinclude.m4, m4/acinclude.m4: Make
+         rrdtool compile on GNU/kFreeBSD. Fix for Debian bug 494874. Patch
+         by Petr Salinger.
+
+2009-06-01 13:58  oetiker
+
+       * branches/1.3/program/src/rrd_update.c, src/rrd_update.c: make
+         negative update times work as diescribed in the documentation (-5
+         is NOW-5seconds) this takes care of debian bug 332766
+
+2009-05-28 05:25  oetiker
+
+       * src/rrd_open.c: madvise hints may not be ORed since they are
+         enumerated ... #224
+
+2009-05-27 16:23  oetiker
+
+       * configure.ac: back to 0.14.4
+
+2009-05-27 16:21  oetiker
+
+       * Makefile.am, configure.ac: get the latest gettext
+
+2009-05-27 16:03  oetiker
+
+       * src/Makefile.am: added gettext.h to dist
+
+2009-05-27 15:00  oetiker
+
+       * src/gettext.h: added missing gettext.h
+
+2009-05-27 14:59  oetiker
+
+       * po/LINGUAS, po/de.po: we don't have any translations ... so do not
+         pretend
+
+2009-05-27 14:31  oetiker
+
+       * autogen.sh, configure.ac: reduce gettext version requirements
+
+2009-05-27 14:14  oetiker
+
+       * MakeMakefile: fix path
+
+2009-05-27 13:39  oetiker
+
+       * MakeMakefile, Makefile.am, acinclude.m4, autogen.sh,
+         bindings/Makefile.am, configure.ac, doc/Makefile.am,
+         m4/acinclude.m4, po/ChangeLog, po/Makevars, src/Makefile.am,
+         src/rrd_i18n.h: the get ready for 1.4rc patch * updated to
+         automake 1.11 with silet build, with this warnings stand out much
+         better in the build process. use make V=1 to see all as before *
+         switched from intltoolize to autopoint for gettext support * moved
+         m4 bits into their own subdirectory --tobi
+
+2009-05-27 13:11  oetiker
+
+       * src/rrd_graph.c, src/rrd_graph.h: Only include libgen.h when we
+         have it. use pbbasename if we do not. Also fixes #223.
+
+2009-05-26 15:25  oetiker
+
+       * doc/rrdtool.pod: update flush to flushcached
+
+2009-05-26 15:24  oetiker
+
+       * bindings/lua/rrdlua.c, bindings/perl-shared/RRDs.pm,
+         bindings/perl-shared/RRDs.xs, bindings/python/rrdtoolmodule.c,
+         bindings/ruby/main.c, bindings/tcl/tclrrd.c: bindings should
+         invoke new rrd_flushcached (instead of rrd_cmd_flush) -- kevin
+
+2009-05-26 15:20  oetiker
+
+       * src/rrd_restore.c: "ulong" is not portable. must be unsigned long
+         -- kevin
+
+2009-05-26 08:55  oetiker
+
+       * doc/Makefile.am: update flush document name
+
+2009-05-26 07:14  oetiker
+
+       * src/Makefile.am: rename rrd_flush.c to rrd_flushcached.c in
+         Makefile.am
+
+2009-05-26 07:13  oetiker
+
+       * doc/rrdflush.pod, doc/rrdflushcached.pod, src/librrd.sym.in.in,
+         src/rrd.h, src/rrd_flush.c, src/rrd_flushcached.c, src/rrd_open.c,
+         src/rrd_tool.c: resolve the "flush inconsistency": * rrd_flush (a
+         leftover from when rrdtool was using stream based io) is turned
+         into a no-op but still exported to preserve library compatibility.
+         * rrd_cmd_flush (the implementation of "rrdtool flush") is renamed
+         to rrd_flushcached along with its command line invocation which is
+         now rrdtool flushcached --tobi
+
+2009-05-25 22:23  oetiker
+
+       * src/rrd_restore.c: While restoring, rrd_restore uses between 30
+         and 40 times as much memory as the size of the rrd file to be
+         restored. This is due to the use of libxml2 for parsing the the
+         whole file prior to turning it into rrd structs. This patch
+         switches rrd_restore to using the xmlTextReader interface for
+         incremental consumtion of xml files. This reduces the memory
+         requirements to about twice the size of the rrd file which should
+         be quite manageable for most cases. The parsing is about 25% less
+         efficient than with the original libxml2 version of rrd_restore.
+         This could be in part due to the more liberal use of malloced
+         memory. Optimizing this is for another day though. --tobi
+
+2009-05-25 06:05  oetiker
+
+       * branches/1.3/program/src/rrd_open.c, src/rrd_open.c: only include
+         utime.h when necessary
+
+2009-05-24 10:31  oetiker
+
+       * branches/1.3/program/configure.ac,
+         branches/1.3/program/src/rrd_info.c,
+         branches/1.3/program/src/rrd_open.c,
+         branches/1.3/program/src/rrd_update.c, configure.ac,
+         src/rrd_info.c, src/rrd_open.c, src/rrd_update.c: * in 1.3:
+         Changed types used in rrd_file_t from off_t to size_t to prevent:
+         warning: comparison between signed and unsigned * Misc other off_t
+         -> size_t changes to prevent signed warning * in 1.3: Added
+         rrd_dump_opt_r declaration to prevent undef warning * Explicit
+         cast to time_t of check in rrd_fetch to prevent signed warning *
+         Added vasprintf path to sprintf_alloc to prevent excessive
+         mallocing * in 1.3: fixed STRERROR_R_CHAR_P undef warning patch by
+         Steven Hartland killing multiplay.co.uk
+
+2009-05-22 14:54  oetiker
+
+       * src/rrd_update.c: reset errno right befor the call I want to test
+
+2009-05-22 13:55  oetiker
+
+       * src/rrd_update.c: inline is something for the compiler to figure
+         ...
+
+2009-05-22 11:32  oetiker
+
+       * branches/1.3/program/src/rrd_update.c, src/rrd_update.c: fix error
+         messages on the road to fix #222
+
+2009-05-21 05:20  oetiker
+
+       * doc/rrdgraph.pod: fix text organisation
+
+2009-05-18 14:23  oetiker
+
+       * branches/1.3/program/bindings/python/rrdtoolmodule.c,
+         bindings/python/rrdtoolmodule.c: integrated fix for debian bug
+         #529291 as supplied by Anders Hammarquist
+
+2009-05-11 21:25  oetiker
+
+       * branches/1.3/program/configure.ac,
+         branches/1.3/program/src/rrd_getopt.c, configure.ac,
+         src/rrd_getopt.c: fix rrd_getops use of external variables (optarg
+         and friends) ... most prominently is kills a segfault when using
+         rrdtool perl bindings on solaris ... thanks to Ihsan Dogan for
+         helping with tracking this down and providing access to sparc
+         solaris boxes.
+
+2009-04-29 06:52  oetiker
+
+       * branches/1.3/program/CONTRIBUTORS,
+         branches/1.3/program/doc/rrdgraph_data.pod, CONTRIBUTORS,
+         doc/rrdgraph_data.pod: added some remarks on CDEF versus VDEF to
+         the docs -- Pablo Sanchez
+
+2009-04-20 06:48  oetiker
+
+       * doc/Makefile.am, doc/librrd.pod, src/librrd.sym.in.in, src/rrd.h,
+         src/rrd_daemon.c, src/rrd_utils.c: This patch includes utility
+         functions to support dynamically sized arrays. This simplifies the
+         code that manages some of the dynamic structures inside rrdcached.
+         A few data types have been changed to size_t. -- kevin
+
+2009-04-20 06:46  oetiker
+
+       * doc/rrdinfo.pod, src/rrd_format.c, src/rrd_info.c, src/rrd_open.c,
+         src/rrd_tool.h: introduced header_property in info output --
+         Daniel.Pocock barclayscapital.com
+
+2009-04-17 09:53  oetiker
+
+       * branches/1.3/program/src/rrd_tool.c, src/rrd_tool.c: add
+         documentation for graph-render-mode
+
+2009-04-15 07:05  oetiker
+
+       * branches/1.3/program/src/rrd_restore.c, src/rrd_restore.c: fix
+         comments and error messages in rrd_restore patch
+
+2009-04-15 07:02  oetiker
+
+       * branches/1.3/program/src/rrd_restore.c, src/rrd_restore.c: Fix
+         casting issues in rrd_restore. The bug manifested when compiled on
+         sparc v9 as explained in #218. Patch based in input provided by
+         poster of #218.
+
+2009-04-14 13:55  oetiker
+
+       * branches/1.2/program/bindings/ruby/main.c,
+         branches/1.3/program/bindings/ruby/main.c, bindings/ruby/main.c:
+         make ruby bindings compile with newer ruby versions ... fix for
+         #217 patch provided by rrd trac user.
+
+2009-04-14 07:51  oetiker
+
+       * branches/1.3/program/bindings/perl-piped/RRDp.pm,
+         bindings/perl-piped/RRDp.pm: Generate an error message when using
+         RRDp with graph - this can not work reliably. (Debian Bug#251701)
+         -- Sebastian Harl
+
+2009-04-14 06:10  oetiker
+
+       * src/Makefile.am, src/librrd.sym.in.in, src/rrd.h,
+         src/rrd_daemon.c, src/rrd_open.c, src/rrd_restore.c,
+         src/rrd_utils.c: rrd_random() is a wrapper around random() that
+         ensures the PRNG is seeded exactly ONCE per process. rrd_utils.c
+         is introduced for functions that do not have a better home.
+         --kevin
+
+2009-04-10 08:52  oetiker
+
+       * src/rrd_daemon.c: This patch consolidates several aspects of the
+         command handling in rrdcached: * all commands, help, and context
+         information is reduced to a single data structure. * all checks
+         for command validity (context and permission) are handled in the
+         main dispatch routine This removes duplicate help/syntax text, and
+         long if..elseif..else chains. It facilitates the addition of new
+         commands. -- kevin
+
+2009-04-09 15:07  oetiker
+
+       * src/rrd_daemon.c: The bookkeeping of all threads is not necessary,
+         since we cannot pthread_join() them anyway. We only need to know
+         when the last one has exited. -- kevin
+
+2009-04-09 15:05  oetiker
+
+       * doc/rrdcached.pod: added missing docs -- kevin
+
+2009-04-09 15:04  oetiker
+
+       * branches/1.3/program/doc/rrdtutorial.pod, doc/rrdtutorial.pod:
+         added missing newline -- by sebastian harl
+
+2009-04-07 17:33  oetiker
+
+       * trunk/talks/swinog18/body.tex,
+         trunk/talks/swinog18/presentation.tex: minor adjustments
+
+2009-04-07 07:23  oetiker
+
+       * m4: make sure an empty m4 is in the checkout
+
+2009-04-07 06:37  oetiker
+
+       * branches/1.3/program/doc/rrdgraph.pod,
+         branches/1.3/program/src/rrd_graph.c, NEWS, doc/rrdgraph.pod,
+         src/rrd_graph.c: fix for #213 restore old behaviour of --lazy.
+         Even in lazy mode all the data calculation elements are run. The
+         main time saving comes from the fact that the graph is not being
+         drawn and the image does not have to be compressed.
+
+2009-04-07 06:03  oetiker
+
+       * branches/1.3/program/src/rrd_dump.c, src/rrd_dump.c: make sure
+         rrd_dump outputs numbers with C locale so that . does not suddenly
+         become , this fixes #212
+
+2009-04-06 14:49  oetiker
+
+       * CONTRIBUTORS, bindings/python/rrdtoolmodule.c, doc/rrdpython.pod:
+         I thought that it would be nice to be able to pass parameters to
+         python bindings as a list (array) of strings. -- Vytautas
+         Zdanavicius vytaszd at yahoo.com
+
+2009-04-06 14:14  oetiker
+
+       * src/rrd_dump.c: use %lld with (long long) conversion when printing
+         time.
+
+2009-04-06 14:12  oetiker
+
+       * branches/1.3/program/src/rrd_update.c, src/rrd_update.c: printf
+         for time_t with %lli can easily cause a segfault since on many
+         platforms time_t is only 32bit ... by adding (long long) to the
+         time value this gets fixed.
+
+2009-04-06 14:08  oetiker
+
+       * src/rrd_graph.c: lets have an error message when creating the
+         graph fails
+
+2009-04-06 12:02  oetiker
+
+       * trunk/talks/goopen-09/active-acache-management.pdf,
+         trunk/talks/goopen-09/active-acache-management.xar,
+         trunk/talks/goopen-09/beamercolorthemetobi.sty,
+         trunk/talks/goopen-09/body.tex,
+         trunk/talks/goopen-09/cache-flow.pdf,
+         trunk/talks/goopen-09/cache-flow.xar,
+         trunk/talks/goopen-09/cache-importance.pdf,
+         trunk/talks/goopen-09/cache-importance.xar,
+         trunk/talks/goopen-09/charles.png,
+         trunk/talks/goopen-09/energy_graph.png,
+         trunk/talks/goopen-09/handouts.tex,
+         trunk/talks/goopen-09/n20e-daily.png,
+         trunk/talks/goopen-09/pings.png,
+         trunk/talks/goopen-09/presentation.tex,
+         trunk/talks/goopen-09/readahead-fix.pdf,
+         trunk/talks/goopen-09/readahead-fix.xar,
+         trunk/talks/goopen-09/systembelastung.png,
+         trunk/talks/goopen-09/update-schematics.pdf,
+         trunk/talks/goopen-09/update-schematics.xar: go open presentation
+         checkin
+
+2009-03-30 18:33  oetiker
+
+       * trunk/talks/swinog18/active-acache-management.xar: sync xara file
+
+2009-03-30 18:31  oetiker
+
+       * trunk/talks/swinog18,
+         trunk/talks/swinog18/active-acache-management.pdf,
+         trunk/talks/swinog18/active-acache-management.xar,
+         trunk/talks/swinog18/beamercolorthemetobi.sty,
+         trunk/talks/swinog18/body.tex,
+         trunk/talks/swinog18/cache-flow.pdf,
+         trunk/talks/swinog18/cache-flow.xar,
+         trunk/talks/swinog18/cache-importance.pdf,
+         trunk/talks/swinog18/cache-importance.xar,
+         trunk/talks/swinog18/handouts.tex,
+         trunk/talks/swinog18/presentation.tex,
+         trunk/talks/swinog18/readahead-fix.pdf,
+         trunk/talks/swinog18/readahead-fix.xar,
+         trunk/talks/swinog18/update-schematics.pdf,
+         trunk/talks/swinog18/update-schematics.xar: initial
+
+2009-03-21 10:45  oetiker
+
+       * src/rrd_hw.c: remove excess flush calls ...
+
+2009-03-21 09:56  oetiker
+
+       * src/librrd.sym.in.in, src/rrd.h: remove flush symbol
+
+2009-03-21 09:53  oetiker
+
+       * src/rrd_hw.c, src/rrd_open.c: the call to rrd_flush has appeared
+         when rrd_open was fleshed out. It used to be an fflush call which
+         is something entirely different than the current rrd_flush
+         implementation with fdatasync was ... we can safely drop this and
+         gain performance for holt winters in the process ... -- tobi
+
+2009-03-18 17:12  oetiker
+
+       * CONTRIBUTORS, NEWS, doc/rrdgraph.pod, src/rrd_graph.c,
+         src/rrd_graph.h: New rrd_graph functions for legend placement
+         control added -- Patch by Melchior Rabe rrdtool mrab.de
+
+2009-03-08 11:12  oetiker
+
+       * src/rrd_flush.c: rrd_flush.c: Provide a more useful error message.
+         by Sebastian Harl
+
+2009-03-08 08:02  oetiker
+
+       * branches/1.2/program/WIN32-BUILD-TIPS.txt,
+         branches/1.2/program/bindings/Makefile.am,
+         branches/1.2/program/bindings/perl-shared/MANIFEST,
+         branches/1.2/program/bindings/perl-shared/ntmake-build,
+         branches/1.2/program/bindings/perl-shared/ntmake.PL,
+         branches/1.3/program/WIN32-BUILD-TIPS.txt,
+         branches/1.3/program/bindings/Makefile.am,
+         branches/1.3/program/bindings/perl-shared/MANIFEST,
+         branches/1.3/program/bindings/perl-shared/ntmake-build,
+         branches/1.3/program/bindings/perl-shared/ntmake.PL,
+         WIN32-BUILD-TIPS.txt, bindings/Makefile.am,
+         bindings/perl-shared/MANIFEST, bindings/perl-shared/ntmake-build,
+         bindings/perl-shared/ntmake.PL: Renaming ntmake.pl to ntmake.PL
+         (r1742) had unforseen side effects. At least on freebsd ntmake.PL
+         gets called automatically during build. This is not desired. I
+         renamed the file to ntmake-build sinc it gets called explicilty on
+         windows anyway. Reported by Kevin modified by Tobi
+
+2009-03-07 10:31  oetiker
+
+       * NEWS, bindings/lua/rrdlua.c, bindings/perl-shared/RRDs.pm,
+         bindings/perl-shared/RRDs.xs, bindings/python/rrdtoolmodule.c,
+         bindings/ruby/main.c, bindings/tcl/tclrrd.c, doc/rrdflush.pod,
+         src/rrd_flush.c: This 2-patch series exposes 'flush' methods to
+         the various language bindings.
+
+2009-03-06 17:30  oetiker
+
+       * trunk/talks/goopen-09, trunk/talks/goopen-09/abstract.txt,
+         trunk/talks/goopen-09/bio.txt: initial
+
+2009-03-06 05:51  oetiker
+
+       * branches/1.3/program/src/rrd_xport.c, src/rrd_xport.c: there were
+         two lines of debug info printed by rrd_xport, messing up the xml
+         code. Fix by Alejandro Galue.
+
+2009-03-06 05:39  oetiker
+
+       * configure.ac, src/rrd_thread_safe.c: Even though POSIX/XSI
+         requires "strerror_r" to return an "int", some systems (e.g. the
+         GNU libc) return a "char *" _and_ ignore the second argument (user
+         provided buffer). The configure script now checks for that
+         behavior using AC_FUNC_STRERROR_R. rrd_strerror() in
+         rrd_thread_safe.c has been updated to (hopefully) handle all
+         possible cases. Previously, rrd_strerror() would have returned
+         "strerror_r failed. sorry!" in mostly any cases when using glibc,
+         since "if (strerror_r())" had been used to check for errors which
+         evaluates to true if a (non-NULL) pointer was returned. Now, we,
+         at least, return the error number in case anything else fails.
+         Thanks to Alessandro Iurlano for reporting this issue after
+         spotting it in collectd <http://collectd.org>. patch by Sebastian
+         Harl
+
+2009-03-06 05:30  oetiker
+
+       * src/rrd_daemon.c: Make sure we use correct sizes for vsnprintf
+         (sizeof) and strncpy (sizeof-1). -- kevin
+
+2009-02-21 09:43  oetiker
+
+       * branches/1.2/program/doc/bin_dec_hex.pod,
+         branches/1.2/program/doc/cdeftutorial.pod,
+         branches/1.2/program/doc/rrdgraph.pod,
+         branches/1.2/program/doc/rrdgraph_data.pod,
+         branches/1.2/program/doc/rrdgraph_examples.pod,
+         branches/1.2/program/doc/rrdgraph_graph.pod,
+         branches/1.2/program/doc/rrdgraph_rpn.pod,
+         branches/1.2/program/doc/rrdresize.pod,
+         branches/1.2/program/doc/rrdtutorial.es.pod,
+         branches/1.2/program/doc/rrdtutorial.pod,
+         branches/1.3/program/doc/bin_dec_hex.pod,
+         branches/1.3/program/doc/cdeftutorial.pod,
+         branches/1.3/program/doc/rrdgraph.pod,
+         branches/1.3/program/doc/rrdgraph_data.pod,
+         branches/1.3/program/doc/rrdgraph_examples.pod,
+         branches/1.3/program/doc/rrdgraph_graph.pod,
+         branches/1.3/program/doc/rrdgraph_rpn.pod,
+         branches/1.3/program/doc/rrdresize.pod,
+         branches/1.3/program/doc/rrdtutorial.es.pod,
+         branches/1.3/program/doc/rrdtutorial.pod, doc/bin_dec_hex.pod,
+         doc/cdeftutorial.pod, doc/rrdgraph.pod, doc/rrdgraph_data.pod,
+         doc/rrdgraph_examples.pod, doc/rrdgraph_graph.pod,
+         doc/rrdgraph_rpn.pod, doc/rrdresize.pod, doc/rrdtutorial.es.pod,
+         doc/rrdtutorial.pod: Misc documentation fixes by Alex van den
+         Bogaerdt
+
+2009-02-21 09:23  oetiker
+
+       * doc/rrdgraph.pod, doc/rrdgraph_rpn.pod: Documentation fixes by
+         Bernhard Reutner-Fischer
+
+2009-01-25 22:39  oetiker
+
+       * branches/1.3/program/doc/rrdgraph.pod, doc/rrdgraph.pod: added doc
+         on new graphv graph_start und graph_end info variables
+
+2009-01-25 22:02  oetiker
+
+       * branches/1.2/program/src/rrd_dump.c,
+         branches/1.2/program/src/rrd_first.c,
+         branches/1.3/program/src/rrd_dump.c,
+         branches/1.3/program/src/rrd_first.c,
+         branches/1.3/program/src/rrd_graph.c, src/rrd_dump.c,
+         src/rrd_first.c, src/rrd_graph.c: fix type conversion error (on
+         windows) unsigned/signed affecting timestamps on rrd_dump.
+
+2009-01-25 21:52  oetiker
+
+       * branches/1.2/program/bindings/Makefile.am,
+         branches/1.2/program/bindings/perl-shared/ntmake.PL,
+         branches/1.2/program/bindings/perl-shared/ntmake.pl,
+         branches/1.3/program/bindings/Makefile.am,
+         branches/1.3/program/bindings/perl-shared/ntmake.PL,
+         branches/1.3/program/bindings/perl-shared/ntmake.pl,
+         bindings/Makefile.am, bindings/perl-shared/ntmake.PL,
+         bindings/perl-shared/ntmake.pl: rename ntmake.pl to ntmake.PL so
+         that it does not get installed
+
+2009-01-25 18:38  oetiker
+
+       * src/rrd_tool.c: define cwd where we need it and not outside
+
+2009-01-14 08:14  oetiker
+
+       * branches/1.3/program/src/rrd_graph.c, src/rrd_graph.c: In
+         rrd_graph: the width/height values are written into
+         rrd_infoval_t's u_cnt but read out of u_int (u_long vs. int). This
+         breaks on LP64BE. Patch by Rafal Boni rafal pobox.com
+
+2009-01-14 07:22  oetiker
+
+       * NEWS, doc/rrdgraph_rpn.pod, src/rrd_graph.c, src/rrd_graph.h:
+         added percentnan (VDEF PERCENT variant that ignores NAN) -- patch
+         by Martin Sperl
+
+2009-01-13 07:37  oetiker
+
+       * trunk/talks/opentuesday/rrd-intro.odp: make the talk ready
+
+2009-01-09 15:42  oetiker
+
+       * trunk/status2: seems to wrok
+
+2009-01-09 15:41  oetiker
+
+       * trunk/status2: test
+
+2009-01-09 15:40  oetiker
+
+       * trunk/status/x: test
+
+2009-01-06 16:32  oetiker
+
+       * branches/1.3/program/src/rrd_resize.c, src/rrd_resize.c: with MMAP
+         enabled rrd resize GROW was broken ... this fixes it ...
+
+2009-01-06 15:45  oetiker
+
+       * branches/1.2/program/src/rrd_graph.c,
+         branches/1.3/program/src/rrd_graph.c, src/rrd_graph.c: TICK with
+         negative numbers should 'start from the top of the graph' looking
+         down ... now it does.
+
+2009-01-06 14:54  oetiker
+
+       * branches/1.2/program/src/rrd_cgi.c,
+         branches/1.3/program/src/rrd_cgi.c, src/rrd_cgi.c: fix for
+         segfault in rrd_cgi: caused by freeing a invalid address when
+         printing an error message.
+
+2008-12-26 08:05  oetiker
+
+       * branches/1.3/program/configure.ac,
+         branches/1.3/program/src/rrd_graph.h, configure.ac,
+         src/rrd_graph.h: Include libgen.h to get prototype for basename in
+         a POSIX conformant way. Fix for #202.
+
+2008-12-26 08:03  oetiker
+
+       * branches/1.3/program/doc/rrdbuild.pod, doc/rrdbuild.pod:
+         improvements for build docs by Zorzoli, Pablo
+
+2008-12-22 21:23  oetiker
+
+       * branches/1.3/program/src/rrd_fetch.c, src/rrd_fetch.c: should free
+         memory after a successful fetch too ...
+
+2008-12-22 21:23  oetiker
+
+       * branches/1.3/program/bindings/perl-shared/RRDs.pm,
+         bindings/perl-shared/RRDs.pm: documentation fixes
+
+2008-12-22 07:21  oetiker
+
+       * branches/1.3/program/doc/rrdgraph.pod, doc/rrdgraph.pod: Fix for
+         POD error by Sebasian Harl
+
+2008-12-22 07:20  oetiker
+
+       * branches/1.3/program/doc/bin_dec_hex.pod,
+         branches/1.3/program/doc/rpntutorial.pod,
+         branches/1.3/program/doc/rrdbuild.pod,
+         branches/1.3/program/doc/rrdcgi.pod,
+         branches/1.3/program/doc/rrdgraph-old.pod,
+         branches/1.3/program/doc/rrdgraph.pod,
+         branches/1.3/program/doc/rrdgraph_data.pod,
+         branches/1.3/program/doc/rrdgraph_examples.pod,
+         branches/1.3/program/doc/rrdgraph_graph.pod,
+         branches/1.3/program/doc/rrdtool.pod,
+         branches/1.3/program/doc/rrdtutorial.pod, doc/bin_dec_hex.pod,
+         doc/rpntutorial.pod, doc/rrdbuild.pod, doc/rrdcgi.pod,
+         doc/rrdgraph-old.pod, doc/rrdgraph.pod, doc/rrdgraph_data.pod,
+         doc/rrdgraph_examples.pod, doc/rrdgraph_graph.pod,
+         doc/rrdgraph_libdbi.pod, doc/rrdtool.pod, doc/rrdtutorial.pod:
+         Fixes some typos by Sebastian Harl.
+
+2008-12-19 14:26  oetiker
+
+       * branches/1.3/program/src/Makefile.am,
+         branches/1.3/program/src/plbasename.c,
+         branches/1.3/program/src/plbasename.h,
+         branches/1.3/program/src/rrd_graph.c,
+         branches/1.3/program/src/rrd_update.c,
+         branches/1.3/program/win32/rrdlib.vcproj, src/Makefile.am,
+         src/plbasename.c, src/plbasename.h, src/rrd_graph.c,
+         src/rrd_update.c, win32/rrdlib.vcproj: win32 fixes by Stefan .
+         Ludewig . exitgames . com * added portable plbasename since
+         basename is not available on stock win32 * rrd_update was using %d
+         to show a timestamp ... %lli is better since it also will show
+         64bit time_t correctly.
+
+2008-12-18 23:56  oetiker
+
+       * branches/1.3/program/Makefile.am, Makefile.am: fix extra dist to
+         include missing win32 files
+
+2008-12-11 14:31  oetiker
+
+       * branches/1.2/program/src/rrd_fetch.c,
+         branches/1.3/program/src/rrd_fetch.c, src/rrd_fetch.c: fixed
+         another seek issue ...
+
+2008-12-11 10:01  oetiker
+
+       * branches/1.2/program/rrdtool.spec,
+         branches/1.3/program/rrdtool.spec, rrdtool.spec: The spec file for
+         building the rrdtool as an RPM that comes with the release is not
+         "friendly" to older versions of RHEL and derivatives. -- Martin
+         Sperl <rrdtool@martin.sperl.org>
+
+2008-12-11 09:59  oetiker
+
+       * doc/Makefile.am: include luapod into distro
+
+2008-12-10 13:18  oetiker
+
+       * branches/1.3/program/src/rrd_restore.c, src/rrd_restore.c: On some
+         architectures, strtod does not turn "NaN" into 'Not A Number
+         (NAN)' se we better provide our own code here.
+
+2008-12-10 12:42  oetiker
+
+       * NEWS, doc/rrddump.pod, src/rrd_dump.c: rrdtool dump is now
+         backward compatible with rrdtool 1.3 dump again. Default is
+         --header=dtd and --no-header is an alias for --header=none
+
+2008-12-10 08:29  oetiker
+
+       * NEWS: minichange
+
+2008-12-09 18:30  oetiker
+
+       * branches/1.3/program/doc/rrdgraph.pod,
+         branches/1.3/program/src/rrd_graph.c,
+         branches/1.3/program/src/rrd_graph.h,
+         branches/1.3/program/src/rrd_tool.c, doc/rrdgraph.pod,
+         src/rrd_graph.c, src/rrd_graph.h, src/rrd_tool.c: * forward ported
+         right axis patch from 1.2 branche * fixed --full-size-mode sizing
+         issue when used in combination with --title the work in the axis
+         patch has been sponsored by VoltWerk.
+
+2008-12-08 16:07  oetiker
+
+       * branches/1.3/program/bindings/ruby/main.c,
+         branches/1.3/program/bindings/ruby/test.rb, bindings/ruby/main.c,
+         bindings/ruby/test.rb: Fix for last method in ruby bindings --
+         Hiroyuki Ikezoe
+
+2008-12-05 15:39  oetiker
+
+       * branches/1.3/program/bindings/perl-shared/Makefile.PL,
+         bindings/perl-shared/Makefile.PL: integrate osx build hints as
+         indicated in #192
+
+2008-12-05 15:23  oetiker
+
+       * src/rrd_open.c: deleted a bit much from rrd_open ... merged back
+         in
+
+2008-12-05 15:18  oetiker
+
+       * branches/1.3/program/acinclude.m4,
+         branches/1.3/program/configure.ac,
+         branches/1.3/program/src/rrd_open.c, acinclude.m4, configure.ac,
+         src/rrd_open.c: check for broken implementations of msync /
+         MS_ASYNC where the mtime does not get updated. warn and explicitly
+         call utime in rrd_open when a file is opened rw.
+
+2008-12-05 10:23  oetiker
+
+       * examples/rrdcached/Makefile.am: added rrdcached examples to
+         extra_dist to make sure they get distributed
+
+2008-12-04 16:01  oetiker
+
+       * branches/1.3/program/src/rrd_graph.c, src/rrd_graph.c: Legends
+         should NOT be placed in reverse order when using --full-size-mode
+         ... this fixed #198.
+
+2008-12-04 15:49  oetiker
+
+       * src/rrd_fetch.c: add de-referencing to step in fetch check
+
+2008-12-04 15:48  oetiker
+
+       * src/rrd.h: fix merg error with win32 port
+
+2008-12-04 09:03  oetiker
+
+       * branches/1.2/program/src/rrd_graph_helper.c,
+         branches/1.3/program/src/rrd_graph_helper.c,
+         src/rrd_graph_helper.c: Catch CDEF:tot= and raise and error to fix
+         #199. Thanks to Jeron Massar for reporting this.
+
+2008-12-04 07:02  oetiker
+
+       * branches/1.3/program/CONTRIBUTORS,
+         branches/1.3/program/WIN32-BUILD-TIPS.txt,
+         branches/1.3/program/src/rrd_tool.c,
+         branches/1.3/program/win32/rrd.sln,
+         branches/1.3/program/win32/rrdtool.vcproj, CONTRIBUTORS,
+         WIN32-BUILD-TIPS.txt, src/rrd_tool.c, win32/rrd.sln,
+         win32/rrdtool.vcproj: Update to WIN32-BUILD-TIPS.txt and VC
+         project files so that the rrdtool.exe can be built too. -- Stefan
+         Ludewig Stefan.Ludewig exitgames.com
+
+2008-12-03 11:08  oetiker
+
+       * branches/1.2/program/src/rrd_fetch.c,
+         branches/1.3/program/src/rrd_fetch.c, src/rrd_fetch.c: When
+         fetching data from 'past' an rra, rrd_fetch was seeking past the
+         rrd file. This did cause seek errors in some setups. Fixed. Thanks
+         to Daniel Pocock for finding the problem.
+
+2008-12-03 09:54  oetiker
+
+       * configure.ac, examples/Makefile.am,
+         examples/rrdcached/Makefile.am: integrated rrdcached examples into
+         build process
+
+2008-12-03 08:47  oetiker
+
+       * examples/rrdcached, examples/rrdcached/RRDCached.pm,
+         examples/rrdcached/rrdcached-size.pl: add rrdcached examples --
+         kevin
+
+2008-12-02 12:35  oetiker
+
+       * branches/1.2/program/doc/rrdbuild.pod,
+         branches/1.3/program/doc/rrdbuild.pod, doc/rrdbuild.pod: fixed
+         solaris build instructions
+
+2008-12-01 11:05  oetiker
+
+       * doc/Makefile.am: added libdbi doc to extradist
+
+2008-11-24 16:08  oetiker
+
+       * trunk/status/2008-11-24.txt: updates by fritz
+
+2008-11-24 14:20  oetiker
+
+       * trunk/status/2008-11-24.txt: slightly revised
+
+2008-11-24 14:00  oetiker
+
+       * trunk/status/2008-11-24.txt: initial
+
+2008-11-20 13:04  oetiker
+
+       * doc/rrdgraph_libdbi.pod, src/rrd_fetch_libdbi.c: Attached a patch
+         for the rrd-tool LIBDBI integration with the following
+         improvements: a) correct error handling in case of libdbi being
+         unable to load the driver was producing segmentation faults. b)
+         better parsing of datasources * until now timestamp fields had to
+         be integer and had to contain a unix timestamp - now you can now
+         also use DateTime fields (you still need to specify it, as the
+         time-range needs to be defined correctly) * data fields are now no
+         longer limited to (var)char or DOUBLE fields - FLOAT, INTEGER,...
+         are now also supported. c) there is a bug with at least LIBDBI
+         0.8.1 in conjunction with mysql that can result in segmentation
+         faults when BINARY/BLOB fields are accessed - rrdtool will now
+         tell you about this fact before dying ;) d) also the value of
+         rrdderivemaxstep only gets applied if derive has been selected
+         correctly. e) "GROUP BY timestamp" has been removed from SQL
+         statement. f) "ORDER BY timestamp" will be added only in the case
+         of fetching "derived" data. -- Martin Sperl
+
+2008-11-18 17:19  oetiker
+
+       * CONTRIBUTORS, src/rrd.h, src/rrd_client.h, src/rrd_create.c,
+         src/rrd_daemon.c, src/rrd_diff.c, src/rrd_error.c,
+         src/rrd_fetch.c, src/rrd_first.c, src/rrd_format.c,
+         src/rrd_getopt.c, src/rrd_getopt.h, src/rrd_getopt1.c,
+         src/rrd_graph.c, src/rrd_graph.h, src/rrd_hw.c, src/rrd_hw_math.c,
+         src/rrd_hw_update.c, src/rrd_info.c, src/rrd_open.c,
+         src/rrd_parsetime.c, src/rrd_resize.c, src/rrd_restore.c,
+         src/rrd_rpncalc.c, src/rrd_thread_safe_nt.c, src/rrd_tool.c,
+         src/rrd_tune.c, src/rrd_update.c, src/rrd_xport.c, win32/config.h,
+         win32/rrdlib.vcproj: win32 portability patch and
+         win32/rrdlib.vcproj file for the source -- christof wegmann
+         Christof.Wegmann with exitgames.com some fixes to make the result
+         work on unix again ...
+
+2008-11-18 08:37  oetiker
+
+       * trunk/contrib/php4/rrdtool.c: added patch suggested by Lars
+         Johannesen
+
+2008-11-18 07:14  oetiker
+
+       * configure.ac: missing , added to help string
+
+2008-11-17 16:21  oetiker
+
+       * doc/Makefile.am: added build instruction for libdbi docu
+
+2008-11-16 22:24  oetiker
+
+       * doc/RRDp.pod, doc/RRDs.pod, doc/rrdgraph_libdbi.pod: missing pod
+         files
+
+2008-11-15 14:35  oetiker
+
+       * configure.ac: switch tu using AS_HELP_STRING for formatting ...
+
+2008-11-15 14:15  oetiker
+
+       * src/rrd_daemon.c: fix typo in handle_request_help --kevin
+
+2008-11-15 14:14  oetiker
+
+       * doc/rrdcached.pod: doc fixups for rrdcacehd * documented the -p
+         option * added nbsp between option and argument * layout one
+         option per line --kevin
+
+2008-11-11 16:36  oetiker
+
+       * src/rrd_graph.c: make basename work
+
+2008-11-11 16:36  oetiker
+
+       * doc/rrdcached.pod, src/rrd_daemon.c: This patch allows for
+         multiple RRD writer threads to service the queue. Now, rrdcached
+         can have more simultaneous I/O requests into the kernel. This may
+         allow the kernel to re-order disk writes, resulting in better disk
+         throughput. In practice, throughput is increased by 2-3x. The
+         flush (-f) timer maintenance has been moved from queue_thread_main
+         into its own thread. Be more careful about when to use
+         pthread_cond_signal vs _broadcast, since multiple threads may be
+         waiting on &queue_cond. --kevin
+
+2008-11-10 16:41  oetiker
+
+       * trunk/tutorial/lisa2008/rrd-intro.odp: added 1.4 notes
+
+2008-11-09 19:19  oetiker
+
+       * branches/1.3/program/configure.ac, configure.ac: be even more
+         liberal with NOT using isfinite on solaris2.8
+
+2008-11-09 00:00  oetiker
+
+       * branches/1.2/program/src/rrd_graph.c,
+         branches/1.2/program/src/rrd_graph.h,
+         branches/1.3/program/src/rrd_graph.c, src/rrd_graph.c: use
+         basename to determine filename for imginfo as suggested by
+         sebastian
+
+2008-11-08 22:57  oetiker
+
+       * src/rrd_daemon.c: rrdcached: move queue length decrement into
+         remove_from_queue -- kevin
+
+2008-11-08 22:57  oetiker
+
+       * doc/rrdcached.pod, src/rrd_daemon.c: rrdcached: examine the
+         current queue with the "QUEUE" command -- kevin
+
+2008-11-08 22:50  oetiker
+
+       * src/rrd_daemon.c: This helps with portability on platforms where
+         realloc doesn't handle NULL. -- kevin
+
+2008-11-08 18:01  oetiker
+
+       * doc/rrdcached.pod, src/rrd_daemon.c: document the "QUIT" command
+         -- kevin
+
+2008-11-08 18:01  oetiker
+
+       * src/rrd_daemon.c: rrdcached: journal_replay default failure
+         reason, just in case avoids a "may be used uninitialized" warning
+         -- kevin
+
+2008-11-08 18:00  oetiker
+
+       * src/rrd_daemon.c: added "quit" command -- kevin
+
+2008-11-08 17:57  oetiker
+
+       * src/rrd_daemon.c: rrdcached now frees all of its resources
+         correctly. This facilitates memory debugging. g_tree now knows how
+         to free the nodes when it removes them. Also, use g_tree_replace
+         instead of g_tree_insert. This fixes a bug triggered when the same
+         file was simultaneously inserted by two clients. --kevin
+
+2008-11-08 17:56  oetiker
+
+       * configure.ac: do not quit when libdbi is missing
+
+2008-11-07 22:46  oetiker
+
+       * branches/1.2/program/src/rrd_graph.c,
+         branches/1.3/program/src/rrd_graph.c, src/rrd_graph.c: fixed
+         --font TITLE:12: problem -- tobi
+
+2008-11-07 22:23  oetiker
+
+       * src/rrd_fetch_libdbi.c: fix scaling issue -- Martin
+
+2008-11-07 14:09  oetiker
+
+       * src/rrd_fetch_libdbi.c: extra file to make libdbi patch work
+
+2008-11-07 14:07  oetiker
+
+       * CONTRIBUTORS, configure.ac, src/Makefile.am, src/rrd_fetch.c,
+         src/rrd_tool.h: As some of you may know that I have created a
+         patch for rrdtool 1.2 a few years ago, so that a database could be
+         queried for values for graphing. The patch has been mostly
+         rewritten and the following changes have been made: * high
+         dependency on mysql has been reduced by avoiding the temporary
+         tables (which was bad for mysql replication) * The number of
+         executed SQL-Statements for one CDEF has been reduced to 1
+         compared to 11 SQLs (including CREATE TEMPORARY TABLE) - for patch
+         against version 1.2 * All consolidation is done in rrdtool itself
+         (MIN,MAX,AVERAGE) * Additional consolidation functions are COUNT
+         and SIGMA, which give information on statistics on a per
+         "time-bin" basis. * All these consolidation values are always
+         returned as separate columns, that are returned by RRD and the
+         consolidation function given as Argument is ignored. Main reason
+         is that this way there is only one call to rrd_fetcht and thus the
+         database even if we need to fetch for example min, avg and max.
+         Compare this to 3 calls in case of different consolidation
+         functions - and if you want to get SIGMA and COUNT as well it is
+         still only one call to the backend and the database. * Some
+         previous existing features have been taken out at the moment to
+         allow for this reduced set of SQL queries. o prediction using the
+         values from the last X days at the same time o the corresponding
+         sigma calculation * The idea is to create generic CDEF's that will
+         do the same thing, but that is also available when using RRD-files
+         (similar to TREND, but with another scope) This will get posted as
+         a separate patch. * Overall performance should be much better and
+         the patch as a whole simpler. * The patch also includes
+         modifications to the configuration infrastructure, to make libdbi
+         support optional. -- Martin Sperl
+
+2008-11-07 14:02  oetiker
+
+       * CONTRIBUTORS, doc/rrdgraph_rpn.pod, src/rrd_rpncalc.c,
+         src/rrd_rpncalc.h: new CDEF functions for predictions PREDICT and
+         PREDICTSIGMA -- Martin Sperl
+
+2008-11-07 13:51  oetiker
+
+       * src/rrd_daemon.c: Much simpler handling of timestamp errors.
+         Return an error to the user when any of the time stamp values are
+         invalid. This is similar to RRDTool's normal behavior. Removed the
+         complex logic previously used to return error codes to the user.
+         This solves a bug where non-advancing timestamps could have
+         produced incorrect error output during "BATCH" mode. The bug was
+         cause by using the sock->wbuf pointer for the error output. --
+         kevin brintnall
+
+2008-11-07 13:45  oetiker
+
+       * src/rrd_daemon.c: rrdcached: pull in rrd_config.h so we can use
+         its defines ... particularly HAVE_VSNPRINTF -- kevin brintnall
+
+2008-11-04 07:12  oetiker
+
+       * bindings/Makefile.am, bindings/lua/Makefile.am, doc/Makefile.am: I
+         realize now that the problem is the line test -f lua/Makefile &&
+         cd lua && $(MAKE) install || true in the target
+         "install-data-local", in bindings/Makefile.am. It forces execution
+         of bindings/lua/Makefile independently of lua being found or not.
+         I added that line in my first patch, following perl, python and
+         ruby build style, but it's not needed after I switched to
+         automake. The make recursion is controlled by SUBDIRS, which will
+         only contain "lua" if BUILD_LUA is true. -- Fidelis Assis
+
+2008-11-02 17:25  oetiker
+
+       * bindings/lua/Makefile.am: fix build process for systems without
+         lua
+
+2008-11-02 17:09  oetiker
+
+       * src/rrd_daemon.c: PTHREAD_COND_INITIALIZER may only be used to
+         initialize static cond variables. -- Sebastian Harl
+
+2008-11-02 15:25  oetiker
+
+       * configure.ac: reverting non build makefile patch ... we need to
+         lua makefile for building the ditribution
+
+2008-11-01 09:09  oetiker
+
+       * configure.ac: do not create the lua makefile if lua is not to be
+         compiled.
+
+2008-10-30 14:32  oetiker
+
+       * doc/cdeftutorial.pod: fix from alex
+
+2008-10-28 08:57  oetiker
+
+       * NEWS, doc/rrddump.pod, doc/rrdtool-dump.xsd, src/rrd_dump.c,
+         src/rrd_tool.c: - remove the spaccing between the elements - add
+         xsd support to dump output - change the argument
+         "[--no-header|-n]" to [--header|-h {xsd,dtd}] -- tobias.lindenmann
+         1und1.de
+
+2008-10-28 08:47  oetiker
+
+       * trunk/website/rrdtool-dump.xsd: xsd for rrdtool
+
+2008-10-26 15:18  oetiker
+
+       * configure.ac, src/Makefile.am, src/librrd.sym.in,
+         src/librrd.sym.in.in: make compilation on older OSX versios work
+         by removing the # from the librrd.sym file
+
+2008-10-24 07:59  oetiker
+
+       * trunk/talks/opentuesday/rrd-intro.odp: added talk
+
+2008-10-24 06:20  oetiker
+
+       * branches/1.2/program/doc/rrdpython.pod,
+         branches/1.3/program/doc/rrdpython.pod, doc/rrdpython.pod: fixed
+         typo ... GAUGE not GUAGE
+
+2008-10-22 20:41  oetiker
+
+       * src/rrd_daemon.c: The previous code relied on the assumption that
+         pthread_cond_init(&cond) was equivalent to memset(&cond,0). This
+         may not be true on all platforms. -- kevin
+
+2008-10-22 06:02  oetiker
+
+       * src/rrd_daemon.c: remove_cache_item() did not check whether a file
+         was in queue before modifying the cache head/tail pointers.
+         Therefore, the process of flushing old files may perturb the
+         cache_queue_head pointer. This caused some nodes with
+         CI_FLAGS_IN_QUEUE to be un-linked from the queue list. Thereafter,
+         they would not be flushed by any periodic process (although they
+         could be revived with FLUSH or UPDATE). This caused a slow memory
+         leak for files that are no longer updated. Pending updates for
+         these "abandoned" files would remain in memory ad infinitum. With
+         this patch, remove_from_queue() will check that the item is queued
+         before modifying the head/tail pointers. This restores the
+         intended behavior. --kevin
+
+2008-10-22 05:08  oetiker
+
+       * bindings/lua/Makefile.lua.in: this was replaced by Makefile.am
+
+2008-10-21 06:10  oetiker
+
+       * bindings/lua/Makefile.am: guess one needs that in svn too for
+         luabindings to work
+
+2008-10-21 05:42  oetiker
+
+       * src/rrd_daemon.c: * Open all listen sockets in daemonize(), while
+         we still have stderr. Changed open_listen_socket_* routines to
+         complain to stderr. Now, any errors in binding to the listen
+         sockets are much more obvious. * Simplified exit of parent after
+         fork() * PID file will be correctly cleaned up if there is a
+         failure in daemonize(). * unlink the unix socket before trying to
+         bind() (after we're sure we have the PID file) -- kevin
+
+2008-10-20 11:46  oetiker
+
+       * src/rrd.h, src/rrd_create.c, src/rrd_open.c, src/rrd_update.c:
+         rrd_notify_row patch: - Delegate choice of starting row for newly
+         created RRD files to the rrd_open.c API. - Introduce the
+         rrd_notify_row() function so that an implementation can choose to
+         align the rows of new RRDs with existing RRDs, if desirable. -
+         Maintain the existing behaviour (random starting row) by default.
+         -- Daniel.Pocock
+
+2008-10-20 06:06  oetiker
+
+       * bindings/Makefile.am, bindings/lua/README,
+         bindings/lua/compat-5.1r5, bindings/lua/compat-5.1r5/compat-5.1.c,
+         bindings/lua/compat-5.1r5/compat-5.1.h,
+         bindings/lua/compat-5.1r5/compat-5.1.lua, bindings/lua/rrdlua.c,
+         bindings/lua/test.lua, bindings/lua/test.lua.bottom, configure.ac:
+         - Lua module build and install process rewritten using
+         automake+libtool - improved check for compat-5.1 availability --
+         Fidelis Assis
+
+2008-10-18 22:32  oetiker
+
+       * src/rrd_open.c: rrd_open should not create files with restrictive
+         masks Removed unnecessary "mode" varaible. The mode is only used
+         when O_CREAT is specified, where we want to use 0666 (as
+         rrd_create_fn did r<=1612). --kevin
+
+2008-10-18 15:50  oetiker
+
+       * src/rrd.h, src/rrd_open.c: - encapsulate fd and mmap related
+         variables within a private data structure - rrd_file_t keeps a
+         pointer to the private data structure of type void*, so that other
+         block storage implementations can store their internal data with
+         rrd_file_t -- Daniel.Pocock
+
+2008-10-17 05:50  oetiker
+
+       * src/rrd_client.c: rrdc_flush_if_daemon should not clobber error
+         message -- kevin (as suggested by Sebastian)
+
+2008-10-17 05:44  oetiker
+
+       * branches/1.3/program/src/rrd_graph.c, src/rrd_graph.c: use double
+         for legend positioning to fight wiggle
+
+2008-10-16 21:33  oetiker
+
+       * doc/rrdbuild.pod: fix version numbers in compile instructions tobi
+
+2008-10-16 21:30  oetiker
+
+       * src/rrd_client.c: In almost all cases where rrdc_flush can fail,
+         it will leave a more descriptive error message anyway.. better not
+         overwrite it. -- kevin
+
+2008-10-16 21:12  oetiker
+
+       * src/rrd.h, src/rrd_create.c, src/rrd_dump.c, src/rrd_fetch.c,
+         src/rrd_first.c, src/rrd_info.c, src/rrd_last.c,
+         src/rrd_lastupdate.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_tune.c, src/rrd_update.c: - rrd_open() calculates file
+         size for new files and calls mmap once for the whole file -
+         rrd_resize() cleaned up, no longer passing a size through the
+         cookie argument - rrd_init(&my_rrd) must be called before
+         rrd_open() - if people are calling rrd_open directly from
+         application code, this might be troublesome. Alternative
+         solutions: creating an additional function, rrd_open_create(), or
+         adding an extra argument to rrd_open() for setting the file size
+         -- Daniel Pocock
+
+2008-10-16 21:05  oetiker
+
+       * src/rrd_open.c, src/rrd_update.c: move rrd_lock into rrd_open
+         where the general rrd_file ops are located. -- Daniel Pocock
+
+2008-10-16 06:11  oetiker
+
+       * src/rrd_daemon.c: error reporting caused crash in journal replay
+
+2008-10-16 06:11  oetiker
+
+       * doc/rrdbuild.pod: better build docs
+
+2008-10-15 05:29  oetiker
+
+       * src/rrd.h, src/rrd_create.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_tool.h, src/rrd_update.c: reverted r1601 and r1606 since
+         r1601 introduced a non portable mremap and r1606 has a dependency
+         on r1601.
+
+2008-10-14 20:14  oetiker
+
+       * src/rrd.h, src/rrd_create.c, src/rrd_open.c, src/rrd_update.c:
+         This moves selection of the initial RRA row into the rrd_open.c
+         API The current implementation (random row) is used by default.
+         However, it now provides an opportunity for alternative
+         implementations to integrate with rrdtool in a single place. Maybe
+         there are other places in rrdtool where I should insert calls to
+         the function rrd_notify_row()? This has been tested with rrdtool
+         create and rrdtool info to verify that random rows are selected by
+         default (existing behaviour preserved). -- Daniel Pocock
+
+2008-10-14 19:23  oetiker
+
+       * src/rrd_daemon.c: Under most circumstances, rrdcached can detect a
+         stale pid file. If the process in the pid file does not exist, or
+         cannot be signalled by the rrdcached owner, then rrdcached will
+         replace the pid file and start normally. Otherwise, it will
+         complain verbosely to STDERR. --kevin
+
+2008-10-14 19:08  oetiker
+
+       * src/rrd_client.c: * this preserves principle of least surprise
+         when dealing with files that are reachable via many path strings.
+         i.e. when $PWD=/base/dir the following files are the same:
+         /base/dir/x.rrd x.rrd ../dir/x.rrd * for performance, absolute
+         paths (starting with '/') are not resolved. this reduces the
+         number of stat(2) system calls.
+
+2008-10-14 19:08  oetiker
+
+       * doc/rrdcached.pod, src/rrd_daemon.c: Now, the daemon will check
+         that a base directory is NOT reached via symbolic link.
+         Documentation added to illustrate the restriction. This allows
+         several simplifying (and performance-enhancing) assumptions to be
+         made elsewhere in the code: * it ensures that paths resolved in
+         the client via realpath() will match our data structure keys *
+         it's possible to generate the correct absolute path when given a
+         relative path by simply prepending the base directory * it's not
+         necessary to resolve paths that begin with '/' -- kevin
+
+2008-10-14 19:08  oetiker
+
+       * CONTRIBUTORS, src/rrd_daemon.c: rrdcached treats relative and
+         absolute paths the same -- kevin
+
+2008-10-14 11:33  oetiker
+
+       * src/rrd_create.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_tool.h: Some observations I made while implementing this:
+         - In rrd_open(), the call to rrd_init() clobbers values already
+         populated in rrd_create.c - The logic for newfile_size in
+         rrd_open() wasn't really able to cope with the logic for a file
+         starting with size 0 I've tried to deal with these issues, but
+         maybe there is a more elegant solution. -- Daniel Pocock
+
+2008-10-14 06:37  oetiker
+
+       * src/rrd_daemon.c: clearer way of advancing the flush time -- kevin
+
+2008-10-13 22:07  oetiker
+
+       * src/rrd_daemon.c: This patch reduces the number of
+         time()/gettimeofday() system calls when doing high volume
+         processing. This enables about 25% speed increase during journal
+         replay and "BATCH" processing. (this is a function of syscall
+         overhead). * note when "BATCH" processing or journal replay
+         starts, use that timestamp for all commands * use the batch start
+         time to detect when we're in batch mode. no longer need a separate
+         boolean. * pass the time_t into handle_request * pass the time_t
+         through to the commands that need it -- kevin brintnall
+
+2008-10-13 18:41  oetiker
+
+       * src/rrd_daemon.c: fixed bad folding marker (on
+         handle_request_flush) -- kevin
+
+2008-10-11 09:53  oetiker
+
+       * src/rrd_daemon.c: this ensures that the response is
+         protocol-compliant even if additional info has been added to the
+         write buffer during processing. -- kevin brintnall
+
+2008-10-11 09:37  oetiker
+
+       * doc/rrdcached.pod, src/rrd_daemon.c: This patch introduces a
+         feature whereby rrdcached will disallow updates that do not
+         advance the update time. This prevents the updates from being
+         discarded later by rrd_update_r. This patch attempts to make the
+         most of the protocol's limited ability to return error text when
+         using a -1 return code. -- kevin brintnall
+
+2008-10-10 05:21  oetiker
+
+       * etc/rrdcached-init: This patch ensures that when rrdcached is
+         stopped, it cleans up the pid file. Apparently this is necessary
+         if RRDCACHED_USER is not the default "rrdcached". -- Bernard Li
+
+2008-10-10 05:21  oetiker
+
+       * rrdtool.spec: The patch I submitted for rrdtool.spec introduced a
+         bug where there are two ldconfig calls in the %postun section.
+         This patch fixes that. -- Bernard Li
+
+2008-10-10 05:19  oetiker
+
+       * etc/rrdcached-default, etc/rrdcached-init: This patch does two
+         things: - remove the --pidfile check, for compatibility with older
+         RHEL/Centos systems - update the username used by the init script,
+         to remain consistent with Bernard's changes to the spec file --
+         Daniel.Pocock
+
+2008-10-08 20:47  oetiker
+
+       * rrdtool.spec: Split-out rrd-cached subpackage -- Bernard Li
+
+2008-10-08 05:49  oetiker
+
+       * rrdtool.spec: This patch updates the spec file and includes the
+         librrd.pc file in the -devel subpackage so that you can build the
+         RPM again. -- Bernard
+
+2008-10-08 05:47  oetiker
+
+       * src/rrd_daemon.c: fix crash reported by Bernard Li -
+         connection_thread_main: avoid double calls to close_connection --
+         kevin
+
+2008-10-08 05:46  oetiker
+
+       * doc/rrdcached.pod: fixed typo
+
+2008-10-07 21:10  oetiker
+
+       * doc/rrdcached.pod, src/rrd_daemon.c: This patch introduces two new
+         commands for cache management: PENDING: shows any un-written
+         updates for a file FORGET : remove a file completely from cache --
+         kevin brintnall
+
+2008-10-07 21:08  oetiker
+
+       * src/rrd_daemon.c: This patch moves the permission handling code
+         around a bit. * moved privilege checks into the command handler
+         functions (possible now that we pass the sock data structures
+         around) * on UPDATE, delay journal_write until after
+         check_file_access(). previously, it was possible for a high-priv
+         socket to introduce commands into the journal that could be
+         replayed if they were still in the journal at next startup. *
+         moved has_privilege() further up in the file to avoid need for
+         prototype. -- kevin brintnall
+
+2008-10-07 16:28  oetiker
+
+       * src/rrd_daemon.c: This patch introduces some extra safety checks
+         in journal processing, and cleans up the code a little bit. *
+         moved journal initialization to its own function; main() is
+         cleaner * any time we process a file, log the results (previous
+         code only loggded if there was a valid entry) * After reading
+         journals at startup, only trigger full flush out to disk if the
+         user specified -F. Avoids unnecessary IO on startup unless the
+         user also wants unnecessary IO on shutdown. * journal_replay is
+         much more careful about files it will open * must be a regular
+         file * must be owned by daemon user * must not be group/other
+         writable * Ensure that the journal gets created with the right
+         permissions. ... even when the daemon is invoked with a permissive
+         umask. equivalent to "chmod a-x,go-w" -- kevin
+
+2008-10-07 15:37  oetiker
+
+       * src/rrd_open.c: aniel Pocock reported that the argument may be
+         NULL in low-diskspace situations, so check for that here to
+         prevent a segmentation fault. -- Florian Forster
+
+2008-10-06 19:14  oetiker
+
+       * bindings/lua/Makefile.lua: Makefile.lua is now generated
+
+2008-10-06 19:13  oetiker
+
+       * bindings/lua/Makefile.lua.in: added missing file from lua 5.0
+         integration patch
+
+2008-10-06 19:05  oetiker
+
+       * doc/rrdcached.pod, src/rrd_daemon.c: This patch introduces "BATCH"
+         mode. In this mode, a client can feed multiple commands to
+         rrdcached without waiting for acknowledgement. This permits
+         multiple commands to be sent for each read()/write(). This can
+         dramatically increase the command throughput by increasing the
+         amount of work done per system call. It enables over 100k
+         updates/second with no CPU utilization due to the reduced system
+         calls. -- kevin brintnall
+
+2008-10-06 19:04  oetiker
+
+       * src/rrd_daemon.c: This patch introduces buffered I/O to rrdcached.
+         Now, rrdcached can interpret as many commands as arrive in a
+         single read(), and it will use fewer write()s when there are
+         multiple output lines. All routines now pass around
+         listen_socket_t objects instead of file descriptors. All I/O is
+         now contained in two routines. It's no longer necessary to specify
+         the line count in multi-line outputs, since that is calculated
+         automatically. This is the foundation for accepting batched
+         commands. -- kevin brintnall
+
+2008-10-06 05:05  oetiker
+
+       * CONTRIBUTORS, bindings/Makefile.am, bindings/lua/Makefile.lua,
+         bindings/lua/README, bindings/lua/rrdlua.c, configure.ac,
+         doc/rrdlua.pod: Support to Lua 5.0 -- Fidelis Assis
+
+2008-10-04 22:35  oetiker
+
+       * trunk/tutorial/lisa2008/rrd-by-example/body.tex,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/v-interfaces.pl,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/v-interfaces.txt: added
+         v-interface example
+
+2008-10-04 16:05  oetiker
+
+       * configure.ac, src/Makefile.am, src/librrd.pc.in: initial librrd
+         file added ... this will need more love since it does not pick up
+         any -rpath settings
+
+2008-10-04 14:09  oetiker
+
+       * trunk/tutorial/lisa2008/rrd-by-example/body.tex,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/graph-examples.pl: added
+         graditents
+
+2008-10-04 13:34  oetiker
+
+       * branches/1.3/program/src/rrd_resize.c, src/rrd_resize.c,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/hw-demo.pl: * rrd_resize
+         fixed so that it does NOT alter the original file (mmap side
+         effect) * rrd_resize can can deal with version 4 files, so let it
+
+2008-10-03 22:53  oetiker
+
+       * branches/1.3/program/src/rrd_open.c, src/rrd_open.c: The patch
+         fixs rrdtools behaviour when presented with trunkated rrd files. *
+         Make sure that we do not extend past the end of the file when
+         reading the header. * Makes sure that the data portion of the file
+         is large enough, based on the ds_cnt and the total number of RRA
+         rows -- kevin brintnall
+
+2008-10-03 12:24  oetiker
+
+       * trunk/tutorial/lisa2008/rrd-by-example/body.tex,
+         trunk/tutorial/lisa2008/rrd-intro.odp: updates for lisa
+
+2008-10-03 06:13  zaucker
+
+       * trunk/talks/opentuesday/intro.txt: Ergaenzt.
+
+2008-10-02 19:17  oetiker
+
+       * src/rrd_daemon.c: folding fix
+
+2008-10-02 19:08  oetiker
+
+       * bindings/ruby/extconf.rb: missing spaces in extconf.rb caused
+         compile failure -- kevin
+
+2008-10-02 17:17  oetiker
+
+       * etc/rrdcached-default: permission fix for rrdcached socket --
+         Daniel.Pocock
+
+2008-10-02 11:03  oetiker
+
+       * trunk/talks/opentuesday/intro.txt: initial
+
+2008-10-02 11:03  oetiker
+
+       * trunk/talks/opentuesday: initial
+
+2008-10-02 06:21  oetiker
+
+       * trunk/tutorial/lisa2008/rrd-by-example/body.tex,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/graph-examples.pl,
+         trunk/tutorial/lisa2008/rrd-by-example/traffic/index.cgi: minor
+         layout adjustments
+
+2008-10-01 20:58  oetiker
+
+       * trunk/tutorial/lisa2008/rrd-by-example/body.tex,
+         trunk/tutorial/lisa2008/rrd-intro.odp: read for lisa2008
+
+2008-10-01 20:25  oetiker
+
+       * trunk/tutorial/lisa2008/rrd-by-example/ex/catch-fetch.sh,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/create-first.sh,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/update-real.sh: export
+         is not required
+
+2008-10-01 20:22  oetiker
+
+       * src/Makefile.am: since rrdcached uses pthread functions, use the
+         threadsafe version of librrd as well. This will also reasolve
+         build problems on boxes there the ptherad functions must be linked
+         explicitly.
+
+2008-10-01 20:04  oetiker
+
+       * doc/rrdcached.pod: Clearer explanation on which paths are
+         permitted when -B is supplied -- kevin
+
+2008-10-01 20:01  oetiker
+
+       * bindings/lua/Makefile.lua, bindings/ruby/extconf.rb,
+         bindings/ruby/main.c, bindings/tcl/Makefile.am,
+         bindings/tcl/tclrrd.c, configure.ac, src/Makefile.am,
+         src/rrd_client.c, src/rrd_dump.c, src/rrd_gfx.c, src/rrd_open.c,
+         src/rrd_xport.c: Fixes for the following compiler warnings: -
+         unused variable - unused parameter - assignment / argument
+         discards qualifiers from pointer target type - comparison between
+         signed and unsigned - too many arguments to function - assignment
+         makes pointer from integer without a cast - incompatible pointer
+         type - differ in signedness - implicit declaration of function -
+         enumeration value not handled in switch - value computed is not
+         used Most notably, a possible segfault in the Rrd_Lastupdate()
+         code of the TCL bindings has been fixed. Also, -Wundef (warn if an
+         undefined identifier is evaluated in an #if directive) has been
+         removed from CFLAGS. I don't see any problem with letting
+         undefined identifiers evaluate to "false" in rrdtool. Keeping that
+         option would produce a lot of (imho unnecessary) errors which
+         would need to be fixed using ugly preprocessor statements like
+         '#if defined(FOO) && FOO'. -- Sebastian Harl
+
+2008-10-01 19:48  oetiker
+
+       * Makefile.am, etc, etc/rrdcached-default, etc/rrdcached-init,
+         rrdtool.spec: I've adapted an init script for rrdcached, and also
+         incorporated it into the spec file so that it is deployed with the
+         RPM. There are also some other changes to the spec file so that I
+         could build an RPM successfully from trunk. I'm happy to tidy up
+         the spec file some more if no one else wants to mandate the best
+         way to do it. By default, rrdcached runs as nobody. I've tested
+         this on a server running Ganglia gmetad. Regards, Daniel
+
+2008-10-01 19:44  oetiker
+
+       * doc/rrdcached.pod, src/rrd_daemon.c: Now, moving a value to the
+         head of the queue is O(1). Before it was O(queue size). This
+         improves performance of individual flushes when there is a large
+         number of files in the queue. As a result, we don't hold the
+         cache_lock as much. Revamped enqueue_cache_item to take advantage
+         of the new structure. Renamed _wipe_ci_values to look nicer with
+         other code. --kevin
+
+2008-10-01 17:16  oetiker
+
+       * trunk/tutorial/lisa2008/rrd-by-example/body.tex,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/hw-demo.data,
+         trunk/tutorial/lisa2008/rrd-by-example/traffic,
+         trunk/tutorial/lisa2008/rrd-by-example/traffic/codewalk.png,
+         trunk/tutorial/lisa2008/rrd-by-example/traffic/graph.inc,
+         trunk/tutorial/lisa2008/rrd-by-example/traffic/ifbyteget.sh,
+         trunk/tutorial/lisa2008/rrd-by-example/traffic/index.cgi: added
+         example code and working presentation
+
+2008-10-01 17:15  oetiker
+
+       * trunk/tutorial/lisa2008/rrd-by-example/ex/biggraph.pl,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/graph-examples.pl,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/hw-demo.pl,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/hw-example.pl: more
+         examples
+
+2008-10-01 16:53  oetiker
+
+       * src/rrd_graph.c: lcd is actually gcd!
+
+2008-09-30 18:44  oetiker
+
+       * doc/rrdcached.pod, src/rrd_daemon.c: When -B is specified, the
+         daemon will only operate on files within the base directory.
+         Symlink detection is omitted for performance reasons (if a user
+         can create a symlink, they can probably overwrite the RRDs
+         anyway). -- kevin
+
+2008-09-30 18:43  oetiker
+
+       * src/rrd_daemon.c: check permissions before writing to journal --
+         kevin
+
+2008-09-30 18:42  oetiker
+
+       * src/rrd_daemon.c: The daemon should behave the same way w/r/t
+         files whether we "-g" or not. -- kevin
+
+2008-09-29 20:00  oetiker
+
+       * src/rrd_daemon.c: This bug caused the last line in each journal
+         file to be processed a second time. Since it had been modified due
+         to tokenizing, it failed syntax check. The daemon would always
+         record one failed line at end-of-journal as a result. No data loss
+         incurred by this bug. -- kevin
+
+2008-09-29 19:58  oetiker
+
+       * doc/rrdcached.pod, src/rrd_daemon.c: This patch introduces the
+         concept of socket privilege levels. "UPDATE" and "FLUSHALL"
+         commands are restricted to high-privilege sockets. "FLUSH"
+         commands can be executed on any socket. This is ideal for
+         multi-user installations where only certain users need write
+         access to the RRD files. Now, nearly all socket information is
+         passed around the daemon in listen_socket_t data structures. In
+         case there is other per-socket state (i.e. if we add
+         authentication) we can put it there. Also, I created a new
+         "open_listen_socket_network" and removed the network setup from
+         "open_listen_socket". -- kevin
+
+2008-09-29 19:56  oetiker
+
+       * src/rrd_client.c, src/rrd_update.c: This patch provides better
+         error messages to the client when something goes wrong with the
+         daemon. When possible, the daemon error message is passed through
+         to rrd_set_error() on the client. Prior to this patch, most error
+         conditions would result in "Internal error", which is not very
+         helpful. -- kevin brintnall
+
+2008-09-29 14:38  oetiker
+
+       * trunk/tutorial/lisa2008/rrd-by-example/ex/catch-fetch.sh: initial
+
+2008-09-29 06:45  oetiker
+
+       * trunk/tutorial/lisa2008/rrd-by-example,
+         trunk/tutorial/lisa2008/rrd-by-example/beamercolorthemetobi.sty,
+         trunk/tutorial/lisa2008/rrd-by-example/body.tex,
+         trunk/tutorial/lisa2008/rrd-by-example/ex,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/create-first.sh,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/create-first.xml,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/graph-examples.pl,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/rrd-size.pl,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/rrd-size.txt,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/update-real.sh,
+         trunk/tutorial/lisa2008/rrd-by-example/ex/update-real.txt,
+         trunk/tutorial/lisa2008/rrd-by-example/handouts.tex,
+         trunk/tutorial/lisa2008/rrd-by-example/presentation.tex: added
+         examples
+
+2008-09-28 21:37  oetiker
+
+       * doc/rrdcached.pod: spelling fix -- kevin
+
+2008-09-28 21:36  oetiker
+
+       * doc/rrdcached.pod, src/rrd_daemon.c: This patch removes an extra
+         "SIGNALS" section in the rrdcached.pod and merges "[BUG] fixed
+         hang in flush_file() introduced by per-file flush condition". --
+         kevin brintnall
+
+2008-09-28 19:27  oetiker
+
+       * src/rrd_daemon.c: a few missing {{{ folding }}} markers added --
+         kevin brintnall
+
+2008-09-28 19:26  oetiker
+
+       * doc/rrdcached.pod, src/rrd_daemon.c: This patch introduces "fast
+         shutdown" mode and two new signals. -- kevin brintnall
+
+2008-09-28 19:25  oetiker
+
+       * doc/rrdcached.pod, src/rrd_daemon.c: Moved signal handler setup
+         out of daemonize(). Coalesced common code in preparation for new
+         signals. Documented behavior of existing signals. -- kevin
+         brintnall
+
+2008-09-28 19:23  oetiker
+
+       * MakeMakefile: Attached is a patch to lower the version
+         requirements of libtool and automake. I have tested this on CentOS
+         4.x with the specified versions of libtool and automake and was
+         able to build RRDTool fine. I did *not* test building with PHP,
+         tcl, ruby or Python though. I also abstracted the version numbers
+         of all the dependencies such that editing them in the future will
+         be easier. -- Bernard Li
+
+2008-09-28 19:12  oetiker
+
+       * doc/rrdcached.pod, src/rrd_daemon.c: added support for FLUSHALL
+         command -- kevin brintnall
+
+2008-09-28 19:10  oetiker
+
+       * src/rrd_daemon.c: move cache broadcast into enqueue_cache_item --
+         kevin brintnall
+
+2008-09-28 19:10  oetiker
+
+       * src/rrd_daemon.c: When -z <jitter> is specified, some updates may
+         be timestamped up to <jitter> seconcds in the future. Therefore, a
+         timeout of now+1 may not be sufficient. Set abs_timeout past the
+         point where any updates are currently specified. -- kevin
+         brintnall
+
+2008-09-28 19:09  oetiker
+
+       * src/rrd_daemon.c: The PID file is created with open() in the
+         parent process, while we still have STDERR open. If it cannot be
+         created, it complains verbosely to stderr. The PID file is written
+         in the child process. The only way the fdopen() will fail on a fd
+         that is already open is if you're completely out of memory. As in
+         other places in the code, I didn't consider this a case that
+         required a very verbose message. (Search for "strdup failed"). If
+         you still think a more verbose message is called for, please
+         suggest one. The attached patch corrects the error message to
+         complain about fdopen() vs fopen(). I hadn't noticed that until
+         you brought it up. -- kevin brintnall
+
+2008-09-28 19:08  oetiker
+
+       * src/librrd.sym.in, src/rrd.h: rrd.h, librrd.sym: Define and export
+         rrd_info_r() -- Sebastian Harl
+
+2008-09-28 15:01  oetiker
+
+       * branches/1.2/program/src/rrd_graph.c,
+         branches/1.3/program/src/rrd_graph.c,
+         branches/1.3/program/src/rrd_tool.c, src/rrd_graph.c,
+         src/rrd_tool.c: vdef calc was using end_orig to determine for
+         which range it should do its calculations which is odd, since orig
+         is only the requested range as invocation time and not the data
+         range deliverd by fetch. It does fall completely flat when
+         shifting since shifting does not affect the original data. Bug
+         #177 reported by hokiel
+
+2008-09-27 11:44  oetiker
+
+       * branches/1.2/program/src/rrd_xport.c,
+         branches/1.3/program/src/rrd_xport.c, src/rrd_xport.c: rrdxport
+         was completely broken for exporting datasources that did not have
+         a uniform step size. Thanks to Peter Valdemar Mørch for finding
+         this.
+
+2008-09-26 05:11  oetiker
+
+       * src/rrd_daemon.c: create the pid file before forking, so we can
+         complain to stderr -- kevin brintnall
+
+2008-09-26 05:10  oetiker
+
+       * doc/rrdcached.pod, doc/rrdflush.pod, src/rrd_daemon.c: This patch
+         ensures that the "FLUSH" command will write the updates out to RRD
+         before returning to the user. Before, it returned when the update
+         was "dequeued"; updates were not necessarily on disk. Also, for
+         new nodes, the cache_lock is not held while we are setting up the
+         new node. We don't want to be holding the lock if the stat()
+         blocks. -- kevin brintnal
+
+2008-09-25 21:07  oetiker
+
+       * src/rrd_client.c: Use the same IPv6/IPv4 as for the client as
+         Florian did for the server -- kevin brintnall
+
+2008-09-25 20:31  oetiker
+
+       * src/rrd_daemon.c: This fixes a couple problems when exiting due to
+         signal: * connection threads exit without closing client socket
+         (so client blocks waiting for response that never comes) *
+         listen_queue_thread blocks on poll() until a new connection comes
+         in (because no poll timeout specified) -- kevin brintnall
+
+2008-09-25 20:11  oetiker
+
+       * src/rrd_daemon.c: set SO_REUSEADDR on the listen socket -- kevin
+         brintnall
+
+2008-09-25 20:10  oetiker
+
+       * doc/rrdcached.pod, src/rrd_daemon.c: Support for IPv6 has been
+         broken with revision 1522: Because IPv6-addresses contain colons,
+         simply checking for a colon and using everything after it does
+         destroy correctly formatted IPv6-addresses. This patch checks for
+         dots '.' in the address. If the address contains at least one dot,
+         it is considered to be a hostname or an IPv4-address and a simple
+         search for a colon is done. If no dot is found, the code will
+         check for an opening square bracket '[' at the beginning of the
+         address. If one if found, the format [address]:port is assumed. If
+         neither applies, the default port will be used. -- Florian Forster
+
+2008-09-25 15:25  oetiker
+
+       * doc/rrdcached.pod, doc/rrddump.pod, doc/rrdfetch.pod,
+         doc/rrdflush.pod, doc/rrdgraph.pod, doc/rrdinfo.pod,
+         doc/rrdlast.pod, doc/rrdlastupdate.pod, doc/rrdupdate.pod,
+         doc/rrdxport.pod, src/rrd_client.c: This adds support for
+         <address>:<port> in the rrd client library. Obviously this is
+         required to take advantage of the server's ability to bind to a
+         non-standard port -- kevin brintnall
+
+2008-09-24 22:21  oetiker
+
+       * doc/rrdcached.pod, src/rrd_daemon.c: Allow ports to be specified
+         with -l addr:port also. -- kevin brintnall
+
+2008-09-24 22:07  oetiker
+
+       * bindings/Makefile.am: the rrdlua.pod is in the doc directory
+
+2008-09-24 06:04  oetiker
+
+       * doc/rrdcached.pod: added section on error reporting -- Florian
+         Forster
+
+2008-09-22 06:35  oetiker
+
+       * MakeMakefile, bindings/Makefile.am, bindings/lua,
+         bindings/lua/Makefile.lua, bindings/lua/README,
+         bindings/lua/rrdlua.c, bindings/lua/test.lua, configure.ac,
+         doc/Makefile.am, doc/rrdlua.pod: I finally finished the first
+         version of the patch (attached) -- Fidelis Assis fidelis pobox.com
+         (this does not seem to quite work yet at least not in my hardy
+         setup)
+
+2008-09-22 05:38  oetiker
+
+       * branches/1.2/program/configure.ac,
+         branches/1.3/program/configure.ac, configure.ac: use default
+         prefix for ruby install if no user defined prefix is available
+
+2008-09-19 20:34  oetiker
+
+       * branches/1.3/program/bindings/perl-shared/Makefile.PL,
+         bindings/perl-shared/Makefile.PL: Bsd lirary path switch added to
+         perl bindings makefile -- as suggested by Kevin Brintnall
+
+2008-09-18 13:30  oetiker
+
+       * branches/1.3/program/src/rrd_open.c, src/rrd_open.c: Fix for
+         debian bug 498183 ... never *advise after the end of the file. On
+         sparc this causes a segfault. Thanks to Sebastian Harl and Jurij
+         Smakov for finding this.
+
+2008-09-14 15:30  oetiker
+
+       * src/rrd_daemon.c: fix RRDCacheD documentation -- Florian Forster
+
+2008-09-14 15:28  oetiker
+
+       * src/rrd_client.c: The previous code was broken: The response was
+         read using `read(2)'. If the server wasn't sending fast enough,
+         the client would stop reading before the entire message had been
+         read. This patch changes the communication code to use the (line
+         based) `fgets' function rather than the lower level `read'
+         function. After reading the first line (which contains the total
+         number of line to be expected), this precise number of lines is
+         read - blocking if necessary. Also, the missing four new statistic
+         values have been added to `rrdc_stats_get'. --Folorian Forester
+
+2008-09-14 10:35  oetiker
+
+       * CONTRIBUTORS, doc/rrdcached.pod, doc/rrdflush.pod,
+         src/librrd.sym.in, src/rrd_client.c, src/rrd_client.h,
+         src/rrd_daemon.c, src/rrd_dump.c, src/rrd_fetch.c,
+         src/rrd_flush.c, src/rrd_graph.c, src/rrd_graph.h, src/rrd_info.c,
+         src/rrd_last.c, src/rrd_lastupdate.c, src/rrd_tool.h,
+         src/rrd_update.c, src/rrd_xport.c: did not pick up all the changes
+         for rrdcached in the first round ... so here is the second batch.
+
+2008-09-14 09:49  oetiker
+
+       * CONTRIBUTORS, configure.ac, doc/Makefile.am, doc/rrdcached.pod,
+         doc/rrddump.pod, doc/rrdfetch.pod, doc/rrdgraph.pod,
+         doc/rrdinfo.pod, doc/rrdlast.pod, doc/rrdlastupdate.pod,
+         doc/rrdtool.pod, doc/rrdupdate.pod, doc/rrdxport.pod,
+         src/Makefile.am, src/librrd.sym.in, src/rrd.h, src/rrd_client.c,
+         src/rrd_client.h, src/rrd_daemon.c, src/rrd_dump.c,
+         src/rrd_fetch.c, src/rrd_graph.c, src/rrd_graph.h, src/rrd_info.c,
+         src/rrd_last.c, src/rrd_lastupdate.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_update.c, src/rrd_xport.c: RRDcached
+         patch. This implements an infrastructure, where rrd updates can be
+         sent to a daemon which caches them prior to bulk-updateing rrd
+         files. See the rrdcached manual page. -- Created by Florian
+         Forster with some help from Kevin Brintnall.
+
+2008-09-13 11:42  oetiker
+
+       * configure.ac, src/Makefile.am, src/rrd_config_bottom.h: regarding
+         #183: * move part of rrd_config.h out to a separate file, so that
+         autoconf is not confused by the more complex expressions. * fix
+         configure variable name to allow caching * another attemt at
+         fixing solaris 2.8 compile so that it works out of the box.
+
+2008-09-13 10:56  oetiker
+
+       * doc/rrdgraph.pod: fix graph docs
+
+2008-09-11 20:27  oetiker
+
+       * src/rrd_graph.c: fixed processing of custom fonts René GARCIA
+         <rene@margar.fr>
+
+2008-09-11 07:01  oetiker
+
+       * src/rrd_format.h: no , on the last member in C
+
+2008-09-08 22:12  oetiker
+
+       * trunk/website/award-white.png,
+         trunk/website/bin/pod2wml.classic.sh, trunk/website/forum.wml,
+         trunk/website/inc/favicon.ico, trunk/website/inc/happy-tobi.png,
+         trunk/website/inc/rrdtool-gold.png,
+         trunk/website/oscon-120x60.gif, trunk/website/stream-pop.png:
+         missing bits atted
+
+2008-09-08 22:09  oetiker
+
+       * trunk/website/.wmkrc, trunk/website/.wmlrc: and they too
+
+2008-09-07 10:22  oetiker
+
+       * trunk/website/index.wml, trunk/website/navbar.inc,
+         trunk/website/support.wml: sync befor branching
+
+2008-09-07 07:19  oetiker
+
+       * src/rrd_update.c: The attached patch eliminates the many places
+         where the rra_current pointer was maintained; the existing
+         write/seek mechanisms update rrd_file->pos correctly. Also, the
+         seek at the front of process_arg is not necessary; write_to_rras
+         will now take care to correct the file position at every update
+         cycle. This eliminates unnecessary rrd_seek() calls when MMAP is
+         not enabled. -- kevin brintnall kbrint rufus.net
+
+2008-09-06 15:50  oetiker
+
+       * trunk/website/index.wml: added note on 1.3.2
+
+2008-09-06 15:48  oetiker
+
+       * trunk/website/bin/pod2wml.sh, trunk/website/inc/template.inc,
+         trunk/website/index.wml, trunk/website/prog/build.sh,
+         trunk/website/rrdworld/gbrrdgraphix.xml,
+         trunk/website/rrdworld/index.wml: improve html generation
+
+2008-09-06 14:24  oetiker
+
+       * bindings/perl-piped/RRDp.pm, bindings/perl-shared/RRDs.pm,
+         doc/rrdbuild.pod, rrdtool.spec, src/pngsize.c, src/rrd.h,
+         src/rrd_cgi.c, src/rrd_create.c, src/rrd_datalang.c,
+         src/rrd_diff.c, src/rrd_dump.c, src/rrd_error.c, src/rrd_fetch.c,
+         src/rrd_first.c, src/rrd_format.c, src/rrd_format.h,
+         src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph_helper.c,
+         src/rrd_hw.c, src/rrd_hw.h, src/rrd_i18n.h, src/rrd_info.c,
+         src/rrd_is_thread_safe.h, src/rrd_last.c, src/rrd_lastupdate.c,
+         src/rrd_not_thread_safe.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_restore.c, src/rrd_rpncalc.c, src/rrd_rpncalc.h,
+         src/rrd_thread_safe.c, src/rrd_thread_safe_nt.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_tune.c, src/rrd_update.c,
+         src/rrd_version.c, src/rrd_xport.c, src/rrd_xport.h,
+         src/rrdupdate.c: prepare for the release of rrdtool-1.3.2
+
+2008-09-06 11:46  oetiker
+
+       * src/rrd_tool.h: we need rrd_format.h here for things to compile
+         properly
+
+2008-09-06 11:43  oetiker
+
+       * doc/rrdbuild.pod: updated opensolaris build instructions
+
+2008-09-06 10:43  oetiker
+
+       * bindings/perl-piped/RRDp.pm, configure.ac, src/Makefile.am,
+         src/librrd.sym, src/librrd.sym.in, src/rrd_tool.h: only link build
+         and use rrd_getopt if there is no getopt_long provided by by the
+         os ... this should also fix problems for solaris 2.8 and other non
+         getopt_long oses who were not taken care of anymore after the
+         intriduction of librrd.sym
+
+2008-09-06 10:39  oetiker
+
+       * src/rrd_update.c: fix for multi update - data corruption bug as
+         reported (and patched) in #178 by kevin brintnall
+
+2008-09-06 10:32  oetiker
+
+       * doc/cdeftutorial.pod, doc/rrdgraph.pod, doc/rrdgraph_graph.pod,
+         doc/rrdtool.pod: make pod checker happy
+
+2008-09-06 08:58  oetiker
+
+       * src/rrd_open.c: inline functions are not supported in c99 ... lets
+         remove this. the compiler will foture it out if they are
+         important.
+
+2008-09-04 21:45  oetiker
+
+       * src/rrd_graph.c: introduced a persistant fontmap ... this makes
+         our pango rendering 6 times faster starting from the second graph
+         we draw.
+
+2008-08-31 23:42  oetiker
+
+       * src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph.h: now the results
+         actually look as they did with the old code only things are FASTER
+
+2008-08-31 22:53  oetiker
+
+       * src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph.h,
+         src/rrd_graph_helper.c: Improve performance by keeping multiple
+         pango font descriptions. For the first invocation we are about 20%
+         times as fast for the second one about 4-5 times faster ...
+
+2008-08-31 10:55  oetiker
+
+       * src/rrd_gfx.c: trying to improve the pango performance I added
+         facilities to cache layout,context and more in between invocations
+         of the text function, unfortunately it does not seem to help all
+         that much.
+
+2008-08-31 10:52  oetiker
+
+       * bindings/perl-shared/RRDs.xs, bindings/ruby/main.c: use the
+         standart info free function
+
+2008-08-28 05:49  oetiker
+
+       * src/rrd_tool.c: fix handling of error conditions in rrd_tool.c ...
+         errno is not for detection, only for information AFTER detection.
+
+2008-08-28 04:13  oetiker
+
+       * src/rrd_tool.c: free the linebuffer if you don't return it!
+
+2008-08-27 22:06  oetiker
+
+       * src/rrd_gfx.c: some more leak plugging
+
+2008-08-27 21:48  oetiker
+
+       * src/rrd_graph.c: drop the dummy surface prior to creating the real
+         one
+
+2008-08-27 21:46  oetiker
+
+       * src/rrd_tool.c: added some freeing for error conditions
+
+2008-08-27 21:25  oetiker
+
+       * src/rrd.h, src/rrd_gfx.c, src/rrd_graph.c, src/rrd_open.c,
+         src/rrd_update.c: fixed indentation ... gnu indent results are not
+         realy beautifl. I might switch to uncrustify.
+
+2008-08-27 21:25  oetiker
+
+       * src/rrd_create.c: Many rrd_create memory leaks patched ... based
+         on valgrind analysis by Sven Engelhardt. Thanks!
+
+2008-08-27 21:18  oetiker
+
+       * src/rrd_restore.c: fix indentation
+
+2008-08-27 06:52  oetiker
+
+       * configure.ac: added sparc-sun-solaris2.8 to list of broken
+         isfinite implementations
+
+2008-08-22 22:04  oetiker
+
+       * bindings/ruby/main.c: it is rb_rrd_infocall not rrd_infocall ...
+
+2008-08-16 07:13  oetiker
+
+       * doc/rrdgraph.pod, src/rrd_graph.c: When in lazy mode, and the
+         graph does not get regenerated, then there are no results from
+         PRINT either. Fix for #163.
+
+2008-08-16 07:11  oetiker
+
+       * bindings/perl-shared/RRDs.pm: documentation updates
+
+2008-08-12 06:48  oetiker
+
+       * src/rrd_graph.c: make sure imageinfo only reports the filename and
+         not the whole path. This is a regression from 1.2.x where this was
+         implemented properly.
+
+2008-08-09 15:22  oetiker
+
+       * trunk/website/gallery/index.en.html,
+         trunk/website/gallery/index.wml,
+         trunk/website/gallery/systembelastung.png,
+         trunk/website/gallery/systembelastung.xml: added systembelastung
+
+2008-08-04 20:35  oetiker
+
+       * src/rrd_gfx.c: The attached patch converts the given string to
+         UTF-8 using glib's g_locale_to_utf8() function. This function
+         expects the string to be encoded as specified by the locale
+         settings, so this will fail if e.g. you're using some script which
+         is encoded in some way incompatible to the locale settings. In
+         this case, the patch falls back to use the original string as
+         provided by the user as there is no way to tell how to interpret
+         that string. So, in this case, you will still get the old behavior
+         (that pango warning and messed up strings in the graph). However,
+         imho this is perfectly fine since it should be up to the user to
+         take care of a sane environment. -- Sebastian Harl
+
+2008-07-29 14:42  oetiker
+
+       * bindings/perl-shared/RRDs.xs: newer perlapi docu suggests to use
+         SV * instead of void for managed returns
+
+2008-07-29 05:08  oetiker
+
+       * src/rrd_gfx.c, src/rrd_hw_update.c: fixed some c++ style lines --
+         David Masterson
+
+2008-07-28 07:23  oetiker
+
+       * doc/rrdgraph_data.pod: spelling fix
+
+2008-07-23 15:03  oetiker
+
+       * src/rrd_restore.c: forward ported patch for rra cur_row
+         randomization in rrd_restore from 1.2.28 to 1.3.1
+
+2008-07-23 13:02  oetiker
+
+       * bindings/perl-piped/RRDp.pm, bindings/perl-shared/RRDs.pm,
+         configure.ac, doc/rrdbuild.pod, rrdtool.spec, src/pngsize.c,
+         src/rrd.h, src/rrd_cgi.c, src/rrd_create.c, src/rrd_datalang.c,
+         src/rrd_diff.c, src/rrd_dump.c, src/rrd_error.c, src/rrd_fetch.c,
+         src/rrd_first.c, src/rrd_format.c, src/rrd_format.h,
+         src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph_helper.c,
+         src/rrd_hw.c, src/rrd_hw.h, src/rrd_i18n.h, src/rrd_info.c,
+         src/rrd_is_thread_safe.h, src/rrd_last.c, src/rrd_lastupdate.c,
+         src/rrd_not_thread_safe.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_restore.c, src/rrd_rpncalc.c, src/rrd_rpncalc.h,
+         src/rrd_thread_safe.c, src/rrd_thread_safe_nt.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_tune.c, src/rrd_update.c,
+         src/rrd_version.c, src/rrd_xport.c, src/rrd_xport.h,
+         src/rrdupdate.c: prepare for the release of rrdtool-1.3.1
+
+2008-07-23 12:45  oetiker
+
+       * src/rrd_restore.c: added support for restoring rrdtool 1.0.x files
+         ... removed support for old HW patched rrd format created by an
+         early patched version of 1.0.x
+
+2008-07-22 15:15  oetiker
+
+       * branches/1.2/program/src/rrd_create.c, src/rrd_create.c: Fix for
+         #167 - rrdcreate is arguably missing a check for 'step>=1' for
+         RRAs with AVERAGE as a CF. I ran into this when specifying 0.6 as
+         a step value, assuming floats were also accepted. Patch attached
+         here. -- Bogdan
+
+2008-07-01 06:35  oetiker
+
+       * doc/rrdbuild.pod: header-fix
+
+2008-07-01 06:34  oetiker
+
+       * doc/rrdbuild.pod: improve layout
+
+2008-06-29 16:23  oetiker
+
+       * src/rrd_update.c: added rrd filename to illegal update message --
+         he at uninett.no
+
+2008-06-29 14:08  oetiker
+
+       * rrdtool.spec: rrdtool.spec fix by Anton Blanchard to build without
+         php patch
+
+2008-06-29 13:57  oetiker
+
+       * doc/rrdgraph.pod, src/rrd_graph.c: make sure image-with and
+         image-height get returned even when rrdtool is called with --lazy
+         as reported in bug #163
+
+2008-06-27 06:08  zaucker
+
+       * trunk/tutorial/lisa2008/abstract-rrdexample.txt,
+         trunk/tutorial/lisa2008/abstract-rrdtut.txt: Etwas ueberarbeitet
+
+2008-06-24 15:05  oetiker
+
+       * trunk/tutorial/lisa2008,
+         trunk/tutorial/lisa2008/abstract-rrdexample.txt,
+         trunk/tutorial/lisa2008/abstract-rrdtut.txt: intial
+
+2008-06-19 13:18  oetiker
+
+       * branches/1.2/program/bindings/python/rrdtoolmodule.c,
+         bindings/python/rrdtoolmodule.c: rrdtool.fetch is returning an
+         extra, bogus, data point. Attached is a patch to correct this. -
+         Gilad Raphaelli. Fix for #161
+
+2008-06-12 13:54  oetiker
+
+       * doc/rrdgraph_graph.pod: added note on %S
+
+2008-06-11 15:49  oetiker
+
+       * NEWS: made news the same as the announcement
+
+2008-06-10 23:12  oetiker
+
+       * bindings/perl-piped/RRDp.pm, bindings/perl-shared/RRDs.pm,
+         doc/rrdbuild.pod, doc/rrdcreate.pod, doc/rrdgraph.pod,
+         doc/rrdgraph_graph.pod, rrdtool.spec, src/pngsize.c, src/rrd.h,
+         src/rrd_cgi.c, src/rrd_create.c, src/rrd_datalang.c,
+         src/rrd_diff.c, src/rrd_dump.c, src/rrd_error.c, src/rrd_fetch.c,
+         src/rrd_first.c, src/rrd_format.c, src/rrd_format.h,
+         src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph_helper.c,
+         src/rrd_hw.c, src/rrd_hw.h, src/rrd_i18n.h, src/rrd_info.c,
+         src/rrd_is_thread_safe.h, src/rrd_last.c, src/rrd_lastupdate.c,
+         src/rrd_not_thread_safe.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_restore.c, src/rrd_rpncalc.c, src/rrd_rpncalc.h,
+         src/rrd_thread_safe.c, src/rrd_thread_safe_nt.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_tune.c, src/rrd_update.c,
+         src/rrd_version.c, src/rrd_xport.c, src/rrd_xport.h,
+         src/rrdupdate.c: prepare for the release of rrdtool-1.3.0
+
+2008-06-10 23:11  oetiker
+
+       * trunk/website/bin/htmlfix.sh, trunk/website/gallery/index.en.html,
+         trunk/website/index.wml: updated website online
+
+2008-06-10 16:41  oetiker
+
+       * branches/1.2/website/index.wml, trunk/website, trunk/website/bin,
+         trunk/website/download.wml, trunk/website/gallery,
+         trunk/website/inc, trunk/website/index.wml,
+         trunk/website/license.wml, trunk/website/logos,
+         trunk/website/newbanner250x60.gif, trunk/website/rrdtool.dtd,
+         trunk/website/rrdworld, trunk/website/sponsor.wml,
+         trunk/website/tut: added website to trunk
+
+2008-06-10 16:38  oetiker
+
+       * trunk/website: using the 1.2 website as a basis for the new 1.3
+         website
+
+2008-06-10 16:38  oetiker
+
+       * configure.ac: oops it is 1.3.0 not 1.4.0 yet
+
+2008-06-10 16:37  oetiker
+
+       * 00README, CONTRIBUTORS, PROJECTS, TODO, configure.ac,
+         rrdtool-1.3-release: preparing for 1.3
+
+2008-06-10 16:32  oetiker
+
+       * src/Makefile.am, src/rrd.h, src/rrd_format.h, src/rrd_hw_math.c,
+         src/rrd_hw_update.c, src/rrd_tool.h: document the 'internal'
+         functions in rrd.h but mark them as deprecated, so that anyone
+         using them will see that they should not be doing it.
+
+2008-06-10 11:16  oetiker
+
+       * src/rrd_create.c: flip check for row_count with setting for better
+         code logic
+
+2008-06-10 09:35  oetiker
+
+       * doc/rrdcreate.pod, src/rrd_create.c: check row count in RRAs to be
+         > 0 ... by Sebastian Harl
+
+2008-06-10 07:18  oetiker
+
+       * src/rrd_create.c, src/rrd_open.c: for windows we must open files
+         in O_BINARY mode
+
+2008-06-09 16:10  oetiker
+
+       * src/rrd.h, src/rrd_tool.h: do NOT export lowlevel functions, since
+         I have no intention supporting these interfaces across versions.
+         The symbols are in the library, so if someone WANTS to code to
+         them, they are free todo so, but they will have to use our
+         internal header files.
+
+2008-06-09 15:49  oetiker
+
+       * src/librrd.sym, src/rrd.h, src/rrd_tool.h: add all lowlevel file
+         access functions from the rrd_open interface to the export list.
+
+2008-06-09 05:12  oetiker
+
+       * bindings/python/rrdtoolmodule.c, src/pngsize.c, src/rrd.h,
+         src/rrd_error.c, src/rrd_graph.h, src/rrd_info.c, src/rrd_open.c,
+         src/rrd_parsetime.c, src/rrd_tool.c, src/rrd_update.c: fixed
+         indenting
+
+2008-06-09 05:11  oetiker
+
+       * rrdtool.spec: update to rpm spec from Jarod Wilson
+
+2008-06-08 21:15  oetiker
+
+       * src/librrd.sym: export some more symbols from librrd
+
+2008-06-08 21:06  oetiker
+
+       * win32/config.h: windows should use a windows font
+
+2008-06-08 21:05  oetiker
+
+       * src/rrd_graph.c: drop special case windows font code ... it makes
+         no sense with the new setup
+
+2008-06-08 17:08  oetiker
+
+       * bindings/perl-piped/RRDp.pm, bindings/perl-shared/RRDs.pm,
+         configure.ac, doc/rrdbuild.pod, rrdtool.spec, src/pngsize.c,
+         src/rrd.h, src/rrd_cgi.c, src/rrd_create.c, src/rrd_datalang.c,
+         src/rrd_diff.c, src/rrd_dump.c, src/rrd_error.c, src/rrd_fetch.c,
+         src/rrd_first.c, src/rrd_format.c, src/rrd_format.h,
+         src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph_helper.c,
+         src/rrd_hw.c, src/rrd_hw.h, src/rrd_i18n.h, src/rrd_info.c,
+         src/rrd_is_thread_safe.h, src/rrd_last.c, src/rrd_lastupdate.c,
+         src/rrd_not_thread_safe.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_restore.c, src/rrd_rpncalc.c, src/rrd_rpncalc.h,
+         src/rrd_thread_safe.c, src/rrd_thread_safe_nt.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_tune.c, src/rrd_update.c,
+         src/rrd_version.c, src/rrd_xport.c, src/rrd_xport.h,
+         src/rrdupdate.c: prepare for the release of rrdtool-1.3rc9
+
+2008-06-08 17:08  oetiker
+
+       * src/rrd_gfx.c, src/rrd_graph.c: fixed pango markup switch ... we
+         still want the text to show even if no markup is enabled
+
+2008-06-08 16:47  oetiker
+
+       * bindings/python/rrdtoolmodule.c, bindings/ruby/main.c,
+         src/librrd.sym: rrd_freemem has to be exported as well ...
+
+2008-06-08 16:23  oetiker
+
+       * branches/1.2/program/src/pngsize.c, src/pngsize.c: fix for ticket
+         #121 jmpbuf on aix is a problem it seems, maybe this makes it
+         better.
+
+2008-06-08 15:59  oetiker
+
+       * src/Makefile.am: prepare for the release of rrdtool-1.3rc8
+
+2008-06-08 15:55  oetiker
+
+       * bindings/perl-piped/RRDp.pm, bindings/perl-shared/RRDs.pm,
+         configure.ac, doc/rrdbuild.pod, rrdtool.spec, src/pngsize.c,
+         src/rrd.h, src/rrd_cgi.c, src/rrd_create.c, src/rrd_datalang.c,
+         src/rrd_diff.c, src/rrd_dump.c, src/rrd_error.c, src/rrd_fetch.c,
+         src/rrd_first.c, src/rrd_format.c, src/rrd_format.h,
+         src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph_helper.c,
+         src/rrd_hw.c, src/rrd_hw.h, src/rrd_i18n.h, src/rrd_info.c,
+         src/rrd_is_thread_safe.h, src/rrd_last.c, src/rrd_lastupdate.c,
+         src/rrd_not_thread_safe.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_restore.c, src/rrd_rpncalc.c, src/rrd_rpncalc.h,
+         src/rrd_thread_safe.c, src/rrd_thread_safe_nt.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_tune.c, src/rrd_update.c,
+         src/rrd_version.c, src/rrd_xport.c, src/rrd_xport.h,
+         src/rrdupdate.c: prepare for the release of rrdtool-1.3rc8
+
+2008-06-08 15:55  oetiker
+
+       * bindings/perl-shared/RRDs.xs, bindings/python/rrdtoolmodule.c,
+         bindings/ruby/main.c, bindings/tcl/tclrrd.c, doc/rrdthreads.pod,
+         netware/Makefile, src/Makefile.am, src/librrd.sym,
+         src/parsetime.c, src/parsetime.h, src/rrd.h, src/rrd_cgi.c,
+         src/rrd_create.c, src/rrd_error.c, src/rrd_fetch.c,
+         src/rrd_graph.c, src/rrd_graph.h, src/rrd_graph_helper.c,
+         src/rrd_info.c, src/rrd_nan_inf.c, src/rrd_not_thread_safe.c,
+         src/rrd_open.c, src/rrd_parsetime.c, src/rrd_parsetime.h,
+         src/rrd_resize.c, src/rrd_thread_safe.c, src/rrd_thread_safe_nt.c,
+         src/rrd_tool.c, src/rrd_tool.h, src/rrd_update.c, src/rrd_xport.c,
+         win32/rrd.dsp, win32/rrd.vcproj: From: Sebastian Harl sh
+         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.
+
+2008-06-07 14:35  oetiker
+
+       * src/rrd_open.c: don't bother with don't need when there is neither
+         madvise not fadvise calls
+
+2008-06-06 16:38  oetiker
+
+       * branches/1.2/program/src/rrd_graph.c, src/rrd_graph.c: fix for
+         --font bug (font names wer not being picked up at all). Bug
+         reported by Dmitry B. Bigunayk
+
+2008-06-05 20:56  oetiker
+
+       * NEWS: added pango markup name
+
+2008-06-05 19:59  oetiker
+
+       * src/rrd.h, src/rrd_error.c, src/rrd_format.h, src/rrd_gfx.c,
+         src/rrd_info.c, src/rrd_open.c, src/rrd_update.c: fixed indenting
+
+2008-06-05 19:59  oetiker
+
+       * src/rrd_graph.c: disable pango markup by default. added
+         --pango-markup option for explicit enablement.
+
+2008-06-05 19:58  oetiker
+
+       * doc/rrdgraph.pod, src/rrd_graph.h: disable pango markup by
+         default. added --pango-markup option for explicit enablement.
+
+2008-06-05 19:57  oetiker
+
+       * bindings/python/rrdtoolmodule.c: fixed indenting
+
+2008-06-04 16:18  oetiker
+
+       * bindings/tcl/Makefile.am, configure.ac: enable new building for
+         newer tcl versions
+
+2008-06-02 06:46  oetiker
+
+       * configure.ac: updated documentation of library versioning system
+
+2008-06-02 06:43  oetiker
+
+       * configure.ac: Had to reset the interface version number due to the
+         change in the rrd_context implementation.
+
+2008-06-02 06:21  oetiker
+
+       * src/rrd_restore.c: use DNAN instead of NAN since there seem to be
+         issues with gcc on solaris and the NAN experession (and we use
+         DNAN everywhere else anyway).
+
+2008-06-01 21:12  oetiker
+
+       * doc/rrdbuild.pod: integrated gentoo bits and spell fixes by Peter
+         Volkov
+
+2008-06-01 20:44  oetiker
+
+       * src/rrd.h, src/rrd_error.c, src/rrd_not_thread_safe.c,
+         src/rrd_thread_safe.c, src/rrd_thread_safe_nt.c: Patch for
+         rrd_context system by sebastian harl, making it simpler and less
+         error prone. Removing one buffer overflow in the process.
+
+2008-05-31 15:58  oetiker
+
+       * doc/rrdbuild.pod: prepare for the release of rrdtool-1.3rc7
+
+2008-05-31 15:57  oetiker
+
+       * bindings/perl-piped/RRDp.pm, bindings/perl-shared/RRDs.pm,
+         configure.ac, doc/rrdbuild.pod, rrdtool.spec, src/pngsize.c,
+         src/rrd.h, src/rrd_cgi.c, src/rrd_create.c, src/rrd_datalang.c,
+         src/rrd_diff.c, src/rrd_dump.c, src/rrd_error.c, src/rrd_fetch.c,
+         src/rrd_first.c, src/rrd_format.c, src/rrd_format.h,
+         src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph_helper.c,
+         src/rrd_hw.c, src/rrd_hw.h, src/rrd_i18n.h, src/rrd_info.c,
+         src/rrd_is_thread_safe.h, src/rrd_last.c, src/rrd_lastupdate.c,
+         src/rrd_not_thread_safe.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_restore.c, src/rrd_rpncalc.c, src/rrd_rpncalc.h,
+         src/rrd_thread_safe.c, src/rrd_thread_safe_nt.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_tune.c, src/rrd_update.c,
+         src/rrd_version.c, src/rrd_xport.c, src/rrd_xport.h,
+         src/rrdupdate.c: prepare for the release of rrdtool-1.3rc7
+
+2008-05-31 15:56  oetiker
+
+       * doc/rrdbuild.pod: added pkgconfig build instructions
+
+2008-05-31 12:28  oetiker
+
+       * branches/1.2/program/src/rrd_graph.c, src/rrd_graph.c: make sure
+         vdef calcs only calc as far as originally requested.
+
+2008-05-27 22:24  oetiker
+
+       * src/rrd_info.c: fixed maxlength for sprintf_alloc 50 was not
+         enough ... 1024+strlen(fmt) is much better. This makes imginfo
+         work even when there are long image paths.
+
+2008-05-26 16:39  oetiker
+
+       * trunk/talks/nordic-nagios-08/talk.odp: initial talk slides
+
+2008-05-26 11:45  oetiker
+
+       * TODO: updated todo list
+
+2008-05-26 08:56  oetiker
+
+       * bindings/perl-piped/RRDp.pm, bindings/perl-shared/RRDs.pm,
+         configure.ac, doc/rrdbuild.pod, rrdtool.spec, src/pngsize.c,
+         src/rrd.h, src/rrd_cgi.c, src/rrd_create.c, src/rrd_datalang.c,
+         src/rrd_diff.c, src/rrd_dump.c, src/rrd_error.c, src/rrd_fetch.c,
+         src/rrd_first.c, src/rrd_format.c, src/rrd_format.h,
+         src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph_helper.c,
+         src/rrd_hw.c, src/rrd_hw.h, src/rrd_i18n.h, src/rrd_info.c,
+         src/rrd_is_thread_safe.h, src/rrd_last.c, src/rrd_lastupdate.c,
+         src/rrd_not_thread_safe.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_restore.c, src/rrd_rpncalc.c, src/rrd_rpncalc.h,
+         src/rrd_thread_safe.c, src/rrd_thread_safe_nt.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_tune.c, src/rrd_update.c,
+         src/rrd_version.c, src/rrd_xport.c, src/rrd_xport.h,
+         src/rrdupdate.c: prepare for the release of rrdtool-1.3rc6
+
+2008-05-26 07:46  oetiker
+
+       * NEWS: added notes about new grid lines
+
+2008-05-26 07:45  oetiker
+
+       * src/rrd_graph.c: * make y-axis labels NOT run into each other
+         (classic and alt) * space horizontal grid lines and lables a bit
+         further apart for tight layouts. * let horizontal lines and y axis
+         labels run a little closer for classic grid
+
+2008-05-25 21:17  oetiker
+
+       * src/rrd_graph.c: fixed calculation of space at the bottom of the
+         graph
+
+2008-05-25 20:02  oetiker
+
+       * bindings/perl-piped/RRDp.pm, bindings/perl-shared/RRDs.pm,
+         configure.ac, doc/rrdbuild.pod, rrdtool.spec, src/pngsize.c,
+         src/rrd.h, src/rrd_cgi.c, src/rrd_create.c, src/rrd_datalang.c,
+         src/rrd_diff.c, src/rrd_dump.c, src/rrd_error.c, src/rrd_fetch.c,
+         src/rrd_first.c, src/rrd_format.c, src/rrd_format.h,
+         src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph_helper.c,
+         src/rrd_hw.c, src/rrd_hw.h, src/rrd_i18n.h, src/rrd_info.c,
+         src/rrd_is_thread_safe.h, src/rrd_last.c, src/rrd_lastupdate.c,
+         src/rrd_not_thread_safe.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_restore.c, src/rrd_rpncalc.c, src/rrd_rpncalc.h,
+         src/rrd_thread_safe.c, src/rrd_thread_safe_nt.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_tune.c, src/rrd_update.c,
+         src/rrd_version.c, src/rrd_xport.c, src/rrd_xport.h,
+         src/rrdupdate.c: prepare for the release of rrdtool-1.3rc5
+
+2008-05-25 20:00  oetiker
+
+       * src/rrd_format.h, src/rrd_open.c, src/rrd_update.c: fix update
+         compatibility with rrd-1.0.x files
+
+2008-05-20 22:22  oetiker
+
+       * bindings/Makefile.am: droped rrd_extra.h from the distro with the
+         python update ... must remove it from the Makefile too
+
+2008-05-19 21:14  oetiker
+
+       * NEWS, bindings/python/README, bindings/python/rrd_extra.h,
+         bindings/python/rrdtoolmodule.c, doc/rrdpython.pod: fixed python
+         bindings: * info call isnnow aequivalent to rrdtool info (the same
+         as in the perl and ruby bindings) * graphv and updatev calls added
+
+2008-05-18 22:11  oetiker
+
+       * src/rrd_gfx.c, src/rrd_graph.c, src/rrd_tool.c: * document
+         tabwidth option * fix \t -> tab expansion
+
+2008-05-18 15:39  oetiker
+
+       * src/rrd_graph.c: ytr boundery checking ... this is from the GD
+         days ... we don't need this anymore
+
+2008-05-18 15:11  oetiker
+
+       * src/rrd_dump.c, src/rrd_tool.c: fix indenting
+
+2008-05-18 15:11  oetiker
+
+       * src/rrd_graph.c: pdf, svg and ps formats were not being created
+
+2008-05-18 13:06  oetiker
+
+       * bindings/perl-piped/RRDp.pm, bindings/perl-shared/RRDs.pm,
+         configure.ac, doc/rrdbuild.pod, rrdtool-1.3-release, rrdtool.spec,
+         src/pngsize.c, src/rrd.h, src/rrd_cgi.c, src/rrd_create.c,
+         src/rrd_datalang.c, src/rrd_diff.c, src/rrd_dump.c,
+         src/rrd_error.c, src/rrd_fetch.c, src/rrd_first.c,
+         src/rrd_format.c, src/rrd_format.h, src/rrd_gfx.c,
+         src/rrd_graph.c, src/rrd_graph_helper.c, src/rrd_hw.c,
+         src/rrd_hw.h, src/rrd_i18n.h, src/rrd_info.c,
+         src/rrd_is_thread_safe.h, src/rrd_last.c, src/rrd_lastupdate.c,
+         src/rrd_not_thread_safe.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_restore.c, src/rrd_rpncalc.c, src/rrd_rpncalc.h,
+         src/rrd_thread_safe.c, src/rrd_thread_safe_nt.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_tune.c, src/rrd_update.c,
+         src/rrd_version.c, src/rrd_xport.c, src/rrd_xport.h,
+         src/rrdupdate.c: prepare for the release of rrdtool-1.3rc4
+
+2008-05-18 13:04  oetiker
+
+       * src/rrd_tool.c, src/rrdupdate.c: fix year
+
+2008-05-18 12:53  oetiker
+
+       * src/rrd_graph.c: PRINT returns from rrd_graph did not work due to
+         an off by one error
+
+2008-05-18 08:42  oetiker
+
+       * doc/rrdbuild.pod: updated build instructions to latest libraries
+
+2008-05-18 06:37  oetiker
+
+       * NEWS, configure.ac, src/rrd_i18n.h, src/rrd_tool.c: improved
+         handling of libintl issues
+
+2008-05-16 12:29  oetiker
+
+       * bindings/perl-piped/RRDp.pm, bindings/perl-shared/RRDs.pm,
+         configure.ac, doc/rrdbuild.pod, rrdtool.spec, src/pngsize.c,
+         src/rrd.h, src/rrd_cgi.c, src/rrd_create.c, src/rrd_datalang.c,
+         src/rrd_diff.c, src/rrd_dump.c, src/rrd_error.c, src/rrd_fetch.c,
+         src/rrd_first.c, src/rrd_format.c, src/rrd_format.h,
+         src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph_helper.c,
+         src/rrd_hw.c, src/rrd_hw.h, src/rrd_i18n.h, src/rrd_info.c,
+         src/rrd_is_thread_safe.h, src/rrd_last.c, src/rrd_lastupdate.c,
+         src/rrd_not_thread_safe.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_restore.c, src/rrd_rpncalc.c, src/rrd_rpncalc.h,
+         src/rrd_thread_safe.c, src/rrd_thread_safe_nt.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_tune.c, src/rrd_update.c,
+         src/rrd_version.c, src/rrd_xport.c, src/rrd_xport.h,
+         src/rrdupdate.c: prepare for the release of rrdtool-1.3rc3
+
+2008-05-16 12:28  oetiker
+
+       * doc/rrddump.pod, src/rrd_dump.c: added --no-header to
+         documentation and fixed implementation
+
+2008-05-16 12:20  oetiker
+
+       * src/rrd_dump.c: fixed dump_opt implementation
+
+2008-05-16 12:18  oetiker
+
+       * NEWS, src/rrd_dump.c: added no-header option to rrd_dump
+
+2008-05-16 12:07  oetiker
+
+       * src/rrd_graph.c: fix handling of values <= zero for logarithmic
+         display
+
+2008-05-15 22:39  oetiker
+
+       * rrdtool-1.3-release: prepare for the release of rrdtool-1.3rc2
+
+2008-05-15 22:39  oetiker
+
+       * bindings/perl-piped/RRDp.pm, bindings/perl-shared/RRDs.pm,
+         configure.ac, doc/rrdbuild.pod, rrdtool.spec, src/pngsize.c,
+         src/rrd.h, src/rrd_cgi.c, src/rrd_create.c, src/rrd_datalang.c,
+         src/rrd_diff.c, src/rrd_dump.c, src/rrd_error.c, src/rrd_fetch.c,
+         src/rrd_first.c, src/rrd_format.c, src/rrd_format.h,
+         src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph_helper.c,
+         src/rrd_hw.c, src/rrd_hw.h, src/rrd_i18n.h, src/rrd_info.c,
+         src/rrd_is_thread_safe.h, src/rrd_last.c, src/rrd_lastupdate.c,
+         src/rrd_not_thread_safe.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_restore.c, src/rrd_rpncalc.c, src/rrd_rpncalc.h,
+         src/rrd_thread_safe.c, src/rrd_thread_safe_nt.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_tune.c, src/rrd_update.c,
+         src/rrd_version.c, src/rrd_xport.c, src/rrd_xport.h,
+         src/rrdupdate.c: prepare for the release of rrdtool-1.3rc2
+
+2008-05-15 22:37  oetiker
+
+       * Makefile.am, configure.ac, rrdtool-1.3-release, src/Makefile.am:
+         fix portability to mac osx and freebsd -- tobi
+
+2008-05-12 23:10  oetiker
+
+       * configure.ac: add solaris pod2man location last
+
+2008-05-12 23:03  oetiker
+
+       * configure.ac, doc/Makefile.am, doc/rrdbuild.pod: some changes to
+         make things work on opensolaris 2008.05
+
+2008-05-12 22:42  oetiker
+
+       * bindings/tcl/Makefile.am: for some reason tclrrd.c does not get
+         picked up using $< at least on opensolaris
+
+2008-05-12 22:23  oetiker
+
+       * src/Makefile.am, src/rrd_graph.h, src/rrd_info.c,
+         src/rrd_rpncalc.c, src/rrd_rpncalc.h, src/rrd_tool.c: allow
+         rrd_rpncalc to build without cairo dependencies ... fix warnings
+
+2008-05-12 21:14  oetiker
+
+       * Makefile.am: add intltool*in files to dist
+
+2008-05-12 16:15  oetiker
+
+       * bindings/python/rrdtoolmodule.c, src/rrd_update.c: fixed indenting
+
+2008-05-12 16:15  oetiker
+
+       * src/rrd_graph.c, src/rrd_hw.c, src/rrd_open.c, src/rrd_rpncalc.c,
+         src/win32comp.c: removed c++ comments //
+
+2008-05-12 16:09  oetiker
+
+       * NEWS: added notes about rewrites in this release
+
+2008-05-12 16:08  oetiker
+
+       * configure.ac, src/Makefile.am: improved handling of library
+         versioning
+
+2008-05-12 15:35  oetiker
+
+       * src/rrd_update.c: should not leave debug enabled
+
+2008-05-12 15:33  oetiker
+
+       * branches/1.2/program/doc/rrdcreate.pod,
+         branches/1.2/program/src/rrd_update.c, doc/rrdcreate.pod,
+         src/rrd_update.c: Fixed handling of unknown data at PDP build
+         time. There was a long standing (even documented) missfeature in
+         rrdtool which caused uknown-data to be accepted as long as it was
+         less than the mrhb. This was never the intended behaviour and had
+         interesting side effects
+         (http://oss.oetiker.ch/rrdtool-trac/ticket/125): If you have a 60
+         Second step with 59s unknown data and 1 second of known data the
+         whole become known data as long as the mrhb was > step. The
+         intended behaviour was that a step should become unknown as soon
+         as it contains more than 50% of unknown data. The patch fixes both
+         the documentation and the code.
+
+2008-05-12 13:22  oetiker
+
+       * src/rrd_create.c: the last_ds value fuer unknown is 'U' and not
+         'UNKN'
+
+2008-05-04 18:59  oetiker
+
+       * src/rrd_getopt.c, src/rrd_getopt1.c: fix for sun sudio 11
+         incompatibility
+         http://www.netbsd.org/cgi-bin/query-pr-single.pl?number=38555
+
+2008-05-01 23:23  oetiker
+
+       * bindings/ruby/main.c, src/rrd.h, src/rrd_info.c, src/rrd_tool.c:
+         indenting fixes
+
+2008-05-01 23:23  oetiker
+
+       * src/rrd_graph.c: make sure properties are shown via graph_v even
+         in --graph-only mode
+
+2008-05-01 06:56  oetiker
+
+       * MakeMakefile: Make the MakeMakefile script a bit more robust
+
+2008-04-22 21:40  oetiker
+
+       * bindings/python/rrd_extra.h, bindings/python/rrdtoolmodule.c: fix
+         for #148
+
+2008-04-20 22:46  oetiker
+
+       * CONTRIBUTORS, NEWS, bindings/perl-shared/RRDs.pm,
+         bindings/perl-shared/RRDs.xs, bindings/ruby/main.c,
+         doc/rrdgraph.pod, src/rrd.h, src/rrd_graph.c, src/rrd_graph.h,
+         src/rrd_info.c, src/rrd_tool.c, src/rrd_tool.h: Introduced a
+         alternated interface to rrd_graph using rrd_info style return
+         values: rrd_graph_v The graph_v interface returnes additional
+         information about the graph including the location of the graphing
+         area within the image. When called with '-' as filename it will
+         even return the image data. The new interface is supported in the
+         rrdtool command line, RRDs perl and ruby bindings.
+
+2008-04-17 07:54  oetiker
+
+       * trunk/talks/nordic-nagios-08,
+         trunk/talks/nordic-nagios-08/abstract-rrdtut.txt: added bullets
+
+2008-04-17 05:35  oetiker
+
+       * src/rrd_update.c: indent fix
+
+2008-04-17 05:35  oetiker
+
+       * src/rrd_tune.c: do not reset the last_ds store unless the data
+         source type is actually changed.
+
+2008-04-16 15:50  oetiker
+
+       * branches/1.2/program/doc/rrdgraph.pod, doc/rrdgraph.pod: fixed 2.
+         x-grid example ... since the lable is valid for the whole day, it
+         must be 86400 wide. --tobi
+
+2008-04-12 09:29  oetiker
+
+       * MakeMakefile, configure.ac: alter order of header inclusion for
+         FreeBSD 4.8 compatibility
+
+2008-04-12 09:21  oetiker
+
+       * branches/1.2/program/src/parsetime.c, src/parsetime.c: do not mess
+         with dst status after running localtime, the system gets confused
+         otherwhise when it comes to times during the switch over periode
+         as DST changes to non DST. -- tobi
+
+2008-03-25 23:02  oetiker
+
+       * src/rrd_dump.c: added missing " escapes to xml header in
+         rrd_tool.c
+
+2008-03-25 22:59  oetiker
+
+       * CONTRIBUTORS: added Florian octo Forster to contrib list
+
+2008-03-25 22:59  oetiker
+
+       * doc/rrdgraph_examples.pod: added missing =back for propper pod
+         syntax
+
+2008-03-25 22:58  oetiker
+
+       * src/rrd_dump.c: fix indent
+
+2008-03-25 22:58  oetiker
+
+       * src/rrd_restore.c: fix broken header comment section -- Florian
+         octo Forster
+
+2008-03-25 22:57  oetiker
+
+       * src/rrd_graph.c: cast size_t to int to avoid warning -- Florian
+         octo Forster
+
+2008-03-25 22:56  oetiker
+
+       * src/rrd_open.c: do not declare DEBUG functions unless compiled in
+         DEBUG mode -- Florian octo Forster
+
+2008-03-25 22:55  oetiker
+
+       * src/rrd_tool.c: make helptexts read only memory
+
+2008-03-25 22:55  oetiker
+
+       * src/rrd_update.c: stop complaints about unused variables if not
+         running DEBUG
+
+2008-03-25 22:49  oetiker
+
+       * MakeMakefile: Added check for intltool version. Use Makefile for
+         cleanup. -- Florian octo Forster
+
+2008-03-23 13:43  oetiker
+
+       * src/rrd_dump.c: added pointer to florian forsters dtd to the rrd
+         dumpt
+
+2008-03-19 23:36  oetiker
+
+       * configure.ac: fix pkgconfig file name
+
+2008-03-15 10:39  oetiker
+
+       * ., .indent.pro, 00README, CONTRIBUTORS, COPYING, COPYRIGHT,
+         MakeMakefile, Makefile.am, NEWS, PROJECTS, README, THREADS, TODO,
+         WIN32-BUILD-TIPS.txt, acinclude.m4, bindings,
+         bindings/Makefile.am, bindings/perl-piped,
+         bindings/perl-piped/MANIFEST, bindings/perl-piped/Makefile.PL,
+         bindings/perl-piped/README, bindings/perl-piped/RRDp.pm,
+         bindings/perl-piped/leaktest.pl, bindings/perl-piped/rrdpl.dsp,
+         bindings/perl-piped/rrdpl.dsw, bindings/perl-piped/t,
+         bindings/perl-piped/t/base.t, bindings/perl-shared,
+         bindings/perl-shared/MANIFEST, bindings/perl-shared/Makefile.PL,
+         bindings/perl-shared/README, bindings/perl-shared/RRDs.pm,
+         bindings/perl-shared/RRDs.ppd, bindings/perl-shared/RRDs.xs,
+         bindings/perl-shared/ntmake.pl, bindings/perl-shared/t,
+         bindings/perl-shared/t/base.t, bindings/python,
+         bindings/python/ACKNOWLEDGEMENT, bindings/python/AUTHORS,
+         bindings/python/COPYING, bindings/python/README,
+         bindings/python/rrd_extra.h, bindings/python/rrdtoolmodule.c,
+         bindings/python/setup.py, bindings/ruby, bindings/ruby/CHANGES,
+         bindings/ruby/README, bindings/ruby/extconf.rb,
+         bindings/ruby/main.c, bindings/ruby/test.rb, bindings/tcl,
+         bindings/tcl/Makefile.am, bindings/tcl/README,
+         bindings/tcl/ifOctets.tcl.in, bindings/tcl/tclrrd.c, configure.ac,
+         debian, debian/README.Debian, debian/build_freetype.sh,
+         debian/changelog, debian/control, debian/copyright,
+         debian/librrd0-dev.files, debian/librrd0.files,
+         debian/librrd0.postinst, debian/librrd0.postrm,
+         debian/librrd0.shlibs, debian/librrdp-perl.files,
+         debian/librrds-perl.files, debian/rrdtool-tcl.files,
+         debian/rrdtool.files, debian/rules, debian/watch, doc,
+         doc/Makefile.am, doc/bin_dec_hex.pod, doc/cdeftutorial.pod,
+         doc/name.inc, doc/rpntutorial.pod, doc/rrd-beginners.pod,
+         doc/rrdbuild.pod, doc/rrdcgi.pod, doc/rrdcreate.pod,
+         doc/rrddump.pod, doc/rrdfetch.pod, doc/rrdfirst.pod,
+         doc/rrdgraph-old.pod, doc/rrdgraph.pod, doc/rrdgraph_data.pod,
+         doc/rrdgraph_examples.pod, doc/rrdgraph_graph.pod,
+         doc/rrdgraph_rpn.pod, doc/rrdinfo.pod, doc/rrdlast.pod,
+         doc/rrdlastupdate.pod, doc/rrdpython.pod, doc/rrdresize.pod,
+         doc/rrdrestore.pod, doc/rrdruby.pod, doc/rrdthreads.pod,
+         doc/rrdtool-dump.dtd, doc/rrdtool-xport.dtd, doc/rrdtool.pod,
+         doc/rrdtune.pod, doc/rrdtutorial.es.pod, doc/rrdtutorial.pod,
+         doc/rrdupdate.pod, doc/rrdxport.pod, examples,
+         examples/4charts.pl.in, examples/Makefile.am,
+         examples/bigtops.pl.in, examples/cgi-demo.cgi.in,
+         examples/minmax.pl.in, examples/perftest.pl.in,
+         examples/piped-demo.pl.in, examples/shared-demo.pl.in,
+         examples/stripes.pl.in, favicon.ico, netware, netware/Makefile,
+         po, po/ChangeLog, po/LINGUAS, po/POTFILES.in, po/de.po,
+         rrdtool-1.2-release, rrdtool-1.3-release, rrdtool.spec, src,
+         src/Makefile.am, src/compile_afm.pl, src/fnv.h, src/get_ver.awk,
+         src/hash_32.c, src/parsetime.c, src/parsetime.h, src/pngsize.c,
+         src/rrd.h, src/rrd_cgi.c, src/rrd_create.c, src/rrd_datalang.c,
+         src/rrd_diff.c, src/rrd_dump.c, src/rrd_error.c, src/rrd_fetch.c,
+         src/rrd_first.c, src/rrd_format.c, src/rrd_format.h,
+         src/rrd_getopt.c, src/rrd_getopt.h, src/rrd_getopt1.c,
+         src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph.h,
+         src/rrd_graph_helper.c, src/rrd_hw.c, src/rrd_hw.h,
+         src/rrd_hw_math.c, src/rrd_hw_math.h, src/rrd_hw_update.c,
+         src/rrd_hw_update.h, src/rrd_i18n.h, src/rrd_info.c,
+         src/rrd_is_thread_safe.h, src/rrd_last.c, src/rrd_lastupdate.c,
+         src/rrd_nan_inf.c, src/rrd_not_thread_safe.c, src/rrd_open.c,
+         src/rrd_resize.c, src/rrd_restore.c, src/rrd_rpncalc.c,
+         src/rrd_rpncalc.h, src/rrd_thread_safe.c,
+         src/rrd_thread_safe_nt.c, src/rrd_tool.c, src/rrd_tool.h,
+         src/rrd_tune.c, src/rrd_update.c, src/rrd_version.c,
+         src/rrd_xport.c, src/rrd_xport.h, src/rrdupdate.c, src/strftime.c,
+         src/strftime.h, src/unused.h, src/win32comp.c, svn2cl.xsl, win32,
+         win32/Makefile, win32/config.h, win32/rrd.dsp, win32/rrd.vcproj,
+         win32/rrd_config.h.msvc, win32/rrdtool.dsp, win32/rrdtool.dsw,
+         win32/rrdtool.vcproj: reindent aded id keyword substitution
+
+2008-03-15 10:32  oetiker
+
+       * src/rrd_rpncalc.c: reat nan as FALSE in an IF CDEF
+
+2008-03-07 08:57  oetiker
+
+       * doc/rrdgraph_rpn.pod, src/rrd_rpncalc.c, src/rrd_rpncalc.h: a
+         nan-safe add operator (ADDNAN) into rrd. I used it to add several
+         incomplete graphs. NaN + NaN => NaN x + NaN => x NaN + y => y x +
+         y => x + y -- Timo Stripf tstripf gmx.de
+
+2008-03-02 15:38  oetiker
+
+       * src/rrd_graph.c: Fix error "cairo_restore without matching
+         cairo_save" when zero-time point is out of graph
+
+2008-02-25 07:11  oetiker
+
+       * branches/1.2/program/src/rrd_fetch.c, src/rrd_fetch.c: Ignore
+         RRA-end when comparing. coverage.
+
+2008-02-24 14:27  oetiker
+
+       * src/rrd_create.c, src/rrd_graph_helper.c, src/rrd_i18n.h,
+         src/rrd_info.c, src/rrd_restore.c, src/rrd_tool.c: fix indents
+         gone out of kileter by patching ...
+
+2008-02-24 14:26  oetiker
+
+       * src/rrd_dump.c, src/rrd_tool.c: get rid of a few type cast
+         warnings
+
+2008-02-24 14:26  oetiker
+
+       * src/rrd_restore.c: fix one segfault while trying to restore an
+         invalid xml file. -- tobi
+
+2008-02-21 07:20  oetiker
+
+       * configure.ac: make ruby obey destdir
+
+2008-02-21 07:18  oetiker
+
+       * po/ChangeLog: initial
+
+2008-02-21 07:12  oetiker
+
+       * MakeMakefile, configure.ac, po/LINGUAS, po/de.po, src/rrd_tool.c:
+         make inbternationalized version actually build
+
+2008-02-21 06:59  oetiker
+
+       * po, po/LINGUAS, po/POTFILES.in, src/rrd_i18n.h: added missing bits
+         from internationalization
+
+2008-02-19 12:56  oetiker
+
+       * branches/1.2/program/src/rrd_create.c,
+         branches/1.2/program/src/rrd_info.c,
+         branches/1.2/program/src/rrd_restore.c, src/rrd_create.c,
+         src/rrd_info.c, src/rrd_restore.c: Generate a random cur_row for
+         each RRA during create/restore operations. This effectively
+         randomizes the block crossings among RRDs created around the same
+         time. Previously, RRDs that were created/restored en masse would
+         cross block boundaries simultaneously, which is sub-optimal. Also,
+         this patch enables the user to see the RRA's cur_row pointer via
+         rrdinfo. This was useful during debugging. -- kevin brintnall
+         kbrint qwest.net
+
+2008-02-18 15:04  oetiker
+
+       * MakeMakefile, Makefile.am, configure.ac, rrdtool.spec,
+         src/Makefile.am, src/rrd_getopt.c, src/rrd_tool.c: Added I18N
+         support for messages printed by rrd_tool.c --
+         http://oss.oetiker.ch/rrdtool-trac/ticket/144
+
+2008-02-15 08:49  oetiker
+
+       * configure.ac: check for pdf as well
+
+2008-02-15 08:44  oetiker
+
+       * configure.ac: check for cairo-ps and cairo-svg explicitly
+
+2008-02-06 00:12  oetiker
+
+       * configure.ac: ready for beta 4
+
+2008-02-06 00:10  oetiker
+
+       * src/rrd_graph.c, src/rrd_graph_helper.c: * replaced strtok with
+         strtok_r for thread safety * fixed im initialization broken after
+         the introduction of dashes ...
+
+2008-02-04 22:34  oetiker
+
+       * src/rrd_create.c: in rrd_create we do not use mmaping and thus
+         need to free rrd struct members in any case ...
+
+2008-02-04 22:16  oetiker
+
+       * src/rrd_update.c: this should help with the memory leak
+
+2008-02-04 18:18  oetiker
+
+       * COPYRIGHT: added zpl 2.1 to floss exception
+
+2008-02-01 06:52  oetiker
+
+       * branches/1.2/program/src/rrd_graph.c, src/rrd_graph.c: fixed
+         parsing of fontnames with embeded spaces
+
+2008-01-14 17:11  oetiker
+
+       * configure.ac: remove c++ warnings flag .. this is not c++ after
+         all
+
+2008-01-14 16:52  oetiker
+
+       * src/rrd_cgi.c, src/rrd_create.c: fixed indenting
+
+2008-01-14 16:51  oetiker
+
+       * src/rrd_gfx.c: fix indents
+
+2008-01-14 16:50  oetiker
+
+       * bindings/tcl/tclrrd.c, configure.ac, src/parsetime.c,
+         src/rrd_cgi.c, src/rrd_create.c, src/rrd_nan_inf.c: * fixed
+         madvise/fadvise detection * fixed many compiler warnings -- Peter
+         Breitenlohner peb mppmu.mpg.de
+
+2008-01-13 11:07  oetiker
+
+       * trunk/tutorial/htwchur/about-oss.odp,
+         trunk/tutorial/htwchur/m7am.odp,
+         trunk/tutorial/htwchur/rrd-exercises.tex: initial
+
+2008-01-12 18:37  oetiker
+
+       * src/rrd_graph.c: weekday and time are too tight
+
+2008-01-04 22:53  oetiker
+
+       * src/rrd_gfx.c, src/rrd_graph.c: fontmap resolution was not
+         matching scaled_font resolution ... this killed layouting ... much
+         better now
+
+2008-01-03 21:30  oetiker
+
+       * doc/rrdgraph_examples.pod, doc/rrdgraph_graph.pod,
+         src/rrd_graph_helper.c: get rid of DASHED, only use dashes syntax.
+         less redundancy -- thomas.gutzler gmail.com
+
+2008-01-02 22:11  oetiker
+
+       * CONTRIBUTORS, NEWS, doc/rrdgraph_examples.pod,
+         doc/rrdgraph_graph.pod, src/rrd_graph.c, src/rrd_graph.h,
+         src/rrd_graph_helper.c: support for dashed lines in graphs
+
+2008-01-02 22:06  oetiker
+
+       * src/pngsize.c, src/rrd_graph.c: fix indenting
+
+2008-01-02 22:06  oetiker
+
+       * doc/Makefile.am: ignore errors with txt doc building
+
+2007-12-11 23:41  oetiker
+
+       * Makefile.am, doc/rrdbuild.pod: building the docs depends on the
+         presence of pod2man ... which does not seem to be installed
+         necessarily
+
+2007-12-08 22:15  oetiker
+
+       * doc/rrdbuild.pod: added shared library locations into the docs
+
+2007-12-08 21:35  oetiker
+
+       * src/rrd_cgi.c: include stdlib since putenv is in there on solaris
+
+2007-12-08 16:57  oetiker
+
+       * configure.ac, doc/rrdbuild.pod, src/rrd_getopt.c: fixing solaris
+         portability * isinf and isnan definitions fixed * check if rt must
+         be linked * ordering must be read write in getopt * updated build
+         instructions
+
+2007-12-02 17:11  oetiker
+
+       * trunk/tutorial/htwchur/abstract-rrdtut.txt: fixed spelling
+
+2007-12-02 16:59  oetiker
+
+       * trunk/tutorial/htwchur,
+         trunk/tutorial/htwchur/abstract-rrdtut.txt,
+         trunk/tutorial/htwchur/m7am.odp: initial text
+
+2007-11-26 20:12  oetiker
+
+       * bindings/python/setup.py: fix libdir for python build
+
+2007-11-21 10:51  oetiker
+
+       * configure.ac: fixed NetBSD spelling
+
+2007-11-21 06:56  oetiker
+
+       * configure.ac: define bsd source to get access to chroot
+
+2007-11-21 06:47  oetiker
+
+       * bindings/perl-shared/Makefile.PL, configure.ac: make perlbuild
+         more portable
+
+2007-11-21 05:54  oetiker
+
+       * trunk/talks/rrdtooltipsandtricks,
+         trunk/talks/rrdtooltipsandtricks/abstract.odt,
+         trunk/talks/rrdtooltipsandtricks/bio.txt,
+         trunk/talks/rrdtooltipsandtricks/rrdtipsandtricks.odp: initial
+         checkin
+
+2007-11-20 22:22  oetiker
+
+       * configure.ac: enable madvise defines in netbsd
+
+2007-11-20 00:17  oetiker
+
+       * trunk/status/2007-11-19.txt: added 1.2.26
+
+2007-11-20 00:08  oetiker
+
+       * src/rrd_format.h, src/rrd_graph.c, src/rrd_nan_inf.c: remove last
+         traces of rrd_nan_inf.h fix handling of min==max where min < 0
+
+2007-11-19 19:05  oetiker
+
+       * trunk/status/2007-11-19.txt: spell checked
+
+2007-11-19 19:02  oetiker
+
+       * trunk/status/2007-11-19.txt: added report
+
+2007-11-15 14:39  oetiker
+
+       * bindings/ruby/main.c, src/rrd.h, src/rrd_create.c, src/rrd_dump.c,
+         src/rrd_format.h, src/rrd_graph.c, src/rrd_hw.c, src/rrd_info.c,
+         src/rrd_open.c, src/rrd_restore.c, src/rrd_tune.c,
+         src/rrd_update.c, src/rrd_xport.c: re-indented files that have
+         gone out of indent-style over the last few weeks
+
+2007-11-15 14:36  oetiker
+
+       * src/Makefile.am, src/rrd.h, src/rrd_nan_inf.h: integrate
+         rrd_nan_inf header
+
+2007-11-15 14:35  oetiker
+
+       * src/rrd_open.c: Improve layout of documentation. -- Bernhard
+         Fischer
+
+2007-11-15 14:34  oetiker
+
+       * src/rrd_update.c: * rrd_update(): Unify error path. *
+         parse_template(): Likewise. * allocate_data_structures(): Make
+         error strings use consistent punctuation. -- bernhard fischer
+
+2007-11-15 14:32  oetiker
+
+       * src/pngsize.c: make sure this compiles with aix
+
+2007-11-13 01:45  oetiker
+
+       * trunk/tutorial/lisa2007/m7am.odp: lisa update
+
+2007-11-08 10:16  oetiker
+
+       * NEWS, bindings/ruby/main.c, doc/rrdruby.pod: fixed start end time
+         in ruby fetch and added step size -- Mike Perham mperham gmail
+
+2007-11-06 21:27  oetiker
+
+       * doc/rrdbuild.pod: updated build instructions
+
+2007-11-04 21:56  oetiker
+
+       * bindings/perl-piped/RRDp.pm: handle errors properly ... in RRDp
+         read call
+
+2007-10-08 14:36  oetiker
+
+       * trunk/tutorial/lisa2007/m7am.odp,
+         trunk/tutorial/lisa2007/rrdtutorial.odp: fixed name for release
+
+2007-10-08 14:36  oetiker
+
+       * trunk/tutorial/lisa2007/abstract-rrdtut.txt,
+         trunk/tutorial/lisa2007/rrdtutorial.odp: getting ready for release
+
+2007-09-16 15:35  oetiker
+
+       * src/rrd_open.c: no reason to die when posixfadvise is not
+         sucessful
+
+2007-09-11 06:28  oetiker
+
+       * doc/rrdcreate.pod, doc/rrdtune.pod, src/rrd_create.c,
+         src/rrd_dump.c, src/rrd_format.h, src/rrd_hw.c, src/rrd_info.c,
+         src/rrd_restore.c, src/rrd_tune.c: Allow to the the smoothing
+         window size other thatn the default 5%. -- Evan Miller emiller
+         imvu.com
+
+2007-09-11 06:16  oetiker
+
+       * configure.ac, src/rrd_open.c: added msync before unmap
+
+2007-09-08 05:23  oetiker
+
+       * CONTRIBUTORS, doc/rrdgraph_rpn.pod, src/rrd_graph.c,
+         src/rrd_graph.h: added STDEV aggregation function for VDEF. --
+         Patrick J Cherry patrick bytemark.co.uk
+
+2007-09-07 22:53  oetiker
+
+       * Makefile.am, examples/Makefile.am, examples/perftest.pl.in: a few
+         leanups -- Bernhard Fischer
+
+2007-09-06 09:06  oetiker
+
+       * src/rrd_open.c: added missing ;
+
+2007-09-06 08:42  oetiker
+
+       * src/rrd_open.c: * mimic write() and read() even better -- Bernhard
+         Fischer
+
+2007-09-06 08:40  oetiker
+
+       * src/rrd_open.c: rrd_open.c (rrd_read): Mimicing read() behaviour
+         for EOF case and NULL buffer case. -- Bernhard Fischer
+
+2007-09-02 16:55  oetiker
+
+       * doc/rrdgraph_graph.pod: fixed speling of horizon
+
+2007-08-29 06:43  oetiker
+
+       * examples/perftest.pl.in: a better performance tester
+
+2007-08-16 07:31  oetiker
+
+       * doc/rrdgraph_examples.pod: added holt winters example back into
+         the examples documentation
+
+2007-08-14 21:59  oetiker
+
+       * src/rrd_update.c: Fix for HoltWinters phase-shift bug described
+         below. When one or more primary data point times were missed, the
+         SEASONAL and DEVSEASONAL archives were marked as being up-to-date,
+         so that they would not be written to. It was correct not to write
+         to these archives, but the code failed to advance the pointers
+         within the SEASONAL and DEVSEASONAL archives so that future
+         updates would go to the correct location in the archives. Rather
+         than mark these archives as up-to-date (by setting
+         rra_step_cnt[rra_idx] = 0), my patch allocates a new "skip_update"
+         array that is set to 1 for SEASONAL and DEVSEASONAL archives that
+         have missed one or more primary data points. When an RRA is
+         written to, the cur_row pointer advancement happens for all
+         archives, but the skip_update array is checked just before
+         actually writing out the changes. Please give it a whirl! -- Evan
+         Miller emiller imvu.com
+
+2007-08-13 20:06  oetiker
+
+       * src/rrd_update.c: Refactored rrd_update code in preparation of
+         finding the HW update problem -- Evan Miller
+
+2007-08-07 15:02  oetiker
+
+       * doc/rrdbuild.pod: updated build instructions for 1.3
+
+2007-08-03 23:46  oetiker
+
+       * trunk/talks/rrdtool13/rrdtool13.odp: initial talk
+
+2007-08-03 21:27  oetiker
+
+       * trunk/talks/rrdtool13/rrdtool13.odp: addded details
+
+2007-08-03 19:46  oetiker
+
+       * svn2cl.xsl: prepare for the release of rrdtool-1.2.99907080300
+
+2007-08-03 19:45  oetiker
+
+       * libraries, src/DejaVuSansMono-Roman.ttf, src/VeraMono.ttf: remove
+         some things we do not need anymore in 1.3
+
+2007-08-03 19:43  oetiker
+
+       * bindings/perl-piped/RRDp.pm, bindings/perl-shared/RRDs.pm,
+         doc/rrdbuild.pod, rrdtool.spec, src/pngsize.c, src/rrd.h,
+         src/rrd_cgi.c, src/rrd_create.c, src/rrd_datalang.c,
+         src/rrd_diff.c, src/rrd_dump.c, src/rrd_error.c, src/rrd_fetch.c,
+         src/rrd_first.c, src/rrd_format.c, src/rrd_format.h,
+         src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph_helper.c,
+         src/rrd_hw.c, src/rrd_hw.h, src/rrd_info.c,
+         src/rrd_is_thread_safe.h, src/rrd_last.c, src/rrd_lastupdate.c,
+         src/rrd_not_thread_safe.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_restore.c, src/rrd_rpncalc.c, src/rrd_rpncalc.h,
+         src/rrd_thread_safe.c, src/rrd_thread_safe_nt.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_tune.c, src/rrd_update.c,
+         src/rrd_version.c, src/rrd_xport.c, src/rrd_xport.h,
+         src/rrdupdate.c: prepare for the release of
+         rrdtool-1.2.99907080300
+
+2007-08-03 19:43  oetiker
+
+       * configure.ac, rrdtool-1.3-release: prepare for 1.3 beta 1
+
+2007-08-03 19:26  oetiker
+
+       * src/Makefile.am: continue with the revision numbering
+
+2007-08-03 19:17  oetiker
+
+       * doc/rrdgraph_graph.pod: added notes on pango inline formatting
+
+2007-08-02 18:21  oetiker
+
+       * trunk/talks/rrdtool13, trunk/talks/rrdtool13/rrdtool13.odp,
+         trunk/talks/rrdtoolfast/monitoring-en.ppt,
+         trunk/talks/rrdtoolfast/onlineusv-de.ppt: talk updates
+
+2007-08-01 15:29  oetiker
+
+       * src/rrd_graph.c: only release cairo stuff if we ever initialized
+         it.
+
+2007-08-01 15:28  oetiker
+
+       * src/rrd_xport.c: fix for 64bit portability problem ... unsigned
+         long is not int
+
+2007-07-31 04:53  oetiker
+
+       * bindings/ruby/main.c: reindented
+
+2007-07-30 22:29  oetiker
+
+       * trunk/tutorial/lisa2007/abstract-rrdtut.txt: added toppics
+
+2007-07-25 20:38  oetiker
+
+       * src/rrd_create.c, src/rrd_dump.c, src/rrd_fetch.c,
+         src/rrd_format.h, src/rrd_graph.c, src/rrd_graph_helper.c,
+         src/rrd_open.c, src/rrd_restore.c, src/rrd_rpncalc.c,
+         src/rrd_tool.h, src/rrd_tune.c: fixed indenting
+
+2007-07-25 20:38  oetiker
+
+       * src/rrd_update.c: make sure we check input even when the previous
+         update was a 'U' ... and some indenting fixes
+
+2007-07-24 20:35  oetiker
+
+       * trunk/contrib/php4/rrdtool_logo.h: added missing header
+
+2007-07-22 15:51  oetiker
+
+       * src/rrd_format.h: new consolidation functions must be added last
+         or this will break binary compatibility
+
+2007-07-22 15:50  oetiker
+
+       * src/rrd_update.c: don't force data out ... let cache management do
+         this
+
+2007-07-22 09:47  oetiker
+
+       * NEWS, src/rrd_create.c, src/rrd_dump.c, src/rrd_format.h,
+         src/rrd_restore.c: Only create version 4 rrd files if the new
+         holtwinters MHW.. CF is used.
+
+2007-07-21 19:55  oetiker
+
+       * src/rrd_open.c: * remove some experimental code from rrd_open *
+         rrd_dontneed will release all data except for header blocks and
+         RRA hot blocks which are going to be updated withing 10 minutes.
+
+2007-07-21 19:53  oetiker
+
+       * src/rrd_fetch.c: for now, do not release fetched data ... in the
+         future we may call rrd_dontneed and let the users decied with an
+         option if they want to keep the data in cache ...
+
+2007-07-21 19:52  oetiker
+
+       * src/Makefile.am: the fonts are now taken from the system. we do
+         not distribute them anympore
+
+2007-07-21 19:37  oetiker
+
+       * NEWS: not on update rrd version
+
+2007-07-20 22:20  oetiker
+
+       * src/rrd_format.h: we have a new holtwinders aggregation fucntion,
+         so we have a new version ...
+
+2007-07-20 22:19  oetiker
+
+       * configure.ac: check for fadvise all the time
+
+2007-07-20 22:16  oetiker
+
+       * doc/rrdcreate.pod: better docs on the aggregate functions
+
+2007-07-18 22:02  oetiker
+
+       * NEWS: added note on locale
+
+2007-07-18 21:59  oetiker
+
+       * src/rrd_graph_helper.c: some more piechart removed
+
+2007-07-18 21:47  oetiker
+
+       * src/rrd_create.c, src/rrd_graph.c, src/rrd_rpncalc.c,
+         src/rrd_tune.c, src/rrd_update.c: make sure all ascii to float
+         parsing uses LC_NUMERIC = C so that we do not stuble on locales
+         confusing the , with a .
+
+2007-07-18 00:30  oetiker
+
+       * src/rrd_create.c, src/rrd_open.c, src/rrd_tool.h,
+         src/rrd_update.c: * rrd_open: rrd_close does not purge file from
+         cache * rrd_open: new function rrd_dontneed for purging un-needed
+         pages from core * rrd_open: in linux at least only fadivse
+         DONTNEED has the power to purge pages from cache, so letst call
+         madvise as well as fadvise * rrd_create: uses open/write/close
+         now, flushes file to disk and keeps only hot pages in core *
+         rrd_update: keeps only hot pages in core * configure enables
+         FADVISE even when mmap is in use
+
+2007-07-17 21:46  oetiker
+
+       * src/rrd_open.c, src/rrd_resize.c: added comment on float cookie
+
+2007-07-16 06:37  oetiker
+
+       * bindings/ruby/main.c: fix for fetch in ruby bindings and support
+         for info added.
+
+2007-07-14 13:26  oetiker
+
+       * trunk/status/2007-07-14.txt: added report
+
+2007-07-12 20:33  oetiker
+
+       * doc/rrdtutorial.pod: improve wording
+
+2007-07-12 20:33  oetiker
+
+       * configure.ac: fix tcl build settup
+
+2007-07-12 20:33  oetiker
+
+       * trunk/tutorial/emanics/rrd-exercises.tex,
+         trunk/tutorial/emanics/rrdtutorial.odp,
+         trunk/tutorial/linuxforum2007/rrdtutorial.odp: monor updates
+
+2007-07-11 23:08  oetiker
+
+       * src/rrd_create.c: usr rrd_free for freeing the rrd structs ...
+         vito caputo
+
+2007-07-10 05:31  oetiker
+
+       * trunk/tutorial/emanics/rrd-exercises.tex: fixed linebreak
+
+2007-07-09 20:18  oetiker
+
+       * trunk/tutorial/emanics/rrd-exercises.tex: remove the 3.5h comment
+
+2007-07-06 14:05  oetiker
+
+       * trunk/tutorial/emanics, trunk/tutorial/emanics/rrd-exercises.tex,
+         trunk/tutorial/emanics/rrdtutorial.odp: initial emanics versions
+
+2007-07-05 15:19  oetiker
+
+       * src/rrd_graph_helper.c: prevent potential segfaults on boxes where
+         time_t is not long
+
+2007-06-24 11:42  oetiker
+
+       * trunk/tutorial/lisa2007/abstract-rrdtut.txt: add halfday
+
+2007-06-24 11:41  oetiker
+
+       * trunk/tutorial/lisa2007/abstract-rrdtut.txt: fritz fix
+
+2007-06-19 06:26  oetiker
+
+       * trunk/tutorial/lisa2007: updated for lisa
+
+2007-06-18 18:25  oetiker
+
+       * src/rrd_cgi.c, src/rrd_graph.c, src/rrd_tune.c, src/rrd_xport.c:
+         fix indentation
+
+2007-06-18 16:05  oetiker
+
+       * configure.ac, src/parsetime.c, src/rrd_cgi.c, src/rrd_create.c,
+         src/rrd_fetch.c, src/rrd_first.c, src/rrd_getopt.c,
+         src/rrd_getopt1.c, src/rrd_graph.c, src/rrd_open.c,
+         src/rrd_restore.c, src/rrd_thread_safe.c, src/rrd_tune.c,
+         src/rrd_update.c, src/rrd_xport.c: Bernhard Fischer: - move
+         several static struct option out of loops and makes them
+         non-static - moves some functions from old-style definitions into
+         new-style definitions
+
+2007-06-16 23:20  oetiker
+
+       * CONTRIBUTORS: Bernhard is not Benrard
+
+2007-06-16 05:32  oetiker
+
+       * trunk/contrib/php4/config.m4, trunk/contrib/php4/configure,
+         trunk/contrib/php4/ltconfig, trunk/contrib/php4/php_rrdtool.h,
+         trunk/contrib/php4/rrdtool.c: lets have just a single rrdtool
+         extension annd not two ... -- Jarod Wilson <jwilson@redhat.com>
+
+2007-06-15 21:49  oetiker
+
+       * NEWS: notes on evans contribs
+
+2007-06-15 21:41  oetiker
+
+       * rrdtool.spec: rrdtool.spec from Jarod "redhat" Wilson
+
+2007-06-15 08:09  oetiker
+
+       * CONTRIBUTORS: updated contributors
+
+2007-06-15 08:01  oetiker
+
+       * NEWS: added note on TEXTALIGN
+
+2007-06-15 07:59  oetiker
+
+       * doc/rrdcreate.pod, doc/rrdtune.pod, src/Makefile.am,
+         src/rrd_create.c, src/rrd_dump.c, src/rrd_format.c,
+         src/rrd_format.h, src/rrd_graph.c, src/rrd_hw.c,
+         src/rrd_hw_math.c, src/rrd_hw_math.h, src/rrd_hw_update.c,
+         src/rrd_hw_update.h, src/rrd_info.c, src/rrd_restore.c,
+         src/rrd_tune.c, src/rrd_update.c: There are two popular variants
+         of the Holt-Winters forecasting method; RRDtool supports the
+         "additive" method, which means that seasonal variation is simply
+         added to the baseline. For our application, it would be more
+         appropriate to use the "multiplicative" Holt-Winters method, where
+         seasonal variation is a coefficient multiplied by the baseline.
+         Quick example to illustrate the difference: if the average doubles
+         season-over-season, the additive method would predict the delta
+         between min and max to be constant, whereas the multiplicative
+         method would predict the delta to double as well. Attached is a
+         patch against trunk to support the multiplicative method. I've
+         done this with a new consolidation function, MHWPREDICT, which is
+         essentially interchangeable with HWPREDICT. There is a noticeable
+         improvement in prediction deviations for certain types of
+         functions; the attachments show HWPREDICT and MHWPREDICT
+         predictions for a function with an x*sin(x) component. Because
+         HWPREDICT and MHWPREDICT differ only in their equations, I've
+         factored out their math into rrd_hw_math.c. The appropriate
+         smoothing functions are passed to the update functions in a
+         container of function pointers, which are called where
+         appropriate. Thus the additive and multiplicative methods use the
+         same update functions, and the right equations are evaluated
+         without having flag checks everywhere. This approach, I think,
+         makes the algorithms quite clear, with minimal duplicate code. I
+         have moved update_hwpredict, update_seasonal, update_devpredict,
+         update_devseasonal, and update_failures into a separate file,
+         rrd_hw_update.c, with some slight refactoring related to
+         rrd_hw_math.c. I ran some regression tests against trunk to make
+         sure I didn't break anything with the existing HWPREDICT code.
+         MHWPREDICT uses the same deviation smoothing and failure detection
+         algorithms as HWPREDICT. Some helpful references on the
+         multiplicative Holt-Winters method:
+         http://www.it.iitb.ac.in/~praj/acads/seminar/04329008_ExponentialSmoothing.pdf
+         (a student's quick overview of additive vs. multiplicative HW)
+         http://ideas.repec.org/p/msh/ebswps/1999-1.html (paper on
+         variations to the multiplicative Holt-Winters, including variance
+         calculations; FYI, my implementation uses "Model 1") My employer
+         and the owner of this patch (IMVU, Inc.) is happy to license it
+         under the same terms as RRDtool, i.e. give it to the project. --
+         Evan Miller emiller imvu.com
+
+2007-06-15 06:43  oetiker
+
+       * src/rrd.h: oops missed to update the in memory output interface
+         here
+
+2007-06-15 06:28  oetiker
+
+       * src/rrd.h, src/rrd_graph.c, src/rrd_graph.h: added interface to
+         get rendered image via a pointer to allocated memory ... Evan
+         Miller emmiller gmail.com
+
+2007-06-15 06:12  oetiker
+
+       * configure.ac: improve fontnaming
+
+2007-06-14 20:30  oetiker
+
+       * bindings/ruby/main.c, src/rrd_graph.c, src/rrd_graph_helper.c,
+         src/rrd_rpncalc.c: fixed indentation
+
+2007-06-14 20:28  oetiker
+
+       * doc/rrdgraph_graph.pod, src/rrd_graph.c, src/rrd_graph.h,
+         src/rrd_graph_helper.c: added TEXTALIGN command to change the
+         default text alignment
+
+2007-06-13 16:53  oetiker
+
+       * bindings/tcl/Makefile.am, bindings/tcl/tclrrd.c: fixed tcl
+         includes
+
+2007-06-13 16:51  oetiker
+
+       * bindings/ruby/main.c: be more helpful when raising rb_eTypeError
+         in string_arr string_arr_new(VALUE rb_strings). This patch
+         indicates which index is in error as well as the type you are
+         erroneously passing. -- anonymous
+
+2007-06-12 20:36  oetiker
+
+       * src/rrd_update.c: Bernhard Fischer: - fix nulling the updvals -
+         cosmetic change to move the option struct out of the loop. - need
+         to operate on a copy of optarg
+
+2007-06-11 19:45  oetiker
+
+       * doc/rrdgraph_rpn.pod, src/rrd_rpncalc.c, src/rrd_rpncalc.h: added
+         TRENDNAN function -- Timo Stripf
+
+2007-06-11 19:45  oetiker
+
+       * src/rrd_graph.c: allow output to stdout -- Timo Stripf
+
+2007-06-11 16:49  oetiker
+
+       * NEWS: added more news
+
+2007-06-11 16:34  oetiker
+
+       * doc/rrdgraph.pod, src/rrd_graph.c: fixed indentation, added
+         documentation note on gridfitting.
+
+2007-06-11 16:33  oetiker
+
+       * src/rrd_graph.c: fix arrow locations
+
+2007-06-11 16:20  oetiker
+
+       * src/rrd_graph.c: disable gridfitting for vector formats
+
+2007-06-11 16:14  oetiker
+
+       * src/rrd_graph.c, src/rrd_graph.h: make pdf output work
+
+2007-06-11 15:32  oetiker
+
+       * NEWS: added detail
+
+2007-06-11 15:11  oetiker
+
+       * NEWS, configure.ac, doc/rrdgraph.pod, src/Makefile.am,
+         src/rrd_gfx.c, src/rrd_gfx.h, src/rrd_graph.c, src/rrd_graph.h,
+         src/rrd_graph_helper.c: --font-render-mode is back, and there is
+         also --graph-render-mode now. It allows to enable mono graphs that
+         look strikingly like rrdtool 1.0.x --tobi
+
+2007-06-11 13:36  oetiker
+
+       * NEWS, src/rrd_update.c: Bernhard Fischer: - NEWS mention the new
+         file accessors - rrd_update.c: remove unused some cruft
+
+2007-06-11 10:09  oetiker
+
+       * src/rrd_update.c: Bernhard Fischer: - fix maintaining our file-pos
+         when we have to wrap the rra_ptr in update(). - no need to update
+         the header-data a second time for the mmap case.
+
+2007-06-11 00:23  oetiker
+
+       * NEWS: updated news
+
+2007-06-11 00:22  oetiker
+
+       * configure.ac, doc/rrdgraph.pod, src/Makefile.am: * added some
+         documentation * updated version number
+
+2007-06-10 23:55  oetiker
+
+       * acinclude.m4, configure.ac, src/Makefile.am, src/art_rgba_svp.c,
+         src/art_rgba_svp.h, src/rrd_afm.c, src/rrd_afm.h,
+         src/rrd_afm_data.c, src/rrd_afm_data.h, src/rrd_gfx.c,
+         src/rrd_gfx.h, src/rrd_graph.c, src/rrd_graph.h,
+         src/rrd_graph_helper.c: switched graphics library from arts to
+         cairo+pango
+
+2007-06-04 11:15  oetiker
+
+       * configure.ac, src/Makefile.am: Bernhard Fischer: - new configure
+         option --enable-static-programs
+
+2007-06-01 19:11  oetiker
+
+       * src/rrd_open.c, src/rrd_resize.c: Bernhard: - implement resize
+         SHRINK for FD (mmap is to be done) - indent
+
+2007-06-01 17:59  oetiker
+
+       * src/rrd_graph.c: --full-size-mode with and without --no-legend
+         should work now -- Matthew Chambers
+
+2007-06-01 17:35  oetiker
+
+       * src/rrd_open.c, src/rrd_resize.c: Bernhard - fill the file with
+         DNAN and add the remaining data in after the fact ...
+
+2007-06-01 17:34  oetiker
+
+       * src/rrd_open.c, src/rrd_resize.c: Bernhard Fischer - implement
+         resize, growing part. truncation is to be done.
+
+2007-06-01 09:10  oetiker
+
+       * src/rrd_cgi.c, src/rrd_graph_helper.c, src/rrd_open.c: Bernhard
+         Fischer - use 'z' length modifier when printing size_t - add a
+         cast to __rrd_read and document why it is there - add RANDOM hint
+         for the header
+
+2007-05-31 18:47  oetiker
+
+       * src/rrd_open.c: Bernhard: - plug tiny mem-leak in error path of
+         rrd_close where freeing the rrd_file was forgotten. - improve two
+         error messages - use CHECK_MADVISE_OVERLAPS per default
+
+2007-05-30 19:56  oetiker
+
+       * src/rrd_open.c, src/rrd_update.c: fix rrd_write -- Bernhard
+         Fischer
+
+2007-05-30 12:41  oetiker
+
+       * src/rrd_fetch.c, src/rrd_first.c, src/rrd_graph.c,
+         src/rrd_graph.h, src/rrd_info.c, src/rrd_lastupdate.c,
+         src/rrd_open.c, src/rrd_resize.c: fix indenting ... again
+
+2007-05-30 12:39  oetiker
+
+       * src/rrd_create.c, src/rrd_dump.c, src/rrd_fetch.c,
+         src/rrd_first.c, src/rrd_info.c, src/rrd_last.c,
+         src/rrd_lastupdate.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_tune.c, src/rrd_update.c: From Bernhard Fischer -
+         rrd_close(): call close on the file and use rrd_close consistently
+         - clean up some error paths. The fadvise error path is leaking
+         memory (see XXX in these spots).
+
+2007-05-30 09:28  oetiker
+
+       * trunk/contrib/php4/examples/rrd_fetch.php,
+         trunk/contrib/php4/php_rrdtool.h, trunk/contrib/php4/rrdtool.c:
+         make this work on recent php4 versions -- Bernhard Fischer
+
+2007-05-30 05:23  oetiker
+
+       * CONTRIBUTORS, doc/rrdgraph.pod, src/rrd_graph.c, src/rrd_graph.h:
+         switch for rrd_graph to specify the outer-size of the graph and
+         not just the size of the canvas: --full-size-mode --
+         matthew.chambers vanderbilt.edu
+
+2007-05-29 21:29  oetiker
+
+       * configure.ac, examples/perftest.pl.in, src/rrd_hw.c,
+         src/rrd_last.c, src/rrd_lastupdate.c, src/rrd_open.c,
+         src/rrd_resize.c, src/rrd_tool.c, src/rrd_tune.c,
+         src/rrd_update.c: More updates from Bernhard Fischer - flag
+         rrd_resize's old file with RRD_COPY - cleanup error-handling
+         pathes in rrd_update and fix a few typos in comments -
+         rrd_close(): implement printing mincore results for the rrd if
+         DEBUG=2 was defined - rrd_open(): madvise start addresses need to
+         be page-aligned; implement alternative path to the fine-grained
+         (i.e. exact) madvise by flagging just the first two pages as
+         needed (see TWO_PAGES). Implement alternative path that records
+         the last madvise()ed area to avoid redundant calls to madvise() on
+         identical areas (due to page-alignment constraints) -- see
+         CHECK_MADVISE_OVERLAPS. Implement path for USE_DIRECT_IO. -
+         configure: add check for O_DIRECT flag to open(2). Add option
+         --enable-direct-io. Add _GNU_SOURCE to CFLAGS to silence warnings
+         about chroot which is marked LEGACY since SUSv2 and is a non POSIX
+         extension. Make checks for posix_fadvise() dependant on
+         --disable-mmap, since we do not need fadvise for the mmap case.
+
+2007-05-25 15:24  oetiker
+
+       * src/rrd_stat.c: tobis old cruft removal program ...
+
+2007-05-25 13:14  oetiker
+
+       * src/rrd_dump.c, src/rrd_fetch.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_update.c: updates from Bernhard Fischer rep dot nop gmail
+         com - add some more rrd_sterror calls to failure paths. - make
+         rrdtool updatev work like 1.2.23 (i.e. multiple values are still
+         broken, but at least updating a single value works; see
+         http://oss.oetiker.ch/rrdtool-trac/ticket/8) - indent touched
+         files.
+
+2007-05-25 13:12  oetiker
+
+       * .indent.pro, Makefile.am, configure.ac, examples/perftest.pl.in,
+         src/rrd_dump.c, src/rrd_fetch.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_tool.h, src/rrd_update.c: Updates from Bernhard Fischer
+         rep dot nop gmail com - fix typo in rrd_fetch where rrd_read
+         result was checked against an incorrect size. - in rrd_fetch, drop
+         rrd_head_size in favour of rrd_file->header_len - in rrd_fetch,
+         make the message "post fetch" unambiguous (now past vs. post). -
+         change usage of param rdwr of rrd_open: allow for RRD_READONLY,
+         RRD_READWRITE, RRD_CREAT, RRD_READAHEAD; adjust callers
+         accordingly: + rrd_resize needs CREAT + rrd_dump may want
+         READAHEAD - implement FD based I/O in rrd_open, rrd_read,
+         rrd_write, rrd_seek. - in rrd_update, unify write_RRA_row(). -
+         sort | uniq the -T in .indent.pro (info_t was duplicated) - add
+         stub of an option to use O_DIRECT to the configury - in
+         Makefile.am, simplify the "indent" invocation of find: My find may
+         not support "-o" resp. "-or" nor braces. Using -name "*.[ch]"
+         works everywhere, AFAIK.
+
+2007-05-24 08:26  oetiker
+
+       * configure.ac: use proper preprocessor directives for the
+         config-checks. -- tobi
+
+2007-05-24 07:21  oetiker
+
+       * configure.ac: fixed version number for development
+
+2007-05-24 06:16  oetiker
+
+       * .indent.pro, Makefile.am, bindings/python/rrd_extra.h,
+         bindings/python/rrdtoolmodule.c, bindings/ruby/main.c,
+         bindings/tcl/tclrrd.c, libraries/afm/test-afm.c, src/.indent.pro,
+         src/hash_32.c, src/parsetime.c, src/rrd.h, src/rrd_afm.c,
+         src/rrd_create.c, src/rrd_dump.c, src/rrd_fetch.c, src/rrd_gfx.c,
+         src/rrd_gfx.h, src/rrd_graph.c, src/rrd_graph_helper.c,
+         src/rrd_hw.c, src/rrd_hw.h, src/rrd_info.c, src/rrd_open.c,
+         src/rrd_rpncalc.c, src/rrd_rpncalc.h, src/rrd_tool.h,
+         src/rrd_update.c, src/rrd_xport.c, win32/config.h: indent all the
+         rest of the code, and add some typedefs to indent.pro there is now
+         a indent makefile target
+
+2007-05-23 21:40  oetiker
+
+       * src/gdpng.c: removed superfluouse code
+
+2007-05-23 21:39  oetiker
+
+       * src/.indent.pro: added more indenting rules
+
+2007-05-23 21:33  oetiker
+
+       * src/.indent.pro, src/art_rgba_svp.c, src/art_rgba_svp.h,
+         src/fnv.h, src/gdpng.c, src/hash_32.c, src/parsetime.c,
+         src/pngsize.c, src/rrd.h, src/rrd_afm.c, src/rrd_afm.h,
+         src/rrd_afm_data.c, src/rrd_afm_data.h, src/rrd_cgi.c,
+         src/rrd_create.c, src/rrd_diff.c, src/rrd_dump.c, src/rrd_error.c,
+         src/rrd_fetch.c, src/rrd_first.c, src/rrd_format.c,
+         src/rrd_format.h, src/rrd_getopt.c, src/rrd_getopt.h,
+         src/rrd_getopt1.c, src/rrd_gfx.c, src/rrd_gfx.h, src/rrd_graph.c,
+         src/rrd_graph.h, src/rrd_graph_helper.c, src/rrd_hw.c,
+         src/rrd_hw.h, src/rrd_info.c, src/rrd_is_thread_safe.h,
+         src/rrd_last.c, src/rrd_lastupdate.c, src/rrd_nan_inf.c,
+         src/rrd_nan_inf.h, src/rrd_not_thread_safe.c, src/rrd_open.c,
+         src/rrd_resize.c, src/rrd_restore.c, src/rrd_rpncalc.c,
+         src/rrd_rpncalc.h, src/rrd_stat.c, src/rrd_thread_safe.c,
+         src/rrd_thread_safe_nt.c, src/rrd_tool.c, src/rrd_tool.h,
+         src/rrd_tune.c, src/rrd_update.c, src/rrd_version.c,
+         src/rrd_xport.c, src/rrd_xport.h, src/rrdupdate.c, src/strftime.c,
+         src/strftime.h, src/win32comp.c: reindented everything according
+         to .indent.pro
+
+2007-05-23 20:57  oetiker
+
+       * .indent.pro, src/.indent.pro: profile must be in the source
+         directory to work
+
+2007-05-23 20:57  oetiker
+
+       * .indent.pro: tabs be gone!
+
+2007-05-23 20:42  oetiker
+
+       * configure.ac, src/Makefile.am, src/rrd.h, src/rrd_dump.c,
+         src/rrd_fetch.c, src/rrd_first.c, src/rrd_hw.c, src/rrd_hw.h,
+         src/rrd_info.c, src/rrd_last.c, src/rrd_lastupdate.c,
+         src/rrd_open.c, src/rrd_resize.c, src/rrd_restore.c,
+         src/rrd_stat.c, src/rrd_thread_safe.c, src/rrd_tool.h,
+         src/rrd_tune.c, src/rrd_update.c, src/unused.h: * progress in
+         moving all the fileaccess over to a wrapper system that can do fd
+         based and mmap based fileaccess transparently ... * small fixes
+         left and right to improve code quality and stability. -- Bernhard
+         Fischer rep dot nop gmail com
+
+2007-05-23 16:10  oetiker
+
+       * .indent.pro: we are going to use indent from now on to keep code
+         indenting consistent throughout the project
+
+2007-05-23 16:08  oetiker
+
+       * .: new trunk based on current 1.2
+
+2007-05-23 16:07  oetiker
+
+       * .: restarting trynk for 1.2
+
+2007-05-22 11:03  oetiker
+
+       * trunk/talks/rrdtoolfast/onlineusv-de.ppt: initial
+
+2007-05-22 09:27  oetiker
+
+       * trunk/talks/rrdtoolfast/fasttalk.ppt,
+         trunk/talks/rrdtoolfast/samfs-de.ppt: renamed
+
+2007-04-10 13:49  oetiker
+
+       * trunk/status/2007-04-10.txt: added sponsors
+
+2007-04-10 13:29  oetiker
+
+       * trunk/status/2007-04-10.txt: second reading ...
+
+2007-04-10 13:14  oetiker
+
+       * trunk/status, trunk/status/2007-04-10.txt: status report initial
+         version
+
+2007-03-19 22:01  oetiker
+
+       * trunk/articles/rrdtool-infoweek-2007-03.doc: infoweek artikel
+         initial
+
+2007-03-19 14:24  oetiker
+
+       * trunk/articles, trunk/articles/rrdtool-infoweek-2007-03.doc:
+         initial
+
+2007-03-02 06:38  oetiker
+
+       * trunk/talks/rrdtoolfast/monitoring-en.ppt: added english version
+
+2007-02-20 06:53  oetiker
+
+       * trunk/tutorial/linuxforum2007/rrdtutorial.odp: ready for
+         presentation at linuxforum
+
+2007-01-31 08:29  oetiker
+
+       * trunk/talks/rrdtoolfast/fasttalk.ppt: updated for samfs
+
+2007-01-30 08:36  oetiker
+
+       * trunk/talks/rrdtoolfast/fasttalk.ppt: added fasttalk for samfs
+
+2007-01-30 08:34  oetiker
+
+       * trunk/talks, trunk/talks/rrdtoolfast: initial
+
+2007-01-22 17:20  oetiker
+
+       * trunk/tutorial, trunk/tutorial/linuxforum2007,
+         trunk/tutorial/linuxforum2007/abstract-rrdtut.txt,
+         trunk/tutorial/linuxforum2007/rrdtutorial.odp,
+         trunk/tutorial/linuxforum2007/tobi-2006.jpg: initial
+
+2006-02-13 14:40  oetiker
+
+       * NEWS: removed extra line from top
+
+2005-04-04 22:15  oetiker
+
+       * trunk/meta/plan-1.2.txt: plan for 1.2 is now in the 1.2 branche
+
+2005-04-03 12:44  oetiker
+
+       * trunk/meta/plan-1.2.txt: verified operation on debian
+
+2005-04-03 12:44  oetiker
+
+       * README, configure.ac: update build instructions and configure
+         output.
+
+2005-04-03 11:44  oetiker
+
+       * doc/rrdgraph_graph.src, src/rrd_graph_helper.c: paramters in SHIFT
+         are separated by : like everywhere else
+
+2005-04-03 11:36  oetiker
+
+       * doc/rrdgraph_graph.src, src/rrd_graph.c, src/rrd_graph.h,
+         src/rrd_graph_helper.c: the piechart code does not produce release
+         ready results ... hide it behind WITH_PIECHART define
+
+2005-04-03 11:11  oetiker
+
+       * src/rrd_gfx.c: improve png writing speed by lowering the
+         compression
+
+2005-04-03 11:11  oetiker
+
+       * src/rrd_graph.c: make sure the font path is shorter than the
+         buffer
+
+2005-04-03 10:10  oetiker
+
+       * CONTRIBUTORS, src/rrd_graph.c, src/rrd_graph.h: fix graph layout
+         ... especially consider text propperties propperly
+
+2005-04-03 09:37  oetiker
+
+       * src/rrd_graph_helper.c: the legend string of a PRINT command gets
+         renderd later by runnning it through printf.
+
+2005-04-03 09:35  oetiker
+
+       * configure.ac, src/rrd_update.c: MMAP support for rrdtool this is
+         suposed to speed-up uptime 4 times. -- Radoslaw Karas
+         <rkaras@tyndall.ie>
+
+2005-03-20 21:17  oetiker
+
+       * bindings/Makefile.am: revert to last working version
+
+2005-03-20 21:11  oetiker
+
+       * bindings/Makefile.am: fix quoting
+
+2005-03-20 21:00  oetiker
+
+       * bindings/Makefile.am: quote sed expression
+
+2005-03-20 20:59  oetiker
+
+       * doc/rrdgraph.src, doc/rrdgraph_graph.src: document tabbing support
+         by adding in the textformatting notes document --tabwidth argument
+         document AXIS color
+
+2005-03-20 20:58  oetiker
+
+       * src/rrd_graph.c, src/rrd_graph.h, src/rrd_graph_helper.c: * allow
+         configuration of the default tab width * added new configurable
+         color AXIS * made tabbing support work with legend elements * use
+         propper font for the legend: LEGEND * fix apperance by makeing
+         grid more transparent * draw axis over the graph not under it.
+
+2005-03-20 20:56  oetiker
+
+       * src/rrd_gfx.c, src/rrd_gfx.h: added tabbig support
+
+2005-03-20 20:55  oetiker
+
+       * configure.ac: include the thread defs after they have been found
+         not before
+
+2005-03-20 20:54  oetiker
+
+       * trunk/meta/plan-1.2.txt: sync with reality
+
+2005-03-13 16:34  oetiker
+
+       * bindings/perl-shared/RRDs.xs: propperly integrate rrd_first
+
+2005-03-13 16:13  oetiker
+
+       * MakeMakefile: autotools still run after configure this is not good
+         ... run autoreconf at the end of MakeMakefile. Maybe this will
+         help
+
+2005-03-13 16:09  oetiker
+
+       * configure.ac: set install patch to version
+
+2005-03-13 16:08  oetiker
+
+       * MakeMakefile: re-run autotools to make sure dependent dates are
+         correct
+
+2005-03-13 16:00  oetiker
+
+       * MakeMakefile: reintroduce final header regen
+
+2005-03-13 15:57  oetiker
+
+       * MakeMakefile: still trying to get the order of calling autotools
+         right
+
+2005-03-13 15:54  oetiker
+
+       * MakeMakefile: create aclocal.m4 before libtoolize
+
+2005-03-13 15:51  oetiker
+
+       * MakeMakefile: make the headers first
+
+2005-03-13 15:16  oetiker
+
+       * doc/rrdfirst.pod: add the documentation for first
+
+2005-03-13 15:14  oetiker
+
+       * src/Makefile.am: ntconfig is no more
+
+2005-03-13 15:12  oetiker
+
+       * src/rrd_first.c: missed the actual code for first
+
+2005-03-12 17:06  oetiker
+
+       * bindings/perl-shared/RRDs.xs, doc/Makefile.am, src/Makefile.am,
+         src/rrd.h, src/rrd_tool.c: rrd_first code contributed by Burton
+         Strauss <Burton@ntopSupport.com>
+
+2005-03-10 17:04  oetiker
+
+       * trunk/meta/plan-1.2.txt: updated plan ...
+
+2005-03-10 17:03  oetiker
+
+       * README, acinclude.m4, bindings/Makefile.am, configure.ac,
+         examples/4charts.pl.in, examples/bigtops.pl.in,
+         examples/cgi-demo.cgi.in, examples/minmax.pl.in,
+         examples/piped-demo.pl.in, examples/shared-demo.pl.in,
+         examples/stripes.pl.in, src/rrd.h, src/rrd_last.c, src/rrd_tool.h:
+         * Updated perl compile system. It now uses Makefile.PL for
+         everything, but gets it to install the perl module in
+         @prefix@/lib/perl such that you can use the same use lib line for
+         whatever platform and perlversion you install into this directory.
+         They all live neatly next to each other. * fixed examples to work
+         with new perl install system. Added a few exec_prefix lines where
+         appropriate * fixed piped demo * fixed VeraMono.ttf file. This
+         somehow got damaged by cvs ... * added thread defines to normal
+         compile run, so that _r functions to not through errors. * tested
+         compilation and installation
+
+2005-03-04 23:53  oetiker
+
+       * acinclude.m4, configure.ac: revamped configure system ... lots
+         more stuff is in acinclude now still a bit of a mess, but better
+         than before ... if configure does not find the dependent libraries
+         and header it will not try to get help from pkg-config. If you
+         know where your libraries are it is best to just use CPPFLAGS and
+         LDFLAGS to specify their location.
+
+2005-03-04 00:12  oetiker
+
+       * MakeMakefile, acinclude.m4, bindings/Makefile.am, configure.ac,
+         src/Makefile.am: Big autotool update: * Upgraded to the latest
+         version of autoconf, automake and libtool * replaced pthread
+         detection code in configure with a more effective one * added a
+         test to see if special solaris defines are required for ctime_r to
+         behave posixly correct. * Fixed lots of warnings from autotools *
+         started adding support for pkg-config ... this will eventually
+         make the whole library finding much simpler
+
+2005-03-04 00:05  oetiker
+
+       * trunk/meta/plan-1.2.txt: added warning cleanup
+
+2005-03-03 13:56  oetiker
+
+       * trunk/meta/plan-1.2.txt: added notes about including stuff
+
+2005-03-03 13:48  oetiker
+
+       * trunk/meta/plan-1.2.txt: spelled
+
+2005-03-03 13:46  oetiker
+
+       * trunk/meta/plan-1.2.txt: initial
+
+2005-03-03 13:34  oetiker
+
+       * trunk/contrib/php4/.cvsignore, trunk/meta,
+         trunk/meta/plan-1.2.txt, ., .cvsignore, bindings/.cvsignore,
+         bindings/perl-piped/.cvsignore, bindings/perl-shared/.cvsignore,
+         bindings/tcl/.cvsignore, doc/.cvsignore, examples/.cvsignore,
+         libraries/.cvsignore, src/.cvsignore: ignorelists are a propperty
+         of an svn directory not a file
+
+2005-03-03 13:21  oetiker
+
+       * branches/gnu, branches/gpl, trunk/CVSROOT, trunk/rrdtool: post
+         cvs2svn conversion cleanup
+
+2005-02-27 22:00  oetiker
+
+       * src/rrd_cgi.c: do not complain when setting environemnt variable
+         sucessfully. -- hunter@mimuw.edu.pl
+
+2005-02-13 16:13  oetiker
+
+       * CONTRIBUTORS, bindings/perl-shared/RRDs.xs, bindings/tcl/tclrrd.c,
+         src/rrd.h, src/rrd_cgi.c, src/rrd_graph.c, src/rrd_graph.h,
+         src/rrd_tool.c: let rrd_graph return the actual value range it
+         picked ... -- Henrik Stoerner <henrik@hswn.dk>
+
+2005-01-28 14:02  oetiker
+
+       * src/rrd_hw.c: fix for memory leak. Rene Gallati <security ..
+         draxinusom.ch>
+
+2005-01-03 22:25  oetiker
+
+       * TODO: update todo
+
+2004-10-25 22:22  oetiker
+
+       * doc/rrdgraph.src, src/rrd_graph.c, src/rrd_graph.h: *
+         fore-rules-legend option -- author name missing
+
+2004-10-25 22:21  oetiker
+
+       * src/rrd_tool.c: fixed segmentation falt problem in rrd_tool --
+         Yasuhiro Sumi <yasuhiro.sumi@hde.co.jp>
+
+2004-10-21 19:21  oetiker
+
+       * src/rrd_tool.c: fix return codes in the error case -- Haroon
+         Rafique <haroon.rafique@utoronto.ca>
+
+2004-09-24 21:11  oetiker
+
+       * CONTRIBUTORS, doc/rrdgraph_data.src, doc/rrdgraph_rpn.src,
+         src/rrd_graph.c, src/rrd_graph.h, src/rrd_graph_helper.c,
+         src/rrd_resize.c, src/rrd_rpncalc.c, src/rrd_rpncalc.h,
+         src/rrd_xport.c: misc fixed and TREND and reduce functionality by
+         -- David M. Grimes <dgrimes@navisite.com>
+
+2004-08-24 05:26  oetiker
+
+       * CONTRIBUTORS, doc/rrdgraph_rpn.src, src/rrd_graph.c,
+         src/rrd_graph.h, src/rrd_graph_helper.c, src/rrd_rpncalc.c,
+         src/rrd_rpncalc.h, src/rrd_xport.c: CDEF operators SHIFT, SQRT,
+         SORT, and REV (reverse). See documentation for what they do. This
+         included removal of redundant code in the rrd_xport path, replaced
+         with a call to rrd_graph_script(). -- David M. Grimes
+         <dgrimes@navisite.com>
+
+2004-08-09 08:38  oetiker
+
+       * doc/rrdcgi.pod: added note on escaping :
+
+2004-08-08 21:54  oetiker
+
+       * src/rrd_cgi.c: port forward of fixes from the stable version of
+         rrd_cgi
+
+2004-08-07 23:46  oetiker
+
+       * src/rrd_graph.c: when moving the data pointers we should take into
+         account how fahr off they are. Found by David M. Grimes
+         <dgrimes@navisite.com>
+
+2004-08-05 21:24  oetiker
+
+       * bindings/perl-piped/MANIFEST, bindings/perl-shared/MANIFEST:
+         removed nonexisting files from manifest
+
+2004-07-30 00:30  jake
+
+       * NT-BUILD-TIPS.txt, bindings/perl-shared/RRDs.ppd,
+         bindings/perl-shared/ntmake.pl, bindings/perl-shared/rrdpl.dsp,
+         bindings/perl-shared/rrdpl.dsw, confignt/config.h, src/rd_cgi.dsp,
+         src/rrd.dsp, src/rrd.dsw, src/rrd.vcproj, src/rrd_afm.c,
+         src/rrd_cgi.dsp, src/rrd_cgi.vcproj, src/rrd_restore.c,
+         src/rrdtool.dsp, src/rrdtool.dsw, src/rrdtool.sln,
+         src/rrdtool.suo, src/rrdtool.vcproj: Fix the Win32 build for
+         executable and perl-shared library. See NT-BUILD-TIPS.txt for step
+         by step instructions. Remove VC++ project files not being
+         maintained by anyone.
+
+2004-07-14 10:55  oetiker
+
+       * CONTRIBUTORS: added Peter Speck
+
+2004-06-26 09:03  oetiker
+
+       * rrdtool.spec: allow building rpm from cvs where there is not
+         ChangeLog (this gets generated when the distribution tar is built)
+         -- Tobi and Mike Slifcak
+
+2004-06-23 20:36  oetiker
+
+       * src/rrd_graph.c: It's quie enough that the title is placed on the
+         top of the graph, there's no need to emphasize it more. --
+         Stanislav Sinyagin <ssinyagin@yahoo.com>
+
+2004-06-23 08:39  oetiker
+
+       * src/Makefile.am: reverted ... $< does not work
+
+2004-06-22 22:09  oetiker
+
+       * src/Makefile.am: The "$^" variable is GNU make-specific, and fails
+         to compile under FreeBSD 4.10. Please change it to $<, which is
+         more compatible. -- Stanislav Sinyagin <ssinyagin@yahoo.com>
+
+2004-06-17 22:09  oetiker
+
+       * src/rrd_graph.h: fixed type for text_prop_conv prototype
+
+2004-06-07 19:07  oetiker
+
+       * debian/changelog, debian/control, debian/rules: Fix for debian
+         freetype linking -- Peter Hirdina <Peter.Hirdina@gmx.net>
+
+2004-05-26 22:11  oetiker
+
+       * src/fnv.h, src/parsetime.c, src/pngsize.c, src/rrd.h,
+         src/rrd_afm.c, src/rrd_afm_data.c, src/rrd_afm_data.h,
+         src/rrd_cgi.c, src/rrd_gfx.c, src/rrd_graph.c,
+         src/rrd_graph_helper.c, src/rrd_last.c, src/rrd_open.c,
+         src/rrd_resize.c, src/rrd_restore.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_tune.c, src/rrd_update.c, src/rrd_xport.c:
+         reduce compiler warnings. Many small fixes. -- Mike Slifcak
+         <slif@bellsouth.net>
+
+2004-05-26 22:10  oetiker
+
+       * rrdtool.spec: improve redhat/fedora building -- Mike Slifcak
+
+2004-05-26 22:09  oetiker
+
+       * debian/rules: update debian build instructions -- Mike Slifcak
+
+2004-05-25 22:08  oetiker
+
+       * examples/shared-demo.pl.in: The counter was the only variable
+         which needed to be an integer. -- Mike Slifcak
+
+2004-05-25 22:07  oetiker
+
+       * rrdtool.spec: The rrdtool RPM package was depending on the
+         rrdtool-perl package. These changes move the examples, which are
+         primarily perl scripts, in with the rrdtool-perl RPM package,
+         thereby de-coupling the rest of the RRDtool from Perl. Perhaps
+         this is silliness ? If there is no need for rrdtool separate from
+         the Perl modules, let me know, then I'll just create an rrdtool
+         and an rrdtool-devel RPM package (that would be a cleaner build
+         and scripting, anyway). -- Mike Slifcak
+
+2004-05-25 22:03  oetiker
+
+       * doc/Makefile.am, doc/rrdtool-dump.dtd, doc/rrdtool-xport.dtd:
+         added dtd descriptsion s or rrdtool xml output -- Wolfgang Schrimm
+         <wolfgang{dot}schrimm{at}urz{dot}uni-heidelberg{dot}de>
+
+2004-05-25 21:06  oetiker
+
+       * bindings/perl-shared/RRDs.xs: added dump and restore ... Mike
+         Schilli <b2b@perlmeister.com>
+
+2004-05-25 21:01  oetiker
+
+       * CONTRIBUTORS: added some contributors
+
+2004-05-25 21:00  oetiker
+
+       * doc/rrdupdate.pod: fixed time stamp in example -- Mike Slifcak
+
+2004-05-25 20:59  oetiker
+
+       * rrdtool.spec: more fixes for rpm spec -- Mike Slifcak
+
+2004-05-25 20:58  oetiker
+
+       * doc/rrdtutorial.pod: single word fix -- Mike Slifcak
+
+2004-05-25 20:57  oetiker
+
+       * src/rrd_xport.c: Clear out previous legend array contents before
+         freeing legend array. Problem could manifest under extreme
+         resource limits. Not tested. -- Mike Slifcak
+
+2004-05-25 20:55  oetiker
+
+       * doc/rrdcreate.pod: subordinate term was improperly formed -- Mike
+         Slifcak
+
+2004-05-25 20:53  oetiker
+
+       * src/rrd_create.c, src/rrd_dump.c: prevent small leak when
+         resources are exhausted -- Mike Slifcak
+
+2004-05-25 20:52  oetiker
+
+       * THREADS, doc/rrdthreads.pod, doc/rrdtutorial.pod,
+         doc/rrdupdate.pod, src/rrd_cgi.c, src/rrd_resize.c,
+         src/rrd_restore.c, src/rrd_tool.c, src/rrd_update.c: fix spelling
+         and syntax, especially in messages that are printed -- Mike
+         Slifcak
+
+2004-05-25 20:51  oetiker
+
+       * src/rrd_cgi.c, src/rrd_dump.c, src/rrd_restore.c, src/rrd_tool.c,
+         src/rrd_update.c: Update displayed copyright messages to be
+         consistent. -- Mike Slifcak
+
+2004-05-25 20:51  oetiker
+
+       * doc/Makefile.am: don't process the old rrdgraph single file
+         document -- Mike Slifcak
+
+2004-05-25 20:50  oetiker
+
+       * rrdtool.spec: fix rpm rules -- Mike Slifcak
+
+2004-05-25 20:50  oetiker
+
+       * debian/control: fixed debian dependencies -- Mike Slifcak
+
+2004-05-25 16:51  oetiker
+
+       * src/rrd_rpncalc.c: The expression's head was first checking for
+         LT, and then for LTIME, and the latter was never reached. --
+         Stanislav Sinyagin <ssinyagin@yahoo.com>
+
+2004-05-19 05:11  oetiker
+
+       * doc/rrd-beginners.pod: integrated from debian
+
+2004-05-18 18:54  oetiker
+
+       * src/parsetime.c: its 365 days in a year .... -- Mike Slifcak
+
+2004-05-18 18:53  oetiker
+
+       * CONTRIBUTORS, NEWS, README, TODO, bindings/perl-piped/README,
+         bindings/perl-piped/RRDp.pm, bindings/perl-piped/t/base.t,
+         bindings/perl-shared/RRDs.pm, bindings/tcl/README,
+         doc/bin_dec_hex.pod, doc/cdeftutorial.pod, doc/rpntutorial.pod,
+         doc/rrdcgi.pod, doc/rrdcreate.pod, doc/rrddump.pod,
+         doc/rrdfetch.pod, doc/rrdgraph-old.pod, doc/rrdgraph.src,
+         doc/rrdgraph_data.src, doc/rrdgraph_examples.src,
+         doc/rrdgraph_graph.src, doc/rrdgraph_rpn.src, doc/rrdinfo.pod,
+         doc/rrdresize.pod, doc/rrdrestore.pod, doc/rrdthreads.pod,
+         doc/rrdtool.pod, doc/rrdtune.pod, doc/rrdtutorial.es.pod,
+         doc/rrdtutorial.pod, doc/rrdupdate.pod, src/parsetime.c,
+         src/rrd_fetch.c, src/rrd_format.c, src/rrd_gfx.c, src/rrd_graph.c:
+         big spell checking patch -- slif@bellsouth.net
+
+2004-05-18 18:51  oetiker
+
+       * src/rrd_restore.c: make force option work
+
+2004-05-18 18:51  oetiker
+
+       * doc/Makefile.am: added beginners guide off debian --
+         slif@bellsouth.net
+
+2004-05-18 18:49  oetiker
+
+       * configure.ac, examples/4charts.pl.in, examples/Makefile.am,
+         examples/shared-demo.pl.in, examples/stripes.pl.in: add 4chars and
+         fix two examples -- Mike Slifcak <slif@bellsouth.net>
+
+2004-05-18 18:40  oetiker
+
+       * debian, debian/README.Debian, debian/build_freetype.sh,
+         debian/changelog, debian/control, debian/copyright,
+         debian/librrd0-dev.files, debian/librrd0.files,
+         debian/librrd0.postinst, debian/librrd0.postrm,
+         debian/librrd0.shlibs, debian/librrdp-perl.files,
+         debian/librrds-perl.files, debian/rrdtool-tcl.files,
+         debian/rrdtool.files, debian/rules, debian/watch: initial debian
+         build system added -- Mike Slifcak <slif@bellsouth.net>
+
+2004-05-18 18:36  oetiker
+
+       * src/Makefile.am: fix library numbering -- Mike Slifcak
+
+2004-05-18 18:25  oetiker
+
+       * doc/Makefile.am: better cleaning -- Mike Slifcak
+
+2004-05-12 21:57  oetiker
+
+       * bindings/Makefile.am, doc/Makefile.am: locate the mkinstalldirs
+         script at top level accept "configure --mandir=" settings -- Mike
+         Slifcak <slif@bellsouth.net>
+
+2004-05-04 21:01  oetiker
+
+       * doc/rrdgraph_rpn.src, src/rrd_rpncalc.c, src/rrd_rpncalc.h: Added
+         the ATAN function. This is being used to convert a DS for each
+         vector component of wind direction into a single direction for
+         graphing.
+         CDEF:avdir=yavg,xavg,/,ATAN,57.296,*,xavg,0,LT,180,0,IF,+,DUP,0,LT,360,0,IF,+
+         -- Daniel Shiels <dan@marge.tofubar.com>
+
+2004-05-04 20:54  oetiker
+
+       * rrdtool.spec: updated spec file -- Chris Adams
+         <cmadams@hiwaay.net>
+
+2004-05-04 05:25  oetiker
+
+       * MakeMakefile: die only after all the version checks are done and
+         not for each one -- Alex van den Bogaerdt <alex@ergens.op.het.net>
+
+2004-05-03 14:05  oetiker
+
+       * Makefile.am, configure.ac, src/Makefile.am: * Allows rrdtool 1.1.x
+         (2004-04-29) to compile on freebsd. * Add configure options
+         --disable-rrdcgi disable building of rrdcgi --disable-pthread
+         disable multithread support -- Stanislav Sinyagin
+         <ssinyagin@yahoo.com>
+
+2004-05-02 21:10  oetiker
+
+       * configure.ac, src/rrd_graph.c: See the patch, attached. It changes
+         the default angle to 90, and allows one to change it from
+         configure command line: ./configure RRDGRAPH_YLEGEND_ANGLE=270.0
+         -- Stanislav Sinyagin <ssinyagin@yahoo.com>
+
+2004-04-18 10:21  oetiker
+
+       * src/rrd_restore.c: fixed O_BINARY
+
+2004-03-23 21:34  oetiker
+
+       * src/rrd_restore.c: make sure fole is opened binary on restore ...
+         to make things work on windows -- "Girod, Laurent"
+         <Laurent.Girod@pmintl.com>
+
+2004-03-21 11:40  oetiker
+
+       * doc/rrdgraph.src, src/rrd_graph.c, src/rrd_graph.h,
+         src/rrd_tool.c: Added --only-graph option -- Piotr Kodzis
+         <Piotr.Kodzis@inteligo.pl>
+
+2004-03-14 20:58  oetiker
+
+       * MakeMakefile, acinclude.m4, bindings/Makefile.am,
+         bindings/perl-shared/Makefile.PL, bindings/perl-shared/RRDs.pm,
+         bindings/perl-shared/t/base.t, configure.ac, doc/Makefile.am: make
+         MakeMaker work on debian and fix some leftovers from the library
+         rip-out transition -- Tobi
+
+2004-01-19 23:41  oetiker
+
+       * src/VeraMono.ttf, src/compile_afm.pl: initial
+
+2004-01-19 23:39  oetiker
+
+       * config: kill
+
+2004-01-19 23:39  oetiker
+
+       * config/Makefile.am: remove
+
+2004-01-19 23:37  oetiker
+
+       * libraries/cgilib-0.4: kill it
+
+2004-01-19 23:35  oetiker
+
+       * libraries/Makefile.am, libraries/cgilib-0.4/Makefile.am,
+         libraries/cgilib-0.4/cgi.5, libraries/cgilib-0.4/cgi.c,
+         libraries/cgilib-0.4/cgi.h, libraries/cgilib-0.4/cgiDebug.3,
+         libraries/cgilib-0.4/cgiGetValue.3,
+         libraries/cgilib-0.4/cgiHeader.3, libraries/cgilib-0.4/cgiInit.3,
+         libraries/cgilib-0.4/cgiRedirect.3,
+         libraries/cgilib-0.4/cgilib.dsp, libraries/cgilib-0.4/cgilib.dsw,
+         libraries/cgilib-0.4/cgilib.vcproj,
+         libraries/cgilib-0.4/cgitest.c, libraries/cgilib-0.4/jumpto.c,
+         libraries/cgilib-0.4/readme, libraries/freetype-2.0.5-import.txt,
+         libraries/libart_lgpl-2.3.7, libraries/libpng-1.2.0,
+         libraries/libpng-1.2.0-import.txt, libraries/zlib-1.1.4: killem
+
+2004-01-19 23:27  oetiker
+
+       * libraries/freetype-2.0.5: all must go
+
+2004-01-19 23:17  oetiker
+
+       * MakeMakefile, Makefile.am, NEWS, README, bindings/Makefile.am,
+         bindings/perl-shared/Makefile.PL, bindings/perl-shared/RRDs.xs,
+         bindings/tcl/Makefile.am, configure.ac, doc/rrdcgi.pod,
+         libraries/Makefile.am: misc fixes to get rrdtool working without
+         included libraries.
+
+2004-01-19 23:16  oetiker
+
+       * src/Makefile.am, src/rrd_gfx.h: modifications for 'no more local
+         libaries'
+
+2004-01-15 18:14  oetiker
+
+       * bindings/perl-shared/RRDs.pm, bindings/perl-shared/RRDs.xs:
+         addition of RRDs::times -- Christophe Kalt <kalt@taranis.org>
+
+2003-12-26 16:54  oetiker
+
+       * src/rrd_graph.c: using --riggid it is possible to get a case where
+         minval is bigger than maxval ... this breaks the horizontal grid
+         ...
+
+2003-11-27 06:31  oetiker
+
+       * bindings/perl-shared/RRDs.xs: Stop RRDs for segfaulting on invalid
+         graph input -- Ian Holsman <Ian.Holsman@cnet.com>
+
+2003-11-27 06:30  oetiker
+
+       * src/rrd_cgi.c: make rrd_cgi build again ... Ian Holsman
+         <rrd.developers@holsman.net>
+
+2003-11-23 12:55  oetiker
+
+       * doc/rrdcgi.pod, src/rrd_cgi.c: New recursive parser for rrdcgi by
+         Arend-Jan Wijtzes <ajwytzes@wise-guys.nl>
+
+2003-11-19 07:06  oetiker
+
+       * src/rrd_gfx.c: Adding attributions: * xml fix was by Ian Holsman
+         <rrd.developers@holsman.net> * font rotation was by Chris
+         Turbeville <turbo@verio.net>
+
+2003-11-19 06:58  oetiker
+
+       * src/rrd_gfx.c: fixed svg generation
+
+2003-11-17 11:43  oetiker
+
+       * src/rrd_restore.c: more correct usage added -- Eldad Zack
+         <eldad@stoneshaft.ath.cx>
+
+2003-11-14 23:10  oetiker
+
+       * src/rrd_cgi.c: handle NULL pointers gracefully
+
+2003-11-12 22:14  oetiker
+
+       * bindings/perl-shared/RRDs.xs, src/rrd.h, src/rrd_cgi.c,
+         src/rrd_graph.c, src/rrd_graph.h, src/rrd_tool.c: allow to pass an
+         open filehandle into rrd_graph as an extra argument
+
+2003-11-12 22:13  oetiker
+
+       * src/rrd_create.c: make create much faster .. -- David M. Grimes
+         <dgrimes@navisite.com>
+
+2003-11-11 21:19  oetiker
+
+       * doc/rrdrestore.pod, src/rrd_restore.c, src/rrd_tool.c: Allow to
+         force overwrite when restoring from xml to rrd. -- Eldad Zack
+         <eldad@stoneshaft.ath.cx>
+
+2003-11-11 19:46  oetiker
+
+       * src/fnv.h, src/parsetime.c, src/rrd.h, src/rrd_cgi.c,
+         src/rrd_create.c, src/rrd_fetch.c, src/rrd_graph.c,
+         src/rrd_graph_helper.c, src/rrd_update.c, src/rrd_xport.c:
+         replaced time_value with rrd_time_value as MacOS X introduced a
+         struct of that name in their standard headers
+
+2003-11-11 19:38  oetiker
+
+       * src/rrd_update.c: rrd files should NOT change size ever ... bulk
+         update code wa buggy. -- David M. Grimes <dgrimes@navisite.com>
+
+2003-11-04 22:20  oetiker
+
+       * src/rrd_tool.c: 20 chars max in a ds name -- Kuba Filipowicz
+         <amo@axit.pl>
+
+2003-10-26 19:29  oetiker
+
+       * src/rrd_gfx.c, src/rrd_gfx.h, src/rrd_graph.c: added rotation ...
+
+2003-09-04 13:16  oetiker
+
+       * src/rrd_update.c: should not assigne but compare ... grrrrr
+
+2003-09-02 21:58  oetiker
+
+       * src/rrd_update.c: be pickier about what we accept in rrd_update.
+         Complain if things do not work out
+
+2003-08-28 21:48  oetiker
+
+       * configure.ac: make sure we get only 1 flag -- Chris Turbeville
+         <turbo@verio.net>
+
+2003-07-28 21:59  oetiker
+
+       * bindings/Makefile.am, libraries/freetype-2.0.5/Makefile.am,
+         src/Makefile.am: I was just taking a look at the development
+         RRDtool build (I'm looking forward to SVG support), and had a few
+         problems with an out-of-tree build. I find using a separate build
+         directory makes things much easier to manage, especially when
+         building for multiple architectures. I've attached the minor
+         patches for the progress that I made (I couldn't get the Perl
+         modules to work, but the main binaries seem okay), and would be
+         grateful if you could commit them (or something like them!). --
+         Joseph Walton <joe@kafsemo.org>
+
+2003-07-24 14:51  jake
+
+       * src/rrd_graph.h: Allow hyphen character in DEF_NAM_FMT to match
+         DS_NAM_FMT.
+
+2003-07-16 23:41  jake
+
+       * confignt/config.h, libraries/cgilib-0.4/cgilib.vcproj,
+         libraries/freetype-2.0.5/freetype.vcproj,
+         libraries/libart_lgpl-2.3.7/libart.vcproj,
+         libraries/libpng-1.2.0/png.vcproj,
+         libraries/zlib-1.1.4/zlib.vcproj, src/rrd.vcproj,
+         src/rrd_cgi.vcproj, src/rrd_graph.c, src/rrdtool.sln,
+         src/rrdtool.vcproj: Updated MS Visual C++ files from 7.0 to 7.1.
+         Add code to rrd_graph.c: rrd_graph_init() to use %windir%
+         environment variable to find the default font at runtime.
+
+2003-07-15 00:58  jake
+
+       * src/rrd_rpncalc.h: Moved OP_COUNT to the end of the enumeration.
+
+2003-05-20 20:51  oetiker
+
+       * doc/rrdcgi.pod, src/rrd_cgi.c: I've made a patch to rrdcgi to add
+         <RRD::TIME::STRFTIME ...> which allows you to re-format start and
+         end-time at-style times using strftime. This allows easy timespans
+         in the graph (e.g. -2weeks) to be formatted into more usual times.
+         -- "Erskine, Thomas" <terskine@NRCan.gc.ca>
+
+2003-05-03 15:41  oetiker
+
+       * src/rrd_create.c: small patch to rrd_create.c that corrects
+         problem with the version number of the newly created files. --
+         Sasha Mikheev <sasha@avalon-net.co.il>
+
+2003-04-29 21:56  oetiker
+
+       * src/rrd_open.c: readline in rrd_open.c reads the file in 8 KB
+         blocks, and calls realloc for each block. realloc is very slow in
+         Mac OS X for huge blocks, e.g. when restoring databases from huge
+         xml files. This patch finds the size of the file, and starts out
+         with malloc'ing the full size. -- Peter Speck <speck@ruc.dk>
+
+2003-04-29 19:37  oetiker
+
+       * doc/rrdfetch.pod: added comment on iso
+
+2003-04-29 19:14  jake
+
+       * MakeMakefile, doc/rrdupdate.pod, src/rrd_update.c: Change updatev
+         RRA return from index_number to cf_nam, pdp_cnt. Also revert
+         accidental addition of -I to aclocal MakeMakefile.
+
+2003-04-26 05:11  oetiker
+
+       * src/rrd_restore.c: only restore V2 and larger.
+
+2003-04-25 18:35  jake
+
+       * MakeMakefile, bindings/perl-shared/RRDs.pm,
+         bindings/perl-shared/RRDs.xs, doc/rrdtool.pod, doc/rrdupdate.pod,
+         src/rrd.h, src/rrd_create.c, src/rrd_info.c, src/rrd_tool.c,
+         src/rrd_tool.h, src/rrd_update.c: Alternate update interface,
+         updatev. Returns info about CDPs written to disk as result of
+         update. Output format is similar to rrd_info, a hash of
+         key-values.
+
+2003-04-23 22:47  oetiker
+
+       * src/rrd_restore.c: fixed version checks to only complain if xml
+         version is > than current RRD version
+
+2003-04-11 19:43  oetiker
+
+       * doc/rrdcreate.pod, doc/rrdgraph-old.pod, doc/rrdgraph_rpn.src,
+         src/rrd_open.c, src/rrd_restore.c, src/rrd_rpncalc.c,
+         src/rrd_rpncalc.h, src/rrd_tool.c: New special value COUNT which
+         allows calculations based on the position of a value within a data
+         set. Bug fix in rrd_rpncalc.c. PREV returned erroneus value for
+         the second value. Bug fix in rrd_restore.c. Bug causing seek error
+         when accesing an RRD restored from an xml that holds an RRD
+         version <3. -- Ruben Justo <ruben@ainek.com>
+
+2003-04-04 20:51  oetiker
+
+       * src/rrd_gfx.c: I think I found a very small bug in rrd graph PDF
+         output : the is_stream member of pdf_buffer is not initialized,
+         making bad PDF sometimes (I found a case where is_stream was != 0
+         for the font initialization). -- lhoudard@netcourrier.com
+
+2003-04-01 22:52  jake
+
+       * THREADS, bindings/perl-shared/ntmake.pl, src/rrd.dsp, src/rrd.h,
+         src/rrd.vcproj, src/rrd_graph.c, src/rrd_nan_inf.c,
+         src/rrd_not_thread_safe.c, src/rrd_thread_safe_nt.c,
+         src/rrd_tool.h, src/rrdtool.dsp: Fix Win32 build. VC++ 6.0 and 7.0
+         now use the thread-safe code.
+
+2003-03-31 21:22  oetiker
+
+       * src/rrd_format.h, src/rrd_open.c, src/rrd_update.c: enables
+         RRDtool updates with microsecond or in case of windows millisecond
+         precision. This is needed to reduce time measurement error when
+         archive step is small. (<30s) -- Sasha Mikheev
+         <sasha@avalon-net.co.il>
+
+2003-03-25 22:29  oetiker
+
+       * bindings/perl-shared/ntmake.pl: use the environment variable
+         VCINSTALLDIR -- Ian Holsman <rrd.developers@holsman.net>
+
+2003-03-24 22:08  oetiker
+
+       * src/rrd_thread_safe.c: strerror should not turn us recursive here
+         ... -- Peter Stamfest <peter@stamfest.at>
+
+2003-03-24 22:05  oetiker
+
+       * src/rrd_not_thread_safe.c: strerror must not become recursive --
+         Peter Stamfest <peter@stamfest.at>
+
+2003-03-12 20:39  oetiker
+
+       * doc/rrdresize.pod: remove ^M
+
+2003-03-10 00:30  oetiker
+
+       * src/rrd_diff.c: handle cases with two negative numbers -- Sasha
+         Mikheev <sasha@avalon-net.co.il>
+
+2003-03-08 18:44  oetiker
+
+       * src/rrd_graph.c: don't display legends for [HV]RULEs out of graph
+         bounds (rrdgraph) -- Christophe Kalt <kalt@taranis.org>
+
+2003-03-01 22:25  oetiker
+
+       * src/rrd_tool.c: realy suppress size output when talking to stdout.
+         Patch from Mat Zimmerman @ debian
+
+2003-02-24 18:26  oetiker
+
+       * examples/piped-demo.pl.in: fix for : in piped demo
+
+2003-02-22 21:57  oetiker
+
+       * doc/rrdthreads.pod: Initial checkin -- Peter Stamfest
+         <peter@stamfest.at>
+
+2003-02-22 21:57  oetiker
+
+       * doc/Makefile.am, src/Makefile.am, src/rrd_error.c: a patch to
+         avoid a memory leak and a Makefile.am patch to distribute all
+         required source files -- Peter Stamfest <peter@stamfest.at>
+
+2003-02-21 22:40  oetiker
+
+       * examples/piped-demo.pl.in: colons must be escaped
+
+2003-02-20 21:48  oetiker
+
+       * libraries/libpng-1.2.0/Makefile.am, src/Makefile.am: make the
+         threading work and compile, fix linking to libpng and update the
+         timestaps int the files
+
+2003-02-20 21:27  oetiker
+
+       * src/Makefile.am, src/rrd_thread_safe.c, src/rrd_tool.c: updated
+         copyrigh dates removed error.h from threaded variant added
+         compiletime to rrdtool
+
+2003-02-16 12:32  oetiker
+
+       * src/rrd_restore.c: if output is - there is not need to deref the
+         handler
+
+2003-02-16 12:31  oetiker
+
+       * src/parsetime.c: expect seems to exist on some systems in the
+         system inc files
+
+2003-02-13 07:05  oetiker
+
+       * Makefile.am, THREADS, configure.ac, src/Makefile.am,
+         src/parsetime.c, src/rrd.h, src/rrd_cgi.c, src/rrd_create.c,
+         src/rrd_dump.c, src/rrd_error.c, src/rrd_format.c,
+         src/rrd_graph.c, src/rrd_graph_helper.c, src/rrd_info.c,
+         src/rrd_is_thread_safe.h, src/rrd_last.c,
+         src/rrd_not_thread_safe.c, src/rrd_open.c, src/rrd_restore.c,
+         src/rrd_rpncalc.c, src/rrd_stat.c, src/rrd_thread_safe.c,
+         src/rrd_tool.c, src/rrd_tool.h, src/rrd_update.c: Find attached
+         the patch I promised to send to you. Please note that there are
+         three new source files (src/rrd_is_thread_safe.h,
+         src/rrd_thread_safe.c and src/rrd_not_thread_safe.c) and the
+         introduction of librrd_th. This library is identical to librrd,
+         but it contains support code for per-thread global variables
+         currently used for error information only. This is similar to how
+         errno per-thread variables are implemented. librrd_th must be
+         linked alongside of libpthred There is also a new file "THREADS",
+         holding some documentation. -- Peter Stamfest <peter@stamfest.at>
+
+2003-02-12 07:06  oetiker
+
+       * CONTRIBUTORS, bindings/Makefile.am, bindings/perl-shared/MANIFEST,
+         libraries/Makefile.am, src/Makefile.am: improved dist target --
+         Peter Stamfest <peter@stamfest.at>
+
+2003-02-11 10:16  oetiker
+
+       * doc/rrdgraph_graph.src: removed lg we do not allow this in the
+         code at th moment
+
+2003-02-05 18:16  oetiker
+
+       * src/rrd_resize.c: rrd resize SHRINK was broken :-) -- Scott Mace
+         <smace@intt.ORG>
+
+2003-01-31 06:46  oetiker
+
+       * src/rrd_graph.c: The NaN check should only use lastgdes when
+         following a STACK -- Scott Mace <smace@intt.ORG>
+
+2003-01-30 21:39  oetiker
+
+       * src/rrd_graph.c: fix stak+nan error ... Scott Mace
+         <smace@intt.ORG>
+
+2003-01-29 07:17  oetiker
+
+       * src/rrd_graph.c: fixed the processing of
+         TICK:vname#color:frac:legend -- Scott Mace <smace@intt.ORG>
+
+2003-01-25 22:50  oetiker
+
+       * CONTRIBUTORS, examples/4charts.pl.in, src/rrd_graph.c,
+         src/rrd_graph.h: added function (--no-minor) to turn off minor
+         gridlines on graphs -- Travis Brown <tebrown@csh.rit.edu>
+
+2003-01-16 23:27  oetiker
+
+       * src/rrd_fetch.c: fix border condition in rra selection of
+         rrd_fetch -- Stanislav Sinyagin <ssinyagin@yahoo.com>
+
+2003-01-15 19:24  oetiker
+
+       * doc/rrdcreate.pod, doc/rrdgraph-old.pod, doc/rrdgraph.src,
+         doc/rrdgraph_graph.src, src/rrd_graph.c: * single letter variants
+         for all graph options -- James Overbeck <grendel@gmo.jp>
+
+2002-12-14 22:30  oetiker
+
+       * configure.ac: fix rrdtool compile on solaris where CC for perl is
+         "gcc -B/usr/ccs/bin/" -- Russell Van Tassell
+         <russell@loosenut.com>
+
+2002-11-29 11:57  oetiker
+
+       * doc/rrdtool.pod: note on info added
+
+2002-11-19 22:33  oetiker
+
+       * doc/rrdgraph-old.pod, doc/rrdgraph_graph.src, src/rrd_graph.c:
+         added g as valid string format value
+
+2002-11-18 23:37  oetiker
+
+       * configure.ac: check for -OPT:IEEE_NaN_inf=ON on SGI C compiler --
+         Albert Chin-A-Young <china@thewrittenword.com>
+
+2002-11-03 15:07  oetiker
+
+       * MakeMakefile: fixed
+
+2002-10-24 21:17  oetiker
+
+       * src/rrd_cgi.c: added patch for apache 2 compatibility
+
+2002-10-07 06:22  oetiker
+
+       * MakeMakefile: keep up with the jhonses
+
+2002-10-07 06:21  oetiker
+
+       * src/rrd_graph.c: -z does not take an option -- Tomoyuki Murakami
+         <tomoyuki@pobox.com>
+
+2002-09-02 18:41  oetiker
+
+       * src/rrd_graph.c: fixed leak in VDEF_PERCENT handlin -- Perry Stoll
+         <perry_stoll@yahoo.com>
+
+2002-08-01 05:42  oetiker
+
+       * src/rrd_graph.c: fix for segfault condition in print_calc -- Paul
+         Clifford <paul.clifford@bbc.co.uk>
+
+2002-07-31 05:42  oetiker
+
+       * doc/rrdfetch.pod: improved fetch explanation --
+         Nenad.Antic@era.ericsson.se
+
+2002-07-13 18:35  oetiker
+
+       * src/rrd_graph.h, src/rrd_rpncalc.c, src/rrd_rpncalc.h: fixed
+         DEF_NAM_FMT definition added double include protection the
+         rrd_graph.h and rrd_rpncalc.h
+
+2002-07-12 11:20  oetiker
+
+       * src/rrd_rpncalc.c: fixed PREV parsing ... Gonzalo Augusto Arana
+         Tagle <garana@uolsinectis.com.ar>
+
+2002-07-06 15:45  oetiker
+
+       * src/rrd_rpncalc.h: new operators must be added at the END of the
+         sequence ...
+
+2002-07-05 18:57  oetiker
+
+       * doc/cdeftutorial.pod, doc/rrdgraph_rpn.src, src/rrd_graph.c,
+         src/rrd_rpncalc.c, src/rrd_rpncalc.h: new operand on rrdgraph
+         CDEFs: PREV(xxxx) -- Gonzalo Augusto Arana Tagle
+         <garana@uolsinectis.com.ar>
+
+2002-07-05 18:47  oetiker
+
+       * bindings/perl-shared/RRDs.pm: explain tzset
+
+2002-07-02 10:35  oetiker
+
+       * src/rrd_nan_inf.c: fixed DINF for win32
+
+2002-06-29 15:33  alex
+
+       * doc/rrdgraph.src, doc/rrdgraph_data.src, doc/rrdgraph_graph.src:
+         Changed DEF:...
+
+2002-06-29 15:24  alex
+
+       * src/rrd_graph.c, src/rrd_graph.h, src/rrd_graph_helper.c: Changed
+         parsing again. Added a DEBUG prefix to all grapher commands
+
+2002-06-29 14:55  alex
+
+       * src/rrd_graph_helper.h: Not needed anymore
+
+2002-06-27 19:34  alex
+
+       * src/rrd_graph.c, src/rrd_graph.h: DEF now takes "--start" and
+         "--end" LINEx, AREA and such now take parameter "STACK"
+
+2002-06-23 23:13  alex
+
+       * doc/rrdgraph_data.src, doc/rrdgraph_examples.src: Updated docs for
+         the "DEF ... :step=nnnn" case
+
+2002-06-23 22:29  alex
+
+       * src/Makefile.am, src/rrd_fetch.c, src/rrd_graph.c,
+         src/rrd_resize.c: Added "step=1800" and such to "DEF" Cleaned some
+         of the signed vs. unsigned problems
+
+2002-06-23 22:28  alex
+
+       * src/rrd_format.h, src/rrd_nan_inf.c, src/rrd_nan_inf.h: In stead
+         of a zillion warnings, only one place gives a warning for DNAN
+
+2002-06-20 00:21  jake
+
+       * NT-BUILD-TIPS.txt, bindings/perl-shared/RRDs.xs,
+         bindings/perl-shared/ntmake.pl, confignt/config.h,
+         libraries/freetype-2.0.5/freetype.dsp,
+         libraries/libart_lgpl-2.3.7/libart.dsp,
+         libraries/libpng-1.2.0/png.dsp, libraries/zlib-1.1.4/zlib.dsp,
+         src/rrd.dsp, src/rrd_open.c, src/rrd_tool.h, src/rrdtool.dsp,
+         src/rrdtool.ncb: More Win32 build changes; thanks to Kerry
+         Calvert.
+
+2002-06-14 12:15  oetiker
+
+       * doc/rrdresize.pod: better explanations -- "Shipway, Steve"
+         <steve.shipway@eds.com>
+
+2002-05-22 15:25  jake
+
+       * doc/rrdtune.pod: Changed intercept to slope for beta parameter in
+         rrdtune doc.
+
+2002-05-22 05:35  oetiker
+
+       * bindings/perl-shared/ntmake.pl,
+         libraries/cgilib-0.4/cgilib.vcproj,
+         libraries/freetype-2.0.5/freetype.vcproj,
+         libraries/libart_lgpl-2.3.7/libart.vcproj,
+         libraries/libpng-1.2.0/png.vcproj,
+         libraries/zlib-1.1.4/zlib.vcproj, src/rrd.vcproj,
+         src/rrd_cgi.vcproj, src/rrdtool.ncb, src/rrdtool.sln,
+         src/rrdtool.suo, src/rrdtool.vcproj: VC++ .NET (7.0) project files
+         -- Eric Chamberlain <echamber@socrates.Berkeley.EDU>
+
+2002-05-16 19:18  oetiker
+
+       * configure.ac: added -W ... lets have some more warnings ... with
+         gcc 3.1 985
+
+2002-05-15 06:14  oetiker
+
+       * libraries/afm/compile_afm.pl, src/rrd_afm_data.c: afm fixes --
+         Peter Speck <speck@ruc.dk>
+
+2002-05-14 21:52  jake
+
+       * NT-BUILD-TIPS.txt, confignt/config.h,
+         libraries/libart_lgpl-2.3.7/libart.dsp, src/rrd.dsp,
+         src/rrd_tool.c, src/rrdtool.dsp: Fix Win32 Build compatibility.
+
+2002-05-14 21:48  oetiker
+
+       * src/rrd_restore.c: double free fixed
+
+2002-05-14 05:28  oetiker
+
+       * configure.ac, src/rrd_tool.c: make opendir readdir chdir chroot
+         and thus the serverfunctionality a conditional compile depending
+         on the presence of the necessary function calls.
+
+2002-05-11 09:09  oetiker
+
+       * configure.ac, src/rrd_tool.c, src/rrd_tool.h: make dirent
+         sys/types and sys/stat autoconfable ....
+
+2002-05-08 20:34  oetiker
+
+       * doc/rrdtool.pod, src/rrd_tool.c, src/rrd_tool.h: Add funtionality
+         to rrdtool to run it as a 'server' -- Hartmut.Vogler@epost.de
+
+2002-05-07 21:58  oetiker
+
+       * CONTRIBUTORS, bindings/perl-shared/RRDs.xs, doc/Makefile.am,
+         doc/rrdtool.pod, doc/rrdxport.pod, examples/shared-demo.pl.in,
+         src/Makefile.am, src/rrd.h, src/rrd_graph.c, src/rrd_graph.h,
+         src/rrd_tool.c, src/rrd_xport.c, src/rrd_xport.h: new command
+         rrdtool xport integrated -- Wolfgang Schrimm
+         <Wolfgang.Schrimm@urz.uni-heidelberg.de>
+
+2002-05-02 13:23  oetiker
+
+       * doc/rrdcreate.pod: typo fixed
+
+2002-04-29 17:11  oetiker
+
+       * src/rrd_graph.c: badformat parser fixed ... for good this time I
+         hope
+
+2002-04-28 19:13  oetiker
+
+       * src/rrd_graph.c: inserted PRINT checker at the wrong point ...
+
+2002-04-28 14:14  oetiker
+
+       * src/rrd_graph.c: It's nice and simple, the error checker for the
+         PRINT stuff which is passed to printf() is not allowing certain
+         valid printf() style operations which make text alignment much
+         more fun. -- Richard A Steenbergen <ras@e-gerbil.net>
+
+2002-04-24 21:04  oetiker
+
+       * examples/cgi-demo.cgi, libraries/libart_lgpl-2.3.7/art_config.h,
+         src/rrd_graph.c: fixed setlocale issues
+
+2002-04-24 20:54  oetiker
+
+       * configure.ac, doc/rrdgraph.src: fixed adress for wishlist
+
+2002-04-24 20:49  oetiker
+
+       * src/rrd_gfx.c, src/rrd_gfx.h, src/rrd_graph.c: Patch for
+         supporting PDF as output format
+
+2002-04-24 20:49  oetiker
+
+       * src/rrd_graph.c, src/rrd_graph.h: Modification of rrd_graph.c/h so
+         it uses dashes for gridlines much like rrdtool 1.0 Avoids drawing
+         minor vertical gridlines where a major gridline is to avoid
+         artifacts from having the grey line beneath the red one. Patches
+         src/rrd_graph.c and src/rrd_graph.h -- Peter Speck <speck@ruc.dk
+
+2002-04-24 20:48  oetiker
+
+       * src/rrd_gfx.c: Improvement of svg/eps dash code so it adjusts
+         dash-lengths for round caps. -- Peter Speck <speck@ruc.dk>
+
+2002-04-24 20:43  oetiker
+
+       * src/rrd_graph.c: fixed setlocale issues
+
+2002-04-16 05:00  oetiker
+
+       * doc/rrdgraph.src: doc for gridfit by peter
+
+2002-04-15 21:29  oetiker
+
+       * src/rrd_gfx.c, src/rrd_graph.c, src/rrd_graph.h: Evenly-spaced
+         y-axis gridlines. I had to split horizontal_grid() into
+         calc_horizontal_grid and draw_horizontal_grid as the calculated
+         info is needed in the adjustment code. My previous patch had the
+         problem that it adjusted the y-axis too late, e.g. after the data
+         lines was drawn. The result of the calc is stored in struct
+         ygrid_scale_t which image_desc_t has as a member. --no-gridfit is
+         implemented. The round-to-integer coordinates for png is moved to
+         the libart code in rrd_gfx. The 'close path' code is cleaned up so
+         the node list is left unchanged if you want to save the same graph
+         in multiple formats in one run. The rounding is done on the scaled
+         coordinates (zoom). I have made a simple version for logarithmic y
+         scales as such a scale might have 5 gridlines with 4 difference
+         spacings. This version only uses y = 10^x values for modifying the
+         scale. -- Peter Speck <speck@ruc.dk>
+
+2002-04-09 21:35  oetiker
+
+       * doc/rrdcgi.pod: remove boguos description of goodfor and refresh
+
+2002-04-09 21:34  oetiker
+
+       * bindings/perl-shared/RRDs.xs: removed tzset as it is in rrd_graph
+         now
+
+2002-04-09 21:34  oetiker
+
+       * configure.ac, src/rrd_graph.c: added tzset and setlocale to
+         rrd_graph
+
+2002-04-07 22:07  oetiker
+
+       * src/rrd_afm.c, src/rrd_afm.h, src/rrd_afm_data.c,
+         src/rrd_afm_data.h: added missing files from peters patch
+
+2002-04-07 20:20  oetiker
+
+       * configure.ac, doc/rrdgraph.src, examples/4charts.pl.in,
+         examples/bigtops.pl, examples/piped-demo.pl,
+         examples/shared-demo.pl, examples/stripes.pl,
+         libraries/Makefile.am, libraries/afm, libraries/afm/COPYRIGHT.txt,
+         libraries/afm/Courier-Bold.afm,
+         libraries/afm/Courier-BoldOblique.afm,
+         libraries/afm/Courier-Oblique.afm, libraries/afm/Courier.afm,
+         libraries/afm/Helvetica-Bold.afm,
+         libraries/afm/Helvetica-BoldOblique.afm,
+         libraries/afm/Helvetica-Oblique.afm, libraries/afm/Helvetica.afm,
+         libraries/afm/Makefile.am, libraries/afm/Symbol.afm,
+         libraries/afm/Times-Bold.afm, libraries/afm/Times-BoldItalic.afm,
+         libraries/afm/Times-Italic.afm, libraries/afm/Times-Roman.afm,
+         libraries/afm/ZapfDingbats.afm, libraries/afm/compile_afm.pl,
+         libraries/afm/glyphlist.txt, libraries/afm/test-afm.c,
+         libraries/libart_lgpl-2.3.7/Makefile.in,
+         libraries/libpng-1.2.0/Makefile.in, src/Makefile.am,
+         src/rrd_gfx.c, src/rrd_gfx.h, src/rrd_graph.c, src/rrd_tool.c: 3
+         patches, #3 depends on #1 as EPS uses AFM for stringwidth too.
+         src/rrd_afm.[ch], src/rrd_afm_data.[ch] and changes to
+         rrd_gfx.[ch] to use AFM for stringwidth in SVG output.
+         libraries/afm with .afm files for the standard 14 postscript fonts
+         and perl script to create src/rrd_afm_data.c Includes small test
+         program to show examples of calculated stringwidth and actual
+         stringwidth. Adds EPS output support. Modifies rrd_tool.c as
+         rrd_graph() changes the argv pointer, and rrd_tool then always
+         wrote the 200x100 output line to the file (which probably doesn't
+         do anything for png images, but eps files fail having it after the
+         %%EOF marker). -- Peter Speck <speck@ruc.dk>
+
+2002-04-06 12:40  alex
+
+       * src/rrd_graph.c: Different default font when WIN32 is defined
+
+2002-04-06 12:25  alex
+
+       * NT-BUILD-TIPS.txt, bindings/perl-shared/Makefile.PL,
+         bindings/perl-shared/RRDs.pm, bindings/perl-shared/ntmake.pl,
+         bindings/perl-shared/t/base.t, doc/Makefile.am,
+         doc/cdeftutorial.pod, doc/rrdgraph-old.pod,
+         doc/rrdgraph_graph.src, doc/rrdtutorial.es.pod,
+         examples/cgi-demo.cgi, examples/cgi-demo.cgi.in,
+         examples/piped-demo.pl, examples/piped-demo.pl.in,
+         examples/shared-demo.pl, examples/shared-demo.pl.in,
+         src/rrd_cgi.c, src/rrd_gfx.c, src/rrd_gfx.h, src/rrd_graph.c,
+         src/rrd_graph.h, src/rrd_tool.h: Removed references to GIF Changed
+         y0 and such into Y0 and so
+
+2002-04-05 23:51  jake
+
+       * NT-BUILD-TIPS.txt, bindings/perl-shared/RRDs.xs,
+         bindings/perl-shared/ntmake.pl, confignt, confignt/config.h,
+         libraries/freetype-2.0.5/freetype.dsp,
+         libraries/libart_lgpl-2.3.7/libart.dsp,
+         libraries/libpng-1.2.0/png.dsp, libraries/zlib-1.1.4/zlib.dsp,
+         src/ntconfig.h, src/rrd.dsp, src/rrd_gfx.c, src/rrd_graph.c,
+         src/rrd_tool.h, src/rrdtool.dsp, src/rrdtool.dsw: Updated/added
+         MVSC++ 6.0 project files for compilation of rrd.lib and
+         rrdtool.exe on Win32. Changes for RRDs compilation on Win32. Moved
+         src/ntconfig.h to confignt/config.h.
+
+2002-04-03 14:52  oetiker
+
+       * src/rrd_gfx.c, src/rrd_gfx.h, src/rrd_graph.c, src/rrd_graph.h: As
+         gfx_canvas_t now has excatly the same lifespan as image_desc_t,
+         I've made 'gfx_canvas_t canvas' a member of image_desc_t and
+         dropped it as a separate parameter in all function calls.
+         imgformat, interlaced and zoom are moved to gfx_canvas_t. I have
+         dropped my old fontlib-enum as imgformat contains that
+         information. The gfx_render_xxx switch is moved to rrd_gfx so
+         rrd_graph is (almost) only bothered with image formats in cmd line
+         parsing. gfx_close_path is added. gfx_new_dashed_line is added
+         with 2 new arguments: length of a dash and length between dashes.
+         gfx_new_line is still there for plain lines. Having dash-length ==
+         0 creates normal line. rrd_graph.c is not updated for dashed
+         lines. It's not decided how one should specify which and how lines
+         are dashed. An extension of the color specification? svg is
+         updated for dashes and opacity, but libart code is not. I've fixed
+         indent in SVG, all lines had a space before func decl. etc. Misc
+         small fixes, e.g. sscanf of gfx_color_t, position of x-axis arrow.
+         --- Peter Speck <speck@ruc.dk>
+
+2002-04-03 05:32  oetiker
+
+       * src/rrd_restore.c: i suck. sorry. add ,0666 to the open(). -- Paul
+         Vixie <paul@vix.com>
+
+2002-04-02 21:32  oetiker
+
+       * bindings/perl-shared/RRDs.xs: added call to tzset to activate TZ
+         settings -- Paul A Vixie <vixie@vix.com>
+
+2002-04-02 21:31  oetiker
+
+       * src/rrd_restore.c: aded missing fcntl.h
+
+2002-04-02 19:37  oetiker
+
+       * src/rrd_restore.c: rrd restore should not burn down existing files
+         -- Paul Vixie <paul@vix.com>
+
+2002-04-01 18:32  oetiker
+
+       * NEWS: merged svg update
+
+2002-04-01 18:31  oetiker
+
+       * src/rrd_diff.c: "!" takes a higher preference than "||" this means
+         rrd_update N:: would segfault -- Oliver Cook <ollie@uk.clara.net>
+
+2002-03-28 17:33  jake
+
+       * MakeMakefile: Added comment suggesting use of -I flag with
+         aclocal.
+
+2002-03-26 07:02  oetiker
+
+       * doc/rrdgraph.src, src/rrd_gfx.c, src/rrd_gfx.h, src/rrd_graph.h:
+         added SVG support -- Peter Speck <speck@ruc.dk>
+
+2002-03-23 20:41  alex
+
+       * src/rrd_graph.c: Better positioning of the pie when it is alone on
+         the canvas
+
+2002-03-23 20:01  alex
+
+       * NEWS: Modified rrd_graph
+
+2002-03-23 19:59  alex
+
+       * src/rrd_graph.c, src/rrd_graph.h: Changes in rrd_graph; see NEWS
+
+2002-03-23 09:05  oetiker
+
+       * doc/rrdcgi.pod, doc/rrdgraph.src, doc/rrdtutorial.pod: removed
+         reference to GIF
+
+2002-03-23 09:01  oetiker
+
+       * src/Makefile.am, src/rrd_graph.c, src/rrd_graph.h, src/rrd_tool.c:
+         remove all traces of GIF and make PNG the default
+
+2002-03-23 08:53  oetiker
+
+       * src/rrd_graph.c: Move CDEF start pointers if start of cdef is a
+         step ahead of the start of the data -- Ashok Mandala
+         <chakri063@yahoo.com>
+
+2002-03-21 22:39  oetiker
+
+       * src/rrd_graph.c: fixed color area in legend and fixed color area
+         in 3d border
+
+2002-03-21 12:00  alex
+
+       * src/rrd_graph.c, src/rrd_graph.h: Pie charts didn't have
+         anti-aliasing; building them clockwise seems to solve this
+         problem.
+
+2002-03-20 22:48  oetiker
+
+       * src/rrd_rpncalc.c: fixed longstanding bug affection CDEFS where
+         values from rrds with different resolutions got mixed ..
+
+2002-03-17 22:40  alex
+
+       * src/rrd_gfx.c, src/rrd_gfx.h, src/rrd_graph.c: Changed the way
+         circle sections are drawn.
+
+2002-03-13 02:58  alex
+
+       * src/rrd_gfx.c, src/rrd_gfx.h, src/rrd_graph.c: New, hopefully
+         better, implementation of PART drawing
+
+2002-03-12 07:19  oetiker
+
+       * libraries/zlib-1.1.3: realy remove zlibe 1.1.3 dir
+
+2002-03-12 07:18  oetiker
+
+       * MakeMakefile, configure.ac, examples/bigtops.pl,
+         examples/piped-demo.pl, examples/shared-demo.pl,
+         examples/stripes.pl: updated
+
+2002-03-12 07:17  oetiker
+
+       * libraries/libart_lgpl-2.3.7,
+         libraries/libart_lgpl-2.3.7/.cvsignore: ignore generated files
+
+2002-03-12 07:16  oetiker
+
+       * libraries/libart_lgpl-2.3.7/libart_lgpl_2.la,
+         libraries/zlib-1.1.4, libraries/zlib-1.1.4/.cvsignore,
+         libraries/zlib-1.1.4/Makefile, libraries/zlib-1.1.4/Makefile.in:
+         missing bits
+
+2002-03-12 06:43  oetiker
+
+       * configure.ac: updated to zlib 1.1.4
+
+2002-03-12 06:42  oetiker
+
+       * libraries/zlib-1.1.3/ChangeLog, libraries/zlib-1.1.3/FAQ,
+         libraries/zlib-1.1.3/INDEX, libraries/zlib-1.1.3/Makefile.am,
+         libraries/zlib-1.1.3/README, libraries/zlib-1.1.3/README.rrdtool,
+         libraries/zlib-1.1.3/adler32.c,
+         libraries/zlib-1.1.3/algorithm.txt,
+         libraries/zlib-1.1.3/compress.c, libraries/zlib-1.1.3/crc32.c,
+         libraries/zlib-1.1.3/deflate.c, libraries/zlib-1.1.3/deflate.h,
+         libraries/zlib-1.1.3/descrip.mms, libraries/zlib-1.1.3/example.c,
+         libraries/zlib-1.1.3/gzio.c, libraries/zlib-1.1.3/infblock.c,
+         libraries/zlib-1.1.3/infblock.h, libraries/zlib-1.1.3/infcodes.c,
+         libraries/zlib-1.1.3/infcodes.h, libraries/zlib-1.1.3/inffast.c,
+         libraries/zlib-1.1.3/inffast.h, libraries/zlib-1.1.3/inffixed.h,
+         libraries/zlib-1.1.3/inflate.c, libraries/zlib-1.1.3/inftrees.c,
+         libraries/zlib-1.1.3/inftrees.h, libraries/zlib-1.1.3/infutil.c,
+         libraries/zlib-1.1.3/infutil.h, libraries/zlib-1.1.3/maketree.c,
+         libraries/zlib-1.1.3/minigzip.c, libraries/zlib-1.1.3/trees.c,
+         libraries/zlib-1.1.3/trees.h, libraries/zlib-1.1.3/uncompr.c,
+         libraries/zlib-1.1.3/zconf.h, libraries/zlib-1.1.3/zlib.3,
+         libraries/zlib-1.1.3/zlib.dsp, libraries/zlib-1.1.3/zlib.dsw,
+         libraries/zlib-1.1.3/zlib.h, libraries/zlib-1.1.3/zutil.c,
+         libraries/zlib-1.1.3/zutil.h, libraries/zlib-1.1.4,
+         libraries/zlib-1.1.4/ChangeLog, libraries/zlib-1.1.4/FAQ,
+         libraries/zlib-1.1.4/INDEX, libraries/zlib-1.1.4/Make_vms.com,
+         libraries/zlib-1.1.4/Makefile, libraries/zlib-1.1.4/Makefile.am,
+         libraries/zlib-1.1.4/Makefile.am~,
+         libraries/zlib-1.1.4/Makefile.in,
+         libraries/zlib-1.1.4/Makefile.riscos, libraries/zlib-1.1.4/README,
+         libraries/zlib-1.1.4/adler32.c,
+         libraries/zlib-1.1.4/algorithm.txt,
+         libraries/zlib-1.1.4/compress.c, libraries/zlib-1.1.4/crc32.c,
+         libraries/zlib-1.1.4/deflate.c, libraries/zlib-1.1.4/deflate.h,
+         libraries/zlib-1.1.4/descrip.mms, libraries/zlib-1.1.4/example.c,
+         libraries/zlib-1.1.4/gzio.c, libraries/zlib-1.1.4/infblock.c,
+         libraries/zlib-1.1.4/infblock.h, libraries/zlib-1.1.4/infcodes.c,
+         libraries/zlib-1.1.4/infcodes.h, libraries/zlib-1.1.4/inffast.c,
+         libraries/zlib-1.1.4/inffast.h, libraries/zlib-1.1.4/inffixed.h,
+         libraries/zlib-1.1.4/inflate.c, libraries/zlib-1.1.4/inftrees.c,
+         libraries/zlib-1.1.4/inftrees.h, libraries/zlib-1.1.4/infutil.c,
+         libraries/zlib-1.1.4/infutil.h, libraries/zlib-1.1.4/maketree.c,
+         libraries/zlib-1.1.4/minigzip.c, libraries/zlib-1.1.4/trees.c,
+         libraries/zlib-1.1.4/trees.h, libraries/zlib-1.1.4/uncompr.c,
+         libraries/zlib-1.1.4/zconf.h, libraries/zlib-1.1.4/zlib.3,
+         libraries/zlib-1.1.4/zlib.h, libraries/zlib-1.1.4/zlib.html,
+         libraries/zlib-1.1.4/zutil.c, libraries/zlib-1.1.4/zutil.h:
+         replace zlib 1.1.3 with zlib 1.1.4
+
+2002-03-10 23:08  alex
+
+       * doc/rrdgraph_graph.src, src/rrd_graph.c, src/rrd_graph.h: Pie
+         chart support added to rrdtool graph
+
+2002-03-10 22:49  oetiker
+
+       * MakeMakefile: added quotes to echo
+
+2002-03-10 16:22  alex
+
+       * src/rrd_rpncalc.c: Realigned function rpn_calc() Stack checking
+         now done using a macro
+
+2002-03-10 14:58  alex
+
+       * doc/rrdgraph.src, doc/rrdgraph_rpn.src: Fixed some typos/errors
+         Updated for the NE and ISINF operators in RPN
+
+2002-03-10 14:53  alex
+
+       * src/rrd_graph.c: Using the font option resulted in a segfault.
+         Needs more care, the current change is just a hack
+
+2002-03-10 14:48  alex
+
+       * src/rrd_rpncalc.c, src/rrd_rpncalc.h: Added NE and ISINF operands
+         to RPN
+
+2002-03-10 12:28  oetiker
+
+       * MakeMakefile: better version check
+
+2002-03-08 22:14  alex
+
+       * doc/rrdtutorial.pod: Needed to escape a wildcard
+
+2002-03-08 22:11  alex
+
+       * doc/rrdtutorial.pod: Fixed some typos
+
+2002-02-18 21:52  oetiker
+
+       * MakeMakefile, examples/bigtops.pl: added autotools version check
+         to MakeMakefiles
+
+2002-02-09 06:21  oetiker
+
+       * src/rrd_gfx.c: do propper error checking and release memmory when
+         it is not required anymore
+
+2002-02-08 18:40  oetiker
+
+       * src/rrd_cgi.c: allow rrd_cgi to deal with umlauts -- Alexander
+         Schwartz <alexander.schwartz@gmx.net>
+
+2002-02-03 08:10  oetiker
+
+       * doc/rrdcreate.pod: fixed spelling
+
+2002-02-02 14:36  oetiker
+
+       * src/rrd_graph.c: fixed data pointer storage
+
+2002-02-01 20:34  oetiker
+
+       * src/gdpng.c, src/gifsize.c, src/pngsize.c, src/rrd_cgi.c,
+         src/rrd_create.c, src/rrd_datalang.c, src/rrd_diff.c,
+         src/rrd_dump.c, src/rrd_error.c, src/rrd_fetch.c,
+         src/rrd_format.c, src/rrd_graph.c, src/rrd_hw.c, src/rrd_info.c,
+         src/rrd_last.c, src/rrd_open.c, src/rrd_resize.c,
+         src/rrd_restore.c, src/rrd_rpncalc.c, src/rrd_stat.c,
+         src/rrd_tool.c, src/rrd_tune.c, src/rrd_update.c: fixed version
+         number and date/time
+
+2002-02-01 20:29  oetiker
+
+       * libraries/libart_lgpl-2.3.7/Makefile,
+         libraries/libpng-1.2.0/Makefile, src/rrd_graph.c: *** empty log
+         message ***
+
+2002-01-31 22:44  oetiker
+
+       * acinclude.m4: this file and all the external libraries
+         configurability was contributed by mat zimmermann
+
+2002-01-31 22:43  oetiker
+
+       * acinclude.m4, configure.ac, examples/minmax.pl,
+         libraries/Makefile.am, src/Makefile.am, src/rrd_graph.c: allow use
+         of libraries already installed on the system
+
+2002-01-31 11:56  oetiker
+
+       * src/parsetime.c: make "18:00 yesterday" work Pavel Mores
+         <pvl@uh.cz>
+
+2002-01-31 07:23  oetiker
+
+       * src/rrd_graph.c: weeknumber is not %V which is more appropriate
+         than %W
+
+2002-01-31 06:36  oetiker
+
+       * MakeMakefile: copy ltmain.sh
+
+2002-01-17 20:31  oetiker
+
+       * libraries/libpng-1.0.9: this also
+
+2002-01-17 20:30  oetiker
+
+       * config/libtool, libraries/gd1.3, libraries/libpng-1.0.9/ANNOUNCE,
+         libraries/libpng-1.0.9/CHANGES, libraries/libpng-1.0.9/INSTALL,
+         libraries/libpng-1.0.9/KNOWNBUG, libraries/libpng-1.0.9/LICENSE,
+         libraries/libpng-1.0.9/Makefil,
+         libraries/libpng-1.0.9/Makefile.am, libraries/libpng-1.0.9/README,
+         libraries/libpng-1.0.9/README.rrdtool,
+         libraries/libpng-1.0.9/TODO, libraries/libpng-1.0.9/Y2KINFO,
+         libraries/libpng-1.0.9/example.c, libraries/libpng-1.0.9/libpng.3,
+         libraries/libpng-1.0.9/libpng.txt,
+         libraries/libpng-1.0.9/libpngpf.3, libraries/libpng-1.0.9/png.5,
+         libraries/libpng-1.0.9/png.c, libraries/libpng-1.0.9/png.dsp,
+         libraries/libpng-1.0.9/png.dsw, libraries/libpng-1.0.9/png.h,
+         libraries/libpng-1.0.9/pngasmrd.h,
+         libraries/libpng-1.0.9/pngbar.jpg,
+         libraries/libpng-1.0.9/pngbar.png,
+         libraries/libpng-1.0.9/pngconf.h,
+         libraries/libpng-1.0.9/pngerror.c,
+         libraries/libpng-1.0.9/pnggccrd.c,
+         libraries/libpng-1.0.9/pngget.c, libraries/libpng-1.0.9/pngmem.c,
+         libraries/libpng-1.0.9/pngnow.png,
+         libraries/libpng-1.0.9/pngpread.c,
+         libraries/libpng-1.0.9/pngread.c, libraries/libpng-1.0.9/pngrio.c,
+         libraries/libpng-1.0.9/pngrtran.c,
+         libraries/libpng-1.0.9/pngrutil.c,
+         libraries/libpng-1.0.9/pngset.c, libraries/libpng-1.0.9/pngtest.c,
+         libraries/libpng-1.0.9/pngtest.png,
+         libraries/libpng-1.0.9/pngtrans.c,
+         libraries/libpng-1.0.9/pngvcrd.c, libraries/libpng-1.0.9/pngwio.c,
+         libraries/libpng-1.0.9/pngwrite.c,
+         libraries/libpng-1.0.9/pngwtran.c,
+         libraries/libpng-1.0.9/pngwutil.c: this is not needed anymore
+
+2002-01-17 20:28  oetiker
+
+       * libraries/gd1.3/.cvsignore: killing remains
+
+2002-01-16 23:11  oetiker
+
+       * MakeMakefile: added places for download
+
+2002-01-15 22:58  oetiker
+
+       * ., .cvsignore, config, config/.cvsignore: ignore irrelevant things
+
+2002-01-15 22:53  oetiker
+
+       * libraries/freetype-2.0.5, libraries/freetype-2.0.5/.cvsignore,
+         libraries/libart_lgpl-2.3.7,
+         libraries/libart_lgpl-2.3.7/.cvsignore, libraries/libpng-1.2.0,
+         libraries/libpng-1.2.0/.cvsignore: added new .cvsignore files
+
+2002-01-15 22:51  oetiker
+
+       * CONTRIBUTORS, MakeMakefile, Makefile.am, NEWS, config/acconfig.h,
+         config/aclocal.m4, config/config.guess, config/config.h.in,
+         config/config.sub, config/install-sh, config/ltconfig,
+         config/ltmain.sh, config/missing, config/mkinstalldirs,
+         config/stamp-h, config/stamp-h.in, configure.ac, configure.in,
+         doc/rrdgraph.src, doc/rrdgraph_graph.src, examples/4charts.pl.in,
+         examples/bigtops.pl, examples/bigtops.pl.in,
+         examples/cgi-demo.cgi, examples/minmax.pl, examples/minmax.pl.in,
+         examples/piped-demo.pl, examples/piped-demo.pl.in,
+         examples/shared-demo.pl, examples/shared-demo.pl.in,
+         examples/stripes.pl, examples/stripes.pl.in,
+         libraries/Makefile.am, libraries/cgilib-0.4/Makefile.am,
+         libraries/freetype-2.0.5, libraries/freetype-2.0.5-import.txt,
+         libraries/freetype-2.0.5/Makefile.am,
+         libraries/freetype-2.0.5/README,
+         libraries/freetype-2.0.5/ahangles.c,
+         libraries/freetype-2.0.5/ahglobal.c,
+         libraries/freetype-2.0.5/ahglyph.c,
+         libraries/freetype-2.0.5/ahhint.c,
+         libraries/freetype-2.0.5/ahmodule.c,
+         libraries/freetype-2.0.5/ahoptim.c,
+         libraries/freetype-2.0.5/autohint.c,
+         libraries/freetype-2.0.5/cff.c,
+         libraries/freetype-2.0.5/cffdrivr.c,
+         libraries/freetype-2.0.5/cffgload.c,
+         libraries/freetype-2.0.5/cffload.c,
+         libraries/freetype-2.0.5/cffobjs.c,
+         libraries/freetype-2.0.5/cffparse.c,
+         libraries/freetype-2.0.5/cidgload.c,
+         libraries/freetype-2.0.5/cidload.c,
+         libraries/freetype-2.0.5/cidobjs.c,
+         libraries/freetype-2.0.5/cidparse.c,
+         libraries/freetype-2.0.5/cidriver.c,
+         libraries/freetype-2.0.5/ftbase.c,
+         libraries/freetype-2.0.5/ftbbox.c,
+         libraries/freetype-2.0.5/ftcache.c,
+         libraries/freetype-2.0.5/ftcalc.c,
+         libraries/freetype-2.0.5/ftcchunk.c,
+         libraries/freetype-2.0.5/ftcglyph.c,
+         libraries/freetype-2.0.5/ftcimage.c,
+         libraries/freetype-2.0.5/ftcmanag.c,
+         libraries/freetype-2.0.5/ftcsbits.c,
+         libraries/freetype-2.0.5/ftdebug.c,
+         libraries/freetype-2.0.5/ftextend.c,
+         libraries/freetype-2.0.5/ftglyph.c,
+         libraries/freetype-2.0.5/ftgrays.c,
+         libraries/freetype-2.0.5/ftinit.c,
+         libraries/freetype-2.0.5/ftlist.c,
+         libraries/freetype-2.0.5/ftlru.c,
+         libraries/freetype-2.0.5/ftmac.c, libraries/freetype-2.0.5/ftmm.c,
+         libraries/freetype-2.0.5/ftnames.c,
+         libraries/freetype-2.0.5/ftobjs.c,
+         libraries/freetype-2.0.5/ftoutln.c,
+         libraries/freetype-2.0.5/ftraster.c,
+         libraries/freetype-2.0.5/ftrend1.c,
+         libraries/freetype-2.0.5/ftsmooth.c,
+         libraries/freetype-2.0.5/ftstream.c,
+         libraries/freetype-2.0.5/ftsynth.c,
+         libraries/freetype-2.0.5/ftsystem.c,
+         libraries/freetype-2.0.5/fttrigon.c,
+         libraries/freetype-2.0.5/include,
+         libraries/freetype-2.0.5/include/Makefile.am,
+         libraries/freetype-2.0.5/include/ahangles.h,
+         libraries/freetype-2.0.5/include/aherrors.h,
+         libraries/freetype-2.0.5/include/ahglobal.h,
+         libraries/freetype-2.0.5/include/ahglyph.h,
+         libraries/freetype-2.0.5/include/ahhint.h,
+         libraries/freetype-2.0.5/include/ahloader.h,
+         libraries/freetype-2.0.5/include/ahmodule.h,
+         libraries/freetype-2.0.5/include/ahoptim.h,
+         libraries/freetype-2.0.5/include/ahtypes.h,
+         libraries/freetype-2.0.5/include/cffdrivr.h,
+         libraries/freetype-2.0.5/include/cfferrs.h,
+         libraries/freetype-2.0.5/include/cffgload.h,
+         libraries/freetype-2.0.5/include/cffload.h,
+         libraries/freetype-2.0.5/include/cffobjs.h,
+         libraries/freetype-2.0.5/include/cffparse.h,
+         libraries/freetype-2.0.5/include/cfftoken.h,
+         libraries/freetype-2.0.5/include/ciderrs.h,
+         libraries/freetype-2.0.5/include/cidgload.h,
+         libraries/freetype-2.0.5/include/cidload.h,
+         libraries/freetype-2.0.5/include/cidobjs.h,
+         libraries/freetype-2.0.5/include/cidparse.h,
+         libraries/freetype-2.0.5/include/cidriver.h,
+         libraries/freetype-2.0.5/include/cidtoken.h,
+         libraries/freetype-2.0.5/include/fnterrs.h,
+         libraries/freetype-2.0.5/include/freetype,
+         libraries/freetype-2.0.5/include/freetype/Makefile.am,
+         libraries/freetype-2.0.5/include/freetype/cache,
+         libraries/freetype-2.0.5/include/freetype/cache/Makefile.am,
+         libraries/freetype-2.0.5/include/freetype/cache/ftcchunk.h,
+         libraries/freetype-2.0.5/include/freetype/cache/ftcglyph.h,
+         libraries/freetype-2.0.5/include/freetype/cache/ftcimage.h,
+         libraries/freetype-2.0.5/include/freetype/cache/ftcmanag.h,
+         libraries/freetype-2.0.5/include/freetype/cache/ftcsbits.h,
+         libraries/freetype-2.0.5/include/freetype/cache/ftlru.h,
+         libraries/freetype-2.0.5/include/freetype/config,
+         libraries/freetype-2.0.5/include/freetype/config/Makefile.am,
+         libraries/freetype-2.0.5/include/freetype/config/ftconfig.h,
+         libraries/freetype-2.0.5/include/freetype/config/ftheader.h,
+         libraries/freetype-2.0.5/include/freetype/config/ftmodule.h,
+         libraries/freetype-2.0.5/include/freetype/config/ftoption.h,
+         libraries/freetype-2.0.5/include/freetype/freetype.h,
+         libraries/freetype-2.0.5/include/freetype/ftbbox.h,
+         libraries/freetype-2.0.5/include/freetype/ftcache.h,
+         libraries/freetype-2.0.5/include/freetype/ftchapters.h,
+         libraries/freetype-2.0.5/include/freetype/fterrors.h,
+         libraries/freetype-2.0.5/include/freetype/ftglyph.h,
+         libraries/freetype-2.0.5/include/freetype/ftimage.h,
+         libraries/freetype-2.0.5/include/freetype/ftlist.h,
+         libraries/freetype-2.0.5/include/freetype/ftmac.h,
+         libraries/freetype-2.0.5/include/freetype/ftmm.h,
+         libraries/freetype-2.0.5/include/freetype/ftmoderr.h,
+         libraries/freetype-2.0.5/include/freetype/ftmodule.h,
+         libraries/freetype-2.0.5/include/freetype/ftoutln.h,
+         libraries/freetype-2.0.5/include/freetype/ftrender.h,
+         libraries/freetype-2.0.5/include/freetype/ftsizes.h,
+         libraries/freetype-2.0.5/include/freetype/ftsnames.h,
+         libraries/freetype-2.0.5/include/freetype/ftsynth.h,
+         libraries/freetype-2.0.5/include/freetype/ftsystem.h,
+         libraries/freetype-2.0.5/include/freetype/fttrigon.h,
+         libraries/freetype-2.0.5/include/freetype/fttypes.h,
+         libraries/freetype-2.0.5/include/freetype/internal,
+         libraries/freetype-2.0.5/include/freetype/internal/Makefile.am,
+         libraries/freetype-2.0.5/include/freetype/internal/autohint.h,
+         libraries/freetype-2.0.5/include/freetype/internal/cfftypes.h,
+         libraries/freetype-2.0.5/include/freetype/internal/fnttypes.h,
+         libraries/freetype-2.0.5/include/freetype/internal/ftcalc.h,
+         libraries/freetype-2.0.5/include/freetype/internal/ftdebug.h,
+         libraries/freetype-2.0.5/include/freetype/internal/ftdriver.h,
+         libraries/freetype-2.0.5/include/freetype/internal/ftextend.h,
+         libraries/freetype-2.0.5/include/freetype/internal/ftmemory.h,
+         libraries/freetype-2.0.5/include/freetype/internal/ftobjs.h,
+         libraries/freetype-2.0.5/include/freetype/internal/ftstream.h,
+         libraries/freetype-2.0.5/include/freetype/internal/internal.h,
+         libraries/freetype-2.0.5/include/freetype/internal/pcftypes.h,
+         libraries/freetype-2.0.5/include/freetype/internal/psaux.h,
+         libraries/freetype-2.0.5/include/freetype/internal/psnames.h,
+         libraries/freetype-2.0.5/include/freetype/internal/sfnt.h,
+         libraries/freetype-2.0.5/include/freetype/internal/t1types.h,
+         libraries/freetype-2.0.5/include/freetype/internal/tttypes.h,
+         libraries/freetype-2.0.5/include/freetype/t1tables.h,
+         libraries/freetype-2.0.5/include/freetype/ttnameid.h,
+         libraries/freetype-2.0.5/include/freetype/tttables.h,
+         libraries/freetype-2.0.5/include/freetype/tttags.h,
+         libraries/freetype-2.0.5/include/ft2build.h,
+         libraries/freetype-2.0.5/include/ftcerror.h,
+         libraries/freetype-2.0.5/include/ftgrays.h,
+         libraries/freetype-2.0.5/include/ftraster.h,
+         libraries/freetype-2.0.5/include/ftrend1.h,
+         libraries/freetype-2.0.5/include/ftsmerrs.h,
+         libraries/freetype-2.0.5/include/ftsmooth.h,
+         libraries/freetype-2.0.5/include/pcf.h,
+         libraries/freetype-2.0.5/include/pcfdriver.h,
+         libraries/freetype-2.0.5/include/pcferror.h,
+         libraries/freetype-2.0.5/include/pcfutil.h,
+         libraries/freetype-2.0.5/include/psauxerr.h,
+         libraries/freetype-2.0.5/include/psauxmod.h,
+         libraries/freetype-2.0.5/include/psmodule.h,
+         libraries/freetype-2.0.5/include/psnamerr.h,
+         libraries/freetype-2.0.5/include/psobjs.h,
+         libraries/freetype-2.0.5/include/pstables.h,
+         libraries/freetype-2.0.5/include/rasterrs.h,
+         libraries/freetype-2.0.5/include/sfdriver.h,
+         libraries/freetype-2.0.5/include/sferrors.h,
+         libraries/freetype-2.0.5/include/sfobjs.h,
+         libraries/freetype-2.0.5/include/t1afm.h,
+         libraries/freetype-2.0.5/include/t1decode.h,
+         libraries/freetype-2.0.5/include/t1driver.h,
+         libraries/freetype-2.0.5/include/t1errors.h,
+         libraries/freetype-2.0.5/include/t1gload.h,
+         libraries/freetype-2.0.5/include/t1load.h,
+         libraries/freetype-2.0.5/include/t1objs.h,
+         libraries/freetype-2.0.5/include/t1parse.h,
+         libraries/freetype-2.0.5/include/t1tokens.h,
+         libraries/freetype-2.0.5/include/ttcmap.h,
+         libraries/freetype-2.0.5/include/ttdriver.h,
+         libraries/freetype-2.0.5/include/tterrors.h,
+         libraries/freetype-2.0.5/include/ttgload.h,
+         libraries/freetype-2.0.5/include/ttinterp.h,
+         libraries/freetype-2.0.5/include/ttload.h,
+         libraries/freetype-2.0.5/include/ttobjs.h,
+         libraries/freetype-2.0.5/include/ttpload.h,
+         libraries/freetype-2.0.5/include/ttpost.h,
+         libraries/freetype-2.0.5/include/ttsbit.h,
+         libraries/freetype-2.0.5/include/winfnt.h,
+         libraries/freetype-2.0.5/license.txt,
+         libraries/freetype-2.0.5/pcf.c,
+         libraries/freetype-2.0.5/pcfdriver.c,
+         libraries/freetype-2.0.5/pcfread.c,
+         libraries/freetype-2.0.5/pcfutil.c,
+         libraries/freetype-2.0.5/psaux.c,
+         libraries/freetype-2.0.5/psauxmod.c,
+         libraries/freetype-2.0.5/psmodule.c,
+         libraries/freetype-2.0.5/psnames.c,
+         libraries/freetype-2.0.5/psobjs.c,
+         libraries/freetype-2.0.5/raster.c,
+         libraries/freetype-2.0.5/sfdriver.c,
+         libraries/freetype-2.0.5/sfnt.c,
+         libraries/freetype-2.0.5/sfobjs.c,
+         libraries/freetype-2.0.5/smooth.c,
+         libraries/freetype-2.0.5/t1afm.c,
+         libraries/freetype-2.0.5/t1decode.c,
+         libraries/freetype-2.0.5/t1driver.c,
+         libraries/freetype-2.0.5/t1gload.c,
+         libraries/freetype-2.0.5/t1load.c,
+         libraries/freetype-2.0.5/t1objs.c,
+         libraries/freetype-2.0.5/t1parse.c,
+         libraries/freetype-2.0.5/test_bbox.c,
+         libraries/freetype-2.0.5/test_trig.c,
+         libraries/freetype-2.0.5/truetype.c,
+         libraries/freetype-2.0.5/ttcmap.c,
+         libraries/freetype-2.0.5/ttdriver.c,
+         libraries/freetype-2.0.5/ttgload.c,
+         libraries/freetype-2.0.5/ttinterp.c,
+         libraries/freetype-2.0.5/ttload.c,
+         libraries/freetype-2.0.5/ttobjs.c,
+         libraries/freetype-2.0.5/ttpload.c,
+         libraries/freetype-2.0.5/ttpost.c,
+         libraries/freetype-2.0.5/ttsbit.c,
+         libraries/freetype-2.0.5/type1.c,
+         libraries/freetype-2.0.5/type1cid.c,
+         libraries/freetype-2.0.5/winfnt.c, libraries/libart_lgpl-2.3.7,
+         libraries/libart_lgpl-2.3.7/AUTHORS,
+         libraries/libart_lgpl-2.3.7/COPYING,
+         libraries/libart_lgpl-2.3.7/ChangeLog,
+         libraries/libart_lgpl-2.3.7/INSTALL,
+         libraries/libart_lgpl-2.3.7/Makefile,
+         libraries/libart_lgpl-2.3.7/Makefile.am,
+         libraries/libart_lgpl-2.3.7/Makefile.in,
+         libraries/libart_lgpl-2.3.7/NEWS,
+         libraries/libart_lgpl-2.3.7/README,
+         libraries/libart_lgpl-2.3.7/art_affine.c,
+         libraries/libart_lgpl-2.3.7/art_affine.h,
+         libraries/libart_lgpl-2.3.7/art_alphagamma.c,
+         libraries/libart_lgpl-2.3.7/art_alphagamma.h,
+         libraries/libart_lgpl-2.3.7/art_bpath.c,
+         libraries/libart_lgpl-2.3.7/art_bpath.h,
+         libraries/libart_lgpl-2.3.7/art_config.h,
+         libraries/libart_lgpl-2.3.7/art_filterlevel.h,
+         libraries/libart_lgpl-2.3.7/art_gray_svp.c,
+         libraries/libart_lgpl-2.3.7/art_gray_svp.h,
+         libraries/libart_lgpl-2.3.7/art_misc.c,
+         libraries/libart_lgpl-2.3.7/art_misc.h,
+         libraries/libart_lgpl-2.3.7/art_pathcode.h,
+         libraries/libart_lgpl-2.3.7/art_pixbuf.c,
+         libraries/libart_lgpl-2.3.7/art_pixbuf.h,
+         libraries/libart_lgpl-2.3.7/art_point.h,
+         libraries/libart_lgpl-2.3.7/art_rect.c,
+         libraries/libart_lgpl-2.3.7/art_rect.h,
+         libraries/libart_lgpl-2.3.7/art_rect_svp.c,
+         libraries/libart_lgpl-2.3.7/art_rect_svp.h,
+         libraries/libart_lgpl-2.3.7/art_rect_uta.c,
+         libraries/libart_lgpl-2.3.7/art_rect_uta.h,
+         libraries/libart_lgpl-2.3.7/art_render.c,
+         libraries/libart_lgpl-2.3.7/art_render.h,
+         libraries/libart_lgpl-2.3.7/art_render_gradient.c,
+         libraries/libart_lgpl-2.3.7/art_render_gradient.h,
+         libraries/libart_lgpl-2.3.7/art_render_svp.c,
+         libraries/libart_lgpl-2.3.7/art_render_svp.h,
+         libraries/libart_lgpl-2.3.7/art_rgb.c,
+         libraries/libart_lgpl-2.3.7/art_rgb.h,
+         libraries/libart_lgpl-2.3.7/art_rgb_a_affine.c,
+         libraries/libart_lgpl-2.3.7/art_rgb_a_affine.h,
+         libraries/libart_lgpl-2.3.7/art_rgb_affine.c,
+         libraries/libart_lgpl-2.3.7/art_rgb_affine.h,
+         libraries/libart_lgpl-2.3.7/art_rgb_affine_private.c,
+         libraries/libart_lgpl-2.3.7/art_rgb_affine_private.h,
+         libraries/libart_lgpl-2.3.7/art_rgb_bitmap_affine.c,
+         libraries/libart_lgpl-2.3.7/art_rgb_bitmap_affine.h,
+         libraries/libart_lgpl-2.3.7/art_rgb_pixbuf_affine.c,
+         libraries/libart_lgpl-2.3.7/art_rgb_pixbuf_affine.h,
+         libraries/libart_lgpl-2.3.7/art_rgb_rgba_affine.c,
+         libraries/libart_lgpl-2.3.7/art_rgb_rgba_affine.h,
+         libraries/libart_lgpl-2.3.7/art_rgb_svp.c,
+         libraries/libart_lgpl-2.3.7/art_rgb_svp.h,
+         libraries/libart_lgpl-2.3.7/art_rgba.c,
+         libraries/libart_lgpl-2.3.7/art_rgba.h,
+         libraries/libart_lgpl-2.3.7/art_svp.c,
+         libraries/libart_lgpl-2.3.7/art_svp.h,
+         libraries/libart_lgpl-2.3.7/art_svp_intersect.c,
+         libraries/libart_lgpl-2.3.7/art_svp_intersect.h,
+         libraries/libart_lgpl-2.3.7/art_svp_ops.c,
+         libraries/libart_lgpl-2.3.7/art_svp_ops.h,
+         libraries/libart_lgpl-2.3.7/art_svp_point.c,
+         libraries/libart_lgpl-2.3.7/art_svp_point.h,
+         libraries/libart_lgpl-2.3.7/art_svp_render_aa.c,
+         libraries/libart_lgpl-2.3.7/art_svp_render_aa.h,
+         libraries/libart_lgpl-2.3.7/art_svp_vpath.c,
+         libraries/libart_lgpl-2.3.7/art_svp_vpath.h,
+         libraries/libart_lgpl-2.3.7/art_svp_vpath_stroke.c,
+         libraries/libart_lgpl-2.3.7/art_svp_vpath_stroke.h,
+         libraries/libart_lgpl-2.3.7/art_svp_wind.c,
+         libraries/libart_lgpl-2.3.7/art_svp_wind.h,
+         libraries/libart_lgpl-2.3.7/art_uta.c,
+         libraries/libart_lgpl-2.3.7/art_uta.h,
+         libraries/libart_lgpl-2.3.7/art_uta_ops.c,
+         libraries/libart_lgpl-2.3.7/art_uta_ops.h,
+         libraries/libart_lgpl-2.3.7/art_uta_rect.c,
+         libraries/libart_lgpl-2.3.7/art_uta_rect.h,
+         libraries/libart_lgpl-2.3.7/art_uta_svp.c,
+         libraries/libart_lgpl-2.3.7/art_uta_svp.h,
+         libraries/libart_lgpl-2.3.7/art_uta_vpath.c,
+         libraries/libart_lgpl-2.3.7/art_uta_vpath.h,
+         libraries/libart_lgpl-2.3.7/art_vpath.c,
+         libraries/libart_lgpl-2.3.7/art_vpath.h,
+         libraries/libart_lgpl-2.3.7/art_vpath_bpath.c,
+         libraries/libart_lgpl-2.3.7/art_vpath_bpath.h,
+         libraries/libart_lgpl-2.3.7/art_vpath_dash.c,
+         libraries/libart_lgpl-2.3.7/art_vpath_dash.h,
+         libraries/libart_lgpl-2.3.7/art_vpath_svp.c,
+         libraries/libart_lgpl-2.3.7/art_vpath_svp.h,
+         libraries/libart_lgpl-2.3.7/gen_art_config.c,
+         libraries/libart_lgpl-2.3.7/install-sh,
+         libraries/libart_lgpl-2.3.7/libart-2.0.pc.in,
+         libraries/libart_lgpl-2.3.7/libart-config.in,
+         libraries/libart_lgpl-2.3.7/libart-features.c,
+         libraries/libart_lgpl-2.3.7/libart-features.h,
+         libraries/libart_lgpl-2.3.7/libart-features.h.in,
+         libraries/libart_lgpl-2.3.7/libart.h,
+         libraries/libart_lgpl-2.3.7/libart_lgpl_2.la,
+         libraries/libart_lgpl-2.3.7/missing,
+         libraries/libart_lgpl-2.3.7/mkinstalldirs,
+         libraries/libart_lgpl-2.3.7/stamp-h.in,
+         libraries/libart_lgpl-2.3.7/testart.c,
+         libraries/libart_lgpl-2.3.7/testuta.c, libraries/libpng-1.2.0,
+         libraries/libpng-1.2.0-import.txt,
+         libraries/libpng-1.2.0/ANNOUNCE, libraries/libpng-1.2.0/CHANGES,
+         libraries/libpng-1.2.0/INSTALL, libraries/libpng-1.2.0/KNOWNBUG,
+         libraries/libpng-1.2.0/LICENSE, libraries/libpng-1.2.0/Makefile,
+         libraries/libpng-1.2.0/Makefile.am,
+         libraries/libpng-1.2.0/Makefile.in, libraries/libpng-1.2.0/README,
+         libraries/libpng-1.2.0/TODO, libraries/libpng-1.2.0/Y2KINFO,
+         libraries/libpng-1.2.0/example.c, libraries/libpng-1.2.0/libpng.3,
+         libraries/libpng-1.2.0/libpng.txt,
+         libraries/libpng-1.2.0/libpngpf.3, libraries/libpng-1.2.0/png.5,
+         libraries/libpng-1.2.0/png.c, libraries/libpng-1.2.0/png.h,
+         libraries/libpng-1.2.0/pngasmrd.h,
+         libraries/libpng-1.2.0/pngbar.jpg,
+         libraries/libpng-1.2.0/pngbar.png,
+         libraries/libpng-1.2.0/pngconf.h,
+         libraries/libpng-1.2.0/pngerror.c,
+         libraries/libpng-1.2.0/pnggccrd.c,
+         libraries/libpng-1.2.0/pngget.c, libraries/libpng-1.2.0/pngmem.c,
+         libraries/libpng-1.2.0/pngnow.png,
+         libraries/libpng-1.2.0/pngpread.c,
+         libraries/libpng-1.2.0/pngread.c, libraries/libpng-1.2.0/pngrio.c,
+         libraries/libpng-1.2.0/pngrtran.c,
+         libraries/libpng-1.2.0/pngrutil.c,
+         libraries/libpng-1.2.0/pngset.c, libraries/libpng-1.2.0/pngtest.c,
+         libraries/libpng-1.2.0/pngtest.png,
+         libraries/libpng-1.2.0/pngtrans.c,
+         libraries/libpng-1.2.0/pngvcrd.c, libraries/libpng-1.2.0/pngwio.c,
+         libraries/libpng-1.2.0/pngwrite.c,
+         libraries/libpng-1.2.0/pngwtran.c,
+         libraries/libpng-1.2.0/pngwutil.c, src/Makefile.am,
+         src/rrd_format.h, src/rrd_gfx.c, src/rrd_gfx.h, src/rrd_graph.c,
+         src/rrd_graph.h, src/rrd_tool.c, src/rrd_tool.h: The BIG graph
+         update * Replace libgd with libart * Added freetype * Updated zlib
+         and libpng * rrd_gfx.c intrduced as libart wrapper * LINE takes
+         now a float as argument * rrdtool uses truetype for fonts * thanks
+         to libart there is now full alpha transparenc and antialiasing. *
+         the new option --font lets customize the font and size for various
+         graph elements * Updated to -> libtool 1.4.2 automake 2.12
+         autoconf 2.52 * new --zoom commandline option for zoom ans
+         shrinking -- tobias oetiker
+
+2002-01-04 01:11  alex
+
+       * src/rrd_graph.c, src/rrd_graph.h: Reworked rrd_graph_script()
+
+2001-12-24 06:51  alex
+
+       * src/rrd_fetch.c, src/rrd_graph.c, src/rrd_graph.h, src/rrd_tool.c:
+         A patch of size 44Kbytes... in short: Found and repaired the
+         off-by-one error in rrd_fetch_fn(). As a result I had to remove
+         the hacks in rrd_fetch_fn(), rrd_tool.c, vdef_calc(), data_calc(),
+         data_proc() and reduce_data(). There may be other places which I
+         didn't find so be careful. Enhanced debugging in rrd_fetch_fn(),
+         it shows the RRA selection process. Added the ability to print
+         VDEF timestamps. At the moment it is a hack, I needed it now to
+         fix the off-by-one error. If the format string is "%c" (and
+         nothing else!), the time will be printed by both ctime() and as a
+         long int. Moved some code around (slightly altering it) from
+         rrd_graph() initializing now in rrd_graph_init() options parsing
+         now in rrd_graph_options() script parsing now in
+         rrd_graph_script()
+
+2001-12-22 02:49  alex
+
+       * src/rrd_graph.c: Somehow eight lines were missing from function
+         rrd_graph(). Also fixed minor bug in vdef_calc().
+
+2001-12-17 12:48  oetiker
+
+       * src/rrd_fetch.c: fix overflow error ...
+
+2001-12-11 22:55  jake
+
+       * src/rrd_info.c: Fixed uninitialized ptr causing seg fault invoking
+         info for COMPUTE data sources.
+
+2001-11-18 08:41  oetiker
+
+       * src/rrd_cgi.c: return "" instead of NULL for <RRD::GETENV
+         UNKNOWN_VARIABLE> -- Michael <sysadmin@qsl.net>
+
+2001-11-17 16:57  oetiker
+
+       * bindings/Makefile.am: fixed path to mkinstalldirs -- Laurent
+         Saehyun Kim&#65533;<LKim@xo.com>
+
+2001-09-08 18:25  oetiker
+
+       * doc/cdeftutorial.pod, doc/rrdtutorial.pod: spelling updates by
+         Martin Schulze <joey@finlandia.infodrom.north.de>
+
+2001-08-22 22:29  jake
+
+       * doc/rrdtune.pod, src/rrd_create.c, src/rrd_hw.c, src/rrd_hw.h,
+         src/rrd_rpncalc.c, src/rrd_tool.c, src/rrd_tune.c: Contents of
+         this patch: (1) Adds/revises documentation for rrd tune in
+         rrd_tool.c and pod files. (2) Moves some initialization code from
+         rrd_create.c to rrd_hw.c. (3) Adds another pass to smoothing for
+         SEASONAL and DEVSEASONAL RRAs. This pass computes the coefficients
+         as deviations from an average; the average is added the baseline
+         coefficient of HWPREDICT. Statistical texts suggest this to
+         preserve algorithm stability. It will not invalidate RRD files
+         created and smoothed with the old code. (4) Adds the
+         aberrant-reset flag to rrd tune. This operation, which is
+         specified for a single data source, causes the holt-winters
+         algorithm to forget everthing it has learned and start over. (5)
+         Fixes a few out-of-date code comments.
+
+2001-08-13 18:58  oetiker
+
+       * src/rrd_graph.c: spell fix for si units (only in comments)
+
+2001-07-28 22:34  alex
+
+       * doc/rrdgraph.pod, doc/rrdgraph_data.pod,
+         doc/rrdgraph_examples.pod, doc/rrdgraph_graph.pod,
+         doc/rrdgraph_rpn.pod: Removing rrdgraph*.pod from the cvs
+
+2001-07-28 22:21  alex
+
+       * doc/Makefile.am, doc/name.inc, doc/rrdgraph.pod,
+         doc/rrdgraph_graph.src, doc/rrdgraph_rpn.src: Edited Makefile.am
+         and the rrdgraph_*.src files. By mistake the previous update was
+         made on the rrdgraph_*.pod files however they are not to be
+         edited. This is now corrected.
+
+2001-07-26 02:27  alex
+
+       * src/rrd_graph_helper.c, src/rrd_graph_helper.h: Supporting
+         functions for rrd_graph. Functions that are used frequently and/or
+         from different places should be added here in stead of repeating
+         them in rrd_graph.c over and over again.
+
+2001-07-26 02:25  alex
+
+       * src/rrd_graph.h: Moved most typedefs etc. from rrd_graph.c to this
+         file
+
+2001-07-26 02:22  alex
+
+       * src/rrd_graph.c: Added VDEF TOTAL Moved most typedefs etc. to
+         rrd_graph.h
+
+2001-07-26 02:19  alex
+
+       * src/Makefile.am: Added rrd_graph_helper
+
+2001-07-26 02:15  alex
+
+       * doc/rrdgraph_rpn.pod: Updated for VDEF TOTAL
+
+2001-07-26 02:11  alex
+
+       * NEWS: Announce of VDEF in rrd_graph
+
+2001-07-21 18:17  alex
+
+       * doc/rrdgraph_graph.pod, src/rrd_graph.c: Made PRINT and GPRINT
+         aware of VDEF statements
+
+2001-07-20 22:34  oetiker
+
+       * doc/rrdgraph_data.pod, doc/rrdgraph_examples.pod,
+         doc/rrdgraph_graph.pod, doc/rrdgraph_rpn.pod: missing
+         documentation ... -- Alex van den Bogaerdt
+         <alex@slot.hollandcasino.nl>
+
+2001-07-20 22:34  oetiker
+
+       * src/rrd_datalang.c: only a comment
+
+2001-07-20 22:33  oetiker
+
+       * src/rrd_graph.c: small fixes -- Alex van den Bogaerdt
+         <alex@slot.hollandcasino.nl>
+
+2001-07-18 22:30  oetiker
+
+       * src/rrd_graph.c, src/rrd_rpncalc.c: VDEF and VRULE are comig along
+         -- Alex van den Bogaerdt <alex@slot.hollandcasino.nl>
+
+2001-06-05 13:42  oetiker
+
+       * doc/rrdupdate.pod: spell fix
+
+2001-05-09 05:31  oetiker
+
+       * NEWS, doc/rrdcreate.pod, src/rrd_update.c: Bug fix: when update of
+         multiple PDP/CDP RRAs coincided with interpolation of multiple
+         PDPs an incorrect value was stored as the CDP. Especially evident
+         for GAUGE data sources. Minor changes to rrdcreate.pod. -- Jake
+         Brutlag <jakeb@corp.webtv.net>
+
+2001-03-31 15:21  oetiker
+
+       * Makefile.am: added 'all' dependancy to site-perl-install rule as
+         many seem to skip this step when instaling cricket ... probably an
+         incomplete cricket doku.
+
+2001-03-15 19:43  oetiker
+
+       * PROJECTS: added PROJECTS file
+
+2001-03-11 12:03  oetiker
+
+       * src/rrd_rpncalc.c, src/rrd_rpncalc.h: reindented
+
+2001-03-11 11:49  oetiker
+
+       * src/rrd_create.c: reindented the cource for better readability
+
+2001-03-10 23:54  oetiker
+
+       * NEWS, doc/rrdcreate.pod, doc/rrdinfo.pod, doc/rrdtune.pod,
+         doc/rrdupdate.pod, src/Makefile.am, src/rrd_create.c,
+         src/rrd_dump.c, src/rrd_format.c, src/rrd_format.h,
+         src/rrd_graph.c, src/rrd_hw.c, src/rrd_hw.h, src/rrd_info.c,
+         src/rrd_restore.c, src/rrd_rpncalc.c, src/rrd_rpncalc.h,
+         src/rrd_tool.h, src/rrd_update.c: Support for COMPUTE data sources
+         (CDEF data sources). Removes the RPN parser and calculator from
+         rrd_graph and puts then in a new file, rrd_rpncalc.c. Changes to
+         core files rrd_create and rrd_update. Some clean-up of aberrant
+         behavior stuff, including a bug fix. Documentation update
+         (rrdcreate.pod, rrdupdate.pod). Change xml format. -- Jake Brutlag
+         <jakeb@corp.webtv.net>
+
+2001-03-07 21:21  oetiker
+
+       * NEWS, doc, doc/.cvsignore, doc/Makefile.am, doc/name.inc,
+         doc/rrdgraph-old.pod, doc/rrdgraph.pod, doc/rrdgraph.src,
+         doc/rrdgraph_data.src, doc/rrdgraph_examples.src,
+         doc/rrdgraph_graph.src, doc/rrdgraph_rpn.src, doc/see_also.inc,
+         src/rrd_tune.c: complete rewrite of rrdgraph documentation. This
+         also includs info on upcomming/planned changes to the rrdgraph
+         interface and functionality -- Alex van den Bogaerdt
+         <alex@slot.hollandcasino.nl>
+
+2001-03-04 14:06  oetiker
+
+       * trunk/CVSROOT/history: want to keep a history of events
+
+2001-03-04 13:50  oetiker
+
+       * NEWS: fidex jackes adderss
+
+2001-03-04 13:12  oetiker
+
+       * doc/rrdcreate.pod: added note on counter vs derive -- Don BAARDA
+         <don.baarda@baesystems.com>
+
+2001-03-04 13:01  oetiker
+
+       * NEWS, doc/rrdcreate.pod, doc/rrdgraph.pod, doc/rrdtool.pod,
+         doc/rrdtune.pod, src/Makefile.am, src/fnv.h, src/hash_32.c,
+         src/rrd_create.c, src/rrd_dump.c, src/rrd_format.h,
+         src/rrd_graph.c, src/rrd_hw.c, src/rrd_info.c, src/rrd_open.c,
+         src/rrd_restore.c, src/rrd_tool.h, src/rrd_tune.c,
+         src/rrd_update.c, src/rrdupdate.c: Aberrant Behavior Detection
+         support. A brief overview added to rrdtool.pod. Major updates to
+         rrd_update.c, rrd_create.c. Minor update to other core files. This
+         is backwards compatible! But new files using the Aberrant stuff
+         are not readable by old rrdtool versions. See
+         http://cricket.sourceforge.net/aberrant/rrd_hw.htm -- Jake Brutlag
+         <jakeb@corp.webtv.net>
+
+2001-03-04 13:00  oetiker
+
+       * examples, examples/.cvsignore: another do not report Makefile and
+         Makefile.in
+
+2001-03-04 12:51  oetiker
+
+       * src/memtest.c: this file has no purpose
+
+2001-03-04 12:32  oetiker
+
+       * src/rrdupdate.c: rrdupdate is a generated file it has no place in
+         cvs
+
+2001-03-04 11:31  oetiker
+
+       * ., .cvsignore, CHANGES, bindings, bindings/.cvsignore,
+         bindings/perl-piped, bindings/perl-piped/.cvsignore,
+         bindings/perl-shared, bindings/perl-shared/.cvsignore,
+         bindings/tcl, bindings/tcl/.cvsignore, config, config.log,
+         config.status, config/.cvsignore, doc, doc/.cvsignore, libraries,
+         libraries/.cvsignore, libraries/cgilib-0.4,
+         libraries/cgilib-0.4/.cvsignore, libraries/gd1.3,
+         libraries/gd1.3/.cvsignore, libraries/libpng-1.0.9,
+         libraries/libpng-1.0.9/.cvsignore, libraries/zlib-1.1.3,
+         libraries/zlib-1.1.3/.cvsignore, libtool, src, src/.cvsignore:
+         repository cleanup
+
+2001-03-04 11:14  oetiker
+
+       * doc/rrdupdate.pod, src/rrd_tool.c, src/rrd_update.c: added
+         at-style-time@value:value syntax to rrd_update -- Dave Bodenstab
+         <imdave@mcs.net>
+
+2001-03-04 10:29  oetiker
+
+       * src/rrd_open.c: fixed filedescriptor leak -- Mike Franusich
+         <mike@franusich.com>
+
+2001-03-02 22:48  oetiker
+
+       * configure.in, libraries/zlib-1.1.3/Makefile.am: added test for
+         hpux. if matched, compile zlib with -fpic instead of -fPIC
+
+2001-03-01 21:37  oetiker
+
+       * configure.in: added check for the presence of the compiler used to
+         build perl modules
+
+2001-02-25 23:18  oetiker
+
+       * config/config.h, configure, doc/test1.ps, doc/test2.ps: they do
+         not belong into cvs ad they are autogenerated
+
+2001-02-25 22:57  oetiker
+
+       * doc/Makefile.am: Fixed ln for modules
+
+2001-02-25 22:53  oetiker
+
+       * Makefile.am: fixed compile order ... src before bindings
+
+2001-02-25 22:30  oetiker
+
+       * trunk/contrib, trunk/contrib/README, trunk/contrib/add_ds,
+         trunk/contrib/add_ds/README, trunk/contrib/add_ds/add_ds.pl,
+         trunk/contrib/add_ds/batch.pl, trunk/contrib/killspike,
+         trunk/contrib/killspike/README,
+         trunk/contrib/killspike/killspike.pl,
+         trunk/contrib/killspike/killspike.pl.in, trunk/contrib/log2rrd,
+         trunk/contrib/log2rrd/README, trunk/contrib/log2rrd/log2rrd.pl,
+         trunk/contrib/log2rrd/log2rrd.pl.in, trunk/contrib/php3,
+         trunk/contrib/php3/INSTALL, trunk/contrib/php3/Makefile,
+         trunk/contrib/php3/README, trunk/contrib/php3/USAGE,
+         trunk/contrib/php3/VERSION, trunk/contrib/php3/examples,
+         trunk/contrib/php3/examples/rrd_create.php,
+         trunk/contrib/php3/examples/rrd_fetch.php,
+         trunk/contrib/php3/examples/rrd_graph.php,
+         trunk/contrib/php3/examples/rrd_last.php,
+         trunk/contrib/php3/examples/rrd_update.php,
+         trunk/contrib/php3/php3_rrdtool.c,
+         trunk/contrib/php3/php3_rrdtool.h, trunk/contrib/php4,
+         trunk/contrib/php4/.cvsignore, trunk/contrib/php4/INSTALL,
+         trunk/contrib/php4/Makefile.in, trunk/contrib/php4/README,
+         trunk/contrib/php4/USAGE, trunk/contrib/php4/acinclude.m4,
+         trunk/contrib/php4/aclocal.m4, trunk/contrib/php4/build,
+         trunk/contrib/php4/build/dynlib.mk,
+         trunk/contrib/php4/build/fastgen.sh,
+         trunk/contrib/php4/build/library.mk,
+         trunk/contrib/php4/build/ltlib.mk,
+         trunk/contrib/php4/build/program.mk,
+         trunk/contrib/php4/build/rules.mk,
+         trunk/contrib/php4/build/shtool, trunk/contrib/php4/config.guess,
+         trunk/contrib/php4/config.m4, trunk/contrib/php4/config.sub,
+         trunk/contrib/php4/configure, trunk/contrib/php4/configure.in,
+         trunk/contrib/php4/dynlib.m4, trunk/contrib/php4/examples,
+         trunk/contrib/php4/examples/rrd_create.php,
+         trunk/contrib/php4/examples/rrd_fetch.php,
+         trunk/contrib/php4/examples/rrd_graph.php,
+         trunk/contrib/php4/examples/rrd_last.php,
+         trunk/contrib/php4/examples/rrd_update.php,
+         trunk/contrib/php4/install-sh, trunk/contrib/php4/ltconfig,
+         trunk/contrib/php4/ltmain.sh, trunk/contrib/php4/missing,
+         trunk/contrib/php4/mkinstalldirs,
+         trunk/contrib/php4/php_config.h.in,
+         trunk/contrib/php4/php_rrdtool.h, trunk/contrib/php4/rrdtool.c,
+         trunk/contrib/rrd-file-icon, trunk/contrib/rrd-file-icon/README,
+         trunk/contrib/rrd-file-icon/rrd.png, trunk/contrib/rrdexplorer,
+         trunk/contrib/rrdexplorer/README.txt,
+         trunk/contrib/rrdexplorer/map.cgi,
+         trunk/contrib/rrdexplorer/png.cgi, trunk/contrib/rrdfetchnames,
+         trunk/contrib/rrdfetchnames/README,
+         trunk/contrib/rrdfetchnames/rrdfetchnames.pl,
+         trunk/contrib/rrdlastds, trunk/contrib/rrdlastds/README,
+         trunk/contrib/rrdlastds/rrdlastds.pl, trunk/contrib/rrdproc,
+         trunk/contrib/rrdproc/README, trunk/contrib/rrdproc/rrdproc.c,
+         trunk/contrib/rrdview, trunk/contrib/rrdview/README,
+         trunk/contrib/rrdview/rrdview.cgi, trunk/contrib/snmpstats,
+         trunk/contrib/snmpstats/README,
+         trunk/contrib/snmpstats/SNMPstats.pl, trunk/contrib/trytime,
+         trunk/contrib/trytime/.deps,
+         trunk/contrib/trytime/.deps/trytime.P,
+         trunk/contrib/trytime/Makefile, trunk/contrib/trytime/Makefile.am,
+         trunk/contrib/trytime/Makefile.in, trunk/contrib/trytime/README,
+         trunk/contrib/trytime/trytime.c, trunk/website,
+         trunk/website/.img, trunk/website/.img/.imgdot-1x1-transp.gif,
+         trunk/website/.pics, trunk/website/.pics/CAIDAlogo.120.gif,
+         trunk/website/.pics/CAIDAlogo.120.png,
+         trunk/website/.pics/CAIDAlogo.gif,
+         trunk/website/.pics/CAIDAlogo.png,
+         trunk/website/.pics/CAIDAlogo.spons.png,
+         trunk/website/.pics/CAIDAlogo.xpm,
+         trunk/website/.pics/CAIDAlogo2.gif,
+         trunk/website/.pics/CAIDAlogo_small.gif,
+         trunk/website/.pics/b.gif, trunk/website/.pics/b.png,
+         trunk/website/.pics/background.xcf, trunk/website/.pics/bbo.gif,
+         trunk/website/.pics/bbo.png, trunk/website/.pics/bco.gif,
+         trunk/website/.pics/bco.png, trunk/website/.pics/border.gif,
+         trunk/website/.pics/bro.gif, trunk/website/.pics/bro.png,
+         trunk/website/.pics/ecke.gif, trunk/website/.pics/logo-apache.gif,
+         trunk/website/.pics/logo-apache.small.gif,
+         trunk/website/.pics/logo-extra.gif,
+         trunk/website/.pics/logo-linux.gif,
+         trunk/website/.pics/logo-wml.gif, trunk/website/.pics/rrddemo.gif,
+         trunk/website/.pics/rrdlogdemo.gif,
+         trunk/website/.pics/rrdtool.gif, trunk/website/.pics/rrdtool.xar,
+         trunk/website/.pics/unten.gif, trunk/website/.ttf,
+         trunk/website/.ttf/futurab.ttf, trunk/website/.ttf/futurabc.ttf,
+         trunk/website/.ttf/futurabi.ttf, trunk/website/.ttf/futurah.ttf,
+         trunk/website/.ttf/futurahi.ttf, trunk/website/.ttf/futurak.ttf,
+         trunk/website/.ttf/futural.ttf, trunk/website/.ttf/futuralc.ttf,
+         trunk/website/.ttf/futurali.ttf, trunk/website/.ttf/futuram.ttf,
+         trunk/website/.ttf/futuramc.ttf, trunk/website/.ttf/futurami.ttf,
+         trunk/website/.ttf/futuran.ttf, trunk/website/.ttf/futurani.ttf,
+         trunk/website/.ttf/futuraxk.ttf, trunk/website/.ttf/futurbci.ttf,
+         trunk/website/.ttf/futurlci.ttf, trunk/website/.ttf/futurmci.ttf,
+         trunk/website/.ttf/futurxkc.ttf, trunk/website/.ttf/futurxki.ttf,
+         trunk/website/.ttf/futuxkci.ttf, trunk/website/.ttf/trebuc.ttf,
+         trunk/website/.ttf/trebucbd.ttf, trunk/website/.ttf/trebucbi.ttf,
+         trunk/website/.ttf/trebucit.ttf, trunk/website/.ttf/zinjaron.ttf,
+         trunk/website/.wml, trunk/website/.wml/navbar.inc,
+         trunk/website/.wml/nestednavbar.inc,
+         trunk/website/.wml/template.inc, trunk/website/.wml/tobis.inc,
+         trunk/website/.wmlrc, trunk/website/compiling.wml,
+         trunk/website/contributors.wml, trunk/website/developers.wml,
+         trunk/website/download.wml, trunk/website/frontends,
+         trunk/website/frontends/bigsister.wml,
+         trunk/website/frontends/bronc.wml,
+         trunk/website/frontends/cricket.wml,
+         trunk/website/frontends/flowscan.wml,
+         trunk/website/frontends/fwgold.wml,
+         trunk/website/frontends/hoth.wml,
+         trunk/website/frontends/index.wml,
+         trunk/website/frontends/nmis.wml, trunk/website/frontends/nrg.wml,
+         trunk/website/frontends/ntop.wml,
+         trunk/website/frontends/orca.wml,
+         trunk/website/frontends/remstats.wml,
+         trunk/website/frontends/rrgrapher.wml,
+         trunk/website/frontends/slamon.wml, trunk/website/gallery,
+         trunk/website/gallery/alex-01.bash,
+         trunk/website/gallery/alex-01.png,
+         trunk/website/gallery/alex-01.wml,
+         trunk/website/gallery/bkw-01.gif, trunk/website/gallery/bkw-01.pl,
+         trunk/website/gallery/bkw-01.wml,
+         trunk/website/gallery/blair-01-a.png,
+         trunk/website/gallery/blair-01-b.png,
+         trunk/website/gallery/blair-01-c.png,
+         trunk/website/gallery/blair-01-d.png,
+         trunk/website/gallery/blair-01-e.png,
+         trunk/website/gallery/blair-01-f.png,
+         trunk/website/gallery/blair-01.wml,
+         trunk/website/gallery/chris-01.wml,
+         trunk/website/gallery/chris-01temp.png,
+         trunk/website/gallery/chris-01wind.png,
+         trunk/website/gallery/colleen-01.gif,
+         trunk/website/gallery/colleen-01.wml,
+         trunk/website/gallery/edvard-01.png,
+         trunk/website/gallery/edvard-01.wml,
+         trunk/website/gallery/index.wml,
+         trunk/website/gallery/jeff-01.png,
+         trunk/website/gallery/jeff-01.wml,
+         trunk/website/gallery/neal-01.png,
+         trunk/website/gallery/neal-01.wml,
+         trunk/website/gallery/simon-01.png,
+         trunk/website/gallery/simon-01.wml,
+         trunk/website/gallery/steve-01.gif,
+         trunk/website/gallery/steve-01.wml, trunk/website/index.wml,
+         trunk/website/license.wml, trunk/website/mailinglists.wml,
+         trunk/website/manual, trunk/website/manual/bin_dec_hex.wml,
+         trunk/website/manual/cdeftutorial.wml,
+         trunk/website/manual/index.wml,
+         trunk/website/manual/rpntutorial.wml,
+         trunk/website/manual/rrdcgi.wml,
+         trunk/website/manual/rrdcreate.wml,
+         trunk/website/manual/rrddump.wml,
+         trunk/website/manual/rrdfetch.wml,
+         trunk/website/manual/rrdgraph.wml,
+         trunk/website/manual/rrdinfo.wml,
+         trunk/website/manual/rrdlast.wml,
+         trunk/website/manual/rrdresize.wml,
+         trunk/website/manual/rrdrestore.wml,
+         trunk/website/manual/rrdtune.wml,
+         trunk/website/manual/rrdtutorial.es.wml,
+         trunk/website/manual/rrdtutorial.wml,
+         trunk/website/manual/rrdupdate.wml, trunk/website/news.wml,
+         trunk/website/perlbind, trunk/website/perlbind/RRDp.wml,
+         trunk/website/perlbind/RRDs.wml, trunk/website/perlbind/index.wml,
+         trunk/website/rrdcgi.wml, trunk/website/screen.wml,
+         trunk/website/site-sync, trunk/website/tutorial,
+         trunk/website/tutorial/bin_dec_hex.wml,
+         trunk/website/tutorial/cdeftutorial.wml,
+         trunk/website/tutorial/index.wml,
+         trunk/website/tutorial/rpntutorial.wml,
+         trunk/website/tutorial/rrdtutorial.es.wml,
+         trunk/website/tutorial/rrdtutorial.ps,
+         trunk/website/tutorial/rrdtutorial.wml: Initial revision
+
+2001-02-25 22:25  oetiker
+
+       * ., 00README, CHANGES, CONTRIBUTORS, COPYING, COPYRIGHT,
+         MakeMakefile, Makefile.am, NT-BUILD-TIPS.txt, README, TODO,
+         bindings, bindings/Makefile.am, bindings/perl-piped,
+         bindings/perl-piped/MANIFEST, bindings/perl-piped/Makefile.PL,
+         bindings/perl-piped/README, bindings/perl-piped/RRDp.pm,
+         bindings/perl-piped/leaktest.pl, bindings/perl-piped/rrdpl.dsp,
+         bindings/perl-piped/rrdpl.dsw, bindings/perl-piped/t,
+         bindings/perl-piped/t/base.t, bindings/perl-shared,
+         bindings/perl-shared/MANIFEST, bindings/perl-shared/Makefile.PL,
+         bindings/perl-shared/README, bindings/perl-shared/RRDs.pm,
+         bindings/perl-shared/RRDs.xs, bindings/perl-shared/ntmake.pl,
+         bindings/perl-shared/rrdpl.dsp, bindings/perl-shared/rrdpl.dsw,
+         bindings/perl-shared/t, bindings/perl-shared/t/base.t,
+         bindings/tcl, bindings/tcl/Makefile.am, bindings/tcl/README,
+         bindings/tcl/ifOctets.tcl, bindings/tcl/tclrrd.c, config,
+         config.log, config.status, config/Makefile.am, config/acconfig.h,
+         config/aclocal.m4, config/config.guess, config/config.h,
+         config/config.h.in, config/config.sub, config/install-sh,
+         config/libtool, config/libtool/libtool.m4, config/ltconfig,
+         config/ltmain.sh, config/missing, config/mkinstalldirs,
+         config/stamp-h, config/stamp-h.in, configure, configure.in, doc,
+         doc/Makefile.am, doc/bin_dec_hex.pod, doc/cdeftutorial.pod,
+         doc/rpntutorial.pod, doc/rrdcgi.pod, doc/rrdcreate.pod,
+         doc/rrddump.pod, doc/rrdfetch.pod, doc/rrdgraph.pod,
+         doc/rrdinfo.pod, doc/rrdlast.pod, doc/rrdresize.pod,
+         doc/rrdrestore.pod, doc/rrdtool.pod, doc/rrdtune.pod,
+         doc/rrdtutorial.es.pod, doc/rrdtutorial.pod, doc/rrdupdate.pod,
+         doc/test1.ps, doc/test2.ps, examples, examples/4charts.pl.in,
+         examples/Makefile.am, examples/bigtops.pl, examples/bigtops.pl.in,
+         examples/cgi-demo.cgi, examples/cgi-demo.cgi.in,
+         examples/minmax.pl, examples/minmax.pl.in, examples/piped-demo.pl,
+         examples/piped-demo.pl.in, examples/shared-demo.pl,
+         examples/shared-demo.pl.in, examples/stripes.pl,
+         examples/stripes.pl.in, libraries, libraries/Makefile.am,
+         libraries/cgilib-0.4, libraries/cgilib-0.4/Makefile.am,
+         libraries/cgilib-0.4/cgi.5, libraries/cgilib-0.4/cgi.c,
+         libraries/cgilib-0.4/cgi.h, libraries/cgilib-0.4/cgiDebug.3,
+         libraries/cgilib-0.4/cgiGetValue.3,
+         libraries/cgilib-0.4/cgiHeader.3, libraries/cgilib-0.4/cgiInit.3,
+         libraries/cgilib-0.4/cgiRedirect.3,
+         libraries/cgilib-0.4/cgilib.dsp, libraries/cgilib-0.4/cgilib.dsw,
+         libraries/cgilib-0.4/cgitest.c, libraries/cgilib-0.4/jumpto.c,
+         libraries/cgilib-0.4/readme, libraries/gd1.3,
+         libraries/gd1.3/Makefile.am, libraries/gd1.3/README.rrdtool,
+         libraries/gd1.3/demoin.gif, libraries/gd1.3/gd.c,
+         libraries/gd1.3/gd.dsp, libraries/gd1.3/gd.dsw,
+         libraries/gd1.3/gd.h, libraries/gd1.3/gddemo.c,
+         libraries/gd1.3/gdfontg.c, libraries/gd1.3/gdfontg.h,
+         libraries/gd1.3/gdfontl.c, libraries/gd1.3/gdfontl.h,
+         libraries/gd1.3/gdfontmb.c, libraries/gd1.3/gdfontmb.h,
+         libraries/gd1.3/gdfonts.c, libraries/gd1.3/gdfonts.h,
+         libraries/gd1.3/gdfontt.c, libraries/gd1.3/gdfontt.h,
+         libraries/gd1.3/gdlucidab10.c, libraries/gd1.3/gdlucidab10.h,
+         libraries/gd1.3/gdlucidab12.c, libraries/gd1.3/gdlucidab12.h,
+         libraries/gd1.3/gdlucidab14.c, libraries/gd1.3/gdlucidab14.h,
+         libraries/gd1.3/gdlucidan10.c, libraries/gd1.3/gdlucidan10.h,
+         libraries/gd1.3/gdlucidan12.c, libraries/gd1.3/gdlucidan12.h,
+         libraries/gd1.3/gdlucidan14.c, libraries/gd1.3/gdlucidan14.h,
+         libraries/gd1.3/giftogd.c, libraries/gd1.3/index.html,
+         libraries/gd1.3/mathmake.c, libraries/gd1.3/mtables.c,
+         libraries/gd1.3/readme.txt, libraries/gd1.3/webgif.c,
+         libraries/libpng-1.0.9, libraries/libpng-1.0.9/ANNOUNCE,
+         libraries/libpng-1.0.9/CHANGES, libraries/libpng-1.0.9/INSTALL,
+         libraries/libpng-1.0.9/KNOWNBUG, libraries/libpng-1.0.9/LICENSE,
+         libraries/libpng-1.0.9/Makefil,
+         libraries/libpng-1.0.9/Makefile.am, libraries/libpng-1.0.9/README,
+         libraries/libpng-1.0.9/README.rrdtool,
+         libraries/libpng-1.0.9/TODO, libraries/libpng-1.0.9/Y2KINFO,
+         libraries/libpng-1.0.9/example.c, libraries/libpng-1.0.9/libpng.3,
+         libraries/libpng-1.0.9/libpng.txt,
+         libraries/libpng-1.0.9/libpngpf.3, libraries/libpng-1.0.9/png.5,
+         libraries/libpng-1.0.9/png.c, libraries/libpng-1.0.9/png.dsp,
+         libraries/libpng-1.0.9/png.dsw, libraries/libpng-1.0.9/png.h,
+         libraries/libpng-1.0.9/pngasmrd.h,
+         libraries/libpng-1.0.9/pngbar.jpg,
+         libraries/libpng-1.0.9/pngbar.png,
+         libraries/libpng-1.0.9/pngconf.h,
+         libraries/libpng-1.0.9/pngerror.c,
+         libraries/libpng-1.0.9/pnggccrd.c,
+         libraries/libpng-1.0.9/pngget.c, libraries/libpng-1.0.9/pngmem.c,
+         libraries/libpng-1.0.9/pngnow.png,
+         libraries/libpng-1.0.9/pngpread.c,
+         libraries/libpng-1.0.9/pngread.c, libraries/libpng-1.0.9/pngrio.c,
+         libraries/libpng-1.0.9/pngrtran.c,
+         libraries/libpng-1.0.9/pngrutil.c,
+         libraries/libpng-1.0.9/pngset.c, libraries/libpng-1.0.9/pngtest.c,
+         libraries/libpng-1.0.9/pngtest.png,
+         libraries/libpng-1.0.9/pngtrans.c,
+         libraries/libpng-1.0.9/pngvcrd.c, libraries/libpng-1.0.9/pngwio.c,
+         libraries/libpng-1.0.9/pngwrite.c,
+         libraries/libpng-1.0.9/pngwtran.c,
+         libraries/libpng-1.0.9/pngwutil.c, libraries/zlib-1.1.3,
+         libraries/zlib-1.1.3/ChangeLog, libraries/zlib-1.1.3/FAQ,
+         libraries/zlib-1.1.3/INDEX, libraries/zlib-1.1.3/Makefile.am,
+         libraries/zlib-1.1.3/README, libraries/zlib-1.1.3/README.rrdtool,
+         libraries/zlib-1.1.3/adler32.c,
+         libraries/zlib-1.1.3/algorithm.txt,
+         libraries/zlib-1.1.3/compress.c, libraries/zlib-1.1.3/crc32.c,
+         libraries/zlib-1.1.3/deflate.c, libraries/zlib-1.1.3/deflate.h,
+         libraries/zlib-1.1.3/descrip.mms, libraries/zlib-1.1.3/example.c,
+         libraries/zlib-1.1.3/gzio.c, libraries/zlib-1.1.3/infblock.c,
+         libraries/zlib-1.1.3/infblock.h, libraries/zlib-1.1.3/infcodes.c,
+         libraries/zlib-1.1.3/infcodes.h, libraries/zlib-1.1.3/inffast.c,
+         libraries/zlib-1.1.3/inffast.h, libraries/zlib-1.1.3/inffixed.h,
+         libraries/zlib-1.1.3/inflate.c, libraries/zlib-1.1.3/inftrees.c,
+         libraries/zlib-1.1.3/inftrees.h, libraries/zlib-1.1.3/infutil.c,
+         libraries/zlib-1.1.3/infutil.h, libraries/zlib-1.1.3/maketree.c,
+         libraries/zlib-1.1.3/minigzip.c, libraries/zlib-1.1.3/trees.c,
+         libraries/zlib-1.1.3/trees.h, libraries/zlib-1.1.3/uncompr.c,
+         libraries/zlib-1.1.3/zconf.h, libraries/zlib-1.1.3/zlib.3,
+         libraries/zlib-1.1.3/zlib.dsp, libraries/zlib-1.1.3/zlib.dsw,
+         libraries/zlib-1.1.3/zlib.h, libraries/zlib-1.1.3/zutil.c,
+         libraries/zlib-1.1.3/zutil.h, libtool, rrdtool.spec, src,
+         src/Makefile.am, src/gdpng.c, src/getopt.c, src/getopt.h,
+         src/getopt1.c, src/gifsize.c, src/memtest.c, src/ntconfig.h,
+         src/parsetime.c, src/parsetime.h, src/pngsize.c, src/rd_cgi.dsp,
+         src/rrd.dsp, src/rrd.dsw, src/rrd.h, src/rrd_cgi.c,
+         src/rrd_cgi.dsp, src/rrd_create.c, src/rrd_diff.c, src/rrd_dump.c,
+         src/rrd_error.c, src/rrd_fetch.c, src/rrd_format.c,
+         src/rrd_format.h, src/rrd_graph.c, src/rrd_info.c, src/rrd_last.c,
+         src/rrd_open.c, src/rrd_resize.c, src/rrd_restore.c,
+         src/rrd_stat.c, src/rrd_tool.c, src/rrd_tool.h, src/rrd_tune.c,
+         src/rrd_update.c, src/rrdtool.dsp, src/rrdtool.dsw,
+         src/rrdupdate.c: Initial revision
+
+2001-02-25 16:09  oetiker
+
+       * trunk/rrdtool, trunk/rrdtool/README: Initial revision
+
+2001-02-25 15:29  cvs
+
+       * trunk/CVSROOT, trunk/CVSROOT/checkoutlist,
+         trunk/CVSROOT/commitinfo, trunk/CVSROOT/config,
+         trunk/CVSROOT/cvswrappers, trunk/CVSROOT/editinfo,
+         trunk/CVSROOT/loginfo, trunk/CVSROOT/modules,
+         trunk/CVSROOT/notify, trunk/CVSROOT/rcsinfo,
+         trunk/CVSROOT/taginfo, trunk/CVSROOT/verifymsg: initial checkin
+
+2001-02-25 15:29  
+
+       * branches, tags, trunk: New repository initialized by cvs2svn.
+
diff --git a/program/CONTRIBUTORS b/program/CONTRIBUTORS
new file mode 100644 (file)
index 0000000..6c651eb
--- /dev/null
@@ -0,0 +1,93 @@
+$Id$
+
+I would like to thank to following people for helping to
+bring RRDtool into existence.
+
+Alan Lichty <alan_lichty with eli.net> 
+Alan Milligan <alan.milligan@last-bastion.net> Python bindings
+Alex van den Bogaerdt <alex with ergens.op.het.net> (rrd_resize.c and more)
+Amos Shapira <amos with gezernet.co.il>
+Andreas Kroomaa <andre with ml.ee>
+Andrew Turner <turner with mint.net> (LAST consolidator)
+Benny Baumann <benbe with geshi.org) rrd_dump with callback support
+Bernard Fischer <bfischer with syslog.ch> 64bit stuff, --alt-autoscale-max
+Bernhard Fischer <rep dot dot dot nop with gmail.com> MMAP rewrite
+Bill Fenner <fenner with research.att.com>
+Blair Zajac <bzajac with geostaff.com>
+Bruce Campbell <bruce.campbell with apnic.net>
+Chin-A-Young <china with thewrittenword.com>
+Christophe VG <Christophe.VanGinneken with ubizen.com>
+Christophe Van Ginneken <Christophe.VanGinneken with ubizen.com> (--no-legend)
+Dan Dunn <dandunn with computer.org>
+Daniel Pocock <daniel in pocock.com.au> rrd_create to mmap port
+Dave Bodenstab <dave@bodenstab.org> AT style time in update, tclfixes
+David Grimes <dgrimes with navisite.com> SQRT/SORT/REV/SHIFT/TREND
+David L. Barker <dave with ncomtech.com> xport function bug fixes
+Evan Miller <emiller with imvu.com> Multiplicative HW Enhancements
+Frank Strauss <strauss with escape.de> TCL bindings
+Florian octo Forster <rrdtool nospam.verplant.org> rrd_restore libxml2 rewrite, deprecated function export, rrdcached
+Fidelis Assis <fidelis pobox.com> lua bindings
+Henrik Storner <henrik with hswn.dk> functions for min/max values of data in graph
+Hermann Hueni <hueni with glue.ch> (SunOS porting)
+Jakob Ilves <jilves with se.oracle.com> HPUX 11
+Jeff R. Allen <jeff.allen with acm.org> (autoconfigure, portability)  
+Jeremy Fischer <jeremy with pobox.com> (Makefile changes & RPM builds)
+Jesús Couto Fandiño
+Joel Becker <jlbec with raleigh.ibm.com> AIX
+Joey Miller <joeym with inficad.com>php3 and php4 bindings
+Jost.Krieger <Jost.Krieger with ruhr-uni-bochum.de>
+Kai Siering <kai.siering with mediaways.net>
+Kevin Brintnall <kbrint with rufus.net> bugfixes in and additions to rrdcached, including journaling support
+Larry Leszczynski <larryl with furph.com>
+Mark Plaksin <happy@usg.edu> rrd_graph_v
+Matt Chambers <matthew.chambers with vanderbilt.edu> --full-size-mode for rrdgraph
+Melchior Rabe <rrdtool at mrab.de> -- legend position patch
+McCreary mccreary with xoanon.colorado.edu
+Mike Mitchell <mcm with unx.sas.com>
+Mike Slifcak <slif with bellsouth.net> many rrdtool-1.1.x fixes
+Oleg Cherevko <olwi with icyb.kiev.ua>
+Otmar Lendl <O.Lendl with Austria.EU.net> (lots of bugfixes)
+Pablo Sanchez <pablo at blueoakdb.com> (CDEF vs VDEF)
+Patrick Cherry <patrick with bytemark.co.uk>
+Paul Joslin <Paul.Joslin with sdrc.com>
+Peter Speck <speck with vitality.dk> eps/svg/pdf file format code in rrdtool-1.x
+Peter Stamfest <peter with stamfest.at> initial multi-thread support
+Peter Breitenlohner <peb with mppmu.mpg.de>  many patches for rrdtool 1.2.x
+Philippe.Simonet <Philippe.Simonet with swisscom.com> (NT porting)
+Poul-Henning Kamp <phk with freebsd.org> CDEF enhancements
+REIBENSCHUH Alfred <alfred.reibenschuh with it-austria.com> AIX
+Radoslaw Karas <rkaras with tyndall.ie>
+Rainer Bawidamann <Rainer.Bawidamann with informatik.uni-ulm.de>
+Roman Hoogant <rhoogant with ee.ethz.ch>
+Ronan Mullally <ronan in 4L.ie>
+Roger J. Meier <roger.meier in terreactive.ch> (arbitrary linelength in rrdtool)
+Russ Wright <rwwright with home.com>
+Sean Summers <sean with Fenstermaker.com> (RPM .spec)
+Selena M Brewington <smbrewin with ichips.intel.com> add_ds
+Shane O'Donnell <shaneo with opennms.org>
+Simon Leinen <simon with switch.ch>
+Steen Linden <Steen.Linden with ebone.net>
+Sebastian Harl <sh at tokkee.org> debian packaging, rrdcached fixes and ehancements
+Stefan Ludewig <Stefan.Ludewig at exitgames.com> 1.3 WIN32 Port
+Stefan Mueller <s.mueller with computer.org> HPUX 11
+Steve Harris <steveh with wesley.com.au> AIX portability
+Steve Rader <rader with teak.wiscnet.net> (rrd_cgi debugging and LAST)
+Terminator rAT <karl_schilke with eli.net>
+Tobias Weingartner <weingart with cs.ualberta.ca>
+Thomas Gutzler <thomas.gutzler with gmail.com> dashed lines
+Tom Crawley <Tom.Crawley with hi.riotinto.com.au> (GCC&HP configuration)
+Travis Brown <tebrown with csh.rit.edu> 
+Tuc <ttsg with ttsg.com>
+Ulf Lilleengen <lulf with pvv.ntnu.no> Python binding for 'rrdtool first'
+Ulrich Schilling <schilling with netz.uni-essen.de> AIX
+Wim Heirman <wim.heirman elis.ugent.be> --units=si option
+Wolfgang Schrimm <wschrimm with uni-hd.de> xport function
+Wrolf Courtney <wrolf with wrolf.net> (HP-UX)
+hendrik visage <hvisage with is.co.za>
+Vytautas Zdanavicius <vytaszd@yahoo.com> -- python argument list exander
+Martin Sperl <rrdtool martin.sperl.org> (CDEF prediction functions, libdbi)
+Philippe Simonet <philippe.simonet with swisscom.ch> (Windows Binaries)
+Alexander Lucke (lucke with dns-net.de) of DNS:NET Internet Services (www.dns-net.de) http://rrdtool.org
+Hedley Simons <heds@metahusky.net>
+Nicola Worthington <nicolaw@cpan.org>
+Wegmann, Christof <Christof.Wegmann@exitgames.com> 1.3/trunk win32 port
diff --git a/program/COPYING b/program/COPYING
new file mode 100644 (file)
index 0000000..37c6105
--- /dev/null
@@ -0,0 +1,280 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for non-commercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
diff --git a/program/COPYRIGHT b/program/COPYRIGHT
new file mode 100644 (file)
index 0000000..8c01b8a
--- /dev/null
@@ -0,0 +1,91 @@
+$Id$
+RRDTOOL - Round Robin Database Tool
+A tool for fast logging of numerical data graphical display
+of this data.
+
+Copyright (c) 1998-2009 Tobias Oetiker
+All rights reserved.
+
+GNU GPL License
+===============
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+
+FLOSS License Exception 
+=======================
+(Adapted from http://www.mysql.com/company/legal/licensing/foss-exception.html)
+
+I want specified Free/Libre and Open Source Software ("FLOSS")
+applications to be able to use specified GPL-licensed RRDtool
+libraries (the "Program") despite the fact that not all FLOSS licenses are
+compatible with version 2 of the GNU General Public License (the "GPL").
+
+As a special exception to the terms and conditions of version 2.0 of the GPL:
+
+You are free to distribute a Derivative Work that is formed entirely from
+the Program and one or more works (each, a "FLOSS Work") licensed under one
+or more of the licenses listed below, as long as:
+
+1. You obey the GPL in all respects for the Program and the Derivative
+Work, except for identifiable sections of the Derivative Work which are
+not derived from the Program, and which can reasonably be considered
+independent and separate works in themselves,
+
+2. all identifiable sections of the Derivative Work which are not derived
+from the Program, and which can reasonably be considered independent and
+separate works in themselves,
+
+1. are distributed subject to one of the FLOSS licenses listed
+below, and
+
+2. the object code or executable form of those sections are
+accompanied by the complete corresponding machine-readable source
+code for those sections on the same medium and under the same FLOSS
+license as the corresponding object code or executable forms of
+those sections, and
+
+3. any works which are aggregated with the Program or with a Derivative
+Work on a volume of a storage or distribution medium in accordance with
+the GPL, can reasonably be considered independent and separate works in
+themselves which are not derivatives of either the Program, a Derivative
+Work or a FLOSS Work.
+
+If the above conditions are not met, then the Program may only be copied,
+modified, distributed or used under the terms and conditions of the GPL.
+
+FLOSS License List
+==================
+License name   Version(s)/Copyright Date
+Academic Free License          2.0
+Apache Software License        1.0/1.1/2.0
+Apple Public Source License    2.0
+Artistic license               From Perl 5.8.0
+BSD license                    "July 22 1999"
+Common Public License          1.0
+GNU Library or "Lesser" General Public License (LGPL)  2.0/2.1
+IBM Public License, Version    1.0
+Jabber Open Source License     1.0
+MIT License (As listed in file MIT-License.txt)        -
+Mozilla Public License (MPL)   1.0/1.1
+Open Software License          2.0
+OpenSSL license (with original SSLeay license) "2003" ("1998")
+PHP License                    3.0
+Python license (CNRI Python License)   -
+Python Software Foundation License     2.1.1
+Sleepycat License              "1999"
+W3C License                    "2001"
+X11 License                    "2001"
+Zlib/libpng License            -
+Zope Public License            2.0/2.1
diff --git a/program/MakeMakefile b/program/MakeMakefile
new file mode 100755 (executable)
index 0000000..90e4700
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/sh
+#
+# Run this script after the first cvs checkout to build
+# makefiles and friends
+
+PATH="/usr/pack/automake-1.11-to/amd64-linux-debian3.1/:/usr/pack/automake-1.11-to/amd64-linux-ubuntu8.04/bin:$PATH"
+export PATH
+
+vcheck (){
+  perl <<PERL
+@t = split /\./, "$1";
+@v = map { int \$_ } split /\./, (split /\s+/, \`$2\`)[3];
+print "$2 = ", (join ".",@v), "  (expecting $1 or later)\n";
+\$v = \$t[0]*1000000+\$t[1]*1000+\$t[2] <= \$v[0]*1000000+\$v[1]*1000+\$v[2];
+exit \$v
+PERL
+}
+
+ERROR=0
+LIBTOOL_VER="1.5.6"
+AUTOMAKE_VER="1.9.2"
+AUTOCONF_VER="2.59"
+
+if vcheck $LIBTOOL_VER "libtool --version"
+then
+  echo "get a copy of GNU libtool >= $LIBTOOL_VER"
+  ERROR=1
+fi
+
+if vcheck $AUTOMAKE_VER  "automake  --version"
+then
+  if vcheck $AUTOMAKE_VER  "automake-1.11 --version"
+  then
+    echo "get a copy of GNU automake >= $AUTOMAKE_VER"
+    ERROR=1
+  else
+    AUTOMAKE="automake-1.11"
+    ACLOCAL="aclocal-1.11"
+    export AUTOMAKE ACLOCAL
+  fi
+fi
+
+
+if vcheck $AUTOCONF_VER "autoconf --version"
+then
+  echo "get a copy of GNU autoconf >= $autoconf_ver"
+  ERROR=1
+fi
+
+if [ $ERROR -ne 0 ]
+then
+  exit 1
+fi
+
+./autogen.sh
+
+# vim: set syntax=sh :
diff --git a/program/Makefile.am b/program/Makefile.am
new file mode 100644 (file)
index 0000000..1961ef2
--- /dev/null
@@ -0,0 +1,49 @@
+# $Id$
+
+## Process this file with automake to produce Makefile.in
+RSYNC = rsync --rsh=ssh
+
+# build the following subdirectories
+
+SUBDIRS = po src examples doc bindings
+
+ # the following files are not mentioned in any other Makefile
+EXTRA_DIST = COPYRIGHT CHANGES WIN32-BUILD-TIPS.txt TODO CONTRIBUTORS THREADS \
+            rrdtool.spec favicon.ico  win32/Makefile win32/config.h autogen.sh \
+             win32/rrd.dsp win32/rrd.sln win32/rrd.vcproj libtool \
+             win32/rrd_config.h.msvc win32/rrdlib.vcproj win32/rrdtool.dsp \
+             win32/rrdtool.dsw win32/rrdtool.vcproj netware/Makefile  \
+             etc/rrdcached-default etc/rrdcached-init
+
+            
+
+CLEANFILES = config.cache
+
+# use relaxed rules when building dists
+AUTOMAKE_OPTIONS= foreign 
+
+# where we keep local rules for automake
+ACLOCAL_AMFLAGS=-I m4
+ACLOCAL_M4= $(top_srcdir)/aclocal.m4
+#AUTOHEADER = @AUTOHEADER@ --localdir=$(top_srcdir)/config
+#AUTOCONF = @AUTOCONF@ --localdir=$(top_srcdir)/config
+
+#      $(RSYNC) CHANGES archive/$(PACKAGE)-$(VERSION).tar.gz tobi@ipn.caida.org:/ipn/web/Tools/RRDtool/pub/
+
+site-perl-inst: site-perl-install
+
+site-perl-install: all bindings/perl-piped/Makefile bindings/perl-shared/Makefile
+       cd bindings/perl-piped && $(MAKE) install
+       cd bindings/perl-shared && $(MAKE) install
+
+site-tcl-install: all
+       cd bindings/tcl && $(MAKE) tcl-install
+
+site-python-install: all
+       cd bindings/python && $(PYTHON) setup.py install $(if $(DESTDIR),--root=$(DESTDIR))
+
+# find . -name "*.c" -or -name "*.h" | xargs perl -0777 -n -e 'while (s/typedef\s+(?:unsigned\s+|signed\s+|unival\s+)?\S+\s+\*?([^{}\s;(]+)//){print "-T$1\n"}'
+indent:
+       find ./ -name "*.[ch]" | xargs indent
+
+##END##
diff --git a/program/NEWS b/program/NEWS
new file mode 100644 (file)
index 0000000..bac4d12
--- /dev/null
@@ -0,0 +1,240 @@
+#####################################
+Major Changes between 1.3.x and 1.4.x
+-------------------------------------
+$Id$
+
+RRD Caching Daemon (rrdcached)
+------------------------------
+by Florian Forster and Kevin Brintnall
+
+The RRD Caching Daemon can dramatically improve the 'update' performance
+of your system.  Due to file handling overheads, the time it takes todo one
+update is virtually the same as to doing two updates in a row.
+
+The Cache Daemon intercepts rrdtool update calls, assembling multiple
+updates before writing them to the actual rrd file. When calling rrdtool
+graph in such a setup, the command will tell the daemon to flush out all
+pending updates for the rrd files, required to draw the graph.
+
+See rrdcached documentation.
+
+RRD Dumping and Restoring (rrdtool dump/restore)
+------------------------------------------------
+by Tobi Oetiker
+
+The output of rrdtool dump has been adjusted to be simpler to parse by
+existing xml parsers.
+
+The restore core has been completely re-written yet again and is now relying
+on an incremental xml parser. This has the advantage that the memory
+consumption while restoring xml files is only slightly larger than the
+resulting rrd file. Which is much less than the requirements of the 1.3 and
+even 1.2
+
+RRD Graphing functions (rrdtool graph)
+--------------------------------------
+by Martin Sperl
+
+* VDEF PERCENTNAN (a PRECENT that ignores NAN)
+
+* CDEF PREDICT and PREDICTSIGMA functions for on-the-fly
+  data prediction without the need to modify existing rrd files as it is
+  required for HoltWinters.
+
+* LibDBI integration provides a path to read data directly of a supported
+  SQL database into rrdtool graph. See rrdgraph_libdbi documentation.
+
+Miscellaneous Changes
+---------------------
+* graph legends can now be placed left, right or above the graph with the
+  new --legend-direction and --legend-positon placement options. 
+  by Melchior Rabe
+
+* switched to using automake 1.11 which provides a 'silent' build process,
+  causing errors and warnings to stand out much more than before.
+  by Tobi Oetiker
+
+* switched from intltoolize to autopoint for the i18n configuration.
+  by Tobi Oetiker
+
+* new graph option --grid-dash on:off to configure the dash length
+  in the grid painted over the graph by Tobi Oetiker
+
+* lua bindings for rrdtool
+  by Fidelis Assis
+
+* various improvements to rrd_open functions and mmap handling
+  by Daniel Pocock
+
+* allow the HW smoothing window size to be set to 0 with rrdtool tune
+  by sylvain luiset
+
+* new graph option --border to set the 3d border width 
+  by Bernhard Reutner-Fischer
+
+* draw different color markers (enable with --dynamic-labels) depending on the
+  type of element in the graph by Loïc Tortay
+
+for more detail see the CHANGES file.
+
+#####################################
+Major Changes between 1.2.x and 1.3.x
+-------------------------------------
+
+NEW Fast file access methods (Bernhard Fischer / Tobi Oetiker)
+----------------------------
+* introduced file-accessor functions rrd_read/rrd_seek/rrd_write
+
+* implemented full mmap-based file access with madvise hints for
+  improved scalability, much reduced memory-footprint and much less
+  blocking while accessing the disk
+
+* implemented optional full file-descriptor access instead of FILE*
+  access
+
+NEW Graphing (Tobi Oetiker)
+------------
+* libart has been replaced by cairo/pango
+
+* pango markup is supported (--pango-markup)
+
+* full grid fitting
+
+* --graph-render-mode=mono for non anti aliased graphing
+
+* --font-render-mode=mono for non anti aliased fonts
+
+* fonts come through fontconfig, use the Pango font naming scheme
+  -> 'Times 20' ... it is not possible to use true-type fonts
+  directly anymore.
+
+* Tabs are position independent.
+
+* TRENDNAN filter that ignores NAN values while calculating the
+  TREND data. (Timo Stripf)
+
+* --full-size-mode to specify the outer border of the image and not
+  just of the graphing canvas (Matthew Chambers)
+
+* TEXTALIGN command to alter default text alignment behavior
+
+* C API in-memory graphing with rrd_graph_v (Evan Miller)
+
+* draw dashed lines in graphs (Thomas Gutzler)
+
+* new interface graphv which returns information using the rrd_info
+  interface (Tobi Oetiker and Mark Plaksin)
+
+* improved horizontal grid. Have a bit more grid lines and y-axis
+  labels while keeping them far enough apart to not run into each
+  other.
+
+NEW Forecasting (Evan Miller)
+---------------
+* the new MHWPREDICT consolidation function uses a variation of the
+  Holt-Winters method. It is a drop-in replacement for HWPREDICT,
+  and is better suited for data whose seasonal variations grow or
+  shrink in proportion to the average.
+
+* If you create an RRD with the new MHWPREDICT function, the
+  resulting rrd file will be version 0004 and can only be used in
+  rrdtool 1.3.
+
+Rewrites
+--------
+* rrd_restore now uses libxml for parsing which makes things much
+  more tolerant towards xml variations. The old code could mostly
+  just parse the XML as it was output by rrdtool dump. See also:
+  the note at the bottom of this document. (by Florian octo
+  Forster)
+
+* rrd_update rewritten to make it more modular. Fixed two
+  longstanding HW bugs in the process (Evan Miller)
+
+Internationalization (Takao Fujiwara and Tobi Oetiker)
+--------------------
+* The help output by rrdtool has been internationalized. There are
+  no real translations included with rrdtool yet, contributions are
+  welcome.
+
+* The internationalization will only be compiled if libintl and
+  friends are available on your system. Use the configure option
+  --disable-libintl if you want to disable this feature
+
+Language Bindings
+-----------------
+* ruby rrd_fetch will return step as a last property -- Mike Perham
+
+RRDtool dump / restore incompatibility
+--------------------------------------
+* rrdtool dump 1.3 does emit completely legal XML. Basically this
+  means that it contains an XML header and a DOCTYPE definition.
+  Unfortunately this causes older versions of rrdtool restore to be
+  unhappy.
+
+* To restore a new dump with an old rrdtool restore version, either
+  remove the XML header and the doctype by hand (both on the first
+  line of the dump) or use rrdtool dump --no-header.
+
+
+######################################################################################
+Major Changes between 1.0.x and 1.2.x
+======================================================================================
+Graphing
+--------
+
+* rewritten graphics generation based on libart.
+  - anti-aliased output
+  - alpha transparency support
+  - truetype fonts
+* additional graphics formats: EPS, PDF, SVG
+
+* extended multi-part documentation
+
+* VDEF support; define and use variables.  Find, and use, the
+  maximum rate seen by rrdtool; compute and show the average
+
+* Sliding window (trend) analysis
+  Compute a smoother average, for instance over the last 6 CDPs
+
+* percentile (95th or other)
+  Remove peaks, 95 percent of all rates are at or below the
+  returned value
+
+Logging
+-------
+* a second logging interface: rrdtool updatev
+  Verbose updating of the database; show CPDs being created
+
+* Aberrant Behavior Detection with Holt-Winters Forecasting
+  Compare current data with expected data, detect and log when
+  the rates are outside expected levels
+
+* COMPUTE data type for artificial data-sources calculating their
+  input using RPN math and data from the other data-sources.
+Incompatibilities
+-----------------
+* Colons in COMMENT arguments to rrdtool graph must be escaped with a backslash
+
+* the --alt-y-mrtg option is gone or rather since 1.2.7 it is back but
+  without functionality.
+
+* In pipe mode, rrdtool answers with OK only if it was successful with the
+  command. Otherwhise the answer will be ERROR...
+
+
+Behind the Scenes
+-----------------
+* In order to support Holt-Winters and Calculated Datasources,
+  the rrdtool data format has changed. While the new version of rrdtool can
+  read files created with rrdtool 1.0.x. It is not possible to read files
+  created by rrdtool-1.2.x with rrdtool-1.0.x
+
+* External libraries are not included with rrdtool anymore. This is in line
+  with todays trend of using shared libraries everywhere. With the exception
+  of the cgi library most things required by rrdtool will be found on every recent
+  system.
+
+* Memory Mapped IO support for faster logging.
diff --git a/program/README b/program/README
new file mode 100644 (file)
index 0000000..1ba71f6
--- /dev/null
@@ -0,0 +1,76 @@
+Round Robin Database Tools
+==========================
+$Id$
+
+It is pretty easy to gather status information from all sorts of things,
+ranging from the temperature in your office to the number of octets which
+have passed through the FDDI interface of your router. But it is not so
+trivial to store this data in a efficient and systematic manner. This is
+where RRDtool kicks in. It lets you log and analyze the data you gather from
+all kinds of data-sources (DS). The data analysis part of RRDtool is based
+on the ability to quickly generate graphical representations of the data
+values collected over a definable time period.
+
+
+To compile:
+-----------
+
+check out the instructions in doc/rrdbuild.txt
+
+Getting Started:
+----------------
+
+Either after compiling or after installing you can try the example
+RRDtool applications in the examples directory.
+
+To learn:
+---------
+
+Read the documentation in the doc directory. Start of with
+RRDtool. All documents are available as html and as ASCII text.  
+
+If you are looking for a more slow paced introduction, make sure to read
+Alex van den Bogaerdt's rrdtutorial which is also available from the doc
+directory. Also read his cdeftutorial and Steve Rader's rpntutorial.
+If you want to know about the format of the log files check
+src/rrd_format.h there are a lot of comments in there ...
+
+How to make Tobi happy:
+-----------------------
+
+If you want to show your appreciation for RRDtool you could make me happy
+by going to http://tobi.oetiker.ch/wish and ordering a CD from
+my CD wish list ... 
+
+
+How to keep in touch:
+---------------------
+
+There are 3 Mailing lists for RRDtool:
+
+rrd-announce   LOW volume RRDtool Announcements List (Only Stable Releases)
+rrd-users       For discussion amongst people who use RRDtool in their applications
+rrd-developers  For people who actually HACK RRDtool code
+
+To subscribe to <MAILGLIST> send a message with the subject 'subscribe'
+to <MAILGLIST>-request@lists.oetiker.ch
+
+Note, that postings to rrd-announce will always be cross-posted 
+to rrd-users and rrd-developers as well.
+
+To Contribute:
+--------------
+
+Contributed feature and bug patches are most welcome. But please
+send complete patches. A complete patch patches the CODE as well
+as the CHANGES, CONTRIBUTORS and the POD files.
+
+Use GNU   diff --unified --recursive olddir newdir   to build your patches.
+
+The latest Version:
+-------------------
+Is available from http://oss.oetiker.ch/rrdtool/
+
+
+Tobias Oetiker <tobi@oetiker.ch>
diff --git a/program/THREADS b/program/THREADS
new file mode 100644 (file)
index 0000000..b048163
--- /dev/null
@@ -0,0 +1,60 @@
+In order to use the librrd in multi-threaded programs you must:
+
+  * Link with librrd_th instead of with librrd
+  * Use the *_r function instead or the *-functions
+  * Never use non *_r functions unless it is explicitly documented that the 
+    function is tread-safe
+
+Every thread SHOULD call rrd_get_context() before the first call to
+any librrd function in order to set up thread specific data. This is
+not strictly required, but it is the only way to test if memory
+allocation can be done by this function. Otherwise the program may die
+with a SIGSEGV in a low-memory situation.
+
+
+IMPORTANT NOTE FOR RRD CONTRIBUTORS:
+
+Some precautions must be followed when developing rrd from now on:
+
+* Only use thread-safe functions in library code. Many often used libc
+  functions aren't thread-safe. Take care if you want to use any of
+  the following functions:
+
+    + direct strerror calls must be avoided: use rrd_strerror instead,
+      it provides a per-thread error message
+    + the getpw*, getgr*, gethost* function families (and some more get*
+      functions): use the *_r variants
+    + Time functions: asctime, ctime, gmtime, localtime: use *_r variants
+    + strtok: use strtok_r
+    + tmpnam: use tmpnam_r
+    + many other (lookup documentation)
+
+As an aide(?) a header file named "rrd_is_thread_safe.h" is provided
+that works with the GNU C-preprocessor to "poison" some of the most
+common non-thread-safe functions using the "#pragma GCC poison"
+directive. Just include this header in source files you want to keep
+thread-safe.
+    
+* Do not introduce global variables!
+
+  If you really, really have to use a global variable you may add a 
+  new field to the rrd_context structure and modify rrd_error.c,
+  rrd_thread_safe.c and rrd_non_thread_safe.c
+
+* Do not use "getopt" or "getopt_long" in *_r (directly or indirectly)
+
+  getopt uses global variables and behaves badly in a multithreaded
+  application when called concurrently. Instead provide a *_r function
+  taking all options as function parameters. You may provide argc and
+  **argv arguments for variable length argument lists. See
+  rrd_update_r as an example. 
+
+* Do not use the parsetime function!
+
+  It uses lots of global vars. You may use it in functions not
+  designed to be thread-safe like functions wrapping the _r version of some
+  operation (eg. rrd_create, but not in rrd_create_r)
+
+WIN32 Platform Note (added 04/01/03):
+
+Both rrdtool.vcproj (MSVC++ 7.0) and rrd.dsw (MSVC++ 6.0) are configured to compile with rrd_thread_safe_nt.c.
diff --git a/program/TODO b/program/TODO
new file mode 100644 (file)
index 0000000..706727f
--- /dev/null
@@ -0,0 +1,18 @@
+Random Feature Ideas for RRDtool
+--------------------------------
+$Id$
+
+make it possible to define order of legend items independant of their order
+on the commandline ...
+
+architecture independant storage format.
+
+micro second precision
+
+2036 stable time
+
+add configurable counter wrap
+
+align data points not to GMT but some free offset
+
+allow starting through symlinks called rrdcreate rrdtune and the like
diff --git a/program/WIN32-BUILD-TIPS.txt b/program/WIN32-BUILD-TIPS.txt
new file mode 100644 (file)
index 0000000..b3fae96
--- /dev/null
@@ -0,0 +1,391 @@
+Compiling RRDtool on Win32 with Microsoft Visual C++:
+---------------------------------------------------------------
+2008-03-12 Stefan Ludewig stefan.ludewig@exitgames.com
+
+Here are step by step instructions for building rrdlib.lib and rrdtool.exe
+version 1.3.5 and newer with Microsoft Visual Studio 2008 (9.0.x).
+
+(1) Download and extract libraries rrdtool depends on:
+
+    - cairo:    http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/cairo_1.8.0-1_win32.zip
+      and       http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/cairo-dev_1.8.0-1_win32.zip
+
+    - glib:     http://ftp.gnome.org/pub/gnome/binaries/win32/glib/2.18/glib_2.18.3-1_win32.zip
+      and       http://ftp.gnome.org/pub/gnome/binaries/win32/glib/2.18/glib-dev_2.18.3-1_win32.zip
+
+    - libpng:   http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libpng_1.2.32-1_win32.zip
+      and       http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libpng-dev_1.2.32-1_win32.zip
+
+    - libxml2:  http://downloads.sourceforge.net/gnuwin32/libxml2-2.4.12-bin.zip?modtime=1009062000&big_mirror=1
+      and       http://downloads.sourceforge.net/gnuwin32/libxml2-2.4.12-1-lib.zip?modtime=1024783200&big_mirror=1
+
+    - pango:    http://ftp.gnome.org/pub/gnome/binaries/win32/pango/1.22/pango_1.22.2-1_win32.zip
+      and       http://ftp.gnome.org/pub/gnome/binaries/win32/pango/1.22/pango-dev_1.22.2-1_win32.zip
+
+    - zlib: http://www.zlib.net/zlib123-dll.zip
+
+(2) Create a folder named "contrib" next to your rrdtool-folder.
+
+(3) Copy the following folders and files from the downloaded archieves into
+    the contrib-folder, you just created:
+
+    a) The folder "bin" from cairo_1.8.0-1_win32 and the folders "lib" and
+       "include" from cairo-dev_1.8.0-1_win32 into contrib/cairo.
+
+    b) The folder "bin" from glib_2.18.3-1_win32 and the folders "lib" and
+       "include" from glib-dev_2.18.3-1_win32 into contrib/glib
+
+    c) The folder "bin" from libpng_1.2.32-1_win32 and the folders "lib" and
+       "include" from libpng-dev_1.2.32-1_win32 into contrib/libpng
+
+    d) The file "libxml2.dll" from libxml2-2.4.12-bin/bin and the file
+       "libxml2.lib" from libxml2-2.4.12-1-lib\lib into contrib/libxml2/lib and
+       the folder "include" from from libxml2-2.4.12-1-lib into contrib/libxml2
+
+    e) The folder "bin" from pango_1.22.2-1_win32 and the folders "lib" and
+       "include" from pango-dev_1.22.2-1_win32 into contrib/pango
+
+    f) The folders "lib" and "include" and the file "zlib1.dll" into
+       contrib/zlib
+
+(4) Open the Visual Studio 2008 Solution "rrd.sln" in the win32 folder of
+    your rrdtool-folder and build either the project rrdlib (for the
+    rrdtool-library), rrdtool (for the rrdtool-executable depending on the
+    libraray) or the complete solution. A post-build event automatically copies
+    all the dlls, needed by rrdtool, next to the .exe, when you build the
+    executable. These DLLs must be available on all hosts where rrdtool will
+    run.
+
+5/1/05 Tobi
+to help windows deal with the reentrant versions of many unix
+calls link with win32comp.c
+
+4/10/05 Tobi
+The windows implementation of strftime does not seem to support
+the ISO 8601 week number (%V) I have therfore included the file
+strftime.[ch] which provides strftime_ ... if you compile rrdtool
+with -Dstrftime=_strftime and link strftime.o then you will
+get propper support for %V.
+
+7/29/04 Jake Brutlag
+
+As of Jan 2004, code for libraries utilized by rrdtool 
+(png, libart, freetype, and zlib) is no longer distributed with
+rrdtool. This requires some changes to the compile process on
+Win32. The solution described here is to compile rrdtool to
+link against these libraries dynamically. There is an advantage
+to this approach: namely the rrdtool distribution doesn't have to
+worry about how to compile these libraries on Win32. In theory,
+since others already provide and maintain Win32 binaries for these
+libraries the users don't have to worry about how to compile them
+either. The disadvantage of this approach is that the DLLs for
+these libraries must be available on the hosts where rrdtool will run.
+
+Here are step by step instructions for compiling rrdtool.exe and
+the perl shared library (RRDS.dll) with Microsoft Visual C++ 6.0.
+(1) Download libraries rrdtool depends on from GnuWin32:
+http://gnuwin32.sourceforge.net/
+For freetype, libpng, and zlib download the "Complete Package"; each of
+these will be a self-extracting self-installing executable.
+For libart, download both the "Binaries" and "Developer Files" packages.
+Unfortunately at this time GnuWin32 doesn't provide the "Complete Package"
+installer for libart. Perhaps by the time you are following these
+instructions GnuWin32 will have a "Complete Package" for libart.
+(2) Install the GnuWin32 libraries by running the executables for freetype,
+libpng, and zlib. These instructions and the Visual C++ project files
+distributed with rrdtool assume that you will use the default install
+location: C:\Program Files\GnuWin32. Extract the two zip files for libart,
+libart-2.3.3-bin.zip and libart-2.3.3-1-lib.zip into the GnuWin32 directory;
+the appropriate libart files will be added to the include, lib, and bin
+subdirectories.
+(3) Add C:\Program Files\GnuWin32\bin to the PATH (Control Panel ->
+System -> Advanced -> Environment Variables).
+(4) Start Microsoft Visual C++ 6.0. Load the workspace file, rrdtool.dsw,
+from the src subdirectory of your rrdtool code directory.
+(5) Compile the Release build of the rrdtool project (since rrdtool depends
+on the rrd project, the rrd library will also be compiled). At this
+time, the compile will fail in zconf.h, a zlib header file. The problem
+is a preprocessor directive that loads unistd.h. Open zconf.h in VC++
+(this file is in C:\Program Files\GnuWin32\include) and find the following
+code block:
+
+#if 1           /* HAVE_UNISTD_H -- this line is updated by ./configure */
+#  include <sys/types.h> /* for off_t */
+#  include <unistd.h>    /* for SEEK_* and off_t */
+#  ifdef VMS
+#    include <unixio.h>   /* for off_t */
+#  endif
+#  define z_off_t  off_t
+#endif 
+
+Change it to reads as follows (this is code from zlib-1.1.4):
+
+#if HAVE_UNISTD_H
+#  include <sys/types.h> /* for off_t */
+#  include <unistd.h>    /* for SEEK_* and off_t */
+#  ifdef VMS
+#    include <unixio.h>   /* for off_t */
+#  endif
+#  define z_off_t  off_t
+#endif 
+
+Note that it is actually just a one line change. Save the file and
+recompile rrdtool. By the time you are following these instructions
+this issue with zconf.h may be resolved.
+(6) At this point, you can run the executable rrdtool.exe in the
+src\toolrelease subdirectory. Note that if you wish to run rrdtool
+on other machines, you will need the following DLLs installed (on the
+path) on those machines:
+zlib1.dll
+libpng12.dll
+libart_lgpl.dll
+freetype6.dll
+msvcrt.dll
+The names of the first four DLLs might vary from what is listed here
+depending on the versions of the packages you downloaded from GnuWin32.
+The fifth DLL, msvcrt.dll, is a system DLL for most versions of Windows.
+If you are running on old version of Windows, you can install/upgrade to
+IE4.0 to get this DLL.
+(7) To compile the perl-shared library, open a Command Prompt (DOS box)
+and cd to the bindings\perl-shared subdirectory.
+(8) Run vcvars32.bat; this batch file, in your vc98\bin directory will
+set necessary environment options for command line compiling.
+(9) In bindings\perl-shared, run
+perl ntmake-build
+nmake
+nmake test
+If nmake test succeeds, you are good to go. RRDs.dll is in 
+blib\arch\auto\RRDs. If you plan to install via the Active State ppm
+tool, tar and gzip the blib directory. You can use the RRDs.ppd file
+in bindings\perl-shared directory. Remember that as in the case of
+rrdtool.exe you will need the DLLs listed in (6) on the machine where
+you are going to use RRDs.dll.
+
+Microsoft Visual C++ 7.1 (.NET 2003):
+
+Unfortunately, this is more difficult than with VC++ 6.0. The problem
+is that by default the C runtime dll for VC++ 7.1 is msvcr71.dll rather
+than msvcrt.dll. The GnuWin32 library binaries are all compiled
+to use msvcrt.dll and you can't mix msvcr71.dll and msvcrt.dll in the
+same process. One option is to download the source code for the libraries
+(available from http://gnuwin32.sourceforge.net) recompile them with
+VC++ 7.l. Then all the components will use msvcr71.dll. Once you are
+going to go this route, you can also use static multi-threaded libraries
+and use static linking between rrdtool (or RRDs.dll) and its dependencies.
+
+To use the GnuWin32 library binaries, you need to trick VC++ 7.1 into
+compiling rrdtool to use the older msvcrt.dll. Follow steps (1) - (3)
+as above, then:
+(4) Obtain a different version of the msvcrt.lib import library that
+is compatible with vc7 and points to msvcrt.dll:
+msvcrtlib_for_vc7.zip from http://xchat.org/win32/testing
+Backup msvcrt.lib in your vc7\lib directory 
+(\Program Files\Microsoft Visual Studio .NET 2003\vc7\lib)
+Then extract the msvcrt.lib from the zip file into the vc7\lib directory.
+WARNING: Use this msvcrt.lib at your own risk! This is not a Microsoft
+supplied file nor a file supported by anyone associated with rrdtool.
+(5) Start Microsoft Visual C++ 7.1. Load the solution file, rrdtool.sln,
+from the src subdirectory of your rrdtool code directory. Edit zconf.h,
+as needed, as described under (5) above. Compile the release build of
+the rrdtool project.
+Proceed with steps (6) - (9) as above, if you are using/picking up
+the wrong msvcrt.lib import library then nmake test for perl-shared
+will fail.
+
+Note: it is possible in the future that GnuWin32 will provide Win32
+binaries that utilize msvcr71.dll rather than msvcrt.dll.
+
+5/14/02 Jake Brutlag
+
+These notes share some insight I gained compiling 1.1.x with
+MS Visual C++ 6.0 (using project files). This information may or
+may not be accurate at the time you are reading this.
+
+(1) freetype and rrdtool cannot use precompiled headers (which are
+enabled by default for MSVC++ projects).  MSVC++ 6.0 does not
+support precompiled headers if #include directives contain MACROS.
+(2) Compile Release build with Default optimization, not the
+Maximize Speed optimization. I encountered some strange errors
+(related to argument processing for complex commands like graph--
+perhaps the getopt stuff is too blame) with Maximize Speed.
+(3) libart relies upon config.h (ostensibly generated by the
+configure script-- but of course not on Win32 platforms). ..\..\confignt
+(which contains a static Win32 version of config.h) should be on
+the include path.
+(4) Fonts are located in the %windir%\fonts, so the default font
+is c:\winnt\fonts\cour.ttf. (6/19/02) At Kerry Calvert's suggestion
+this setting was moved to confignt\config.h.
+(5) libart requires a custom build step to generate art_config.h; this
+is done manually via the commands:
+cl -I..\..\confignt gen_art_config.c
+gen_art_config.exe > art_config.h
+
+Currently, to compile rrd.lib and rrdtool.exe using
+the MSVC++ project files, first start MSVC++ 6.0. Open the rrdtool
+workspace (rrdtool.dsw in the src directory). The active project/
+configuration should be rrdtool-Win32 Release. Select Rebuild All
+from the Build menu. The static link library (rrd.lib) will
+be generated in src\release directory and executable will be generated
+in the src\toolrelease directory.
+
+Compiling RRDtool on NT ... work in progress
+---------------------------------------------------------------
+                         by Tamas Kovacshazy (khazy@mit.bme.hu)
+
+Persisting Problems with the current NT port:
+
+Unfortunately, the RRD perl modules does not work with Perl
+(ActivePerl) using the current distribution.
+
+The RRD shared perl module can be compiled after some
+modification...
+
+Follow these steps:
+
+0. Install perl if you do not have it!
+   Visit http://www.ActiveState.com/pw32/ for a complete distribution.
+
+1. Copy ..\gd1.2\release\gd.lib  to ..\gd1.2\
+2. Copy ..\src\release\rrd.lib to ..\src
+3. perl Makefile.pl
+
+In this step the system complains about something I do not
+understand. The error message is the following:
+
+Note (probably harmless): No library found for '-lm'
+
+Is a library missing? But it does not stop with an error...
+
+4. nmake test (You must have Visual C++ on the machine!)
+
+After these steps it generates the test files (svgs and rrds),
+and they seem to be good.
+
+The real problem in the shared perl modul is the following:
+
+I do not know how this installation stuff works. The problem is
+that the installation stuff looks for the gd.lib and the
+rrd.lib in the ..\gd1.2 and ..\src directory. The UNIX compile
+puts the files into these directories, but the NT compile does
+not.
+
+It is all for today,
+
+khazy
+
+Tamas Kovacshazy  E-mail: khazy@mit.bme.hu  
+WWW: http://www.mit.bme.hu/~khazy
+Technical University of Budapest 
+Department of Measurement and Information Systems
+
+
+Compiling RRDtool 1.2.x on Win32 with MingW32 gcc:
+---------------------------------------------------------------
+
+1. Obtain and install the current version of the MingW package.
+
+     http://www.mingw.org/download.shtml
+
+   In the MinGW set you will need the gcc and binutils as a minimum.
+
+2. Obtain either of the following awk versions and install in a directory
+   on your System Path:
+
+   - awk.exe
+
+     http://cm.bell-labs.com/cm/cs/awkbook/index.html
+
+     Note: This version has no dependencies to other libs.
+
+   - gawk.exe  (GnuWin32 version)
+
+     http://gnuwin32.sourceforge.net/packages/gawk.htm
+
+     Note: Also fetch the dependant libraries for it from the same page.
+
+3. If you plan to create a 'distribution' release of the RRD Tools, the
+   Makefile.Win32 will copy all the needed files to an output directory and
+   then zip the entire directory. A suitable zip utility can be obtained here:
+
+     http://www.info-zip.org/
+
+   Install in a directory on your System Path.
+
+4. Obtain the following libraries, ideally install them all under a common
+   directory:
+
+   = zlib
+
+     http://oss.oetiker.ch/rrdtool/pub/libs/zlib-1.2.3.tar.gz
+     http://www.zlib.net/
+
+   = libpng
+
+     http://oss.oetiker.ch/rrdtool/pub/libs/libpng-1.2.12.tar.gz
+     http://libpng.sourceforge.net/
+
+   = freetype
+
+     http://oss.oetiker.ch/rrdtool/pub/libs/freetype-2.2.1.tar.gz
+     http://freetype.sourceforge.net/index2.html
+   = libart_lgpl
+
+     http://oss.oetiker.ch/rrdtool/pub/libs/libart_lgpl-2.3.17.tar.gz
+     http://www.levien.com/libart/
+
+     Note: libart_lgpl needs a special tweak because the archive contains
+     only the base directory, but the libart headers are usually included with
+     a directory prefix; therefore create a subfolder 'libart_lgpl' and move
+     all files into this subfolder.
+
+5. Set up for DOS environment.
+
+   Add MingW\bin and MSYS\bin directories to your System path.
+
+   If the libraries share a common directory set the following environment var:
+
+     set LIBBASE=<shared director>
+     e.g set LIBBASE=C:\Libraries
+
+   If the libraries are scattered, set the following environment vers:
+
+     set ZLIBSDK=<path to zlib>
+     e.g set ZLIBSDK=C:\mytest\zlib-1.2.3
+     set LIBPNG=<path to libpng>
+     set LIBFT2=<path to freetype>
+     set LIBART=<path to libart>
+
+   If using the Gnu Awk (gawk.exe), edit the Makefile.Win32 and change the line:
+
+      AWK      = awk
+
+   to
+
+      AWK      = gawk
+
+6. Compile the project.
+
+   All dependent libs are statically linked in. This has the benefit that the
+   binaries do not depend on any other DLLs.
+   In order to build the static freetype lib enter the freetype base directory
+   and type 'make'. If everything is fine a message appears that gcc is detected,
+   and that you should again type 'make'. Follow that in order to build freetype.
+   All other libs are build from the sources with the RRDTool Makefile.Win32.
+
+   Switch to the RRDTOOL .\src directory. Then:
+
+      make -f Makefile.Win32 help
+
+   to see the build options, or
+
+      make -f Makefile.Win32 all
+
+   should build the entire package.
+
+6. Happy Graphing!
+
+
+written by normw & gk.
+
+
diff --git a/program/autogen.sh b/program/autogen.sh
new file mode 100755 (executable)
index 0000000..a7226cc
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+libtoolize
+autoreconf --force --install --verbose -I m4
diff --git a/program/bindings/Makefile.am b/program/bindings/Makefile.am
new file mode 100644 (file)
index 0000000..c4fdc10
--- /dev/null
@@ -0,0 +1,59 @@
+.PHONY: python ruby
+
+if BUILD_TCL
+SUB_tcl = tcl
+endif
+if BUILD_LUA
+SUB_lua = lua
+endif
+
+SUBDIRS = $(SUB_tcl) $(SUB_lua)
+
+# the following files are not mentioned in any other Makefile
+EXTRA_DIST = perl-piped/MANIFEST perl-piped/README perl-piped/Makefile.PL perl-piped/RRDp.pm perl-piped/t/base.t \
+            perl-shared/ntmake-build perl-shared/MANIFEST perl-shared/README perl-shared/Makefile.PL perl-shared/RRDs.pm  perl-shared/RRDs.xs perl-shared/t/base.t \
+            ruby/CHANGES     ruby/README      ruby/extconf.rb  ruby/main.c      ruby/test.rb \
+             python/ACKNOWLEDGEMENT python/AUTHORS python/COPYING python/README python/rrdtoolmodule.c python/setup.py
+
+
+# add the following to the all target
+all-local:  @COMP_PERL@ @COMP_RUBY@ @COMP_PYTHON@
+
+install-data-local:
+       $(AM_V_GEN)test -f perl-piped/Makefile && cd perl-piped && $(MAKE) install || true
+       $(AM_V_GEN)test -f perl-shared/Makefile && cd perl-shared && $(MAKE) install || true
+       $(AM_V_GEN)test -f ruby/Makefile && cd ruby && $(MAKE) EPREFIX=$(DESTDIR)$(exec_prefix) $(RUBY_MAKE_OPTIONS) install || true
+       $(AM_V_GEN)test -d python/build && cd python && env BUILDLIBDIR=../../src/.libs $(PYTHON) setup.py install --skip-build --prefix=$(DESTDIR)$(prefix) --exec-prefix=$(DESTDIR)$(exec_prefix) || true
+
+# rules for building the ruby module
+# RUBYARCHDIR= is to work around in a makefile quirk not sure 
+# it is is the right thing todo, but it makes rrdtool build on freebsd as well
+ruby:   Makefile
+       cd ruby && $(RUBY) extconf.rb && $(MAKE) EPREFIX=$(exec_prefix) $(RUBY_MAKE_OPTIONS) RUBYARCHDIR=
+
+# rules for building the pyton module
+python: Makefile
+       cd python && env BUILDLIBDIR=../../src/.libs $(PYTHON) setup.py build_ext --rpath=$(libdir) && env LIBDIR=../../src/.libs $(PYTHON) setup.py build
+
+# rules for building the perl module
+perl_piped: perl-piped/Makefile
+       cd perl-piped && $(MAKE)
+
+perl-piped/Makefile: perl-piped/Makefile.PL
+       cd perl-piped && $(PERL) Makefile.PL $(PERL_MAKE_OPTIONS)
+
+perl_shared: perl-shared/Makefile
+       cd perl-shared && $(MAKE)
+
+perl-shared/Makefile: perl-shared/Makefile.PL Makefile
+       cd perl-shared && $(PERL) Makefile.PL $(PERLFLAGS) $(PERL_MAKE_OPTIONS) RPATH=$(libdir)
+# LIBS="$(LDFLAGS) $(LIBS)" $(PERLFLAGS) $(PERL_MAKE_OPTIONS)
+
+clean-local:
+       test -f perl-piped/Makefile && cd perl-piped && $(MAKE) clean || true
+       test -f perl-piped/Makefile && rm perl-piped/Makefile || true
+       test -f perl-shared/Makefile && cd perl-shared && $(MAKE) clean || true
+       test -f perl-shared/Makefile && rm -f perl-shared/Makefile || true 
+       test -f ruby/Makefile && cd ruby && $(MAKE) clean && rm Makefile || true 
+       test -d python/build && cd python &&  rm -rf build || true
+##END##
diff --git a/program/bindings/lua/Makefile.am b/program/bindings/lua/Makefile.am
new file mode 100644 (file)
index 0000000..f1389d6
--- /dev/null
@@ -0,0 +1,66 @@
+# These files are not mentioned in any other Makefile
+# compat-5.1.lua is only necessary for Lua 5.0 in distros where
+# it's not already installed.
+EXTRA_DIST = README test.lua.bottom compat-5.1r5/compat-5.1.lua
+
+LIB_VERSION_INFO=0:0:0
+
+LUA                 = @LUA@
+LUA_INSTALL_CMOD    = @LUA_INSTALL_CMOD@
+LUA_INSTALL_LMOD    = @LUA_INSTALL_LMOD@
+LUA_DEFINES         = @LUA_DEFINES@
+LUA_CFLAGS          = @LUA_CFLAGS@
+LUA_LFLAGS          = @LUA_LFLAGS@
+
+CLEANFILES = rrd.o rrd.so test.lua test.rrd test.png
+
+lualibdir=$(LUA_INSTALL_CMOD)
+lualib_LTLIBRARIES = rrd.la
+if LUA_NEED_OUR_COMPAT51
+  rrd_la_SOURCES = rrdlua.c compat-5.1r5/compat-5.1.c compat-5.1r5/compat-5.1.h
+  compatdir = $(LUA_INSTALL_LMOD)
+  compat_DATA = compat-5.1r5/compat-5.1.lua
+else
+  rrd_la_SOURCES = rrdlua.c
+endif
+rrd_la_LIBADD = -lrrd -lm
+rrd_la_LDFLAGS = -module -version-info $(LIB_VERSION_INFO) $(LUA_LFLAGS) -L$(top_builddir)/src/.libs
+rrd_la_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir) $(LUA_CFLAGS) $(LUA_DEFINES) -DLIB_VERSION=\"$(LIB_VERSION)\"
+
+.PHONY: test.lua
+
+test.lua: test.lua.bottom
+       @echo "-- Created by Makefile." > test.lua
+       @echo "-- Test script adapted from the one in the Ruby binding." > test.lua
+       @echo >> test.lua
+if LUA50
+       @echo "--- compat-5.1.lua is only required for Lua 5.0 ----------" >> test.lua
+if LUA_NEED_OUR_COMPAT51
+if !LUA_SITE_LINSTALL
+       @echo "original_LUA_PATH = LUA_PATH" >> test.lua
+       @echo "-- try only compat-5.1.lua installed with RRDtool" >> test.lua
+       @echo "LUA_PATH = '$(LUA_INSTALL_LMOD)/?.lua'" >> test.lua
+endif
+endif
+       @echo "local r = pcall(require, 'compat-5.1')" >> test.lua
+       @echo "if not r then" >> test.lua
+       @echo "  print('** compat-5.1.lua not found')" >> test.lua
+       @echo "  os.exit(1)" >> test.lua
+       @echo "end" >> test.lua
+if LUA_NEED_OUR_COMPAT51
+if !LUA_SITE_LINSTALL
+       @echo "LUA_PATH = original_LUA_PATH" >> test.lua
+       @echo "original_LUA_PATH = nil" >> test.lua
+endif
+endif
+       @echo "----------------------------------------------------------" >> test.lua
+       @echo >> test.lua
+endif
+if !LUA_SITE_CINSTALL
+       @echo "package.cpath = '$(LUA_INSTALL_CMOD)/?.so;' .. package.cpath" >> test.lua
+endif
+       @cat test.lua.bottom >> test.lua
+
+test: test.lua
+       $(LUA) test.lua
+
diff --git a/program/bindings/lua/README b/program/bindings/lua/README
new file mode 100644 (file)
index 0000000..a42339d
--- /dev/null
@@ -0,0 +1,68 @@
+RRDLua is a Lua module for RRD functions.
+
+- Configuration
+
+  From the top dir of RRDtool package, run "./configure", or
+  "./configure --enable-lua-site-install" if you prefer to install in
+  Lua's search path.
+
+ You should have lua 5.0, or superior, and respective lua-dev packages
+ installed before executing configure.
+
+- Compilation and installation
+
+ Run 'make' and 'sudo make install'. If you don't enable lua-site-install,
+ the Lua modules will be installed together with RRDtool, under the subdir
+ <INSTALL_PREFIX>lib/lua/<lua_version>.
+
+- Testing
+
+ Install RRDtool first, as above. Then, enter the bindings/lua dir, run
+ 'make test' and use your preferred viewer to display the just created
+ 'test.png'. If you can read "Enjoy Lua RRDtool module!" on the picture,
+ everything went fine.
+
+- Using with Lua 5.1
+
+ Start your programs with:
+
+  ------..-----------------------------------------------------------
+  package.cpath = '<INSTALL_PREFIX/lib/lua/5.1/?.so;' ..
+                  package.cpath
+  require 'rrd'
+  -------------------------------------------------------------------
+
+ OBS: If you use the option --enable-lua-site-install you won't need
+      to change package.cpath like above. 
+
+- Using with Lua 5.0
+
+ The Lua binding for RRDtool needs the compat-5.1 module to work with
+ Lua 5.0. Some Linux distros, like Ubuntu gutsy and hardy, have it
+ already integrated in Lua 5.0 -dev packages, so you just have to
+ require:
+
+  require 'compat-5.1'
+
+ For other platforms, the compat-5.1 module that comes with this Lua
+ binding will be installed for you in the same dir where RRDtool was
+ installed, under the subdir .../lib/lua/5.0. In this case, you must
+ tell your Lua programs where to find it by changing the Lua var
+ LUA_PATH:
+
+  --- compat-5.1.lua is only necessary for Lua 5.0 ------------------
+  original_LUA_PATH = LUA_PATH
+  -- try only compat-5.1 installed with RRDtool package
+  LUA_PATH = '<INSTALL_PREFIX>/lib/lua/5.0/?.lua'
+  require 'compat-5.1'
+  LUA_PATH = original_LUA_PATH
+  original_LUA_PATH = nil
+  --- end of code to require compat-5.1 -----------------------------
+
+  Now we can require the rrd module just like we did for 5.1 above:
+
+  -------------------------------------------------------------------
+  package.cpath = '<INSTALL_PREFIX>/lib/lua/5.0/?.so;' ..
+                  package.cpath
+  require 'rrd'
+  -------------------------------------------------------------------
diff --git a/program/bindings/lua/compat-5.1r5/compat-5.1.c b/program/bindings/lua/compat-5.1r5/compat-5.1.c
new file mode 100644 (file)
index 0000000..e94929e
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+** Compat-5.1
+** Copyright Kepler Project 2004-2006 (http://www.keplerproject.org/compat)
+** $Id$
+
+Copyright Â© 2004-2006 The Kepler Project.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include "lua.h"
+#include "lauxlib.h"
+#include "compat-5.1.h"
+
+static void getfield(lua_State *L, int idx, const char *name) {
+    const char *end = strchr(name, '.');
+    lua_pushvalue(L, idx);
+    while (end) {
+        lua_pushlstring(L, name, end - name);
+        lua_gettable(L, -2);
+        lua_remove(L, -2);
+        if (lua_isnil(L, -1)) return;
+        name = end+1;
+        end = strchr(name, '.');
+    }
+    lua_pushstring(L, name);
+    lua_gettable(L, -2);
+    lua_remove(L, -2);
+}
+
+static void setfield(lua_State *L, int idx, const char *name) {
+    const char *end = strchr(name, '.');
+    lua_pushvalue(L, idx);
+    while (end) {
+        lua_pushlstring(L, name, end - name);
+        lua_gettable(L, -2);
+        /* create table if not found */
+        if (lua_isnil(L, -1)) {
+            lua_pop(L, 1);
+            lua_newtable(L);
+            lua_pushlstring(L, name, end - name);
+            lua_pushvalue(L, -2);
+            lua_settable(L, -4);
+        }
+        lua_remove(L, -2);
+        name = end+1;
+        end = strchr(name, '.');
+    }
+    lua_pushstring(L, name);
+    lua_pushvalue(L, -3);
+    lua_settable(L, -3);
+    lua_pop(L, 2);
+}
+
+LUALIB_API void luaL_module(lua_State *L, const char *libname,
+                              const luaL_reg *l, int nup) {
+  if (libname) {
+    getfield(L, LUA_GLOBALSINDEX, libname);  /* check whether lib already exists */
+    if (lua_isnil(L, -1)) { 
+      int env, ns;
+      lua_pop(L, 1); /* get rid of nil */
+      lua_pushliteral(L, "require");
+      lua_gettable(L, LUA_GLOBALSINDEX); /* look for require */
+      lua_getfenv(L, -1); /* getfenv(require) */
+      lua_remove(L, -2); /* remove function require */
+      env = lua_gettop(L);
+
+      lua_newtable(L); /* create namespace for lib */
+      ns = lua_gettop(L);
+      getfield(L, env, "package.loaded"); /* get package.loaded table */
+      if (lua_isnil(L, -1)) { /* create package.loaded table */
+          lua_pop(L, 1); /* remove previous result */
+          lua_newtable(L);
+          lua_pushvalue(L, -1);
+          setfield(L, env, "package.loaded");
+      }
+      else if (!lua_istable(L, -1))
+        luaL_error(L, "name conflict for library `%s'", libname);
+      lua_pushstring(L, libname);
+      lua_pushvalue(L, ns); 
+      lua_settable(L, -3); /* package.loaded[libname] = ns */
+      lua_pop(L, 1); /* get rid of package.loaded table */
+      lua_pushvalue(L, ns); /* copy namespace */
+      setfield(L, LUA_GLOBALSINDEX, libname);
+      lua_remove (L, env); /* remove env */
+    }
+    lua_insert(L, -(nup+1));  /* move library table to below upvalues */
+  }
+  for (; l->name; l++) {
+    int i;
+    lua_pushstring(L, l->name);
+    for (i=0; i<nup; i++)  /* copy upvalues to the top */
+      lua_pushvalue(L, -(nup+1));
+    lua_pushcclosure(L, l->func, nup);
+    lua_settable(L, -(nup+3));
+  }
+  lua_pop(L, nup);  /* remove upvalues */
+}
+
diff --git a/program/bindings/lua/compat-5.1r5/compat-5.1.h b/program/bindings/lua/compat-5.1r5/compat-5.1.h
new file mode 100644 (file)
index 0000000..d835069
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+** Compat-5.1
+** Copyright Kepler Project 2004-2006 (http://www.keplerproject.org/compat/)
+** $Id$
+
+Copyright Â© 2004-2006 The Kepler Project.
+  
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in 
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.   
+
+*/
+
+#ifndef COMPAT_H
+
+LUALIB_API void luaL_module(lua_State *L, const char *libname,
+                                       const luaL_reg *l, int nup);
+#define luaL_openlib luaL_module
+
+#endif
diff --git a/program/bindings/lua/compat-5.1r5/compat-5.1.lua b/program/bindings/lua/compat-5.1r5/compat-5.1.lua
new file mode 100644 (file)
index 0000000..b662270
--- /dev/null
@@ -0,0 +1,287 @@
+--
+-- Compat-5.1
+-- Copyright Kepler Project 2004-2006 (http://www.keplerproject.org/compat)
+-- According to Lua 5.1
+-- $Id: compat-5.1.lua,v 1.22 2006/02/20 21:12:47 carregal Exp $
+--
+-- Copyright Â© 2004-2006 The Kepler Project.
+--   
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to
+-- deal in the Software without restriction, including without limitation the
+-- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+-- sell copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+-- 
+-- The above copyright notice and this permission notice shall be included in 
+-- all copies or substantial portions of the Software.
+-- 
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+-- IN THE SOFTWARE.   
+--
+
+_COMPAT51 = "Compat-5.1 R5"
+
+local LUA_DIRSEP = '/'
+local LUA_OFSEP = '_'
+local OLD_LUA_OFSEP = ''
+local POF = 'luaopen_'
+local LUA_PATH_MARK = '?'
+local LUA_IGMARK = ':'
+
+local assert, error, getfenv, ipairs, loadfile, loadlib, pairs, setfenv, setmetatable, type = assert, error, getfenv, ipairs, loadfile, loadlib, pairs, setfenv, setmetatable, type
+local find, format, gfind, gsub, sub = string.find, string.format, string.gfind, string.gsub, string.sub
+
+--
+-- avoid overwriting the package table if it's already there
+--
+package = package or {}
+local _PACKAGE = package
+
+package.path = LUA_PATH or os.getenv("LUA_PATH") or
+             ("./?.lua;" ..
+              "/usr/local/share/lua/5.0/?.lua;" ..
+              "/usr/local/share/lua/5.0/?/?.lua;" ..
+              "/usr/local/share/lua/5.0/?/init.lua" )
+package.cpath = LUA_CPATH or os.getenv("LUA_CPATH") or
+             "./?.so;" ..
+             "./l?.so;" ..
+             "/usr/local/lib/lua/5.0/?.so;" ..
+             "/usr/local/lib/lua/5.0/l?.so"
+
+--
+-- make sure require works with standard libraries
+--
+package.loaded = package.loaded or {}
+package.loaded.debug = debug
+package.loaded.string = string
+package.loaded.math = math
+package.loaded.io = io
+package.loaded.os = os
+package.loaded.table = table 
+package.loaded.base = _G
+package.loaded.coroutine = coroutine
+local _LOADED = package.loaded
+
+--
+-- avoid overwriting the package.preload table if it's already there
+--
+package.preload = package.preload or {}
+local _PRELOAD = package.preload
+
+
+--
+-- looks for a file `name' in given path
+--
+local function findfile (name, pname)
+       name = gsub (name, "%.", LUA_DIRSEP)
+       local path = _PACKAGE[pname]
+       assert (type(path) == "string", format ("package.%s must be a string", pname))
+       for c in gfind (path, "[^;]+") do
+               c = gsub (c, "%"..LUA_PATH_MARK, name)
+               local f = io.open (c)
+               if f then
+                       f:close ()
+                       return c
+               end
+       end
+       return nil -- not found
+end
+
+
+--
+-- check whether library is already loaded
+--
+local function loader_preload (name)
+       assert (type(name) == "string", format (
+               "bad argument #1 to `require' (string expected, got %s)", type(name)))
+       assert (type(_PRELOAD) == "table", "`package.preload' must be a table")
+       return _PRELOAD[name]
+end
+
+
+--
+-- Lua library loader
+--
+local function loader_Lua (name)
+       assert (type(name) == "string", format (
+               "bad argument #1 to `require' (string expected, got %s)", type(name)))
+       local filename = findfile (name, "path")
+       if not filename then
+               return false
+       end
+       local f, err = loadfile (filename)
+       if not f then
+               error (format ("error loading module `%s' (%s)", name, err))
+       end
+       return f
+end
+
+
+local function mkfuncname (name)
+       name = gsub (name, "^.*%"..LUA_IGMARK, "")
+       name = gsub (name, "%.", LUA_OFSEP)
+       return POF..name
+end
+
+local function old_mkfuncname (name)
+       --name = gsub (name, "^.*%"..LUA_IGMARK, "")
+       name = gsub (name, "%.", OLD_LUA_OFSEP)
+       return POF..name
+end
+
+--
+-- C library loader
+--
+local function loader_C (name)
+       assert (type(name) == "string", format (
+               "bad argument #1 to `require' (string expected, got %s)", type(name)))
+       local filename = findfile (name, "cpath")
+       if not filename then
+               return false
+       end
+       local funcname = mkfuncname (name)
+       local f, err = loadlib (filename, funcname)
+       if not f then
+               funcname = old_mkfuncname (name)
+               f, err = loadlib (filename, funcname)
+               if not f then
+                       error (format ("error loading module `%s' (%s)", name, err))
+               end
+       end
+       return f
+end
+
+
+local function loader_Croot (name)
+       local p = gsub (name, "^([^.]*).-$", "%1")
+       if p == "" then
+               return
+       end
+       local filename = findfile (p, "cpath")
+       if not filename then
+               return
+       end
+       local funcname = mkfuncname (name)
+       local f, err, where = loadlib (filename, funcname)
+       if f then
+               return f
+       elseif where ~= "init" then
+               error (format ("error loading module `%s' (%s)", name, err))
+       end
+end
+
+-- create `loaders' table
+package.loaders = package.loaders or { loader_preload, loader_Lua, loader_C, loader_Croot, }
+local _LOADERS = package.loaders
+
+
+--
+-- iterate over available loaders
+--
+local function load (name, loaders)
+       -- iterate over available loaders
+       assert (type (loaders) == "table", "`package.loaders' must be a table")
+       for i, loader in ipairs (loaders) do
+               local f = loader (name)
+               if f then
+                       return f
+               end
+       end
+       error (format ("module `%s' not found", name))
+end
+
+-- sentinel
+local sentinel = function () end
+
+--
+-- new require
+--
+function _G.require (modname)
+       assert (type(modname) == "string", format (
+               "bad argument #1 to `require' (string expected, got %s)", type(name)))
+       local p = _LOADED[modname]
+       if p then -- is it there?
+               if p == sentinel then
+                       error (format ("loop or previous error loading module '%s'", modname))
+               end
+               return p -- package is already loaded
+       end
+       local init = load (modname, _LOADERS)
+       _LOADED[modname] = sentinel
+       local actual_arg = _G.arg
+       _G.arg = { modname }
+       local res = init (modname)
+       if res then
+               _LOADED[modname] = res
+       end
+       _G.arg = actual_arg
+       if _LOADED[modname] == sentinel then
+               _LOADED[modname] = true
+       end
+       return _LOADED[modname]
+end
+
+
+-- findtable
+local function findtable (t, f)
+       assert (type(f)=="string", "not a valid field name ("..tostring(f)..")")
+       local ff = f.."."
+       local ok, e, w = find (ff, '(.-)%.', 1)
+       while ok do
+               local nt = rawget (t, w)
+               if not nt then
+                       nt = {}
+                       t[w] = nt
+               elseif type(t) ~= "table" then
+                       return sub (f, e+1)
+               end
+               t = nt
+               ok, e, w = find (ff, '(.-)%.', e+1)
+       end
+       return t
+end
+
+--
+-- new package.seeall function
+--
+function _PACKAGE.seeall (module)
+       local t = type(module)
+       assert (t == "table", "bad argument #1 to package.seeall (table expected, got "..t..")")
+       local meta = getmetatable (module)
+       if not meta then
+               meta = {}
+               setmetatable (module, meta)
+       end
+       meta.__index = _G
+end
+
+
+--
+-- new module function
+--
+function _G.module (modname, ...)
+       local ns = _LOADED[modname]
+       if type(ns) ~= "table" then
+               ns = findtable (_G, modname)
+               if not ns then
+                       error (string.format ("name conflict for module '%s'", modname))
+               end
+               _LOADED[modname] = ns
+       end
+       if not ns._NAME then
+               ns._NAME = modname
+               ns._M = ns
+               ns._PACKAGE = gsub (modname, "[^.]*$", "")
+       end
+       setfenv (2, ns)
+       for i, f in ipairs (arg) do
+               f (ns)
+       end
+end
diff --git a/program/bindings/lua/rrdlua.c b/program/bindings/lua/rrdlua.c
new file mode 100644 (file)
index 0000000..a733184
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ * Lua bindings for RRDTool
+ *
+ * This software is licensed to the public under the Free Software
+ * Foundation's GNU GPL, version 2 or later. You may obtain a copy
+ * of the GPL by visiting the Free Software Foundations web site at
+ * www.fsf.org, and a copy is included in this distribution.
+ *
+ * Copyright 2008 Fidelis Assis, all rights reserved.
+ *
+ */
+
+#include <ctype.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+#include <dirent.h>
+#include <inttypes.h>
+
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+#include "../../src/rrd_tool.h"
+
+#ifdef LUA50
+#ifdef HAVE_COMPAT51
+#include "compat-5.1.h"
+#else
+#include "compat-5.1r5/compat-5.1.h"
+#endif
+#endif
+
+extern void rrd_freemem(void *mem);
+
+extern int luaopen_rrd (lua_State * L);
+typedef int (*RRD_FUNCTION)(int, char **);
+typedef rrd_info_t *(RRD_FUNCTION_V)(int, char **);
+
+/**********************************************************/
+
+static void reset_rrd_state(void)
+{
+    optind = 0;
+    opterr = 0;
+    rrd_clear_error();
+}
+
+static char **make_argv(const char *cmd, lua_State * L)
+{
+  char **argv;
+  int i;
+  int argc = lua_gettop(L) + 1;
+
+  if (!(argv = calloc(argc, sizeof (char *)))) 
+    /* raise an error and never return */
+    luaL_error(L, "Can't allocate memory for arguments array", cmd);
+
+  /* fprintf(stderr, "Args:\n"); */
+  argv[0] = (char *) cmd; /* Dummy arg. Cast to (char *) because rrd */
+                          /* functions don't expect (const * char)   */
+  /* fprintf(stderr, "%s\n", argv[0]); */
+  for (i=1; i<argc; i++) {
+    /* accepts string or number */
+    if (lua_isstring(L, i) || lua_isnumber(L, i)) {
+      if (!(argv[i] = strdup(lua_tostring (L, i)))) {
+        /* raise an error and never return */
+        luaL_error(L, "%s - error duplicating string area for arg #%d",
+                   cmd, i);
+      }
+    } else {
+      /* raise an error and never return */
+      luaL_error(L, "Invalid arg #%d to %s: args must be strings or numbers",
+                 i, cmd);
+    }
+    /* fprintf(stderr, "%s\n", argv[i]); */
+  }
+  return argv;
+}
+
+static int
+rrd_common_call (lua_State *L, const char *cmd, RRD_FUNCTION rrd_function)
+{
+  char **argv;
+  int argc = lua_gettop(L) + 1;
+
+  argv = make_argv(cmd, L);
+  reset_rrd_state();
+  rrd_function(argc, argv);
+  free(argv);
+  if (rrd_test_error()) luaL_error(L, rrd_get_error());
+  return 0;
+}
+
+#if defined(DINF)
+static int
+lua_rrd_infocall(lua_State *L, const char *cmd, RRD_FUNCTION_V rrd_function)
+{
+  char **argv;
+  rrd_info_t *p, *data;
+  int argc = lua_gettop(L) + 1;
+
+  argv = make_argv(cmd, L);
+  reset_rrd_state();
+  data = rrd_function(argc, argv);
+  free(argv);
+  if (rrd_test_error()) luaL_error(L, rrd_get_error());
+
+  lua_newtable(L);
+  p = data;
+  while (data) {
+    lua_pushstring(L, data->key);
+    switch (data->type) {
+      case RD_I_CNT:
+        if (isnan(data->value.u_val)) {
+          lua_pushnil(L); 
+        } else {
+          lua_pushnumber(L, (lua_Number) data->value.u_val);
+        }
+        lua_rawset(L, -3);
+        break;
+      case RD_I_VAL:
+        lua_pushnumber(L, (lua_Number) data->value.u_val);
+        lua_rawset(L, -3);
+        break;
+      case RD_I_STR:
+        lua_pushstring(L, data->value.u_str);
+        lua_rawset(L, -3);
+        break;
+      case RD_I_BLO:
+        lua_pushlstring(L, (const char *) data->value.u_blo.ptr,
+                        data->value.u_blo.size);
+        lua_rawset(L, -3);
+        break;
+      default:
+        rrd_info_free(p); 
+        return luaL_error(L, "Wrong data type to info call");
+        break;
+    }
+    data = data->next;
+  }
+  rrd_info_free(p); 
+  return 1;
+}
+#endif
+
+/**********************************************************/
+
+static int
+lua_rrd_create (lua_State * L)
+{
+  rrd_common_call(L, "create", rrd_create);
+  return 0;
+}
+
+static int
+lua_rrd_dump (lua_State * L)
+{
+  rrd_common_call(L, "dump", rrd_dump);
+  return 0;
+}
+
+static int
+lua_rrd_resize (lua_State * L)
+{
+  rrd_common_call(L, "resize", rrd_resize);
+  return 0;
+}
+
+static int
+lua_rrd_restore (lua_State * L)
+{
+  rrd_common_call(L, "restore", rrd_restore);
+  return 0;
+}
+
+static int
+lua_rrd_tune (lua_State * L)
+{
+  rrd_common_call(L, "tune", rrd_tune);
+  return 0;
+}
+
+static int
+lua_rrd_update (lua_State * L)
+{
+  rrd_common_call(L, "update", rrd_update);
+  return 0;
+}
+
+static int
+lua_rrd_fetch (lua_State * L)
+{
+  int argc = lua_gettop(L) + 1;
+  char **argv = make_argv("fetch", L);
+  unsigned long i, j, step, ds_cnt;
+  rrd_value_t *data, *p;
+  char    **names;
+  time_t  t, start, end;
+
+  reset_rrd_state();
+  rrd_fetch(argc, argv, &start, &end, &step, &ds_cnt, &names, &data);
+  free(argv);
+  if (rrd_test_error()) luaL_error(L, rrd_get_error());
+
+  lua_pushnumber(L, (lua_Number) start);
+  lua_pushnumber(L, (lua_Number) step);
+  /* fprintf(stderr, "%lu, %lu, %lu, %lu\n", start, end, step, num_points); */
+
+  /* create the ds names array */
+  lua_newtable(L);
+  for (i=0; i<ds_cnt; i++) {
+    lua_pushstring(L, names[i]);
+    lua_rawseti(L, -2, i+1);
+    rrd_freemem(names[i]);
+  }
+  rrd_freemem(names);
+
+  /* create the data points array */
+  lua_newtable(L);
+  p = data;
+  for (t=start, i=0; t<end; t+=step, i++) {
+    lua_newtable(L);
+    for (j=0; j<ds_cnt; j++) {
+      /*fprintf(stderr, "Point #%lu\n", j+1); */
+      lua_pushnumber(L, (lua_Number) *p++);
+      lua_rawseti(L, -2, j+1);
+    }
+    lua_rawseti(L, -2, i+1);
+  }
+  rrd_freemem(data);
+
+  /* return the end as the last value */
+  lua_pushnumber(L, (lua_Number) end);
+
+  return 5;
+}
+
+static int
+lua_rrd_first (lua_State * L)
+{
+  time_t first;
+  int argc = lua_gettop(L) + 1;
+  char **argv = make_argv("first", L);
+  reset_rrd_state();
+  first = rrd_first(argc, argv);
+  free(argv);
+  if (rrd_test_error()) luaL_error(L, rrd_get_error());
+  lua_pushnumber(L, (lua_Number) first);
+  return 1;
+}
+
+static int
+lua_rrd_last (lua_State * L)
+{
+  time_t last;
+  int argc = lua_gettop(L) + 1;
+  char **argv = make_argv("last", L);
+  reset_rrd_state();
+  last = rrd_last(argc, argv);
+  free(argv);
+  if (rrd_test_error()) luaL_error(L, rrd_get_error());
+  lua_pushnumber(L, (lua_Number) last);
+  return 1;
+}
+
+static int
+lua_rrd_graph (lua_State * L)
+{
+  int argc = lua_gettop(L) + 1;
+  char **argv = make_argv("last", L);
+  char **calcpr;
+  int i, xsize, ysize;
+  double ymin, ymax;
+
+  reset_rrd_state();
+  rrd_graph(argc, argv, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
+  free(argv);
+  if (rrd_test_error()) luaL_error(L, rrd_get_error());
+  lua_pushnumber(L, (lua_Number) xsize);
+  lua_pushnumber(L, (lua_Number) ysize);
+  lua_newtable(L);
+  for (i = 0; calcpr && calcpr[i]; i++) {
+      lua_pushstring(L, calcpr[i]);
+      lua_rawseti(L, -2, i+1);
+      rrd_freemem(calcpr[i]);
+  }
+  rrd_freemem(calcpr);
+  return 3;
+}
+
+static int
+lua_rrd_flushcached(lua_State *L)
+{
+  return rrd_common_call(L, "flushcached", rrd_flushcached);
+}
+
+#if defined(DINF)
+static int
+lua_rrd_info (lua_State * L)
+{
+  return lua_rrd_infocall(L, "info", rrd_info);
+}
+
+static int
+lua_rrd_graphv (lua_State * L)
+{
+  return lua_rrd_infocall(L, "graphv", rrd_graph_v);
+}
+
+static int
+lua_rrd_updatev (lua_State * L)
+{
+  return lua_rrd_infocall(L, "updatev", rrd_update_v);
+}
+#endif
+
+/**********************************************************/
+
+/*
+** Assumes the table is on top of the stack.
+*/
+static void
+set_info (lua_State * L)
+{
+  lua_pushliteral (L, "_COPYRIGHT");
+  lua_pushliteral (L, "Copyright (C) 2008 Fidelis Assis");
+  lua_settable (L, -3);
+  lua_pushliteral (L, "_DESCRIPTION");
+  lua_pushliteral (L, "RRD-lua is a Lua binding for RRDTool.");
+  lua_settable (L, -3);
+  lua_pushliteral (L, "_NAME");
+  lua_pushliteral (L, "RRD-Lua");
+  lua_settable (L, -3);
+  lua_pushliteral (L, "_VERSION");
+  lua_pushliteral (L, LIB_VERSION);
+  lua_settable (L, -3);
+}
+
+/**********************************************************/
+
+static const struct luaL_reg rrd[] = {
+  {"create", lua_rrd_create},
+  {"dump", lua_rrd_dump},
+  {"fetch", lua_rrd_fetch},
+  {"first", lua_rrd_first},
+  {"graph", lua_rrd_graph},
+  {"last", lua_rrd_last},
+  {"resize", lua_rrd_resize},
+  {"restore", lua_rrd_restore},
+  {"tune", lua_rrd_tune},
+  {"update", lua_rrd_update},
+  {"flushcached", lua_rrd_flushcached},
+#if defined(DINF)
+  {"info", lua_rrd_info},
+  {"updatev", lua_rrd_updatev},
+  {"graphv", lua_rrd_graphv},
+#endif
+  {NULL, NULL}
+};
+
+
+/*
+** Open RRD library
+*/
+int
+luaopen_rrd (lua_State * L)
+{
+#if defined LUA50
+  /* luaL_module is defined in compat-5.1.c */
+  luaL_module (L, "rrd", rrd, 0);
+#else
+  luaL_register (L, "rrd", rrd);
+#endif
+  set_info (L);
+  return 1;
+}
diff --git a/program/bindings/lua/test.lua.bottom b/program/bindings/lua/test.lua.bottom
new file mode 100644 (file)
index 0000000..caa590c
--- /dev/null
@@ -0,0 +1,90 @@
+
+local rrd = require 'rrd'
+
+local name = 'test.rrd'
+local start = 300 * math.floor(os.time() / 300)
+
+io.write('\n-- Creating ', name, '\n')
+rrd.create(
+    name,
+    '--start', start-1,
+    '--step', '300',
+    'DS:a:GAUGE:600:U:U',
+    'DS:b:GAUGE:600:U:U',
+    'RRA:AVERAGE:0.5:1:300')
+
+local num_points = 0
+for t=start, start+300*300, 300 do
+  local s = string.format('%d:%d:%f', t,
+                          math.random(100), math.sin(t/800)*50+50)
+  rrd.update(name, s)
+  num_points = num_points + 1
+end
+
+io.write('rrd file created with ', num_points, ' points, from ', start,
+         ' to ', start+300*300, '\n')
+
+io.write('\n-- Testing rrd.info\n')
+local info = rrd.info(name)
+for k,v in pairs(info) do
+  io.write(k, '=', v, '\n')
+end
+io.write('\n')
+
+io.write('-- Testing rrd.fetch\n') 
+io.write('fetching data from ', name, ' - interval: ', start, ' to ',
+         start+300*300, '\n') 
+local fstart, fstep, fnames, fdata =
+  rrd.fetch(name, '--start', start, '--end', start+300*300+10, 'AVERAGE')
+io.write('got ', table.getn(fdata[1]), ' data sources with ', table.getn(fdata),
+         ' data points each.\n')
+
+io.write('\n-- Printing fetched data\n') 
+io.write('            ')
+for i, n in ipairs(fnames) do
+  io.write(n, '            ')
+end
+io.write('\n')
+for i, v in ipairs(fdata) do
+  local time = fstart + (i-1)*fstep
+  io.write(string.format('%s (%d): ', os.date('%c', time), time))
+  for _, w in ipairs(v) do
+    io.write(string.format('%e ', w))
+  end
+  io.write('\n')
+end
+io.write('\n')
+
+io.write('\n-- Testing rrd.graphv - creates test.png and returns values\n') 
+local t = rrd.graphv(
+   'test.png',
+   '--title', 'Enjoy Lua RRDtool module!',
+   '--start', start+3600,
+   '--end', 'start + 1000 min',
+   '--interlace',
+   '--imgformat', 'PNG',
+   '--width=450',
+   'DEF:a=' .. name .. ':a:AVERAGE',
+   'DEF:b=' .. name .. ':b:AVERAGE',
+   'CDEF:line=TIME,2400,%,300,LT,a,UNKN,IF',
+   'AREA:b#00b6e4:beta',
+   'AREA:line#0022e9:alpha',
+   'LINE3:line#ff0000',
+   'VDEF:va=a,AVERAGE',
+   'VDEF:vb=b,AVERAGE',
+   'PRINT:va:%5.2lf',
+   'PRINT:vb:%5.2lf')
+
+io.write('\n-- Returned values:\n') 
+io.write('print[0]: ', t['print[0]'], '\n')
+io.write('print[1]: ', t['print[1]'], '\n')
+for k, v in pairs(t) do
+  if not string.find(k, '^print%[%d+%]') then
+    io.write(k, ': ', v, '\n')
+  end
+end
+io.write('\n')
+
+io.write('-- The graph "test.png" was created.\n')
+io.write('-- Use your preferred viewer to display it.\n\n')
+
diff --git a/program/bindings/perl-piped/MANIFEST b/program/bindings/perl-piped/MANIFEST
new file mode 100644 (file)
index 0000000..a530ade
--- /dev/null
@@ -0,0 +1,5 @@
+MANIFEST
+README
+Makefile.PL
+RRDp.pm
+t/base.t
diff --git a/program/bindings/perl-piped/Makefile.PL b/program/bindings/perl-piped/Makefile.PL
new file mode 100644 (file)
index 0000000..5c1a98f
--- /dev/null
@@ -0,0 +1,10 @@
+use ExtUtils::MakeMaker;
+
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+WriteMakefile(
+    'NAME'         => 'RRDp',
+    'VERSION'      => '0.99.0', # finds $VERSION
+    'linkext'   => {LINKTYPE => ''},
+    'dist'    =>    {COMPRESS=>'gzip', SUFFIX=>'gz'},
+);
diff --git a/program/bindings/perl-piped/README b/program/bindings/perl-piped/README
new file mode 100644 (file)
index 0000000..672061d
--- /dev/null
@@ -0,0 +1,5 @@
+This is a Perl module for using RRDtool process via a set of pipes.
+
+perl Makefile.PL
+make test
+make install
diff --git a/program/bindings/perl-piped/RRDp.pm b/program/bindings/perl-piped/RRDp.pm
new file mode 100644 (file)
index 0000000..bf2abe8
--- /dev/null
@@ -0,0 +1,213 @@
+package RRDp;
+
+=head1 NAME
+
+RRDp - Attach RRDtool from within a perl script via a set of pipes;
+
+=head1 SYNOPSIS
+
+use B<RRDp>
+
+B<RRDp::start> I<path to RRDtool executable>
+
+B<RRDp::cmd>  I<rrdtool commandline>
+
+$answer = B<RRD::read>
+
+$status = B<RRD::end>
+
+B<$RRDp::user>,  B<$RRDp::sys>, B<$RRDp::real>, B<$RRDp::error_mode>, B<$RRDp::error>
+
+=head1 DESCRIPTION
+
+With this module you can safely communicate with the RRDtool. 
+
+After every B<RRDp::cmd> you have to issue an B<RRDp::read> command to get
+B<RRDtool>s answer to your command. The answer is returned as a pointer,
+in order to speed things up. If the last command did not return any
+data, B<RRDp::read> will return an undefined variable. 
+
+If you import the PERFORMANCE variables into your namespace, 
+you can access RRDtool's internal performance measurements.
+
+=over 8
+
+=item  use B<RRDp>
+
+Load the RRDp::pipe module.
+
+=item B<RRDp::start> I<path to RRDtool executable>
+
+start RRDtool. The argument must be the path to the RRDtool executable
+
+=item B<RRDp::cmd> I<rrdtool commandline>
+
+pass commands on to RRDtool. Check the RRDtool documentation for
+more info on the RRDtool commands.
+
+B<Note>: Due to design limitations, B<RRDp::cmd> does not support the
+C<graph -> command - use C<graphv -> instead.
+
+=item $answer = B<RRDp::read>
+
+read RRDtool's response to your command. Note that the $answer variable will
+only contain a pointer to the returned data. The reason for this is, that
+RRDtool can potentially return quite excessive amounts of data
+and we don't want to copy this around in memory. So when you want to 
+access the contents of $answer you have to use $$answer which dereferences
+the variable.
+
+=item $status = B<RRDp::end>
+
+terminates RRDtool and returns RRDtool's status ... 
+
+=item B<$RRDp::user>,  B<$RRDp::sys>, B<$RRDp::real>
+
+these variables will contain totals of the user time, system time and
+real time as seen by RRDtool.  User time is the time RRDtool is
+running, System time is the time spend in system calls and real time
+is the total time RRDtool has been running.
+
+The difference between user + system and real is the time spent
+waiting for things like the hard disk and new input from the Perl
+script.
+
+=item B<$RRDp::error_mode> and B<$RRDp::error>
+
+If you set the variable $RRDp::error_mode to the value 'catch' before you run RRDp::read a potential
+ERROR message will not cause the program to abort but will be returned in this variable. If no error
+occurs the variable will be empty.
+
+ $RRDp::error_mode = 'catch';
+ RRDp::cmd qw(info file.rrd);
+ print $RRDp::error if $RRDp::error;
+
+=back
+
+
+=head1 EXAMPLE
+
+ use RRDp;
+ RRDp::start "/usr/local/bin/rrdtool";
+ RRDp::cmd   qw(create demo.rrd --step 100 
+               DS:in:GAUGE:100:U:U
+              RRA:AVERAGE:0.5:1:10);
+ $answer = RRDp::read;
+ print $$answer;
+ ($usertime,$systemtime,$realtime) =  ($RRDp::user,$RRDp::sys,$RRDp::real);
+
+=head1 SEE ALSO
+
+For more information on how to use RRDtool, check the manpages.
+
+=head1 AUTHOR
+
+Tobias Oetiker <tobi@oetiker.ch>
+
+=cut
+
+#'  this is to make cperl.el happy
+
+use strict;
+use Fcntl;
+use Carp;
+use IO::Handle;
+use IPC::Open2;
+use vars qw($Sequence $RRDpid $VERSION);
+my $Sequence;
+my $RRDpid;
+
+# Prototypes
+
+sub start ($);
+sub cmd (@);
+sub end ();
+sub read ();
+
+$VERSION=1.4003;
+
+sub start ($){
+  croak "rrdtool is already running"
+    if defined $Sequence;
+  $Sequence = 'S';    
+  my $rrdtool = shift @_;    
+  $RRDpid = open2 \*RRDreadHand,\*RRDwriteHand, $rrdtool,"-" 
+    or croak "Can't Start rrdtool: $!";
+  RRDwriteHand->autoflush(); #flush after every write    
+  fcntl RRDreadHand, F_SETFL,O_NONBLOCK|O_NDELAY; #make readhandle NON BLOCKING
+  return $RRDpid;
+}
+
+
+sub read () {
+  croak "RRDp::read can only be called after RRDp::cmd" 
+    unless $Sequence eq 'C';
+  $RRDp::error = undef;
+  $Sequence = 'R';
+  my $inmask = 0;
+  my $srbuf;
+  my $minibuf;
+  my $buffer;
+  my $nfound;
+  my $timeleft;
+  vec($inmask,fileno(RRDreadHand),1) = 1; # setup select mask for Reader
+  while (1) {
+    my $rout;    
+    $nfound = select($rout=$inmask,undef,undef,2);
+    if ($nfound == 0 ) {
+      # here, we could do something sensible ...
+      next;
+    }
+    sysread(RRDreadHand,$srbuf,4096);
+    $minibuf .= $srbuf;
+    while ($minibuf =~ s|^(.+?)\n||s) {
+      my $line = $1;
+      # print $line,"\n";      
+      $RRDp::error = undef;
+      if ($line =~  m|^ERROR|) {       
+       $RRDp::error_mode eq 'catch' ? $RRDp::error = $line : croak $line;
+        $RRDp::sys = undef;
+        $RRDp::user = undef;
+        $RRDp::real = undef;
+       return undef;
+      } 
+      elsif ($line =~ m|^OK(?: u:([\d\.]+) s:([\d\.]+) r:([\d\.]+))?|){
+       ($RRDp::sys,$RRDp::user,$RRDp::real)=($1,$2,$3);
+       return \$buffer;
+      } else {
+       $buffer .= $line. "\n";
+      }
+    }
+  }
+}
+
+sub cmd (@){
+  croak "RRDp::cmd can only be called after RRDp::read or RRDp::start"
+    unless $Sequence eq 'R' or $Sequence eq 'S';
+  $Sequence = 'C';
+  my $cmd = join " ", @_;
+  if ($Sequence ne 'S') {
+  }
+  $cmd =~ s/\n/ /gs;
+  $cmd =~ s/\s/ /gs;
+
+  # The generated graphs aren't necessarily terminated by a newline,
+  # causing RRDp::read() to wait for a line matching '^OK' forever.
+  if ($cmd =~ m/^\s*graph\s+-\s+/) {
+    croak "RRDp does not support the 'graph -' command - "
+        . "use 'graphv -' instead";
+  }
+  print RRDwriteHand "$cmd\n";
+}
+
+sub end (){
+  croak "RRDp::end can only be called after RRDp::start"
+    unless $Sequence;
+  close RRDwriteHand;
+  close RRDreadHand;
+  $Sequence = undef;
+  waitpid $RRDpid,0;
+  return $?
+}
+
+1;
diff --git a/program/bindings/perl-piped/leaktest.pl b/program/bindings/perl-piped/leaktest.pl
new file mode 100644 (file)
index 0000000..cebf1c7
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/perl -w
+$ENV{PATH}="/usr/ucb";
+use strict;   
+use RRDp;     
+my $rrdfile='/tmp/test.rrd';
+RRDp::start '/home/oetiker/data/projects/AABN-rrdtool/src/rrdtool';
+print grep /rrdtool/,`ps au`;
+print grep /rrdtool/,`ps au`;
+my $i=0;
+while ($i<1000) {
+ RRDp::cmd 'info /tmp/test.rrd';
+ $_ = RRDp::read;
+ $i++;
+}
+$_ = RRDp::end;
+print grep /rrdtool/,`ps au`;
diff --git a/program/bindings/perl-piped/rrdpl.dsp b/program/bindings/perl-piped/rrdpl.dsp
new file mode 100644 (file)
index 0000000..bf3a15d
--- /dev/null
@@ -0,0 +1,115 @@
+# Microsoft Developer Studio Project File - Name="rrd" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=rrd - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "rrdpl.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "rrdpl.mak" CFG="rrd - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "rrd - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "rrd - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "rrd - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32
+# ADD BASE RSC /l 0x100c /d "NDEBUG"
+# ADD RSC /l 0x100c /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF  "$(CFG)" == "rrd - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "C:\perl\lib\site\auto\RRD\"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c
+# ADD CPP /W3 /I "C:\perl\lib\CORE" /D "WIN32" /D VERSION=\"0.02\" /D XS_VERSION=\"0.02\" /D "_DEBUG" /D "_CONSOLE" /FR -I../src/ -I../gd1.2 RRD.c /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32
+# ADD BASE RSC /l 0x100c /d "_DEBUG"
+# ADD RSC /l 0x100c /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 c:\perl\lib\core\perl.lib ..\src\debug\rrd.lib ..\gd1.2\debug\gd.lib /dll /incremental:no /debug /machine:IX86 /out:"C:\perl\lib\site\auto\RRD\rrd.dll"
+# SUBTRACT LINK32 /pdb:none
+
+!ENDIF 
+
+# Begin Target
+
+# Name "rrd - Win32 Release"
+# Name "rrd - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\RRD.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\RRD.xs
+
+!IF  "$(CFG)" == "rrd - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "rrd - Win32 Debug"
+
+# Begin Custom Build
+InputPath=.\RRD.xs
+
+"rrd.c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+       C:\Perl\bin\perl -Ic:\perl\lib -Ic:\perl\lib C:\perl\lib\ExtUtils/xsubpp\
+    -typemap C:\perl\lib\ExtUtils\typemap RRD.xs >RRD.tc && C:\Perl\bin\perl\
+   -Ic:\perl\lib -Ic:\perl\lib -MExtUtils::Command -e mv RRD.tc RRD.c
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# End Target
+# End Project
diff --git a/program/bindings/perl-piped/rrdpl.dsw b/program/bindings/perl-piped/rrdpl.dsw
new file mode 100644 (file)
index 0000000..73296e1
--- /dev/null
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 5.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "rrd"=".\rrd.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/program/bindings/perl-piped/t/base.t b/program/bindings/perl-piped/t/base.t
new file mode 100755 (executable)
index 0000000..5f0b2ae
--- /dev/null
@@ -0,0 +1,42 @@
+#! /usr/bin/perl 
+
+# this exercises just the perl module .. not RRDtool as such ... 
+
+BEGIN { $| = 1; print "1..5\n"; }
+END {
+  print "not ok 1\n" unless $loaded;
+  unlink "demo.rrd";
+}
+
+sub ok
+{
+    $ok_count++;
+    my($what, $result) = @_ ;
+    print "not " unless $result;
+    print "ok $ok_count $what\n";
+}
+
+use RRDp;
+
+$loaded = 1;
+$ok_count = 1;
+
+print "ok 1 module load\n";
+
+ok("RRDp::start", RRDp::start "../../src/rrdtool" > 0);
+
+$now=time();
+RRDp::cmd qw(create demo.rrd --start ), $now, qw(--step 100 ),
+  qw( DS:in:GAUGE:100:U:U RRA:AVERAGE:0.5:1:10 );
+
+$answer = RRDp::read;
+ok("RRDp::cmd",  -s "demo.rrd" );
+
+RRDp::cmd qw(last demo.rrd);
+$answer = RRDp::read;
+
+ok("RRDp::read", $$answer =~ /$now/);
+
+$status = RRDp::end;
+
+ok("RRDp::end", $status == 0);
diff --git a/program/bindings/perl-shared/MANIFEST b/program/bindings/perl-shared/MANIFEST
new file mode 100644 (file)
index 0000000..0f30b7e
--- /dev/null
@@ -0,0 +1,7 @@
+ntmake-build
+MANIFEST
+README
+Makefile.PL
+RRDs.pm
+RRDs.xs
+t/base.t
diff --git a/program/bindings/perl-shared/Makefile.PL b/program/bindings/perl-shared/Makefile.PL
new file mode 100644 (file)
index 0000000..c457307
--- /dev/null
@@ -0,0 +1,47 @@
+use ExtUtils::MakeMaker;
+use Config;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+
+# if the last argument when calling Makefile.PL is RPATH=/... and ... is the
+# path to librrd.so then the Makefile will be written such that RRDs.so knows
+# where to find librrd.so later on ... 
+my $R="";
+if ($ARGV[-1] =~ /RPATH=(\S+)/){
+       pop @ARGV;
+       my $rp = $1;
+       for ($^O){
+               /linux/   && do{ $R = "-Wl,--rpath -Wl,$rp"};
+               /hpux/    && do{ $R = "+b$rp"};
+               /solaris/ && do{ $R = "-R$rp"};
+               /bsd/     && do{ $R = "-R$rp"};
+                /aix/     && do{ $R = "-Wl,-blibpath:$rp"};
+       }
+}
+
+# darwin works without this because librrd contains its
+# install_name which will includes the final location of the
+# library after it is installed. This install_name gets transfered
+# to the perl shared object.
+
+my $librrd;
+if ($^O eq 'darwin'){
+        $librrd = '-lrrd';
+}
+else {
+        $librrd = "-L../../src/.libs/ $R -lrrd";
+}
+
+WriteMakefile(
+    'NAME'         => 'RRDs',
+    'VERSION_FROM' => 'RRDs.pm', # finds $VERSION
+    'DEFINE'      => "-DPERLPATCHLEVEL=$Config{PATCHLEVEL}",
+    'INC'          => '-I../../src',
+    # Perl will figure out which one is valid
+    #'dynamic_lib'  => {'OTHERLDFLAGS' => "$librrd -lm"},
+    'depend'       => {'RRDs.c' => "../../src/librrd.la"},
+    'LDFROM'       => '$(OBJECT) '.$librrd, 
+    'realclean'    => {FILES => 't/demo?.rrd t/demo?.png' },
+    ($^O eq 'darwin') ? ( 'LDDLFLAGS'    => "-L../../src/.libs/ $Config{lddlflags}" ) : ()
+);
+
diff --git a/program/bindings/perl-shared/README b/program/bindings/perl-shared/README
new file mode 100644 (file)
index 0000000..0918ecf
--- /dev/null
@@ -0,0 +1,12 @@
+These are the Perl bindings for rrdtool as a shared library. To compile do
+the following:
+
+perl Makefile.PL
+make test
+
+(win32 users try perl ntmake.pl)
+
+* if dynamic linking does not work, try
+
+perl Makefile.PL LINKTYPE=static
+make test
diff --git a/program/bindings/perl-shared/RRDs.pm b/program/bindings/perl-shared/RRDs.pm
new file mode 100644 (file)
index 0000000..3da8ffd
--- /dev/null
@@ -0,0 +1,152 @@
+package RRDs;
+
+use strict;
+use vars qw(@ISA $VERSION);
+
+@ISA = qw(DynaLoader);
+
+require DynaLoader;
+
+$VERSION=1.4003;
+
+bootstrap RRDs $VERSION;
+
+1;
+__END__
+
+=head1 NAME
+
+RRDs - Access RRDtool as a shared module
+
+=head1 SYNOPSIS
+
+  use RRDs;
+  RRDs::error
+  RRDs::last ...
+  RRDs::info ...
+  RRDs::create ...
+  RRDs::update ...
+  RRDs::updatev ...
+  RRDs::graph ...
+  RRDs::fetch ...
+  RRDs::tune ...
+  RRDs::times(start, end)
+  RRDs::dump ...
+  RRDs::restore ...
+  RRDs::flushcached ...
+
+=head1 DESCRIPTION
+
+=head2 Calling Sequence
+
+This module accesses RRDtool functionality directly from within Perl. The
+arguments to the functions listed in the SYNOPSIS are explained in the regular
+RRDtool documentation. The command line call
+
+ rrdtool update mydemo.rrd --template in:out N:12:13
+
+gets turned into
+
+ RRDs::update ("mydemo.rrd", "--template", "in:out", "N:12:13");
+
+Note that
+
+ --template=in:out
+
+is also valid.
+
+The RRDs::times function takes two parameters:  a "start" and "end" time.
+These should be specified in the B<AT-STYLE TIME SPECIFICATION> format
+used by RRDtool.  See the B<rrdfetch> documentation for a detailed
+explanation on how to specify time.
+
+=head2 Error Handling
+
+The RRD functions will not abort your program even when they can not make
+sense out of the arguments you fed them.
+
+The function RRDs::error should be called to get the error status
+after each function call. If RRDs::error does not return anything
+then the previous function has completed its task successfully.
+
+ use RRDs;
+ RRDs::update ("mydemo.rrd","N:12:13");
+ my $ERR=RRDs::error;
+ die "ERROR while updating mydemo.rrd: $ERR\n" if $ERR;
+
+=head2 Return Values
+
+The functions RRDs::last, RRDs::graph, RRDs::info, RRDs::fetch and RRDs::times
+return their findings.
+
+B<RRDs::last> returns a single INTEGER representing the last update time.
+
+ $lastupdate = RRDs::last ...
+
+B<RRDs::graph> returns an ARRAY containing the x-size and y-size of the
+created image and a pointer to an array with the results of the PRINT arguments.
+
+ ($result_arr,$xsize,$ysize) = RRDs::graph ...
+ print "Imagesize: ${xsize}x${ysize}\n";
+ print "Averages: ", (join ", ", @$averages);
+
+B<RRDs::info> returns a pointer to a hash. The keys of the hash
+represent the property names of the RRD and the values of the hash are
+the values of the properties.  
+
+ $hash = RRDs::info "example.rrd";
+ foreach my $key (keys %$hash){
+   print "$key = $$hash{$key}\n";
+ }
+
+B<RRDs::graphv> takes the same parameters as B<RRDs::graph> but it returns a
+pointer to hash. The hash returned contains meta information about the
+graph. Like its size as well as the position of the graph area on the image.
+When calling with and empty filename than the contents of the graph will be
+returned in the hash as well (key 'image').
+
+B<RRDs::updatev> also returns a pointer to hash. The keys of the hash
+are concatenated strings of a timestamp, RRA index, and data source name for
+each consolidated data point (CDP) written to disk as a result of the
+current update call. The hash values are CDP values.
+
+B<RRDs::fetch> is the most complex of
+the pack regarding return values. There are 4 values. Two normal
+integers, a pointer to an array and a pointer to a array of pointers.
+
+  my ($start,$step,$names,$data) = RRDs::fetch ... 
+  print "Start:       ", scalar localtime($start), " ($start)\n";
+  print "Step size:   $step seconds\n";
+  print "DS names:    ", join (", ", @$names)."\n";
+  print "Data points: ", $#$data + 1, "\n";
+  print "Data:\n";
+  for my $line (@$data) {
+    print "  ", scalar localtime($start), " ($start) ";
+    $start += $step;
+    for my $val (@$line) {
+      printf "%12.1f ", $val;
+    }
+    print "\n";
+  }
+
+B<RRDs::times> returns two integers which are the number of seconds since
+epoch (1970-01-01) for the supplied "start" and "end" arguments, respectively.
+
+See the examples directory for more ways to use this extension.
+
+=head1 NOTE
+
+If you are manipulating the TZ variable you should also call the POSIX
+function L<tzset(3)> to initialize all internal state of the library for properly
+operating in the timezone of your choice.
+
+ use POSIX qw(tzset);
+ $ENV{TZ} = 'CET';   
+ POSIX::tzset();     
+
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
+=cut
diff --git a/program/bindings/perl-shared/RRDs.ppd b/program/bindings/perl-shared/RRDs.ppd
new file mode 100755 (executable)
index 0000000..3a28cd9
--- /dev/null
@@ -0,0 +1,10 @@
+<SOFTPKG NAME="RRDs" VERSION="1,100001,0,0">
+       <TITLE>RRDs</TITLE>
+       <ABSTRACT>Round Robin Database Tool</ABSTRACT>
+       <AUTHOR>Tobias Oetiker (tobi@oetiker.ch)</AUTHOR>
+       <IMPLEMENTATION>
+               <OS NAME="MSWin32" />
+               <ARCHITECTURE NAME="MSWin32-x86-multi-thread-5.8" />
+               <CODEBASE HREF="RRDs.tar.gz" />
+       </IMPLEMENTATION>
+</SOFTPKG>
diff --git a/program/bindings/perl-shared/RRDs.xs b/program/bindings/perl-shared/RRDs.xs
new file mode 100644 (file)
index 0000000..029b3be
--- /dev/null
@@ -0,0 +1,456 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+ * rrd_tool.h includes config.h, but at least on Ubuntu Breezy Badger
+ * 5.10 with gcc 4.0.2, the C preprocessor picks up Perl's config.h
+ * which is included from the Perl includes and never reads rrdtool's
+ * config.h.  Without including rrdtool's config.h, this module does
+ * not compile, so include it here with an explicit path.
+ *
+ * Because rrdtool's config.h redefines VERSION which is originally
+ * set via Perl's Makefile.PL and passed down to the C compiler's
+ * command line, save the original value and reset it after the
+ * includes.
+ */
+#define VERSION_SAVED VERSION
+#undef VERSION
+#include "../../rrd_config.h"
+#include "../../src/rrd_tool.h"
+#undef VERSION
+#define VERSION VERSION_SAVED
+#undef VERSION_SAVED
+
+/* perl 5.004 compatibility */
+#if PERLPATCHLEVEL < 5 
+#define PL_sv_undef sv_undef
+#endif
+
+
+#define rrdcode(name) \
+               argv = (char **) malloc((items+1)*sizeof(char *));\
+               argv[0] = "dummy";\
+               for (i = 0; i < items; i++) { \
+                   STRLEN len; \
+                   char *handle= SvPV(ST(i),len);\
+                   /* actually copy the data to make sure possible modifications \
+                      on the argv data does not backfire into perl */ \
+                   argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char)); \
+                   strcpy(argv[i+1],handle); \
+               } \
+               rrd_clear_error();\
+               RETVAL=name(items+1,argv); \
+               for (i=0; i < items; i++) {\
+                   free(argv[i+1]);\
+               } \
+               free(argv);\
+               \
+               if (rrd_test_error()) XSRETURN_UNDEF;
+
+#define hvs(VAL) hv_store_ent(hash, sv_2mortal(newSVpv(data->key,0)),VAL,0)                
+
+#define rrdinfocode(name) \
+               /* prepare argument list */ \
+               argv = (char **) malloc((items+1)*sizeof(char *)); \
+               argv[0] = "dummy"; \
+               for (i = 0; i < items; i++) { \
+                   STRLEN len; \
+                   char *handle= SvPV(ST(i),len); \
+                   /* actually copy the data to make sure possible modifications \
+                      on the argv data does not backfire into perl */ \
+                   argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char)); \
+                   strcpy(argv[i+1],handle); \
+               } \
+                rrd_clear_error(); \
+                data=name(items+1, argv); \
+                for (i=0; i < items; i++) { \
+                   free(argv[i+1]); \
+               } \
+               free(argv); \
+                if (rrd_test_error()) XSRETURN_UNDEF; \
+                hash = newHV(); \
+               save=data; \
+                while (data) { \
+               /* the newSV will get copied by hv so we create it as a mortal \
+           to make sure it does not keep hanging round after the fact */ \
+                   switch (data->type) { \
+                   case RD_I_VAL: \
+                       if (isnan(data->value.u_val)) \
+                           hvs(&PL_sv_undef); \
+                       else \
+                           hvs(newSVnv(data->value.u_val)); \
+                       break; \
+                       case RD_I_INT: \
+                       hvs(newSViv(data->value.u_int)); \
+                       break; \
+                   case RD_I_CNT: \
+                       hvs(newSViv(data->value.u_cnt)); \
+                       break; \
+                   case RD_I_STR: \
+                       hvs(newSVpv(data->value.u_str,0)); \
+                       break; \
+                   case RD_I_BLO: \
+                       hvs(newSVpv(data->value.u_blo.ptr,data->value.u_blo.size)); \
+                       break; \
+                   } \
+                   data = data->next; \
+               } \
+            rrd_info_free(save); \
+            RETVAL = newRV_noinc((SV*)hash);
+
+/*
+ * should not be needed if libc is linked (see ntmake.pl)
+#ifdef WIN32
+ #define free free
+ #define malloc malloc
+ #define realloc realloc
+#endif
+*/
+
+
+MODULE = RRDs  PACKAGE = RRDs  PREFIX = rrd_
+
+BOOT:
+#ifdef MUST_DISABLE_SIGFPE
+       signal(SIGFPE,SIG_IGN);
+#endif
+#ifdef MUST_DISABLE_FPMASK
+       fpsetmask(0);
+#endif 
+       
+
+SV*
+rrd_error()
+       CODE:
+               if (! rrd_test_error()) XSRETURN_UNDEF;
+                RETVAL = newSVpv(rrd_get_error(),0);
+       OUTPUT:
+               RETVAL
+
+       
+int
+rrd_last(...)
+      PROTOTYPE: @
+      PREINIT:
+      int i;
+      char **argv;
+      CODE:
+              rrdcode(rrd_last);
+      OUTPUT:
+            RETVAL
+
+int
+rrd_first(...)
+      PROTOTYPE: @
+      PREINIT:
+      int i;
+      char **argv;
+      CODE:
+              rrdcode(rrd_first);
+      OUTPUT:
+            RETVAL
+
+
+int
+rrd_create(...)
+       PROTOTYPE: @    
+       PREINIT:
+        int i;
+       char **argv;
+       CODE:
+               rrdcode(rrd_create);
+               RETVAL = 1;
+        OUTPUT:
+               RETVAL
+
+
+int
+rrd_update(...)
+       PROTOTYPE: @    
+       PREINIT:
+        int i;
+       char **argv;
+       CODE:
+               rrdcode(rrd_update);
+                       RETVAL = 1;
+       OUTPUT:
+               RETVAL
+
+
+int
+rrd_tune(...)
+       PROTOTYPE: @    
+       PREINIT:
+        int i;
+       char **argv;
+       CODE:
+               rrdcode(rrd_tune);
+                       RETVAL = 1;
+       OUTPUT:
+               RETVAL
+
+
+SV *
+rrd_graph(...)
+       PROTOTYPE: @    
+       PREINIT:
+       char **calcpr=NULL;
+       int i,xsize,ysize;
+       double ymin,ymax;
+       char **argv;
+       AV *retar;
+       PPCODE:
+               argv = (char **) malloc((items+1)*sizeof(char *));
+               argv[0] = "dummy";
+               for (i = 0; i < items; i++) { 
+                   STRLEN len;
+                   char *handle = SvPV(ST(i),len);
+                   /* actually copy the data to make sure possible modifications
+                      on the argv data does not backfire into perl */ 
+                   argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char));
+                   strcpy(argv[i+1],handle);
+               }
+               rrd_clear_error();
+               rrd_graph(items+1,argv,&calcpr,&xsize,&ysize,NULL,&ymin,&ymax); 
+               for (i=0; i < items; i++) {
+                   free(argv[i+1]);
+               }
+               free(argv);
+
+               if (rrd_test_error()) {
+                       if(calcpr)
+                          for(i=0;calcpr[i];i++)
+                               rrd_freemem(calcpr[i]);
+                       XSRETURN_UNDEF;
+               }
+               retar=newAV();
+               if(calcpr){
+                       for(i=0;calcpr[i];i++){
+                                av_push(retar,newSVpv(calcpr[i],0));
+                                rrd_freemem(calcpr[i]);
+                       }
+                       rrd_freemem(calcpr);
+               }
+               EXTEND(sp,4);
+               PUSHs(sv_2mortal(newRV_noinc((SV*)retar)));
+               PUSHs(sv_2mortal(newSViv(xsize)));
+               PUSHs(sv_2mortal(newSViv(ysize)));
+
+SV *
+rrd_fetch(...)
+       PROTOTYPE: @    
+       PREINIT:
+               time_t        start,end;                
+               unsigned long step, ds_cnt,i,ii;
+               rrd_value_t   *data,*datai;
+               char **argv;
+               char **ds_namv;
+               AV *retar,*line,*names;
+       PPCODE:
+               argv = (char **) malloc((items+1)*sizeof(char *));
+               argv[0] = "dummy";
+               for (i = 0; i < items; i++) { 
+                   STRLEN len;
+                   char *handle= SvPV(ST(i),len);
+                   /* actually copy the data to make sure possible modifications
+                      on the argv data does not backfire into perl */ 
+                   argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char));
+                   strcpy(argv[i+1],handle);
+               }
+               rrd_clear_error();
+               rrd_fetch(items+1,argv,&start,&end,&step,&ds_cnt,&ds_namv,&data); 
+               for (i=0; i < items; i++) {
+                   free(argv[i+1]);
+               }
+               free(argv);
+               if (rrd_test_error()) XSRETURN_UNDEF;
+                /* convert the ds_namv into perl format */
+               names=newAV();
+               for (ii = 0; ii < ds_cnt; ii++){
+                   av_push(names,newSVpv(ds_namv[ii],0));
+                   rrd_freemem(ds_namv[ii]);
+               }
+               rrd_freemem(ds_namv);                   
+               /* convert the data array into perl format */
+               datai=data;
+               retar=newAV();
+               for (i = start+step; i <= end; i += step){
+                       line = newAV();
+                       for (ii = 0; ii < ds_cnt; ii++){
+                         av_push(line,(isnan(*datai) ? &PL_sv_undef : newSVnv(*datai)));
+                         datai++;
+                       }
+                       av_push(retar,newRV_noinc((SV*)line));
+               }
+               rrd_freemem(data);
+               EXTEND(sp,5);
+               PUSHs(sv_2mortal(newSViv(start+step)));
+               PUSHs(sv_2mortal(newSViv(step)));
+               PUSHs(sv_2mortal(newRV_noinc((SV*)names)));
+               PUSHs(sv_2mortal(newRV_noinc((SV*)retar)));
+
+SV *
+rrd_times(start, end)
+         char *start
+         char *end
+       PREINIT:
+               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 = rrd_parsetime(start, &start_tv))) {
+                       rrd_set_error("start time: %s", parsetime_error);
+                       XSRETURN_UNDEF;
+               }
+               if ((parsetime_error = rrd_parsetime(end, &end_tv))) {
+                       rrd_set_error("end time: %s", parsetime_error);
+                       XSRETURN_UNDEF;
+               }
+               if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+                       XSRETURN_UNDEF;
+               }
+               EXTEND(sp,2);
+               PUSHs(sv_2mortal(newSVuv(start_tmp)));
+               PUSHs(sv_2mortal(newSVuv(end_tmp)));
+
+int
+rrd_xport(...)
+       PROTOTYPE: @    
+       PREINIT:
+                time_t start,end;              
+                int xsize;
+               unsigned long step, col_cnt,row_cnt,i,ii;
+               rrd_value_t *data,*ptr;
+                char **argv,**legend_v;
+               AV *retar,*line,*names;
+       PPCODE:
+               argv = (char **) malloc((items+1)*sizeof(char *));
+               argv[0] = "dummy";
+               for (i = 0; i < items; i++) { 
+                   STRLEN len;
+                   char *handle = SvPV(ST(i),len);
+                   /* actually copy the data to make sure possible modifications
+                      on the argv data does not backfire into perl */ 
+                   argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char));
+                   strcpy(argv[i+1],handle);
+               }
+               rrd_clear_error();
+               rrd_xport(items+1,argv,&xsize,&start,&end,&step,&col_cnt,&legend_v,&data); 
+               for (i=0; i < items; i++) {
+                   free(argv[i+1]);
+               }
+               free(argv);
+               if (rrd_test_error()) XSRETURN_UNDEF;
+
+                /* convert the legend_v into perl format */
+               names=newAV();
+               for (ii = 0; ii < col_cnt; ii++){
+                   av_push(names,newSVpv(legend_v[ii],0));
+                   rrd_freemem(legend_v[ii]);
+               }
+               rrd_freemem(legend_v);                  
+
+               /* convert the data array into perl format */
+               ptr=data;
+               retar=newAV();
+               for (i = start+step; i <= end; i += step){
+                       line = newAV();
+                       for (ii = 0; ii < col_cnt; ii++){
+                         av_push(line,(isnan(*ptr) ? &PL_sv_undef : newSVnv(*ptr)));
+                         ptr++;
+                       }
+                       av_push(retar,newRV_noinc((SV*)line));
+               }
+               rrd_freemem(data);
+
+               EXTEND(sp,7);
+               PUSHs(sv_2mortal(newSViv(start+step)));
+               PUSHs(sv_2mortal(newSViv(end)));
+               PUSHs(sv_2mortal(newSViv(step)));
+               PUSHs(sv_2mortal(newSViv(col_cnt)));
+               PUSHs(sv_2mortal(newRV_noinc((SV*)names)));
+               PUSHs(sv_2mortal(newRV_noinc((SV*)retar)));
+
+SV*
+rrd_info(...)
+       PROTOTYPE: @    
+       PREINIT:
+               rrd_info_t *data,*save;
+                int i;
+                char **argv;
+               HV *hash;
+       CODE:
+               rrdinfocode(rrd_info);  
+    OUTPUT:
+          RETVAL
+
+SV*
+rrd_updatev(...)
+       PROTOTYPE: @    
+       PREINIT:
+               rrd_info_t *data,*save;
+                int i;
+                char **argv;
+               HV *hash;
+       CODE:
+               rrdinfocode(rrd_update_v);      
+    OUTPUT:
+          RETVAL
+
+SV*
+rrd_graphv(...)
+       PROTOTYPE: @    
+       PREINIT:
+               rrd_info_t *data,*save;
+                int i;
+                char **argv;
+               HV *hash;
+       CODE:
+               rrdinfocode(rrd_graph_v);       
+    OUTPUT:
+          RETVAL
+
+int
+rrd_dump(...)
+       PROTOTYPE: @
+       PREINIT:
+        int i;
+       char **argv;
+       CODE:
+               rrdcode(rrd_dump);
+                       RETVAL = 1;
+       OUTPUT:
+               RETVAL
+
+int
+rrd_restore(...)
+       PROTOTYPE: @
+       PREINIT:
+        int i;
+       char **argv;
+       CODE:
+               rrdcode(rrd_restore);
+                       RETVAL = 1;
+       OUTPUT:
+               RETVAL
+
+int
+rrd_flushcached(...)
+       PROTOTYPE: @
+       PREINIT:
+       int i;
+       char **argv;
+       CODE:
+               rrdcode(rrd_flushcached);
+       OUTPUT:
+               RETVAL
diff --git a/program/bindings/perl-shared/ntmake-build b/program/bindings/perl-shared/ntmake-build
new file mode 100644 (file)
index 0000000..047b76f
--- /dev/null
@@ -0,0 +1,27 @@
+use ExtUtils::MakeMaker;
+use Config;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+# Run VCVARS32.BAT before generating makefile/compiling.
+WriteMakefile(
+    'NAME'     => 'RRDs',
+    'VERSION_FROM' => 'RRDs.pm',
+#    'DEFINE'     => "-DPERLPATCHLEVEL=$Config{PATCHLEVEL}",
+# keep compatible w/ ActiveState 5xx builds
+    'DEFINE'      => "-DPERLPATCHLEVEL=5",
+
+   'INC'       => '-I../../src/ "-I/Program Files/GnuWin32/include"',
+# Since we are now using GnuWin32 libraries dynamically (instead of static
+# complile with code redistributed with rrdtool), use /MD instead of /MT.
+# Yes, this means we need msvcrt.dll but GnuWin32 dlls already require it
+# and it is available on most versions of Windows.
+   'OPTIMIZE' => '-O2 -MD',
+   'LIBS'  => '../../src/release/rrd.lib "/Program Files/GnuWin32/lib/libart_lgpl.lib" "/Program Files/GnuWin32/lib/libz.lib" "/Program Files/GnuWin32/lib/libpng.lib" "/Program Files/GnuWin32/lib/libfreetype.lib"', 
+    'realclean'    => {FILES => 't/demo?.rrd t/demo?.png' },
+    ($] ge '5.005') ? (
+        'AUTHOR' => 'Tobias Oetiker (tobi@oetiker.ch)',
+        'ABSTRACT' => 'Round Robin Database Tool',
+    ) : ()
+
+
+);
diff --git a/program/bindings/perl-shared/t/base.t b/program/bindings/perl-shared/t/base.t
new file mode 100755 (executable)
index 0000000..7b6c016
--- /dev/null
@@ -0,0 +1,171 @@
+#! /usr/bin/perl 
+
+BEGIN { $| = 1; print "1..7\n"; }
+END {
+  print "not ok 1\n" unless $loaded;
+  unlink "demo.rrd";
+}
+
+sub ok
+{
+    my($what, $result) = @_ ;
+    $ok_count++;
+    print "not " unless $result;
+    print "ok $ok_count $what\n";
+}
+
+use strict;
+use vars qw(@ISA $loaded);
+
+use RRDs;
+$loaded = 1;
+my $ok_count = 1;
+
+ok("loading",1);
+
+######################### End of black magic.
+
+my $STEP  = 100;
+my $RUNS  = 500;
+my $GRUNS = 4;
+my $RRD1  = "demo1.rrd";
+my $RRD2  = "demo2.rrd";
+my $PNG1  = "demo1.png";
+my $PNG2  = "demo2.png";
+my $time  = 30*int(time/30);
+my $START = $time-$RUNS*$STEP;
+
+my @options = ("-b", $START, "-s", $STEP,
+ "DS:a:GAUGE:2000:U:U",
+ "DS:b:GAUGE:200:U:U",
+ "DS:c:GAUGE:200:U:U",
+ "DS:d:GAUGE:200:U:U",
+ "DS:e:DERIVE:200:U:U",
+ "RRA:AVERAGE:0.5:1:5000",
+ "RRA:AVERAGE:0.5:10:500");
+
+print "* Creating RRD $RRD1 starting at $time.\n\n";
+RRDs::create $RRD1, @options;
+
+my $ERROR = RRDs::error;
+ok("create 1", !$ERROR);                                                       #  2
+if ($ERROR) {
+  die "$0: unable to create `$RRD1': $ERROR\n";
+}
+
+print "* Creating RRD $RRD2 starting at $time.\n\n";
+RRDs::create $RRD2, @options;
+
+$ERROR= RRDs::error;
+ok("create 2",!$ERROR);                                                        #  3
+if ($ERROR) {
+  die "$0: unable to create `$RRD2': $ERROR\n";
+}
+
+my $last = RRDs::last $RRD1;
+if ($ERROR = RRDs::error) {
+  die "$0: unable to get last `$RRD1': $ERROR\n";
+}
+ok("last 1", $last == $START);                                         #  4
+
+$last = RRDs::last $RRD2;
+if ($ERROR = RRDs::error) {
+  die "$0: unable to get last `$RRD2': $ERROR\n";
+}
+ok("last 2", $last == $START);                                         #  5
+
+print "* Filling $RRD1 and $RRD2 with $RUNS*5 values. One moment please ...\n";
+print "* If you are running over NFS this will take *MUCH* longer\n\n";
+
+srand(int($time / 100));
+
+@options = ();
+
+my $counter = 1e7;
+for (my $t=$START+1;
+     $t<$START+$STEP*$RUNS;
+     $t+=$STEP+int((rand()-0.5)*7)){
+  $counter += int(2500*sin($t/2000)*$STEP);
+  my $data = (1000+500*sin($t/1000)).":".
+      (1000+900*sin($t/2330)).":".
+      (2000*cos($t/1550)).":".
+      (3220*sin($t/3420)).":$counter";
+  push(@options, "$t:$data");
+  RRDs::update $RRD1, "$t:$data";
+  if ($ERROR = RRDs::error) {
+    warn "$0: unable to update `$RRD1': $ERROR\n";
+  }
+}
+
+ok("update 1",!$ERROR);                                                        #  3
+
+RRDs::update $RRD2, @options;
+
+ok("update 2",!$ERROR);                                                        #  3
+
+if ($ERROR = RRDs::error) {
+  die "$0: unable to update `$RRD2': $ERROR\n";
+}
+
+print "* Creating $GRUNS graphs: $PNG1 & $PNG2\n\n";
+my $now = $time;
+for (my $i=0;$i<$GRUNS;$i++) {
+  my @rrd_pngs = ($RRD1, $PNG1, $RRD2, $PNG2);
+  while (@rrd_pngs) {
+    my $RRD = shift(@rrd_pngs);
+    my $PNG = shift(@rrd_pngs);
+    my ($graphret,$xs,$ys) = RRDs::graph $PNG, "--title", 'Test GRAPH',
+          "--vertical-label", 'Dummy Units', "--start", (-$RUNS*$STEP),
+          "DEF:alpha=$RRD:a:AVERAGE",
+          "DEF:beta=$RRD:b:AVERAGE",
+          "DEF:gamma=$RRD:c:AVERAGE",
+          "DEF:delta=$RRD:d:AVERAGE",
+          "DEF:epsilon=$RRD:e:AVERAGE",
+          "CDEF:calc=alpha,beta,+,2,/",
+          "AREA:alpha#0022e9:Short",
+          "STACK:beta#00b871:Demo Text",
+          "LINE1:gamma#ff0000:Line 1",
+          "LINE2:delta#888800:Line 2",
+          "LINE3:calc#00ff44:Line 3",
+          "LINE3:epsilon#000000:Line 4",
+          "HRULE:1500#ff8800:Horizontal Line at 1500",
+          "PRINT:alpha:AVERAGE:Average Alpha %1.2lf",
+          "PRINT:alpha:MIN:Min Alpha %1.2lf %s",
+          "PRINT:alpha:MIN:Min Alpha %1.2lf",
+          "PRINT:alpha:MAX:Max Alpha %1.2lf",
+          "GPRINT:calc:AVERAGE:Average calc %1.2lf %s",
+          "GPRINT:calc:AVERAGE:Average calc %1.2lf",
+          "GPRINT:calc:MAX:Max calc %1.2lf",
+          "GPRINT:calc:MIN:Min calc %1.2lf",
+          "VRULE:".($now-3600)."#008877:60 Minutes ago",
+          "VRULE:".($now-7200)."#008877:120 Minutes ago";
+
+    if ($ERROR = RRDs::error) {
+      print "ERROR: $ERROR\n";
+    } else {
+      print "Image Size: ${xs}x${ys}\n";
+      print "Graph Return:\n",(join "\n", @$graphret),"\n\n";
+    }
+  }
+}
+
+
+
+my ($start,$step,$names,$array) = RRDs::fetch $RRD1, "AVERAGE";
+$ERROR = RRDs::error;
+print "ERROR: $ERROR\n" if $ERROR ;
+print "start=$start, step=$step\n";
+print "             "; 
+map {printf("%12s",$_)} @$names ;
+foreach my $line (@$array){
+  print "".localtime($start),"   ";
+  $start += $step; 
+  foreach my $val (@$line) {           
+    if (not defined $val){
+        printf "%12s", "UNKNOWN";
+    } else {
+      printf "%12.1f", $val;
+    }
+  }
+  print "\n";
+}
diff --git a/program/bindings/python/ACKNOWLEDGEMENT b/program/bindings/python/ACKNOWLEDGEMENT
new file mode 100644 (file)
index 0000000..8cf3658
--- /dev/null
@@ -0,0 +1,7 @@
+ACKNOWLEDGMENT
+==============
+
+This is a list of people who have made contributions to py-rrdtool.
+
+Matthew W. Samsonoff <mws@rochester.rr.com>
+Brian E. Gallew <geek+python@cmu.edu>
diff --git a/program/bindings/python/AUTHORS b/program/bindings/python/AUTHORS
new file mode 100644 (file)
index 0000000..b3b5713
--- /dev/null
@@ -0,0 +1 @@
+Hye-Shik Chang <perky@fallin.lv>
diff --git a/program/bindings/python/COPYING b/program/bindings/python/COPYING
new file mode 100644 (file)
index 0000000..b1e3f5a
--- /dev/null
@@ -0,0 +1,504 @@
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                 GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/program/bindings/python/README b/program/bindings/python/README
new file mode 100644 (file)
index 0000000..c234a6b
--- /dev/null
@@ -0,0 +1,34 @@
+Based on Python-RRDtool 0.2.1
+-----------------------------
+
+The python-rrdtool provides a interface to rrdtool, the wonderful
+graphing and logging utility. This wrapper implementation has
+worked from the scratch (without SWIG), and it's under LGPL.
+
+This module have not documented yet. Please refer pydoc.
+
+
+Project
+-------
+
+Homepage: http://www.nongnu.org/py-rrdtool/
+
+Mailing Lists:
+
+  CVS Checkins     py-rrdtool-cvs@nongnu.org
+  Users & Hackers  py-rrdtool-users@nongnu.org
+
+
+Original Author
+---------------
+
+Hye-Shik Chang <perky@FreeBSD.org>
+
+Any comments, suggestions, and/or patches are very welcome.
+Thank you for using py-rrdtool!
+
+
+CHANGES
+-------
+2008-05-19 - tobi
+* rewrote the info method to conform to rrdtool info standard
diff --git a/program/bindings/python/rrdtoolmodule.c b/program/bindings/python/rrdtoolmodule.c
new file mode 100644 (file)
index 0000000..a2647b5
--- /dev/null
@@ -0,0 +1,642 @@
+/*
+ * rrdtoolmodule.c
+ *
+ * RRDTool Python binding
+ *
+ * Author  : Hye-Shik Chang <perky@fallin.lv>
+ * Date    : $Date: 2003/02/22 07:41:19 $
+ * Created : 23 May 2002
+ *
+ * $Revision: 1.14 $
+ *
+ *  ==========================================================================
+ *  This file is part of py-rrdtool.
+ *
+ *  py-rrdtool is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published
+ *  by the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  py-rrdtool is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with Foobar; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifdef UNUSED
+#elif defined(__GNUC__)
+# define UNUSED(x) x __attribute__((unused))
+#elif defined(__LCLINT__)
+# define UNUSED(x) /*@unused@*/ x
+#else
+# define UNUSED(x) x
+#endif
+
+
+#include "../../rrd_config.h"
+static const char *__version__ = PACKAGE_VERSION;
+
+#include "Python.h"
+#include "../../src/rrd_tool.h"
+//#include "rrd.h"
+//#include "rrd_extra.h"
+
+static PyObject *ErrorObject;
+extern int optind;
+extern int opterr;
+
+/* forward declaration to keep compiler happy */
+void      initrrdtool(
+    void);
+
+static int create_args(
+    char *command,
+    PyObject * args,
+    int *argc,
+    char ***argv)
+{
+    PyObject *o, *lo;
+    int       args_count,
+              argv_count,
+              element_count,
+              i, j;
+
+    args_count = PyTuple_Size(args);
+    element_count = 0;
+    for (i = 0; i < args_count; i++) {
+        o = PyTuple_GET_ITEM(args, i);
+        if (PyString_Check(o))
+            element_count++;
+        else if (PyList_CheckExact(o))
+                element_count += PyList_Size(o);
+             else {
+                 PyErr_Format(PyExc_TypeError, "argument %d must be string or list of strings", i);
+                 return -1;
+             }
+    }
+   
+    *argv = PyMem_New(char *,
+                      element_count + 1);
+
+    if (*argv == NULL)
+        return -1;
+
+    argv_count = 0;
+    for (i = 0; i < args_count; i++) {
+        o = PyTuple_GET_ITEM(args, i);
+        if (PyString_Check(o)) {
+            argv_count++;
+            (*argv)[argv_count] = PyString_AS_STRING(o);
+        } else if (PyList_CheckExact(o))
+                   for (j = 0; j < PyList_Size(o); j++) {
+                       lo = PyList_GetItem(o, j);
+                       if (PyString_Check(lo)) {
+                           argv_count++;
+                           (*argv)[argv_count] = PyString_AS_STRING(lo);
+                       } else {
+                             PyMem_Del(*argv);
+                             PyErr_Format(PyExc_TypeError, "element %d in argument %d must be string", j, i);
+                             return -1;
+                       }
+                   }
+               else {
+                   PyMem_Del(*argv);
+                   PyErr_Format(PyExc_TypeError, "argument %d must be string or list of strings", i);
+                   return -1;
+               }
+    }
+
+    (*argv)[0] = command;
+    *argc = element_count + 1;
+
+    /* reset getopt state */
+    opterr = optind = 0;
+
+    return 0;
+}
+
+static void destroy_args(
+    char ***argv)
+{
+    PyMem_Del(*argv);
+    *argv = NULL;
+}
+
+static char PyRRD_create__doc__[] =
+    "create(args..): Set up a new Round Robin Database\n\
+    create filename [--start|-b start time] \
+[--step|-s step] [DS:ds-name:DST:heartbeat:min:max] \
+[RRA:CF:xff:steps:rows]";
+
+static PyObject *PyRRD_create(
+    PyObject UNUSED(*self),
+    PyObject * args)
+{
+    PyObject *r;
+    char    **argv;
+    int       argc;
+
+    if (create_args("create", args, &argc, &argv) < 0)
+        return NULL;
+
+    if (rrd_create(argc, argv) == -1) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        r = NULL;
+    } else {
+        Py_INCREF(Py_None);
+        r = Py_None;
+    }
+
+    destroy_args(&argv);
+    return r;
+}
+
+static char PyRRD_update__doc__[] =
+    "update(args..): Store a new set of values into the rrd\n"
+    "    update filename [--template|-t ds-name[:ds-name]...] "
+    "N|timestamp:value[:value...] [timestamp:value[:value...] ...]";
+
+static PyObject *PyRRD_update(
+    PyObject UNUSED(*self),
+    PyObject * args)
+{
+    PyObject *r;
+    char    **argv;
+    int       argc;
+
+    if (create_args("update", args, &argc, &argv) < 0)
+        return NULL;
+
+    if (rrd_update(argc, argv) == -1) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        r = NULL;
+    } else {
+        Py_INCREF(Py_None);
+        r = Py_None;
+    }
+
+    destroy_args(&argv);
+    return r;
+}
+
+static char PyRRD_fetch__doc__[] =
+    "fetch(args..): fetch data from an rrd.\n"
+    "    fetch filename CF [--resolution|-r resolution] "
+    "[--start|-s start] [--end|-e end]";
+
+static PyObject *PyRRD_fetch(
+    PyObject UNUSED(*self),
+    PyObject * args)
+{
+    PyObject *r;
+    rrd_value_t *data, *datai;
+    unsigned long step, ds_cnt;
+    time_t    start, end;
+    int       argc;
+    char    **argv, **ds_namv;
+
+    if (create_args("fetch", args, &argc, &argv) < 0)
+        return NULL;
+
+    if (rrd_fetch(argc, argv, &start, &end, &step,
+                  &ds_cnt, &ds_namv, &data) == -1) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        r = NULL;
+    } else {
+        /* Return :
+           ((start, end, step), (name1, name2, ...), [(data1, data2, ..), ...]) */
+        PyObject *range_tup, *dsnam_tup, *data_list, *t;
+        unsigned long i, j, row;
+        rrd_value_t dv;
+
+        row = (end - start) / step;
+
+        r = PyTuple_New(3);
+        range_tup = PyTuple_New(3);
+        dsnam_tup = PyTuple_New(ds_cnt);
+        data_list = PyList_New(row);
+        PyTuple_SET_ITEM(r, 0, range_tup);
+        PyTuple_SET_ITEM(r, 1, dsnam_tup);
+        PyTuple_SET_ITEM(r, 2, data_list);
+
+        datai = data;
+
+        PyTuple_SET_ITEM(range_tup, 0, PyInt_FromLong((long) start));
+        PyTuple_SET_ITEM(range_tup, 1, PyInt_FromLong((long) end));
+        PyTuple_SET_ITEM(range_tup, 2, PyInt_FromLong((long) step));
+
+        for (i = 0; i < ds_cnt; i++)
+            PyTuple_SET_ITEM(dsnam_tup, i, PyString_FromString(ds_namv[i]));
+
+        for (i = 0; i < row; i++) {
+            t = PyTuple_New(ds_cnt);
+            PyList_SET_ITEM(data_list, i, t);
+
+            for (j = 0; j < ds_cnt; j++) {
+                dv = *(datai++);
+                if (isnan(dv)) {
+                    PyTuple_SET_ITEM(t, j, Py_None);
+                    Py_INCREF(Py_None);
+                } else {
+                    PyTuple_SET_ITEM(t, j, PyFloat_FromDouble((double) dv));
+                }
+            }
+        }
+
+        for (i = 0; i < ds_cnt; i++)
+            rrd_freemem(ds_namv[i]);
+        rrd_freemem(ds_namv);   /* rrdtool don't use PyMem_Malloc :) */
+        rrd_freemem(data);
+    }
+
+    destroy_args(&argv);
+    return r;
+}
+
+static char PyRRD_graph__doc__[] =
+    "graph(args..): Create a graph based on data from one or several RRD\n"
+    "    graph filename [-s|--start seconds] "
+    "[-e|--end seconds] [-x|--x-grid x-axis grid and label] "
+    "[-y|--y-grid y-axis grid and label] [--alt-y-grid] [--alt-y-mrtg] "
+    "[--alt-autoscale] [--alt-autoscale-max] [--units-exponent] value "
+    "[-v|--vertical-label text] [-w|--width pixels] [-h|--height pixels] "
+    "[-i|--interlaced] "
+    "[-f|--imginfo formatstring] [-a|--imgformat GIF|PNG|GD] "
+    "[-B|--background value] [-O|--overlay value] "
+    "[-U|--unit value] [-z|--lazy] [-o|--logarithmic] "
+    "[-u|--upper-limit value] [-l|--lower-limit value] "
+    "[-g|--no-legend] [-r|--rigid] [--step value] "
+    "[-b|--base value] [-c|--color COLORTAG#rrggbb] "
+    "[-t|--title title] [DEF:vname=rrd:ds-name:CF] "
+    "[CDEF:vname=rpn-expression] [PRINT:vname:CF:format] "
+    "[GPRINT:vname:CF:format] [COMMENT:text] "
+    "[HRULE:value#rrggbb[:legend]] [VRULE:time#rrggbb[:legend]] "
+    "[LINE{1|2|3}:vname[#rrggbb[:legend]]] "
+    "[AREA:vname[#rrggbb[:legend]]] " "[STACK:vname[#rrggbb[:legend]]]";
+
+static PyObject *PyRRD_graph(
+    PyObject UNUSED(*self),
+    PyObject * args)
+{
+    PyObject *r;
+    char    **argv, **calcpr;
+    int       argc, xsize, ysize, i;
+    double    ymin, ymax;
+
+    if (create_args("graph", args, &argc, &argv) < 0)
+        return NULL;
+
+    if (rrd_graph(argc, argv, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax) ==
+        -1) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        r = NULL;
+    } else {
+        r = PyTuple_New(3);
+
+        PyTuple_SET_ITEM(r, 0, PyInt_FromLong((long) xsize));
+        PyTuple_SET_ITEM(r, 1, PyInt_FromLong((long) ysize));
+
+        if (calcpr) {
+            PyObject *e, *t;
+
+            e = PyList_New(0);
+            PyTuple_SET_ITEM(r, 2, e);
+
+            for (i = 0; calcpr[i]; i++) {
+                t = PyString_FromString(calcpr[i]);
+                PyList_Append(e, t);
+                Py_DECREF(t);
+                rrd_freemem(calcpr[i]);
+            }
+            rrd_freemem(calcpr);
+        } else {
+            Py_INCREF(Py_None);
+            PyTuple_SET_ITEM(r, 2, Py_None);
+        }
+    }
+
+    destroy_args(&argv);
+    return r;
+}
+
+static char PyRRD_tune__doc__[] =
+    "tune(args...): Modify some basic properties of a Round Robin Database\n"
+    "    tune filename [--heartbeat|-h ds-name:heartbeat] "
+    "[--minimum|-i ds-name:min] [--maximum|-a ds-name:max] "
+    "[--data-source-type|-d ds-name:DST] [--data-source-rename|-r old-name:new-name]";
+
+static PyObject *PyRRD_tune(
+    PyObject UNUSED(*self),
+    PyObject * args)
+{
+    PyObject *r;
+    char    **argv;
+    int       argc;
+
+    if (create_args("tune", args, &argc, &argv) < 0)
+        return NULL;
+
+    if (rrd_tune(argc, argv) == -1) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        r = NULL;
+    } else {
+        Py_INCREF(Py_None);
+        r = Py_None;
+    }
+
+    destroy_args(&argv);
+    return r;
+}
+
+static char PyRRD_first__doc__[] =
+    "first(filename): Return the timestamp of the first data sample in an RRD";
+
+static PyObject *PyRRD_first(
+    PyObject UNUSED(*self),
+    PyObject * args)
+{
+    PyObject *r;
+    int       argc, ts;
+    char    **argv;
+
+    if (create_args("first", args, &argc, &argv) < 0)
+        return NULL;
+
+    if ((ts = rrd_first(argc, argv)) == -1) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        r = NULL;
+    } else
+        r = PyInt_FromLong((long) ts);
+
+    destroy_args(&argv);
+    return r;
+}
+
+static char PyRRD_last__doc__[] =
+    "last(filename): Return the timestamp of the last data sample in an RRD";
+
+static PyObject *PyRRD_last(
+    PyObject UNUSED(*self),
+    PyObject * args)
+{
+    PyObject *r;
+    int       argc, ts;
+    char    **argv;
+
+    if (create_args("last", args, &argc, &argv) < 0)
+        return NULL;
+
+    if ((ts = rrd_last(argc, argv)) == -1) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        r = NULL;
+    } else
+        r = PyInt_FromLong((long) ts);
+
+    destroy_args(&argv);
+    return r;
+}
+
+static char PyRRD_resize__doc__[] =
+    "resize(args...): alters the size of an RRA.\n"
+    "    resize filename rra-num GROW|SHRINK rows";
+
+static PyObject *PyRRD_resize(
+    PyObject UNUSED(*self),
+    PyObject * args)
+{
+    PyObject *r;
+    char    **argv;
+    int       argc, ts;
+
+    if (create_args("resize", args, &argc, &argv) < 0)
+        return NULL;
+
+    if ((ts = rrd_resize(argc, argv)) == -1) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        r = NULL;
+    } else {
+        Py_INCREF(Py_None);
+        r = Py_None;
+    }
+
+    destroy_args(&argv);
+    return r;
+}
+
+static PyObject *PyDict_FromInfo(
+    rrd_info_t * data)
+{
+    PyObject *r;
+
+    r = PyDict_New();
+    while (data) {
+        PyObject *val = NULL;
+
+        switch (data->type) {
+        case RD_I_VAL:
+            val = isnan(data->value.u_val)
+                ? (Py_INCREF(Py_None), Py_None)
+                : PyFloat_FromDouble(data->value.u_val);
+            break;
+        case RD_I_CNT:
+            val = PyLong_FromUnsignedLong(data->value.u_cnt);
+            break;
+        case RD_I_INT:
+            val = PyLong_FromLong(data->value.u_int);
+            break;
+        case RD_I_STR:
+            val = PyString_FromString(data->value.u_str);
+            break;
+        case RD_I_BLO:
+            val =
+                PyString_FromStringAndSize((char *) data->value.u_blo.ptr,
+                                           data->value.u_blo.size);
+            break;
+        }
+        if (val) {
+            PyDict_SetItemString(r, data->key, val);
+            Py_DECREF(val);
+        }
+        data = data->next;
+    }
+    return r;
+}
+
+static char PyRRD_info__doc__[] =
+    "info(filename): extract header information from an rrd";
+
+static PyObject *PyRRD_info(
+    PyObject UNUSED(*self),
+    PyObject * args)
+{
+    PyObject *r;
+    int       argc;
+    char    **argv;
+    rrd_info_t *data;
+
+    if (create_args("info", args, &argc, &argv) < 0)
+        return NULL;
+
+    if ((data = rrd_info(argc, argv)) == NULL) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        r = NULL;
+    } else {
+        r = PyDict_FromInfo(data);
+        rrd_info_free(data);
+    }
+
+    destroy_args(&argv);
+    return r;
+}
+
+static char PyRRD_graphv__doc__[] =
+    "graphv is called in the same manner as graph";
+
+static PyObject *PyRRD_graphv(
+    PyObject UNUSED(*self),
+    PyObject * args)
+{
+    PyObject *r;
+    int       argc;
+    char    **argv;
+    rrd_info_t *data;
+
+    if (create_args("graphv", args, &argc, &argv) < 0)
+        return NULL;
+
+    if ((data = rrd_graph_v(argc, argv)) == NULL) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        r = NULL;
+    } else {
+        r = PyDict_FromInfo(data);
+        rrd_info_free(data);
+    }
+
+    destroy_args(&argv);
+    return r;
+}
+
+static char PyRRD_updatev__doc__[] =
+    "updatev is called in the same manner as update";
+
+static PyObject *PyRRD_updatev(
+    PyObject UNUSED(*self),
+    PyObject * args)
+{
+    PyObject *r;
+    int       argc;
+    char    **argv;
+    rrd_info_t *data;
+
+    if (create_args("updatev", args, &argc, &argv) < 0)
+        return NULL;
+
+    if ((data = rrd_update_v(argc, argv)) == NULL) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        r = NULL;
+    } else {
+        r = PyDict_FromInfo(data);
+        rrd_info_free(data);
+    }
+
+    destroy_args(&argv);
+    return r;
+}
+
+static char PyRRD_flushcached__doc__[] =
+  "flush(args..): flush RRD files from memory\n"
+  "   flush [--daemon address] file [file ...]";
+
+static PyObject *PyRRD_flushcached(
+    PyObject UNUSED(*self),
+    PyObject * args)
+{
+    PyObject *r;
+    int       argc;
+    char    **argv;
+
+    if (create_args("flushcached", args, &argc, &argv) < 0)
+        return NULL;
+
+    if (rrd_flushcached(argc, argv) != 0) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        r = NULL;
+    } else {
+        Py_INCREF(Py_None);
+        r = Py_None;
+    }
+
+    destroy_args(&argv);
+    return r;
+}
+
+/* List of methods defined in the module */
+#define meth(name, func, doc) {name, (PyCFunction)func, METH_VARARGS, doc}
+
+static PyMethodDef _rrdtool_methods[] = {
+    meth("create", PyRRD_create, PyRRD_create__doc__),
+    meth("update", PyRRD_update, PyRRD_update__doc__),
+    meth("fetch", PyRRD_fetch, PyRRD_fetch__doc__),
+    meth("graph", PyRRD_graph, PyRRD_graph__doc__),
+    meth("tune", PyRRD_tune, PyRRD_tune__doc__),
+    meth("first", PyRRD_first, PyRRD_first__doc__),
+    meth("last", PyRRD_last, PyRRD_last__doc__),
+    meth("resize", PyRRD_resize, PyRRD_resize__doc__),
+    meth("info", PyRRD_info, PyRRD_info__doc__),
+    meth("graphv", PyRRD_graphv, PyRRD_graphv__doc__),
+    meth("updatev", PyRRD_updatev, PyRRD_updatev__doc__),
+    meth("flushcached", PyRRD_flushcached, PyRRD_flushcached__doc__),
+    {NULL, NULL, 0, NULL}
+};
+
+#define SET_INTCONSTANT(dict, value) \
+            t = PyInt_FromLong((long)value); \
+            PyDict_SetItemString(dict, #value, t); \
+            Py_DECREF(t);
+#define SET_STRCONSTANT(dict, value) \
+            t = PyString_FromString(value); \
+            PyDict_SetItemString(dict, #value, t); \
+            Py_DECREF(t);
+
+/* Initialization function for the module */
+void initrrdtool(
+    void)
+{
+    PyObject *m, *d, *t;
+
+    /* Create the module and add the functions */
+    m = Py_InitModule("rrdtool", _rrdtool_methods);
+
+    /* Add some symbolic constants to the module */
+    d = PyModule_GetDict(m);
+
+    SET_STRCONSTANT(d, __version__);
+    ErrorObject = PyErr_NewException("rrdtool.error", NULL, NULL);
+    PyDict_SetItemString(d, "error", ErrorObject);
+
+    /* Check for errors */
+    if (PyErr_Occurred())
+        Py_FatalError("can't initialize the rrdtool module");
+}
+
+/*
+ * $Id: _rrdtoolmodule.c,v 1.14 2003/02/22 07:41:19 perky Exp $
+ * ex: ts=8 sts=4 et
+ */
diff --git a/program/bindings/python/setup.py b/program/bindings/python/setup.py
new file mode 100644 (file)
index 0000000..83f35b6
--- /dev/null
@@ -0,0 +1,55 @@
+#! /usr/bin/env python
+#
+# setup.py
+#
+# py-rrdtool distutil setup
+#
+# Author  : Hye-Shik Chang <perky@fallin.lv>
+# Date    : $Date: 2003/02/14 02:38:16 $
+# Created : 24 May 2002
+#
+# $Revision: 1.7 $
+#
+#  ==========================================================================
+#  This file is part of py-rrdtool.
+#
+#  py-rrdtool is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU Lesser General Public License as published
+#  by the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  py-rrdtool is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with Foobar; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+
+from distutils.core import setup, Extension
+import sys, os
+
+RRDBASE = os.environ.get('LOCALBASE', '../../src')
+library_dir = os.environ.get('BUILDLIBDIR', os.path.join(RRDBASE, '.libs'))
+include_dir = os.environ.get('INCDIR', RRDBASE)
+
+setup(name = "py-rrdtool",
+      version = "0.2.1",
+      description = "Python Interface to RRDTool",
+      author = "Hye-Shik Chang",
+      author_email = "perky@fallin.lv",
+      license = "LGPL",
+      url = "http://oss.oetiker.ch/rrdtool",
+      #packages = ['rrdtool'],
+      ext_modules = [
+          Extension(
+            "rrdtoolmodule",
+            ["rrdtoolmodule.c"],
+            libraries=['rrd'],
+            library_dirs=[library_dir],
+            include_dirs=[include_dir],
+          )
+      ]
+)
diff --git a/program/bindings/ruby/CHANGES b/program/bindings/ruby/CHANGES
new file mode 100644 (file)
index 0000000..9e01ed5
--- /dev/null
@@ -0,0 +1,7 @@
+2010-02-05 Pavel Pachkovskij (Azati corp.)
+ add rrd_xport
+ update test.rb with rrd_xport example
+
+2006-07-25 Loïs Lherbier
+ add y_min and y_max parameters to rrd_graph
+ update test.rb to send only strings to RRD.fetch
diff --git a/program/bindings/ruby/README b/program/bindings/ruby/README
new file mode 100644 (file)
index 0000000..888a062
--- /dev/null
@@ -0,0 +1,22 @@
+#
+# ruby librrd bindings
+# author: Miles Egan <miles@caddr.com>
+#
+
+- Introduction 
+
+This module provides ruby bindings for librrd, with functionality
+comparable to the native perl bindings.  See test.rb for a script that
+exercises all ruby-librrd functionality.
+
+- Installation
+
+Installation is standard.  Simply run:
+
+ruby extconf.rb
+make
+make install
+
+I hope this works for you.  Please let me know if you have any
+problems or suggestions.  Someday when I'm feeling less lazy I'll
+actually document this thing.  Thanks to Tobi for rrdtool!
diff --git a/program/bindings/ruby/extconf.rb b/program/bindings/ruby/extconf.rb
new file mode 100644 (file)
index 0000000..2045e5a
--- /dev/null
@@ -0,0 +1,18 @@
+# $Id: extconf.rb,v 1.2 2001/11/28 18:30:16 miles Exp $
+# Lost ticket pays maximum rate.
+
+require 'mkmf'
+
+if /linux/ =~ RUBY_PLATFORM
+   $LDFLAGS += '-Wl,--rpath -Wl,$(EPREFIX)/lib'
+elsif /solaris/ =~ RUBY_PLATFORM
+   $LDFLAGS += '-R$(EPREFIX)/lib'
+elsif /hpux/ =~ RUBY_PLATFORM
+   $LDFLAGS += '+b$(EPREFIX)/lib'
+elsif /aix/ =~ RUBY_PLATFORM
+   $LDFLAGS += '-Wl,-blibpath:$(EPREFIX)/lib'
+end
+
+dir_config("rrd","../../src","../../src/.libs")
+have_library("rrd", "rrd_create")
+create_makefile("RRD")
diff --git a/program/bindings/ruby/main.c b/program/bindings/ruby/main.c
new file mode 100644 (file)
index 0000000..6468582
--- /dev/null
@@ -0,0 +1,390 @@
+/* $Id$
+ * Substantial penalty for early withdrawal.
+ */
+
+#include <unistd.h>
+#include <ruby.h>
+#include <math.h>
+#include "../../src/rrd_tool.h"
+
+typedef struct string_arr_t {
+    int       len;
+    char    **strings;
+} string_arr;
+
+VALUE     mRRD;
+VALUE     rb_eRRDError;
+
+typedef int (
+    *RRDFUNC) (
+    int argc,
+    char **argv);
+
+typedef rrd_info_t *(
+    *RRDINFOFUNC) (
+    int argc,
+    char **argv);
+
+#define RRD_CHECK_ERROR  \
+    if (rrd_test_error()) \
+      rb_raise(rb_eRRDError, rrd_get_error()); \
+    rrd_clear_error();
+
+string_arr string_arr_new(
+    VALUE rb_strings)
+{
+    string_arr a;
+    char      buf[64];
+    int       i;
+
+    Check_Type(rb_strings, T_ARRAY);
+    a.len = RARRAY_LEN(rb_strings) + 1;
+
+    a.strings = malloc(a.len * sizeof(char *));
+    a.strings[0] = "dummy"; /* first element is a dummy element */
+
+    for (i = 0; i < a.len - 1; i++) {
+        VALUE     v = rb_ary_entry(rb_strings, i);
+
+        switch (TYPE(v)) {
+        case T_STRING:
+            a.strings[i + 1] = strdup(STR2CSTR(v));
+            break;
+        case T_FIXNUM:
+            snprintf(buf, 63, "%d", FIX2INT(v));
+            a.strings[i + 1] = strdup(buf);
+            break;
+        default:
+            rb_raise(rb_eTypeError,
+                     "invalid argument - %s, expected T_STRING or T_FIXNUM on index %d",
+                     rb_class2name(CLASS_OF(v)), i);
+            break;
+        }
+    }
+
+    return a;
+}
+
+void string_arr_delete(
+    string_arr a)
+{
+    int       i;
+
+    /* skip dummy first entry */
+    for (i = 1; i < a.len; i++) {
+        free(a.strings[i]);
+    }
+
+    free(a.strings);
+}
+
+void reset_rrd_state(
+    )
+{
+    optind = 0;
+    opterr = 0;
+    rrd_clear_error();
+}
+
+/* Simple Calls */
+
+VALUE rrd_call(
+    RRDFUNC func,
+    VALUE args)
+{
+    string_arr a;
+
+    a = string_arr_new(args);
+    reset_rrd_state();
+    func(a.len, a.strings);
+    string_arr_delete(a);
+
+    RRD_CHECK_ERROR return Qnil;
+}
+
+VALUE rb_rrd_create(
+    VALUE self,
+    VALUE args)
+{
+    return rrd_call(rrd_create, args);
+}
+
+VALUE rb_rrd_dump(
+    VALUE self,
+    VALUE args)
+{
+    return rrd_call(rrd_dump, args);
+}
+
+VALUE rb_rrd_resize(
+    VALUE self,
+    VALUE args)
+{
+    return rrd_call(rrd_resize, args);
+}
+
+VALUE rb_rrd_restore(
+    VALUE self,
+    VALUE args)
+{
+    return rrd_call(rrd_restore, args);
+}
+
+VALUE rb_rrd_tune(
+    VALUE self,
+    VALUE args)
+{
+    return rrd_call(rrd_tune, args);
+}
+
+VALUE rb_rrd_update(
+    VALUE self,
+    VALUE args)
+{
+    return rrd_call(rrd_update, args);
+}
+
+VALUE rb_rrd_flushcached(
+    VALUE self,
+    VALUE args)
+{
+    return rrd_call(rrd_flushcached, args);
+}
+
+
+/* Calls Returning Data via the Info Interface */
+
+VALUE rb_rrd_infocall(
+    RRDINFOFUNC func,
+    VALUE args)
+{
+    string_arr a;
+    rrd_info_t *p, *data;
+    VALUE     result;
+
+    a = string_arr_new(args);
+    reset_rrd_state();
+    data = func(a.len, a.strings);
+    string_arr_delete(a);
+
+    RRD_CHECK_ERROR result = rb_hash_new();
+
+    p = data;
+    while (data) {
+        VALUE     key = rb_str_new2(data->key);
+
+        switch (data->type) {
+        case RD_I_VAL:
+            if (isnan(data->value.u_val)) {
+                rb_hash_aset(result, key, Qnil);
+            } else {
+                rb_hash_aset(result, key, rb_float_new(data->value.u_val));
+            }
+            break;
+        case RD_I_CNT:
+            rb_hash_aset(result, key, INT2FIX(data->value.u_cnt));
+            break;
+        case RD_I_STR:
+            rb_hash_aset(result, key, rb_str_new2(data->value.u_str));
+            break;
+        case RD_I_INT:
+            rb_hash_aset(result, key, INT2FIX(data->value.u_int));
+            break;
+        case RD_I_BLO:
+            rb_hash_aset(result, key,
+                         rb_str_new((char *)data->value.u_blo.ptr,
+                                    data->value.u_blo.size));
+            break;
+        }
+        data = data->next;
+    }
+    rrd_info_free(p);
+    return result;
+}
+
+VALUE rb_rrd_info(
+    VALUE self,
+    VALUE args)
+{
+    return rb_rrd_infocall(rrd_info, args);
+}
+
+VALUE rb_rrd_updatev(
+    VALUE self,
+    VALUE args)
+{
+    return rb_rrd_infocall(rrd_update_v, args);
+}
+
+VALUE rb_rrd_graphv(
+    VALUE self,
+    VALUE args)
+{
+    return rb_rrd_infocall(rrd_graph_v, args);
+}
+
+
+/* Other Calls */
+
+VALUE rb_rrd_fetch(
+    VALUE self,
+    VALUE args)
+{
+    string_arr a;
+    unsigned long i, j, k, step, ds_cnt;
+    rrd_value_t *raw_data;
+    char    **raw_names;
+    VALUE     data, names, result;
+    time_t    start, end;
+
+    a = string_arr_new(args);
+    reset_rrd_state();
+    rrd_fetch(a.len, a.strings, &start, &end, &step, &ds_cnt, &raw_names,
+              &raw_data);
+    string_arr_delete(a);
+
+    RRD_CHECK_ERROR names = rb_ary_new();
+
+    for (i = 0; i < ds_cnt; i++) {
+        rb_ary_push(names, rb_str_new2(raw_names[i]));
+        rrd_freemem(raw_names[i]);
+    }
+    rrd_freemem(raw_names);
+
+    k = 0;
+    data = rb_ary_new();
+    for (i = start; i <= end; i += step) {
+        VALUE     line = rb_ary_new2(ds_cnt);
+
+        for (j = 0; j < ds_cnt; j++) {
+            rb_ary_store(line, j, rb_float_new(raw_data[k]));
+            k++;
+        }
+        rb_ary_push(data, line);
+    }
+    rrd_freemem(raw_data);
+
+    result = rb_ary_new2(5);
+    rb_ary_store(result, 0, INT2NUM(start));
+    rb_ary_store(result, 1, INT2NUM(end));
+    rb_ary_store(result, 2, names);
+    rb_ary_store(result, 3, data);
+    rb_ary_store(result, 4, INT2FIX(step));
+    return result;
+}
+
+VALUE rb_rrd_graph(
+    VALUE self,
+    VALUE args)
+{
+    string_arr a;
+    char    **calcpr, **p;
+    VALUE     result, print_results;
+    int       xsize, ysize;
+    double    ymin, ymax;
+
+    a = string_arr_new(args);
+    reset_rrd_state();
+    rrd_graph(a.len, a.strings, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
+    string_arr_delete(a);
+
+    RRD_CHECK_ERROR result = rb_ary_new2(3);
+
+    print_results = rb_ary_new();
+    p = calcpr;
+    for (p = calcpr; p && *p; p++) {
+        rb_ary_push(print_results, rb_str_new2(*p));
+        rrd_freemem(*p);
+    }
+    rrd_freemem(calcpr);
+    rb_ary_store(result, 0, print_results);
+    rb_ary_store(result, 1, INT2FIX(xsize));
+    rb_ary_store(result, 2, INT2FIX(ysize));
+    return result;
+}
+
+
+VALUE rb_rrd_last(
+    VALUE self,
+    VALUE args)
+{
+    string_arr a;
+    time_t    last;
+
+    a = string_arr_new(args);
+    reset_rrd_state();
+    last = rrd_last(a.len, a.strings);
+    string_arr_delete(a);
+
+    RRD_CHECK_ERROR
+        return rb_funcall(rb_cTime, rb_intern("at"), 1, UINT2NUM(last));
+}
+
+VALUE rb_rrd_xport(
+    VALUE self,
+    VALUE args)
+{
+    string_arr a;
+    unsigned long i, j, k, step, col_cnt;
+    int xxsize;
+    rrd_value_t *data;
+    char **legend_v;
+    VALUE legend, result, rdata;
+    time_t start, end;
+
+    a = string_arr_new(args);
+    rrd_xport(a.len, a.strings, &xxsize, &start, &end, &step, &col_cnt, &legend_v, &data);
+    string_arr_delete(a);
+
+    RRD_CHECK_ERROR;
+            
+    legend = rb_ary_new();
+    for (i = 0; i < col_cnt; i++) {
+        rb_ary_push(legend, rb_str_new2(legend_v[i]));
+        free(legend_v[i]);
+    }
+    free(legend_v);
+
+    k = 0;
+    rdata = rb_ary_new();
+    for (i = start; i <= end; i += step) {
+        VALUE line = rb_ary_new2(col_cnt);
+        for (j = 0; j < col_cnt; j++) {
+            rb_ary_store(line, j, rb_float_new(data[k]));
+            k++;
+        }
+        rb_ary_push(rdata, line);
+    }
+    free(data);
+
+    result = rb_ary_new2(6);
+    rb_ary_store(result, 0, INT2FIX(start));
+    rb_ary_store(result, 1, INT2FIX(end));
+    rb_ary_store(result, 2, INT2FIX(step));
+    rb_ary_store(result, 3, INT2FIX(col_cnt));
+    rb_ary_store(result, 4, legend);
+    rb_ary_store(result, 5, rdata);
+    return result;
+}
+
+void Init_RRD(
+    )
+{
+    mRRD = rb_define_module("RRD");
+    rb_eRRDError = rb_define_class("RRDError", rb_eStandardError);
+
+    rb_define_module_function(mRRD, "create", rb_rrd_create, -2);
+    rb_define_module_function(mRRD, "dump", rb_rrd_dump, -2);
+    rb_define_module_function(mRRD, "fetch", rb_rrd_fetch, -2);
+    rb_define_module_function(mRRD, "graph", rb_rrd_graph, -2);
+    rb_define_module_function(mRRD, "last", rb_rrd_last, -2);
+    rb_define_module_function(mRRD, "resize", rb_rrd_resize, -2);
+    rb_define_module_function(mRRD, "restore", rb_rrd_restore, -2);
+    rb_define_module_function(mRRD, "tune", rb_rrd_tune, -2);
+    rb_define_module_function(mRRD, "update", rb_rrd_update, -2);
+    rb_define_module_function(mRRD, "flushcached", rb_rrd_flushcached, -2);
+    rb_define_module_function(mRRD, "info", rb_rrd_info, -2);
+    rb_define_module_function(mRRD, "updatev", rb_rrd_updatev, -2);
+    rb_define_module_function(mRRD, "graphv", rb_rrd_graphv, -2);
+    rb_define_module_function(mRRD, "xport", rb_rrd_xport, -2);
+}
diff --git a/program/bindings/ruby/test.rb b/program/bindings/ruby/test.rb
new file mode 100755 (executable)
index 0000000..2ca502d
--- /dev/null
@@ -0,0 +1,72 @@
+#!/usr/bin/env ruby
+# $Id: test.rb,v 1.2 2002/10/22 17:34:00 miles Exp $
+# Driver does not carry cash.
+
+$: << '/scratch/rrd12build/lib/ruby/1.8/i386-linux/'
+
+require "RRD"
+
+name = "test"
+rrd = "#{name}.rrd"
+start_time = Time.now.to_i
+end_time = start_time.to_i + 300 * 300
+
+puts "creating #{rrd}"
+RRD.create(
+    rrd,
+    "--start", "#{start_time - 1}",
+    "--step", "300",
+       "DS:a:GAUGE:600:U:U",
+    "DS:b:GAUGE:600:U:U",
+    "RRA:AVERAGE:0.5:1:300")
+puts
+
+puts "updating #{rrd}"
+start_time.step(end_time, 300) { |i|
+    RRD.update(rrd, "#{i}:#{rand(100)}:#{Math.sin(i / 800) * 50 + 50}")
+}
+puts
+
+puts "fetching data from #{rrd}"
+(fstart, fend, data) = RRD.fetch(rrd, "--start", start_time.to_s, "--end", end_time.to_s, "AVERAGE")
+puts "got #{data.length} data points from #{fstart} to #{fend}"
+puts
+
+puts "generating graph #{name}.png"
+RRD.graph(
+   "#{name}.png",
+    "--title", " RubyRRD Demo", 
+    "--start", "#{start_time+3600}",
+    "--end", "start + 1000 min",
+    "--interlace", 
+    "--imgformat", "PNG",
+    "--width=450",
+    "DEF:a=#{rrd}:a:AVERAGE",
+    "DEF:b=#{rrd}:b:AVERAGE",
+    "CDEF:line=TIME,2400,%,300,LT,a,UNKN,IF",
+    "AREA:b#00b6e4:beta",
+    "AREA:line#0022e9:alpha",
+    "LINE3:line#ff0000")
+puts
+
+# last method test
+if end_time != RRD.last("#{rrd}").to_i
+    puts "last method expects #{Time.at(end_time)}."
+    puts "                But #{RRD.last("#{rrd}")} returns."
+end
+puts
+
+# xport method test
+puts "xporting data from #{rrd}"
+(fstart,fend,step,col,legend,data)=RRD.xport(
+       "--start", start_time.to_s, 
+       "--end", (start_time + 300 * 300).to_s, 
+       "--step", 10.to_s, 
+       "DEF:A=#{rrd}:a:AVERAGE",
+       "DEF:B=#{rrd}:b:AVERAGE",
+       "XPORT:A:a",
+       "XPORT:B:b")
+puts "Xported #{col} columns(#{legend.join(", ")}) with #{data.length} rows from #{fstart} to #{fend} and step #{step}\n"
+
+print "This script has created #{name}.png in the current directory\n";
+print "This demonstrates the use of the TIME and % RPN operators\n";
diff --git a/program/bindings/tcl/Makefile.am b/program/bindings/tcl/Makefile.am
new file mode 100644 (file)
index 0000000..c0e8b0f
--- /dev/null
@@ -0,0 +1,58 @@
+
+EXTRA_DIST = README tclrrd.c
+
+VERSION = @VERSION@
+
+AM_CFLAGS = @CFLAGS@
+### no including this by default @WERROR@
+
+TCL_PREFIX = @TCL_PREFIX@
+TCL_SHLIB_LD = @TCL_SHLIB_LD@
+TCL_SHLIB_CFLAGS = @TCL_SHLIB_CFLAGS@
+TCL_SHLIB_SUFFIX = @TCL_SHLIB_SUFFIX@
+TCL_PACKAGE_PATH = @TCL_PACKAGE_PATH@
+TCL_LD_SEARCH_FLAGS = @TCL_LD_SEARCH_FLAGS@
+TCL_STUB_LIB_SPEC = @TCL_STUB_LIB_SPEC@
+TCL_INCLUDE_SPEC = @TCL_INCLUDE_SPEC@
+
+CLEANFILES = tclrrd.o tclrrd.so
+
+SRC_DIR            = $(top_srcdir)/src
+AM_CPPFLAGS        = $(TCL_INCLUDE_SPEC) -I$(SRC_DIR) -DUSE_TCL_STUBS
+LIBDIRS            = -L$(top_builddir)/src/.libs -L$(top_builddir)/src -L$(libdir)
+LIB_RUNTIME_DIR    = $(libdir)
+
+if BUILD_TCL_SITE
+tclpkgdir = @TCL_PACKAGE_DIR@
+tclpkg_DATA = pkgIndex.tcl
+tclpkg_SCRIPTS = ifOctets.tcl
+else
+pkglib_DATA = pkgIndex.tcl
+pkglib_SCRIPTS = ifOctets.tcl
+endif
+
+# Automake doen't like `tclrrd$(VERSION)$(TCL_SHLIB_SUFFIX)' as
+# library name. So we build and install this library `by hand'.
+#
+# We do, however, specify a lib_LIBRARIES target such that
+# automake creates the directory (if neecessary).
+#
+TCL_RRD_LIB = tclrrd$(VERSION)$(TCL_SHLIB_SUFFIX)
+
+lib_LIBRARIES =
+
+all-local: $(TCL_RRD_LIB)
+
+$(TCL_RRD_LIB): tclrrd.o
+       $(TCL_SHLIB_LD) $(TCL_LD_SEARCH_FLAGS) $(LIBDIRS) $< -o $@ -lrrd_th -lm $(TCL_STUB_LIB_SPEC) $(LDFLAGS) $(LIBS)
+
+tclrrd.o: tclrrd.c
+       $(CC) $(AM_CFLAGS) $(CFLAGS) $(TCL_SHLIB_CFLAGS) $(AM_CPPFLAGS) -c tclrrd.c -DVERSION=\"$(VERSION)\"
+
+pkgIndex.tcl:
+       echo "package ifneeded Rrd $(VERSION) \"load $(libdir)/tclrrd$(VERSION)[info sharedlibextension]\"" > $@
+
+install-exec-local: $(TCL_RRD_LIB)
+       @$(NORMAL_INSTALL)
+       $(INSTALL_PROGRAM) $(TCL_RRD_LIB) $(DESTDIR)$(libdir)/$(TCL_RRD_LIB)
+
diff --git a/program/bindings/tcl/README b/program/bindings/tcl/README
new file mode 100644 (file)
index 0000000..065a03a
--- /dev/null
@@ -0,0 +1,31 @@
+TCLRRD -- A TCL interpreter extension to access the RRD library,
+         contributed to Tobias Oetiker's RRD tools.
+
+Copyright (c) 1999,2000 Frank Strauss, Technical University of Braunschweig.
+
+See the file "COPYING" for information on usage and redistribution
+of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+
+TCLRRD adds a dynamically loadable package to the Tcl 8.x interpreter
+to access all RRD functions as of RRDtool 1.0.13. All command names
+and arguments are equal to those of RRDtool. They are assigned to the
+namespace `Rrd', e.g.  `Rrd::create'. Return values are a bit
+different from plain RRDtool behavior to enable more native Tcl
+usage. Errors are mapped to the TCL_ERROR return code together with
+the RRD error strings.
+
+TCLRRD makes it easy to combine RRD use with advanced SNMP functionality
+of scotty (http://wwwsnmp.cs.utwente.nl/~schoenw/scotty/). E.g., it's easy
+to use some scotty code to get the counters of some interfaces by their
+interface name and then use Rrd::update to store the values. Furthermore,
+data source types (see RRD::create documentation) and integer value ranges
+could be easily retrieved from MIB information.
+
+TCLRRD has been written on a Linux system for use with Tcl 8.x. It should
+work on many other platforms, although it has not been tested. There are
+no fool proof installation procedures. Take a look at Makefile.am and
+adapt it, if required.
+
+TCLRRD has been written for RRD 1.0.13.
+
+       Frank Strauss <strauss@ibr.cs.tu-bs.de>, 09-Mar-2000
diff --git a/program/bindings/tcl/ifOctets.tcl.in b/program/bindings/tcl/ifOctets.tcl.in
new file mode 100644 (file)
index 0000000..7a36397
--- /dev/null
@@ -0,0 +1,45 @@
+#!/bin/sh
+# the next line restarts using tclsh -*- tcl -*- \
+exec tclsh@TCL_VERSION@ "$0" "$@"
+
+#package require Tnm 3.0
+package require Rrd @VERSION@
+
+set rrdfile "[lindex $argv 0]-[lindex $argv 1].rrd"
+
+# create rrdfile if not yet existent
+if {[file exists $rrdfile] == 0} {
+    Rrd::create $rrdfile --step 5 \
+           DS:inOctets:COUNTER:10:U:U DS:outOctets:COUNTER:10:U:U \
+           RRA:AVERAGE:0.5:1:12
+}
+
+# get an snmp session context
+set session [Tnm::snmp generator -address [lindex $argv 0]]
+
+# walk through the ifDescr column to find the right interface
+$session walk descr IF-MIB!ifDescr {
+
+    # is this the right interface?
+    if {"[Tnm::snmp value $descr 0]" == "[lindex $argv 1]"} {
+
+       # get the instance part of this table row
+       set inst [lindex [Tnm::mib split [Tnm::snmp oid $descr 0]] 1]
+
+       # get the two interface's octet counter values
+       set in [lindex [lindex [$session get IF-MIB!ifInOctets.$inst] 0] 2]
+       set out [lindex [lindex [$session get IF-MIB!ifOutOctets.$inst] 0] 2]
+
+       # write the values to the rrd
+       puts "$in $out"
+       Rrd::update $rrdfile --template inOctets:outOctets N:$in:$out
+
+       Rrd::graph gaga.png --title "gaga" \
+               DEF:in=$rrdfile:inOctets:AVERAGE \
+               DEF:out=$rrdfile:outOctets:AVERAGE \
+               AREA:in#0000FF:inOctets \
+               LINE2:out#00C000:outOctets
+
+       #puts [Rrd::fetch $rrdfile AVERAGE]
+    }
+}
diff --git a/program/bindings/tcl/tclrrd.c b/program/bindings/tcl/tclrrd.c
new file mode 100644 (file)
index 0000000..a98a2d7
--- /dev/null
@@ -0,0 +1,762 @@
+/*
+ * tclrrd.c -- A TCL interpreter extension to access the RRD library.
+ *
+ * Copyright (c) 1999,2000 Frank Strauss, Technical University of Braunschweig.
+ *
+ * Thread-safe code copyright (c) 2005 Oleg Derevenetz, CenterTelecom Voronezh ISP.
+ *
+ * See the file "COPYING" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * $Id$
+ */
+
+
+
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <tcl.h>
+#include <stdlib.h>
+#include "../../src/rrd_tool.h"
+#include "../../src/rrd_format.h"
+
+/* support pre-8.4 tcl */
+
+#ifndef CONST84
+#   define CONST84
+#endif
+
+extern int Tclrrd_Init(
+    Tcl_Interp *interp);
+extern int Tclrrd_SafeInit(
+    Tcl_Interp *interp);
+
+
+/*
+ * some rrd_XXX() and new thread-safe versions of Rrd_XXX()
+ * functions might modify the argv strings passed to it.
+ * Hence, we need to do some preparation before
+ * calling the rrd library functions.
+ */
+static char **getopt_init(
+    int argc,
+    CONST84 char *argv[])
+{
+    char    **argv2;
+    int       i;
+
+    argv2 = calloc(argc, sizeof(char *));
+    for (i = 0; i < argc; i++) {
+        argv2[i] = strdup(argv[i]);
+    }
+    return argv2;
+}
+
+static void getopt_cleanup(
+    int argc,
+    char **argv2)
+{
+    int       i;
+
+    for (i = 0; i < argc; i++) {
+        if (argv2[i] != NULL) {
+            free(argv2[i]);
+        }
+    }
+    free(argv2);
+}
+
+static void getopt_free_element(
+    char *argv2[],
+    int argn)
+{
+    if (argv2[argn] != NULL) {
+        free(argv2[argn]);
+        argv2[argn] = NULL;
+    }
+}
+
+static void getopt_squieeze(
+    int *argc,
+    char *argv2[])
+{
+    int       i, null_i = 0, argc_tmp = *argc;
+
+    for (i = 0; i < argc_tmp; i++) {
+        if (argv2[i] == NULL) {
+            (*argc)--;
+        } else {
+            argv2[null_i++] = argv2[i];
+        }
+    }
+}
+
+
+
+/* Thread-safe version */
+static int Rrd_Create(
+    ClientData __attribute__((unused)) clientData,
+    Tcl_Interp *interp,
+    int argc,
+    CONST84 char *argv[])
+{
+    int       argv_i;
+    char    **argv2;
+    char     *parsetime_error = NULL;
+    time_t    last_up = time(NULL) - 10;
+    long int  long_tmp;
+    unsigned long int pdp_step = 300;
+    rrd_time_value_t last_up_tv;
+
+    argv2 = getopt_init(argc, argv);
+
+    for (argv_i = 1; argv_i < argc; argv_i++) {
+        if (!strcmp(argv2[argv_i], "--start") || !strcmp(argv2[argv_i], "-b")) {
+            if (argv_i++ >= argc) {
+                Tcl_AppendResult(interp, "RRD Error: option '",
+                                 argv2[argv_i - 1], "' needs an argument",
+                                 (char *) NULL);
+                getopt_cleanup(argc, argv2);
+                return TCL_ERROR;
+            }
+            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);
+                return TCL_ERROR;
+            }
+            if (last_up_tv.type == RELATIVE_TO_END_TIME ||
+                last_up_tv.type == RELATIVE_TO_START_TIME) {
+                Tcl_AppendResult(interp,
+                                 "RRD Error: specifying time relative to the 'start' ",
+                                 "or 'end' makes no sense here",
+                                 (char *) NULL);
+                getopt_cleanup(argc, argv2);
+                return TCL_ERROR;
+            }
+            last_up = mktime(&last_up_tv.tm) +last_up_tv.offset;
+            if (last_up < 3600 * 24 * 365 * 10) {
+                Tcl_AppendResult(interp,
+                                 "RRD Error: the first entry to the RRD should be after 1980",
+                                 (char *) NULL);
+                getopt_cleanup(argc, argv2);
+                return TCL_ERROR;
+            }
+            getopt_free_element(argv2, argv_i - 1);
+            getopt_free_element(argv2, argv_i);
+        } else if (!strcmp(argv2[argv_i], "--step")
+                   || !strcmp(argv2[argv_i], "-s")) {
+            if (argv_i++ >= argc) {
+                Tcl_AppendResult(interp, "RRD Error: option '",
+                                 argv2[argv_i - 1], "' needs an argument",
+                                 (char *) NULL);
+                getopt_cleanup(argc, argv2);
+                return TCL_ERROR;
+            }
+            long_tmp = atol(argv2[argv_i]);
+            if (long_tmp < 1) {
+                Tcl_AppendResult(interp,
+                                 "RRD Error: step size should be no less than one second",
+                                 (char *) NULL);
+                getopt_cleanup(argc, argv2);
+                return TCL_ERROR;
+            }
+            pdp_step = long_tmp;
+            getopt_free_element(argv2, argv_i - 1);
+            getopt_free_element(argv2, argv_i);
+        } else if (!strcmp(argv2[argv_i], "--")) {
+            getopt_free_element(argv2, argv_i);
+            break;
+        } else if (argv2[argv_i][0] == '-') {
+            Tcl_AppendResult(interp, "RRD Error: unknown option '",
+                             argv2[argv_i], "'", (char *) NULL);
+            getopt_cleanup(argc, argv2);
+            return TCL_ERROR;
+        }
+    }
+
+    getopt_squieeze(&argc, argv2);
+
+    if (argc < 2) {
+        Tcl_AppendResult(interp, "RRD Error: needs rrd filename",
+                         (char *) NULL);
+        getopt_cleanup(argc, argv2);
+        return TCL_ERROR;
+    }
+
+    rrd_create_r(argv2[1], pdp_step, last_up, argc - 2,
+                 (const char **)argv2 + 2);
+
+    getopt_cleanup(argc, argv2);
+
+    if (rrd_test_error()) {
+        Tcl_AppendResult(interp, "RRD Error: ",
+                         rrd_get_error(), (char *) NULL);
+        rrd_clear_error();
+        return TCL_ERROR;
+    }
+
+    return TCL_OK;
+}
+
+
+
+/* Thread-safe version */
+static int Rrd_Dump(
+    ClientData __attribute__((unused)) clientData,
+    Tcl_Interp *interp,
+    int argc,
+    CONST84 char *argv[])
+{
+    if (argc < 2) {
+        Tcl_AppendResult(interp, "RRD Error: needs rrd filename",
+                         (char *) NULL);
+        return TCL_ERROR;
+    }
+
+    rrd_dump_r(argv[1], NULL);
+
+    /* NOTE: rrd_dump() writes to stdout. No interaction with TCL. */
+
+    if (rrd_test_error()) {
+        Tcl_AppendResult(interp, "RRD Error: ",
+                         rrd_get_error(), (char *) NULL);
+        rrd_clear_error();
+        return TCL_ERROR;
+    }
+
+    return TCL_OK;
+}
+
+/* Thread-safe version */
+static int Rrd_Flushcached(
+    ClientData __attribute__((unused)) clientData,
+    Tcl_Interp *interp,
+    int argc,
+    CONST84 char *argv[])
+{
+    if (argc < 2) {
+        Tcl_AppendResult(interp, "RRD Error: needs rrd filename",
+                         (char *) NULL);
+        return TCL_ERROR;
+    }
+
+    rrd_flushcached(argc, (char**)argv);
+
+    if (rrd_test_error()) {
+        Tcl_AppendResult(interp, "RRD Error: ",
+                         rrd_get_error(), (char *) NULL);
+        rrd_clear_error();
+        return TCL_ERROR;
+    }
+
+    return TCL_OK;
+}
+
+
+/* Thread-safe version */
+static int Rrd_Last(
+    ClientData __attribute__((unused)) clientData,
+    Tcl_Interp *interp,
+    int argc,
+    CONST84 char *argv[])
+{
+    time_t    t;
+
+    if (argc < 2) {
+        Tcl_AppendResult(interp, "RRD Error: needs rrd filename",
+                         (char *) NULL);
+        return TCL_ERROR;
+    }
+
+    t = rrd_last_r(argv[1]);
+
+    if (rrd_test_error()) {
+        Tcl_AppendResult(interp, "RRD Error: ",
+                         rrd_get_error(), (char *) NULL);
+        rrd_clear_error();
+        return TCL_ERROR;
+    }
+
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), t);
+
+    return TCL_OK;
+}
+
+
+
+/* Thread-safe version */
+static int Rrd_Update(
+    ClientData __attribute__((unused)) clientData,
+    Tcl_Interp *interp,
+    int argc,
+    CONST84 char *argv[])
+{
+    int       argv_i;
+    char    **argv2, *template = NULL;
+
+    argv2 = getopt_init(argc, argv);
+
+    for (argv_i = 1; argv_i < argc; argv_i++) {
+        if (!strcmp(argv2[argv_i], "--template")
+            || !strcmp(argv2[argv_i], "-t")) {
+            if (argv_i++ >= argc) {
+                Tcl_AppendResult(interp, "RRD Error: option '",
+                                 argv2[argv_i - 1], "' needs an argument",
+                                 (char *) NULL);
+                if (template != NULL) {
+                    free(template);
+                }
+                getopt_cleanup(argc, argv2);
+                return TCL_ERROR;
+            }
+            if (template != NULL) {
+                free(template);
+            }
+            template = strdup(argv2[argv_i]);
+            getopt_free_element(argv2, argv_i - 1);
+            getopt_free_element(argv2, argv_i);
+        } else if (!strcmp(argv2[argv_i], "--")) {
+            getopt_free_element(argv2, argv_i);
+            break;
+        } else if (argv2[argv_i][0] == '-') {
+            Tcl_AppendResult(interp, "RRD Error: unknown option '",
+                             argv2[argv_i], "'", (char *) NULL);
+            if (template != NULL) {
+                free(template);
+            }
+            getopt_cleanup(argc, argv2);
+            return TCL_ERROR;
+        }
+    }
+
+    getopt_squieeze(&argc, argv2);
+
+    if (argc < 2) {
+        Tcl_AppendResult(interp, "RRD Error: needs rrd filename",
+                         (char *) NULL);
+        if (template != NULL) {
+            free(template);
+        }
+        getopt_cleanup(argc, argv2);
+        return TCL_ERROR;
+    }
+
+    rrd_update_r(argv2[1], template, argc - 2, (const char **)argv2 + 2);
+
+    if (template != NULL) {
+        free(template);
+    }
+    getopt_cleanup(argc, argv2);
+
+    if (rrd_test_error()) {
+        Tcl_AppendResult(interp, "RRD Error: ",
+                         rrd_get_error(), (char *) NULL);
+        rrd_clear_error();
+        return TCL_ERROR;
+    }
+
+    return TCL_OK;
+}
+
+static int Rrd_Lastupdate(
+    ClientData __attribute__((unused)) clientData,
+    Tcl_Interp *interp,
+    int argc,
+    CONST84 char *argv[])
+{
+    time_t    last_update;
+    char    **argv2;
+    char    **ds_namv;
+    char    **last_ds;
+    char      s[30];
+    Tcl_Obj  *listPtr;
+    unsigned long ds_cnt, i;
+
+    /* TODO: support for rrdcached */
+    if (argc != 2) {
+        Tcl_AppendResult(interp, "RRD Error: needs a single rrd filename",
+                         (char *) NULL);
+        return TCL_ERROR;
+    }
+
+    argv2 = getopt_init(argc, argv);
+    if (rrd_lastupdate_r(argv2[1], &last_update,
+                       &ds_cnt, &ds_namv, &last_ds) == 0) {
+        listPtr = Tcl_GetObjResult(interp);
+        for (i = 0; i < ds_cnt; i++) {
+            sprintf(s, " %28s", ds_namv[i]);
+            Tcl_ListObjAppendElement(interp, listPtr,
+                                     Tcl_NewStringObj(s, -1));
+            sprintf(s, "\n\n%10lu:", last_update);
+            Tcl_ListObjAppendElement(interp, listPtr,
+                                     Tcl_NewStringObj(s, -1));
+            for (i = 0; i < ds_cnt; i++) {
+                sprintf(s, " %s", last_ds[i]);
+                Tcl_ListObjAppendElement(interp, listPtr,
+                                         Tcl_NewStringObj(s, -1));
+                free(last_ds[i]);
+                free(ds_namv[i]);
+            }
+            sprintf(s, "\n");
+            Tcl_ListObjAppendElement(interp, listPtr,
+                                     Tcl_NewStringObj(s, -1));
+            free(last_ds);
+            free(ds_namv);
+        }
+    }
+    return TCL_OK;
+}
+
+static int Rrd_Fetch(
+    ClientData __attribute__((unused)) clientData,
+    Tcl_Interp *interp,
+    int argc,
+    CONST84 char *argv[])
+{
+    time_t    start, end, j;
+    unsigned long step, ds_cnt, i, ii;
+    rrd_value_t *data, *datai;
+    char    **ds_namv;
+    Tcl_Obj  *listPtr;
+    char      s[30];
+    char    **argv2;
+
+    argv2 = getopt_init(argc, argv);
+    if (rrd_fetch(argc, argv2, &start, &end, &step,
+                  &ds_cnt, &ds_namv, &data) != -1) {
+        datai = data;
+        listPtr = Tcl_GetObjResult(interp);
+        for (j = start; j <= end; j += step) {
+            for (ii = 0; ii < ds_cnt; ii++) {
+                sprintf(s, "%.2f", *(datai++));
+                Tcl_ListObjAppendElement(interp, listPtr,
+                                         Tcl_NewStringObj(s, -1));
+            }
+        }
+        for (i = 0; i < ds_cnt; i++)
+            free(ds_namv[i]);
+        free(ds_namv);
+        free(data);
+    }
+    getopt_cleanup(argc, argv2);
+
+    if (rrd_test_error()) {
+        Tcl_AppendResult(interp, "RRD Error: ",
+                         rrd_get_error(), (char *) NULL);
+        rrd_clear_error();
+        return TCL_ERROR;
+    }
+
+    return TCL_OK;
+}
+
+
+
+static int Rrd_Graph(
+    ClientData __attribute__((unused)) clientData,
+    Tcl_Interp *interp,
+    int argc,
+    CONST84 char *argv[])
+{
+    Tcl_Channel channel;
+    int       mode, fd2;
+    ClientData fd1;
+    FILE     *stream = NULL;
+    char    **calcpr = NULL;
+    int       rc, xsize, ysize;
+    double    ymin, ymax;
+    char      dimensions[50];
+    char    **argv2;
+    CONST84 char *save;
+
+    /*
+     * If the "filename" is a Tcl fileID, then arrange for rrd_graph() to write to
+     * that file descriptor.  Will this work with windoze?  I have no idea.
+     */
+    if ((channel = Tcl_GetChannel(interp, argv[1], &mode)) != NULL) {
+        /*
+         * It >is< a Tcl fileID
+         */
+        if (!(mode & TCL_WRITABLE)) {
+            Tcl_AppendResult(interp, "channel \"", argv[1],
+                             "\" wasn't opened for writing", (char *) NULL);
+            return TCL_ERROR;
+        }
+        /*
+         * Must flush channel to make sure any buffered data is written before
+         * rrd_graph() writes to the stream
+         */
+        if (Tcl_Flush(channel) != TCL_OK) {
+            Tcl_AppendResult(interp, "flush failed for \"", argv[1], "\": ",
+                             strerror(Tcl_GetErrno()), (char *) NULL);
+            return TCL_ERROR;
+        }
+        if (Tcl_GetChannelHandle(channel, TCL_WRITABLE, &fd1) != TCL_OK) {
+            Tcl_AppendResult(interp,
+                             "cannot get file descriptor associated with \"",
+                             argv[1], "\"", (char *) NULL);
+            return TCL_ERROR;
+        }
+        /*
+         * Must dup() file descriptor so we can fclose(stream), otherwise the fclose()
+         * would close Tcl's file descriptor
+         */
+        if ((fd2 = dup((int)fd1)) == -1) {
+            Tcl_AppendResult(interp,
+                             "dup() failed for file descriptor associated with \"",
+                             argv[1], "\": ", strerror(errno), (char *) NULL);
+            return TCL_ERROR;
+        }
+        /*
+         * rrd_graph() wants a FILE*
+         */
+        if ((stream = fdopen(fd2, "wb")) == NULL) {
+            Tcl_AppendResult(interp,
+                             "fdopen() failed for file descriptor associated with \"",
+                             argv[1], "\": ", strerror(errno), (char *) NULL);
+            close(fd2); /* plug potential file descriptor leak */
+            return TCL_ERROR;
+        }
+
+        save = argv[1];
+        argv[1] = "-";
+        argv2 = getopt_init(argc, argv);
+        argv[1] = save;
+    } else {
+        Tcl_ResetResult(interp);    /* clear error from Tcl_GetChannel() */
+        argv2 = getopt_init(argc, argv);
+    }
+
+    rc = rrd_graph(argc, argv2, &calcpr, &xsize, &ysize, stream, &ymin,
+                   &ymax);
+    getopt_cleanup(argc, argv2);
+
+    if (stream != NULL)
+        fclose(stream); /* plug potential malloc & file descriptor leak */
+
+    if (rc != -1) {
+        sprintf(dimensions, "%d %d", xsize, ysize);
+        Tcl_AppendResult(interp, dimensions, (char *) NULL);
+        if (calcpr) {
+#if 0
+            int       i;
+
+            for (i = 0; calcpr[i]; i++) {
+                printf("%s\n", calcpr[i]);
+                free(calcpr[i]);
+            }
+#endif
+            free(calcpr);
+        }
+    }
+
+    if (rrd_test_error()) {
+        Tcl_AppendResult(interp, "RRD Error: ",
+                         rrd_get_error(), (char *) NULL);
+        rrd_clear_error();
+        return TCL_ERROR;
+    }
+
+    return TCL_OK;
+}
+
+
+
+static int Rrd_Tune(
+    ClientData __attribute__((unused)) clientData,
+    Tcl_Interp *interp,
+    int argc,
+    CONST84 char *argv[])
+{
+    char    **argv2;
+
+    argv2 = getopt_init(argc, argv);
+    rrd_tune(argc, argv2);
+    getopt_cleanup(argc, argv2);
+
+    if (rrd_test_error()) {
+        Tcl_AppendResult(interp, "RRD Error: ",
+                         rrd_get_error(), (char *) NULL);
+        rrd_clear_error();
+        return TCL_ERROR;
+    }
+
+    return TCL_OK;
+}
+
+
+
+static int Rrd_Resize(
+    ClientData __attribute__((unused)) clientData,
+    Tcl_Interp *interp,
+    int argc,
+    CONST84 char *argv[])
+{
+    char    **argv2;
+
+    argv2 = getopt_init(argc, argv);
+    rrd_resize(argc, argv2);
+    getopt_cleanup(argc, argv2);
+
+    if (rrd_test_error()) {
+        Tcl_AppendResult(interp, "RRD Error: ",
+                         rrd_get_error(), (char *) NULL);
+        rrd_clear_error();
+        return TCL_ERROR;
+    }
+
+    return TCL_OK;
+}
+
+
+
+static int Rrd_Restore(
+    ClientData __attribute__((unused)) clientData,
+    Tcl_Interp *interp,
+    int argc,
+    CONST84 char *argv[])
+{
+    char    **argv2;
+
+    argv2 = getopt_init(argc, argv);
+    rrd_restore(argc, argv2);
+    getopt_cleanup(argc, argv2);
+
+    if (rrd_test_error()) {
+        Tcl_AppendResult(interp, "RRD Error: ",
+                         rrd_get_error(), (char *) NULL);
+        rrd_clear_error();
+        return TCL_ERROR;
+    }
+
+    return TCL_OK;
+}
+
+
+
+/*
+ * The following structure defines the commands in the Rrd extension.
+ */
+
+typedef struct {
+    char     *name;     /* Name of the command. */
+    Tcl_CmdProc *proc;  /* Procedure for command. */
+    int       hide;     /* Hide if safe interpreter */
+} CmdInfo;
+
+static CmdInfo rrdCmds[] = {
+    {"Rrd::create", Rrd_Create, 1}, /* Thread-safe version */
+    {"Rrd::dump", Rrd_Dump, 0}, /* Thread-safe version */
+    {"Rrd::flushcached", Rrd_Flushcached, 0},
+    {"Rrd::last", Rrd_Last, 0}, /* Thread-safe version */
+    {"Rrd::lastupdate", Rrd_Lastupdate, 0}, /* Thread-safe version */
+    {"Rrd::update", Rrd_Update, 1}, /* Thread-safe version */
+    {"Rrd::fetch", Rrd_Fetch, 0},
+    {"Rrd::graph", Rrd_Graph, 1},   /* Due to RRD's API, a safe
+                                       interpreter cannot create
+                                       a graph since it writes to
+                                       a filename supplied by the
+                                       caller */
+    {"Rrd::tune", Rrd_Tune, 1},
+    {"Rrd::resize", Rrd_Resize, 1},
+    {"Rrd::restore", Rrd_Restore, 1},
+    {(char *) NULL, (Tcl_CmdProc *) NULL, 0}
+};
+
+
+
+static int init(
+    Tcl_Interp *interp,
+    int safe)
+{
+    CmdInfo  *cmdInfoPtr;
+    Tcl_CmdInfo info;
+
+    if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL)
+        return TCL_ERROR;
+
+    if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 1) == NULL) {
+        return TCL_ERROR;
+    }
+
+    /*
+     * Why a global array?  In keeping with the Rrd:: namespace, why
+     * not simply create a normal variable Rrd::version and set it?
+     */
+    Tcl_SetVar2(interp, "rrd", "version", VERSION, TCL_GLOBAL_ONLY);
+
+    for (cmdInfoPtr = rrdCmds; cmdInfoPtr->name != NULL; cmdInfoPtr++) {
+        /*
+         * Check if the command already exists and return an error
+         * to ensure we detect name clashes while loading the Rrd
+         * extension.
+         */
+        if (Tcl_GetCommandInfo(interp, cmdInfoPtr->name, &info)) {
+            Tcl_AppendResult(interp, "command \"", cmdInfoPtr->name,
+                             "\" already exists", (char *) NULL);
+            return TCL_ERROR;
+        }
+        if (safe && cmdInfoPtr->hide) {
+#if 0
+            /*
+             * Turns out the one cannot hide a command in a namespace
+             * due to a limitation of Tcl, one can only hide global
+             * commands.  Thus, if we created the commands without
+             * the Rrd:: namespace in a safe interpreter, then the
+             * "unsafe" commands could be hidden -- which would allow
+             * an owning interpreter either un-hiding them or doing
+             * an "interp invokehidden".  If the Rrd:: namespace is
+             * used, then it's still possible for the owning interpreter
+             * to fake out the missing commands:
+             *
+             *   # Make all Rrd::* commands available in master interperter
+             *   package require Rrd
+             *   set safe [interp create -safe]
+             *   # Make safe Rrd::* commands available in safe interperter
+             *   interp invokehidden $safe -global load ./tclrrd1.2.11.so
+             *   # Provide the safe interpreter with the missing commands
+             *   $safe alias Rrd::update do_update $safe
+             *   proc do_update {which_interp $args} {
+             *     # Do some checking maybe...
+             *       :
+             *     return [eval Rrd::update $args]
+             *   }
+             *
+             * Our solution for now is to just not create the "unsafe"
+             * commands in a safe interpreter.
+             */
+            if (Tcl_HideCommand(interp, cmdInfoPtr->name, cmdInfoPtr->name) !=
+                TCL_OK)
+                return TCL_ERROR;
+#endif
+        } else
+            Tcl_CreateCommand(interp, cmdInfoPtr->name, cmdInfoPtr->proc,
+                              (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+    }
+
+    if (Tcl_PkgProvide(interp, "Rrd", VERSION) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    return TCL_OK;
+}
+
+int Tclrrd_Init(
+    Tcl_Interp *interp)
+{
+    return init(interp, 0);
+}
+
+/*
+ * See the comments above and note how few commands are considered "safe"...
+ * Using rrdtool in a safe interpreter has very limited functionality.  It's
+ * tempting to just return TCL_ERROR and forget about it.
+ */
+int Tclrrd_SafeInit(
+    Tcl_Interp *interp)
+{
+    return init(interp, 1);
+}
diff --git a/program/configure.ac b/program/configure.ac
new file mode 100644 (file)
index 0000000..eda5dca
--- /dev/null
@@ -0,0 +1,975 @@
+dnl RRDtool AutoConf script ... 
+dnl ---------------------------
+dnl $Id$
+dnl
+dnl Created by Jeff Allen, Tobi Oetiker, Blair Zajac
+dnl
+dnl Inspiration from http://autoconf-archive.cryp.to
+dnl tell automake the this script is for rrdtool
+
+dnl Minimum Autoconf version required.
+AC_PREREQ(2.59)
+
+dnl the official version number is
+dnl a.b.c
+AC_INIT([rrdtool],[1.4.3])
+
+dnl for testing a numberical version number comes handy
+dnl the released version are
+dnl a.bccc
+dnl the devel versions will be something like
+dnl a.b999yymmddhh 
+NUMVERS=1.4003
+AC_SUBST(NUMVERS)
+
+dnl for the linker to understand which versions the library are compatible with
+dnl each other we must keep a separate library version cout of the format c:r:a.
+dnl - if only implementation changed but all interfaces are kept, do r++
+dnl - if only functionality was added do c++,r=0,a++
+dnl - if any functionality was removed do c++,r=0,a=0.
+dnl
+dnl see http://sourceware.org/autobook/autobook/autobook_91.html
+dnl 
+LIBVERS=5:3:1
+AC_SUBST(LIBVERS)
+
+AC_CANONICAL_TARGET
+m4_version_prereq(2.60, [AC_USE_SYSTEM_EXTENSIONS], [#])
+AM_INIT_AUTOMAKE
+AM_MAINTAINER_MODE
+# Enable silent build rules by default, requires at least
+# Automake-1.11. Disable by either passing --disable-silent-rules to
+# configure or passing V=1 to make
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+AC_CONFIG_HEADERS([rrd_config.h])
+AC_CONFIG_MACRO_DIR([m4])
+
+dnl all our local stuff like install scripts and include files
+dnl is in there
+
+
+dnl determine the type of system we are running on
+
+AC_SUBST(VERSION)
+
+AC_PREFIX_DEFAULT( /opt/rrdtool-$PACKAGE_VERSION )
+
+dnl At the TOP of the HEADER
+
+AH_TOP([
+#ifndef RRD_CONFIG_H
+#define RRD_CONFIG_H
+
+/* IEEE can be prevented from raising signals with fpsetmask(0) */
+#undef MUST_DISABLE_FPMASK
+
+/* IEEE math only works if SIGFPE gets actively set to IGNORE */
+#undef MUST_DISABLE_SIGFPE
+
+/* realloc does not support NULL as argument */
+#undef NO_NULL_REALLOC
+
+/* lets enable madvise defines in NetBSD */ 
+#if defined(__NetBSD__)
+# if !defined(_NETBSD_SOURCE)
+#  define _NETBSD_SOURCE
+# endif
+#endif
+
+])
+
+AH_BOTTOM([
+
+#ifdef MUST_HAVE_MALLOC_MALLOC_H
+#  include <malloc/malloc.h>
+#endif
+
+#include "src/rrd_config_bottom.h"
+
+#endif
+])
+
+dnl Process Special Options
+dnl -----------------------------------
+
+dnl How the vertical axis label is printed
+AC_ARG_VAR(RRDGRAPH_YLEGEND_ANGLE, 
+ [Vertical label angle: -90.0 (default) or 90.0])
+AC_DEFINE_UNQUOTED(RRDGRAPH_YLEGEND_ANGLE,${RRDGRAPH_YLEGEND_ANGLE:-90.0},
+ [Vertical label angle: -90.0 (default) or 90.0])
+
+AC_ARG_ENABLE(rrdcgi,AS_HELP_STRING([--disable-rrdcgi],[disable building of rrdcgi]),
+[],[enable_rrdcgi=yes])
+
+dnl Check if we run on a system that has fonts
+AC_ARG_WITH(rrd-default-font,
+[  --with-rrd-default-font=[OPTIONS]  set the full path to your default font.],
+[RRD_DEFAULT_FONT=$withval],[
+  if test -d ${WINDIR:-nodir}/cour.ttf ; then
+       RRD_DEFAULT_FONT=`cd $WINDIR;pwd`/cour.ttf
+  else
+       RRD_DEFAULT_FONT='"DejaVu Sans Mono,Bitstream Vera Sans Mono,monospace,Courier"'
+  fi
+])
+
+dnl Use mmap in rrd_update instead of seek+write
+AC_ARG_ENABLE([mmap],
+AS_HELP_STRING([--disable-mmap],[disable mmap in rrd_update, use seek+write instead]),
+[],
+[enable_mmap=yes])
+
+AC_ARG_ENABLE(pthread,AS_HELP_STRING([--disable-pthread],[disable multithread support]),
+[],[enable_pthread=yes])
+
+AC_ARG_ENABLE(static-programs,
+     AS_HELP_STRING([--enable-static-programs],[Build static programs]),
+     [case "${enableval}" in
+       yes) staticprogs=yes ;;
+       no)  staticprogs=no ;;
+       *) AC_MSG_ERROR(bad value ${enableval} for --enable-static-programs) ;;
+     esac],[staticprogs=no])
+AM_CONDITIONAL(STATIC_PROGRAMS,[test "x$staticprogs" = "xyes"])
+
+
+CONFIGURE_PART(Audit Compilation Environment)
+
+
+dnl Check for the compiler and static/shared library creation.
+AC_PROG_CPP
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_LIBTOOL
+
+dnl Try to detect/use GNU features
+CFLAGS="$CFLAGS -D_GNU_SOURCE"
+
+dnl check for -Werror separatly
+dnl (quite a few autotool checks do not work with -Werror; also, the
+dnl check for -Werror fails after checking and adding the other flags)
+AC_CACHE_CHECK([if gcc likes the -Werror flag], rd_cv_gcc_flag__Werror,
+  [AC_COMPILE_IFELSE(
+    [AC_LANG_PROGRAM([[]], [[return 0 ]])],
+    [rd_cv_gcc_flag__Werror="yes"],
+    [rd_cv_gcc_flag__Werror="no"])])
+if test "x$rd_cv_gcc_flag__Werror" = "xyes"; then
+  WERROR="-Werror"
+else
+  WERROR=""
+fi
+AC_SUBST(WERROR)
+
+dnl which flags does the compiler support?
+if test "x$GCC" = "xyes"; then
+  for flag in -fno-strict-aliasing -Wall -std=c99 -pedantic -Wundef -Wshadow -Wpointer-arith -Wcast-align -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -Winline -Wold-style-definition -W; do
+    oCFLAGS="$CFLAGS"
+    CFLAGS="$CFLAGS $flag"
+    cachename=rd_cv_gcc_flag_`echo $flag|sed 's/[[^A-Za-z]]/_/g'`
+    AC_CACHE_CHECK([if gcc likes the $flag flag], $cachename,
+       [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[return 0 ]])],[eval $cachename=yes],[eval $cachename=no])])
+    if eval test \$$cachename = no; then
+         CFLAGS="$oCFLAGS"
+    fi
+  done
+fi
+
+
+
+AC_SUBST(RRD_DEFAULT_FONT)
+
+CONFIGURE_PART(Checking for Header Files)
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_HEADER_DIRENT
+AC_CHECK_HEADERS(langinfo.h stdint.h inttypes.h libgen.h features.h sys/stat.h sys/types.h fcntl.h fp_class.h malloc.h unistd.h ieeefp.h math.h sys/times.h sys/param.h sys/resource.h signal.h float.h stdio.h stdlib.h errno.h string.h ctype.h)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_HEADER_TIME
+AC_STRUCT_TM
+
+CONFIGURE_PART(Test Library Functions)
+
+dnl Checks for libraries.
+AC_CHECK_FUNC(acos, , AC_CHECK_LIB(m, acos))
+
+
+dnl add pic flag in any case this makes sure all our code is relocatable
+eval `./libtool --config | grep pic_flag`
+CFLAGS="$CFLAGS $pic_flag"
+
+
+dnl Checks for library functions.
+AC_FUNC_STRFTIME
+AC_FUNC_VPRINTF
+
+AC_C_BIGENDIAN
+
+dnl for each function found we get a definition in config.h 
+dnl of the form HAVE_FUNCTION
+
+AC_CHECK_FUNCS(nl_langinfo tzset fsync mbstowcs opendir readdir chdir chroot getuid strerror snprintf vsnprintf vasprintf fpclass class fp_class isnan memmove strchr mktime getrusage gettimeofday)
+
+AC_FUNC_STRERROR_R
+
+CONFIGURE_PART(Map/Fadvis/Madvise checking)
+
+dnl Could use these to know if we need to provide a prototype
+dnl AC_CHECK_DECLS(fdatasync, [], [], [#include <unistd.h>])
+
+dnl check for fdatasync. Solaris has fdatasync in the librt
+
+AC_CHECK_FUNCS(fdatasync, [],  AC_CHECK_LIB(rt, fdatasync, [LIBS="${LIBS} -lrt"; AC_DEFINE(HAVE_FDATASYNC)],[]))
+dnl if there is no fdatasync we may get lucky with fsync
+AC_CHECK_FUNCS(fsync)
+
+dnl check for socket and nsl solaris again ... we need this for the new rrd_daemon stuff
+
+AC_CHECK_FUNCS(socket, [],  AC_CHECK_LIB(socket, socket, [LIBS="${LIBS} -lsocket"; AC_DEFINE(HAVE_SOCKET)],[]))
+AC_CHECK_FUNCS(getaddrinfo, [],  AC_CHECK_LIB(nsl, getaddrinfo, [LIBS="${LIBS} -lnsl"; AC_DEFINE(HAVE_GETADDRINFO)],[]))
+
+
+
+dnl XXX: dunno about windows.. add AC_CHECK_FUNCS(munmap) there too?
+if test "x$enable_mmap" = "xyes"; then
+  case "$host" in
+  *cygwin*)
+    # the normal mmap test does not work in cygwin
+    AC_CHECK_FUNCS(mmap)
+    if test "x$ac_cv_func_mmap" = "xyes"; then
+      ac_cv_func_mmap_fixed_mapped=yes
+    fi
+  ;;
+  *)
+    AC_CHECK_HEADERS(sys/mman.h)
+    AC_FUNC_MMAP
+    AC_CHECK_FUNCS(mmap munmap msync)
+    AC_CHECK_DECLS(madvise, [], [], [#ifdef HAVE_SYS_MMAN_H
+                                    # include <sys/mman.h>
+                                    #endif])
+    if test "x$ac_cv_have_decl_madvise" = "xyes";
+    then
+      AC_CHECK_FUNCS(madvise)
+    else
+      AC_CHECK_FUNCS(posix_madvise)
+      if test "x$ac_cv_func_posix_madvise" != "xyes"; then
+        AC_MSG_WARN([madvise() nor posix_madvise() found.])
+      fi
+    fi
+  ;;
+  esac
+  if test "x$ac_cv_func_mmap" != "xyes";
+  then
+    AC_MSG_ERROR([--enable-mmap requested but mmap() was not detected])
+dnl enable_mmap="no"
+  fi
+fi
+
+dnl can we use posix_fadvise
+AC_CHECK_DECLS(posix_fadvise, [], [], [#define _XOPEN_SOURCE 600
+#include <fcntl.h>])
+AC_CHECK_FUNCS(posix_fadvise)
+
+CONFIGURE_PART(Libintl Processing)
+
+AM_GNU_GETTEXT_VERSION(0.17)
+AM_GNU_GETTEXT()
+
+CONFIGURE_PART(IEEE Math Checks)
+
+dnl actual code to check if this works
+AC_CHECK_FUNCS(fpclassify, ,
+  [AC_MSG_CHECKING(for fpclassify with <math.h>)
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <math.h>
+volatile int x;volatile float f; ]], [[x = fpclassify(f)]])],[AC_MSG_RESULT(yes)
+      AC_DEFINE(HAVE_FPCLASSIFY)],[AC_MSG_RESULT(no)])])
+
+AC_CHECK_FUNCS(isinf, ,
+  [AC_MSG_CHECKING(for isinf with <math.h>)
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <math.h>
+volatile int x;volatile float f;  ]], [[x = isinf(f)]])],[AC_MSG_RESULT(yes)
+      AC_DEFINE(HAVE_ISINF)],[AC_MSG_RESULT(no)])])
+
+dnl finite is BSD, isfinite is C99, so prefer the latter
+AC_CACHE_CHECK([whether isfinite is broken],[ac_cv_have_broken_isfinite],[
+AC_TRY_RUN([
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+int main ()
+{
+#ifdef isfinite
+#ifdef LDBL_MAX
+  if (!isfinite(LDBL_MAX)) return 1;
+#endif
+#ifdef DBL_MAX
+  if (!isfinite(DBL_MAX)) return 1;
+#endif
+#endif
+return 0;
+}],[ac_cv_have_broken_isfinite=no],[ac_cv_have_broken_isfinite=yes],[
+case "${target}" in
+  hppa*-*-hpux*) ac_cv_have_broken_isfinite=yes ;;
+  *-solaris2.8) ac_cv_have_broken_isfinite=yes ;;
+  *-solaris2.9) ac_cv_have_broken_isfinite=yes ;;
+  *) ac_cv_have_broken_isfinite=no ;;
+esac])
+])
+
+dnl the test does not seem to work on solaris 2.8
+dnl so lets fix this by hand
+case "${target}" in
+  *-solaris2.8) ac_cv_have_broken_isfinite=yes ;;
+  *-solaris2.9) ac_cv_have_broken_isfinite=yes ;;
+esac
+
+if test "x$ac_cv_have_broken_isfinite" = "xno"; then
+  AC_DEFINE(HAVE_ISFINITE)
+else
+  AC_CHECK_FUNCS(finite,[],
+      [AC_CHECK_FUNCS(isfinite,[],
+         [AC_MSG_CHECKING(for isfinite with <math.h>)
+          AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <math.h>
+volatile int x;volatile float f;  ]],[[x = isfinite(f)]])],[AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_ISFINITE)],[AC_MSG_RESULT(no)])])])
+fi  
+
+AC_FULL_IEEE
+
+CONFIGURE_PART(Resolve Portability Issues)
+
+CHECK_FOR_WORKING_MS_ASYNC
+
+dnl do we have nl_langinfo(_NL_TIME_WEEK_1STDAY)
+AC_CHECK_FUNCS(_NL_TIME_WEEK_1STDAY, ,
+  [AC_MSG_CHECKING([for nl_langinfo(_NL_TIME_WEEK_1STDAY) with langinfo.h])
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <langinfo.h>]], [[nl_langinfo(_NL_TIME_WEEK_1STDAY)]])],[AC_MSG_RESULT(yes)
+      AC_DEFINE(HAVE__NL_TIME_WEEK_1STDAY)],[AC_MSG_RESULT(no)])])
+
+dnl Do we need getopt_long
+
+dnl even when including our own getopt implementation
+dnl we may want to make sure we use the external
+dnl defined by libc to not run into linker resolve trouble
+
+AC_CACHE_CHECK([for opterr], rd_cv_var_int_opterr,
+[AC_TRY_LINK([#include <unistd.h>],
+  [extern int opterr; opterr = 1;],
+  [rd_cv_var_int_opterr=yes],
+  [rd_cv_var_int_opterr=no])])
+if test x"$rd_cv_var_int_opterr" = x"yes"; then
+  AC_DEFINE(HAVE_INT_OPTERR, 1, [Define to 1 if you have the global variable 'int opterr'.])
+fi
+
+build_getopt=no
+RRD_GETOPT_LONG="LIBC_HAS_GETOPT_LONG"
+AC_CHECK_FUNC(getopt_long,[],[
+RRD_GETOPT_LONG="getopt_long"
+build_getopt=yes
+])
+AC_SUBST(RRD_GETOPT_LONG)
+AM_CONDITIONAL(BUILD_GETOPT,[test $build_getopt = yes])
+
+dnl what does realloc do if it gets called with a NULL pointer
+
+AC_CACHE_CHECK([if realloc can deal with NULL], rd_cv_null_realloc,
+[AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdlib.h>
+             int main(void){
+              char *x = NULL;
+             x = realloc (x,10);
+             if (x==NULL) return 1;
+             return 0;
+             }]])],[rd_cv_null_realloc=yes],[rd_cv_null_realloc=nope],[:])])
+
+if test x"$rd_cv_null_realloc" = xnope; then
+AC_DEFINE(NO_NULL_REALLOC)
+fi
+
+AC_LANG_PUSH(C)
+dnl solaris has some odd defines it needs in order to propperly compile ctime_r
+AC_MSG_CHECKING([if ctime_r need special care to act posixly correct])
+AC_LINK_IFELSE(
+    AC_LANG_PROGRAM(
+           [[#include <time.h>]],
+           [[ctime_r(NULL,NULL,0)]]
+                   ),
+    [ CPPFLAGS="$CPPFLAGS -D_POSIX_PTHREAD_SEMANTICS"
+      AC_LINK_IFELSE(
+          AC_LANG_PROGRAM(
+                [[#include <time.h>]],
+                [[ctime_r(NULL,NULL)]]
+                        ),
+          [AC_MSG_RESULT([yes, this seems to be solaris style])],
+          [AC_MSG_ERROR([Can't figure how to compile ctime_r])]
+      )
+    ],  
+    [ AC_LINK_IFELSE(
+          AC_LANG_PROGRAM(
+                [[#include <time.h>]],
+                [[ctime_r(NULL,NULL)]]
+                        ),
+          [AC_MSG_RESULT(no)],
+          [AC_MSG_ERROR([Can't figure how to compile ctime_r])]
+      )
+    ]  
+)
+AC_LANG_POP(C)
+
+dnl Check for pthreads
+dnl http://autoconf-archive.cryp.to/acx_pthread.m4
+AC_SUBST(MULTITHREAD_CFLAGS)
+AC_SUBST(MULTITHREAD_LDFLAGS)
+
+if test $enable_pthread != no; then 
+ ACX_PTHREAD([
+    MULTITHREAD_CFLAGS=$PTHREAD_CFLAGS
+    MULTITHREAD_LDFLAGS=$PTHREAD_LIBS
+            ],
+            [])
+fi
+
+dnl since we use lots of *_r functions all over the code we better
+dnl make sure they are known
+
+if test  "x$x_rflag" != "xno"; then
+   CPPFLAGS="$CPPFLAGS $x_rflag"
+fi
+
+AM_CONDITIONAL(BUILD_MULTITHREAD,[test $enable_pthread != no])
+
+AC_LANG_PUSH(C)
+dnl see if we have to include malloc/malloc.h
+AC_MSG_CHECKING([do we need malloc/malloc.h])
+AC_LINK_IFELSE(
+    AC_LANG_PROGRAM(
+           [[#include <stdlib.h>]],
+           [[malloc(1)]]
+                   ),
+    [ AC_MSG_RESULT([nope, works out of the box]) ],
+    [ AC_LINK_IFELSE(
+          AC_LANG_PROGRAM(
+                [[#include <stdlib.h>
+                  #include <malloc/malloc.h>]],
+                [[malloc(1)]]
+                        ),[
+           AC_DEFINE([MUST_HAVE_MALLOC_MALLOC_H])
+           AC_MSG_RESULT([yes we do])],
+          [AC_MSG_ERROR([Can not figure how to compile malloc])]
+      )
+    ]  
+)
+
+dnl is time_t 32 of 64 bit ?
+AC_DEFINE([TIME_T_IS_32BIT], [], [time_t is 32bit])
+AC_DEFINE([TIME_T_IS_64BIT], [], [time_t is 64bit])
+AC_MSG_CHECKING([the type of time_t])
+AC_RUN_IFELSE(
+    AC_LANG_PROGRAM(
+        [[#include <time.h>]],
+        [[if (sizeof(time_t) != 4) return 1; ]]
+        ),
+    [ AC_MSG_RESULT([time_t is 32 bit])
+      AC_DEFINE([TIME_T_IS_32BIT])
+    ],
+    [ AC_RUN_IFELSE(
+        AC_LANG_PROGRAM(
+        [[#include <time.h>]],
+        [[if (sizeof(time_t) != 8) return 1; ]]
+        ),
+        [
+          AC_MSG_RESULT([time_t is 64 bit])
+          AC_DEFINE([TIME_T_IS_64BIT])
+        ],
+        [AC_MSG_ERROR([can not figure type of time_t])]
+      )
+    ]  
+)
+
+AC_LANG_POP(C)
+
+CONFIGURE_PART(Find 3rd-Party Libraries)
+
+AC_ARG_ENABLE(libdbi,AS_HELP_STRING([--disable-libdbi],[do not build in support for libdbi]),[have_libdbi=no],[
+  XXX=$LIBS
+  LIBS="$LIBS -ldbi -ldl"
+  AC_MSG_CHECKING(for libdbi)
+  AC_LINK_IFELSE(
+    [AC_LANG_PROGRAM([[#include <dbi/dbi.h>]], 
+                     [[dbi_initialize(NULL)]]
+                    )
+    ],[AC_DEFINE(HAVE_LIBDBI,[1],[have got libdbi installed])
+       AC_MSG_RESULT([yes])
+       have_libdbi=yes
+    ],[LIBS=$XXX
+       AC_MSG_RESULT([no])
+       have_libdbi=no
+    ]
+  )
+])
+AM_CONDITIONAL(BUILD_LIBDBI,[test $have_libdbi != no])
+
+AM_CONDITIONAL(BUILD_RRDCGI,[test $enable_rrdcgi != no])
+
+
+CORE_LIBS="$LIBS"
+
+dnl EX_CHECK_ALL(z,          zlibVersion,               zlib.h,                 zlib,        1.2.3,  http://www.gzip.org/zlib/, "")
+dnl EX_CHECK_ALL(png,        png_access_version_number, png.h,                  libpng,      1.2.10,  http://prdownloads.sourceforge.net/libpng/, "")
+dnl EX_CHECK_ALL(freetype,   FT_Init_FreeType,          ft2build.h,            freetype2,   2.1.10,  http://prdownloads.sourceforge.net/freetype/, /usr/include/freetype2)
+dnl EX_CHECK_ALL(fontconfig, FcInit,                    fontconfig.h,          fontconfig,  2.3.1,  http://fontconfig.org/release/, /usr/include)
+EX_CHECK_ALL(cairo,      cairo_font_options_create,     cairo.h,                cairo-png,   1.4.6,  http://cairographics.org/releases/, "")
+EX_CHECK_ALL(cairo,      cairo_svg_surface_create,      cairo-svg.h,            cairo-svg,   1.4.6,  http://cairographics.org/releases/, "")
+EX_CHECK_ALL(cairo,      cairo_pdf_surface_create,      cairo-pdf.h,            cairo-pdf,   1.4.6,  http://cairographics.org/releases/, "")
+EX_CHECK_ALL(cairo,      cairo_ps_surface_create,       cairo-ps.h,             cairo-ps,    1.4.6,  http://cairographics.org/releases/, "")
+EX_CHECK_ALL(glib-2.0,   glib_check_version,            glib.h,                 glib-2.0,    2.12.12, ftp://ftp.gtk.org/pub/glib/2.12/, "")
+EX_CHECK_ALL(pango-1.0,  pango_cairo_context_set_font_options,  pango/pango.h,  pangocairo,  1.17,    http://ftp.gnome.org/pub/GNOME/sources/pango/1.17, "")
+EX_CHECK_ALL(xml2,       xmlParseFile,                  libxml/parser.h,        libxml-2.0,        2.6.31,  http://xmlsoft.org/downloads.html, /usr/include/libxml2)
+
+if test "$EX_CHECK_ALL_ERR" = "YES"; then
+  AC_MSG_ERROR([Please fix the library issues listed above and try again.])
+fi
+
+ALL_LIBS="$LIBS"
+LIBS=
+
+AC_SUBST(CORE_LIBS)
+AC_SUBST(ALL_LIBS)
+
+CONFIGURE_PART(Prep for Building Language Bindings)
+  
+dnl Check for Perl and friends
+PATH=$PATH:/usr/perl5/bin
+export PATH
+AC_PATH_PROG(PERL, perl, no)
+AC_PATH_PROG(POD2MAN, pod2man, no)
+AC_PATH_PROG(POD2HTML, pod2html, no)
+
+
+AC_ARG_ENABLE(perl,AS_HELP_STRING([--disable-perl],[do not build the perl modules]),
+[],[enable_perl=yes])
+
+
+AC_ARG_VAR(PERLCC, [C compiler for Perl modules])
+AC_ARG_VAR(PERLCCFLAGS, [CC flags for Perl modules])
+AC_ARG_VAR(PERLLD, [Linker for Perl modules])
+AC_ARG_VAR(PERLLDFLAGS, [LD flags for Perl modules])
+
+if test "x$PERL" = "xno" -o  x$enable_perl = xno; then
+       COMP_PERL=
+else
+       COMP_PERL="perl_piped perl_shared"
+        AC_MSG_CHECKING(for the perl version you are running)
+       PERL_VERSION=`$PERL -MConfig -e 'print $Config{version}'`
+       AC_MSG_RESULT($PERL_VERSION)
+        if test -z "$PERLCC"; then
+            AC_MSG_CHECKING(for the C compiler perl wants to use to build its modules)
+           perlcc=`$PERL -MConfig -e 'print $Config{cc}'`
+           AC_MSG_RESULT($perlcc)
+           if test ! -x "$perlcc"; then
+               AC_PATH_PROG(PERL_CC, ${perlcc}, no)
+               if test "$PERL_CC" = "no"; then
+                    AC_MSG_WARN([
+I would not find the Compiler ($perlcc) that was originally used to compile
+your perl binary. You should either make sure that this compiler is
+available on your system, pick an other compiler and set PERLCC
+appropriately, or use a different perl setup that was compiled locally.
+
+I will disable the compilation of the RRDs perl module for now.
+])
+                   COMP_PERL="perl_piped"
+               fi
+            fi    
+        fi
+fi
+
+AC_MSG_CHECKING(Perl Modules to build)
+AC_MSG_RESULT(${COMP_PERL:-No Perl Modules will be built})
+
+# Options to pass when configuring perl module
+langpref=$prefix
+test "$langpref" = '$(DESTDIR)NONE' && langpref='$(DESTDIR)'$ac_default_prefix
+test "$langpref" = "NONE" && langpref=$ac_default_prefix
+
+PERL_MAKE_OPTIONS="PREFIX=$langpref LIB=$langpref/lib/perl/$PERL_VERSION"
+
+dnl pass additional perl options when generating Makefile from Makefile.PL
+AC_ARG_ENABLE(perl-site-install,
+AS_HELP_STRING([--enable-perl-site-install],[by default the rrdtool perl modules are installed together with rrdtool in $prefix/lib/perl. You have to put a 'use lib qw($prefix/lib/perl)' into your scripts when you want to use them. When you set this option the perl modules will get installed wherever your perl setup thinks it is best.]),
+[PERL_MAKE_OPTIONS=],[])
+
+if test ! -z "$PERLCC"; then
+   PERL_MAKE_OPTIONS="$PERL_MAKE_OPTIONS CC=$PERLCC"
+
+   if test ! -z "$PERLCCFLAGS"; then
+       PERL_MAKE_OPTIONS="$PERL_MAKE_OPTIONS CCFLAGS=$PERLCCFLAGS"
+   fi
+   
+   if test -z "$PERLLD"; then
+       PERLLD=$PERLCC
+   fi
+   PERL_MAKE_OPTIONS="$PERL_MAKE_OPTIONS LD=$PERLLD"
+  
+   if test ! -z "$PERLLDFLAGS"; then
+       PERL_MAKE_OPTIONS="$PERL_MAKE_OPTIONS LDFLAGS=$PERLLDFLAGS"
+   fi
+fi
+        
+AC_ARG_WITH(perl-options,
+[  --with-perl-options=[OPTIONS]  options to pass on command-line when
+                          generating Makefile from Makefile.PL. If you set this
+                          option, interesting things may happen unless you know
+                          what you are doing!],
+[PERL_MAKE_OPTIONS=$withval])
+
+AC_SUBST(PERL_MAKE_OPTIONS)
+AC_SUBST(PERL)
+AC_SUBST(COMP_PERL)
+AC_SUBST(PERL_VERSION)
+
+dnl Check for Ruby.
+AC_PATH_PROG(RUBY, ruby, no)
+
+AC_ARG_ENABLE(ruby,AS_HELP_STRING([--disable-ruby],[do not build the ruby modules]),
+[],[enable_ruby=yes])
+
+AC_MSG_CHECKING(if ruby modules can be built)
+
+if test "x$RUBY" = "xno" -o  x$enable_ruby = xno; then
+       COMP_RUBY=
+       AC_MSG_RESULT(No .. Ruby not found or disabled)
+else
+       if $RUBY -e 'require "mkmf"' >/dev/null 2>&1; then
+               COMP_RUBY="ruby"
+               AC_MSG_RESULT(YES)
+       else
+               COMP_RUBY=
+               AC_MSG_RESULT(Ruby found but mkmf is missing! Install the -dev package)         
+       fi                              
+fi
+
+dnl pass additional ruby options when generating Makefile from Makefile.PL
+AC_ARG_ENABLE(ruby-site-install,
+AS_HELP_STRING([--enable-ruby-site-install],[by default the rrdtool ruby modules are installed together with rrdtool in $prefix/lib/ruby. You have to add $prefix/lib/ruby/$ruby_version/$sitearch to your $: variable for ruby to find the RRD.so file.]),
+[RUBY_MAKE_OPTIONS=],[RUBY_MAKE_OPTIONS="sitedir=$langpref/lib/ruby"])
+
+    
+AC_ARG_WITH(ruby-options,
+[  --with-ruby-options=[OPTIONS]  options to pass on command-line when
+                          generating Makefile from extconf.rb. If you set this
+                          option, interesting things may happen unless you know
+                          what you are doing!],
+[RUBY_MAKE_OPTIONS=$withval])
+
+AC_SUBST(RUBY_MAKE_OPTIONS)
+AC_SUBST(RUBY)
+AC_SUBST(COMP_RUBY)
+
+dnl Check for Lua.
+AC_PATH_PROG(LUA, lua, no)
+
+AC_ARG_ENABLE(lua,AS_HELP_STRING([--disable-lua],[do not build the lua modules]),
+[],[enable_lua=yes])
+
+COMP_LUA=
+if test "$LUA" = "no" -o "$enable_lua" = "no"; then
+  enable_lua=no
+else
+  AC_MSG_CHECKING(for lua >= 5.0)
+  read LUA_MAJOR LUA_MINOR LUA_POINT <<LUA_EOF
+    $($LUA -v 2>&1 | cut -f2 -d' ' | sed -e 's/\./ /g')
+LUA_EOF
+  if test 0$LUA_MAJOR -lt 5; then
+    AC_MSG_RESULT([no, version found is $LUA_MAJOR.$LUA_MINOR])
+  else
+    AC_MSG_RESULT([$LUA_MAJOR.$LUA_MINOR found])
+    lua_vdot=$LUA_MAJOR.$LUA_MINOR
+    lua_vndot=$LUA_MAJOR$LUA_MINOR
+    lua_version=$LUA_MAJOR.$LUA_MINOR.$LUA_POINT
+    AC_CHECK_HEADERS(lua$lua_vndot/lua.h,
+      [AC_CHECK_HEADERS(lua$lua_vndot/lualib.h,
+        [AC_CHECK_HEADER(lua$lua_vndot/lauxlib.h,
+          [lua_headerdir=lua$lua_vndot],
+          [])],
+        [])],
+      [AC_CHECK_HEADERS(lua$lua_vdot/lua.h,
+        [AC_CHECK_HEADERS(lua$lua_vdot/lualib.h,
+          [AC_CHECK_HEADER(lua$lua_vdot/lauxlib.h,
+            [lua_headerdir=lua$lua_vdot],
+            [])],
+          [])],
+        [AC_CHECK_HEADERS(lua.h,
+          [AC_CHECK_HEADERS(lualib.h,
+            [AC_CHECK_HEADER(lauxlib.h,
+              [lua_headerdir=""],
+              [lua_headerdir="no"])],
+            [])],
+          [])])])
+
+    if test "$lua_headerdir" = "no"; then
+      enable_lua=no
+    else
+      COMP_LUA=lua
+    fi
+
+    if test "$COMP_LUA" != "lua"; then
+      enable_lua=no
+      AC_MSG_WARN([Lua $lua_vdot found but not lua.h, lualib.h and lauxlib.h! Please install the -dev packages for Lua $lua_vdot])
+    else
+      # OK, headers found, let's check the libraries (LIBS is not used)
+      LIBS=
+      lua_havelib=no
+      LUA_HAVE_COMPAT51=DONT_HAVE_COMPAT51
+      AC_SEARCH_LIBS(lua_call, lua$lua_vdot lua$lua_vndot lua,
+        [AC_SEARCH_LIBS(luaL_register, lua$lua_vdot lua$lua_vndot lua,
+          [lua_havelib=LUA$lua_vndot],
+          [AC_SEARCH_LIBS(luaL_module, lualib$lua_vndot lualib$lua_vdot lualib,
+            [lua_havelib=$lua_vndot; $LUA -l compat-5.1 2>/dev/null;
+             test "$?" = "0" && LUA_HAVE_COMPAT51=HAVE_COMPAT51],
+            [AC_SEARCH_LIBS(luaL_openlib, lualib$lua_vdot lualib$lua_vndot lualib,
+              [lua_havelib=$lua_vndot],
+              [COMP_LUA=], [-lm])], [-lm])], [-lm])],
+        [COMP_LUA=], [-lm])
+      lua_libs=$LIBS
+      LIBS=
+
+      # Options to pass when configuring Lua module
+      if test  "$lua_havelib" != "no"; then
+        # OK, headers and libs found. Try to set lua flags
+        # and modules installation dirs with pkg-config
+        if test "$PKGCONFIG" != "no"; then
+          if test "$lua_vndot" = "50"; then
+            lua_pkg_prefix=lualib
+          else
+            lua_pkg_prefix=lua
+          fi
+          # try with dot, without dot and finally without version
+          for f in $lua_pkg_prefix$lua_vdot $lua_pkg_prefix$lua_vndot $lua_pkg_prefix; do
+            lua_exec_prefix=`$PKGCONFIG --variable=prefix $f 2>/dev/null`
+            # same binaries?
+            if test "$lua_exec_prefix/bin/lua" = "$LUA"; then 
+                # OK, found CFLAGS. Get Lua LFLAGS and modules install dir
+                LUA_CFLAGS=`$PKGCONFIG --cflags $f 2>/dev/null`
+                LUA_LFLAGS=`$PKGCONFIG --libs $f 2>/dev/null`
+                LUA_INSTALL_CMOD=`$PKGCONFIG --variable=INSTALL_CMOD $f 2>/dev/null`
+                LUA_INSTALL_LMOD=`$PKGCONFIG --variable=INSTALL_LMOD $f 2>/dev/null`
+                break
+            fi
+          done
+        fi
+
+        LUA_RRD_LIBDIR="$langpref/lib/lua/$lua_vdot"
+        # if lua 5.0 can't find compat-5.1, force installation of
+        # compat-5.1.lua together with RRDtool.
+        if test "$lua_vdot" = "5.0" -a "$LUA_HAVE_COMPAT51" != "HAVE_COMPAT51"; then
+          lua_need_compat51=1 
+          LUA_INSTALL_LMOD="$LUA_RRD_LIBDIR"
+        fi
+
+        # if not set with pkg-config, use default values in src packages compat-5.1, lua 5.1
+        if test "$LUA_CFLAGS" = ""; then
+          AC_MSG_WARN(Setting Lua include and lib flags to defaults in compat-5.1 and lua 5.1 sources)
+          LUA_CFLAGS="-I/usr/local/include -I/usr/local/include/lua -I/usr/local/include/lua/$lua_vdot"
+          LUA_LFLAGS="-L/usr/local/lib -L/usr/local/lib/lua -L/usr/local/lib/lua/$lua_vdot $lua_libs"
+          LUA_INSTALL_CMOD="/usr/local/lib/lua/$lua_vdot"
+        fi
+
+        dnl pass additional lua options
+        dnl if lua-site-install is not set, overwrite LUA_INSTALL_CMOD already
+        dnl found and install together with RRDtool, under $langpref.
+        AC_ARG_ENABLE(lua-site-install,
+        AS_HELP_STRING([--enable-lua-site-install],[by default the lua module is installed together with rrdtool in $prefix/lib/lua/$lua_version. You have to add $prefix/lib/lua/$lua_version/?.so to package.cpath for lua to find 'rrd.so'. For lua 5.0 you may also need to change LUA_PATH to the same dir, to require 'compat-5.1'. When you set this option the lua modules will get installed wherever your Lua setup thinks it is best. WARNING: if you set this option, system lua modules compat-5.1.lua and rrd.so, if any, may be overwritten.]),
+        [],
+        [LUA_INSTALL_CMOD="$LUA_RRD_LIBDIR"; LUA_INSTALL_LMOD="$LUA_RRD_LIBDIR"])
+
+        LUA_DEFINES="-DLUA$lua_vndot -D$LUA_HAVE_COMPAT51"
+        AC_SUBST(LUA)
+        AC_SUBST(COMP_LUA)
+        AC_SUBST(LUA_INSTALL_CMOD)
+        AC_SUBST(LUA_INSTALL_LMOD)
+        AC_SUBST(LUA_CFLAGS)
+        AC_SUBST(LUA_LFLAGS)
+       AC_SUBST(LUA_DEFINES)
+      else
+        enable_lua=no
+        AC_MSG_RESULT([Lua headers found but not the libraries! Please reinstall the dev packages for Lua $LUA_MAJOR.$LUA_MINOR])
+      fi
+    fi
+  fi
+fi
+dnl If Lua 5.0, we need compat-5.1. Add ours unless already
+dnl integrated as in Debian/Ubuntu 5.0 -dev packages.
+AM_CONDITIONAL(LUA_NEED_OUR_COMPAT51,
+      [test "$lua_vdot" = "5.0" -a "$LUA_HAVE_COMPAT51" != "HAVE_COMPAT51"])
+AM_CONDITIONAL(LUA_SITE_CINSTALL, [test "$LUA_INSTALL_CMOD" != "$LUA_RRD_LIBDIR"])
+AM_CONDITIONAL(LUA_SITE_LINSTALL, [test "$LUA_INSTALL_LMOD" != "$LUA_RRD_LIBDIR"])
+AM_CONDITIONAL(LUA50, [test "$lua_vndot" = "50"])
+AM_CONDITIONAL(BUILD_LUA, [test "$enable_lua" = "yes"])
+
+enable_tcl_site=no
+
+AC_ARG_ENABLE(tcl,AS_HELP_STRING([--disable-tcl],[do not build the tcl modules]),
+[],[enable_tcl=yes])
+
+if test  "$enable_tcl" = "yes"; then
+  dnl Check for Tcl.
+  withval=""
+  AC_ARG_WITH(tcllib,[  --with-tcllib=DIR       location of the tclConfig.sh])
+  enable_tcl=no
+  for dir in $withval /usr/lib /usr/local/lib /usr/lib/tcl8.4 /usr/lib/tcl8.3 ; do
+    AC_MSG_CHECKING(for tclConfig.sh in $dir)
+    if test -f "$dir/tclConfig.sh" ; then
+       tcl_config=$dir/tclConfig.sh
+        enable_tcl=yes
+        AC_MSG_RESULT(yes)
+        break
+    else
+        AC_MSG_RESULT(no)
+    fi
+  done
+
+  if test "$enable_tcl" = "no"; then
+        AC_MSG_WARN([tclConfig.sh not found - Tcl interface will not be built])
+  else
+       . $tcl_config
+       TCL_PACKAGE_DIR="$TCL_PACKAGE_PATH/tclrrd$VERSION"
+       if test -n "$TCL_INC_DIR"; then
+         TCL_INCLUDE_SPEC="$TCL_INCLUDE_SPEC -I$TCL_INC_DIR"
+       fi
+  fi
+  AC_ARG_ENABLE(tcl,AS_HELP_STRING([--enable-tcl-site],[install the tcl extension in the tcl tree]),
+  [],[enable_tcl_site=yes])
+
+fi
+
+AM_CONDITIONAL(BUILD_TCL, test "$enable_tcl" = "yes" )
+AM_CONDITIONAL(BUILD_TCL_SITE, test "$enable_tcl_site" = "yes" )
+
+
+AC_SUBST(TCL_PREFIX)
+AC_SUBST(TCL_SHLIB_CFLAGS)
+AC_SUBST(TCL_SHLIB_LD)
+AC_SUBST(TCL_SHLIB_SUFFIX)
+AC_SUBST(TCL_PACKAGE_PATH)
+AC_SUBST(TCL_LD_SEARCH_FLAGS)
+AC_SUBST(TCL_STUB_LIB_SPEC)
+AC_SUBST(TCL_VERSION)
+AC_SUBST(TCL_PACKAGE_DIR)
+AC_SUBST(TCL_INCLUDE_SPEC)
+
+AC_ARG_ENABLE(python,AS_HELP_STRING([--disable-python],[do not build the python modules]),
+[],[enable_python=yes])
+
+if test  "$enable_python" = "yes"; then
+dnl Check for python
+AM_PATH_PYTHON(2.3,[],[enable_python=no])
+AM_CHECK_PYTHON_HEADERS(,[enable_python=no;AC_MSG_WARN(could not find Python headers)])
+fi
+
+if test  x$enable_python = xno; then
+       COMP_PYTHON=
+else
+       COMP_PYTHON="python"
+fi
+
+AC_SUBST(COMP_PYTHON)
+
+dnl Check for nroff
+AC_PATH_PROGS(NROFF, gnroff nroff)
+AC_PATH_PROGS(TROFF, groff troff)
+
+AC_ARG_VAR(RRDDOCDIR, [[DATADIR/doc/PACKAGE-VERSION] Documentation directory])
+if test -z "$RRDDOCDIR"; then
+   RRDDOCDIR='${datadir}/doc/${PACKAGE}-${VERSION}'; fi
+
+
+CONFIGURE_PART(Apply Configuration Information)
+AC_CONFIG_FILES([examples/shared-demo.pl])
+AC_CONFIG_FILES([examples/piped-demo.pl])
+AC_CONFIG_FILES([examples/stripes.pl])
+AC_CONFIG_FILES([examples/bigtops.pl])
+AC_CONFIG_FILES([examples/minmax.pl])
+AC_CONFIG_FILES([examples/4charts.pl])
+AC_CONFIG_FILES([examples/perftest.pl])
+AC_CONFIG_FILES([examples/Makefile])
+AC_CONFIG_FILES([examples/rrdcached/Makefile])
+AC_CONFIG_FILES([doc/Makefile])
+AC_CONFIG_FILES([po/Makefile.in]) 
+AC_CONFIG_FILES([intl/Makefile]) 
+AC_CONFIG_FILES([src/Makefile])
+AC_CONFIG_FILES([src/librrd.sym.in])
+AC_CONFIG_FILES([src/librrd.pc])
+AC_CONFIG_FILES([bindings/Makefile])
+AC_CONFIG_FILES([bindings/tcl/Makefile])
+AC_CONFIG_FILES([bindings/tcl/ifOctets.tcl])
+AC_CONFIG_FILES([Makefile])          
+AC_CONFIG_FILES([bindings/lua/Makefile])
+
+AC_CONFIG_COMMANDS([default],[[ chmod +x examples/*.pl]],[[]])
+
+dnl intl requires our config to be called config.h. indulge it.
+AC_CONFIG_COMMANDS_POST([ test -f config.h || ln -s rrd_config.h config.h ])
+
+AC_OUTPUT
+
+AC_MSG_CHECKING(in)
+AC_MSG_RESULT(and out again)
+
+echo $ECHO_N "ordering CD from http://tobi.oetiker.ch/wish $ECHO_C" 1>&6
+sleep 1
+echo $ECHO_N ".$ECHO_C" 1>&6
+sleep 1
+echo $ECHO_N ".$ECHO_C" 1>&6
+sleep 1
+echo $ECHO_N ".$ECHO_C" 1>&6
+sleep 1
+echo $ECHO_N ".$ECHO_C" 1>&6
+sleep 1
+AC_MSG_RESULT([ just kidding ;-)])
+echo
+echo "----------------------------------------------------------------"
+echo "Config is DONE!"
+echo
+echo "          With MMAP IO: $enable_mmap"
+echo "      Build rrd_getopt: $build_getopt"
+echo "       Static programs: $staticprogs"
+echo "          Perl Modules: $COMP_PERL"
+echo "           Perl Binary: $PERL"
+echo "          Perl Version: $PERL_VERSION"
+echo "          Perl Options: $PERL_MAKE_OPTIONS"
+echo "          Ruby Modules: $COMP_RUBY"
+echo "           Ruby Binary: $RUBY"
+echo "          Ruby Options: $RUBY_MAKE_OPTIONS"
+echo "    Build Lua Bindings: $enable_lua"
+if test "$enable_lua" = "yes"; then
+echo "            Lua Binary: $LUA"
+echo "           Lua Version: $lua_version"
+echo "     Lua C-modules dir: $LUA_INSTALL_CMOD"
+if test "$lua_need_compat51" = "1"; then
+echo "   Lua Lua-modules dir: $LUA_INSTALL_LMOD"
+fi
+fi
+echo "    Build Tcl Bindings: $enable_tcl"
+echo " Build Python Bindings: $enable_python"
+echo "          Build rrdcgi: $enable_rrdcgi"
+echo "       Build librrd MT: $enable_pthread"
+echo "           Use gettext: $USE_NLS"
+echo "           With libDBI: $have_libdbi"
+echo
+echo "             Libraries: $ALL_LIBS"
+echo
+echo "Type 'make' to compile the software and use 'make install' to "
+echo "install everything to: $prefix."
+echo 
+echo "       ... that wishlist is NO JOKE. If you find RRDtool useful"
+echo "make me happy. Go to http://tobi.oetiker.ch/wish and"
+echo "place an order."
+echo 
+echo "                               -- Tobi Oetiker <tobi@oetiker.ch>"
+echo "----------------------------------------------------------------"
diff --git a/program/debian/README b/program/debian/README
new file mode 100644 (file)
index 0000000..0c2c5a0
--- /dev/null
@@ -0,0 +1,3 @@
+Find the latest debian packaging files at
+git://git.snow-crash.org/pkg-rrdtool.git and mirrored at
+git://git.tokkee.org/pkg-rrdtool.git
diff --git a/program/doc/Makefile.am b/program/doc/Makefile.am
new file mode 100644 (file)
index 0000000..c805740
--- /dev/null
@@ -0,0 +1,87 @@
+## Process this file with automake to produce Makefile.in
+
+SUFFIXES = .pod .pl .1 .3 .man .html .txt .pm .pdf .inc
+
+AUTOMAKE_OPTIONS        =  foreign
+
+#ACLOCAL_M4 = $(top_srcdir)/config/aclocal.m4
+
+CLEANFILES = *.1 *.3 *.html *.txt *-dircache RRD?.pod *.pdf *~ core *itemcache *.rej *.orig *.tmp
+
+POD = bin_dec_hex.pod        rrddump.pod            rrdgraph_examples.pod  rrdrestore.pod         rrdupdate.pod  \
+      cdeftutorial.pod       rrdfetch.pod           rrdgraph_graph.pod     rrdthreads.pod         rrdxport.pod   \
+      rpntutorial.pod        rrdfirst.pod           rrdgraph_rpn.pod       rrdtool.pod            rrdcached.pod  \
+      rrd-beginners.pod      rrdinfo.pod            rrdtune.pod            rrdbuild.pod           rrdflushcached.pod   \
+      rrdcgi.pod             rrdgraph.pod           rrdlast.pod            rrdlastupdate.pod                     \
+      rrdcreate.pod          rrdgraph_data.pod      rrdresize.pod          rrdtutorial.pod
+
+if BUILD_LIBDBI
+  POD += rrdgraph_libdbi.pod
+endif
+
+if BUILD_LUA
+  POD += rrdlua.pod
+endif
+
+POD3 = librrd.pod
+
+PMP = RRDs.pod RRDp.pod
+
+MAN = $(POD:.pod=.1)
+MAN3 = $(POD3:.pod=.3)
+TXT = $(MAN:.1=.txt) $(MAN3:.3=.txt)
+HTML = $(POD:.pod=.html) $(POD3:.pod=.html) $(PMP:.pod=.html)
+PDF = $(MAN:.1=.pdf) $(MAN3:.3=.pdf)
+
+# what should go into the distribution
+EXTRA_DIST= $(POD) $(POD3) $(HTML) $(MAN) $(MAN3) $(TXT) rrdtool-dump.dtd rrdtool-xport.dtd rrdgraph_libdbi.pod rrdlua.pod
+
+idocdir = $(RRDDOCDIR)/txt
+idoc_DATA = $(POD) $(TXT)
+ihtmldir = $(RRDDOCDIR)/html
+ihtml_DATA = $(HTML)
+imandir = $(mandir)/man1
+iman_DATA = $(MAN)
+iman3dir = $(mandir)/man3
+iman3_DATA = $(MAN3)
+
+all-local: link man txt html-local
+
+.src.pod:
+       perl -n -e 'if (/^=include\s+(\S+)/){open F,"$$1.inc" || die $$?;print <F>; close F} else {print}'  $<  > $@
+
+.pod.1 .pm.1 .pl.1:
+       $(AM_V_GEN)@POD2MAN@ --release=$(VERSION) --center=rrdtool $<  > $@
+
+.pod.3:
+       $(AM_V_GEN)@POD2MAN@ --release=$(VERSION) --center=rrdtool --section=3 $< > $@
+
+.1.txt .3.txt:
+       $(AM_V_GEN)GROFF_NO_SGR=1 @NROFF@ -man -Tlp $< > $@
+
+.1.pdf .3.pdf:
+       $(AM_V_GEN)@TROFF@ -man $< | ps2pdf - $@
+
+.pm.html .pod.html .pl.html:
+       $(AM_V_GEN)@POD2HTML@ --infile=$< --outfile=$@ --noindex --htmlroot=. --podpath=. --title=$*
+
+RRDs.pod:
+       $(AM_V_GEN)$(LN_S) $(top_srcdir)/bindings/perl-shared/RRDs.pm RRDs.pod
+
+RRDp.pod:
+       $(AM_V_GEN)$(LN_S) $(top_srcdir)/bindings/perl-piped/RRDp.pm RRDp.pod
+
+link: RRDp.pod RRDs.pod
+
+man: $(MAN) $(MAN3)
+
+html-local: $(HTML)
+
+txt: $(TXT)
+
+pdf-local: $(PDF)
+
+pod: $(POD) $(POD3)
+
+install-data-hook:
+       $(AM_V_GEN)cd $(DESTDIR)$(ihtmldir) && rm -f index.html && $(LN_S) rrdtool.html index.html
diff --git a/program/doc/RRDp.pod b/program/doc/RRDp.pod
new file mode 120000 (symlink)
index 0000000..11703f7
--- /dev/null
@@ -0,0 +1 @@
+../bindings/perl-piped/RRDp.pm
\ No newline at end of file
diff --git a/program/doc/RRDs.pod b/program/doc/RRDs.pod
new file mode 120000 (symlink)
index 0000000..30a99d2
--- /dev/null
@@ -0,0 +1 @@
+../bindings/perl-shared/RRDs.pm
\ No newline at end of file
diff --git a/program/doc/bin_dec_hex.pod b/program/doc/bin_dec_hex.pod
new file mode 100644 (file)
index 0000000..e52f200
--- /dev/null
@@ -0,0 +1,370 @@
+=head1 NAME
+
+bin_dec_hex - How to use binary, decimal, and hexadecimal notation.
+
+=head1 DESCRIPTION
+
+Most people use the decimal numbering system. This system uses ten
+symbols to represent numbers. When those ten symbols are used up, they
+start all over again and increment the position to the left. The
+digit 0 is only shown if it is the only symbol in the sequence, or if
+it is not the first one.
+
+If this sounds cryptic to you, this is what I've just said in numbers:
+
+     0
+     1
+     2
+     3
+     4
+     5
+     6
+     7
+     8
+     9
+    10
+    11
+    12
+    13
+
+and so on.
+
+Each time the digit nine is incremented, it is reset to 0 and the
+position before (to the left) is incremented (from 0 to 1). Then
+number 9 can be seen as "00009" and when we should increment 9, we
+reset it to zero and increment the digit just before the 9 so the
+number becomes "00010". Leading zeros we don't write except if it is
+the only digit (number 0). And of course, we write zeros if they occur
+anywhere inside or at the end of a number:
+
+ "00010" -> " 0010" -> " 010" -> "  10", but not "  1 ".
+
+This was pretty basic, you already knew this. Why did I tell it?
+Well, computers usually do not represent numbers with 10 different
+digits. They only use two different symbols, namely "0" and "1". Apply
+the same rules to this set of digits and you get the binary numbering
+system:
+
+     0
+     1
+    10
+    11
+   100
+   101
+   110
+   111
+  1000
+  1001
+  1010
+  1011
+  1100
+  1101
+
+and so on.
+
+If you count the number of rows, you'll see that these are again 14
+different numbers. The numbers are the same and mean the same as in
+the first list, we just used a different representation. This means
+that you have to know the representation used, or as it is called the
+numbering system or base.  Normally, if we do not explicitly specify
+the numbering system used, we implicitly use the decimal system. If we
+want to use any other numbering system, we'll have to make that
+clear. There are a few widely adopted methods to do so. One common
+form is to write 1010(2) which means that you wrote down a number in
+its binary representation. It is the number ten. If you would write
+1010 without specifying the base, the number is interpreted as one
+thousand and ten using base 10.
+
+In books, another form is common. It uses subscripts (little
+characters, more or less in between two rows). You can leave out the
+parentheses in that case and write down the number in normal
+characters followed by a little two just behind it.
+
+As the numbering system used is also called the base, we talk of the
+number 1100 base 2, the number 12 base 10.
+
+Within the binary system, it is common to write leading zeros. The
+numbers are written down in series of four, eight or sixteen depending
+on the context.
+
+We can use the binary form when talking to computers
+(...programming...), but the numbers will have large
+representations. The number 65'535 (often in the decimal system a ' is
+used to separate blocks of three digits for readability) would be
+written down as 1111111111111111(2) which is 16 times the digit 1.
+This is difficult and prone to errors. Therefore, we usually would use
+another base, called hexadecimal. It uses 16 different symbols. First
+the symbols from the decimal system are used, thereafter we continue
+with alphabetic characters. We get 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+A, B, C, D, E and F. This system is chosen because the hexadecimal
+form can be converted into the binary system very easily (and back).
+
+There is yet another system in use, called the octal system. This was
+more common in the old days, but is not used very often anymore. As
+you might find it in use sometimes, you should get used to it and
+we'll show it below. It's the same story as with the other
+representations, but with eight different symbols.
+
+ Binary      (2)
+ Octal       (8)
+ Decimal     (10)
+ Hexadecimal (16)
+
+ (2)    (8) (10) (16)
+ 00000   0    0    0
+ 00001   1    1    1
+ 00010   2    2    2
+ 00011   3    3    3
+ 00100   4    4    4
+ 00101   5    5    5
+ 00110   6    6    6
+ 00111   7    7    7
+ 01000  10    8    8
+ 01001  11    9    9
+ 01010  12   10    A
+ 01011  13   11    B
+ 01100  14   12    C
+ 01101  15   13    D
+ 01110  16   14    E
+ 01111  17   15    F
+ 10000  20   16   10
+ 10001  21   17   11
+ 10010  22   18   12
+ 10011  23   19   13
+ 10100  24   20   14
+ 10101  25   21   15
+
+Most computers used nowadays are using bytes of eight bits. This means
+that they store eight bits at a time. You can see why the octal system
+is not the most practical for that: You'd need three digits to represent
+the eight bits and this means that you'd have to use one complete digit
+to represent only two bits (2+3+3=8). This is a waste. For hexadecimal
+digits, you need only two digits which are used completely:
+
+ (2)      (8)  (10) (16)
+ 11111111 377  255   FF
+
+You can see why binary and hexadecimal can be converted quickly: For
+each hexadecimal digit there are exactly four binary digits.  Take a
+binary number: take four digits from the right and make a hexadecimal
+digit from it (see the table above). Repeat this until there are no
+more digits. And the other way around: Take a hexadecimal number. For
+each digit, write down its binary equivalent.
+
+Computers (or rather the parsers running on them) would have a hard
+time converting a number like 1234(16). Therefore hexadecimal numbers
+are specified with a prefix. This prefix depends on the language
+you're writing in. Some of the prefixes are "0x" for C, "$" for
+Pascal, "#" for HTML.  It is common to assume that if a number starts
+with a zero, it is octal. It does not matter what is used as long as
+you know what it is. I will use "0x" for hexadecimal, "%" for binary
+and "0" for octal.  The following numbers are all the same, just their
+representation (base) is different: 021 0x11 17 %00010001
+
+To do arithmetics and conversions you need to understand one more thing.
+It is something you already know but perhaps you do not "see" it yet:
+
+If you write down 1234, (no prefix, so it is decimal) you are talking
+about the number one thousand, two hundred and thirty four. In sort of
+a formula:
+
+ 1 * 1000 = 1000
+ 2 *  100 =  200
+ 3 *   10 =   30
+ 4 *    1 =    4
+
+This can also be written as:
+
+ 1 * 10^3
+ 2 * 10^2
+ 3 * 10^1
+ 4 * 10^0
+
+where ^ means "to the power of".
+
+We are using the base 10, and the positions 0,1,2 and 3.
+The right-most position should NOT be multiplied with 10. The second
+from the right should be multiplied one time with 10. The third from
+the right is multiplied with 10 two times. This continues for whatever
+positions are used.
+
+It is the same in all other representations:
+
+0x1234 will be
+
+ 1 * 16^3
+ 2 * 16^2
+ 3 * 16^1
+ 4 * 16^0
+
+01234 would be
+
+ 1 * 8^3
+ 2 * 8^2
+ 3 * 8^1
+ 4 * 8^0
+
+This example can not be done for binary as that system only uses two
+symbols. Another example:
+
+%1010 would be
+
+ 1 * 2^3
+ 0 * 2^2
+ 1 * 2^1
+ 0 * 2^0
+
+It would have been easier to convert it to its hexadecimal form and
+just translate %1010 into 0xA. After a while you get used to it. You will
+not need to do any calculations anymore, but just know that 0xA means 10.
+
+To convert a decimal number into a hexadecimal you could use the next
+method. It will take some time to be able to do the estimates, but it
+will be easier when you use the system more frequently. We'll look at
+yet another way afterwards.
+
+First you need to know how many positions will be used in the other
+system. To do so, you need to know the maximum numbers you'll be
+using. Well, that's not as hard as it looks. In decimal, the maximum
+number that you can form with two digits is "99". The maximum for
+three: "999". The next number would need an extra position. Reverse
+this idea and you will see that the number can be found by taking 10^3
+(10*10*10 is 1000) minus 1 or 10^2 minus one.
+
+This can be done for hexadecimal as well:
+
+ 16^4 = 0x10000 = 65536
+ 16^3 =  0x1000 =  4096
+ 16^2 =   0x100 =   256
+ 16^1 =    0x10 =    16
+
+If a number is smaller than 65'536 it will fit in four positions.
+If the number is bigger than 4'095, you must use position 4.
+How many times you can subtract 4'096 from the number without going below
+zero is the first digit you write down. This will always be a number
+from 1 to 15 (0x1 to 0xF). Do the same for the other positions.
+
+Let's try with 41'029. It is smaller than 16^4 but bigger than 16^3-1. This
+means that we have to use four positions.
+We can subtract 16^3 from 41'029 ten times without going below zero.
+The left-most digit will therefore be "A", so we have 0xA????.
+The number is reduced to 41'029 - 10*4'096 = 41'029-40'960 = 69.
+69 is smaller than 16^3 but not bigger than 16^2-1. The second digit
+is therefore "0" and we now have 0xA0??.
+69 is smaller than 16^2 and bigger than 16^1-1. We can subtract 16^1
+(which is just plain 16) four times and write down "4" to get 0xA04?.
+Subtract 64 from 69 (69 - 4*16) and the last digit is 5 --> 0xA045.
+
+The other method builds up the number from the right. Let's try 41'029
+again.  Divide by 16 and do not use fractions (only whole numbers).
+
+ 41'029 / 16 is 2'564 with a remainder of 5. Write down 5.
+ 2'564 / 16 is 160 with a remainder of 4. Write the 4 before the 5.
+ 160 / 16 is 10 with no remainder. Prepend 45 with 0.
+ 10 / 16 is below one. End here and prepend 0xA. End up with 0xA045.
+
+Which method to use is up to you. Use whatever works for you.  I use
+them both without being able to tell what method I use in each case,
+it just depends on the number, I think. Fact is, some numbers will
+occur frequently while programming. If the number is close to one I am
+familiar with, then I will use the first method (like 32'770 which is
+into 32'768 + 2 and I just know that it is 0x8000 + 0x2 = 0x8002).
+
+For binary the same approach can be used. The base is 2 and not 16,
+and the number of positions will grow rapidly. Using the second method
+has the advantage that you can see very easily if you should write down
+a zero or a one: if you divide by two the remainder will be zero if it
+is an even number and one if it is an odd number:
+
+ 41029 / 2 = 20514 remainder 1
+ 20514 / 2 = 10257 remainder 0
+ 10257 / 2 =  5128 remainder 1
+  5128 / 2 =  2564 remainder 0
+  2564 / 2 =  1282 remainder 0
+  1282 / 2 =   641 remainder 0
+   641 / 2 =   320 remainder 1
+   320 / 2 =   160 remainder 0
+   160 / 2 =    80 remainder 0
+    80 / 2 =    40 remainder 0
+    40 / 2 =    20 remainder 0
+    20 / 2 =    10 remainder 0
+    10 / 2 =     5 remainder 0
+     5 / 2 =     2 remainder 1
+     2 / 2 =     1 remainder 0
+     1 / 2 below 0 remainder 1
+
+Write down the results from right to left: %1010000001000101
+
+Group by four:
+
+ %1010000001000101
+ %101000000100 0101
+ %10100000 0100 0101
+ %1010 0000 0100 0101
+
+Convert into hexadecimal: 0xA045
+
+Group %1010000001000101 by three and convert into octal:
+
+ %1010000001000101
+ %1010000001000 101
+ %1010000001 000 101
+ %1010000 001 000 101
+ %1010 000 001 000 101
+ %1 010 000 001 000 101
+ %001 010 000 001 000 101
+    1   2   0   1   0   5 --> 0120105
+
+ So: %1010000001000101 = 0120105 = 0xA045 = 41029
+ Or: 1010000001000101(2) = 120105(8) = A045(16) = 41029(10)
+ Or: 1010000001000101(2) = 120105(8) = A045(16) = 41029
+
+
+At first while adding numbers, you'll convert them to their decimal
+form and then back into their original form after doing the addition.
+If you use the other numbering system often, you will see that you'll
+be able to do arithmetics directly in the base that is used.
+In any representation it is the same, add the numbers on the right,
+write down the right-most digit from the result, remember the other
+digits and use them in the next round. Continue with the second digit
+from the right and so on:
+
+    %1010 + %0111 --> 10 + 7 --> 17 --> %00010001
+
+will become
+
+    %1010
+    %0111 +
+     ||||
+     |||+-- add 0 + 1, result is 1, nothing to remember
+     ||+--- add 1 + 1, result is %10, write down 0 and remember 1
+     |+---- add 0 + 1 + 1(remembered), result = 0, remember 1
+     +----- add 1 + 0 + 1(remembered), result = 0, remember 1
+            nothing to add, 1 remembered, result = 1
+ --------
+   %10001 is the result, I like to write it as %00010001
+
+For low values, try to do the calculations yourself, then check them with
+a calculator. The more you do the calculations yourself, the more you'll
+find that you didn't make mistakes. In the end, you'll do calculi in
+other bases as easily as you do them in decimal.
+
+When the numbers get bigger, you'll have to realize that a computer is
+not called a computer just to have a nice name. There are many
+different calculators available, use them. For Unix you could use "bc"
+which is short for Binary Calculator. It calculates not only in
+decimal, but in all bases you'll ever want to use (among them Binary).
+
+For people on Windows:
+Start the calculator (start->programs->accessories->calculator)
+and if necessary click view->scientific. You now have a scientific
+calculator and can compute in binary or hexadecimal.
+
+=head1 AUTHOR
+
+I hope you enjoyed the examples and their descriptions. If you do, help
+other people by pointing them to this document when they are asking
+basic questions. They will not only get their answer, but at the same
+time learn a whole lot more.
+
+Alex van den Bogaerdt  E<lt>alex@vandenbogaerdt.nlE<gt>
diff --git a/program/doc/cdeftutorial.pod b/program/doc/cdeftutorial.pod
new file mode 100644 (file)
index 0000000..1c17046
--- /dev/null
@@ -0,0 +1,889 @@
+=head1 NAME
+
+cdeftutorial - Alex van den Bogaerdt's CDEF tutorial
+
+=head1 DESCRIPTION
+
+Intention of this document: to provide some examples of the commonly
+used parts of RRDtool's CDEF language.
+
+If you think some important feature is not explained properly, and if
+adding it to this document would benefit most users, please do ask me
+to add it.  I will then try to provide an answer in the next release
+of this tutorial.  No feedback equals no changes! Additions to
+this document are also welcome.  -- Alex van den Bogaerdt
+E<lt>alex@vandenbogaerdt.nlE<gt>
+
+=head2 Why this tutorial?
+
+One of the powerful parts of RRDtool is its ability to do all sorts
+of calculations on the data retrieved from its databases. However,
+RRDtool's many options and syntax make it difficult for the average
+user to understand. The manuals are good at explaining what these
+options do; however they do not (and should not) explain in detail
+why they are useful. As with my RRDtool tutorial: if you want a
+simple document in simple language you should read this tutorial.
+If you are happy with the official documentation, you may find this
+document too simple or even boring. If you do choose to read this
+tutorial, I also expect you to have read and fully understand my
+other tutorial.
+
+=head2 More reading
+
+If you have difficulties with the way I try to explain it please read
+Steve Rader's L<rpntutorial>. It may help you understand how this all works.
+
+=head1 What are CDEFs?
+
+When retrieving data from an RRD, you are using a "DEF" to work with
+that data. Think of it as a variable that changes over time (where
+time is the x-axis). The value of this variable is what is found in
+the database at that particular time and you can't do any
+modifications on it. This is what CDEFs are for: they takes values
+from DEFs and perform calculations on them.
+
+=head1 Syntax
+
+   DEF:var_name_1=some.rrd:ds_name:CF
+   CDEF:var_name_2=RPN_expression
+
+You first define "var_name_1" to be data collected from data source
+"ds_name" found in RRD "some.rrd" with consolidation function "CF".
+
+Assume the ifInOctets SNMP counter is saved in mrtg.rrd as the DS "in".
+Then the following DEF defines a variable for the average of that
+data source:
+
+   DEF:inbytes=mrtg.rrd:in:AVERAGE
+
+Say you want to display bits per second (instead of bytes per second
+as stored in the database.)  You have to define a calculation
+(hence "CDEF") on variable "inbytes" and use that variable (inbits)
+instead of the original:
+
+   CDEF:inbits=inbytes,8,*
+
+This tells RRDtool to multiply inbytes by eight to get inbits. I'll
+explain later how this works. In the graphing or printing functions,
+you can now use inbits where you would use inbytes otherwise.
+
+Note that the variable name used in the CDEF (inbits) must not be the
+same as the variable named in the DEF (inbytes)!
+
+=head1 RPN-expressions
+
+RPN is short-hand for Reverse Polish Notation. It works as follows.
+You put the variables or numbers on a stack. You also put operations
+(things-to-do) on the stack and this stack is then processed. The result
+will be placed on the stack. At the end, there should be exactly one
+number left: the outcome of the series of operations. If there is not
+exactly one number left, RRDtool will complain loudly.
+
+Above multiplication by eight will look like:
+
+=over 4
+
+=item 1.
+
+Start with an empty stack
+
+=item 2.
+
+Put the content of variable inbytes on the stack
+
+=item 3.
+
+Put the number eight on the stack
+
+=item 4.
+
+Put the operation multiply on the stack
+
+=item 5.
+
+Process the stack
+
+=item 6.
+
+Retrieve the value from the stack and put it in variable inbits
+
+=back
+
+We will now do an example with real numbers. Suppose the variable
+inbytes would have value 10, the stack would be:
+
+=over 4
+
+=item 1.
+
+||
+
+=item 2.
+
+|10|
+
+=item 3.
+
+|10|8|
+
+=item 4.
+
+|10|8|*|
+
+=item 5.
+
+|80|
+
+=item 6.
+
+||
+
+=back
+
+Processing the stack (step 5) will retrieve one value from the stack
+(from the right at step 4). This is the operation multiply and this
+takes two values off the stack as input. The result is put back on the
+stack (the value 80 in this case). For multiplication the order doesn't
+matter, but for other operations like subtraction and division it does.
+Generally speaking you have the following order:
+
+   y = A - B  -->  y=minus(A,B)  -->  CDEF:y=A,B,-
+
+This is not very intuitive (at least most people don't think so). For
+the function f(A,B) you reverse the position of "f", but you do not
+reverse the order of the variables.
+
+=head1 Converting your wishes to RPN
+
+First, get a clear picture of what you want to do. Break down the problem
+in smaller portions until they cannot be split anymore. Then it is rather
+simple to convert your ideas into RPN.
+
+Suppose you have several RRDs and would like to add up some counters in
+them. These could be, for instance, the counters for every WAN link you
+are monitoring.
+
+You have:
+
+   router1.rrd with link1in link2in
+   router2.rrd with link1in link2in
+   router3.rrd with link1in link2in
+
+Suppose you would like to add up all these counters, except for link2in
+inside router2.rrd. You need to do:
+
+(in this example, "router1.rrd:link1in" means the DS link1in inside the
+RRD router1.rrd)
+
+   router1.rrd:link1in
+   router1.rrd:link2in
+   router2.rrd:link1in
+   router3.rrd:link1in
+   router3.rrd:link2in
+   --------------------   +
+   (outcome of the sum)
+
+As a mathematical function, this could be written:
+
+C<add(router1.rrd:link1in , router1.rrd:link2in , router2.rrd:link1in , router3.rrd:link1in , router3.rrd:link2.in)>
+
+With RRDtool and RPN, first, define the inputs:
+
+   DEF:a=router1.rrd:link1in:AVERAGE
+   DEF:b=router1.rrd:link2in:AVERAGE
+   DEF:c=router2.rrd:link1in:AVERAGE
+   DEF:d=router3.rrd:link1in:AVERAGE
+   DEF:e=router3.rrd:link2in:AVERAGE
+
+Now, the mathematical function becomes: C<add(a,b,c,d,e)>
+
+In RPN, there's no operator that sums more than two values so you need
+to do several additions. You add a and b, add c to the result, add d
+to the result and add e to the result.
+
+   push a:         a     stack contains the value of a
+   push b and add: b,+   stack contains the result of a+b
+   push c and add: c,+   stack contains the result of a+b+c
+   push d and add: d,+   stack contains the result of a+b+c+d
+   push e and add: e,+   stack contains the result of a+b+c+d+e
+
+What was calculated here would be written down as:
+
+   ( ( ( (a+b) + c) + d) + e) >
+
+This is in RPN:  C<CDEF:result=a,b,+,c,+,d,+,e,+>
+
+This is correct but it can be made more clear to humans. It does
+not matter if you add a to b and then add c to the result or first
+add b to c and then add a to the result. This makes it possible to
+rewrite the RPN into C<CDEF:result=a,b,c,d,e,+,+,+,+> which is
+evaluated differently:
+
+   push value of variable a on the stack: a
+   push value of variable b on the stack: a b
+   push value of variable c on the stack: a b c
+   push value of variable d on the stack: a b c d
+   push value of variable e on the stack: a b c d e
+   push operator + on the stack:          a b c d e +
+   and process it:                        a b c P   (where P == d+e)
+   push operator + on the stack:          a b c P +
+   and process it:                        a b Q     (where Q == c+P)
+   push operator + on the stack:          a b Q +
+   and process it:                        a R       (where R == b+Q)
+   push operator + on the stack:          a R +
+   and process it:                        S         (where S == a+R)
+
+As you can see the RPN expression C<a,b,c,d,e,+,+,+,+,+> will evaluate in
+C<((((d+e)+c)+b)+a)> and it has the same outcome as C<a,b,+,c,+,d,+,e,+>.
+This is called the commutative law of addition,
+but you may forget this right away, as long as you remember what it
+means.
+
+Now look at an expression that contains a multiplication:
+
+First in normal math: C<let result = a+b*c>. In this case you can't
+choose the order yourself, you have to start with the multiplication
+and then add a to it. You may alter the position of b and c, you must
+not alter the position of a and b.
+
+You have to take this in consideration when converting this expression
+into RPN. Read it as: "Add the outcome of b*c to a" and then it is
+easy to write the RPN expression: C<result=a,b,c,*,+>
+Another expression that would return the same: C<result=b,c,*,a,+>
+
+In normal math, you may encounter something like "a*(b+c)" and this
+can also be converted into RPN. The parenthesis just tell you to first
+add b and c, and then multiply a with the result. Again, now it is
+easy to write it in RPN: C<result=a,b,c,+,*>. Note that this is very
+similar to one of the expressions in the previous paragraph, only the
+multiplication and the addition changed places.
+
+When you have problems with RPN or when RRDtool is complaining, it's
+usually a good thing to write down the stack on a piece of paper
+and see what happens. Have the manual ready and pretend to be RRDtool.
+Just do all the math by hand to see what happens, I'm sure this will
+solve most, if not all, problems you encounter.
+
+=head1 Some special numbers
+
+=head2 The unknown value
+
+Sometimes collecting your data will fail. This can be very common,
+especially when querying over busy links. RRDtool can be configured
+to allow for one (or even more) unknown value(s) and calculate the missing
+update. You can, for instance, query your device every minute. This is
+creating one so called PDP or primary data point per minute. If you
+defined your RRD to contain an RRA that stores 5-minute values, you need
+five of those PDPs to create one CDP (consolidated data point).
+These PDPs can become unknown in two cases:
+
+=over 4
+
+=item 1.
+
+The updates are too far apart. This is tuned using the "heartbeat" setting.
+
+=item 2.
+
+The update was set to unknown on purpose by inserting no value (using the
+template option) or by using "U" as the value to insert.
+
+=back
+
+When a CDP is calculated, another mechanism determines if this CDP is valid
+or not. If there are too many PDPs unknown, the CDP is unknown as well.
+This is determined by the xff factor. Please note that one unknown counter
+update can result in two unknown PDPs! If you only allow for one unknown
+PDP per CDP, this makes the CDP go unknown!
+
+Suppose the counter increments with one per second and you retrieve it
+every minute:
+
+   counter value    resulting rate
+   10'000
+   10'060            1; (10'060-10'000)/60 == 1
+   10'120            1; (10'120-10'060)/60 == 1
+   unknown           unknown; you don't know the last value
+   10'240            unknown; you don't know the previous value
+   10'300            1; (10'300-10'240)/60 == 1
+
+If the CDP was to be calculated from the last five updates, it would get
+two unknown PDPs and three known PDPs. If xff would have been set to 0.5
+which by the way is a commonly used factor, the CDP would have a known
+value of 1. If xff would have been set to 0.2 then the resulting CDP
+would be unknown.
+
+You have to decide the proper values for heartbeat, number of PDPs per
+CDP and the xff factor. As you can see from the previous text they define
+the behavior of your RRA.
+
+=head2 Working with unknown data in your database
+
+As you have read in the previous chapter, entries in an RRA can be
+set to the unknown value. If you do calculations with this type of
+value, the result has to be unknown too. This means that an expression
+such as C<result=a,b,+> will be unknown if either a or b is unknown.
+It would be wrong to just ignore the unknown value and return the value
+of the other parameter. By doing so, you would assume "unknown" means "zero"
+and this is not true.
+
+There has been a case where somebody was collecting data for over a year.
+A new piece of equipment was installed, a new RRD was created and the
+scripts were changed to add a counter from the old database and a counter
+from the new database. The result was disappointing, a large part of
+the statistics seemed to have vanished mysteriously ...
+They of course didn't, values from the old database (known values) were
+added to values from the new database (unknown values) and the result was
+unknown.
+
+In this case, it is fairly reasonable to use a CDEF that alters unknown
+data into zero. The counters of the device were unknown (after all, it
+wasn't installed yet!) but you know that the data rate through the device
+had to be zero (because of the same reason: it was not installed).
+
+There are some examples below that make this change.
+
+=head2 Infinity
+
+Infinite data is another form of a special number. It cannot be
+graphed because by definition you would never reach the infinite
+value. You can think of positive and negative infinity depending on
+the position relative to zero.
+
+RRDtool is capable of representing (-not- graphing!) infinity by stopping
+at its current maximum (for positive infinity) or minimum (for negative
+infinity) without knowing this maximum (minimum).
+
+Infinity in RRDtool is mostly used to draw an AREA without knowing its
+vertical dimensions. You can think of it as drawing an AREA with an
+infinite height and displaying only the part that is visible in the
+current graph. This is probably a good way to approximate infinity
+and it sure allows for some neat tricks. See below for examples.
+
+=head2 Working with unknown data and infinity
+
+Sometimes you would like to discard unknown data and pretend it is zero
+(or any other value for that matter) and sometimes you would like to
+pretend that known data is unknown (to discard known-to-be-wrong data).
+This is why CDEFs have support for unknown data. There are also examples
+available that show unknown data by using infinity.
+
+=head1 Some examples
+
+=head2 Example: using a recently created RRD
+
+You are keeping statistics on your router for over a year now. Recently
+you installed an extra router and you would like to show the combined
+throughput for these two devices.
+
+If you just add up the counters from router.rrd and router2.rrd, you
+will add known data (from router.rrd) to unknown data (from router2.rrd) for
+the bigger part of your stats. You could solve this in a few ways:
+
+=over 4
+
+=item *
+
+While creating the new database, fill it with zeros from the start to now.
+You have to make the database start at or before the least recent time in
+the other database.
+
+=item *
+
+Alternatively, you could use CDEF and alter unknown data to zero.
+
+=back
+
+Both methods have their pros and cons. The first method is troublesome and
+if you want to do that you have to figure it out yourself. It is not
+possible to create a database filled with zeros, you have to put them in
+manually. Implementing the second method is described next:
+
+What we want is: "if the value is unknown, replace it with zero". This
+could be written in pseudo-code as:  if (value is unknown) then (zero)
+else (value). When reading the L<rrdgraph> manual you notice the "UN"
+function that returns zero or one. You also notice the "IF" function
+that takes zero or one as input.
+
+First look at the "IF" function. It takes three values from the stack,
+the first value is the decision point, the second value is returned to
+the stack if the evaluation is "true" and if not, the third value is
+returned to the stack. We want the "UN" function to decide what happens
+so we combine those two functions in one CDEF.
+
+Lets write down the two possible paths for the "IF" function:
+
+   if true  return a
+   if false return b
+
+In RPN:  C<result=x,a,b,IF> where "x" is either true or false.
+
+Now we have to fill in "x", this should be the "(value is unknown)" part
+and this is in RPN:  C<result=value,UN>
+
+We now combine them: C<result=value,UN,a,b,IF> and when we fill in the
+appropriate things for "a" and "b" we're finished:
+
+C<CDEF:result=value,UN,0,value,IF>
+
+You may want to read Steve Rader's RPN guide if you have difficulties
+with the way I explained this last example.
+
+If you want to check this RPN expression, just mimic RRDtool behavior:
+
+   For any known value, the expression evaluates as follows:
+   CDEF:result=value,UN,0,value,IF  (value,UN) is not true so it becomes 0
+   CDEF:result=0,0,value,IF         "IF" will return the 3rd value
+   CDEF:result=value                The known value is returned
+
+   For the unknown value, this happens:
+   CDEF:result=value,UN,0,value,IF  (value,UN) is true so it becomes 1
+   CDEF:result=1,0,value,IF         "IF" sees 1 and returns the 2nd value
+   CDEF:result=0                    Zero is returned
+
+Of course, if you would like to see another value instead of zero, you
+can use that other value.
+
+Eventually, when all unknown data is removed from the RRD, you may want
+to remove this rule so that unknown data is properly displayed.
+
+=head2 Example: better handling of unknown data, by using time
+
+The above example has one drawback. If you do log unknown data in
+your database after installing your new equipment, it will also be
+translated into zero and therefore you won't see that there was a
+problem. This is not good and what you really want to do is:
+
+=over 4
+
+=item *
+
+If there is unknown data, look at the time that this sample was taken.
+
+=item *
+
+If the unknown value is before time xxx, make it zero.
+
+=item *
+
+If it is after time xxx, leave it as unknown data.
+
+=back
+
+This is doable: you can compare the time that the sample was taken
+to some known time. Assuming you started to monitor your device on
+Friday September 17, 1999, 00:35:57 MET DST. Translate this time in seconds
+since 1970-01-01 and it becomes 937'521'357. If you process unknown values
+that were received after this time, you want to leave them unknown and
+if they were "received" before this time, you want to translate them
+into zero (so you can effectively ignore them while adding them to your
+other routers counters).
+
+Translating Friday September 17, 1999, 00:35:57 MET DST into 937'521'357 can
+be done by, for instance, using gnu date:
+
+   date -d "19990917 00:35:57" +%s
+
+You could also dump the database and see where the data starts to be
+known. There are several other ways of doing this, just pick one.
+
+Now we have to create the magic that allows us to process unknown
+values different depending on the time that the sample was taken.
+This is a three step process:
+
+=over 4
+
+=item 1.
+
+If the timestamp of the value is after 937'521'357, leave it as is.
+
+=item 2.
+
+If the value is a known value, leave it as is.
+
+=item 3.
+
+Change the unknown value into zero.
+
+=back
+
+Lets look at part one:
+
+    if (true) return the original value
+
+We rewrite this:
+
+    if (true) return "a"
+    if (false) return "b"
+
+We need to calculate true or false from step 1. There is a function
+available that returns the timestamp for the current sample. It is
+called, how surprisingly, "TIME". This time has to be compared to
+a constant number, we need "GT". The output of "GT" is true or false
+and this is good input to "IF". We want "if (time > 937521357) then
+(return a) else (return b)".
+
+This process was already described thoroughly in the previous chapter
+so lets do it quick:
+
+   if (x) then a else b
+      where x represents "time>937521357"
+      where a represents the original value
+      where b represents the outcome of the previous example
+
+   time>937521357       --> TIME,937521357,GT
+
+   if (x) then a else b --> x,a,b,IF
+   substitute x         --> TIME,937521357,GT,a,b,IF
+   substitute a         --> TIME,937521357,GT,value,b,IF
+   substitute b         --> TIME,937521357,GT,value,value,UN,0,value,IF,IF
+
+We end up with:
+C<CDEF:result=TIME,937521357,GT,value,value,UN,0,value,IF,IF>
+
+This looks very complex, however, as you can see, it was not too hard to
+come up with.
+
+=head2 Example: Pretending weird data isn't there
+
+Suppose you have a problem that shows up as huge spikes in your graph.
+You know this happens and why, so you decide to work around the problem.
+Perhaps you're using your network to do a backup at night and by doing
+so you get almost 10mb/s while the rest of your network activity does
+not produce numbers higher than 100kb/s.
+
+There are two options:
+
+=over 4
+
+=item 1.
+
+If the number exceeds 100kb/s it is wrong and you want it masked out
+by changing it into unknown.
+
+=item 2.
+
+You don't want the graph to show more than 100kb/s.
+
+=back
+
+Pseudo code: if (number > 100) then unknown else number
+or
+Pseudo code: if (number > 100) then 100 else number.
+
+The second "problem" may also be solved by using the rigid option of
+RRDtool graph, however this has not the same result. In this example
+you can end up with a graph that does autoscaling. Also, if you use
+the numbers to display maxima they will be set to 100kb/s.
+
+We use "IF" and "GT" again. "if (x) then (y) else (z)" is written
+down as "CDEF:result=x,y,z,IF"; now fill in x, y and z.
+For x you fill in "number greater than 100kb/s" becoming
+"number,100000,GT" (kilo is 1'000 and b/s is what we measure!).
+The "z" part is "number" in both cases and the "y" part is either
+"UNKN" for unknown or "100000" for 100kb/s.
+
+The two CDEF expressions would be:
+
+    CDEF:result=number,100000,GT,UNKN,number,IF
+    CDEF:result=number,100000,GT,100000,number,IF
+
+=head2 Example: working on a certain time span
+
+If you want a graph that spans a few weeks, but would only want to
+see some routers' data for one week, you need to "hide" the rest of
+the time frame. Don't ask me when this would be useful, it's just
+here for the example :)
+
+We need to compare the time stamp to a begin date and an end date.
+Comparing isn't difficult:
+
+       TIME,begintime,GE
+       TIME,endtime,LE
+
+These two parts of the CDEF produce either 0 for false or 1 for true.
+We can now check if they are both 0 (or 1) using a few IF statements
+but, as Wataru Satoh pointed out, we can use the "*" or "+" functions
+as logical AND and logical OR.
+
+For "*", the result will be zero (false) if either one of the two
+operators is zero.  For "+", the result will only be false (0) when
+two false (0) operators will be added.  Warning: *any* number not
+equal to 0 will be considered "true". This means that, for instance,
+"-1,1,+" (which should be "true or true") will become FALSE ...
+In other words, use "+" only if you know for sure that you have positive
+numbers (or zero) only.
+
+Let's compile the complete CDEF:
+
+       DEF:ds0=router1.rrd:AVERAGE
+       CDEF:ds0modified=TIME,begintime,GT,TIME,endtime,LE,*,ds0,UNKN,IF
+
+This will return the value of ds0 if both comparisons return true. You
+could also do it the other way around:
+
+       DEF:ds0=router1.rrd:AVERAGE
+       CDEF:ds0modified=TIME,begintime,LT,TIME,endtime,GT,+,UNKN,ds0,IF
+
+This will return an UNKNOWN if either comparison returns true.
+
+=head2 Example: You suspect to have problems and want to see unknown data.
+
+Suppose you add up the number of active users on several terminal servers.
+If one of them doesn't give an answer (or an incorrect one) you get "NaN"
+in the database ("Not a Number") and NaN is evaluated as Unknown.
+
+In this case, you would like to be alerted to it and the sum of the
+remaining values is of no value to you.
+
+It would be something like:
+
+    DEF:users1=location1.rrd:onlineTS1:LAST
+    DEF:users2=location1.rrd:onlineTS2:LAST
+    DEF:users3=location2.rrd:onlineTS1:LAST
+    DEF:users4=location2.rrd:onlineTS2:LAST
+    CDEF:allusers=users1,users2,users3,users4,+,+,+
+
+If you now plot allusers, unknown data in one of users1..users4 will
+show up as a gap in your graph. You want to modify this to show a
+bright red line, not a gap.
+
+Define an extra CDEF that is unknown if all is okay and is infinite if
+there is an unknown value:
+
+    CDEF:wrongdata=allusers,UN,INF,UNKN,IF
+
+"allusers,UN" will evaluate to either true or false, it is the (x) part
+of the "IF" function and it checks if allusers is unknown.
+The (y) part of the "IF" function is set to "INF" (which means infinity)
+and the (z) part of the function returns "UNKN".
+
+The logic is: if (allusers == unknown) then return INF else return UNKN.
+
+You can now use AREA to display this "wrongdata" in bright red. If it
+is unknown (because allusers is known) then the red AREA won't show up.
+If the value is INF (because allusers is unknown) then the red AREA will
+be filled in on the graph at that particular time.
+
+   AREA:allusers#0000FF:combined user count
+   AREA:wrongdata#FF0000:unknown data
+
+=head2 Same example useful with STACKed data:
+
+If you use stack in the previous example (as I would do) then you don't
+add up the values. Therefore, there is no relationship between the
+four values and you don't get a single value to test.
+Suppose users3 would be unknown at one point in time: users1 is plotted,
+users2 is stacked on top of users1, users3 is unknown and therefore
+nothing happens, users4 is stacked on top of users2.
+Add the extra CDEFs anyway and use them to overlay the "normal" graph:
+
+   DEF:users1=location1.rrd:onlineTS1:LAST
+   DEF:users2=location1.rrd:onlineTS2:LAST
+   DEF:users3=location2.rrd:onlineTS1:LAST
+   DEF:users4=location2.rrd:onlineTS2:LAST
+   CDEF:allusers=users1,users2,users3,users4,+,+,+
+   CDEF:wrongdata=allusers,UN,INF,UNKN,IF
+   AREA:users1#0000FF:users at ts1
+   STACK:users2#00FF00:users at ts2
+   STACK:users3#00FFFF:users at ts3
+   STACK:users4#FFFF00:users at ts4
+   AREA:wrongdata#FF0000:unknown data
+
+If there is unknown data in one of users1..users4, the "wrongdata" AREA
+will be drawn and because it starts at the X-axis and has infinite height
+it will effectively overwrite the STACKed parts.
+
+You could combine the two CDEF lines into one (we don't use "allusers")
+if you like.  But there are good reasons for writing two CDEFS:
+
+=over 4
+
+=item *
+
+It improves the readability of the script.
+
+=item *
+
+It can be used inside GPRINT to display the total number of users.
+
+=back
+
+If you choose to combine them, you can substitute the "allusers" in the
+second CDEF with the part after the equal sign from the first line:
+
+   CDEF:wrongdata=users1,users2,users3,users4,+,+,+,UN,INF,UNKN,IF
+
+If you do so, you won't be able to use these next GPRINTs:
+
+   COMMENT:"Total number of users seen"
+   GPRINT:allusers:MAX:"Maximum: %6.0lf"
+   GPRINT:allusers:MIN:"Minimum: %6.0lf"
+   GPRINT:allusers:AVERAGE:"Average: %6.0lf"
+   GPRINT:allusers:LAST:"Current: %6.0lf\n"
+
+=head1 The examples from the RRD graph manual page
+
+=head2 Degrees Celsius vs. Degrees Fahrenheit
+
+To convert Celsius into Fahrenheit use the formula
+F=9/5*C+32
+
+   rrdtool graph demo.png --title="Demo Graph" \
+      DEF:cel=demo.rrd:exhaust:AVERAGE \
+      CDEF:far=9,5,/,cel,*,32,+ \
+      LINE2:cel#00a000:"D. Celsius" \
+      LINE2:far#ff0000:"D. Fahrenheit\c"
+
+This example gets the DS called "exhaust" from database "demo.rrd"
+and puts the values in variable "cel". The CDEF used is evaluated
+as follows:
+
+   CDEF:far=9,5,/,cel,*,32,+
+   1. push 9, push 5
+   2. push function "divide" and process it
+      the stack now contains 9/5
+   3. push variable "cel"
+   4. push function "multiply" and process it
+      the stack now contains 9/5*cel
+   5. push 32
+   6. push function "plus" and process it
+      the stack contains now the temperature in Fahrenheit
+
+=head2 Changing unknown into zero
+
+   rrdtool graph demo.png --title="Demo Graph" \
+      DEF:idat1=interface1.rrd:ds0:AVERAGE \
+      DEF:idat2=interface2.rrd:ds0:AVERAGE \
+      DEF:odat1=interface1.rrd:ds1:AVERAGE \
+      DEF:odat2=interface2.rrd:ds1:AVERAGE \
+      CDEF:agginput=idat1,UN,0,idat1,IF,idat2,UN,0,idat2,IF,+,8,* \
+      CDEF:aggoutput=odat1,UN,0,odat1,IF,odat2,UN,0,odat2,IF,+,8,* \
+      AREA:agginput#00cc00:Input Aggregate \
+      LINE1:aggoutput#0000FF:Output Aggregate
+
+These two CDEFs are built from several functions. It helps to split
+them when viewing what they do. Starting with the first CDEF we would
+get:
+
+ idat1,UN --> a
+ 0        --> b
+ idat1    --> c
+ if (a) then (b) else (c)
+
+The result is therefore "0" if it is true that "idat1" equals "UN".
+If not, the original value of "idat1" is put back on the stack.
+Lets call this answer "d". The process is repeated for the next
+five items on the stack, it is done the same and will return answer
+"h". The resulting stack is therefore "d,h".
+The expression has been simplified to "d,h,+,8,*" and it will now be
+easy to see that we add "d" and "h", and multiply the result with eight.
+
+The end result is that we have added "idat1" and "idat2" and in the
+process we effectively ignored unknown values. The result is multiplied
+by eight, most likely to convert bytes/s to bits/s.
+
+=head2 Infinity demo
+
+   rrdtool graph example.png --title="INF demo" \
+      DEF:val1=some.rrd:ds0:AVERAGE \
+      DEF:val2=some.rrd:ds1:AVERAGE \
+      DEF:val3=some.rrd:ds2:AVERAGE \
+      DEF:val4=other.rrd:ds0:AVERAGE \
+      CDEF:background=val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF \
+      CDEF:wipeout=val1,val2,val3,val4,+,+,+,UN,INF,UNKN,IF \
+      AREA:background#F0F0F0 \
+      AREA:val1#0000FF:Value1 \
+      STACK:val2#00C000:Value2 \
+      STACK:val3#FFFF00:Value3 \
+      STACK:val4#FFC000:Value4 \
+      AREA:whipeout#FF0000:Unknown
+
+This demo demonstrates two ways to use infinity. It is a bit tricky
+to see what happens in the "background" CDEF.
+
+   "val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF"
+
+This RPN takes the value of "val4" as input and then immediately
+removes it from the stack using "POP". The stack is now empty but
+as a side effect we now know the time that this sample was taken.
+This time is put on the stack by the "TIME" function.
+
+"TIME,7200,%" takes the modulo of time and 7'200 (which is two hours).
+The resulting value on the stack will be a number in the range from
+0 to 7199.
+
+For people who don't know the modulo function: it is the remainder
+after an integer division. If you divide 16 by 3, the answer would
+be 5 and the remainder would be 1. So, "16,3,%" returns 1.
+
+We have the result of "TIME,7200,%" on the stack, lets call this
+"a". The start of the RPN has become "a,3600,LE" and this checks
+if "a" is less or equal than "3600". It is true half of the time.
+We now have to process the rest of the RPN and this is only a simple
+"IF" function that returns either "INF" or "UNKN" depending on the
+time. This is returned to variable "background".
+
+The second CDEF has been discussed earlier in this document so we
+won't do that here.
+
+Now you can draw the different layers. Start with the background
+that is either unknown (nothing to see) or infinite (the whole
+positive part of the graph gets filled).
+
+Next you draw the data on top of this background, it will overlay
+the background. Suppose one of val1..val4 would be unknown, in that
+case you end up with only three bars stacked on top of each other.
+You don't want to see this because the data is only valid when all
+four variables are valid. This is why you use the second CDEF, it
+will overlay the data with an AREA so the data cannot be seen anymore.
+
+If your data can also have negative values you also need to overwrite
+the other half of your graph. This can be done in a relatively simple
+way: what you need is the "wipeout" variable and place a negative
+sign before it:  "CDEF:wipeout2=wipeout,-1,*"
+
+=head2 Filtering data
+
+You may do some complex data filtering:
+
+  MEDIAN FILTER: filters shot noise
+
+    DEF:var=database.rrd:traffic:AVERAGE
+    CDEF:prev1=PREV(var)
+    CDEF:prev2=PREV(prev1)
+    CDEF:prev3=PREV(prev2)
+    CDEF:median=prev1,prev2,prev3,+,+,3,/
+    LINE3:median#000077:filtered
+    LINE1:prev2#007700:'raw data'
+
+
+  DERIVATE:
+
+    DEF:var=database.rrd:traffic:AVERAGE
+    CDEF:prev1=PREV(var)
+    CDEF:time=TIME
+    CDEF:prevtime=PREV(time)
+    CDEF:derivate=var,prev1,-,time,prevtime,-,/
+    LINE3:derivate#000077:derivate
+    LINE1:var#007700:'raw data'
+
+
+=head1 Out of ideas for now
+
+This document was created from questions asked by either myself or by
+other people on the RRDtool mailing list. Please let me know if you
+find errors in it or if you have trouble understanding it. If you
+think there should be an addition, mail me:
+E<lt>alex@vandenbogaerdt.nlE<gt>
+
+Remember: B<No feedback equals no changes!>
+
+=head1 SEE ALSO
+
+The RRDtool manpages
+
+=head1 AUTHOR
+
+Alex van den Bogaerdt
+E<lt>alex@vandenbogaerdt.nlE<gt>
diff --git a/program/doc/librrd.pod b/program/doc/librrd.pod
new file mode 100644 (file)
index 0000000..dfe6f29
--- /dev/null
@@ -0,0 +1,136 @@
+=pod
+
+=head1 NAME
+
+librrd - RRD library functions
+
+=head1 DESCRIPTION
+
+B<librrd> contains most of the functionality in B<RRDTool>.  The command
+line utilities and language bindings are often just wrappers around the
+code contained in B<librrd>.
+
+This manual page documents the B<librrd> API.
+
+B<NOTE:> This document is a work in progress, and should be considered
+incomplete as long as this warning persists.  For more information about
+the B<librrd> functions, always consult the source code.
+
+=head1 CORE FUNCTIONS
+
+=over 4
+
+=item B<rrd_dump_cb_r(char *filename, int opt_header, rrd_output_callback_t cb, void *user)>
+
+In some situations it is necessary to get the output of C<rrd_dump> without
+writing it to a file or the standard output. In such cases an application
+can ask B<rrd_dump_cb_r> to call an user-defined function each time there
+is output to be stored somewhere. This can be used, to e.g. directly feed
+an XML parser with the dumped output or transfer the resulting string
+in memory.
+
+The arguments for B<rrd_dump_cb_r> are the same as for B<rrd_dump_opt_r>
+except that the output filename parameter is replaced by the user-defined
+callback function and an additional parameter for the callback function
+that is passed untouched, i.e. to store information about the callback state
+needed for the user-defined callback to function properly.
+
+Recent versions of B<rrd_dump_opt_r> internally use this callback mechanism
+to write their output to the file provided by the user.
+
+    size_t rrd_dump_opt_cb_fileout(
+        const void *data,
+        size_t len,
+        void *user)
+    {
+        return fwrite(data, 1, len, (FILE *)user);
+    }
+
+The associated call for B<rrd_dump_cb_r> looks like
+
+    res = rrd_dump_cb_r(filename, opt_header,
+        rrd_dump_opt_cb_fileout, (void *)out_file);
+
+where the last parameter specifies the file handle B<rrd_dump_opt_cb_fileout>
+should write to. There's no specific condition for the callback to detect
+when it is called for the first time, nor for the last time. If you require
+this for initialization and cleanup you should do those tasks before and
+after calling B<rrd_dump_cr_r> respectively.
+
+=back
+
+=head1 UTILITY FUNCTIONS
+
+=over 4
+
+=item B<rrd_random()>
+
+Generates random numbers just like random().  This further ensures that
+the random number generator is seeded exactly once per process.
+
+=item B<rrd_add_ptr(void ***dest, size_t *dest_size, void *src)>
+
+Dynamically resize the array pointed to by C<dest>.  C<dest_size> is a
+pointer to the current size of C<dest>.  Upon successful realloc(), the
+C<dest_size> is incremented by 1 and the C<src> pointer is stored at the
+end of the new C<dest>.  Returns 1 on success, 0 on failure.
+
+    type **arr = NULL;
+    type *elem = "whatever";
+    size_t arr_size = 0;
+    if (!rrd_add_ptr(&arr, &arr_size, elem))
+        handle_failure();
+
+=item B<rrd_add_strdup(char ***dest, size_t *dest_size, char *src)>
+
+Like C<rrd_add_ptr>, except adds a C<strdup> of the source string.
+
+    char **arr = NULL;
+    size_t arr_size = NULL;
+    char *str  = "example text";
+    if (!rrd_add_strdup(&arr, &arr_size, str))
+        handle_failure();
+
+=item B<rrd_free_ptrs(void ***src, size_t *cnt)>
+
+Free an array of pointers allocated by C<rrd_add_ptr> or
+C<rrd_add_strdup>.  Also frees the array pointer itself.  On return, the
+source pointer will be NULL and the count will be zero.
+
+    /* created as above */
+    rrd_free_ptrs(&arr, &arr_size);
+    /* here, arr == NULL && arr_size == 0 */
+
+=item B<rrd_mkdir_p(const char *pathname, mode_t mode)>
+
+Create the directory named C<pathname> including all of its parent
+directories (similar to C<mkdir -p> on the command line - see L<mkdir(1)> for
+more information). The argument C<mode> specifies the permissions to use. It
+is modified by the process's C<umask>. See L<mkdir(2)> for more details.
+
+The function returns 0 on success, a negative value else. In case of an error,
+C<errno> is set accordingly. Aside from the errors documented in L<mkdir(2)>,
+the function may fail with the following errors:
+
+=over 4
+
+=item B<EINVAL>
+
+C<pathname> is C<NULL> or the empty string.
+
+=item B<ENOMEM>
+
+Insufficient memory was available.
+
+=item B<any error returned by L<stat(2)>>
+
+=back
+
+In contrast to L<mkdir(2)>, the function does B<not> fail if C<pathname>
+already exists and is a directory.
+
+=back
+
+=head1 AUTHOR
+
+RRD Contributors <rrd-developers@lists.oetiker.ch>
diff --git a/program/doc/name.inc b/program/doc/name.inc
new file mode 100644 (file)
index 0000000..dffcae4
--- /dev/null
@@ -0,0 +1,11 @@
+=head1 NAME
+
+=cut
+
+WARNING: DO NOT EDIT THE POD FILES. THEY ARE AUTO-GENERATED
+
+=pod
+
+rrdtool graph - Round Robin Database tool grapher functions
+
+Documentation for version 1.2.0
diff --git a/program/doc/rpntutorial.pod b/program/doc/rpntutorial.pod
new file mode 100644 (file)
index 0000000..0ab3131
--- /dev/null
@@ -0,0 +1,197 @@
+=head1 NAME
+
+rpntutorial - Reading RRDtool RPN Expressions by Steve Rader
+
+=head1 DESCRIPTION
+
+This tutorial should help you get to grips with RRDtool RPN expressions
+as seen in CDEF arguments of RRDtool graph.
+
+=head1 Reading Comparison Operators
+
+The LT, LE, GT, GE and EQ RPN logic operators are not as tricky as
+they appear.  These operators act on the two values on the stack
+preceding them (to the left).  Read these two values on the stack
+from left to right inserting the operator in the middle.  If the
+resulting statement is true, then replace the three values from the
+stack with "1".  If the statement if false, replace the three values
+with "0".
+
+For example, think about "2,1,GT".  This RPN expression could be
+read as "is two greater than one?"  The answer to that question is
+"true".  So the three values should be replaced with "1".  Thus the
+RPN expression 2,1,GT evaluates to 1.
+
+Now consider "2,1,LE".  This RPN expression could be read as "is
+two less than or equal to one?".   The natural response is "no"
+and thus the RPN expression 2,1,LE evaluates to 0.
+
+=head1 Reading the IF Operator
+
+The IF RPN logic operator can be straightforward also.  The key
+to reading IF operators is to understand that the condition part
+of the traditional "if X than Y else Z" notation has *already*
+been evaluated.  So the IF operator acts on only one value on the
+stack: the third value to the left of the IF value.  The second
+value to the left of the IF corresponds to the true ("Y") branch.
+And the first value to the left of the IF corresponds to the false
+("Z") branch.  Read the RPN expression "X,Y,Z,IF" from left to
+right like so: "if X then Y else Z".
+
+For example, consider "1,10,100,IF".  It looks bizarre to me.
+But when I read "if 1 then 10 else 100" it's crystal clear: 1 is true
+so the answer is 10.  Note that only zero is false; all other values
+are true.  "2,20,200,IF" ("if 2 then 20 else 200") evaluates to 20.
+And "0,1,2,IF" ("if 0 then 1 else 2) evaluates to 2.
+
+
+Notice that none of the above examples really simulate the whole
+"if X then Y else Z" statement.  This is because computer programmers
+read this statement as "if Some Condition then Y else Z".  So it's
+important to be able to read IF operators along with the LT, LE,
+GT, GE and EQ operators.
+
+=head1 Some Examples
+
+While compound expressions can look overly complex, they can be
+considered elegantly simple.  To quickly comprehend RPN expressions,
+you must know the algorithm for evaluating RPN expressions:
+iterate searches from the left to the right looking for an operator.
+When it's found, apply that operator by popping the operator and some
+number of values (and by definition, not operators) off the stack.
+
+For example, the stack "1,2,3,+,+" gets "2,3,+" evaluated (as "2+3")
+during the first iteration and is replaced by 5.  This results in
+the stack "1,5,+".  Finally, "1,5,+" is evaluated resulting in the
+answer 6.  For convenience, it's useful to write this set of
+operations as:
+
+ 1) 1,2,3,+,+    eval is 2,3,+ = 5    result is 1,5,+
+ 2) 1,5,+        eval is 1,5,+ = 6    result is 6
+ 3) 6
+
+Let's use that notation to conveniently solve some complex RPN expressions
+with multiple logic operators:
+
+ 1) 20,10,GT,10,20,IF  eval is 20,10,GT = 1     result is 1,10,20,IF
+
+read the eval as pop "20 is greater than 10" so push 1
+
+ 2) 1,10,20,IF         eval is 1,10,20,IF = 10  result is 10
+
+read pop "if 1 then 10 else 20" so push 10.  Only 10 is left so
+10 is the answer.
+
+Let's read a complex RPN expression that also has the traditional
+multiplication operator:
+
+ 1) 128,8,*,7000,GT,7000,128,8,*,IF  eval 128,8,*       result is 1024
+ 2) 1024   ,7000,GT,7000,128,8,*,IF  eval 1024,7000,GT  result is 0
+ 3) 0,              7000,128,8,*,IF  eval 128,8,*       result is 1024
+ 4) 0,              7000,1024,   IF                     result is 1024
+
+Now let's go back to the first example of multiple logic operators,
+but replace the value 20 with the variable "input":
+
+ 1) input,10,GT,10,input,IF  eval is input,10,GT  ( lets call this A )
+
+Read eval as "if input > 10 then true" and replace "input,10,GT"
+with "A":
+
+ 2) A,10,input,IF            eval is A,10,input,IF
+
+read "if A then 10 else input".  Now replace A with it's verbose
+description again and--voila!--you have an easily readable description
+of the expression:
+
+ if input > 10 then 10 else input
+
+Finally, let's go back to the first most complex example and replace
+the value 128 with "input":
+
+ 1) input,8,*,7000,GT,7000,input,8,*,IF  eval input,8,*     result is A
+
+where A is "input * 8"
+
+ 2) A,7000,GT,7000,input,8,*,IF          eval is A,7000,GT  result is B
+
+where B is "if ((input * 8) > 7000) then true"
+
+ 3) B,7000,input,8,*,IF                  eval is input,8,*  result is C
+
+where C is "input * 8"
+
+ 4) B,7000,C,IF
+
+At last we have a readable decoding of the complex RPN expression with
+a variable:
+
+ if ((input * 8) > 7000) then 7000 else (input * 8)
+
+=head1 Exercises
+
+Exercise 1:
+
+Compute "3,2,*,1,+ and "3,2,1,+,*" by hand.  Rewrite them in
+traditional notation.  Explain why they have different answers.
+
+Answer 1:
+
+    3*2+1 = 7 and 3*(2+1) = 9.  These expressions have
+    different answers because the altering of the plus and
+    times operators alter the order of their evaluation.
+
+
+Exercise 2:
+
+One may be tempted to shorten the expression
+
+ input,8,*,56000,GT,56000,input,*,8,IF
+
+by removing the redundant use of "input,8,*" like so:
+
+ input,56000,GT,56000,input,IF,8,*
+
+Use traditional notation to show these expressions are not the same.
+Write an expression that's equivalent to the first expression, but
+uses the LE and DIV operators.
+
+Answer 2:
+
+    if (input <= 56000/8 ) { input*8 } else { 56000 }
+    input,56000,8,DIV,LT,input,8,*,56000,IF
+
+
+Exercise 3:
+
+Briefly explain why traditional mathematic notation requires the
+use of parentheses.  Explain why RPN notation does not require
+the use of parentheses.
+
+Answer 3:
+
+    Traditional mathematic expressions are evaluated by
+    doing multiplication and division first, then addition and
+    subtraction.  Parentheses are used to force the evaluation of
+    addition before multiplication (etc).  RPN does not require
+    parentheses because the ordering of objects on the stack
+    can force the evaluation of addition before multiplication.
+
+
+Exercise 4:
+
+Explain why it was desirable for the RRDtool developers to implement
+RPN notation instead of traditional mathematical notation.
+
+Answer 4:
+
+    The algorithm that implements traditional mathematical
+    notation is more complex then algorithm used for RPN.
+    So implementing RPN allowed Tobias Oetiker to write less
+    code!  (The code is also less complex and therefore less
+    likely to have bugs.)
+
+
+=head1 AUTHOR
+
+Steve Rader E<lt>rader@wiscnet.netE<gt>
diff --git a/program/doc/rrd-beginners.pod b/program/doc/rrd-beginners.pod
new file mode 100644 (file)
index 0000000..f3c209b
--- /dev/null
@@ -0,0 +1,326 @@
+=head1 NAME
+
+rrd-beginners - RRDtool Beginners' Guide
+
+=head1 SYNOPSIS
+
+Helping new RRDtool users to understand the basics of RRDtool
+
+=head1 DESCRIPTION
+
+This manual is an attempt to assist beginners in understanding the concepts
+of RRDtool. It sheds a light on differences between RRDtool and other
+databases. With help of an example, it explains the structure of RRDtool
+database. This is followed by an overview of the "graph" feature of RRDtool.
+At the end, it has sample scripts that illustrate the
+usage/wrapping of RRDtool within Shell or Perl scripts.
+
+=head2 What makes RRDtool so special?
+
+RRDtool is GNU licensed software developed by Tobias Oetiker, a system
+manager at the Swiss Federal Institute of Technology. Though it is a
+database, there are distinct differences between RRDtool databases and other
+databases as listed below:
+
+=over
+
+=item *
+
+RRDtool stores data; that makes it a back-end tool. The RRDtool command set
+allows the creation of graphs; that makes it a front-end tool as well. Other
+databases just store data and can not create graphs.
+
+=item *
+
+In case of linear databases, new data gets appended at the bottom of
+the database table. Thus its size keeps on increasing, whereas the size of
+an RRDtool database is determined at creation time. Imagine an RRDtool
+database as the perimeter of a circle. Data is added along the
+perimeter. When new data reaches the starting point, it overwrites
+existing data. This way, the size of an RRDtool database always
+remains constant. The name "Round Robin" stems from this behavior.
+
+=item *
+
+Other databases store the values as supplied. RRDtool can be configured to
+calculate the rate of change from the previous to the current value and
+store this information instead.
+
+=item *
+
+Other databases get updated when values are supplied. The RRDtool database
+is structured in such a way that it needs data at predefined time
+intervals. If it does not get a new value during the interval, it stores an
+UNKNOWN value for that interval. So, when using the RRDtool database, it is
+imperative to use scripts that run at regular intervals to ensure a constant
+data flow to update the RRDtool database.
+
+=back
+
+RRDtool is designed to store time series of data. With every data
+update, an associated time stamp is stored. Time is always expressed
+in seconds passed since epoch (01-01-1970). RRDtool can be installed
+on Unix as well as Windows. It comes with a command set to carry out
+various operations on RRD databases. This command set can be accessed
+from the command line, as well as from Shell or Perl scripts. The
+scripts act as wrappers for accessing data stored in RRDtool
+databases.
+
+=head2 Understanding by an example
+
+The structure of an RRD database is different than other linear databases.
+Other databases define tables with columns, and many other parameters. These
+definitions sometimes are very complex, especially in large databases.
+RRDtool databases are primarily used for monitoring purposes and
+hence are very simple in structure. The parameters
+that need to be defined are variables that hold values and archives of those
+values. Being time sensitive, a couple of time related parameters are also
+defined. Because of its structure, the definition of an RRDtool database also
+includes a provision to specify specific actions to take in the absence of
+update values. Data Source (DS), heartbeat, Date Source Type (DST), Round
+Robin Archive (RRA), and Consolidation Function (CF) are some of the
+terminologies related to RRDtool databases.
+
+The structure of a database and the terminology associated with it can be
+best explained with an example.
+
+ rrdtool create target.rrd \
+         --start 1023654125 \
+         --step 300 \
+         DS:mem:GAUGE:600:0:671744 \
+         RRA:AVERAGE:0.5:12:24 \
+         RRA:AVERAGE:0.5:288:31
+
+This example creates a database named F<target.rrd>. Start time
+(1'023'654'125) is specified in total number of seconds since epoch
+(time in seconds since 01-01-1970). While updating the database, the
+update time is also specified.  This update time MUST be large (later)
+then start time and MUST be in seconds since epoch.
+
+The step of 300 seconds indicates that database expects new values every
+300 seconds. The wrapper script should be scheduled to run every B<step>
+seconds so that it updates the database every B<step> seconds.
+
+DS (Data Source) is the actual variable which relates to the parameter on
+the device that is monitored. Its syntax is
+
+ DS:variable_name:DST:heartbeat:min:max
+
+B<DS> is a key word. C<variable_name> is a name under which the parameter is
+saved in the database. There can be as many DSs in a database as needed. After
+every step interval, a new value of DS is supplied to update the database.
+This value is also called Primary Data Point B<(PDP)>. In our example
+mentioned above, a new PDP is generated every 300 seconds.
+
+Note, that if you do NOT supply new data points exactly every 300 seconds,
+this is not a problem, RRDtool will interpolate the data accordingly.
+
+B<DST> (Data Source Type) defines the type of the DS. It can be
+COUNTER, DERIVE, ABSOLUTE, GAUGE. A DS declared as COUNTER will save
+the rate of change of the value over a step period. This assumes that
+the value is always increasing (the difference between the current and
+the previous value is greater than 0). Traffic counters on a router
+are an ideal candidate for using COUNTER as DST. DERIVE is the same as
+COUNTER, but it allows negative values as well. If you want to see the
+rate of I<change> in free disk space on your server, then you might
+want to use the DERIVE data type. ABSOLUTE also saves the rate of
+change, but it assumes that the previous value is set to 0. The
+difference between the current and the previous value is always equal
+to the current value. Thus it just stores the current value divided by
+the step interval (300 seconds in our example). GAUGE does not save
+the rate of change. It saves the actual value itself. There are no
+divisions or calculations. Memory consumption in a server is a typical
+example of gauge. The difference between the different types DSTs can be
+explained better with the following example:
+
+ Values       = 300, 600, 900, 1200
+ Step         = 300 seconds
+ COUNTER DS   =    1,  1,   1,    1
+ DERIVE DS    =    1,  1,   1,    1
+ ABSOLUTE DS  =    1,  2,   3,    4
+ GAUGE DS     = 300, 600, 900, 1200
+
+The next parameter is B<heartbeat>. In our example, heartbeat is 600
+seconds. If the database does not get a new PDP within 300 seconds, it
+will wait for another 300 seconds (total 600 seconds).  If it doesn't
+receive any PDP within 600 seconds, it will save an UNKNOWN value into
+the database. This UNKNOWN value is a special feature of RRDtool - it
+is much better than to assume a missing value was 0 (zero) or any
+other number which might also be a valid data value.  For example, the
+traffic flow counter on a router keeps increasing. Lets say, a value
+is missed for an interval and 0 is stored instead of UNKNOWN. Now when
+the next value becomes available, it will calculate the difference
+between the current value and the previous value (0) which is not
+correct. So, inserting the value UNKNOWN makes much more sense here.
+
+The next two parameters are the minimum and maximum value,
+respectively. If the variable to be stored has predictable maximum and
+minimum values, this should be specified here. Any update value
+falling out of this range will be stored as UNKNOWN.
+
+The next line declares a round robin archive (RRA). The syntax for
+declaring an RRA is
+
+ RRA:CF:xff:step:rows
+
+RRA is the keyword to declare RRAs. The consolidation function (CF)
+can be AVERAGE, MINIMUM, MAXIMUM, and LAST. The concept of the
+consolidated data point (CDP) comes into the picture here. A CDP is
+CFed (averaged, maximum/minimum value or last value) from I<step>
+number of PDPs. This RRA will hold I<rows> CDPs.
+
+Lets have a look at the example above. For the first RRA, 12 (steps)
+PDPs (DS variables) are AVERAGEed (CF) to form one CDP. 24 (rows) of
+theses CDPs are archived. Each PDP occurs at 300 seconds. 12 PDPs
+represent 12 times 300 seconds which is 1 hour. It means 1 CDP (which
+is equal to 12 PDPs) represents data worth 1 hour. 24 such CDPs
+represent 1 day (1 hour times 24 CDPs). This means, this RRA is an
+archive for one day. After 24 CDPs, CDP number 25 will replace the 1st
+CDP. The second RRA saves 31 CDPs; each CPD represents an AVERAGE
+value for a day (288 PDPs, each covering 300 seconds = 24
+hours). Therefore this RRA is an archive for one month. A single
+database can have many RRAs. If there are multiple DSs, each
+individual RRA will save data for all the DSs in the database. For
+example, if a database has 3 DSs and daily, weekly, monthly, and
+yearly RRAs are declared, then each RRA will hold data from all 3 data
+sources.
+
+=head2 Graphical Magic
+
+Another important feature of RRDtool is its ability to create
+graphs. The "graph" command uses the "fetch" command internally to
+retrieve values from the database. With the retrieved values it draws
+graphs as defined by the parameters supplied on the command line. A
+single graph can show different DS (Data Sources) from a database. It
+is also possible to show the values from more than one database in a
+single graph. Often, it is necessary to perform some math on the
+values retrieved from the database before plotting them. For example,
+in SNMP replies, memory consumption values are usually specified in
+KBytes and traffic flow on interfaces is specified in Bytes. Graphs
+for these values will be more meaningful if values are represented in
+MBytes and mbps. The RRDtool graph command allows to define such
+conversions. Apart from mathematical calculations, it is also possible
+to perform logical operations such as greater than, less than, and
+if/then/else. If a database contains more than one RRA archive, then a
+question may arise - how does RRDtool decide which RRA archive to use
+for retrieving the values? RRDtool looks at several things when making
+its choice. First it makes sure that the RRA covers as much of the
+graphing time frame as possible. Second it looks at the resolution of
+the RRA compared to the resolution of the graph. It tries to find one
+which has the same or higher better resolution. With the "-r" option
+you can force RRDtool to assume a different resolution than the one
+calculated from the pixel width of the graph.
+
+Values of different variables can be presented in 5 different shapes
+in a graph - AREA, LINE1, LINE2, LINE3, and STACK. AREA is represented
+by a solid colored area with values as the boundary of this
+area. LINE1/2/3 (increasing width) are just plain lines representing
+the values. STACK is also an area but it is "stack"ed on top AREA or
+LINE1/2/3. Another important thing to note is that variables are
+plotted in the order they are defined in the graph command. Therefore
+care must be taken to define STACK only after defining AREA/LINE. It
+is also possible to put formatted comments within the graph.  Detailed
+instructions can be found in the graph manual.
+
+=head2 Wrapping RRDtool within Shell/Perl script
+
+After understanding RRDtool it is now a time to actually use RRDtool
+in scripts. Tasks involved in network management are data collection,
+data storage, and data retrieval. In the following example, the
+previously created target.rrd database is used. Data collection and
+data storage is done using Shell scripts. Data retrieval and report
+generation is done using Perl scripts. These scripts are shown below:
+
+=head3 Shell script (collects data, updates database)
+
+ #!/bin/sh
+ a=0
+ while [ "$a" == 0 ]; do
+ snmpwalk -c public 192.168.1.250 hrSWRunPerfMem > snmp_reply
+     total_mem=`awk 'BEGIN {tot_mem=0}
+                           { if ($NF == "KBytes")
+                             {tot_mem=tot_mem+$(NF-1)}
+                           }
+                     END {print tot_mem}' snmp_reply`
+     # I can use N as a replacement for the current time
+     rrdtool update target.rrd N:$total_mem
+     # sleep until the next 300 seconds are full
+     perl -e 'sleep 300 - time % 300'
+ done # end of while loop
+
+=head3 Perl script (retrieves data from database and generates graphs and statistics)
+
+ #!/usr/bin/perl -w
+ # This script fetches data from target.rrd, creates a graph of memory
+ # consumption on the target (Dual P3 Processor 1 GHz, 656 MB RAM)
+
+ # call the RRD perl module
+ use lib qw( /usr/local/rrdtool-1.0.41/lib/perl ../lib/perl );
+ use RRDs;
+ my $cur_time = time();                # set current time
+ my $end_time = $cur_time - 86400;     # set end time to 24 hours ago
+ my $start_time = $end_time - 2592000; # set start 30 days in the past
+
+ # fetch average values from the RRD database between start and end time
+ my ($start,$step,$ds_names,$data) =
+     RRDs::fetch("target.rrd", "AVERAGE",
+                 "-r", "600", "-s", "$start_time", "-e", "$end_time");
+ # save fetched values in a 2-dimensional array
+ my $rows = 0;
+ my $columns = 0;
+ my $time_variable = $start;
+ foreach $line (@$data) {
+   $vals[$rows][$columns] = $time_variable;
+   $time_variable = $time_variable + $step;
+   foreach $val (@$line) {
+           $vals[$rows][++$columns] = $val;}
+   $rows++;
+   $columns = 0;
+ }
+ my $tot_time = 0;
+ my $count = 0;
+ # save the values from the 2-dimensional into a 1-dimensional array
+ for $i ( 0 .. $#vals ) {
+     $tot_mem[$count] = $vals[$i][1];
+     $count++;
+ }
+ my $tot_mem_sum = 0;
+ # calculate the total of all values
+ for $i ( 0 .. ($count-1) ) {
+     $tot_mem_sum = $tot_mem_sum + $tot_mem[$i];
+ }
+ # calculate the average of the array
+ my $tot_mem_ave = $tot_mem_sum/($count);
+ # create the graph
+ RRDs::graph ("/images/mem_$count.png",   
+             "--title= Memory Usage",    
+             "--vertical-label=Memory Consumption (MB)", 
+             "--start=$start_time",      
+             "--end=$end_time",          
+             "--color=BACK#CCCCCC",      
+             "--color=CANVAS#CCFFFF",    
+             "--color=SHADEB#9999CC",    
+             "--height=125",             
+             "--upper-limit=656",       
+             "--lower-limit=0",          
+             "--rigid",                  
+             "--base=1024",              
+             "DEF:tot_mem=target.rrd:mem:AVERAGE", 
+             "CDEF:tot_mem_cor=tot_mem,0,671744,LIMIT,UN,0,tot_mem,IF,1024,/",
+             "CDEF:machine_mem=tot_mem,656,+,tot_mem,-",
+             "COMMENT:Memory Consumption between $start_time",
+             "COMMENT:    and $end_time                     ",
+             "HRULE:656#000000:Maximum Available Memory - 656 MB",
+             "AREA:machine_mem#CCFFFF:Memory Unused",   
+             "AREA:tot_mem_cor#6699CC:Total memory consumed in MB");
+ my $err=RRDs::error;
+ if ($err) {print "problem generating the graph: $err\n";}
+ # print the output
+ print "Average memory consumption is ";
+ printf "%5.2f",$tot_mem_ave/1024;
+ print " MB. Graphical representation can be found at /images/mem_$count.png.";
+
+=head1 AUTHOR
+
+Ketan Patel E<lt>k2pattu@yahoo.comE<gt>
+
diff --git a/program/doc/rrdbuild.pod b/program/doc/rrdbuild.pod
new file mode 100644 (file)
index 0000000..99bab00
--- /dev/null
@@ -0,0 +1,364 @@
+=head1 NAME
+
+rrdbuild - Instructions for building RRDtool
+
+=head1 OVERVIEW
+
+If you downloaded the source of RRDtool you have to compile it. This
+document will give some information on how this is done.
+
+RRDtool relies on services of third part libraries. Some of these libraries
+may already be installed on your system. You have to compile copies of the other
+ones before you can build RRDtool.
+
+This document will tell you about all the necessary steps to get going.
+
+These instructions assume you are using a B<bash> shell. If you use csh/tcsh,
+then you can either type F<bash> to switch to bash for the compilation or if
+you know what you are doing just replace the export bits with
+setenv.
+
+We further assume that your copies of B<tar> and B<make> are actually B<GNU
+tar> and B<GNU make> respectively. It could be that they are installed as
+B<gtar> and B<gmake> on your system.
+
+=head1 OPTIMISTIC BUILD
+
+Before you start to build RRDtool, you have to decide two things:
+
+=over
+
+=item 1.
+
+In which directory you want to build the software.
+
+=item 2.
+
+Where you want to install the software.
+
+=back
+
+Once you have decided. Save the two locations into environment variables.
+
+ BUILD_DIR=/tmp/rrdbuild
+ INSTALL_DIR=/opt/rrdtool-1.4.3
+
+
+If your F</tmp> is mounted with the option noexec (RHEL seems todo that) you have to choose
+a different directory!
+
+Now make sure the BUILD_DIR exists and go there:
+
+ mkdir -p $BUILD_DIR
+ cd $BUILD_DIR
+
+Lets first assume you already have all the necessary libraries
+pre-installed. 
+
+ wget http://oss.oetiker.ch/rrdtool/pub/rrdtool-1.4.3.tar.gz
+ gunzip -c rrdtool-1.4.3.tar.gz | tar xf -
+ cd rrdtool-1.4.3
+ ./configure --prefix=$INSTALL_DIR && make && make install
+
+Ok, this was very optimistic. This try will probably have ended with
+B<configure> complaining about several missing libraries.
+
+=head1 INSTALLING DEPENDENCIES
+
+If your OS lets you install additional packages from a software repository,
+you may get away with installing the missing packages. When the packages are
+installed, run configure again and try to compile again. Below you find some
+hints on getting your OS ready for compiling RRDtool.
+
+Additions to this list are welcome. In general RRDtool should work with the
+latest versions of the libraries. The versions listed here are just what was
+current when I tested this.
+
+=head2 OpenSolaris 2008.05
+
+Just add a compiler and the gnome development package:
+
+ pkg install sunstudioexpress
+ pkg install SUNWgnome-common-devel
+
+There is a problem with F<cairo.pc> on OpenSolaris. It suggests that
+xrender is required for compilation with cairo. This is not true and also
+bad since OpenSolaris does not include an F<xrender.pc> file. Use Perl to
+fix this:
+
+ perl -i~ -p -e 's/(Requires.*?)\s*xrender.*/$1/' /usr/lib/pkgconfig/cairo.pc 
+
+Make sure the RRDtool build system finds your new compiler
+
+ export PATH=/opt/SunStudioExpress/bin
+
+Since there does not seem to be a viable msgfmt tool on OpenSolaris (short
+of installing it yourself). You have to call configure with the
+
+ --disable-libintl
+
+option.
+
+=head2 Debian / Ubuntu
+
+Use apt-get to make sure you have all that is required. A number
+of packages will get added through dependencies.
+
+ apt-get install libpango1.0-dev libxml2-dev
+
+=head2 Gentoo
+
+In Gentoo installing RRDtool is really simple you just need to B<emerge
+rrdtool>. All dependencies will be handled automatically by the portage
+system. The only thing you should care about are USE flags, which allow you
+fine tune features RRDtool will be built with. Currently the following USE
+flags are available:
+
+ doc    - install .html and .txt documentation
+          into /usr/share/doc/rrdtool-1.x.xx/
+ perl   - build and install perl language bindings
+ python - build and install python language bindings
+ ruby   - build and install ruby language bindings
+ tcl    - build and install tcl language bindings
+ rrdcgi - build and install rrdcgi
+
+After you've decided which USE flags you need, set them either in
+F<make.conf> or F</etc/portage/package.use> and finally run:
+
+ # emerge -va rrdtool
+
+Take a look at Gentoo handbook for further details on how to manage USE
+flags: http://www.gentoo.org/doc/en/handbook/handbook-x86.xml?part=2
+
+=head1 BUILDING DEPENDENCIES
+
+But again this may have been too optimistic still, and you actually have to
+compile your own copies of some of the required libraries. Things like
+libpng and zlib are pretty standard so you will probably have them on your
+system anyway. Freetype, Fontinst, Cairo, Pango may be installed, but it is
+possible that they are pretty old and thus don't live up to our
+expectations, so you may want to compile their latest versions.
+
+=head2 General build tips for AIX
+
+If you are working with AIX, you may find the B<--disable-shared> option
+will cause things to break for you. In that case you may have to install the
+shared libraries into the RRDtool PREFIX and work with B<--disable-static>
+instead.
+
+Another hint to get RRDtool working on AIX is to use the IBM XL C Compiler:
+
+ export CC=/usr/vac/bin/cc
+ export PERLCC=$CC
+
+(Better instructions for AIX welcome!)
+
+=head2 Build Instructions
+
+Some libraries want to know where other libraries are. For this to work,
+set the following environment variable
+
+ export PKG_CONFIG_PATH=${INSTALL_DIR}/lib/pkgconfig
+ export PATH=$INSTALL_DIR/bin:$PATH
+
+The above relies on the presence of the F<pkgconfig> program. Below you find instructions
+on how to compile pkgconfig.
+
+Since we are compiling libraries dynamically, they must know where to find
+each other. This is done by setting an appropriate LDFLAGS. Unfortunately,
+the syntax again differs from system to system:
+
+=over
+
+=item Solaris
+
+ export LDFLAGS=-R${INSTALL_DIR}/lib 
+
+if you are using the Sun Studio/Forte compiler, you may also want to set
+
+ CFLAGS="-xO3 -xcode=pic13"   (SPARC)
+ CFLAGS="-xO3 -Kpic"          (x86)
+
+=item Linux
+
+ export LDFLAGS="-Wl,--rpath -Wl,${INSTALL_DIR}/lib" 
+
+=item HPUX
+
+ export LDFLAGS="+b${INSTALL_DIR}/lib"
+
+=item AIX
+
+ export LDFLAGS="-Wl,-blibpath:${INSTALL_DIR}/lib"
+
+=back 
+
+If you have GNU make installed and it is not called 'make',
+then do
+
+ export MAKE=gmake
+ export GNUMAKE=gmake
+
+otherwise just do
+
+ export MAKE=make
+
+=head3 Building pkgconfig
+
+As mentioned above, without pkgconfig the whole build process will be lots
+of pain and suffering, so make sure you have a copy on your system. If it is
+not available natively, here is how to compile it.
+
+ wget http://pkgconfig.freedesktop.org/releases/pkg-config-0.23.tar.gz
+ gunzip -c pkg-config-0.23.tar.gz | tar xf -
+ cd pkg-config-0.23
+ ./configure --prefix=$INSTALL_DIR CFLAGS="-O3 -fPIC"
+ $MAKE
+ $MAKE install
+
+After installing pkgconfig in a custom directory, setting up the corresponding
+environment variable will be helpful.
+
+ export PKG_CONFIG=$INSTALL_DIR/bin/pkg-config
+
+=head3 Building zlib
+
+Chances are very high that you already have that on your system ... 
+
+ cd $BUILD_DIR
+ wget http://oss.oetiker.ch/rrdtool/pub/libs/zlib-1.2.3.tar.gz
+ gunzip -c zlib-1.2.3.tar.gz | tar xf -
+ cd zlib-1.2.3
+ ./configure --prefix=$INSTALL_DIR CFLAGS="-O3 -fPIC" --shared
+ $MAKE
+ $MAKE install
+
+=head3 Building libpng
+
+Libpng itself requires zlib to build, so we need to help a bit. If you
+already have a copy of zlib on your system (which is very likely) you can
+drop the settings of LDFLAGS and CPPFLAGS. Note that the backslash (\) at
+the end of line 4 means that line 4 and line 5 are on one line.
+
+ cd $BUILD_DIR
+ wget http://oss.oetiker.ch/rrdtool/pub/libs/libpng-1.2.18.tar.gz
+ gunzip -c libpng-1.2.18.tar.gz | tar xf -
+ cd libpng-1.2.10
+ env CFLAGS="-O3 -fPIC" ./configure --prefix=$INSTALL_DIR
+ $MAKE
+ $MAKE install
+
+=head3 Building freetype
+
+ cd $BUILD_DIR
+ wget http://oss.oetiker.ch/rrdtool/pub/libs/freetype-2.3.5.tar.gz
+ gunzip -c freetype-2.3.5.tar.gz | tar xf -
+ cd freetype-2.3.5
+ ./configure --prefix=$INSTALL_DIR CFLAGS="-O3 -fPIC"
+ $MAKE
+ $MAKE install
+
+If you run into problems building freetype on Solaris, you may want to try to
+add the following at the start the configure line:
+
+ env EGREP=egrep
+
+=head3 Building LibXML2
+
+ cd $BUILD_DIR
+ wget http://oss.oetiker.ch/rrdtool/pub/libs/libxml2-2.6.32.tar.gz
+ gunzip -c libxml2-2.6.32.tar.gz | tar xf -
+ cd libxml2-2.6.32
+ ./configure --prefix=$INSTALL_DIR CFLAGS="-O3 -fPIC"
+ $MAKE
+ $MAKE install
+
+=head3 Building fontconfig
+
+Note that fontconfig has a run time configuration file in INSTALL_DIR/etc you
+may want to adjust that so that fontconfig finds the fonts on your system.
+Run the fc-cache program to build the fontconfig cache after changing the
+config file.
+
+ cd $BUILD_DIR
+ wget http://oss.oetiker.ch/rrdtool/pub/libs/fontconfig-2.4.2.tar.gz
+ gunzip -c fontconfig-2.4.2.tar.gz   | tar xf -
+ cd fontconfig-2.4.2
+ ./configure --prefix=$INSTALL_DIR CFLAGS="-O3 -fPIC" --with-freetype-config=$INSTALL_DIR/bin/freetype-config
+ $MAKE
+ $MAKE install
+
+=head3 Building Pixman
+
+ cd $BUILD_DIR
+ wget http://oss.oetiker.ch/rrdtool/pub/libs/pixman-0.10.0.tar.gz
+ gunzip -c pixman-0.10.0.tar.gz  | tar xf -
+ cd pixman-0.10.0
+ ./configure --prefix=$INSTALL_DIR CFLAGS="-O3 -fPIC"
+ $MAKE
+ $MAKE install
+
+=head3 Building Cairo
+
+ cd $BUILD_DIR
+ wget http://oss.oetiker.ch/rrdtool/pub/libs/cairo-1.6.4.tar.gz
+ gunzip -c cairo-1.6.4.tar.gz   | tar xf -
+ cd cairo-1.6.4
+ ./configure --prefix=$INSTALL_DIR \
+    --enable-xlib=no \
+    --enable-xlib-render=no \
+    --enable-win32=no \
+    CFLAGS="-O3 -fPIC"
+ $MAKE
+ $MAKE install
+
+=head3 Building Glib
+
+ cd $BUILD_DIR
+ wget http://oss.oetiker.ch/rrdtool/pub/libs/glib-2.15.4.tar.gz
+ gunzip -c glib-2.15.4.tar.gz  | tar xf -
+ cd glib-2.15.4
+ ./configure --prefix=$INSTALL_DIR CFLAGS="-O3 -fPIC"
+ $MAKE
+ $MAKE install
+
+=head3 Building Pango
+
+ cd $BUILD_DIR
+ wget http://oss.oetiker.ch/rrdtool/pub/libs/pango-1.21.1.tar.bz2
+ bunzip2 -c pango-1.21.1.tar.bz2 | tar xf -
+ cd pango-1.21.1
+ ./configure --prefix=$INSTALL_DIR CFLAGS="-O3 -fPIC" --without-x
+ $MAKE
+ $MAKE install
+
+=head2 Building rrdtool (second try)
+
+Now all the dependent libraries are built and you can try again. This time
+you tell configure where it should be looking for libraries and include
+files. This is done via environment variables. Depending on the shell you
+are running, the syntax for setting environment variables is different.
+
+And finally try building again. We disable the python and tcl bindings
+because it seems that a fair number of people have ill configured python and
+tcl setups that would prevent RRDtool from building if they are included in
+their current state.
+
+ cd $BUILD_DIR/rrdtool-1.4.3
+ ./configure --prefix=$INSTALL_DIR --disable-tcl --disable-python
+ $MAKE clean
+ $MAKE
+ $MAKE install
+
+SOLARIS HINT: if you want to build  the Perl module for the native Perl (the
+one shipping with Solaris) you will need the Sun Forte compiler installed on
+your box or you have to hand-tune bindings/perl-shared/Makefile while
+building!
+
+Now go to I<$INSTALL_DIR>B</share/rrdtool/examples/> and run them to see if
+your build has been successful.
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
diff --git a/program/doc/rrdcached.pod b/program/doc/rrdcached.pod
new file mode 100644 (file)
index 0000000..d6bfec3
--- /dev/null
@@ -0,0 +1,699 @@
+=pod
+
+=head1 NAME
+
+rrdcached - Data caching daemon for rrdtool
+
+=head1 SYNOPSIS
+
+B<rrdcached>
+[B<-P>E<nbsp>I<permissions>]
+[B<-l>E<nbsp>I<address>]
+[B<-s>E<nbsp>I<group>]
+[B<-w>E<nbsp>I<timeout>]
+[B<-z>E<nbsp>I<delay>]
+[B<-f>E<nbsp>I<timeout>]
+[B<-p>E<nbsp>I<pid_file>]
+[B<-t>E<nbsp>I<write_threads>]
+[B<-j>E<nbsp>I<journal_dir>]
+[-F]
+[-g]
+[B<-b>E<nbsp>I<base_dir>E<nbsp>[B<-B>]]
+
+=head1 DESCRIPTION
+
+B<rrdcached> is a daemon that receives updates to existing RRD files,
+accumulates them and, if enough have been received or a defined time has
+passed, writes the updates to the RRD file. A I<flush> command may be used to
+force writing of values to disk, so that graphing facilities and similar can
+work with up-to-date data.
+
+The daemon was written with big setups in mind. Those setups usually run into
+IOE<nbsp>related problems sooner or later for reasons that are beyond the scope
+of this document. Check the wiki at the RRDtool homepage for details. Also
+check L</"SECURITY CONSIDERATIONS"> below before using this daemon! A detailed
+description of how the daemon operates can be found in the L</"HOW IT WORKS">
+section below.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-l> I<address>
+
+Tells the daemon to bind to I<address> and accept incoming connections on that
+socket. If I<address> begins with C<unix:>, everything following that prefix is
+interpreted as the path to a UNIX domain socket. Otherwise the address or node
+name are resolved using C<getaddrinfo()>.
+
+For network sockets, a port may be specified by using the form
+C<B<[>I<address>B<]:>I<port>>. If the address is an IPv4 address or a fully
+qualified domain name (i.E<nbsp>e. the address contains at least one dot
+(C<.>)), the square brackets can be omitted, resulting in the (simpler)
+C<I<address>B<:>I<port>> pattern. The default port is B<42217/udp>. If you
+specify a network socket, it is mandatory to read the
+L</"SECURITY CONSIDERATIONS"> section.
+
+The following formats are accepted. Please note that the address of the UNIX
+domain socket B<must> start with a slash in the second case!
+
+   unix:</path/to/unix.sock>
+   /<path/to/unix.sock>
+   <hostname-or-ip>
+   [<hostname-or-ip>]:<port>
+   <hostname-or-ipv4>:<port>
+
+If the B<-l> option is not specified the default address,
+C<unix:/tmp/rrdcached.sock>, will be used.
+
+=item B<-s> I<group_name>|I<gid>
+
+Set the group permissions of a UNIX domain socket. The option accepts either
+a numeric group id or group name. That group will then have both read and write
+permissions (the socket will have file permissions 0750) for the socket and,
+therefore, is able to send commands to the daemon. This
+may be useful in cases where you cannot easily run all RRD processes with the same
+user privileges (e.g. graph generating CGI scripts that typically run in the
+permission context of the web server).
+
+This option affects the I<following> UNIX socket addresses (the following
+B<-l> options), i.e., you may specify different settings for different
+sockets.
+
+The default is not to change ownership or permissions of the socket and, thus,
+use the system default.
+
+=item B<-m> I<mode>
+
+Set the file permissions of a UNIX domain socket. The option accepts an octal
+number representing the bit pattern for the mode (see L<chmod(1)> for
+details).
+
+Please note that not all systems honor this setting. On Linux, read/write
+permissions are required to connect to a UNIX socket. However, many
+BSD-derived systems ignore permissions for UNIX sockets. See L<unix(7)> for
+details.
+
+This option affects the I<following> UNIX socket addresses (the following
+B<-l> options), i.e., you may specify different settings for different
+sockets.
+
+The default is not to change ownership or permissions of the socket and, thus,
+use the system default.
+
+=item B<-P> I<command>[,I<command>[,...]]
+
+Specifies the commands accepted via a network socket. This allows
+administrators of I<RRDCacheD> to control the actions accepted from various
+sources.
+
+The arguments given to the B<-P> option is a comma separated list of commands.
+For example, to allow the C<FLUSH> and C<PENDING> commands one could specify:
+
+  rrdcached -P FLUSH,PENDING $MORE_ARGUMENTS
+
+The B<-P> option affects the I<following> socket addresses (the following B<-l>
+options). In the following example, only the IPv4 network socket (address
+C<10.0.0.1>) will be restricted to the C<FLUSH> and C<PENDING> commands:
+
+  rrdcached -l unix:/some/path -P FLUSH,PENDING -l 10.0.0.1
+
+A complete list of available commands can be found in the section
+L</"Valid Commands"> below. There are two minor special exceptions:
+
+=over 4
+
+=item *
+
+The C<HELP> and C<QUIT> commands are always allowed.
+
+=item *
+
+If the C<BATCH> command is accepted, the B<.>E<nbsp>command will automatically
+be accepted, too.
+
+=back
+
+Please also read L</"SECURITY CONSIDERATIONS"> below.
+
+=item B<-w> I<timeout>
+
+Data is written to disk every I<timeout> seconds. If this option is not
+specified the default interval of 300E<nbsp>seconds will be used.
+
+=item B<-z> I<delay>
+
+If specified, rrdcached will delay writing of each RRD for a random number
+of seconds in the rangeE<nbsp>[0,I<delay>).  This will avoid too many
+writes being queued simultaneously.  This value should be no greater than
+the value specified in B<-w>.  By default, there is no delay.
+
+=item B<-f> I<timeout>
+
+Every I<timeout> seconds the entire cache is searched for old values which are
+written to disk. This only concerns files to which updates have stopped, so
+setting this to a high value, such as 3600E<nbsp>seconds, is acceptable in most
+cases. This timeout defaults to 3600E<nbsp>seconds.
+
+=item B<-p> I<file>
+
+Sets the name and location of the PID-file. If not specified, the default,
+C<I<$localststedir>/run/rrdcached.pid> will be used.
+
+=item B<-t> I<write_threads>
+
+Specifies the number of threads used for writing RRD files.  The default
+isE<nbsp>4.  Increasing this number will allow rrdcached to have more
+simultaneous I/O requests into the kernel.  This may allow the kernel to
+re-order disk writes, resulting in better disk throughput.
+
+=item B<-j> I<dir>
+
+Write updates to a journal in I<dir>.  In the event of a program or system
+crash, this will allow the daemon to write any updates that were pending
+at the time of the crash.
+
+On startup, the daemon will check for journal files in this directory.  If
+found, all updates therein will be read into memory before the daemon
+starts accepting new connections.
+
+The journal will be rotated with the same frequency as the flush timer
+given by B<-f>.
+
+When journaling is enabled, the daemon will use a fast shutdown procedure.
+Rather than flushing all files to disk, it will make sure the journal is
+properly written and exit immediately.  Although the RRD data files are
+not fully up-to-date, no information is lost; all pending updates will be
+replayed from the journal next time the daemon starts up.
+
+To disable fast shutdown, use the B<-F> option.
+
+=item B<-F>
+
+ALWAYS flush all updates to the RRD data files when the daemon is shut
+down, regardless of journal setting.
+
+=item B<-g>
+
+Run in the foreground.  The daemon will not fork().
+
+=item B<-b> I<dir>
+
+The daemon will change into a specific directory at startup. All files passed
+to the daemon, that are specified by a B<relative> path, will be interpreted
+to be relative to this directory. If not given the default, C</tmp>, will be
+used.
+
+  +------------------------+------------------------+
+  ! Command line           ! File updated           !
+  +------------------------+------------------------+
+  ! foo.rrd                ! /tmp/foo.rrd           !
+  ! foo/bar.rrd            ! /tmp/foo/bar.rrd       !
+  ! /var/lib/rrd/foo.rrd   ! /var/lib/rrd/foo.rrd   !
+  +------------------------+------------------------+
+  Paths given on the command  line and paths actually
+  updated by the daemon,  assuming the base directory
+  "/tmp".
+
+B<WARNING:> The paths up to and including the base directory B<MUST NOT BE>
+symbolic links.  In other words, if the base directory is
+specified as:
+
+    -b /base/dir/somewhere
+
+... then B<NONE> of the following should be symbolic links:
+
+    /base
+    /base/dir
+    /base/dir/somewhere
+
+=item B<-B>
+
+Only permit writes into the base directory specified in B<-b> (and any
+sub-directories).  This does B<NOT> detect symbolic links.  Paths
+containing C<../> will also be blocked.
+
+=back
+
+=head1 AFFECTED RRDTOOL COMMANDS
+
+The following commands may be made aware of the B<rrdcached> using the command
+line argument B<--daemon> or the environment variable B<RRDCACHED_ADDRESS>:
+
+=over
+
+=item *
+
+dump
+
+=item *
+
+fetch
+
+=item *
+
+flush
+
+=item *
+
+graph
+
+=item *
+
+graphv
+
+=item *
+
+info
+
+=item *
+
+last
+
+=item *
+
+lastupdate
+
+=item *
+
+update
+
+=item *
+
+xport
+
+=back
+
+The B<update> command can send values to the daemon instead of writing them to
+the disk itself. All other commands can send a B<FLUSH> command (see below) to
+the daemon before accessing the files, so they work with up-to-date data even
+if the cache timeout is large.
+
+=head1 ERROR REPORTING
+
+The daemon reports errors in one of two ways: During startup, error messages
+are printed to C<STDERR>. One of the steps when starting up is to fork to the
+background and closing C<STDERR> - after this writing directly to the user is
+no longer possible. Once this has happened, the daemon will send log messages
+to the system logging daemon using syslog(3). The facility used is
+C<LOG_DAEMON>.
+
+=head1 HOW IT WORKS
+
+When receiving an update, B<rrdcached> does not write to disk but looks for an
+entry for that file in its internal tree. If not found, an entry is created
+including the current time (called "First" in the diagram below). This time is
+B<not> the time specified on the command line but the time the operating system
+considers to be "now". The value and time of the value (called "Time" in the
+diagram below) are appended to the tree node.
+
+When appending a value to a tree node, it is checked whether it's time to write
+the values to disk. Values are written to disk if
+S<C<now() - First E<gt>= timeout>>, where C<timeout> is the timeout specified
+using the B<-w> option, see L</OPTIONS>. If the values are "old enough" they
+will be enqueued in the "update queue", i.E<nbsp>e. they will be appended to
+the linked list shown below.  Because the tree nodes and the elements of the
+linked list are the same data structures in memory, any update to a file that
+has already been enqueued will be written with the next write to the RRD file,
+too.
+
+A separate "update thread" constantly dequeues the first element in the update
+queue and writes all its values to the appropriate file. So as long as the
+update queue is not empty files are written at the highest possible rate.
+
+Since the timeout of files is checked only when new values are added to the
+file, "dead" files, i.E<nbsp>e. files that are not updated anymore, would never
+be written to disk. Therefore, every now and then, controlled by the B<-f>
+option, the entire tree is walked and all "old" values are enqueued. Since this
+only affects "dead" files and walking the tree is relatively expensive, you
+should set the "flush interval" to a reasonably high value. The default is
+3600E<nbsp>seconds (one hour).
+
+The downside of caching values is that they won't show up in graphs generated
+from the RRDE<nbsp>files. To get around this, the daemon provides the "flush
+command" to flush specific files. This means that the file is inserted at the
+B<head> of the update queue or moved there if it is already enqueued. The flush
+command will return only after the file's pending updates have been written
+to disk.
+
+ +------+   +------+                               +------+
+ ! head !   ! root !                               ! tail !
+ +---+--+   +---+--+                               +---+--+
+     !         /\                                      !
+     !        /  \                                     !
+     !       /\  /\                                    !
+     !      /\/\ \ `----------------- ... --------,    !
+     V     /      `-------,                       !    V
+ +---+----+---+    +------+-----+             +---+----+---+
+ ! File:  foo !    ! File:  bar !             ! File:  qux !
+ ! First: 101 !    ! First: 119 !             ! First: 180 !
+ ! Next:&bar -+--->! Next:&... -+---> ... --->! Next:NULL  !
+ | Prev:NULL  !<---+-Prev:&foo  !<--- ... ----+-Prev: &... !
+ +============+    +============+             +============+
+ ! Time:  100 !    ! Time:  120 !             ! Time:  180 !
+ ! Value:  10 !    ! Value: 0.1 !             ! Value: 2,2 !
+ +------------+    +------------+             +------------+
+ ! Time:  110 !    ! Time:  130 !             ! Time:  190 !
+ ! Value:  26 !    ! Value: 0.1 !             ! Value: 7,3 !
+ +------------+    +------------+             +------------+
+ :            :    :            :             :            :
+ +------------+    +------------+             +------------+
+ ! Time:  230 !    ! Time:  250 !             ! Time:  310 !
+ ! Value:  42 !    ! Value: 0.2 !             ! Value: 1,2 !
+ +------------+    +------------+             +------------+
+
+The above diagram demonstrates:
+
+=over
+
+=item *
+
+Files/values are stored in a (balanced) tree.
+
+=item *
+
+Tree nodes and entries in the update queue are the same data structure.
+
+=item *
+
+The local time ("First") and the time specified in updates ("Time") may differ.  
+
+=item *
+
+Timed out values are inserted at the "tail".
+
+=item *
+
+Explicitly flushed values are inserted at the "head".
+
+=item *
+
+ASCII art rocks.
+
+=back
+
+=head1 SECURITY CONSIDERATIONS
+
+=head2 Authentication
+
+There is no authentication.
+
+The client/server protocol does not yet have any authentication mechanism. It
+is likely that authentication and encryption will be added in a future version,
+but for the time being it is the administrator's responsibility to secure the
+traffic from/to the daemon!
+
+It is highly recommended to install a packet filter or similar mechanism to
+prevent unauthorized connections. Unless you have a dedicated VLAN or VPN for
+this, using network sockets is probably a bad idea!
+
+=head2 Authorization
+
+There is minimal per-socket authorization.
+
+Authorization is currently done on a per-socket basis. That means each socket
+has a list of commands it will accept and it will accept. It will accept only
+those commands explicitly listed but it will (currently) accept these commands
+from anyone reaching the socket.
+
+If the networking sockets are to be used, it is necessary to restrict the
+accepted commands to those needed by external clients. If, for example,
+external clients want to draw graphs of the cached data, they should only be
+allowed to use the C<FLUSH> command.
+
+=head2 Encryption
+
+There is no encryption.
+
+Again, this may be added in the future, but for the time being it is your job
+to keep your private data private. Install a VPN or an encrypted tunnel if you
+statistics are confidential!
+
+=head2 Sanity checking
+
+There is no sanity checking.
+
+The daemon will blindly write to any file it gets told, so you really should
+create a separate user just for this daemon. Also it does not do any sanity
+checks, so if it gets told to write values for a time far in the future, your
+files will be messed up good!
+
+=head2 Conclusion
+
+=over 4
+
+=item *
+
+Security is the job of the administrator.
+
+=item *
+
+We recommend to allow write access via UNIX domain sockets only.
+
+=item *
+
+You have been warned.
+
+=back
+
+=head1 PROTOCOL
+
+The daemon communicates with clients using a line based ASCII protocol which is
+easy to read and easy to type. This makes it easy for scripts to implement the
+protocol and possible for users to use telnet to connect to the daemon
+and test stuff "by hand".
+
+The protocol is line based, this means that each record consists of one or more
+lines. A line is terminated by the line feed character C<0x0A>, commonly
+written as C<\n>. In the examples below, this character will be written as
+C<E<lt>LFE<gt>> ("line feed").
+
+After the connection has been established, the client is expected to send a
+"command". A command consists of the command keyword, possibly some arguments,
+and a terminating newline character. For a list of commands, see
+L</"Valid Commands"> below.
+
+Example:
+
+  FLUSH /tmp/foo.rrd<LF>
+
+The daemon answers with a line consisting of a status code and a short status
+message, separated by one or more space characters. A negative status code
+signals an error, a positive status code or zero signal success. If the status
+code is greater than zero, it indicates the number of lines that follow the
+status line.
+
+Examples:
+
+ 0 Success<LF>
+
+ 2 Two lines follow<LF>
+ This is the first line<LF>
+ And this is the second line<LF>
+
+=head2 Valid Commands
+
+The following commands are understood by the daemon:
+
+=over 4
+
+=item B<FLUSH> I<filename>
+
+Causes the daemon to put I<filename> to the B<head> of the update queue
+(possibly moving it there if the node is already enqueued). The answer will be
+sent B<after> the node has been dequeued.
+
+=item B<FLUSHALL>
+
+Causes the daemon to start flushing ALL pending values to disk.  This
+returns immediately, even though the writes may take a long time.
+
+=item B<PENDING> I<filename>
+
+Shows any "pending" updates for a file, in order.  The updates shown have
+not yet been written to the underlying RRD file.
+
+=item B<FORGET> I<filename>
+
+Removes I<filename> from the cache.  Any pending updates B<WILL BE LOST>.
+
+=item B<QUEUE>
+
+Shows the files that are on the output queue.  Returns zero or more lines
+in the following format, where E<lt>num_valsE<gt> is the number of values
+to be written for the E<lt>fileE<gt>:
+
+    <num_vals> <file>
+
+=item B<HELP> [I<command>]
+
+Returns a short usage message. If no command is given, or I<command> is
+B<HELP>, a list of commands supported by the daemon is returned. Otherwise a
+short description, possibly containing a pointer to a manual page, is returned.
+Obviously, this is meant for interactive usage and the format in which the
+commands and usage summaries are returned is not well defined.
+
+=item B<STATS>
+
+Returns a list of metrics which can be used to measure the daemons performance
+and check its status. For a description of the values returned, see
+L</"Performance Values"> below.
+
+The format in which the values are returned is similar to many other line based
+protocols: Each value is printed on a separate line, each consisting of the
+name of the value, a colon, one or more spaces and the actual value.
+
+Example:
+
+ 9 Statistics follow
+ QueueLength: 0
+ UpdatesReceived: 30
+ FlushesReceived: 2
+ UpdatesWritten: 13
+ DataSetsWritten: 390
+ TreeNodesNumber: 13
+ TreeDepth: 4
+ JournalBytes: 190
+ JournalRotate: 0
+
+=item B<UPDATE> I<filename> I<values> [I<values> ...]
+
+Adds more data to a filename. This is B<the> operation the daemon was designed
+for, so describing the mechanism again is unnecessary. Read L</"HOW IT WORKS">
+above for a detailed explanation.
+
+Note that rrdcached only accepts absolute timestamps in the update values.
+Updates strings like "N:1:2:3" are automatically converted to absolute
+time by the RRD client library before sending to rrdcached.
+
+=item B<WROTE> I<filename>
+
+This command is written to the journal after a file is successfully
+written out to disk.  It is used during journal replay to determine which
+updates have already been applied.  It is I<only> valid in the journal; it
+is not accepted from the other command channels.
+
+=item B<BATCH>
+
+This command initiates the bulk load of multiple commands.  This is
+designed for installations with extremely high update rates, since it
+permits more than one command to be issued per read() and write().
+
+All commands are executed just as they would be if given individually,
+except for output to the user.  Messages indicating success are
+suppressed, and error messages are delayed until the client is finished.
+
+Command processing is finished when the client sends a dot (".") on its
+own line.  After the client has finished, the server responds with an
+error count and the list of error messages (if any).  Each error messages
+indicates the number of the command to which it corresponds, and the error
+message itself.  The first user command after B<BATCH> is command number one.
+
+    client:  BATCH
+    server:  0 Go ahead.  End with dot '.' on its own line.
+    client:  UPDATE x.rrd 1223661439:1:2:3            <--- command #1
+    client:  UPDATE y.rrd 1223661440:3:4:5            <--- command #2
+    client:  and so on...
+    client:  .
+    server:  2 Errors
+    server:  1 message for command 1
+    server:  12 message for command 12
+
+=item B<QUIT>
+
+Disconnect from rrdcached.
+
+=back
+
+=head2 Performance Values
+
+The following counters are returned by the B<STATS> command:
+
+=over 4
+
+=item B<QueueLength> I<(unsigned 64bit integer)>
+
+Number of nodes currently enqueued in the update queue.
+
+=item B<UpdatesReceived> I<(unsigned 64bit integer)>
+
+Number of UPDATE commands received.
+
+=item B<FlushesReceived> I<(unsigned 64bit integer)>
+
+Number of FLUSH commands received.
+
+=item B<UpdatesWritten> I<(unsigned 64bit integer)>
+
+Total number of updates, i.E<nbsp>e. calls to C<rrd_update_r>, since the
+daemon was started.
+
+=item B<DataSetsWritten> I<(unsigned 64bit integer)>
+
+Total number of "data sets" written to disk since the daemon was
+started. A data set is one or more values passed to the B<UPDATE>
+command. For example: C<1223661439:123:456> is one data set with two
+values. The term "data set" is used to prevent confusion whether
+individual values or groups of values are counted.
+
+=item B<TreeNodesNumber> I<(unsigned 64bit integer)>
+
+Number of nodes in the cache.
+
+=item B<TreeDepth> I<(unsigned 64bit integer)>
+
+Depth of the tree used for fast key lookup.
+
+=item B<JournalBytes> I<(unsigned 64bit integer)>
+
+Total number of bytes written to the journal since startup.
+
+=item B<JournalRotate> I<(unsigned 64bit integer)>
+
+Number of times the journal has been rotated since startup.
+
+=back
+
+=head1 SIGNALS
+
+=over 4
+
+=item SIGINT and SIGTERM
+
+The daemon exits normally on receipt of either of these signals.  Pending
+updates are handled in accordance with the B<-j> and B<-F> options.
+
+=item SIGUSR1
+
+The daemon exits AFTER flushing all updates out to disk.  This may take a
+while.
+
+=item SIGUSR2
+
+The daemon exits immediately, without flushing updates out to disk.
+Pending updates will be replayed from the journal when the daemon starts
+up again.  B<WARNING: if journaling (-j) is NOT enabled, any pending
+updates WILL BE LOST>.
+
+=back
+
+=head1 BUGS
+
+No known bugs at the moment.
+
+=head1 SEE ALSO
+
+L<rrdtool>, L<rrdgraph>
+
+=head1 AUTHOR
+
+Florian Forster E<lt>octoE<nbsp>atE<nbsp>verplant.orgE<gt>
+
+Both B<rrdcached> and this manual page have been written by Florian.
+
+=head1 CONTRIBUTORS
+
+kevin brintnall E<lt>kbrint@rufus.netE<gt>
+
+=cut
+
diff --git a/program/doc/rrdcgi.pod b/program/doc/rrdcgi.pod
new file mode 100644 (file)
index 0000000..e34d4c0
--- /dev/null
@@ -0,0 +1,225 @@
+=head1 NAME
+
+rrdcgi - Create web pages containing RRD graphs based on templates
+
+=head1 SYNOPSIS
+
+C<#!/path/to/>B<rrdcgi> S<[B<--filter>]>
+
+=head1 DESCRIPTION
+
+B<rrdcgi> is a sort of very limited script interpreter. Its purpose
+is to run as a cgi-program and parse a web page template containing special
+E<lt>RRD:: tags. B<rrdcgi> will interpret and act according to these tags.
+In the end it will printout a web page including the necessary CGI headers.
+
+B<rrdcgi> parses the contents of the template in 3 steps. In each step it looks
+only for a subset of tags. This allows nesting of tags.
+
+The argument parser uses the same semantics as you are used from your C-shell.
+
+=over 8
+
+=item B<--filter>
+
+Assume that rrdcgi is run as a filter and not as a cgi.
+
+=back
+
+=head2 Keywords
+
+=over 8
+
+=item RRD::CV I<name>
+
+Inserts the CGI variable of the given name.
+
+=item RRD::CV::QUOTE I<name>
+
+Inserts the CGI variable of the given name but quotes it, ready for
+use as an argument in another RRD:: tag. So even when there are spaces in the
+value of the CGI variable it will still be considered to be one argument.
+
+=item RRD::CV::PATH I<name>
+
+Inserts the CGI variable of the given name, quotes it and makes sure
+it starts neither with a '/' nor contains '..'. This is to make
+sure that no problematic pathnames can be introduced through the
+CGI interface.
+
+=item RRD::GETENV I<variable>
+
+Get the value of an environment variable.
+
+ <RRD::GETENV REMOTE_USER>
+
+might give you the name of the remote user given you are using
+some sort of access control on the directory.
+
+
+=item RRD::GOODFOR I<seconds>
+
+Specify the number of seconds this page should remain valid. This will prompt
+the rrdcgi to output a Last-Modified, an Expire and if the number of
+seconds is I<negative> a Refresh header.
+
+=item RRD::INCLUDE I<filename>
+
+Include the contents of the specified file into the page returned from the cgi.
+
+=item RRD::SETENV I<variable> I<value>
+
+If you want to present your graphs in another time zone than your own, you
+could use
+
+ <RRD::SETENV TZ UTC>
+
+to make sure everything is presented in Universal Time. Note that the
+values permitted to TZ depend on your OS.
+
+=item RRD::SETVAR I<variable> I<value>
+
+Analog to SETENV but for local variables.
+
+=item RRD::GETVAR I<variable>
+
+Analog to GETENV but for local variables.
+
+=item RRD::TIME::LAST I<rrd-file> I<strftime-format>
+
+This gets replaced by the last modification time of the selected RRD. The
+time is I<strftime>-formatted with the string specified in the second argument.
+
+=item RRD::TIME::NOW I<strftime-format>
+
+This gets replaced by the current time of day. The time is
+I<strftime>-formatted with the string specified in the argument.
+
+Note that if you return : (colons) from your strftime format you may
+have to escape them using \ if the time is to be used as an argument
+to a GRAPH command.
+
+=item RRD::TIME::STRFTIME I<START|END> I<start-spec> I<end-spec> I<strftime-format>
+
+This gets replaced by a strftime-formatted time using the format
+I<strftime-format> on either I<start-spec> or I<end-spec> depending on
+whether I<START> or I<END> is specified.  Both I<start-spec> and I<end-spec>
+must be supplied as either could be relative to the other.  This is intended
+to allow pretty titles on graphs with times that are easier for non RRDtool
+folks to figure out than "-2weeks".
+
+Note that again, if you return : (colon) from your strftime format,
+you may have to escape them using \ if the time is to be used as an
+argument to a GRAPH command.
+
+=item RRD::GRAPH I<rrdgraph arguments>
+
+This tag creates the RRD graph defined by its argument and then is
+replaced by an appropriate E<lt>IMG ... E<gt> tag referring to the graph.
+The B<--lazy> option in RRD graph can be used to make sure that graphs
+are only regenerated when they are out of date. The arguments
+to the B<RRD::GRAPH> tag work as described in the B<rrdgraph> manual page.
+
+Use the B<--lazy> option in your RRD::GRAPH tags, to reduce the load
+on your server. This option makes sure that graphs are only regenerated when
+the old ones are out of date.
+
+If you do not specify your own B<--imginfo> format, the following will
+be used:
+
+ <IMG SRC="%s" WIDTH="%lu" HEIGHT="%lu">
+
+Note that %s stands for the filename part of the graph generated, all
+directories given in the PNG file argument will get dropped.
+
+=item RRD::PRINT I<number>
+
+If the preceding  B<RRD::GRAPH> tag contained and B<PRINT> arguments,
+then you can access their output with this tag. The I<number> argument refers to the
+number of the B<PRINT> argument. This first B<PRINT> has I<number> 0.
+
+=item RRD::INTERNAL <var>
+
+This tag gets replaced by an internal var. Currently these vars are known:
+VERSION, COMPILETIME.
+These vars represent the compiled-in values. 
+
+=back
+
+=head1 EXAMPLE 1
+
+The example below creates a web pages with a single RRD graph.
+
+ #!/usr/local/bin/rrdcgi
+ <HTML>
+ <HEAD><TITLE>RRDCGI Demo</TITLE></HEAD>
+ <BODY>
+ <H1>RRDCGI Example Page</H1>
+ <P>
+ <RRD::GRAPH demo.png --lazy --title="Temperatures"
+          DEF:cel=demo.rrd:exhaust:AVERAGE
+          LINE2:cel#00a000:"D. Celsius">
+
+ </P>
+ </BODY>
+ </HTML>
+
+=head1 EXAMPLE 2
+
+This script is slightly more elaborate, it allows you to run it from
+a form which sets RRD_NAME. RRD_NAME is then used to select which RRD
+you want to use as source for your graph.
+
+ #!/usr/local/bin/rrdcgi
+ <HTML>
+ <HEAD><TITLE>RRDCGI Demo</TITLE></HEAD>
+ <BODY>
+ <H1>RRDCGI Example Page for <RRD::CV RRD_NAME></H1>
+ <H2>Selection</H2>
+ <FORM><INPUT NAME=RRD_NAME TYPE=RADIO VALUE=roomA> Room A,
+       <INPUT NAME=RRD_NAME TYPE=RADIO VALUE=roomB> Room B.
+       <INPUT TYPE=SUBMIT></FORM>
+ <H2>Graph</H2>
+ <P>
+ <RRD::GRAPH <RRD::CV::PATH RRD_NAME>.png --lazy
+          --title "Temperatures for "<RRD::CV::QUOTE RRD_NAME>
+          DEF:cel=<RRD::CV::PATH RRD_NAME>.rrd:exhaust:AVERAGE
+          LINE2:cel#00a000:"D. Celsius">
+
+ </P>
+ </BODY>
+ </HTML>
+
+=head1 EXAMPLE 3
+
+This example shows how to handle the case where the RRD, graphs and
+cgi-bins are separate directories
+
+ #!/.../bin/rrdcgi
+ <HTML>
+ <HEAD><TITLE>RRDCGI Demo</TITLE></HEAD>
+ <BODY>
+ <H1>RRDCGI test Page</H1>
+ <RRD::GRAPH
+  /.../web/pngs/testhvt.png
+  --imginfo '<IMG SRC=/.../pngs/%s WIDTH=%lu HEIGHT=%lu >'
+  --lazy --start -1d --end now
+  DEF:http_src=/.../rrds/test.rrd:http_src:AVERAGE
+  AREA:http_src#00ff00:http_src
+ >
+ </BODY>
+ </HTML>
+
+Note 1: Replace /.../ with the relevant directories
+
+Note 2: The SRC=/.../pngs should be paths from the view of the
+webserver/browser
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
+
+
+
+
diff --git a/program/doc/rrdcreate.pod b/program/doc/rrdcreate.pod
new file mode 100644 (file)
index 0000000..b321de6
--- /dev/null
@@ -0,0 +1,598 @@
+=head1 NAME
+
+rrdcreate - Set up a new Round Robin Database
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<create> I<filename>
+S<[B<--start>|B<-b> I<start time>]>
+S<[B<--step>|B<-s> I<step>]>
+S<[B<--no-overwrite>]>
+S<[B<DS:>I<ds-name>B<:>I<DST>B<:>I<dst arguments>]>
+S<[B<RRA:>I<CF>B<:>I<cf arguments>]>
+
+=head1 DESCRIPTION
+
+The create function of RRDtool lets you set up new Round Robin
+Database (B<RRD>) files.  The file is created at its final, full size
+and filled with I<*UNKNOWN*> data.
+
+=head2 I<filename>
+
+The name of the B<RRD> you want to create. B<RRD> files should end
+with the extension F<.rrd>. However, B<RRDtool> will accept any
+filename.
+
+=head2 B<--start>|B<-b> I<start time> (default: now - 10s)
+
+Specifies the time in seconds since 1970-01-01 UTC when the first
+value should be added to the B<RRD>. B<RRDtool> will not accept
+any data timed before or at the time specified.
+
+See also AT-STYLE TIME SPECIFICATION section in the
+I<rrdfetch> documentation for other ways to specify time.
+
+=head2 B<--step>|B<-s> I<step> (default: 300 seconds)
+
+Specifies the base interval in seconds with which data will be fed
+into the B<RRD>.
+
+=head2 B<--no-overwrite>
+
+Do not clobber an existing file of the same name.
+
+=head2 B<DS:>I<ds-name>B<:>I<DST>B<:>I<dst arguments>
+
+A single B<RRD> can accept input from several data sources (B<DS>),
+for example incoming and outgoing traffic on a specific communication
+line. With the B<DS> configuration option you must define some basic
+properties of each data source you want to store in the B<RRD>.
+
+I<ds-name> is the name you will use to reference this particular data
+source from an B<RRD>. A I<ds-name> must be 1 to 19 characters long in
+the characters [a-zA-Z0-9_].
+
+I<DST> defines the Data Source Type. The remaining arguments of a
+data source entry depend on the data source type. For GAUGE, COUNTER,
+DERIVE, and ABSOLUTE the format for a data source entry is:
+
+B<DS:>I<ds-name>B<:>I<GAUGE | COUNTER | DERIVE | ABSOLUTE>B<:>I<heartbeat>B<:>I<min>B<:>I<max>
+
+For COMPUTE data sources, the format is:
+
+B<DS:>I<ds-name>B<:>I<COMPUTE>B<:>I<rpn-expression>
+
+In order to decide which data source type to use, review the
+definitions that follow. Also consult the section on "HOW TO MEASURE"
+for further insight.
+
+=over
+
+=item B<GAUGE>
+
+is for things like temperatures or number of people in a room or the
+value of a RedHat share.
+
+=item B<COUNTER>
+
+is for continuous incrementing counters like the ifInOctets counter in
+a router. The B<COUNTER> data source assumes that the counter never
+decreases, except when a counter overflows.  The update function takes
+the overflow into account.  The counter is stored as a per-second
+rate. When the counter overflows, RRDtool checks if the overflow
+happened at the 32bit or 64bit border and acts accordingly by adding
+an appropriate value to the result.
+
+=item B<DERIVE>
+
+will store the derivative of the line going from the last to the
+current value of the data source. This can be useful for gauges, for
+example, to measure the rate of people entering or leaving a
+room. Internally, derive works exactly like COUNTER but without
+overflow checks. So if your counter does not reset at 32 or 64 bit you
+might want to use DERIVE and combine it with a MIN value of 0.
+
+B<NOTE on COUNTER vs DERIVE>
+
+by Don Baarda E<lt>don.baarda@baesystems.comE<gt>
+
+If you cannot tolerate ever mistaking the occasional counter reset for a
+legitimate counter wrap, and would prefer "Unknowns" for all legitimate
+counter wraps and resets, always use DERIVE with min=0. Otherwise, using
+COUNTER with a suitable max will return correct values for all legitimate
+counter wraps, mark some counter resets as "Unknown", but can mistake some
+counter resets for a legitimate counter wrap.
+
+For a 5 minute step and 32-bit counter, the probability of mistaking a
+counter reset for a legitimate wrap is arguably about 0.8% per 1Mbps of
+maximum bandwidth. Note that this equates to 80% for 100Mbps interfaces, so
+for high bandwidth interfaces and a 32bit counter, DERIVE with min=0 is
+probably preferable. If you are using a 64bit counter, just about any max
+setting will eliminate the possibility of mistaking a reset for a counter
+wrap.
+
+=item B<ABSOLUTE>
+
+is for counters which get reset upon reading. This is used for fast counters
+which tend to overflow. So instead of reading them normally you reset them
+after every read to make sure you have a maximum time available before the
+next overflow. Another usage is for things you count like number of messages
+since the last update.
+
+=item B<COMPUTE>
+
+is for storing the result of a formula applied to other data sources
+in the B<RRD>. This data source is not supplied a value on update, but
+rather its Primary Data Points (PDPs) are computed from the PDPs of
+the data sources according to the rpn-expression that defines the
+formula. Consolidation functions are then applied normally to the PDPs
+of the COMPUTE data source (that is the rpn-expression is only applied
+to generate PDPs). In database software, such data sets are referred
+to as "virtual" or "computed" columns.
+
+=back
+
+I<heartbeat> defines the maximum number of seconds that may pass
+between two updates of this data source before the value of the
+data source is assumed to be I<*UNKNOWN*>.
+
+I<min> and I<max> define the expected range values for data supplied by a
+data source. If I<min> and/or I<max> any value outside the defined range
+will be regarded as I<*UNKNOWN*>. If you do not know or care about min and
+max, set them to U for unknown. Note that min and max always refer to the
+processed values of the DS. For a traffic-B<COUNTER> type DS this would be
+the maximum and minimum data-rate expected from the device.
+
+I<If information on minimal/maximal expected values is available,
+always set the min and/or max properties. This will help RRDtool in
+doing a simple sanity check on the data supplied when running update.>
+
+I<rpn-expression> defines the formula used to compute the PDPs of a
+COMPUTE data source from other data sources in the same <RRD>. It is
+similar to defining a B<CDEF> argument for the graph command. Please
+refer to that manual page for a list and description of RPN operations
+supported. For COMPUTE data sources, the following RPN operations are
+not supported: COUNT, PREV, TIME, and LTIME. In addition, in defining
+the RPN expression, the COMPUTE data source may only refer to the
+names of data source listed previously in the create command. This is
+similar to the restriction that B<CDEF>s must refer only to B<DEF>s
+and B<CDEF>s previously defined in the same graph command.
+
+=head2 B<RRA:>I<CF>B<:>I<cf arguments>
+
+The purpose of an B<RRD> is to store data in the round robin archives
+(B<RRA>). An archive consists of a number of data values or statistics for
+each of the defined data-sources (B<DS>) and is defined with an B<RRA> line.
+
+When data is entered into an B<RRD>, it is first fit into time slots
+of the length defined with the B<-s> option, thus becoming a I<primary
+data point>.
+
+The data is also processed with the consolidation function (I<CF>) of
+the archive. There are several consolidation functions that
+consolidate primary data points via an aggregate function: B<AVERAGE>,
+B<MIN>, B<MAX>, B<LAST>. 
+
+=over
+
+=item AVERAGE
+
+the average of the data points is stored.
+
+=item MIN
+
+the smallest of the data points is stored.
+
+=item MAX
+
+the largest of the data points is stored.
+
+=item LAST
+
+the last data points is used.
+
+=back
+
+Note that data aggregation inevitably leads to loss of precision and
+information. The trick is to pick the aggregate function such that the
+I<interesting> properties of your data is kept across the aggregation
+process.
+
+
+The format of B<RRA> line for these
+consolidation functions is:
+
+B<RRA:>I<AVERAGE | MIN | MAX | LAST>B<:>I<xff>B<:>I<steps>B<:>I<rows>
+
+I<xff> The xfiles factor defines what part of a consolidation interval may
+be made up from I<*UNKNOWN*> data while the consolidated value is still
+regarded as known. It is given as the ratio of allowed I<*UNKNOWN*> PDPs
+to the number of PDPs in the interval. Thus, it ranges from 0 to 1 (exclusive).
+
+
+I<steps> defines how many of these I<primary data points> are used to build
+a I<consolidated data point> which then goes into the archive.
+
+I<rows> defines how many generations of data values are kept in an B<RRA>.
+Obviously, this has to be greater than zero.
+
+=head1 Aberrant Behavior Detection with Holt-Winters Forecasting
+
+In addition to the aggregate functions, there are a set of specialized
+functions that enable B<RRDtool> to provide data smoothing (via the
+Holt-Winters forecasting algorithm), confidence bands, and the
+flagging aberrant behavior in the data source time series:
+
+=over
+
+=item *
+
+B<RRA:>I<HWPREDICT>B<:>I<rows>B<:>I<alpha>B<:>I<beta>B<:>I<seasonal period>[B<:>I<rra-num>]
+
+=item *
+
+B<RRA:>I<MHWPREDICT>B<:>I<rows>B<:>I<alpha>B<:>I<beta>B<:>I<seasonal period>[B<:>I<rra-num>]
+
+=item *
+
+B<RRA:>I<SEASONAL>B<:>I<seasonal period>B<:>I<gamma>B<:>I<rra-num>[B<:smoothing-window=>I<fraction>]
+
+=item *
+
+B<RRA:>I<DEVSEASONAL>B<:>I<seasonal period>B<:>I<gamma>B<:>I<rra-num>[B<:smoothing-window=>I<fraction>]
+
+=item *
+
+B<RRA:>I<DEVPREDICT>B<:>I<rows>B<:>I<rra-num>
+
+=item *
+
+B<RRA:>I<FAILURES>B<:>I<rows>B<:>I<threshold>B<:>I<window length>B<:>I<rra-num>
+
+=back
+
+These B<RRAs> differ from the true consolidation functions in several ways.
+First, each of the B<RRA>s is updated once for every primary data point.
+Second, these B<RRAs> are interdependent. To generate real-time confidence
+bounds, a matched set of SEASONAL, DEVSEASONAL, DEVPREDICT, and either
+HWPREDICT or MHWPREDICT must exist. Generating smoothed values of the primary
+data points requires a SEASONAL B<RRA> and either an HWPREDICT or MHWPREDICT 
+B<RRA>. Aberrant behavior detection requires FAILURES, DEVSEASONAL, SEASONAL,
+and either HWPREDICT or MHWPREDICT.
+
+The predicted, or smoothed, values are stored in the HWPREDICT or MHWPREDICT
+B<RRA>. HWPREDICT and MHWPREDICT are actually two variations on the
+Holt-Winters method. They are interchangeable. Both attempt to decompose data
+into three components: a baseline, a trend, and a seasonal coefficient.
+HWPREDICT adds its seasonal coefficient to the baseline to form a prediction, whereas
+MHWPREDICT multiplies its seasonal coefficient by the baseline to form a
+prediction. The difference is noticeable when the baseline changes
+significantly in the course of a season; HWPREDICT will predict the seasonality
+to stay constant as the baseline changes, but MHWPREDICT will predict the
+seasonality to grow or shrink in proportion to the baseline. The proper choice
+of method depends on the thing being modeled. For simplicity, the rest of this
+discussion will refer to HWPREDICT, but MHWPREDICT may be substituted in its
+place.
+
+The predicted deviations are stored in DEVPREDICT (think a standard deviation
+which can be scaled to yield a confidence band). The FAILURES B<RRA> stores 
+binary indicators. A 1 marks the indexed observation as failure; that is, the 
+number of confidence bounds violations in the preceding window of observations 
+met or exceeded a specified threshold. An example of using these B<RRAs> to graph 
+confidence bounds and failures appears in L<rrdgraph>.
+
+The SEASONAL and DEVSEASONAL B<RRAs> store the seasonal coefficients for the
+Holt-Winters forecasting algorithm and the seasonal deviations, respectively.
+There is one entry per observation time point in the seasonal cycle. For
+example, if primary data points are generated every five minutes and the
+seasonal cycle is 1 day, both SEASONAL and DEVSEASONAL will have 288 rows.
+
+In order to simplify the creation for the novice user, in addition to
+supporting explicit creation of the HWPREDICT, SEASONAL, DEVPREDICT,
+DEVSEASONAL, and FAILURES B<RRAs>, the B<RRDtool> create command supports
+implicit creation of the other four when HWPREDICT is specified alone and
+the final argument I<rra-num> is omitted.
+
+I<rows> specifies the length of the B<RRA> prior to wrap around. Remember
+that there is a one-to-one correspondence between primary data points and
+entries in these RRAs. For the HWPREDICT CF, I<rows> should be larger than
+the I<seasonal period>. If the DEVPREDICT B<RRA> is implicitly created, the
+default number of rows is the same as the HWPREDICT I<rows> argument. If the
+FAILURES B<RRA> is implicitly created, I<rows> will be set to the I<seasonal
+period> argument of the HWPREDICT B<RRA>. Of course, the B<RRDtool>
+I<resize> command is available if these defaults are not sufficient and the
+creator wishes to avoid explicit creations of the other specialized function
+B<RRAs>.
+
+I<seasonal period> specifies the number of primary data points in a seasonal
+cycle. If SEASONAL and DEVSEASONAL are implicitly created, this argument for
+those B<RRAs> is set automatically to the value specified by HWPREDICT. If
+they are explicitly created, the creator should verify that all three
+I<seasonal period> arguments agree.
+
+I<alpha> is the adaption parameter of the intercept (or baseline)
+coefficient in the Holt-Winters forecasting algorithm. See L<rrdtool> for a
+description of this algorithm. I<alpha> must lie between 0 and 1. A value
+closer to 1 means that more recent observations carry greater weight in
+predicting the baseline component of the forecast. A value closer to 0 means
+that past history carries greater weight in predicting the baseline
+component.
+
+I<beta> is the adaption parameter of the slope (or linear trend) coefficient
+in the Holt-Winters forecasting algorithm. I<beta> must lie between 0 and 1
+and plays the same role as I<alpha> with respect to the predicted linear
+trend.
+
+I<gamma> is the adaption parameter of the seasonal coefficients in the
+Holt-Winters forecasting algorithm (HWPREDICT) or the adaption parameter in
+the exponential smoothing update of the seasonal deviations. It must lie
+between 0 and 1. If the SEASONAL and DEVSEASONAL B<RRAs> are created
+implicitly, they will both have the same value for I<gamma>: the value
+specified for the HWPREDICT I<alpha> argument. Note that because there is
+one seasonal coefficient (or deviation) for each time point during the
+seasonal cycle, the adaptation rate is much slower than the baseline. Each
+seasonal coefficient is only updated (or adapts) when the observed value
+occurs at the offset in the seasonal cycle corresponding to that
+coefficient.
+
+If SEASONAL and DEVSEASONAL B<RRAs> are created explicitly, I<gamma> need not
+be the same for both. Note that I<gamma> can also be changed via the
+B<RRDtool> I<tune> command.
+
+I<smoothing-window> specifies the fraction of a season that should be
+averaged around each point. By default, the value of I<smoothing-window> is
+0.05, which means each value in SEASONAL and DEVSEASONAL will be occasionally
+replaced by averaging it with its (I<seasonal period>*0.05) nearest neighbors.
+Setting I<smoothing-window> to zero will disable the running-average smoother
+altogether.
+
+I<rra-num> provides the links between related B<RRAs>. If HWPREDICT is
+specified alone and the other B<RRAs> are created implicitly, then
+there is no need to worry about this argument. If B<RRAs> are created
+explicitly, then carefully pay attention to this argument. For each
+B<RRA> which includes this argument, there is a dependency between
+that B<RRA> and another B<RRA>. The I<rra-num> argument is the 1-based
+index in the order of B<RRA> creation (that is, the order they appear
+in the I<create> command). The dependent B<RRA> for each B<RRA>
+requiring the I<rra-num> argument is listed here:
+
+=over
+
+=item *
+
+HWPREDICT I<rra-num> is the index of the SEASONAL B<RRA>.
+
+=item *
+
+SEASONAL I<rra-num> is the index of the HWPREDICT B<RRA>.
+
+=item *
+
+DEVPREDICT I<rra-num> is the index of the DEVSEASONAL B<RRA>.
+
+=item *
+
+DEVSEASONAL I<rra-num> is the index of the HWPREDICT B<RRA>.
+
+=item *
+
+FAILURES I<rra-num> is the index of the DEVSEASONAL B<RRA>.
+
+=back
+
+I<threshold> is the minimum number of violations (observed values outside
+the confidence bounds) within a window that constitutes a failure. If the
+FAILURES B<RRA> is implicitly created, the default value is 7.
+
+I<window length> is the number of time points in the window. Specify an
+integer greater than or equal to the threshold and less than or equal to 28.
+The time interval this window represents depends on the interval between
+primary data points. If the FAILURES B<RRA> is implicitly created, the
+default value is 9.
+
+=head1 The HEARTBEAT and the STEP
+
+Here is an explanation by Don Baarda on the inner workings of RRDtool.
+It may help you to sort out why all this *UNKNOWN* data is popping
+up in your databases:
+
+RRDtool gets fed samples/updates at arbitrary times. From these it builds Primary
+Data Points (PDPs) on every "step" interval. The PDPs are
+then accumulated into the RRAs.
+
+The "heartbeat" defines the maximum acceptable interval between
+samples/updates. If the interval between samples is less than "heartbeat",
+then an average rate is calculated and applied for that interval. If
+the interval between samples is longer than "heartbeat", then that
+entire interval is considered "unknown". Note that there are other
+things that can make a sample interval "unknown", such as the rate
+exceeding limits, or a sample that was explicitly marked as unknown.
+
+The known rates during a PDP's "step" interval are used to calculate
+an average rate for that PDP. If the total "unknown" time accounts for
+more than B<half> the "step", the entire PDP is marked
+as "unknown". This means that a mixture of known and "unknown" sample
+times in a single PDP "step" may or may not add up to enough "known"
+time to warrant a known PDP.
+
+The "heartbeat" can be short (unusual) or long (typical) relative to
+the "step" interval between PDPs. A short "heartbeat" means you
+require multiple samples per PDP, and if you don't get them mark the
+PDP unknown. A long heartbeat can span multiple "steps", which means
+it is acceptable to have multiple PDPs calculated from a single
+sample. An extreme example of this might be a "step" of 5 minutes and a
+"heartbeat" of one day, in which case a single sample every day will
+result in all the PDPs for that entire day period being set to the
+same average rate. I<-- Don Baarda E<lt>don.baarda@baesystems.comE<gt>>
+
+       time|
+       axis|
+ begin__|00|
+        |01|
+       u|02|----* sample1, restart "hb"-timer
+       u|03|   /
+       u|04|  /
+       u|05| /
+       u|06|/     "hbt" expired
+       u|07|
+        |08|----* sample2, restart "hb" 
+        |09|   / 
+        |10|  /
+       u|11|----* sample3, restart "hb"
+       u|12|   /
+       u|13|  /
+ step1_u|14| /
+       u|15|/     "swt" expired
+       u|16|
+        |17|----* sample4, restart "hb", create "pdp" for step1 = 
+        |18|   /  = unknown due to 10 "u" labled secs > 0.5 * step
+        |19|  /
+        |20| /
+        |21|----* sample5, restart "hb"
+        |22|   /
+        |23|  /
+        |24|----* sample6, restart "hb"
+        |25|   /
+        |26|  /
+        |27|----* sample7, restart "hb"
+ step2__|28|   /
+        |22|  /
+        |23|----* sample8, restart "hb", create "pdp" for step1, create "cdp" 
+        |24|   /
+        |25|  /
+
+graphics by I<vladimir.lavrov@desy.de>.
+
+
+=head1 HOW TO MEASURE
+
+Here are a few hints on how to measure:
+
+=over
+
+
+=item Temperature
+
+Usually you have some type of meter you can read to get the temperature.
+The temperature is not really connected with a time. The only connection is
+that the temperature reading happened at a certain time. You can use the
+B<GAUGE> data source type for this. RRDtool will then record your reading
+together with the time.
+
+=item Mail Messages
+
+Assume you have a method to count the number of messages transported by
+your mail server in a certain amount of time, giving you data like '5
+messages in the last 65 seconds'. If you look at the count of 5 like an
+B<ABSOLUTE> data type you can simply update the RRD with the number 5 and the
+end time of your monitoring period. RRDtool will then record the number of
+messages per second. If at some later stage you want to know the number of
+messages transported in a day, you can get the average messages per second
+from RRDtool for the day in question and multiply this number with the
+number of seconds in a day. Because all math is run with Doubles, the
+precision should be acceptable.
+
+=item It's always a Rate
+
+RRDtool stores rates in amount/second for COUNTER, DERIVE and ABSOLUTE
+data.  When you plot the data, you will get on the y axis
+amount/second which you might be tempted to convert to an absolute
+amount by multiplying by the delta-time between the points. RRDtool
+plots continuous data, and as such is not appropriate for plotting
+absolute amounts as for example "total bytes" sent and received in a
+router. What you probably want is plot rates that you can scale to
+bytes/hour, for example, or plot absolute amounts with another tool
+that draws bar-plots, where the delta-time is clear on the plot for
+each point (such that when you read the graph you see for example GB
+on the y axis, days on the x axis and one bar for each day).
+
+=back
+
+
+=head1 EXAMPLE
+
+ rrdtool create temperature.rrd --step 300 \
+  DS:temp:GAUGE:600:-273:5000 \
+  RRA:AVERAGE:0.5:1:1200 \
+  RRA:MIN:0.5:12:2400 \
+  RRA:MAX:0.5:12:2400 \
+  RRA:AVERAGE:0.5:12:2400
+
+This sets up an B<RRD> called F<temperature.rrd> which accepts one
+temperature value every 300 seconds. If no new data is supplied for
+more than 600 seconds, the temperature becomes I<*UNKNOWN*>.  The
+minimum acceptable value is -273 and the maximum is 5'000.
+
+A few archive areas are also defined. The first stores the
+temperatures supplied for 100 hours (1'200 * 300 seconds = 100
+hours). The second RRA stores the minimum temperature recorded over
+every hour (12 * 300 seconds = 1 hour), for 100 days (2'400 hours). The
+third and the fourth RRA's do the same for the maximum and
+average temperature, respectively.
+
+=head1 EXAMPLE 2
+
+ rrdtool create monitor.rrd --step 300        \
+   DS:ifOutOctets:COUNTER:1800:0:4294967295   \
+   RRA:AVERAGE:0.5:1:2016                     \
+   RRA:HWPREDICT:1440:0.1:0.0035:288
+
+This example is a monitor of a router interface. The first B<RRA> tracks the
+traffic flow in octets; the second B<RRA> generates the specialized
+functions B<RRAs> for aberrant behavior detection. Note that the I<rra-num>
+argument of HWPREDICT is missing, so the other B<RRAs> will implicitly be
+created with default parameter values. In this example, the forecasting
+algorithm baseline adapts quickly; in fact the most recent one hour of
+observations (each at 5 minute intervals) accounts for 75% of the baseline
+prediction. The linear trend forecast adapts much more slowly. Observations
+made during the last day (at 288 observations per day) account for only
+65% of the predicted linear trend. Note: these computations rely on an
+exponential smoothing formula described in the LISA 2000 paper.
+
+The seasonal cycle is one day (288 data points at 300 second intervals), and
+the seasonal adaption parameter will be set to 0.1. The RRD file will store 5
+days (1'440 data points) of forecasts and deviation predictions before wrap
+around. The file will store 1 day (a seasonal cycle) of 0-1 indicators in
+the FAILURES B<RRA>.
+
+The same RRD file and B<RRAs> are created with the following command,
+which explicitly creates all specialized function B<RRAs>.
+
+ rrdtool create monitor.rrd --step 300 \
+   DS:ifOutOctets:COUNTER:1800:0:4294967295 \
+   RRA:AVERAGE:0.5:1:2016 \
+   RRA:HWPREDICT:1440:0.1:0.0035:288:3 \
+   RRA:SEASONAL:288:0.1:2 \
+   RRA:DEVPREDICT:1440:5 \
+   RRA:DEVSEASONAL:288:0.1:2 \
+   RRA:FAILURES:288:7:9:5
+
+Of course, explicit creation need not replicate implicit create, a
+number of arguments could be changed.
+
+=head1 EXAMPLE 3
+
+ rrdtool create proxy.rrd --step 300 \
+   DS:Total:DERIVE:1800:0:U  \
+   DS:Duration:DERIVE:1800:0:U  \
+   DS:AvgReqDur:COMPUTE:Duration,Requests,0,EQ,1,Requests,IF,/ \
+   RRA:AVERAGE:0.5:1:2016
+
+This example is monitoring the average request duration during each 300 sec
+interval for requests processed by a web proxy during the interval.
+In this case, the proxy exposes two counters, the number of requests
+processed since boot and the total cumulative duration of all processed
+requests. Clearly these counters both have some rollover point, but using the
+DERIVE data source also handles the reset that occurs when the web proxy is
+stopped and restarted.
+
+In the B<RRD>, the first data source stores the requests per second rate
+during the interval. The second data source stores the total duration of all
+requests processed during the interval divided by 300. The COMPUTE data source
+divides each PDP of the AccumDuration by the corresponding PDP of
+TotalRequests and stores the average request duration. The remainder of the
+RPN expression handles the divide by zero case.
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
diff --git a/program/doc/rrddump.pod b/program/doc/rrddump.pod
new file mode 100644 (file)
index 0000000..e8d802e
--- /dev/null
@@ -0,0 +1,97 @@
+=head1 NAME
+
+rrddump - dump the contents of an RRD to XML format
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<dump> I<filename.rrd> [I<filename.xml>]
+S<[B<--header>|B<-h> {none,xsd,dtd}]>
+S<[B<--no-header>]>
+S<[B<--daemon> I<address>]>
+S<E<gt> I<filename.xml>>
+
+=head1 DESCRIPTION
+
+The B<dump> function writes the contents of an B<RRD> in human
+readable (?) XML format to a file or to stdout. This format can
+be read by rrdrestore. Together they allow you to transfer your
+files from one computer architecture to another as well to
+manipulate the contents of an B<RRD> file in a somewhat more
+convenient manner.
+
+=over 8
+
+=item I<filename.rrd>
+
+The name of the B<RRD> you want to dump.
+
+=item I<filename.xml>
+
+The (optional) filename that you want to write the XML output to.
+If not specified, the XML will be printed to stdout.
+
+=item B<--header>|B<-h> {none,xsd,dtd}
+
+By default RRDtool will add a dtd header to the xml file. Here
+you can customize this to and xsd header or no header at all.
+
+
+=item B<--no-header>
+
+A shortcut for S<--header=none>.
+
+If you want to restore the dump with RRDtool 1.2 you should use the
+S<--no-header> option since 1.2 can not deal with xml headers.
+
+=item B<--daemon> I<address>
+
+Address of the L<rrdcached> daemon. If specified, a C<flush> command is sent
+to the server before reading the RRD files. This allows B<rrdtool> to return
+fresh data even if the daemon is configured to cache values for a long time.
+For a list of accepted formats, see the B<-l> option in the L<rrdcached> manual.
+
+ rrdtool dump --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd
+
+=back
+
+=head1 EXAMPLES
+
+To transfer an RRD between architectures, follow these steps:
+
+=over 4
+
+=item 1.
+
+On the same system where the RRD was created, use B<rrdtool> B<dump>
+to export the data to XML format.
+
+=item 2.
+
+Transfer the XML dump to the target system.
+
+=item 3.
+
+Run B<rrdtool> B<restore> to create a new RRD from the XML dump. See
+B<rrdrestore> for details.
+
+=back
+
+=head1 ENVIRONMENT VARIABLES
+
+The following environment variables may be used to change the behavior of
+C<rrdtoolE<nbsp>dump>:
+
+=over 4
+
+=item B<RRDCACHED_ADDRESS>
+
+If this environment variable is set it will have the same effect as specifying
+the C<--daemon> option on the command line. If both are present, the command
+line argument takes precedence.
+
+=back
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
diff --git a/program/doc/rrdfetch.pod b/program/doc/rrdfetch.pod
new file mode 100644 (file)
index 0000000..0052aa6
--- /dev/null
@@ -0,0 +1,288 @@
+=head1 NAME
+
+rrdfetch - Fetch data from an RRD.
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<fetch> I<filename> I<CF>
+S<[B<--resolution>|B<-r> I<resolution>]>
+S<[B<--start>|B<-s> I<start>]>
+S<[B<--end>|B<-e> I<end>]>
+S<[B<--daemon> I<address>]>
+
+=head1 DESCRIPTION
+
+The B<fetch> function is normally used internally by the graph
+function to get data from B<RRD>s. B<fetch> will analyze the B<RRD>
+and try to retrieve the data in the resolution requested.
+The data fetched is printed to stdout. I<*UNKNOWN*> data is often
+represented by the string "NaN" depending on your OS's printf
+function.
+
+=over 8
+
+=item I<filename>
+
+the name of the B<RRD> you want to fetch the data from.
+
+=item I<CF>
+
+the consolidation function that is applied to the data you
+want to fetch (AVERAGE,MIN,MAX,LAST)
+
+=item B<--resolution>|B<-r> I<resolution> (default is the highest resolution)
+
+the interval you want the values to have (seconds per
+value). B<rrdfetch> will try to match your request, but it will return
+data even if no absolute match is possible. B<NB.> See note below.
+
+=item B<--start>|B<-s> I<start> (default end-1day)
+
+start of the time series. A time in seconds since epoch (1970-01-01)
+is required. Negative numbers are relative to the current time. By default,
+one day worth of data will be fetched. See also AT-STYLE TIME SPECIFICATION
+section for a detailed explanation on  ways to specify the start time.
+
+=item B<--end>|B<-e> I<end> (default now)
+
+the end of the time series in seconds since epoch. See also AT-STYLE
+TIME SPECIFICATION section for a detailed explanation of how to
+specify the end time.
+
+=item B<--daemon> I<address>
+
+Address of the L<rrdcached> daemon. If specified, a C<flush> command is sent
+to the server before reading the RRD files. This allows B<rrdtool> to return
+fresh data even if the daemon is configured to cache values for a long time.
+For a list of accepted formats, see the B<-l> option in the L<rrdcached> manual.
+
+ rrdtool fetch --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd AVERAGE
+
+=back
+
+=head2 RESOLUTION INTERVAL
+
+In order to get RRDtool to fetch anything other than the finest resolution RRA
+B<both> the start and end time must be specified on boundaries that are
+multiples of the desired resolution. Consider the following example:
+
+ rrdtool create subdata.rrd -s 10 DS:ds0:GAUGE:300:0:U \
+  RRA:AVERAGE:0.5:30:3600 \
+  RRA:AVERAGE:0.5:90:1200 \
+  RRA:AVERAGE:0.5:360:1200 \
+  RRA:MAX:0.5:360:1200 \
+  RRA:AVERAGE:0.5:8640:600 \
+  RRA:MAX:0.5:8640:600
+
+This RRD collects data every 10 seconds and stores its averages over 5
+minutes, 15 minutes, 1 hour, and 1 day, as well as the maxima for 1 hour
+and 1 day.
+
+Consider now that you want to fetch the 15 minute average data for the
+last hour.  You might try
+
+ rrdtool fetch subdata.rrd AVERAGE -r 900 -s -1h
+
+However, this will almost always result in a time series that is
+B<NOT> in the 15 minute RRA. Therefore, the highest resolution RRA,
+i.e. 5 minute averages, will be chosen which in this case is not
+what you want.
+
+Hence, make sure that
+
+=over 3
+
+=item 1.
+
+both start and end time are a multiple of 900
+
+=item 2.
+
+both start and end time are within the desired RRA
+
+=back
+
+So, if time now is called "t", do
+
+ end time == int(t/900)*900,
+ start time == end time - 1hour,
+ resolution == 900.
+
+Using the bash shell, this could look be:
+
+ TIME=$(date +%s)
+ RRDRES=900
+ rrdtool fetch subdata.rrd AVERAGE -r $RRDRES \
+    -e $(($TIME/$RRDRES*$RRDRES)) -s e-1h
+
+Or in Perl:
+
+ perl -e '$ctime = time; $rrdres = 900; \
+          system "rrdtool fetch subdata.rrd AVERAGE \
+                  -r $rrdres -e @{[int($ctime/$rrdres)*$rrdres]} -s e-1h"'
+
+
+=head2 AT-STYLE TIME SPECIFICATION
+
+Apart from the traditional I<Seconds since epoch>, RRDtool does also
+understand at-style time specification. The specification is called
+"at-style" after the Unix command at(1) that has moderately complex
+ways to specify time to run your job at a certain date and time. The
+at-style specification consists of two parts: the B<TIME REFERENCE>
+specification and the B<TIME OFFSET> specification.
+
+=head2 TIME REFERENCE SPECIFICATION
+
+The time reference specification is used, well, to establish a reference
+moment in time (to which the time offset is then applied to). When present,
+it should come first, when omitted, it defaults to B<now>. On its own part,
+time reference consists of a I<time-of-day> reference (which should come
+first, if present) and a I<day> reference.
+
+The I<time-of-day> can be specified as B<HH:MM>, B<HH.MM>,
+or just B<HH>. You can suffix it with B<am> or B<pm> or use
+24-hours clock. Some special times of day are understood as well,
+including B<midnight> (00:00), B<noon> (12:00) and British
+B<teatime> (16:00).
+
+The I<day> can be specified as I<month-name> I<day-of-the-month> and
+optional a 2- or 4-digit I<year> number (e.g. March 8 1999). Alternatively,
+you can use I<day-of-week-name> (e.g. Monday), or one of the words:
+B<yesterday>, B<today>, B<tomorrow>. You can also specify the I<day> as a
+full date in several numerical formats, including B<MM/DD/[YY]YY>,
+B<DD.MM.[YY]YY>, or B<YYYYMMDD>.
+
+I<NOTE1>: this is different from the original at(1) behavior, where a
+single-number date is interpreted as MMDD[YY]YY.
+
+I<NOTE2>: if you specify the I<day> in this way, the I<time-of-day> is
+REQUIRED as well.
+
+Finally, you can use the words B<now>, B<start>, or B<end> as your time
+reference. B<Now> refers to the current moment (and is also the default
+time reference). B<Start> (B<end>) can be used to specify a time
+relative to the start (end) time for those tools that use these
+categories (B<rrdfetch>, L<rrdgraph>).
+
+Month and day of the week names can be used in their naturally
+abbreviated form (e.g., Dec for December, Sun for Sunday, etc.). The
+words B<now>, B<start>, B<end> can be abbreviated as B<n>, B<s>, B<e>.
+
+=head2 TIME OFFSET SPECIFICATION
+
+The time offset specification is used to add/subtract certain time
+intervals to/from the time reference moment. It consists of a I<sign>
+(S<B<+> or B<->>) and an I<amount>. The following time units can be
+used to specify the I<amount>: B<years>, B<months>, B<weeks>, B<days>,
+B<hours>, B<minutes>, or B<seconds>. These units can be used in
+singular or plural form, and abbreviated naturally or to a single
+letter (e.g. +3days, -1wk, -3y). Several time units can be combined
+(e.g., -5mon1w2d) or concatenated (e.g., -5h45min = -5h-45min =
+-6h+15min = -7h+1h30m-15min, etc.)
+
+I<NOTE3>: If you specify time offset in days, weeks, months, or years,
+you will end with the time offset that may vary depending on your time
+reference, because all those time units have no single well defined
+time interval value (S<1 year> contains either 365 or 366 days, S<1 month>
+is 28 to 31 days long, and even S<1 day> may be not equal to 24 hours
+twice a year, when DST-related clock adjustments take place).
+To cope with this, when you use days, weeks, months, or years
+as your time offset units your time reference date is adjusted
+accordingly without too much further effort to ensure anything
+about it (in the hope that mktime(3) will take care of this later).
+This may lead to some surprising (or even invalid!) results,
+e.g. S<'May 31 -1month'> = S<'Apr 31'> (meaningless) = S<'May 1'>
+(after mktime(3) normalization); in the EET timezone
+'3:30am Mar 29 1999 -1 day' yields '3:30am Mar 28 1999' (Sunday)
+which is an invalid time/date combination (because of 3am -> 4am DST
+forward clock adjustment, see the below example).
+
+In contrast, hours, minutes, and seconds are well defined time
+intervals, and these are guaranteed to always produce time offsets
+exactly as specified (e.g. for EET timezone, S<'8:00 Mar 27 1999 +2
+days'> = S<'8:00 Mar 29 1999'>, but since there is 1-hour DST forward
+clock adjustment that occurs around S<3:00 Mar 28 1999>, the actual
+time interval between S<8:00 Mar 27 1999> and S<8:00 Mar 29 1999>
+equals 47 hours; on the other hand, S<'8:00 Mar 27 1999 +48 hours'> =
+S<'9:00 Mar 29 1999'>, as expected)
+
+I<NOTE4>: The single-letter abbreviation for both B<months> and B<minutes>
+is B<m>. To disambiguate them, the parser tries to read your S<mind :)>
+by applying the following two heuristics:
+
+=over 3
+
+=item 1
+
+If B<m> is used in context of (i.e. right after the) years,
+months, weeks, or days it is assumed to mean B<months>, while
+in the context of hours, minutes, and seconds it means minutes.
+(e.g., in -1y6m or +3w1m B<m> is interpreted as B<months>, while in
+-3h20m or +5s2m B<m> the parser decides for B<minutes>).
+
+=item 2
+
+Out of context (i.e. right after the B<+> or B<-> sign) the
+meaning of B<m> is guessed from the number it directly follows.
+Currently, if the number's absolute value is below 25 it is assumed
+that B<m> means B<months>, otherwise it is treated as B<minutes>.
+(e.g., -25m == -25 minutes, while +24m == +24 months)
+
+=back
+
+I<Final NOTES>: Time specification is case-insensitive.
+Whitespace can be inserted freely or omitted altogether.
+There are, however, cases when whitespace is required
+(e.g., S<'midnight Thu'>). In this case you should either quote the
+whole phrase to prevent it from being taken apart by your shell or use
+'_' (underscore) or ',' (comma) which also count as whitespace
+(e.g., midnight_Thu or midnight,Thu).
+
+
+=head2 TIME SPECIFICATION EXAMPLES
+
+I<Oct 12> -- October 12 this year
+
+I<-1month> or I<-1m> -- current time of day, only a month before
+(may yield surprises, see NOTE3 above).
+
+I<noon yesterday -3hours> -- yesterday morning; can also be specified
+as I<9am-1day>.
+
+I<23:59 31.12.1999> -- 1 minute to the year 2000.
+
+I<12/31/99 11:59pm> -- 1 minute to the year 2000 for imperialists.
+
+I<12am 01/01/01> -- start of the new millennium
+
+I<end-3weeks> or I<e-3w> -- 3 weeks before end time
+(may be used as start time specification).
+
+I<start+6hours> or I<s+6h> -- 6 hours after start time
+(may be used as end time specification).
+
+I<931225537> -- 18:45  July 5th, 1999
+(yes, seconds since 1970 are valid as well).
+
+I<19970703 12:45> -- 12:45  July 3th, 1997
+(my favorite, and its even got an ISO number (8601)).
+
+=head1 ENVIRONMENT VARIABLES
+
+The following environment variables may be used to change the behavior of
+C<rrdtoolE<nbsp>fetch>:
+
+=over 4
+
+=item B<RRDCACHED_ADDRESS>
+
+If this environment variable is set it will have the same effect as specifying
+the C<--daemon> option on the command line. If both are present, the command
+line argument takes precedence.
+
+=back
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
diff --git a/program/doc/rrdfirst.pod b/program/doc/rrdfirst.pod
new file mode 100644 (file)
index 0000000..9a3c690
--- /dev/null
@@ -0,0 +1,32 @@
+=head1 NAME
+
+rrdfirst - Return the date of the first data sample in an RRA within an RRD
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<first> I<filename> [I<--rraindex number>]
+
+=head1 DESCRIPTION
+
+The B<first> function returns the UNIX timestamp of the first data
+sample entered into the specified RRA of the RRD file.
+
+=over 8
+
+=item I<filename>
+
+The name of the B<RRD> that contains the data.
+
+=item I<--rraindex number>
+
+The index number of the B<RRA> that is to be examined. If not specified, the
+index defaults to zero. B<RRA> index numbers can be determined through
+B<rrdtool info>.
+
+=back
+
+=head1 AUTHOR
+
+Burton Strauss <Burton@ntopSupport.com>
+
+
diff --git a/program/doc/rrdflushcached.pod b/program/doc/rrdflushcached.pod
new file mode 100644 (file)
index 0000000..c1aeeaa
--- /dev/null
@@ -0,0 +1,53 @@
+=head1 NAME
+
+rrdflushcached - Flush the values for a spcific RRD file from memory.
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<flushcached>
+S<[B<--daemon> I<address>]>
+I<filename> [I<filename> ...]
+
+=head1 DESCRIPTION
+
+The B<flushcached> function connects to L<rrdcached>, the RRD caching daemon,
+and issues a "flush" command for the given files. The daemon will put the
+files to the head of the update queue so they are written "soon". The
+status will be returned only after the files' pending updates have been
+written to disk.
+
+=over 8
+
+=item I<filename>
+
+The name(s) of the B<RRD> file(s) that are to be written to disk.
+
+=item B<--daemon> I<address>
+
+Address of the L<rrdcached> daemon. If not specified, the
+RRDCACHED_ADDRESS environment variable must be set (see below).  For a
+list of accepted formats, see the B<-l> option in the L<rrdcached>
+manual.
+
+ rrdtool flush --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd
+
+=back
+
+=head1 ENVIRONMENT VARIABLES
+
+The following environment variables may be used to change the behavior of
+C<rrdtoolE<nbsp>flushcached>:
+
+=over 4
+
+=item B<RRDCACHED_ADDRESS>
+
+If this environment variable is set it will have the same effect as specifying
+the C<--daemon> option on the command line. If both are present, the command
+line argument takes precedence.
+
+=back
+
+=head1 AUTHOR
+
+Florian Forster E<lt>octoE<nbsp>atE<nbsp>verplant.orgE<gt>
diff --git a/program/doc/rrdgraph-old.pod b/program/doc/rrdgraph-old.pod
new file mode 100644 (file)
index 0000000..ac661a9
--- /dev/null
@@ -0,0 +1,664 @@
+=head1 NAME
+
+rrdtool graph - Create a graph based on data from one or several RRD
+
+=for html <div align="right"><a href="rrdgraph.pdf">PDF</a> version.</div>
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<graph> I<filename>
+S<[B<-s>|B<--start> I<seconds>]>
+S<[B<-e>|B<--end> I<seconds>]>
+S<[B<-x>|B<--x-grid> I<x-axis grid and label>]>
+S<[B<-y>|B<--y-grid> I<y-axis grid and label>]>
+S<[B<-Y>|B<--alt-y-grid>]>
+S<[B<-A>|B<--alt-autoscale>]>
+S<[B<-M>|B<--alt-autoscale-max>]>
+S<[B<-X>|B<--units-exponent>]> I<value>]>
+S<[B<-v>|B<--vertical-label> I<text>]>
+S<[B<-w>|B<--width> I<pixels>]>
+S<[B<-h>|B<--height> I<pixels>]>
+S<[B<-i>|B<--interlaced>]>
+S<[B<-f>|B<--imginfo> I<formatstring>]>
+S<[B<-a>|B<--imgformat> B<SVG>|B<PNG>]>
+S<[B<-z>|B<--lazy>]>
+S<[B<-o>|B<--logarithmic>]>
+S<[B<-u>|B<--upper-limit> I<value>]>
+S<[B<-l>|B<--lower-limit> I<value>]>
+S<[B<-g>|B<--no-legend>]>
+S<[B<-r>|B<--rigid>]>
+S<[B<-S>|B<--step> I<value>]>
+S<[B<-b>|B<--base> I<value>]>
+S<[B<-c>|B<--color> I<COLORTAG>B<#>I<rrggbb>]>
+S<[B<-t>|B<--title> I<title>]>
+S<[B<DEF:>I<vname>B<=>I<rrd>B<:>I<ds-name>B<:>I<CF>]>
+S<[B<CDEF:>I<vname>B<=>I<rpn-expression>]>
+S<[B<PRINT:>I<vname>B<:>I<CF>B<:>I<format>]>
+S<[B<GPRINT:>I<vname>B<:>I<CF>B<:>I<format>]>
+S<[B<COMMENT:>I<text>]>
+S<[B<HRULE:>I<value>B<#>I<rrggbb>[B<:>I<legend>]]>
+S<[B<VRULE:>I<time>B<#>I<rrggbb>[B<:>I<legend>]]>
+S<[B<LINE>{B<1>|B<2>|B<3>}B<:>I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]]>
+S<[B<AREA:>I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]]>
+S<[B<STACK:>I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]]>
+S<[B<TICK:>I<vname>B<#>I<rrggbb>[B<:>I<axis-fraction>[B<:>I<legend>]]]>
+
+=head1 DESCRIPTION
+
+The B<graph> functions main purpose is to create graphical
+representations of the data stored in one or several B<RRD>s. Apart
+from generating graphs, it can also extract numerical reports.
+
+=over
+
+=item I<filename>
+
+The name of the graph to generate. Since B<RRDtool> outputs
+SVGs and PNGs, it's recommended that the filename end in either
+F<.svg> or F<.png>.  B<RRDtool> does not enforce this, however.
+If the  I<filename> is set to '-' the image file will be written
+to standard out.  All other output will get suppressed.
+
+If no graph functions are called, the graph will not be created.
+
+=item B<-s>|B<--start> I<seconds> (default end-1day)
+
+The time when the graph should begin. Time in seconds since
+epoch (1970-01-01) is required. Negative numbers are relative to the
+current time. By default one day worth of data will be graphed.
+See also AT-STYLE TIME SPECIFICATION section in the I<rrdfetch>
+documentation for a detailed explanation on how to specify time.
+
+=item B<-e>|B<--end> I<seconds> (default now)
+
+The time when the graph should end. Time in seconds since epoch.
+See also AT-STYLE TIME SPECIFICATION section in the I<rrdfetch>
+documentation for a detailed explanation of ways to specify time.
+
+=item B<-x>|B<--x-grid> I<x-axis grid and label> (default autoconfigure)
+
+The x-axis label is quite complex to configure. So if you don't have
+very special needs, you can rely on the auto configuration to get this
+right.
+
+If you want no x-grid at all, use the magic setting B<none>.
+
+The x-axis label and grid can be configured, using the following format:
+
+I<GTM>B<:>I<GST>B<:>I<MTM>B<:>I<MST>B<:>I<LTM>:I<LST>B<:>I<LPR>B<:>I<LFM>
+
+You have to configure three elements making up the x-axis labels and
+grid. The base grid (I<G??>), the major grid (I<M??>) and the labels
+(I<L??>). The configuration is based on the idea that you first
+specify a well known amount of time (I<?TM>) and then say how many
+times it has to pass between each grid line or label (I<?ST>). For the
+label you have to define two additional items: The precision of the
+label in seconds (I<LPR>) and the strftime format used to generate the
+text of the label (I<LFM>).
+
+The I<?TM> elements must be one of the following keywords: B<SECOND>,
+B<MINUTE>, B<HOUR>, B<DAY>, B<WEEK>, B<MONTH> or B<YEAR>.
+
+If you wanted a graph with a base grid every 10 minutes and a major
+one every hour, with labels every hour you would use the following
+x-axis definition.
+
+C<MINUTE:10:HOUR:1:HOUR:1:0:%X>
+
+The precision in this example is 0 because the %X format is exact. If
+the label was the name of the day, we would have had a precision of 24
+hours, because when you say something like 'Monday' you mean the whole
+day and not Monday morning 00:00. Thus the label should be positioned
+at noon. By defining a precision of 24 hours or rather 86400 seconds,
+you make sure that this happens.
+
+=item B<-y>|B<--y-grid> I<grid step>:I<label factor> (default autoconfigure)
+
+Makes vertical grid lines appear at I<grid step> interval. Every
+I<label factor> grid step, a major grid line is printed, along with
+label showing the value of the grid line.
+
+If you want no y-grid at all set specify the magic word B<none>.
+
+=item B<--alt-y-grid>
+
+Place Y grid dynamically based on graph Y range. Algorithm ensures
+that you always have grid, that there are enough but not too many
+grid lines and the grid is metric. That is grid lines are placed
+every 1, 2, 5 or 10 units.  (contributed by Sasha Mikheev)
+
+
+=item B<--alt-autoscale>
+
+Compute Y range  based on function absolute minimum and
+maximum values. Default algorithm uses predefined set of ranges.
+This is good in many cases but it fails miserably when you need
+to graph something like 260 + 0.001 * sin(x). Default algorithm
+will use Y range from 250 to 300 and on the graph you will see
+almost straight line. With --alt-autoscale Y range will be
+from slightly less the 260 - 0.001 to slightly more then 260 + 0.001
+and periodic behavior will be seen.   (contributed by Sasha Mikheev)
+
+=item B<--alt-autoscale-max>
+
+Where --alt-autoscale will modify both the absolute maximum AND minimum
+values, this option will only affect the maximum value. The minimum
+value, if not defined on the command line, will be 0. This option can
+be useful when graphing router traffic when the WAN line uses compression,
+and thus the throughput may be higher than the WAN line speed.
+
+=item B<--units-exponent> I<value> (default autoconfigure)
+
+This sets the 10**exponent scaling of the y-axis values.  Normally
+values will be scaled to the appropriate units (k, M, etc.).  However
+you may wish to display units always in k (Kilo, 10e3) even if the data
+is in the M (Mega, 10e6) range for instance.  Value should be an
+integer which is a multiple of 3 between -18 and 18 inclusive.  It is
+the exponent on the units you which to use.  For example, use 3 to
+display the y-axis values in k (Kilo, 10e3, thousands), use -6 to
+display the y-axis values in u (Micro, 10e-6, millionths).  Use a value
+of 0 to prevent any scaling of the y-axis values.
+
+=item B<-v>|B<--vertical-label> I<text>
+
+Vertical label on the left side of the graph. This is normally used to
+specify the units used.
+
+=item B<-w>|B<--width> I<pixels> (default 400 pixel)
+
+Width of the drawing area within the graph. This affects the size of the
+image.
+
+=item B<-h>|B<--height> I<pixels> (default 100 pixel)
+
+Width of the drawing area within the graph. This affects the size of the
+image.
+
+=item B<-i>|B<--interlaced> (default: false)
+
+If you set this option, then the resulting image will be interlaced.
+Most web browsers display these incrementally as they load. If
+you do not use this option, the image defaults to being progressive
+scanned. The only effect of this option is to control the format
+of the image on disk. It makes no changes to the layout or contents
+of the graph.
+
+=item B<-f>|B<--imginfo> I<formatstring>
+
+After the image has been created, the graph function uses printf
+together with this format string to create output similar to the PRINT
+function, only that the printf is supplied with the parameters
+I<filename>, I<xsize> and I<ysize>. In order to generate an B<IMG> tag
+suitable for including the graph into a web page, the command line
+would look like this:
+
+ --imginfo '<IMG SRC="/img/%s" WIDTH="%lu" HEIGHT="%lu" ALT="Demo">'
+
+=item B<-a>|B<--imgformat> B<SVG>|B<PNG> (default: PNG)
+
+Allows you to produce PNG output from RRDtool.
+
+=item B<-z>|B<--lazy> (default: false)
+
+Only generate the graph, if the current image is out of date or not
+existent.
+
+=item B<-u>|B<--upper-limit> I<value> (default autoconfigure)
+
+Defines the value normally located at the upper border of the
+graph. If the graph contains higher values, the upper border will
+move upward to accommodate these values as well.
+
+If you want to define an upper-limit which will not move in any
+event you have to set the B<--rigid> option as well.
+
+=item B<-l>|B<--lower-limit> I<value> (default autoconfigure)
+
+This is not the lower limit of a graph.  But rather, this is the
+maximum lower bound of a graph.  For example, the value -100 will
+result in a graph that has a lower limit of -100 or less.  Use this
+keyword to expand graphs down.
+
+=item B<-r>|B<--rigid>
+
+rigid boundaries mode.  Normally rrdgraph will automatically expand the
+lower and upper limit if the graph contains a value outside the valid
+range. With the r option you can disable this behavior
+
+=item B<-b>|B<--base> I<value>
+
+if you are graphing memory (and NOT network traffic) this switch
+should be set to 1024 so that one Kb is 1024 byte. For traffic
+measurement, 1 kb/s is 1000 b/s.
+
+=item B<-o>|B<--logarithmic>
+
+logarithmic y-axis scaling
+
+=item B<-c>|B<--color> I<COLORTAG>B<#>I<rrggbb> (default colors)
+
+override the colors for the standard elements of the graph. The I<COLORTAG>
+must be one of the following symbolic names: B<BACK> ground, B<CANVAS>,
+B<SHADEA> left/top border, B<SHADEB> right/bottom border, B<GRID>, B<MGRID>
+major grid, B<FONT>, B<FRAME> and axis of the graph or B<ARROW>. This option
+can be called multiple times to set several colors.
+
+=item B<-g>|B<--no-legend>
+
+Suppress generation of legend; only render the graph.
+
+=item B<-t>|B<--title> I<text> (default no title)
+
+Define a title to be written into the graph
+
+=item B<--step> I<value> (default automatic)
+
+By default rrdgraph calculates the width of one pixel in the time domain and
+tries to get data at that resolution from the RRD. With this switch you can
+override this behavior. If you want rrdgraph to get data at 1 hour
+resolution from the RRD, then you can set the step to 3600 seconds. Note,
+that a step smaller than 1 pixel will be silently ignored.
+
+=item B<DEF:>I<vname>B<=>I<rrd>B<:>I<ds-name>B<:>I<CF>
+
+Define virtual name for a data source. This name can then be used
+in the functions explained below. The
+DEF call automatically chooses an B<RRA> which contains I<CF> consolidated data in a
+resolution appropriate for the size of the graph to be drawn.  Ideally
+this means that one data point from the B<RRA> should be represented
+by one pixel in the graph.  If the resolution of the B<RRA> is higher
+than the resolution of the graph, the data in the RRA will be further
+consolidated according to the consolidation function (I<CF>) chosen.
+
+=item B<CDEF:>I<vname>B<=>I<rpn-expression>
+
+Create a new virtual data source by evaluating a mathematical expression,
+specified in Reverse Polish Notation (RPN). If you have ever used a traditional
+HP calculator you already know RPN. The idea behind RPN notation is,
+that you have a stack and push your data onto this stack. When ever
+you execute an operation, it takes as many data values from the stack
+as needed. The pushing of data is implicit, so when ever you specify a number
+or a variable, it gets pushed automatically.
+
+If this is all a big load of incomprehensible words for you, maybe an
+example helps (a more complete explanation is given in [1]): The
+expression I<vname+3/2> becomes C<vname,3,2,/,+> in RPN. First the three
+values get pushed onto the stack (which now contains (the current
+value of) vname, a 3 and a 2).  Then the / operator pops two values
+from the stack (3 and 2), divides the first argument by the second
+(3/2) and pushes the result (1.5) back onto the stack. Then the +
+operator pops two values (vname and 1.5) from the stack; both values
+are added up and the result gets pushes back onto the stack. In the
+end there is only one value left on the stack: The result of the
+expression.
+
+The I<rpn-expression> in the B<CDEF> function takes both, constant values
+as well as I<vname> variables. The following operators can be used on these
+values:
+
+=over
+
+=item +, -, *, /, %
+
+Pops two values from the stack applies the selected operator and pushes
+the result back onto the stack. The % operator stands for the modulo
+operation.
+
+=item SIN, COS, LOG, EXP, FLOOR, CEIL
+
+Pops one value from the stack, applies the selected function and pushes
+the result back onto the stack.
+
+=item LT, LE, GT, GE, EQ
+
+Pops two values from the stack, compares them according to the selected
+condition and pushes either 1 back onto the stack if the condition is true
+and 0 if the condition was not true.
+
+=item IF
+
+Pops three values from the stack. If the last value is not 0, the
+second value will be pushed back onto the stack, otherwise the
+first value is pushed back.
+
+If the stack contains the values A, B, C, D, E are presently on the
+stack, the IF operator will pop the values E D and C of the stack. It will
+look at C and if it is not 0 it will push D back onto the stack, otherwise
+E will be sent back to the stack.
+
+=item MIN, MAX
+
+Selects the lesser or larger of the two top stack values respectively
+
+=item LIMIT
+
+Replaces the value with I<*UNKNOWN*> if it is outside the limits specified
+by the two values above it on the stack.
+
+ CDEF:a=alpha,0,100,LIMIT
+
+=item DUP, EXC, POP
+
+These manipulate the stack directly.  DUP will duplicate the top of the
+stack, pushing the result back onto the stack.  EXC will exchange the top
+two elements of the stack, and POP will pop off the top element of the
+stack.  Having insufficient elements on the stack for these operations is
+an error.
+
+=item UN
+
+Pops one value off the stack, if it is I<*UNKNOWN*>, 1 will be pushed
+back otherwise 0.
+
+=item UNKN
+
+Push an I<*UNKNOWN*> value onto the stack.
+
+=item PREV
+
+Push I<*UNKNOWN*> if its at the first value of a data set or otherwise
+the value of this CDEF at the previous time step. This allows you to
+perform calculations across the data.
+
+=item COUNT
+
+Pushes the number 1 if it is at the first value of the data set, the
+number 2 if it is at the second, and so on. This special value, allows
+you to make calculations based on the position of the value within
+the data set.
+
+=item INF, NEGINF
+
+Push a positive or negative infinite (oo) value onto the stack. When
+drawing an infinite number it appears right at the top or bottom edge of the
+graph, depending whether you have a positive or negative infinite number.
+
+=item NOW
+
+Push the current (real world) time onto the stack.
+
+=item TIME
+
+Push the time the current sample was taken onto the stack. This is the
+number of non-skip seconds since 0:00:00 January 1, 1970.
+
+=item LTIME
+
+This is like TIME B<+ current timezone offset in seconds>. The current
+offset takes daylight saving time into account, given your OS supports
+this. If you were looking at a sample, in Zurich, in summer, the
+offset would be 2*3600 seconds, as Zurich at that time of year is 2
+hours ahead of UTC.
+
+Note that the timezone offset is always calculated for the time the
+current sample was taken at. It has nothing to do with the time you are
+doing the calculation.
+
+=back
+
+Please note that you may only use I<vname> variables that you
+previously defined by either B<DEF> or B<CDEF>. Furthermore, as of
+this writing (version 0.99.25), you must use at least one I<vname>
+per expression, that is "CDEF:fourtytwo=2,40,+" will yield an error
+message but not a I<vname> fourtytwo that's always equal to 42.
+
+=item B<PRINT:>I<vname>B<:>I<CF>B<:>I<format>
+
+Calculate the chosen consolidation function I<CF> over the data-source
+variable I<vname> and C<printf> the result to stdout using I<format>.
+In the I<format> string there should be a '%lf', '%le' or'%lg' marker in the
+place where the number should be printed.
+
+If an additional '%s' is found AFTER the marker, the value will be scaled
+and an appropriate SI magnitude unit will be printed in place of the '%s'
+marker. The scaling will take the '--base' argument into consideration!
+
+If a '%S' is used instead of a '%s', then instead of calculating the
+appropriate SI magnitude unit for this value, the previously calculated
+SI magnitude unit will be used.  This is useful if you want all the values
+in a PRINT statement to have the same SI magnitude unit.  If there was
+no previous SI magnitude calculation made, then '%S' behaves like a '%s',
+unless the value is 0, in which case it does not remember a SI magnitude
+unit and a SI magnitude unit will only be calculated when the next '%s' is
+seen or the next '%S' for a non-zero value.
+
+If you want to put a '%' into your PRINT string, use '%%' instead.
+
+=item B<GPRINT:>I<vname>B<:>I<CF>B<:>I<format>
+
+Same as B<PRINT> but the result is printed into the graph below the legend.
+
+=back
+
+B<Caveat:> When using the B<PRINT> and B<GRPRINT> functions to
+calculate data summaries over time periods bounded by the current
+time, it is important to note that the last sample will almost always
+yield a value of UNKNOWN as it lies after the last update time.  This
+can result in slight data skewing, particularly with the B<AVERAGE>
+function.  In order to avoid this, make sure that your end time is at
+least one heartbeat prior to the current time.
+
+=over
+
+
+=item B<COMMENT:>I<text>
+
+Like B<GPRINT> but the I<text> is simply printed into the graph.
+
+=item B<HRULE:>I<value>B<#>I<rrggbb>[B<:>I<legend>]
+
+Draw a horizontal rule into the graph and optionally add a legend
+
+=item B<VRULE:>I<time>B<#>I<rrggbb>[B<:>I<legend>]
+
+Draw a vertical rule into the graph and optionally add a legend
+
+=item B<LINE>{B<1>|B<2>|B<3>}B<:>I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]
+
+Plot for the requested data, using the color specified. Write a legend
+into the graph. The 3 possible keywords B<LINE1>, B<LINE2>, and B<LINE3>
+generate increasingly wide lines. If no color is defined,
+the drawing is done 'blind' this is useful in connection with the
+B<STACK> function when you want to ADD the values of two
+data-sources without showing it in the graph.
+
+=item B<AREA>:I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]
+
+Does the same as B<LINE?>, but the area between 0 and
+the graph will be filled with the color specified.
+
+=item B<STACK>:I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]
+
+Does the same as B<LINE?>, but the graph gets stacked on top of the previous
+B<LINE?>, B<AREA> or B<STACK> graph. Depending on the type of the
+previous graph, the B<STACK> will be either a B<LINE?> or an B<AREA>.
+This obviously implies that the first B<STACK> must be preceded by an
+B<AREA> or B<LINE?> -- you need something to stack something onto in
+the first place ;)
+
+Note, that when you STACK onto *UNKNOWN* data, RRDtool will not draw
+any graphics ... *UNKNOWN* is not zero ... if you want it to be zero
+then you might want to use a CDEF argument with IF and UN functions to
+turn *UNKNOWN* into zero ...
+
+=item B<TICK:>I<vname>B<#>I<rrggbb>[B<:>I<axis-fraction>[B<:>I<legend>]]
+
+Plot a tick mark (a vertical line) for each value of I<vname> that is
+non-zero and not *UNKNOWN*. The I<axis-fraction> argument specifies the
+length of the tick mark as a fraction of the y-axis; the default value
+is 0.1 (10% of the axis). Note that the color specification is not
+optional.
+
+=back
+
+=head1 NOTES on legend arguments
+
+=head2 Escaping the colon
+
+In a ':' in a I<legend> argument will mark the end of the legend. To
+enter a ':' into a legend, the colon must be escaped with a backslash '\:'.
+Beware, that many environments look for backslashes themselves, so it may
+be necessary to write two backslashes so that one is passed onto rrd_graph.
+
+=head2 String Formatting
+
+The text printed below the actual graph can be formated by appending special
+escaped characters at the end of a text. When ever such a character occurs,
+all pending text is pushed onto the graph according to the character
+specified.
+
+Valid markers are: B<\j> for justified, B<\l> for left aligned, B<\r> for
+right aligned and B<\c> for centered. In the next section there is an
+example showing how to use centered formating.
+
+Normally there are two space characters inserted between every two items
+printed into the graph. The space following a string can be suppressed by
+putting a B<\g> at the end of the string. The B<\g> also ignores any space
+inside the string if it is at the very end of the string. This can be used
+in connection with B<%s> to suppress empty unit strings.
+
+ GPRINT:a:MAX:%lf%s\g
+
+A special case is COMMENT:B<\s> this inserts some additional vertical space
+before placing the next row of legends.
+
+=head1 NOTE on Return Values
+
+Whenever rrd_graph gets called, it prints a line telling the size of
+the image it has just created to stdout. This line looks like this: XSIZExYSIZE.
+
+=head1 EXAMPLE 1
+
+  rrdtool graph demo.png --title="Demo Graph" \
+          DEF:cel=demo.rrd:exhaust:AVERAGE \
+          "CDEF:far=cel,1.8,*,32,+"" \
+          LINE2:cel#00a000:"D. Celsius" \
+          LINE2:far#ff0000:"D. Fahrenheit\c"
+
+=head1 EXAMPLE 2
+
+This example demonstrates the syntax for using IF and UN to set
+I<*UNKNOWN*> values to 0.  This technique is useful if you are
+aggregating interface data where the start dates of the data sets
+doesn't match.
+
+  rrdtool graph demo.png --title="Demo Graph" \
+         DEF:idat1=interface1.rrd:ds0:AVERAGE \
+         DEF:idat2=interface2.rrd:ds0:AVERAGE \
+         DEF:odat1=interface1.rrd:ds1:AVERAGE \
+         DEF:odat2=interface2.rrd:ds1:AVERAGE \
+         CDEF:agginput=idat1,UN,0,idat1,IF,idat2,UN,0,idat2,IF,+,8,* \
+         CDEF:aggoutput=odat1,UN,0,odat1,IF,odat2,UN,0,odat2,IF,+,8,* \
+         AREA:agginput#00cc00:Input Aggregate \
+         LINE1:agginput#0000FF:Output Aggregate
+
+Assuming that idat1 has a data value of I<*UNKNOWN*>, the CDEF expression
+
+ idat1,UN,0,idat1,IF
+
+leaves us with a stack with contents of 1,0,NaN and the IF function
+will pop off the 3 values and replace them with 0.  If idat1 had a
+real value like 7942099, then the stack would have 0,0,7942099 and the
+real value would be the replacement.
+
+=head1 EXAMPLE 3
+
+This example shows two ways to use the INF function. First it makes
+the background change color during half of the hours. Then, it uses
+AREA and STACK to draw a picture. If one of the inputs was UNKNOWN,
+all inputs are overlaid with another AREA.
+
+  rrdtool graph example.png --title="INF demo" \
+         DEF:val1=some.rrd:ds0:AVERAGE \
+         DEF:val2=some.rrd:ds1:AVERAGE \
+         DEF:val3=some.rrd:ds2:AVERAGE \
+         DEF:val4=other.rrd:ds0:AVERAGE \
+         CDEF:background=val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF \
+         CDEF:wipeout=val1,val2,val3,val4,+,+,+,UN,INF,UNKN,IF \
+         AREA:background#F0F0F0 \
+         AREA:val1#0000FF:Value1 \
+         STACK:val2#00C000:Value2 \
+         STACK:val3#FFFF00:Value3 \
+         STACK:val4#FFC000:Value4 \
+         AREA:wipeout#FF0000:Unknown
+
+The first CDEF uses val4 as a dummy value. It's value is removed immediately
+from the stack. Then a decision is made based on the time that a sample was
+taken. If it is an even hour (UTC time !) then the area will be filled. If
+it is not, the value is set to UNKN and is not plotted.
+
+The second CDEF looks if any of val1,val2,val3,val4 is unknown. It does so by
+checking the outcome of sum(val1,val2,val3,val4). Again, INF is returned when
+the condition is true, UNKN is used to not plot the data.
+
+The different items are plotted in a particular order. First do the background, then use a
+normal area to overlay it with data. Stack the other data until they are all plotted. Last but
+not least, overlay everything with eye-hurting red
+to signal any unknown data.
+
+Note that this example assumes that your data is in the positive half of the y-axis
+otherwise you would would have to add NEGINF in order to extend the coverage
+of the area to whole graph.
+
+=head1 EXAMPLE 4
+
+If the specialized function B<RRAs> exist for aberrant behavior detection, they
+can be used to generate the graph of a time series with confidence bands and
+failures.
+
+   rrdtool graph example.png \
+          DEF:obs=monitor.rrd:ifOutOctets:AVERAGE \
+          DEF:pred=monitor.rrd:ifOutOctets:HWPREDICT \
+          DEF:dev=monitor.rrd:ifOutOctets:DEVPREDICT \
+          DEF:fail=monitor.rrd:ifOutOctets:FAILURES \
+          TICK:fail#ffffa0:1.0:"Failures\: Average bits out" \
+          CDEF:scaledobs=obs,8,* \
+          CDEF:upper=pred,dev,2,*,+ \
+          CDEF:lower=pred,dev,2,*,- \
+          CDEF:scaledupper=upper,8,* \
+          CDEF:scaledlower=lower,8,* \
+          LINE2:scaledobs#0000ff:"Average bits out" \
+          LINE1:scaledupper#ff0000:"Upper Confidence Bound: Average bits out" \
+          LINE1:scaledlower#ff0000:"Lower Confidence Bound: Average bits out"
+
+This example generates a graph of the data series in blue (LINE2 with the scaledobs
+virtual data source), confidence bounds in red (scaledupper and scaledlower virtual
+data sources), and potential failures (i.e. potential aberrant aberrant behavior)
+marked by vertical yellow lines (the fail data source).
+
+The raw data comes from an AVERAGE B<RRA>, the finest resolution of the observed
+time series (one consolidated data point per primary data point). The predicted
+(or smoothed) values are stored in the HWPREDICT B<RRA>. The predicted deviations
+(think standard deviation) values are stored in the DEVPREDICT B<RRA>. Finally,
+the FAILURES B<RRA> contains indicators, with 1 denoting a potential failure.
+
+All of the data is rescaled to bits (instead of Octets) by multiplying by 8.
+The confidence bounds are computed by an offset of 2 deviations both above
+and below the predicted values (the CDEFs upper and lower). Vertical lines
+indicated potential failures are graphed via the TICK graph element, which
+converts non-zero values in an B<RRA> into tick marks. Here an axis-fraction
+argument of 1.0 means the tick marks span the entire y-axis, and hence become
+vertical lines on the graph.
+
+The choice of 2 deviations (a scaling factor) matches the default used internally
+by the FAILURES B<RRA>. If the internal value is changed (see L<rrdtune>), this
+graphing command should be changed to be consistent.
+
+=head2 A note on data reduction:
+
+The B<rrdtool> I<graph> command is designed to plot data at a specified temporal
+resolution, regardless of the actually resolution of the data in the RRD file.
+This can present a problem for the specialized consolidation functions which
+maintain a one-to-one mapping between primary data points and consolidated
+data points. If a graph insists on viewing the contents of these B<RRAs> on a
+coarser temporal scale, the I<graph> command tries to do something intelligent,
+but the confidence bands and failures no longer have the same meaning and may
+be misleading.
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
+=head1 REFERENCES
+
+[1] http://www.dotpoint.com/xnumber/rpn_or_adl.htm
diff --git a/program/doc/rrdgraph.pod b/program/doc/rrdgraph.pod
new file mode 100644 (file)
index 0000000..fbc217b
--- /dev/null
@@ -0,0 +1,558 @@
+=head1 NAME
+
+rrdgraph - Round Robin Database tool graphing functions
+
+=head1 SYNOPSIS
+
+B<rrdtool graph|graphv> I<filename>
+[I<L<option|rrdgraph/OPTIONS>> ...]
+[I<L<data definition|rrdgraph_data/DEF>> ...]
+[I<L<data calculation|rrdgraph_data/CDEF>> ...]
+[I<L<variable definition|rrdgraph_data/VDEF>> ...]
+[I<L<graph element|rrdgraph_graph/GRAPH>> ...]
+[I<L<print element|rrdgraph_graph/PRINT>> ...]
+
+=head1 DESCRIPTION
+
+The B<graph> function of B<RRDtool> is used to present the
+data from an B<RRD> to a human viewer.  Its main purpose is to
+create a nice graphical representation, but it can also generate
+a numerical report.
+
+=head1 OVERVIEW
+
+B<rrdtool graph> needs data to work with, so you must use one or more
+B<L<data definition|rrdgraph_data/DEF>> statements to collect this
+data.  You are not limited to one database, it's perfectly legal to
+collect data from two or more databases (one per statement, though).
+
+If you want to display averages, maxima, percentiles, etcetera
+it is best to collect them now using the
+B<L<variable definition|rrdgraph_data/VDEF>> statement.
+Currently this makes no difference, but in a future version
+of RRDtool you may want to collect these values before consolidation.
+
+The data fetched from the B<RRA> is then B<consolidated> so that
+there is exactly one data point per pixel in the graph. If you do
+not take care yourself, B<RRDtool> will expand the range slightly
+if necessary. Note, in that case the first and/or last pixel may very
+well become unknown!
+
+Sometimes data is not exactly in the format you would like to display
+it. For instance, you might be collecting B<bytes> per second, but
+want to display B<bits> per second. This is what the B<L<data
+calculation|rrdgraph_data/CDEF>> command is designed for. After
+B<consolidating> the data, a copy is made and this copy is modified
+using a rather powerful B<L<RPN|rrdgraph_rpn>> command set.
+
+When you are done fetching and processing the data, it is time to
+graph it (or print it).  This ends the B<rrdtool graph> sequence.
+
+Use B<graphv> instead of B<graph> to get detailed information about the
+graph geometry and data once it is drawn. See the bottom of the document for
+more information.
+
+=head1 OPTIONS
+
+
+
+=head2 I<filename>
+
+The name and path of the graph to generate. It is recommended to
+end this in C<.png>, C<.svg> or C<.eps>, but B<RRDtool> does not enforce this.
+
+I<filename> can be 'C<->' to send the image to C<stdout>. In
+this case, no other output is generated.
+
+=head2 Time range
+
+[B<-s>|B<--start> I<time>]
+[B<-e>|B<--end> I<time>]
+[B<-S>|B<--step> I<seconds>]
+
+The start and end of the time series you would like to display, and which
+B<RRA> the data should come from.  Defaults are: 1 day ago until
+now, with the best possible resolution. B<Start> and B<end> can
+be specified in several formats, see
+L<AT-STYLE TIME SPECIFICATION|rrdfetch> and L<rrdgraph_examples>.
+By default, B<rrdtool graph> calculates the width of one pixel in
+the time domain and tries to get data from an B<RRA> with that
+resolution.  With the B<step> option you can alter this behavior.
+If you want B<rrdtool graph> to get data at a one-hour resolution
+from the B<RRD>, set B<step> to 3'600. Note: a step smaller than
+one pixel will silently be ignored.
+
+=head2 Labels
+
+[B<-t>|B<--title> I<string>]
+[B<-v>|B<--vertical-label> I<string>]
+
+A horizontal string at the top of the graph and/or a vertically
+placed string at the left hand side of the graph.
+
+
+=head2 Size
+
+[B<-w>|B<--width> I<pixels>]
+[B<-h>|B<--height> I<pixels>]
+[B<-j>|B<--only-graph>]
+[B<-D>|B<--full-size-mode>]
+
+By default, the width and height of the B<canvas> (the part with
+the actual data and such). This defaults to 400 pixels by 100 pixels.
+
+If you specify the B<--full-size-mode> option, the width and height
+specify the final dimensions of the output image and the canvas
+is automatically resized to fit.
+
+If you specify the B<--only-graph> option and set the height E<lt> 32
+pixels you will get a tiny graph image (thumbnail) to use as an icon
+for use in an overview, for example. All labeling will be stripped off
+the graph.
+
+=head2 Limits
+
+[B<-u>|B<--upper-limit> I<value>]
+[B<-l>|B<--lower-limit> I<value>]
+[B<-r>|B<--rigid>]
+
+By default the graph will be autoscaling so that it will adjust the
+y-axis to the range of the data. You can change this behavior by
+explicitly setting the limits. The displayed y-axis will then range at
+least from B<lower-limit> to B<upper-limit>. Autoscaling will still
+permit those boundaries to be stretched unless the B<rigid> option is
+set.
+
+[B<-A>|B<--alt-autoscale>]
+
+Sometimes the default algorithm for selecting the y-axis scale is not
+satisfactory. Normally the scale is selected from a predefined
+set of ranges and this fails miserably when you need to graph something
+like C<260 + 0.001 * sin(x)>. This option calculates the minimum and
+maximum y-axis from the actual minimum and maximum data values. Our example
+would display slightly less than C<260-0.001> to slightly more than
+C<260+0.001> (this feature was contributed by Sasha Mikheev).
+
+[B<-J>|B<--alt-autoscale-min>]
+
+Where C<--alt-autoscale> will modify both the absolute maximum AND minimum
+values, this option will only affect the minimum value. The maximum
+value, if not defined on the command line, will be 0. This option can
+be useful when graphing router traffic when the WAN line uses compression,
+and thus the throughput may be higher than the WAN line speed.
+
+[B<-M>|B<--alt-autoscale-max>]
+
+Where C<--alt-autoscale> will modify both the absolute maximum AND minimum
+values, this option will only affect the maximum value. The minimum
+value, if not defined on the command line, will be 0. This option can
+be useful when graphing router traffic when the WAN line uses compression,
+and thus the throughput may be higher than the WAN line speed.
+
+[B<-N>|B<--no-gridfit>]
+
+In order to avoid anti-aliasing blurring effects RRDtool snaps
+points to device resolution pixels, this results in a crisper
+appearance. If this is not to your liking, you can use this switch
+to turn this behavior off.
+
+Grid-fitting is turned off for PDF, EPS, SVG output by default.
+
+=head2 X-Axis
+
+[B<-x>|B<--x-grid> I<GTM>B<:>I<GST>B<:>I<MTM>B<:>I<MST>B<:>I<LTM>B<:>I<LST>B<:>I<LPR>B<:>I<LFM>]
+
+[B<-x>|B<--x-grid> B<none>]
+
+The x-axis label is quite complex to configure. If you don't have
+very special needs it is probably best to rely on the auto configuration
+to get this right. You can specify the string C<none> to suppress the grid
+and labels altogether.
+
+The grid is defined by specifying a certain amount of time in the I<?TM>
+positions. You can choose from C<SECOND>, C<MINUTE>, C<HOUR>, C<DAY>,
+C<WEEK>, C<MONTH> or C<YEAR>. Then you define how many of these should
+pass between each line or label.  This pair (I<?TM:?ST>) needs to be
+specified for the base grid (I<G??>), the major grid (I<M??>) and the
+labels (I<L??>). For the labels you also must define a precision
+in I<LPR> and a I<strftime> format string in I<LFM>.  I<LPR> defines
+where each label will be placed. If it is zero, the label will be
+placed right under the corresponding line (useful for hours, dates
+etcetera).  If you specify a number of seconds here the label is
+centered on this interval (useful for Monday, January etcetera).
+
+ --x-grid MINUTE:10:HOUR:1:HOUR:4:0:%X
+
+This places grid lines every 10 minutes, major grid lines every hour,
+and labels every 4 hours. The labels are placed under the major grid
+lines as they specify exactly that time.
+
+ --x-grid HOUR:8:DAY:1:DAY:1:86400:%A
+
+This places grid lines every 8 hours, major grid lines and labels
+each day. The labels are placed exactly between two major grid lines
+as they specify the complete day and not just midnight.
+
+=head2 Y-Axis
+
+[B<-y>|B<--y-grid> I<grid step>B<:>I<label factor>]
+
+[B<-y>|B<--y-grid> B<none>]
+
+Y-axis grid lines appear at each I<grid step> interval.  Labels are
+placed every I<label factor> lines.  You can specify C<-y none> to
+suppress the grid and labels altogether.  The default for this option is
+to automatically select sensible values.
+
+If you have set --y-grid to 'none' not only the labels get suppressed, also
+the space reserved for the labels is removed. You can still add space
+manually if you use the --units-length command to explicitly reserve space.
+
+[B<-Y>|B<--alt-y-grid>]
+
+Place the Y grid dynamically based on the graph's Y range. The algorithm
+ensures that you always have a grid, that there are enough but not too many
+grid lines, and that the grid is metric. That is the grid lines are placed
+every 1, 2, 5 or 10 units. This parameter will also ensure that you get
+enough decimals displayed even if your graph goes from 69.998 to 70.001. 
+(contributed by Sasha Mikheev).
+
+[B<-o>|B<--logarithmic>]
+
+Logarithmic y-axis scaling.
+
+[B<-X>|B<--units-exponent> I<value>]
+
+This sets the 10**exponent scaling of the y-axis values. Normally,
+values will be scaled to the appropriate units (k, M, etc.).  However,
+you may wish to display units always in k (Kilo, 10e3) even if the data
+is in the M (Mega, 10e6) range, for instance. Value should be an
+integer which is a multiple of 3 between -18 and 18 inclusively.  It is
+the exponent on the units you wish to use. For example, use 3 to
+display the y-axis values in k (Kilo, 10e3, thousands), use -6 to
+display the y-axis values in u (Micro, 10e-6, millionths).  Use a value
+of 0 to prevent any scaling of the y-axis values.
+
+This option is very effective at confusing the heck out of the default
+RRDtool autoscaling function and grid painter. If RRDtool detects that it is not
+successful in labeling the graph under the given circumstances, it will switch
+to the more robust B<--alt-y-grid> mode.
+
+[B<-L>|B<--units-length> I<value>]
+
+How many digits should RRDtool assume the y-axis labels to be? You
+may have to use this option to make enough space once you start
+fiddling with the y-axis labeling.
+
+[B<--units=si>]
+
+With this option y-axis values on logarithmic graphs will be scaled to 
+the appropriate units (k, M, etc.) instead of using exponential notation.
+Note that for linear graphs, SI notation is used by default.
+
+=head2 Right Y Axis
+
+[B<--right-axis> I<scale>B<:>I<shift>]
+[B<--right-axis-label> I<label>]
+
+A second axis will be drawn to the right of the graph. It is tied to the
+left axis via the scale and shift parameters. You can also define a label
+for the right axis.
+
+[B<--right-axis-format> I<format-string>]
+
+By default the format of the axis labels gets determined automatically. If
+you want to do this your self, use this option with the same %lf arguments
+you know from the PRING and GPRINT commands.
+
+=head2 Legend
+
+[B<-g>|B<--no-legend>]
+
+Suppress generation of the legend; only render the graph.
+
+[B<-F>|B<--force-rules-legend>]
+
+Force the generation of HRULE and VRULE legends even if those HRULE or
+VRULE will not be drawn because out of graph boundaries (mimics
+behavior of pre 1.0.42 versions).
+
+[B<--legend-position>=(north|south|west|east)]
+
+Place the legend at the given side of the graph. The default is south.
+In west or east position it is necessary to add line breaks manually.
+
+[B<--legend-direction>=(topdown|bottomup)]
+
+Place the legend items in the given vertical order. The default is topdown.
+Using bottomup the legend items appear in the same vertical order as a
+stack of lines or areas.
+
+=head2 Miscellaneous
+
+[B<-z>|B<--lazy>]
+
+Only generate the graph if the current graph is out of date or not existent.
+Note, that all the calculations will happen regardless so that the output of
+PRINT and graphv will be complete regardless. Note that the behavior of
+lazy in this regard has seen several changes over time. The only thing you
+can really rely on before RRDtool 1.3.7 is that lazy will not generate the
+graph when it is already there and up to date, and also that it will output
+the size of the graph. 
+
+[B<--daemon> I<address>]
+
+Address of the L<rrdcached> daemon. If specified, a C<flush> command is sent
+to the server before reading the RRD files. This allows the graph to contain
+fresh data even if the daemon is configured to cache values for a long time.
+For a list of accepted formats, see the B<-l> option in the L<rrdcached> manual.
+
+ rrdtool graph [...] --daemon unix:/var/run/rrdcached.sock [...]
+
+[B<-f>|B<--imginfo> I<printfstr>]
+
+After the image has been created, the graph function uses printf
+together with this format string to create output similar to the PRINT
+function, only that the printf function is supplied with the parameters
+I<filename>, I<xsize> and I<ysize>. In order to generate an B<IMG> tag
+suitable for including the graph into a web page, the command line
+would look like this:
+
+ --imginfo '<IMG SRC="/img/%s" WIDTH="%lu" HEIGHT="%lu" ALT="Demo">'
+
+[B<-c>|B<--color> I<COLORTAG>#I<rrggbb>[I<aa>]]
+
+Override the default colors for the standard elements of the graph. The
+I<COLORTAG> is one of C<BACK> background, C<CANVAS> for the background of
+the actual graph, C<SHADEA> for the left and top border, C<SHADEB> for the
+right and bottom border, C<GRID>, C<MGRID> for the major grid, C<FONT> for
+the color of the font, C<AXIS> for the axis of the graph, C<FRAME> for the
+line around the color spots, and finally C<ARROW> for the arrow head pointing
+up and forward. Each color is composed out of three hexadecimal numbers
+specifying its rgb color component (00 is off, FF is maximum) of red, green
+and blue. Optionally you may add another hexadecimal number specifying the
+transparency (FF is solid). You may set this option several times to alter
+multiple defaults.
+
+A green arrow is made by: C<--color ARROW#00FF00>
+
+[B<--grid-dash> I<on>B<:>I<off>]
+
+by default the grid is drawn in a 1 on, 1 off pattern. With this option you can set this yourself
+
+ --grid-dash 1:3    for a dot grid
+ --grid-dash 1:0    for uninterrupted grid lines
+
+[B<--border> I<width>]]
+
+Width in pixels for the 3d border drawn around the image. Default 2, 0
+disables the border. See C<SHADEA> and C<SHADEB> above for setting the border
+color.
+
+[B<--dynamic-labels>]
+
+Pick the shape of the color marker next to the label according to the element drawn on the graph.
+
+[B<-m>|B<--zoom> I<factor>]
+
+Zoom the graphics by the given amount. The factor must be E<gt> 0
+
+[B<-n>|B<--font> I<FONTTAG>B<:>I<size>B<:>[I<font>]]
+
+This lets you customize which font to use for the various text elements on
+the RRD graphs. C<DEFAULT> sets the default value for all elements, C<TITLE>
+for the title, C<AXIS> for the axis labels, C<UNIT> for the vertical unit
+label, C<LEGEND> for the graph legend, C<WATERMARK> for the watermark on the
+edge of the graph.
+
+Use Times for the title: C<--font TITLE:13:Times>
+
+Note that you need to quote the argument to B<--font> if the font-name
+contains whitespace:
+--font "TITLE:13:Some Font"
+
+If you do not give a font string you can modify just the size of the default font:
+C<--font TITLE:13:>.
+
+If you specify the size 0 then you can modify just the font without touching
+the size. This is especially useful for altering the default font without
+resetting the default fontsizes: C<--font DEFAULT:0:Courier>.
+
+RRDtool comes with a preset default font. You can set the environment
+variable C<RRD_DEFAULT_FONT> if you want to change this.
+
+RRDtool uses Pango for its font handling. This means you can to use
+the full Pango syntax when selecting your font:
+
+The font name has the form "[I<FAMILY-LIST>] [I<STYLE-OPTIONS>] [I<SIZE>]",
+where I<FAMILY-LIST> is a comma separated list of families optionally
+terminated by a comma, I<STYLE_OPTIONS> is a whitespace separated list of
+words where each WORD describes one of style, variant, weight, stretch, or
+gravity, and I<SIZE> is a decimal number (size in points) or optionally
+followed by the unit modifier "px" for absolute size. Any one of the options
+may be absent.
+
+[B<-R>|B<--font-render-mode> {B<normal>,B<light>,B<mono>}]
+
+There are 3 font render modes:
+
+B<normal>: Full Hinting and Anti-aliasing (default)
+
+B<light>: Slight Hinting and Anti-aliasing
+
+B<mono>: Full Hinting and NO Anti-aliasing
+
+
+[B<-B>|B<--font-smoothing-threshold> I<size>]
+
+(this gets ignored in 1.3 for now!)
+
+This specifies the largest font size which will be rendered
+bitmapped, that is, without any font smoothing. By default,
+no text is rendered bitmapped.
+
+[B<-P>|B<--pango-markup>]
+
+All text in RRDtool is rendered using Pango. With the B<--pango-markup> option, all
+text will be processed by pango markup. This allows to embed some simple html
+like markup tags using 
+
+ <span key="value">text</span>
+
+Apart from the verbose syntax, there are also the following short tags available.
+
+ b     Bold
+ big   Makes font relatively larger, equivalent to <span size="larger">
+ i     Italic
+ s     Strikethrough
+ sub   Subscript
+ sup   Superscript
+ small Makes font relatively smaller, equivalent to <span size="smaller">
+ tt    Monospace font
+ u     Underline 
+
+More details on L<http://developer.gnome.org/doc/API/2.0/pango/PangoMarkupFormat.html>.
+
+[B<-G>|B<--graph-render-mode> {B<normal>,B<mono>}]
+
+There are 2 render modes:
+
+B<normal>: Graphs are fully Anti-aliased (default)
+
+B<mono>: No Anti-aliasing
+
+[B<-E>|B<--slope-mode>]
+
+RRDtool graphs are composed of stair case curves by default. This is in line with
+the way RRDtool calculates its data. Some people favor a more 'organic' look
+for their graphs even though it is not all that true.
+
+[B<-a>|B<--imgformat> B<PNG>|B<SVG>|B<EPS>|B<PDF>]
+
+Image format for the generated graph. For the vector formats you can
+choose among the standard Postscript fonts Courier-Bold,
+Courier-BoldOblique, Courier-Oblique, Courier, Helvetica-Bold,
+Helvetica-BoldOblique, Helvetica-Oblique, Helvetica, Symbol,
+Times-Bold, Times-BoldItalic, Times-Italic, Times-Roman, and ZapfDingbats.
+
+[B<-i>|B<--interlaced>]
+
+(this gets ignored in 1.3 for now!)
+
+If images are interlaced they become visible on browsers more quickly.
+
+[B<-T>|B<--tabwidth> I<value>]
+
+By default the tab-width is 40 pixels, use this option to change it.
+
+[B<-b>|B<--base> I<value>]
+
+If you are graphing memory (and NOT network traffic) this switch
+should be set to 1024 so that one Kb is 1024 byte. For traffic
+measurement, 1 kb/s is 1000 b/s.
+
+[B<-W>|B<--watermark> I<string>]
+
+Adds the given string as a watermark, horizontally centered, at the bottom 
+of the graph.
+
+=head2 Data and variables
+
+B<DEF:>I<vname>B<=>I<rrdfile>B<:>I<ds-name>B<:>I<CF>[B<:step=>I<step>][B<:start=>I<time>][B<:end=>I<time>]
+
+B<CDEF:>I<vname>B<=>I<RPN expression>
+
+B<VDEF:>I<vname>B<=>I<RPN expression>
+
+You need at least one B<DEF> statement to generate anything. The
+other statements are useful but optional.
+See L<rrdgraph_data> and L<rrdgraph_rpn> for the exact format.
+
+NOTE: B<Graph and print elements>
+
+You need at least one graph element to generate an image and/or
+at least one print statement to generate a report.
+See L<rrdgraph_graph> for the exact format.
+
+=head2 graphv
+
+Calling RRDtool with the graphv option will return information in the
+RRDtool info format. On the command line this means that all output will be
+in key=value format. When used from the Perl and Ruby bindings a hash
+pointer will be returned from the call.
+
+When the filename '-' is given, the contents of the graph itself will also
+be returned through this interface (hash key 'image'). On the command line
+the output will look like this:
+
+ print[0] = "0.020833"
+ print[1] = "0.0440833"
+ graph_left = 51
+ graph_top = 22
+ graph_width = 400
+ graph_height = 100
+ graph_start = 1232908800
+ graph_end = 1232914200
+ image_width = 481
+ image_height = 154
+ value_min = 0.0000000000e+00
+ value_max = 4.0000000000e-02
+ image = BLOB_SIZE:8196
+ [... 8196 bytes of image data ...]
+
+There is more information returned than in the standard interface.
+Especially the 'graph_*' keys are new. They help applications that want to
+know what is where on the graph.
+
+=head1 ENVIRONMENT VARIABLES
+
+The following environment variables may be used to change the behavior of
+C<rrdtoolE<nbsp>graph>:
+
+=over 4
+
+=item B<RRDCACHED_ADDRESS>
+
+If this environment variable is set it will have the same effect as specifying
+the C<--daemon> option on the command line. If both are present, the command
+line argument takes precedence.
+
+=back
+
+=head1 SEE ALSO
+
+L<rrdgraph> gives an overview of how B<rrdtool graph> works.
+L<rrdgraph_data> describes B<DEF>,B<CDEF> and B<VDEF> in detail.
+L<rrdgraph_rpn> describes the B<RPN> language used in the B<?DEF> statements.
+L<rrdgraph_graph> page describes all of the graph and print functions.
+
+Make sure to read L<rrdgraph_examples> for tipsE<amp>tricks.
+
+=head1 AUTHOR
+
+Program by Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
+This manual page by Alex van den Bogaerdt E<lt>alex@vandenbogaerdt.nlE<gt>
+with corrections and/or additions by several people
+
diff --git a/program/doc/rrdgraph_data.pod b/program/doc/rrdgraph_data.pod
new file mode 100644 (file)
index 0000000..d5ceddb
--- /dev/null
@@ -0,0 +1,135 @@
+=head1 NAME
+
+rrdgraph_data - preparing data for graphing in rrdtool graph
+
+=head1 SYNOPSIS
+
+B<DEF:>I<E<lt>vnameE<gt>>=I<E<lt>rrdfileE<gt>>:I<E<lt>ds-nameE<gt>>:I<E<lt>CFE<gt>>[:step=I<E<lt>stepE<gt>>][:start=I<E<lt>timeE<gt>>][:end=I<E<lt>timeE<gt>>][:reduce=I<E<lt>B<CF>E<gt>>]
+
+B<VDEF>:I<vname>=I<RPN expression>
+
+B<CDEF>:I<vname>=I<RPN expression>
+
+=head1 DESCRIPTION
+
+These three instructions extract data values out of the B<RRD> files,
+optionally altering them (think, for example, of a bytes to bits
+conversion). If so desired, you can also define variables containing
+useful information such as maximum, minimum etcetera. Two of the
+instructions use a language called B<RPN> which is described in its
+own manual page.
+
+Variable names (I<vname>) must be made up strings of the following characters
+C<A-Z, a-z, 0-9, -,_> and a maximum length of 255 characters.
+
+When picking variable names, make sure you do not choose a name that is
+already taken by an RPN operator. A safe bet it to use lowercase or
+mixed case names for variables since operators will always be in uppercase.
+
+=head1 DEF
+
+B<DEF:>I<E<lt>vnameE<gt>>=I<E<lt>rrdfileE<gt>>:I<E<lt>ds-nameE<gt>>:I<E<lt>CFE<gt>>[:step=I<E<lt>stepE<gt>>][:start=I<E<lt>timeE<gt>>][:end=I<E<lt>timeE<gt>>][:reduce=I<E<lt>B<CF>E<gt>>]
+
+This command fetches data from an B<RRD> file.  The virtual name
+I<vname> can then be used throughout the rest of the script. By
+default, an B<RRA> which contains the correct consolidated data
+at an appropriate resolution will be chosen.  The resolution can
+be overridden with the L<--step|rrdgraph/item_Time> option.
+The resolution can again be overridden by specifying the B<step size>.
+The time span of this data is the same as for the graph by default,
+you can override this by specifying B<start and end>.  Remember to
+escape colons in the time specification!
+
+If the resolution of the data is higher than the resolution of the
+graph, the data will be further consolidated. This may result in
+a graph that spans slightly more time than requested.
+Ideally each point in the graph should correspond with one B<CDP>
+from an B<RRA>.  For instance, if your B<RRD> has an B<RRA> with
+a resolution of 1800 seconds per B<CDP>, you should create an
+image with width 400 and time span 400*1800 seconds (use appropriate
+start and end times, such as C<--start end-8days8hours>).
+
+If consolidation needs to be done, the B<CF> of the B<RRA> specified in the
+B<DEF> itself will be used to reduce the data density. This behavior can
+be changed using C<:reduce=I<E<lt>B<CF>E<gt>>>.  This optional parameter
+specifies the B<CF> to use during the data reduction phase.
+
+Example:
+
+        DEF:ds0=router.rrd:ds0:AVERAGE
+        DEF:ds0weekly=router.rrd:ds0:AVERAGE:step=7200
+        DEF:ds0weekly=router.rrd:ds0:AVERAGE:start=end-1h
+        DEF:ds0weekly=router.rrd:ds0:AVERAGE:start=11\:00:end=start+1h
+
+=head1 VDEF
+
+B<VDEF>:I<vname>=I<RPN expression>
+
+This command returns a value and/or a time according to the B<RPN>
+statements used. The resulting I<vname> will, depending on the
+functions used, have a value and a time component.  When you use
+this I<vname> in another B<RPN> expression, you are effectively
+inserting its value just as if you had put a number at that place.
+The variable can also be used in the various graph and print
+elements.
+
+Example: C<VDEF:avg=mydata,AVERAGE>
+
+Note that currently only aggregation functions work in VDEF rpn expressions.
+Patches to change this are welcome.
+
+=head1 CDEF
+
+B<CDEF>:I<vname>=I<RPN expression>
+
+This command creates a new set of data points (in memory only, not
+in the B<RRD> file) out of one or more other data series. The B<RPN>
+instructions are used to evaluate a mathematical function on each
+data point. The resulting I<vname> can then be used further on in
+the script, just as if it were generated by a B<DEF> instruction.
+
+Example: C<CDEF:mydatabits=mydata,8,*>
+
+=head1 About CDEF versus VDEF
+
+At some point in processing, B<RRDtool> has gathered an array of rates
+ready to display.
+
+B<CDEF> works on such an array.  For example, I<CDEF:new=ds0,8,*>
+would multiply each of the array members by eight (probably
+transforming bytes into bits). The result is an array containing the
+new values.
+
+B<VDEF> also works on such an array but in a different way.  For
+example, I<VDEF:max=ds0,MAXIMUM> would scan each of the array members
+and store the maximum value.
+
+=head2 When do you use B<VDEF> versus B<CDEF>?  
+
+Use B<CDEF> to transform your data prior to graphing.  In the above
+example, we'd use a B<CDEF> to transform bytes to bits before
+graphing the bits.
+
+You use a B<VDEF> if you want I<max(1,5,3,2,4)> to return five which
+would be displayed in the graph's legend (to answer, what was the
+maximum value during the graph period).
+
+If you want to apply 'complex' operations to the result of a VDEF you have
+to use a CDEF again since VDEFs only look like RPN expressions, they aren't
+really.
+
+=head1 SEE ALSO
+
+L<rrdgraph> gives an overview of how B<rrdtool graph> works.
+L<rrdgraph_data> describes B<DEF>,B<CDEF> and B<VDEF> in detail.
+L<rrdgraph_rpn> describes the B<RPN> language used in the B<?DEF> statements.
+L<rrdgraph_graph> page describes all of the graph and print functions.
+
+Make sure to read L<rrdgraph_examples> for tipsE<amp>tricks.
+
+=head1 AUTHOR
+
+Program by Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
+This manual page by Alex van den Bogaerdt E<lt>alex@vandenbogaerdt.nlE<gt>
+with corrections and/or additions by several people
diff --git a/program/doc/rrdgraph_examples.pod b/program/doc/rrdgraph_examples.pod
new file mode 100644 (file)
index 0000000..b544275
--- /dev/null
@@ -0,0 +1,210 @@
+=head1 NAME
+
+rrdgraph_examples - Examples for rrdtool graph
+
+=head1 SYNOPSIS
+
+B<rrdtool graph /home/httpd/html/test.png --img-format PNG>
+
+followed by any of the examples below
+
+=head1 DESCRIPTION
+
+For your convenience some of the commands are explained here
+by using detailed examples. They are not always cut-and-paste
+ready because comments are intermixed with the examples.
+
+=head1 EXAMPLES
+
+=head2 Data with multiple resolutions
+
+    --end now --start end-120000s --width 400
+    DEF:ds0a=/home/rrdtool/data/router1.rrd:ds0:AVERAGE
+    DEF:ds0b=/home/rrdtool/data/router1.rrd:ds0:AVERAGE:step=1800
+    DEF:ds0c=/home/rrdtool/data/router1.rrd:ds0:AVERAGE:step=7200
+    LINE1:ds0a#0000FF:"default resolution\l"
+    LINE1:ds0b#00CCFF:"resolution 1800 seconds per interval\l"
+    LINE1:ds0c#FF00FF:"resolution 7200 seconds per interval\l"
+
+=head2 Nicely formatted legend section
+
+    DEF:ds0=/home/rrdtool/data/router1.rrd:ds0:AVERAGE
+    DEF:ds1=/home/rrdtool/data/router1.rrd:ds1:AVERAGE
+    VDEF:ds0max=ds0,MAXIMUM
+    VDEF:ds0avg=ds0,AVERAGE
+    VDEF:ds0min=ds0,MINIMUM
+    VDEF:ds0pct=ds0,95,PERCENT
+    VDEF:ds1max=ds1,MAXIMUM
+    VDEF:ds1avg=ds1,AVERAGE
+    VDEF:ds1min=ds1,MINIMUM
+    VDEF:ds1pct=ds1,95,PERCENT
+
+Note: consolidation occurs here.
+
+    CDEF:ds0bits=ds0,8,*
+    CDEF:ds1bits=ds1,8,*
+
+Note: 10 spaces to move text to the right
+
+    COMMENT:"          "
+
+Note: the column titles have to be as wide as the columns
+
+    COMMENT:"Maximum    "
+    COMMENT:"Average    "
+    COMMENT:"Minimum    "
+
+    COMMENT:"95th percentile\l"
+    AREA:ds0bits#00C000:"Inbound "
+    GPRINT:ds0max:"%6.2lf %Sbps"
+    GPRINT:ds0avg:"%6.2lf %Sbps"
+    GPRINT:ds0min:"%6.2lf %Sbps"
+    GPRINT:ds0pct:"%6.2lf %Sbps\l"
+    LINE1:ds1bits#0000FF:"Outbound"
+    GPRINT:ds1max:"%6.2lf %Sbps"
+    GPRINT:ds1avg:"%6.2lf %Sbps"
+    GPRINT:ds1min:"%6.2lf %Sbps"
+    GPRINT:ds1pct:"%6.2lf %Sbps\l"
+
+=head2 Offsetting a line on the y-axis
+
+Depending on your needs you can do this in two ways:
+
+=over
+
+=item *
+
+Offset the data, then graph this
+
+    DEF:mydata=my.rrd:ds:AVERAGE
+
+Note: this will also influence any other command that uses "data"
+
+    CDEF:data=mydata,100,+
+    LINE1:data#FF0000:"Data with offset"
+
+=item *
+
+Graph the original data, with an offset
+
+    DEF:mydata=my.rrd:ds:AVERAGE
+
+Note: no color in the first line so it is not visible
+
+    LINE1:100
+
+Note: the second line gets stacked on top of the first one
+
+    LINE1:data#FF0000:"Data with offset":STACK
+
+=back
+
+=head2 Drawing dashed lines
+
+Also works for HRULE and VRULE
+
+=over
+
+=item *
+
+default style: - - - - -
+    LINE1:data#FF0000:"dashed line":dashes
+
+=item *
+
+more fancy style with offset: - -  --- -  --- -
+    LINE1:data#FF0000:"another dashed line":dashes=15,5,5,10:dash-offset=10
+
+=back
+
+=head2 Time ranges
+
+    Last four weeks: --start end-4w --end 00:00
+    January 2001:    --start 20010101 --end start+31d
+    January 2001:    --start 20010101 --end 20010201
+    Last hour:       --start end-1h
+    Last 24 hours:   <nothing at all>
+    Yesterday:       --end 00:00
+
+=head2 Viewing the current and previous week together
+
+    --end now --start end-1w
+    DEF:thisweek=router.rrd:ds0:AVERAGE
+    DEF:lastweek=router.rrd:ds0:AVERAGE:end=now-1w:start=end-1w
+
+Shift the data forward by one week (604800 seconds)
+
+    SHIFT:lastweek:604800
+    [ more of the usual VDEF and CDEF stuff if you like ]
+    AREA:lastweek#0000FF:Last\ week
+    LINE1:thisweek#FF0000:This\ week
+
+=head2 Aberrant Behaviour Detection
+
+If the specialized function B<RRAs> exist for aberrant behavior detection, they
+can be used to generate the graph of a time series with confidence bands and
+failures.
+
+   rrdtool graph example.png \
+          DEF:obs=monitor.rrd:ifOutOctets:AVERAGE \
+          DEF:pred=monitor.rrd:ifOutOctets:HWPREDICT \
+          DEF:dev=monitor.rrd:ifOutOctets:DEVPREDICT \
+          DEF:fail=monitor.rrd:ifOutOctets:FAILURES \
+          TICK:fail#ffffa0:1.0:"Failures\: Average bits out" \
+          CDEF:scaledobs=obs,8,* \
+          CDEF:upper=pred,dev,2,*,+ \
+          CDEF:lower=pred,dev,2,*,- \
+          CDEF:scaledupper=upper,8,* \
+          CDEF:scaledlower=lower,8,* \
+          LINE2:scaledobs#0000ff:"Average bits out" \
+          LINE1:scaledupper#ff0000:"Upper Confidence Bound: Average bits out" \
+          LINE1:scaledlower#ff0000:"Lower Confidence Bound: Average bits out"
+
+This example generates a graph of the data series in blue (LINE2 with the scaledobs
+virtual data source), confidence bounds in red (scaledupper and scaledlower virtual
+data sources), and potential failures (i.e. potential aberrant aberrant behavior)
+marked by vertical yellow lines (the fail data source).
+
+The raw data comes from an AVERAGE B<RRA>, the finest resolution of the observed
+time series (one consolidated data point per primary data point). The predicted
+(or smoothed) values are stored in the HWPREDICT B<RRA>. The predicted deviations
+(think standard deviation) values are stored in the DEVPREDICT B<RRA>. Finally,
+the FAILURES B<RRA> contains indicators, with 1 denoting a potential failure.
+
+All of the data is rescaled to bits (instead of Octets) by multiplying by 8.
+The confidence bounds are computed by an offset of 2 deviations both above
+and below the predicted values (the CDEFs upper and lower). Vertical lines
+indicated potential failures are graphed via the TICK graph element, which
+converts non-zero values in an B<RRA> into tick marks. Here an axis-fraction
+argument of 1.0 means the tick marks span the entire y-axis, and hence become
+vertical lines on the graph.
+
+The choice of 2 deviations (a scaling factor) matches the default used internally
+by the FAILURES B<RRA>. If the internal value is changed (see L<rrdtune>), this
+graphing command should be changed to be consistent.
+
+=head3 A note on data reduction:
+
+The B<rrdtool> I<graph> command is designed to plot data at a specified temporal
+resolution, regardless of the actually resolution of the data in the RRD file.
+This can present a problem for the specialized consolidation functions which
+maintain a one-to-one mapping between primary data points and consolidated
+data points. If a graph insists on viewing the contents of these B<RRAs> on a
+coarser temporal scale, the I<graph> command tries to do something intelligent,
+but the confidence bands and failures no longer have the same meaning and may
+be misleading.
+
+
+=head1 SEE ALSO
+
+L<rrdgraph> gives an overview of how B<rrdtool graph> works.
+L<rrdgraph_data> describes B<DEF>,B<CDEF> and B<VDEF> in detail.
+L<rrdgraph_rpn> describes the B<RPN> language used in the B<xDEF> statements.
+L<rrdgraph_graph> page describes all the graph and print functions.
+
+=head1 AUTHOR
+
+Program by Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
+This manual page by Alex van den Bogaerdt E<lt>alex@vandenbogaerdt.nlE<gt>
+with corrections and/or additions by several people
diff --git a/program/doc/rrdgraph_graph.pod b/program/doc/rrdgraph_graph.pod
new file mode 100644 (file)
index 0000000..c740aad
--- /dev/null
@@ -0,0 +1,529 @@
+=pod
+
+=head1 NAME
+
+rrdgraph_graph - rrdtool graph command reference
+
+=head1 SYNOPSIS
+
+B<PRINT>B<:>I<vname>B<:>I<format>
+
+B<GPRINT>B<:>I<vname>B<:>I<format>
+
+B<COMMENT>B<:>I<text>
+
+B<VRULE>B<:>I<time>B<#>I<color>[B<:>I<legend>][B<:dashes>[B<=>I<on_s>[,I<off_s>[,I<on_s>,I<off_s>]...]][B<:dash-offset=>I<offset>]]
+
+B<HRULE>B<:>I<value>B<#>I<color>[B<:>I<legend>][B<:dashes>[B<=>I<on_s>[,I<off_s>[,I<on_s>,I<off_s>]...]][B<:dash-offset=>I<offset>]]
+
+B<LINE>[I<width>]B<:>I<value>[B<#>I<color>][B<:>[I<legend>][B<:STACK>]][B<:dashes>[B<=>I<on_s>[,I<off_s>[,I<on_s>,I<off_s>]...]][B<:dash-offset=>I<offset>]]
+
+B<AREA>B<:>I<value>[B<#>I<color>][B<:>[I<legend>][B<:STACK>]]
+
+B<TICK>B<:>I<vname>B<#>I<rrggbb>[I<aa>][B<:>I<fraction>[B<:>I<legend>]]
+
+B<SHIFT>B<:>I<vname>B<:>I<offset>
+
+B<TEXTALIGN>B<:>{B<left>|B<right>|B<justified>|B<center>}
+
+B<PRINT>B<:>I<vname>B<:>I<CF>B<:>I<format> (deprecated)
+
+B<GPRINT>B<:>I<vname>B<:>I<CF>B<:>I<format> (deprecated)
+
+B<STACK>B<:>I<vname>B<#>I<color>[B<:>I<legend>] (deprecated)
+
+=head1 DESCRIPTION
+
+These instructions allow you to generate your image or report.
+If you don't use any graph elements, no graph is generated.
+Similarly, no report is generated if you don't use print options.
+
+=head2 PRINT
+
+=head3 B<PRINT:>I<vname>B<:>I<format>[B<:strftime>]
+
+Depending on the context, either the value component or the time
+component of a B<VDEF> is printed using I<format>. It is an error
+to specify a I<vname> generated by a B<DEF> or B<CDEF>.
+
+Any text in I<format> is printed literally with one exception:
+The percent character introduces a formatter string. This string
+can be:
+
+For printing values:
+
+=over
+
+=item B<%%>
+
+just prints a literal '%' character
+
+=item B<%#.#le>
+
+prints numbers like 1.2346e+04. The optional integers # denote field
+width and decimal precision.
+
+=item B<%#.#lf>
+
+prints numbers like 12345.6789, with optional field width
+and precision.
+
+=item B<%s>
+
+place this after B<%le>, B<%lf> or B<%lg>. This will be replaced by the
+appropriate SI magnitude unit and the value will be scaled
+accordingly (123456 -> 123.456 k).
+
+=item B<%S>
+
+is similar to B<%s>. It does, however, use a previously defined
+magnitude unit. If there is no such unit yet, it tries to define
+one (just like B<%s>) unless the value is zero, in which case the magnitude
+unit stays undefined. Thus, formatter strings using B<%S> and no B<%s>
+will all use the same magnitude unit except for zero values.
+
+=back
+
+If you PRINT a VDEF value, you can also print the time associated with it by appending the string
+B<:strftime> to the format. Note that RRDtool uses the strftime function of your OSs C library. This means that
+the conversion specifier may vary. Check the manual page if you are uncertain. The following is a list of
+conversion specifiers usually supported across the board. 
+
+=over
+
+=item B<%a>
+
+The abbreviated weekday name according to the current locale.
+
+=item B<%A>
+
+The full weekday name according to the current locale.
+
+=item B<%b>
+
+The abbreviated month name according to the current locale.
+
+=item B<%B>
+
+The full month name according to the current locale.
+
+=item B<%c>
+
+The preferred date and time representation for the current locale.
+
+=item B<%d>
+
+The day of the month as a decimal number (range 01 to 31).
+
+=item B<%H>
+
+The hour as a decimal number using a 24-hour clock (range 00 to 23).
+
+=item B<%I>
+
+The hour as a decimal number using a 12-hour clock (range 01 to 12).
+
+=item B<%j>
+
+The day of the year as a decimal number (range 001 to 366).
+
+=item B<%m>
+
+The month as a decimal number (range 01 to 12).
+
+=item B<%M>
+
+The minute as a decimal number (range 00 to 59).
+
+=item B<%p>
+
+Either `AM' or `PM' according to the given time value, or the corresponding
+strings for the current locale.  Noon is treated as `pm' and midnight as
+`am'.  Note that in many locales and `pm' notation is unsupported and in
+such cases %p will return an empty string.
+
+=item B<%s>
+
+The second as a decimal number (range 00 to 61).
+
+=item B<%S>
+
+The seconds since the epoch (1.1.1970) (libc dependent non standard!)
+
+=item B<%U>
+
+The  week  number  of  the current year as a decimal number, range 00 to 53, starting with the
+first Sunday as the first day of week 01. See also %V and %W.
+
+=item B<%V>
+
+The ISO 8601:1988 week number of the current year as a decimal number, range 01 to  53,  where
+week  1 is the first week that has at least 4 days in the current year, and with Monday as the
+first day of the week. See also %U and %W.
+
+=item B<%w>
+
+The day of the week as a decimal, range 0 to 6, Sunday being 0.  See also %u.
+
+=item B<%W>
+
+The week number of the current year as a decimal number, range 00 to  53,  starting  with  the
+first Monday as the first day of week 01.
+
+=item B<%x>
+
+The preferred date representation for the current locale without the time.
+
+=item B<%X>
+
+The preferred time representation for the current locale without the date.
+
+=item B<%y>
+
+The year as a decimal number without a century (range 00 to 99).
+
+=item B<%Y>
+
+The year as a decimal number including the century.
+
+=item B<%Z>
+
+The time zone or name or abbreviation.
+
+=item B<%%>
+
+A literal `%' character.
+
+=back
+
+=head3 B<PRINT:>I<vname>B<:>I<CF>B<:>I<format>
+
+I<Deprecated. Use the new form of this command in new scripts.>
+The first form of this command is to be used with B<CDEF> I<vname>s.
+
+
+=head2 GRAPH
+
+=head3 B<GPRINT>B<:>I<vname>B<:>I<format>
+
+This is the same as C<PRINT>, but printed inside the graph.
+
+=head3 B<GPRINT>B<:>I<vname>B<:>I<CF>B<:>I<format>
+
+I<Deprecated. Use the new form of this command in new scripts.>
+This is the same as C<PRINT>, but printed inside the graph.
+
+=head3 B<COMMENT>B<:>I<text>
+
+Text is printed literally in the legend section of the graph. Note that in
+RRDtool 1.2 you have to escape colons in COMMENT text in the same way you
+have to escape them in B<*PRINT> commands by writing B<'\:'>.
+
+=head3 B<VRULE>B<:>I<time>B<#>I<color>[B<:>I<legend>][B<:dashes>[B<=>I<on_s>[,I<off_s>[,I<on_s>,I<off_s>]...]][B<:dash-offset=>I<offset>]]
+
+Draw a vertical line at I<time>.  Its color is composed from three
+hexadecimal numbers specifying the rgb color components (00 is off, FF is
+maximum) red, green and blue followed by an optional alpha. Optionally, a legend box and string is
+printed in the legend section. I<time> may be a number or a variable
+from a B<VDEF>. It is an error to use I<vname>s from B<DEF> or B<CDEF> here.
+Dashed lines can be drawn using the B<dashes> modifier. See B<LINE> for more
+details.
+
+=head3 B<HRULE>B<:>I<value>B<#>I<color>[B<:>I<legend>][B<:dashes>[B<=>I<on_s>[,I<off_s>[,I<on_s>,I<off_s>]...]][B<:dash-offset=>I<offset>]]
+
+Draw a horizontal line at I<value>.  HRULE acts much like LINE except that
+will have no effect on the scale of the graph. If a HRULE is outside the
+graphing area it will just not be visible.
+
+=head3 B<LINE>[I<width>]B<:>I<value>[B<#>I<color>][B<:>[I<legend>][B<:STACK>]][B<:dashes>[B<=>I<on_s>[,I<off_s>[,I<on_s>,I<off_s>]...]][B<:dash-offset=>I<offset>]]
+
+Draw a line of the specified width onto the graph. I<width> can be a
+floating point number. If the color is not specified, the drawing is done
+'invisibly'. This is useful when stacking something else on top of this
+line. Also optional is the legend box and string which will be printed in
+the legend section if specified. The B<value> can be generated by B<DEF>,
+B<VDEF>, and B<CDEF>.  If the optional B<STACK> modifier is used, this line
+is stacked on top of the previous element which can be a B<LINE> or an
+B<AREA>.
+
+The B<dashes> modifier enables dashed line style. Without any further options
+a symmetric dashed line with a segment length of 5 pixels will be drawn. The
+dash pattern can be changed if the B<dashes=> parameter is followed by either
+one value or an even number (1, 2, 4, 6, ...) of positive values. Each value
+provides the length of alternate I<on_s> and I<off_s> portions of the
+stroke. The B<dash-offset> parameter specifies an I<offset> into the pattern
+at which the stroke begins.
+
+When you do not specify a color, you cannot specify a legend.  Should
+you want to use STACK, use the "LINEx:<value>::STACK" form.
+
+=head3 B<AREA>B<:>I<value>[B<#>I<color>][B<:>[I<legend>][B<:STACK>]]
+
+See B<LINE>, however the area between the x-axis and the line will
+be filled.
+
+=head3 B<TICK>B<:>I<vname>B<#>I<rrggbb>[I<aa>][B<:>I<fraction>[B<:>I<legend>]]
+
+Plot a tick mark (a vertical line) for each value of I<vname> that is
+non-zero and not *UNKNOWN*. The I<fraction> argument specifies the length of
+the tick mark as a fraction of the y-axis; the default value is 0.1 (10% of
+the axis). Note that the color specification is not optional. The TICK marks normally
+start at the lower edge of the graphing area. If the fraction is negative they start
+at the upper border of the graphing area.
+
+=head3 B<SHIFT>B<:>I<vname>B<:>I<offset>
+
+Using this command B<RRDtool> will graph the following elements
+with the specified offset.  For instance, you can specify an
+offset of S<( 7*24*60*60 = ) 604'800 seconds> to "look back" one
+week. Make sure to tell the viewer of your graph you did this ...
+As with the other graphing elements, you can specify a number or
+a variable here.
+
+=head3 B<TEXTALIGN>B<:>{B<left>|B<right>|B<justified>|B<center>}
+
+Labels are placed below the graph. When they overflow to the left, they wrap
+to the next line. By default, lines are justified left and right. The
+B<TEXTALIGN> function lets you change this default. This is a command and
+not an option, so that you can change the default several times in your
+argument list.
+
+=cut
+
+# This section describes the currently defunct
+# PieChart code.
+#
+# =item B<PART>B<:>I<vname>B<#>I<rrggbb>[I<aa>][B<:>I<legend>]
+#
+# B<RRDtool> has now support for B<pie charts>. If you include the
+# B<PART> command, the canvas is extended to make room for a chart.
+# The size of the canvas is determined by the lesser of
+# L<width and height|rrdgraph/item_Size>.
+#
+# Pie parts will be concatenated, the first one will start at the
+# top and parts will be created clockwise.  The size of the part
+# is defined by the value part of the L<VDEF|rrdgraph_data/VDEF>
+# function.  It should return a number between 0 and 100, being a
+# percentage.  Providing wrong input will produce undefined results.
+#
+#
+
+=pod
+
+=head3 B<STACK>B<:>I<vname>B<#>I<color>[B<:>I<legend>]
+
+I<Deprecated.  Use the B<STACK> modifiers on the other commands instead!>
+
+B<Some notes on stacking>
+
+When stacking, an element is not placed above the X-axis but rather
+on top of the previous element.  There must be something to stack
+upon.
+
+You can use an B<invisible> LINE or AREA to stacked upon.
+
+An B<unknown> value makes the entire stack unknown from that moment on.
+You don't know where to begin (the unknown value) and therefore do
+not know where to end.
+
+If you want to make sure you will be displaying a certain variable,
+make sure never to stack upon the unknown value.  Use a CDEF instruction
+with B<IF> and B<UN> to do so.
+
+=head1 NOTES on legend arguments
+
+=head2 Escaping the colon
+
+A colon ':' in a I<legend> argument will mark the end of the
+legend. To enter a ':' as part of a legend, the colon must be escaped
+with a backslash '\:'.  Beware that many environments process
+backslashes themselves, so it may be necessary to write two
+backslashes in order to one being passed onto rrd_graph.
+
+=head2 String Formatting
+
+The text printed below the actual graph can be formatted by appending special
+escape characters at the end of a text. When ever such a character occurs,
+all pending text is pushed onto the graph according to the character
+specified.
+
+Valid markers are: B<\j> for justified, B<\l> for left aligned, B<\r> for
+right aligned, and B<\c> for centered. In the next section there is an
+example showing how to use centered formatting.
+
+B<\n> is a valid alias for B<\l> since incomplete parsing in earlier
+versions of RRDtool lead to this behavior and a number of people has been using it.
+
+Normally there are two space characters inserted between every two items
+printed into the graph. The space following a string can be suppressed by
+putting a B<\g> at the end of the string. The B<\g> also ignores any space
+inside the string if it is at the very end of the string. This can be used
+in connection with B<%s> to suppress empty unit strings.
+
+ GPRINT:a:MAX:%lf%s\g
+
+A special case is COMMENT:B<\s> which inserts some additional vertical space
+before placing the next row of legends.
+
+If you want to have left and right aligned legends on the same line use COMMENT:B<\u>
+to go one line back like this:
+
+ COMMENT:left\l
+ COMMENT:\u
+ COMMENT:right\r
+
+When using a proportional font in your graph, the tab
+characters or the sequence B<\t> will line-up legend elements. Note that
+the tabs inserted are relative to the start of the current legend
+element!
+
+Since RRDtool 1.3 is using Pango for rending text, you can use Pango markup.
+Pango uses the xml B<span> tags for inline formatting instructions.:
+
+A simple example of a marked-up string might be: 
+
+ <span foreground="blue" size="x-large">Blue text</span> is <i>cool</i>!
+
+The complete list of attributes for the span tag (taken from the pango documentation):
+
+=over
+
+=item B<font_desc>
+
+A font description string, such as "Sans Italic 12"; note that any other span attributes will override this description. So if you have "Sans Italic" and also a style="normal" attribute, you will get Sans normal, not italic.
+
+=item B<font_family>
+
+A font family name
+
+=item B<face>
+
+Synonym for font_family
+
+=item B<size>
+
+Font size in 1024ths of a point, or one of the absolute sizes 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', or one of the relative sizes 'smaller' or 'larger'. If you want to specify a absolute size, it's usually easier to take advantage of the ability to specify a partial font description using 'font_desc'; you can use font_desc='12.5' rather than size='12800'.
+
+=item B<style>
+
+One of 'normal', 'oblique', 'italic'
+
+=item B<weight>
+
+One of 'ultralight', 'light', 'normal', 'bold', 'ultrabold', 'heavy', or a numeric weight
+
+=item B<variant>
+
+'normal' or 'smallcaps'
+
+=item B<stretch>
+
+One of 'ultracondensed', 'extracondensed', 'condensed', 'semicondensed', 'normal', 'semiexpanded', 'expanded', 'extraexpanded', 'ultraexpanded'
+
+=item B<foreground>
+
+An RGB color specification such as '#00FF00' or a color name such as 'red'
+
+=item B<background>
+
+An RGB color specification such as '#00FF00' or a color name such as 'red'
+
+=item B<underline>
+
+One of 'none', 'single', 'double', 'low', 'error'
+
+=item B<underline_color>
+
+The color of underlines; an RGB color specification such as '#00FF00' or a color name such as 'red'
+
+=item B<rise>
+
+Vertical displacement, in 10000ths of an em. Can be negative for subscript, positive for superscript.
+
+=item B<strikethrough>
+
+'true' or 'false' whether to strike through the text
+
+=item B<strikethrough_color>
+
+The color of crossed out lines; an RGB color specification such as '#00FF00' or a color name such as 'red'
+
+=item B<fallback>
+
+'true' or 'false' whether to enable fallback. If disabled, then characters will only be used from the closest matching font on the system. No fallback will be done to other fonts on the system that might contain the characters in the text. Fallback is enabled by default. Most applications should not disable fallback.
+
+=item B<lang>
+
+A language code, indicating the text language
+
+=item B<letter_spacing>
+
+Inter-letter spacing in 1024ths of a point.
+
+=item B<gravity>
+
+One of 'south', 'east', 'north', 'west', 'auto'.
+
+=item B<gravity_hint>
+
+One of 'natural', 'strong', 'line'.
+
+=back
+
+To save you some typing, there are also some shortcuts:
+
+=over
+
+=item B<b>
+
+Bold
+
+=item B<big>
+
+Makes font relatively larger, equivalent to <span size="larger">
+
+=item B<i>
+
+Italic
+
+=item B<s>
+
+Strike through
+
+=item B<sub>
+
+Subscript
+
+=item B<sup>
+
+Superscript
+
+=item B<small>
+
+Makes font relatively smaller, equivalent to <span size="smaller">
+
+=item B<tt>
+
+Monospace font
+
+=item B<u>
+
+Underline 
+
+=back
+
+=head1 SEE ALSO
+
+L<rrdgraph> gives an overview of how B<rrdtool graph> works.
+L<rrdgraph_data> describes B<DEF>,B<CDEF> and B<VDEF> in detail.
+L<rrdgraph_rpn> describes the B<RPN> language used in the B<?DEF> statements.
+L<rrdgraph_graph> page describes all of the graph and print functions.
+
+Make sure to read L<rrdgraph_examples> for tipsE<amp>tricks.
+
+=head1 AUTHOR
+
+Program by Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
+This manual page by Alex van den Bogaerdt E<lt>alex@vandenbogaerdt.nlE<gt>
+with corrections and/or additions by several people
diff --git a/program/doc/rrdgraph_libdbi.pod b/program/doc/rrdgraph_libdbi.pod
new file mode 100644 (file)
index 0000000..c84465d
--- /dev/null
@@ -0,0 +1,158 @@
+=head1 NAME
+
+rrdgraph_libdbi - fetching data for graphing in rrdtool graph via libdbi
+
+=head1 SYNOPSIS
+
+E<lt>rrdfileE<gt> = B<sql//E<lt>libdbi driverE<gt>/E<lt>driver-option-nameE<gt>=E<lt>driver-option-valueE<gt>/...[/rrdminstepsize=E<lt>stepsizeE<gt>][/rrdfillmissing=E<lt>fill missing n samplesE<gt>]//E<lt>tableE<gt>/E<lt>unixtimestamp columnE<gt>/E<lt>data value columnE<gt>[/derive]/E<lt>where clause 1E<gt>/.../E<lt>where clause nE<gt>>
+
+=head1 DESCRIPTION
+
+This pseudo-rrd-filename defines a sql datasource:
+
+=over 8
+
+=item B<sql//>
+
+  magic cookie-prefix for a libdbi type datasource
+
+=item B<E<lt>libdbi driverE<gt>>
+
+  which libdbi driver to use (e.g: mysql)
+
+=item B<E<lt>driver-option-nameE<gt>>=B<E<lt>driver-option-valueE<gt>>
+
+  defines the parameters that are required to connect to the database with the given libdbi driver
+  (These drivers are libdbi dependent - for details please look at the driver documentation of libdbi!)
+
+=item B</rrdminstepsize>=B<E<lt>minimum step sizeE<gt>>
+
+  defines the minimum number of the step-length used for graphing (default: 300 seconds)
+
+=item B</rrdfillmissing>=B<E<lt>fill missing stepsE<gt>>
+
+  defines the number of steps to fill with the last value to avoid NaN boxes due to data-insertation jitter (default: 0 steps)
+
+=item B<E<lt>tableE<gt>>
+
+  defines the table from which to fetch the resultset.
+
+  If there is a need to fetch data from several tables, these tables can be defined by separating the tablenames with a "+"
+
+  hex-type-encoding via %xx are translated to the actual value, use %% to use %
+
+=item B<E<lt>[*]unixtimestamp columnE<gt>>
+
+  defines the column of E<lt>tableE<gt> which contains the unix-timestamp 
+  - if this is a DATETIME field in the database, then prefix with leading '*'
+
+  hex-type-encoding via %xx are translated to the actual value, use %% to use %
+
+=item B<E<lt>data value columnE<gt>>
+
+  defines the column of E<lt>tableE<gt> which contains the value column, which should be graphed
+
+  hex-type-encoding via %xx are translated to the actual value, use %% to use %
+
+=item B</derive>
+
+  defines that the data value used should be the delta of the 2 consecutive values (to simulate COUNTER or DERIVE type datasources)
+
+=item B</E<lt>where clause(s)E<gt>>
+
+  defines one (ore more) where clauses that are joined with AND to filter the entries in the <lt>table<gt>
+
+  hex-type-encoding via %xx are translated to the actual value, use %% to use %
+
+=back
+
+the returned value column-names, which can be used as ds-names, are:
+
+=over 8
+
+=item B<min>, B<avg>, B<max>, B<count> and B<sigma>
+
+  are returned to be used as ds-names in your DS definition.
+  The reason for using this is that if the consolidation function is used for min/avg and max, then the engine is used several times.
+  And this results in the same SQL Statements used several times
+
+=back
+
+=head1 EXAMPLES
+
+Here an example of a table in a MySQL database:
+
+  DB connect information
+    dbhost=127.0.0.1
+    user=rrd
+    password=secret
+    database=rrd
+
+  here the table:
+    CREATE TABLE RRDValue (
+      RRDKeyID      bigint(20) NOT NULL,
+      UnixTimeStamp int(11) NOT NULL,
+      value         double default NOT NULL,
+      PRIMARY KEY  (RRDKeyID,UnixTimeStamp)
+    );
+
+and the RRDKeyID we want to graph for is: 1141942900757789274
+
+The pseudo rrd-filename to access this is:
+"sql//mysql/host=127.0.0.1/dbname=rrd/username=rrd/password=secret//RRDValue/UnixTimeStamp/value/RRDKeyID=1141464142203608274"
+
+To illustrate this here a command to create a graph that contains the actual values.
+
+  DS_BASE="sql//mysql/host=127.0.0.1/dbname=rrd/username=rrd/password=passwd//RRDValue/UnixTimeStamp/value/RRDKeyID=1141942900757789274"
+  rrdtool graph test.png --imgformat=PNG --start=-1day --end=+3hours --width=1000 --height=600 \
+    "DEF:min=$DS_BASE:min:AVERAGE" \
+    "LINE1:min#FF0000:value" \
+    "DEF:avg=$DS_BASE:avg:AVERAGE" \
+    "LINE1:avg#00FF00:average" \
+    "DEF:max=$DS_BASE:max:AVERAGE" \
+    "LINE1:max#FF0000:max" \
+    "DEF:sigma=$DS_BASE:sigma:AVERAGE" \
+    "CDEF:upper=avg,4,sigma,*,+" \
+    "LINE1:upper#0000FF:+4 sigma" \
+    "CDEF:lower=avg,4,sigma,*,-" \
+    "LINE1:lower#0000FF:-4 sigma"
+
+=head1 NOTES
+
+* Naturally you can also use any other kind of driver that libdbi supports - e.g postgres, ...
+
+* From the way the data source is joined, it should also be possible to do joins over different tables 
+  (separate tables with "," in table and add in the WHERE Clauses the table equal joins. 
+  This has not been tested!!!)
+
+* It should also be relatively simple to add to the database using the same data source string.
+  This has not been implemented...
+
+* The aggregation functions are ignored and several data columns are used instead 
+  to avoid querying the same SQL several times when minimum, average and maximum are needed for graphing...
+
+* for DB efficiency you should think of having 2 tables, one containing historic values and the other containing the latest data.
+  This second table should be kept small to allow for the least ammount of blocking SQL statements.
+  Whith mysql you can even use myisam table-type for the first and InnoDB for the second. 
+  This is especially interresting as with tables with +100M rows myisam is much smaller then InnoDB.
+
+* To debug the SQL statements set the environment variable RRDDEBUGSQL and the actual SQL statements and the timing is printed to stderr.
+
+=head1 BUGS
+
+* at least on Linux please make sure that the libdbi driver is explicitly linked against libdbi.so.0 
+  check via ldd /usr/lib/dbd/libmysql.so, that there is a line with libdbi.so.0. 
+  otherwise at least the perl module RRDs will fail because the dynamic linker can not find some symbols from libdbi.so.
+  (this only happens when the libdbi driver is actually used the first time!)
+  This is KNOWN to be the case with RHEL4 and FC4 and FC5! (But actually this is a bug with libdbi make files!)
+
+* at least version 0.8.1 of libdbiexhibits a bug with BINARY fields
+  (shorttext,text,mediumtext,longtext and possibly also BINARY and BLOB fields), 
+  that can result in coredumps of rrdtool. 
+  The tool will tell you on stderr if this occures, so that you know what may be the reason.
+  If you are not experiencing these coredumps, then set the environment variable RRD_NO_LIBDBI_BUG_WARNING, 
+  and then the message will not get shown.
+
+=head1 AUTHOR
+
+Martin Sperl <rrdtool@martin.sperl.org>
diff --git a/program/doc/rrdgraph_rpn.pod b/program/doc/rrdgraph_rpn.pod
new file mode 100644 (file)
index 0000000..16f7610
--- /dev/null
@@ -0,0 +1,405 @@
+=head1 NAME
+
+rrdgraph_rpn - About RPN Math in rrdtool graph
+
+=head1 SYNOPSIS
+
+I<RPN expression>:=I<vname>|I<operator>|I<value>[,I<RPN expression>]
+
+=head1 DESCRIPTION
+
+If you have ever used a traditional HP calculator you already know
+B<RPN> (Reverse Polish Notation).
+The idea behind B<RPN> is that you have a stack and push
+your data onto this stack. Whenever you execute an operation, it
+takes as many elements from the stack as needed. Pushing is done
+implicitly, so whenever you specify a number or a variable, it gets
+pushed onto the stack automatically.
+
+At the end of the calculation there should be one and only one value left on
+the stack.  This is the outcome of the function and this is what is put into
+the I<vname>.  For B<CDEF> instructions, the stack is processed for each
+data point on the graph. B<VDEF> instructions work on an entire data set in
+one run. Note, that currently B<VDEF> instructions only support a limited
+list of functions.
+
+Example: C<VDEF:maximum=mydata,MAXIMUM>
+
+This will set variable "maximum" which you now can use in the rest
+of your RRD script.
+
+Example: C<CDEF:mydatabits=mydata,8,*>
+
+This means:  push variable I<mydata>, push the number 8, execute
+the operator I<*>. The operator needs two elements and uses those
+to return one value.  This value is then stored in I<mydatabits>.
+As you may have guessed, this instruction means nothing more than
+I<mydatabits = mydata * 8>.  The real power of B<RPN> lies in the
+fact that it is always clear in which order to process the input.
+For expressions like C<a = b + 3 * 5> you need to multiply 3 with
+5 first before you add I<b> to get I<a>. However, with parentheses
+you could change this order: C<a = (b + 3) * 5>. In B<RPN>, you
+would do C<a = b, 3, +, 5, *> without the need for parentheses.
+
+=head1 OPERATORS
+
+=over 4
+
+=item Boolean operators
+
+B<LT, LE, GT, GE, EQ, NE>
+
+Pop two elements from the stack, compare them for the selected condition
+and return 1 for true or 0 for false. Comparing an I<unknown> or an
+I<infinite> value will always result in 0 (false).
+
+B<UN, ISINF>
+
+Pop one element from the stack, compare this to I<unknown> respectively
+to I<positive or negative infinity>. Returns 1 for true or 0 for false.
+
+B<IF>
+
+Pops three elements from the stack.  If the element popped last is 0
+(false), the value popped first is pushed back onto the stack,
+otherwise the value popped second is pushed back. This does, indeed,
+mean that any value other than 0 is considered to be true.
+
+Example: C<A,B,C,IF> should be read as C<if (A) then (B) else (C)>
+
+Z<>
+
+=item Comparing values
+
+B<MIN, MAX>
+
+Pops two elements from the stack and returns the smaller or larger,
+respectively.  Note that I<infinite> is larger than anything else.
+If one of the input numbers is I<unknown> then the result of the operation will be
+I<unknown> too.
+
+B<LIMIT>
+
+Pops two elements from the stack and uses them to define a range.
+Then it pops another element and if it falls inside the range, it
+is pushed back. If not, an I<unknown> is pushed.
+
+The range defined includes the two boundaries (so: a number equal
+to one of the boundaries will be pushed back). If any of the three
+numbers involved is either I<unknown> or I<infinite> this function
+will always return an I<unknown>
+
+Example: C<CDEF:a=alpha,0,100,LIMIT> will return I<unknown> if
+alpha is lower than 0 or if it is higher than 100.
+
+Z<>
+
+=item Arithmetics
+
+B<+, -, *, /, %>
+
+Add, subtract, multiply, divide, modulo
+
+B<ADDNAN>
+
+NAN-safe addition. If one parameter is NAN/UNKNOWN it'll be treated as
+zero. If both parameters are NAN/UNKNOWN, NAN/UNKNOWN will be returned.
+
+B<SIN, COS, LOG, EXP, SQRT>
+
+Sine and cosine (input in radians), log and exp (natural logarithm),
+square root.
+
+B<ATAN>
+
+Arctangent (output in radians).
+
+B<ATAN2>
+
+Arctangent of y,x components (output in radians).
+This pops one element from the stack, the x (cosine) component, and then
+a second, which is the y (sine) component.
+It then pushes the arctangent of their ratio, resolving the ambiguity between
+quadrants.
+
+Example: C<CDEF:angle=Y,X,ATAN2,RAD2DEG> will convert C<X,Y>
+components into an angle in degrees.
+
+B<FLOOR, CEIL>
+
+Round down or up to the nearest integer.
+
+B<DEG2RAD, RAD2DEG>
+
+Convert angle in degrees to radians, or radians to degrees.
+
+B<ABS>
+
+Take the absolute value.
+
+=item Set Operations
+
+B<SORT, REV>
+
+Pop one element from the stack.  This is the I<count> of items to be sorted
+(or reversed).  The top I<count> of the remaining elements are then sorted
+(or reversed) in place on the stack.
+
+Example: C<CDEF:x=v1,v2,v3,v4,v5,v6,6,SORT,POP,5,REV,POP,+,+,+,4,/> will
+compute the average of the values v1 to v6 after removing the smallest and
+largest.
+
+B<AVG>
+
+Pop one element (I<count>) from the stack. Now pop I<count> elements and build the
+average, ignoring all UNKNOWN values in the process.
+
+Example: C<CDEF:x=a,b,c,d,4,AVG>
+
+B<TREND, TRENDNAN>
+
+Create a "sliding window" average of another data series.
+
+Usage:
+CDEF:smoothed=x,1800,TREND
+
+This will create a half-hour (1800 second) sliding window average of x.  The
+average is essentially computed as shown here:
+
+                 +---!---!---!---!---!---!---!---!--->
+                                                     now
+                       delay     t0
+                 <--------------->
+                         delay       t1
+                     <--------------->
+                              delay      t2
+                         <--------------->
+
+
+     Value at sample (t0) will be the average between (t0-delay) and (t0)
+     Value at sample (t1) will be the average between (t1-delay) and (t1)
+     Value at sample (t2) will be the average between (t2-delay) and (t2)
+
+TRENDNAN is - in contrast to TREND - NAN-safe. If you use TREND and one 
+source value is NAN the complete sliding window is affected. The TRENDNAN 
+operation ignores all NAN-values in a sliding window and computes the 
+average of the remaining values.
+
+B<PREDICT, PREDICTSIGMA>
+
+Create a "sliding window" average/sigma of another data series, that also
+shifts the data series by given amounts of of time as well
+
+Usage - explicit stating shifts:
+CDEF:predict=<shift n>,...,<shift 1>,n,<window>,x,PREDICT
+CDEF:sigma=<shift n>,...,<shift 1>,n,<window>,x,PREDICTSIGMA
+
+Usage - shifts defined as a base shift and a number of time this is applied
+CDEF:predict=<shift multiplier>,-n,<window>,x,PREDICT
+CDEF:sigma=<shift multiplier>,-n,<window>,x,PREDICTSIGMA
+
+Example:
+CDEF:predict=172800,86400,2,1800,x,PREDICT
+
+This will create a half-hour (1800 second) sliding window average/sigma of x, that
+average is essentially computed as shown here:
+
+ +---!---!---!---!---!---!---!---!---!---!---!---!---!---!---!---!---!--->
+                                                                     now
+                                                  shift 1        t0
+                                         <----------------------->
+                               window
+                         <--------------->
+                                       shift 2
+                 <----------------------------------------------->
+       window
+ <--------------->
+                                                      shift 1        t1
+                                             <----------------------->
+                                   window
+                             <--------------->
+                                            shift 2
+                     <----------------------------------------------->
+           window
+     <--------------->
+
+ Value at sample (t0) will be the average between (t0-shift1-window) and (t0-shift1)
+                                      and between (t0-shift2-window) and (t0-shift2)
+ Value at sample (t1) will be the average between (t1-shift1-window) and (t1-shift1)
+                                      and between (t1-shift2-window) and (t1-shift2)
+
+
+The function is by design NAN-safe. 
+This also allows for extrapolation into the future (say a few days)
+- you may need to define the data series whit the optional start= parameter, so that 
+the source data series has enough data to provide prediction also at the beginning of a graph...
+
+Here an example, that will create a 10 day graph that also shows the 
+prediction 3 days into the future with its uncertainty value (as defined by avg+-4*sigma)
+This also shows if the prediction is exceeded at a certain point.
+
+rrdtool graph image.png --imgformat=PNG \
+ --start=-7days --end=+3days --width=1000 --height=200 --alt-autoscale-max \
+ DEF:value=value.rrd:value:AVERAGE:start=-14days \
+ LINE1:value#ff0000:value \
+ CDEF:predict=86400,-7,1800,value,PREDICT \
+ CDEF:sigma=86400,-7,1800,value,PREDICTSIGMA \
+ CDEF:upper=predict,sigma,3,*,+ \
+ CDEF:lower=predict,sigma,3,*,- \
+ LINE1:predict#00ff00:prediction \
+ LINE1:upper#0000ff:upper\ certainty\ limit \
+ LINE1:lower#0000ff:lower\ certainty\ limit \
+ CDEF:exceeds=value,UN,0,value,lower,upper,LIMIT,UN,IF \
+ TICK:exceeds#aa000080:1
+
+Note: Experience has shown that a factor between 3 and 5 to scale sigma is a good 
+discriminator to detect abnormal behavior. This obviously depends also on the type 
+of data and how "noisy" the data series is.
+
+This prediction can only be used for short term extrapolations - say a few days into the future-
+
+=item Special values
+
+B<UNKN>
+
+Pushes an unknown value on the stack
+
+B<INF, NEGINF>
+
+Pushes a positive or negative infinite value on the stack. When
+such a value is graphed, it appears at the top or bottom of the
+graph, no matter what the actual value on the y-axis is.
+
+B<PREV>
+
+Pushes an I<unknown> value if this is the first value of a data
+set or otherwise the result of this B<CDEF> at the previous time
+step. This allows you to do calculations across the data.  This
+function cannot be used in B<VDEF> instructions.
+
+B<PREV(vname)>
+
+Pushes an I<unknown> value if this is the first value of a data
+set or otherwise the result of the vname variable at the previous time
+step. This allows you to do calculations across the data. This
+function cannot be used in B<VDEF> instructions.
+
+B<COUNT>
+
+Pushes the number 1 if this is the first value of the data set, the
+number 2 if it is the second, and so on. This special value allows
+you to make calculations based on the position of the value within
+the data set. This function cannot be used in B<VDEF> instructions.
+
+=item Time
+
+Time inside RRDtool is measured in seconds since the epoch. The
+epoch is defined to be S<C<Thu Jan  1 00:00:00 UTC 1970>>.
+
+B<NOW>
+
+Pushes the current time on the stack.
+
+B<TIME>
+
+Pushes the time the currently processed value was taken at onto the stack.
+
+B<LTIME>
+
+Takes the time as defined by B<TIME>, applies the time zone offset
+valid at that time including daylight saving time if your OS supports
+it, and pushes the result on the stack.  There is an elaborate example
+in the examples section below on how to use this.
+
+=item Processing the stack directly
+
+B<DUP, POP, EXC>
+
+Duplicate the top element, remove the top element, exchange the two
+top elements.
+
+Z<>
+
+=back
+
+=head1 VARIABLES
+
+These operators work only on B<VDEF> statements. Note that currently ONLY these work for B<VDEF>.
+
+=over 4
+
+=item MAXIMUM, MINIMUM, AVERAGE
+
+Return the corresponding value, MAXIMUM and MINIMUM also return
+the first occurrence of that value in the time component.
+
+Example: C<VDEF:avg=mydata,AVERAGE>
+
+=item STDEV
+
+Returns the standard deviation of the values.
+
+Example: C<VDEF:stdev=mydata,STDEV>
+
+=item LAST, FIRST
+
+Return the last/first value including its time.  The time for
+FIRST is actually the start of the corresponding interval, whereas
+LAST returns the end of the corresponding interval.
+
+Example: C<VDEF:first=mydata,FIRST>
+
+=item TOTAL
+
+Returns the rate from each defined time slot multiplied with the
+step size.  This can, for instance, return total bytes transferred
+when you have logged bytes per second. The time component returns
+the number of seconds.
+
+Example: C<VDEF:total=mydata,TOTAL>
+
+=item PERCENT, PERCENTNAN
+
+This should follow a B<DEF> or B<CDEF> I<vname>. The I<vname> is popped,
+another number is popped which is a certain percentage (0..100). The
+data set is then sorted and the value returned is chosen such that
+I<percentage> percent of the values is lower or equal than the result.
+For PERCENTNAN I<Unknown> values are ignored, but for PERCENT
+I<Unknown> values are considered lower than any finite number for this
+purpose so if this operator returns an I<unknown> you have quite a lot
+of them in your data.  B<Inf>inite numbers are lesser, or more, than the
+finite numbers and are always more than the I<Unknown> numbers.
+(NaN E<lt> -INF E<lt> finite values E<lt> INF)
+
+Example: C<VDEF:perc95=mydata,95,PERCENT>
+         C<VDEF:percnan95=mydata,95,PERCENTNAN>
+
+=item LSLSLOPE, LSLINT, LSLCORREL
+
+Return the parameters for a B<L>east B<S>quares B<L>ine I<(y = mx +b)> 
+which approximate the provided dataset.  LSLSLOPE is the slope I<(m)> of
+the line related to the COUNT position of the data.  LSLINT is the 
+y-intercept I<(b)>, which happens also to be the first data point on the 
+graph. LSLCORREL is the Correlation Coefficient (also know as Pearson's 
+Product Moment Correlation Coefficient).  It will range from 0 to +/-1 
+and represents the quality of fit for the approximation.   
+
+Example: C<VDEF:slope=mydata,LSLSLOPE>
+
+=back
+
+=head1 SEE ALSO
+
+L<rrdgraph> gives an overview of how B<rrdtool graph> works.
+L<rrdgraph_data> describes B<DEF>,B<CDEF> and B<VDEF> in detail.
+L<rrdgraph_rpn> describes the B<RPN> language used in the B<?DEF> statements.
+L<rrdgraph_graph> page describes all of the graph and print functions.
+
+Make sure to read L<rrdgraph_examples> for tipsE<amp>tricks.
+
+=head1 AUTHOR
+
+Program by Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
+This manual page by Alex van den Bogaerdt E<lt>alex@vandenbogaerdt.nlE<gt>
+with corrections and/or additions by several people
diff --git a/program/doc/rrdinfo.pod b/program/doc/rrdinfo.pod
new file mode 100644 (file)
index 0000000..d147756
--- /dev/null
@@ -0,0 +1,88 @@
+=head1 NAME
+
+rrdinfo - extract header information from an RRD
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<info> I<filename>
+S<[B<--daemon> I<address>]>
+
+=head1 DESCRIPTION
+
+The B<info> function prints the header information from an RRD in
+a parsing friendly format.
+
+Check L<rrdcreate> if you are uncertain about the meaning of the
+individual keys.
+
+=over 8
+
+=item I<filename>
+
+The name of the B<RRD> you want to examine.
+
+=item B<--daemon> I<address>
+
+Address of the L<rrdcached> daemon. If specified, a C<flush> command is sent
+to the server before reading the RRD files. This allows B<rrdtool> to return
+fresh data even if the daemon is configured to cache values for a long time.
+For a list of accepted formats, see the B<-l> option in the L<rrdcached> manual.
+
+ rrdtool info --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd
+
+=back
+
+=head1 EXAMPLE
+
+This is the output generated by running B<info> on a simple RRD which
+contains two data sources and one RRA. Note that the number after the
+I<last_update> keyword is in seconds since 1970. The string B<NaN>
+stands for I<*UNKNOWN*> data. In the example it means that this RRD
+has neither minimum nor maximum values defined for either of its
+data sources.
+
+ filename = "random.rrd"
+ rrd_version = "0001"
+ step = 300
+ last_update = 955892996
+ header_size = 2872
+ ds[a].type = "GAUGE"
+ ds[a].minimal_heartbeat = 600
+ ds[a].min = NaN
+ ds[a].max = NaN
+ ds[a].last_ds = "UNKN"
+ ds[a].value = 2.1824421548e+04
+ ds[a].unknown_sec = 0
+ ds[b].type = "GAUGE"
+ ds[b].minimal_heartbeat = 600
+ ds[b].min = NaN
+ ds[b].max = NaN
+ ds[b].last_ds = "UNKN"
+ ds[b].value = 3.9620838224e+03
+ ds[b].unknown_sec = 0
+ rra[0].cf = "AVERAGE"
+ rra[0].pdp_per_row = 1
+ rra[0].cdp_prep[0].value = nan
+ rra[0].cdp_prep[0].unknown_datapoints = 0
+ rra[0].cdp_prep[1].value = nan
+ rra[0].cdp_prep[1].unknown_datapoints = 0
+
+=head1 ENVIRONMENT VARIABLES
+
+The following environment variables may be used to change the behavior of
+C<rrdtoolE<nbsp>info>:
+
+=over 4
+
+=item B<RRDCACHED_ADDRESS>
+
+If this environment variable is set it will have the same effect as specifying
+the C<--daemon> option on the command line. If both are present, the command
+line argument takes precedence.
+
+=back
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
diff --git a/program/doc/rrdlast.pod b/program/doc/rrdlast.pod
new file mode 100644 (file)
index 0000000..a8bf757
--- /dev/null
@@ -0,0 +1,52 @@
+=head1 NAME
+
+rrdlast - Return the date of the last data sample in an RRD
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<last> I<filename>
+S<[B<--daemon> I<address>]>
+
+=head1 DESCRIPTION
+
+The B<last> function returns the UNIX timestamp of the most recent
+update of the RRD.
+
+=over 8
+
+=item I<filename>
+
+The name of the B<RRD> that contains the data.
+
+=item B<--daemon> I<address>
+
+Address of the L<rrdcached> daemon. If specified, a C<flush> command is sent
+to the server before reading the RRD files. This allows B<rrdtool> to return
+fresh data even if the daemon is configured to cache values for a long time.
+For a list of accepted formats, see the B<-l> option in the L<rrdcached> manual.
+
+ rrdtool last --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd
+
+=back
+
+=head1 ENVIRONMENT VARIABLES
+
+The following environment variables may be used to change the behavior of
+C<rrdtoolE<nbsp>last>:
+
+=over 4
+
+=item B<RRDCACHED_ADDRESS>
+
+If this environment variable is set it will have the same effect as specifying
+the C<--daemon> option on the command line. If both are present, the command
+line argument takes precedence.
+
+=back
+
+=head1 AUTHOR
+
+Russ Wright <rwwright@home.com>
+
+
+
diff --git a/program/doc/rrdlastupdate.pod b/program/doc/rrdlastupdate.pod
new file mode 100644 (file)
index 0000000..ecdff8d
--- /dev/null
@@ -0,0 +1,52 @@
+=head1 NAME
+
+rrdlastupdate - Return the most recent update to an RRD
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<lastupdate> I<filename>
+S<[B<--daemon> I<address>]>
+
+=head1 DESCRIPTION
+
+The B<lastupdate> function returns the UNIX timestamp and the 
+value stored for each datum in the most recent update of an RRD.
+
+=over 8
+
+=item I<filename>
+
+The name of the B<RRD> that contains the data.
+
+=item B<--daemon> I<address>
+
+Address of the L<rrdcached> daemon. If specified, a C<flush> command is sent
+to the server before reading the RRD files. This allows B<rrdtool> to return
+fresh data even if the daemon is configured to cache values for a long time.
+For a list of accepted formats, see the B<-l> option in the L<rrdcached> manual.
+
+ rrdtool lastupdate --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd
+
+=back
+
+=head1 ENVIRONMENT VARIABLES
+
+The following environment variables may be used to change the behavior of
+C<rrdtoolE<nbsp>lastupdate>:
+
+=over 4
+
+=item B<RRDCACHED_ADDRESS>
+
+If this environment variable is set it will have the same effect as specifying
+the C<--daemon> option on the command line. If both are present, the command
+line argument takes precedence.
+
+=back
+
+=head1 AUTHOR
+
+Andy Riebs E<lt>andy.riebs@hp.comE<gt>
+
+
+
diff --git a/program/doc/rrdlua.pod b/program/doc/rrdlua.pod
new file mode 100644 (file)
index 0000000..7d234fe
--- /dev/null
@@ -0,0 +1,204 @@
+=head1 NAME
+
+RRDLua -  Lua binding for RRDTool
+
+=head1 SYNOPSIS
+
+  require 'rrd'
+  rrd.create(...)
+  rrd.dump(...)
+  rrd.fetch(...)
+  rrd.first(...)
+  rrd.graph(...)
+  rrd.graphv(...)
+  rrd.info(...)
+  rrd.last(...)
+  rrd.resize(...)
+  rrd.restore(...)
+  rrd.tune(...)
+  rrd.update(...)
+  rrd.updatev(...)
+
+=head1 DESCRIPTION
+
+=head2 Calling Sequence
+
+This module accesses RRDtool functionality directly from within Lua.
+The arguments to the functions listed in the SYNOPSIS are explained in
+the regular RRDtool documentation. The command-line call
+
+    rrdtool update mydemo.rrd --template in:out N:12:13
+
+gets turned into
+
+    rrd.update ("mydemo.rrd", "--template", "in:out", "N:12:13")
+
+Note that --template=in:out is also valid.
+
+=head2 Using with Lua 5.1
+
+Start your programs with:
+
+    ---------------------------------------------------------------
+    package.cpath = '/usr/local/rrdtool-1.3.2/lib/lua/5.1/?.so;' ..
+                    package.cpath
+    require 'rrd'
+    ---------------------------------------------------------------
+   
+OBS: If you configured with --enable-lua-site-install, you don't need
+to set package.cpath like above.
+
+=head2 Using with Lua 5.0
+
+The Lua binding for RRDtool needs the Lua module compat-5.1 to work with
+Lua 5.0. Some Linux distros, like Ubuntu gutsy and hardy, have it already
+integrated in Lua 5.0 -dev packages, so you just have to require it:
+
+    require 'compat-5.1'
+
+For other platforms, the compat-5.1 module that comes with this binding
+will be installed for you in the same dir where RRDtool was installed,
+under the subdir .../lib/lua/5.0. In this case, you must tell your Lua
+programs where to find it by changing the Lua var LUA_PATH:
+
+    -- compat-5.1.lua is only necessary for Lua 5.0 ----------------
+    -- try only compat-5.1 installed with RRDtool package
+    local original_LUA_PATH = LUA_PATH
+    LUA_PATH = '/usr/local/rrdtool-1.3.2/lib/lua/5.0/?.lua'
+    require 'compat-5.1'
+    LUA_PATH = original_LUA_PATH
+    original_LUA_PATH = nil
+    --- end of code to require compat-5.1 ---------------------------
+    
+    Now we can require the rrd module in the same way we did for 5.1 above:
+    
+    ---------------------------------------------------------------
+    package.cpath = '/usr/local/rrdtool-1.3.2/lib/lua/5.0/?.so;' ..
+                    package.cpath
+    require 'rrd'
+    ---------------------------------------------------------------
+
+=head2 Error Handling
+
+The Lua RRDTool module functions will abort your program with a stack
+traceback when they can not make sense out of the arguments you fed them.
+However, you can capture and handle the errors yourself, instead of just
+letting the program abort, by calling the module functions through Lua
+protected calls - 'pcall' or 'xpcall'.
+
+     Ex: program t.lua
+      
+     --- compat-5.1.lua is only necessary for Lua 5.0 ----------------
+     -- uncomment below if your distro has not compat-5.1
+     -- original_LUA_PATH = LUA_PATH
+     -- try only compat-5.1.lua installed with RRDtool package
+     -- LUA_PATH = '/usr/local/rrdtool-1.3.2/lib/lua/5.0/?.lua'
+      
+     -- here we use a protected call to require compat-5.1
+     local r = pcall(require, 'compat-5.1')
+     if not r then
+       print('** could not load compat-5.1.lua')
+       os.exit(1)
+     end
+     
+     -- uncomment below if your distro has not compat-5.1
+     -- LUA_PATH = original_LUA_PATH
+     -- original_LUA_PATH = nil
+     --- end of code to require compat-5.1 ---------------------------
+     
+     -- If the Lua RRDTool module was installed together with RRDTool,
+     -- in /usr/local/rrdtool-1.3.2/lib/lua/5.0, package.cpath must be
+     -- set accordingly so that 'require' can find the module:
+    
+     package.cpath = '/usr/local/rrdtool-1.3.2/lib/lua/5.0/?.so;' ..
+                     package.cpath
+      
+     local rrd = require 'rrd'
+     rrd.update ("mydemo.rrd","N:12:13")
+     
+If we execute the program above we'll get:
+
+     $ lua t.lua
+      
+     lua: t.lua:27: opening 'mydemo.rrd': No such file or directory
+     stack traceback:
+           [C]: in function `update'
+           t.lua:27: in main chunk
+           [C]: ?
+
+=head2 Return Values
+
+The functions rrd.first, rrd.last, rrd.graph, rrd.info and rrd.fetch
+return their findings.
+
+B<rrd.first> returns a single INTEGER representing the timestamp of the
+first data sample in an RRA within an RRD file. Example returning the
+first timestamp of the third RRA (index 2):
+
+     local firstdate = rrd.first('example.rrd', '--rraindex', 2)
+
+B<rrd.last> returns a single INTEGER representing the last update time.
+
+     local lastupdate = rrd.last('example.rrd')
+
+B<rrd.graph> returns the x-size and y-size of the created image and a table
+with the results of the PRINT arguments.
+
+     local xsize, ysize, averages = rrd.graph ...
+     print(string.format("Image size: %dx%d", xsize, ysize)
+     print("Averages: ", table.concat(averages, ', '))
+
+B<rrd.info> returns a table where the keys and the values represent property
+names and property values of the RRD.
+
+     local info = rrd.info("test.rrd")
+     for key, value in pairs(info) do
+       print(key, ' = ', value)
+     end
+
+B<rrd.graphv> takes the same parameters as rrd.graph but it returns a table
+only. The table returned contains meta information about the graph, like
+its size as well as the position of the graph area on the image. When
+called with and empty filename, the contents of the graph will be returned
+in the table as well (key 'image').
+
+B<rrd.updatev> also returns a table. The keys of the table are strings
+formed by the concatenation of timestamp, RRA index and data source name
+for each consolidated data point (CDP) written to disk as a result of the
+current update call. The key values are CDP values.
+
+B<rrd.fetch> is the most complex of the pack regarding return values. It
+returns 5 values: the initial timestamp, the step, two parallel arrays
+containing the data source names and their data points respectively, and
+the final timestamp.
+
+     --require compat-5.1 if necessary
+    
+     package.cpath = '/usr/local/rrdtool-1.3.2/lib/lua/5.0/?.so;' ..
+                     package.cpath
+    
+     local rrd = require "rrd"
+     local first, last = rrd.first("test.rrd"), rrd.last("test.rrd")
+     local start, step, names, data =
+       rrd.fetch("test.rrd", "--start", first, "--end", last, "AVERAGE")
+     io.write(string.format("Start:       %s (%d)\n",
+                            os.date("%c", start),start))
+     io.write("Step size:   ", step, " seconds\n")
+     io.write("DS names:    ", table.concat(names, ', '), "\n")
+     io.write("Data points: ", #data[1], "\n")
+     io.write("Data:\n")
+     for i,dp in ipairs(data) do
+       io.write(os.date("%t", start), " (", start, "): ")
+       start = start + step
+       for j,v in ipairs(dp) do
+         io.write(v, " ")
+       end
+     io.write("\n")
+     end
+
+=head1 AUTHOR
+
+Fidelis Assis E<lt>fidelis@pobox.comE<gt>
+
+
+
diff --git a/program/doc/rrdpython.pod b/program/doc/rrdpython.pod
new file mode 100644 (file)
index 0000000..56fc347
--- /dev/null
@@ -0,0 +1,78 @@
+=head1 NAME
+
+rrdpython - About the RRD Python bindings
+
+=head1 SYNOPSIS
+
+ import rrdtool
+ rrdtool.create('/tmp/test.rrd', 'DS:foo:GAUGE:20:0:U')
+
+=head1 DESCRIPTION
+
+The B<rrdtool> functions are directly callable via the Python programming
+language. This wrapper implementation has been written from the scratch
+(without  SWIG)
+
+The API's expects strings and/or list of strings as parameters to the functions.
+Please refer to the other B<rrdtool> documentation for functions and valid arguments.
+
+=head1 EXAMPLES
+
+=head2 Example 1
+
+ import sys
+ sys.path.append('/path/to/rrdtool/lib/python2.6/site-packages/')
+ import rrdtool, tempfile
+
+ DAY = 86400
+ YEAR = 365 * DAY
+ fd,path = tempfile.mkstemp('.png')
+
+ rrdtool.graph(path,
+              '--imgformat', 'PNG',
+              '--width', '540',
+              '--height', '100',
+              '--start', "-%i" % YEAR,
+              '--end', "-1",
+              '--vertical-label', 'Downloads/Day',
+              '--title', 'Annual downloads',
+              '--lower-limit', '0',
+              'DEF:downloads=downloads.rrd:downloads:AVERAGE',
+              'AREA:downloads#990033:Downloads')
+
+ info = rrdtool.info('downloads.rrd')
+ print info['last_update']
+ print info['ds']['downloads']['minimal_heartbeat']
+
+=head2 Example 2
+
+ import sys
+ sys.path.append('/path/to/rrdtool/lib/python2.6/site-packages/')
+ import rrdtool
+
+ # in real life data_sources would be populated in loop or something similar
+ data_sources=[ 'DS:speed1:COUNTER:600:U:U',
+                'DS:speed2:COUNTER:600:U:U',
+                'DS:speed3:COUNTER:600:U:U' ]
+
+ rrdtool.create( 'speed.rrd',
+                 '--start', '920804400',
+                 data_sources,
+                 'RRA:AVERAGE:0.5:1:24',
+                 'RRA:AVERAGE:0.5:6:10' )
+
+If you use the B<site-python-install> make target you can drop to first sys.path.append
+line since the RRDtool module will be available everywhere.
+
+If RRDtool runs into trouble, it will throw an exception which you might want to catch.
+
+=head1 SEE ALSO
+
+rrdcreate, rrdupdate, rrdgraph, rrddump, rrdfetch, rrdtune, rrdlast,
+rrdxport, rrdinfo
+
+=head1 AUTHOR
+
+Hye-Shik Chang E<lt>perky@i18n.orgE<gt>
+
+Alan Milligan E<lt>alan.milligan@last-bastion.netE<gt>
diff --git a/program/doc/rrdresize.pod b/program/doc/rrdresize.pod
new file mode 100644 (file)
index 0000000..af285a5
--- /dev/null
@@ -0,0 +1,54 @@
+=head1 NAME
+
+rrdresize - alters the size of an RRA and creates a new .rrd file
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<resize> I<filename> I<rra-num>  B<GROW>I<|>B<SHRINK> I<rows>
+
+=head1 DESCRIPTION
+
+The B<resize> function is used to modify the number of rows in
+an B<RRA>.
+
+=over 8
+
+=item I<filename>
+
+the name of the B<RRD> you want to alter.
+
+=item I<rra-num>
+
+the B<RRA> you want to alter. You can find the number using B<rrdtool info>.
+
+=item B<GROW>
+
+used if you want to add extra rows to an RRA. The extra rows will be inserted
+as the rows that are oldest.
+
+=item B<SHRINK>
+
+used if you want to remove rows from an RRA. The rows that will be removed
+are the oldest rows.
+
+=item I<rows>
+
+the number of rows you want to add or remove.
+
+=back
+
+=head1 NOTES
+
+The new .rrd file, with the modified RRAs, is written to the file
+B<resize.rrd> in the current directory.  B<The original .rrd file is not
+modified>.
+
+It is possible to abuse this tool and get strange results
+by first removing some rows and then reinserting the same amount (effectively
+clearing them to be Unknown). You may thus end up with unknown data in one
+RRA while at the same timestamp this data is available in another RRA.
+
+=head1 AUTHOR
+
+Alex van den Bogaerdt <alex@vandenbogaerdt.nl>
+
diff --git a/program/doc/rrdrestore.pod b/program/doc/rrdrestore.pod
new file mode 100644 (file)
index 0000000..e098e0d
--- /dev/null
@@ -0,0 +1,38 @@
+=head1 NAME
+
+rrdrestore - Restore the contents of an RRD from its XML dump format
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<restore> I<filename.xml> I<filename.rrd>
+S<[B<--range-check>|B<-r>]>
+
+=head1 DESCRIPTION
+
+The B<restore> function reads the XML representation of an RRD and converts
+it to the native B<RRD> format.
+
+=over 8
+
+=item I<filename.xml>
+
+The name of the B<XML> file you want to restore.
+
+=item I<filename.rrd>
+
+The name of the B<RRD> to restore.
+
+=item B<--range-check>|B<-r>
+
+Make sure the values in the RRAs do not exceed the limits defined for
+the various data sources.
+
+=item B<--force-overwrite>|B<-f>
+
+Allows B<RRDtool> to overwrite the destination B<RRD>.
+
+=back
+
+=head1 AUTHOR
+
+Tobias Oetiker <tobi@oetiker.ch>
diff --git a/program/doc/rrdruby.pod b/program/doc/rrdruby.pod
new file mode 100644 (file)
index 0000000..c1c246e
--- /dev/null
@@ -0,0 +1,86 @@
+=head1 NAME
+
+rrdruby - About the RRD Ruby bindings
+
+=head1 SYNOPSIS
+
+ require "RRD"
+ RRD.create(
+    rrd,
+    "--step", "300",
+    "DS:a:GAUGE:600:U:U",
+    "DS:b:GAUGE:600:U:U",
+    "RRA:AVERAGE:0.5:1:300")
+
+=head1 DESCRIPTION
+
+The B<rrdtool> functions are directly callable via the Ruby programming
+language. This wrapper implementation has been written from the scratch
+(without  SWIG)
+
+The API's simply expects string parameters to the functions.  Please refer
+to the other B<rrdtool> documentation for functions and valid arguments.
+
+=head1 EXAMPLE
+
+ $: << '/path/to/rrdtool/lib/ruby/1.8/i386-linux'
+ require "RRD" 
+
+ name = "test"
+ rrd = "#{name}.rrd"
+ start = Time.now.to_i
+
+ RRD.create(    
+    rrd,
+    "--start", "#{start - 1}",
+    "--step", "300",
+        "DS:a:GAUGE:600:U:U",
+    "DS:b:GAUGE:600:U:U",
+    "RRA:AVERAGE:0.5:1:300")
+ puts
+
+ puts "updating #{rrd}"
+ start.to_i.step(start.to_i + 300 * 300, 300) { |i|
+    RRD.update(rrd, "#{i}:#{rand(100)}:#{Math.sin(i / 800) * 50 + 50}")
+ }
+ puts
+
+ puts "fetching data from #{rrd}"
+ (fstart, fend, data, step) = RRD.fetch(rrd, "--start", start.to_s, "--end", 
+      (start + 300 * 300).to_s, "AVERAGE")
+ puts "got #{data.length} data points from #{fstart} to #{fend}"
+ puts
+
+ puts "generating graph #{name}.png"
+ RRD.graph(
+   "#{name}.png",
+    "--title", " RubyRRD Demo", 
+    "--start", "#{start+3600}",
+    "--end", "start + 1000 min",
+    "--interlace", 
+    "--imgformat", "PNG",
+    "--width=450",
+    "DEF:a=#{rrd}:a:AVERAGE",
+    "DEF:b=#{rrd}:b:AVERAGE",
+    "CDEF:line=TIME,2400,%,300,LT,a,UNKN,IF",
+    "AREA:b#00b6e4:beta",
+    "AREA:line#0022e9:alpha",
+    "LINE3:line#ff0000") 
+ puts
+
+If you use the B<--ruby-site-install> configure option you can drop the $:
+line since the RRDtool module will be found automatically.
+
+If RRDtool runs into trouble, it will throw an exception which you might
+want to catch.
+
+=head1 SEE ALSO
+
+rrdcreate, rrdupdate, rrdgraph, rrddump, rrdfetch, rrdtune, rrdlast,
+rrdxport, rrdinfo
+
+=head1 AUTHOR
+
+Loïs Lherbier E<lt>lois.lherbier@covadis.chE<gt>
+
+Miles Egan E<lt>miles@caddr.comE<gt>
diff --git a/program/doc/rrdthreads.pod b/program/doc/rrdthreads.pod
new file mode 100644 (file)
index 0000000..71506e9
--- /dev/null
@@ -0,0 +1,144 @@
+=head1 NAME
+
+rrdthreads - Provisions for linking the RRD library to use in multi-threaded programs
+
+=head1 SYNOPSIS
+
+Using librrd in multi-threaded programs requires some extra
+precautions, as the RRD library in its original form was not
+thread-safe at all. This document describes requirements and pitfalls
+on the way to use the multi-threaded version of librrd in your own
+programs. It also gives hints for future RRD development to keep the
+library thread-safe.
+
+Currently only some RRD operations are implemented in a thread-safe
+way. They all end in the usual "C<_r>" suffix.
+
+=head1 DESCRIPTION
+
+In order to use librrd in multi-threaded programs you must:
+
+=over
+
+=item *
+
+Link with F<librrd_th> instead of F<librrd> (use C<-lrrd_th> when
+linking)
+
+=item *
+
+Use the "C<_r>" functions instead of the normal API-functions
+
+=item *
+
+Do not use any at-style time specifications. Parsing of such time
+specifications is terribly non-thread-safe.
+
+=item *
+
+Never use non *C<_r> functions unless it is explicitly documented that
+the function is tread-safe.
+
+=item *
+
+Every thread SHOULD call C<rrd_get_context()> before its first call to
+any C<librrd_th> function in order to set up thread specific data. This
+is not strictly required, but it is the only way to test if memory
+allocation can be done by this function. Otherwise the program may die
+with a SIGSEGV in a low-memory situation.
+
+=item *
+
+Always call C<rrd_error_clear()> before any call to the
+library. Otherwise the call might fail due to some earlier error.
+
+=back
+
+=head2 NOTES FOR RRD CONTRIBUTORS
+
+Some precautions must be followed when developing RRD from now on:
+
+=over
+
+=item *
+
+Only use thread-safe functions in library code. Many often used libc
+functions aren't thread-safe. Take care in the following
+situations or when using the following library functions:
+
+=over
+
+=item *
+
+Direct calls to C<strerror()> must be avoided: use C<rrd_strerror()>
+instead, it provides a per-thread error message.
+
+=item *
+
+The C<getpw*>, C<getgr*>, C<gethost*> function families (and some more
+C<get*> functions) are not thread-safe: use the *C<_r> variants
+
+=item *
+
+Time functions: C<asctime>, C<ctime>, C<gmtime>, C<localtime>: use
+*C<_r> variants
+
+=item *
+
+C<strtok>: use C<strtok_r>
+
+=item *
+
+C<tmpnam>: use C<tmpnam_r>
+
+=item *
+
+Many others (lookup documentation)
+
+=back
+
+=item *
+
+A header file named F<rrd_is_thread_safe.h> is provided
+that works with the GNU C-preprocessor to "poison" some of the most
+common non-thread-safe functions using the C<#pragma GCC poison>
+directive. Just include this header in source files you want to keep
+thread-safe.
+
+=item *
+
+Do not introduce global variables!
+
+If you really, really have to use a global variable you may add a new
+field to the C<rrd_context> structure and modify F<rrd_error.c>,
+F<rrd_thread_safe.c> and F<rrd_non_thread_safe.c>
+
+=item *
+
+Do not use C<getopt> or C<getopt_long> in *C<_r> (neither directly nor
+indirectly).
+
+C<getopt> uses global variables and behaves badly in a multi-threaded
+application when called concurrently. Instead provide a *_r function
+taking all options as function parameters. You may provide argc and
+**argv arguments for variable length argument lists. See
+C<rrd_update_r> as an example.
+
+=item *
+
+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
+operation (e.g., C<rrd_create>, but not in C<rrd_create_r>)
+
+=back
+
+=head2 CURRENTLY IMPLEMENTED THREAD SAFE FUNCTIONS
+
+Currently there exist thread-safe variants of C<rrd_update>,
+C<rrd_create>, C<rrd_dump>, C<rrd_info>, C<rrd_last>, and C<rrd_fetch>.
+
+=head1 AUTHOR
+
+Peter Stamfest E<lt>peter@stamfest.atE<gt>
diff --git a/program/doc/rrdtool-dump.dtd b/program/doc/rrdtool-dump.dtd
new file mode 100644 (file)
index 0000000..4dc2ca2
--- /dev/null
@@ -0,0 +1,39 @@
+<!-- rrdtool-dump.dtd -->
+<!-- wolfgang{dot}schrimm{at}urz{dot}uni-heidelberg{dot}de -->
+
+<!-- root element -->
+<!ELEMENT rrd (version, step, lastupdate, ds+, rra+)>
+
+<!-- rrd's children -->
+<!ELEMENT version (#PCDATA)>
+<!ELEMENT step (#PCDATA)>
+<!ELEMENT lastupdate (#PCDATA)>
+<!-- There are two different elements with the same name -->
+<!-- /rrd/ds and /rrd/rra/cdp_prep/ds                    -->
+<!ELEMENT ds ((name, type, minimal_heartbeat, min, max, last_ds, value, 
+unknown_sec)|(value, unknown_datapoints))>
+<!ELEMENT rra (cf, pdp_per_row, xff, cdp_prep, database)>
+
+<!-- ds's children -->
+<!ELEMENT name (#PCDATA)>
+<!ELEMENT type (#PCDATA)>
+<!ELEMENT minimal_heartbeat (#PCDATA)>
+<!ELEMENT min (#PCDATA)>
+<!ELEMENT max (#PCDATA)>
+<!ELEMENT last_ds (#PCDATA)>
+<!ELEMENT unknown_sec (#PCDATA)>
+<!ELEMENT unknown_datapoints (#PCDATA)>
+<!-- There are two different elements with the same name -->
+<!-- /rrd/ds/value and /rrd/rra/cdp_prep/ds/value        -->
+<!ELEMENT value (#PCDATA)>
+
+<!-- rra's children -->
+<!ELEMENT cf (#PCDATA)>
+<!ELEMENT pdp_per_row (#PCDATA)>
+<!ELEMENT xff (#PCDATA)>
+<!ELEMENT cdp_prep (ds+)>
+<!ELEMENT database (row+)>
+
+<!-- database's children -->
+<!ELEMENT row (v+)>
+<!ELEMENT v (#PCDATA)>
diff --git a/program/doc/rrdtool-dump.xsd b/program/doc/rrdtool-dump.xsd
new file mode 100644 (file)
index 0000000..22cd7ca
--- /dev/null
@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+       XML schema definition for the RRDTool dump output.
+
+       Author: 
+               Tobias Lindenmann <tobias.lindenmann at 1und1.de>
+-->
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
+       xmlns:ns="http://oss.oetiker.ch/rrdtool/rrdtool-dump.xml" 
+       targetNamespace="http://oss.oetiker.ch/rrdtool/rrdtool-dump.xml" 
+       elementFormDefault="qualified">
+       
+       <!-- Root element -->
+       <xsd:element name="rrd" type="ns:RrdType">
+               <!-- Check datasource name of unique. -->
+               <xsd:unique name="Ds.Name">
+                       <xsd:selector xpath="ns:ds/ns:name"/>
+                       <xsd:field xpath="."/>
+               </xsd:unique>
+       </xsd:element>
+       
+       <!-- Start of complexTypes -->
+       <xsd:complexType name="RrdType">
+               <xsd:sequence>
+                       <!-- RRD file version/RRD Archiv version number-->
+                       <xsd:element name="version" type="xsd:nonNegativeInteger"/>
+                       <!-- The primary RRD Archiv step in seconds.-->
+                       <xsd:element name="step" type="xsd:nonNegativeInteger"/>
+                       <!-- The unixtime from the last rrd_update.-->
+                       <xsd:element name="lastupdate" type="xsd:nonNegativeInteger"/>
+                       <xsd:element name="ds" type="ns:DsType" minOccurs="1" maxOccurs="unbounded"/>
+                       <!-- Round Robin Archive (rra). This is the place where the
+                            consolidated data points (cdp) get stored. The data is
+                            organized in rows (row) and columns (col). The Round Robin
+                            Archive got its name from the method data is stored in
+                            there. An RRD database can contain several Round Robin
+                            Archives. Each Round Robin Archive can have a different row
+                            spacing along the time axis (pdp_cnt) and a different
+                            consolidation function (cf) used to build its consolidated
+                            data points (cdp). -->
+                       <xsd:element name="rra" type="ns:RraType" minOccurs="1" maxOccurs="unbounded"/>
+               </xsd:sequence>
+       </xsd:complexType>
+       
+       <xsd:complexType name="DsType">
+               <xsd:sequence>
+                       <!-- The name of the data source. -->
+                       <xsd:element name="name" type="ns:DataSourceNameType"/>
+                       <!-- The Data Source Type (GAUGE, COUNTER, DERIVE, ABSOLUTE, COMPUTE)
+                            defines the applied to Build Primary Data Points from the 
+                            input provided by the data sources (ds). -->
+                       <xsd:element name="type" type="ns:DataSourceType"/>
+                       <!-- Chocie one groups of elements. -->
+                       <xsd:choice>
+                               <!-- Group with the elements min,max and heartbeat_min. -->
+                               <xsd:group ref="ns:Ds.NormalType"/>
+                               <!-- Group with the element cdef.-->
+                               <xsd:group ref="ns:Ds.CdefType"/>
+                       </xsd:choice>
+                       <xsd:element name="last_ds" type="ns:NumberWithUNKNType"/>
+                       <!-- Current value of the primary data point, this depends on the data source type. -->
+                       <xsd:element name="value" type="ns:LexicalNumberWithNaNType"/>
+                       <!-- How many seconds of the current 
+                                       * pdp value is unknown data?-->
+                       <xsd:element name="unknown_sec" type="xsd:nonNegativeInteger"/>
+               </xsd:sequence>
+       </xsd:complexType>
+
+       <xsd:complexType name="RraType">
+               <xsd:sequence>
+                       <!-- Consolidation Function (cf). An arbitrary Consolidation Function (cf)
+                            (averaging, min, max) is applied to the primary data points (pdp) to
+                            calculate the consolidated data point. -->
+                       <xsd:element name="cf" type="ns:ConsolidationFunctionType"/>
+                       <xsd:element name="pdp_per_row" type="xsd:nonNegativeInteger"/>
+                       <xsd:element name="params">
+                               <!-- Chocie one groups of elements. -->
+                               <xsd:complexType>
+                                       <xsd:choice>
+                                               <xsd:group ref="ns:Params.HwPredictType"/> 
+                                               <xsd:group ref="ns:Params.SeasonalType"/>
+                                               <xsd:group ref="ns:Params.FailuresType"/>
+                                               <xsd:group ref="ns:Params.DevPredictType"/>
+                                               <xsd:group ref="ns:Params.AvgMinMaxType"/>
+                                       </xsd:choice>
+                               </xsd:complexType>
+                       </xsd:element>
+                       <!-- Data prep area for cdp values -->
+                       <xsd:element name="cdp_prep" type="ns:CdpPrepType"/>
+                       <xsd:element name="database" type="ns:DatabaseType"/>
+               </xsd:sequence>
+       </xsd:complexType>
+
+       <xsd:complexType name="DatabaseType">
+               <xsd:sequence>
+                       <xsd:element name="row" type="ns:VType" minOccurs="1" maxOccurs="unbounded"/>
+               </xsd:sequence>
+       </xsd:complexType>
+
+       <xsd:complexType name="VType">
+               <xsd:sequence>
+                       <xsd:element name="v" type="ns:LexicalNumberWithNaNType" minOccurs="1" maxOccurs="unbounded"/>
+               </xsd:sequence> 
+       </xsd:complexType>
+
+       <xsd:complexType name="CdpPrepType">
+               <xsd:sequence>
+                       <xsd:element name="ds" minOccurs="1" maxOccurs="unbounded">
+                               <xsd:complexType>
+                                       <xsd:sequence>
+                                               <xsd:group ref="ns:Ds.Cdp"/>
+                                               <!-- Chocie one groups of elements. -->
+                                               <xsd:choice>
+                                                       <xsd:group ref="ns:Ds.Cdp.HwPredictType"/>
+                                                       <xsd:group ref="ns:Ds.Cdp.SeasonalType"/>
+                                                       <xsd:group ref="ns:Ds.Cdp.FailuresType"/>
+                                                       <xsd:group ref="ns:Ds.Cdp.AvgMinMaxType"/>
+                                               </xsd:choice>
+                                       </xsd:sequence>
+                               </xsd:complexType>
+                       </xsd:element>
+                </xsd:sequence>
+       </xsd:complexType>
+       <!-- End of complexTypes -->
+
+       <!-- Start of groups -->
+       <!-- Start of groups for the element rrd/ds. -->
+       <xsd:group name="Ds.NormalType">
+               <xsd:sequence>
+                       <!-- Minimum required heartbeat. A
+                                     * data source must provide input at
+                                     * least every ds_mrhb seconds,
+                                     * otherwise it is regarded dead and
+                                     * will be set to UNKNOWN -->
+                       <xsd:element name="minimal_heartbeat" type="xsd:integer"/>
+                       <!-- Min and max define the expected range values for data supplied by a data source. -->
+                       <xsd:element name="min" type="ns:LexicalNumberWithNaNType"/>
+                       <xsd:element name="max" type="ns:LexicalNumberWithNaNType"/>
+               </xsd:sequence>
+       </xsd:group>
+
+       <xsd:group name="Ds.CdefType">
+               <xsd:sequence>
+                       <xsd:element name="cdef" type="xsd:string"/>
+               </xsd:sequence>
+       </xsd:group>
+       <!-- End of groups for the element rrd/ds. -->
+       <!-- Start of groups for the element rrd/rra/cd_prep/ds. -->
+       <xsd:group name="Ds.Cdp.HwPredictType">
+               <xsd:sequence>
+                       <xsd:element name="intercept" type="ns:LexicalNumberWithNaNType"/>
+                       <xsd:element name="last_intercept" type="ns:LexicalNumberWithNaNType"/>
+                       <xsd:element name="slope" type="ns:LexicalNumberWithNaNType"/>
+                       <xsd:element name="last_slope" type="ns:LexicalNumberWithNaNType"/>
+                       <xsd:element name="nan_count" type="xsd:integer"/>
+                       <xsd:element name="last_nan_count" type="xsd:integer"/>
+               </xsd:sequence>
+       </xsd:group>
+
+       <xsd:group name="Ds.Cdp.SeasonalType">
+               <xsd:sequence>
+                       <xsd:element name="seasonal" type="ns:LexicalNumberWithNaNType"/>
+                       <xsd:element name="last_seasonal" type="ns:LexicalNumberWithNaNType"/>
+                       <xsd:element name="init_flag" type="xsd:integer"/>
+               </xsd:sequence>
+       </xsd:group>
+
+       <xsd:group name="Ds.Cdp.FailuresType">
+               <xsd:sequence>
+                       <!-- History failures message.-->
+                       <xsd:element name="history" type="xsd:string"/>
+               </xsd:sequence>
+       </xsd:group>
+
+       <xsd:group name="Ds.Cdp.AvgMinMaxType">
+               <xsd:sequence>
+                       <xsd:element name="value" type="ns:LexicalNumberWithNaNType"/>
+                       <xsd:element name="unknown_datapoints" type="xsd:integer"/>
+               </xsd:sequence>
+       </xsd:group>
+
+       <xsd:group name="Ds.Cdp">
+               <xsd:sequence>
+                       <xsd:element name="primary_value" type="ns:LexicalNumberWithNaNType"/>
+                       <xsd:element name="secondary_value" type="ns:LexicalNumberWithNaNType"/>
+               </xsd:sequence>
+       </xsd:group>
+       <!-- End of groups for the element cd_prep/ds. -->
+       <!-- Start of groups for the element params. -->
+       <xsd:group name="Params.HwPredictType">
+               <xsd:sequence>
+                        <xsd:element name="hw_alpha" type="ns:LexicalNumberType"/>
+                        <xsd:element name="hw_beta" type="ns:LexicalNumberType"/>
+                       <xsd:element name="dependent_rra_idx" type="xsd:integer"/>
+               </xsd:sequence>
+       </xsd:group>
+       
+       <xsd:group name="Params.SeasonalType">
+               <xsd:sequence>
+                        <xsd:element name="seasonal_gamma" type="ns:LexicalNumberType"/>
+                        <xsd:element name="seasonal_smooth_idx" type="xsd:integer"/>
+                       <xsd:element name="smoothing_window" type="ns:LexicalNumberType"/>
+                       <xsd:element name="dependent_rra_idx" type="xsd:integer"/>
+               </xsd:sequence>
+       </xsd:group>
+
+       <xsd:group name="Params.FailuresType">
+               <xsd:sequence>
+                        <xsd:element name="delta_pos" type="ns:LexicalNumberType"/>
+                        <xsd:element name="delta_neg" type="ns:LexicalNumberType"/>
+                       <xsd:element name="window_len" type="xsd:integer"/>
+                       <xsd:element name="failure_threshold" type="xsd:integer"/>
+               </xsd:sequence>
+       </xsd:group>
+
+       <xsd:group name="Params.DevPredictType">
+               <xsd:sequence>
+                       <xsd:element name="dependent_rra_idx" type="xsd:integer"/>
+               </xsd:sequence>
+       </xsd:group>
+
+       <xsd:group name="Params.AvgMinMaxType">
+               <xsd:sequence>
+                       <!-- The xfiles factor defines what part of a consolidation interval 
+                            may be made up from *UNKNOWN* data while the consolidated value
+                            is still regarded as known. It is given as the ratio of allowed
+                            *UNKNOWN* PDPs to the number of PDPs in the interval. Thus, it 
+                            ranges from 0 to 1 (exclusive). -->
+                       <xsd:element name="xff" type="ns:LexicalNumberType"/>
+               </xsd:sequence>
+       </xsd:group>
+       <!-- End of Groups for the element params. -->
+       <!-- End of groups -->
+       
+       <!-- Start of simpleTypes -->
+       <!-- Allowed data source types. -->
+       <!-- @see http://oss.oetiker.ch/rrdtool/doc/rrdcreate.en.html-->
+       <xsd:simpleType name="DataSourceType">
+               <xsd:restriction base="xsd:string">
+                       <xsd:enumeration value="GAUGE"/>
+                       <xsd:enumeration value="COUNTER"/>
+                       <xsd:enumeration value="DERIVE"/>
+                       <xsd:enumeration value="ABSOLUTE"/>
+                       <xsd:enumeration value="COMPUTE"/>
+               </xsd:restriction>
+       </xsd:simpleType>
+       
+       <!-- A ds-name must be 1 to 19 characters long in the characters [a-zA-Z0-9_]. -->
+       <!-- @see http://oss.oetiker.ch/rrdtool/doc/rrdcreate.en.html-->
+       <xsd:simpleType name="DataSourceNameType">
+               <xsd:restriction base="xsd:string">
+                       <xsd:pattern value="[a-zA-Z0-9_]{1,19}"/>
+               </xsd:restriction>
+       </xsd:simpleType>
+
+       <!-- Allowed consolidation function (cf). -->
+       <!-- @see http://oss.oetiker.ch/rrdtool/doc/rrdcreate.en.html-->
+       <xsd:simpleType name="ConsolidationFunctionType">
+               <xsd:restriction base="xsd:string">
+                       <xsd:enumeration value="AVERAGE"/>
+                       <xsd:enumeration value="MIN"/>
+                       <xsd:enumeration value="MAX"/>
+                       <xsd:enumeration value="LAST"/>
+               </xsd:restriction>
+       </xsd:simpleType>
+       
+       <!-- Allowed a lexical number or the string "NaN".-->
+        <xsd:simpleType name="LexicalNumberWithNaNType">
+                <xsd:restriction base="xsd:string">
+                        <xsd:pattern value="([-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?|NaN)"/>
+                </xsd:restriction>
+        </xsd:simpleType>
+
+       <!-- Allowed only a lexical number. -->
+        <xsd:simpleType name="LexicalNumberType">
+                <xsd:restriction base="xsd:string">
+                        <xsd:pattern value="[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?"/>
+                </xsd:restriction>
+        </xsd:simpleType>
+       
+       <!-- Allowed a number or the string "UNKN"  or "U". -->
+       <xsd:simpleType name="NumberWithUNKNType">
+               <xsd:restriction base="xsd:string">
+                       <xsd:pattern value="([\d]+|UNKN|U)"/>
+               </xsd:restriction>
+       </xsd:simpleType>
+       <!-- End of simpleTypes -->
+</xsd:schema>
diff --git a/program/doc/rrdtool-xport.dtd b/program/doc/rrdtool-xport.dtd
new file mode 100644 (file)
index 0000000..0e68015
--- /dev/null
@@ -0,0 +1,32 @@
+<!-- rrdtool-xport.dtd                                     -->
+<!-- the attributes of the row and the t elements are used -->
+<!-- in the examples/shared-demo.pl, but not in the output -->
+<!-- of the native xport command.                          -->
+<!-- wolfgang{dot}schrimm{at}urz{dot}uni-heidelberg{dot}de -->
+
+<!-- root element -->
+<!ELEMENT xport (meta, data)>
+
+<!-- root's children -->
+<!ELEMENT meta (start, step, end, rows, columns, legend)>
+<!ELEMENT data (row+)>
+
+<!-- meta's children -->
+<!ELEMENT start (#PCDATA)>
+<!ELEMENT step (#PCDATA)>
+<!ELEMENT end (#PCDATA)>
+<!ELEMENT rows (#PCDATA)>
+<!ELEMENT columns (#PCDATA)>
+<!ELEMENT legend (entry+)>
+
+<!-- legend's children -->
+<!ELEMENT entry (#PCDATA)>
+
+<!-- data's children -->
+<!ELEMENT row (t, v+)>
+<!ATTLIST row id CDATA #IMPLIED>
+
+<!-- row's children -->
+<!ELEMENT t (#PCDATA)>
+<!ATTLIST t is CDATA #IMPLIED>
+<!ELEMENT v (#PCDATA)>
diff --git a/program/doc/rrdtool.pod b/program/doc/rrdtool.pod
new file mode 100644 (file)
index 0000000..0d701ca
--- /dev/null
@@ -0,0 +1,324 @@
+=head1 NAME
+
+rrdtool - Round Robin Database Tool
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<-> [workdir]| I<function>
+
+=head1 DESCRIPTION
+
+=head2 OVERVIEW
+
+It is pretty easy to gather status information from all sorts of
+things, ranging from the temperature in your office to the number of
+octets which have passed through the FDDI interface of your
+router. But it is not so trivial to store this data in an efficient and
+systematic manner. This is where B<RRDtool> comes in handy. It lets you
+I<log and analyze> the data you gather from all kinds of data-sources
+(B<DS>). The data analysis part of RRDtool is based on the ability to
+quickly generate graphical representations of the data values
+collected over a definable time period.
+
+In this man page you will find general information on the design and
+functionality of the Round Robin Database Tool (RRDtool). For a more
+detailed description of how to use the individual functions of
+B<RRDtool> check the corresponding man page.
+
+For an introduction to the usage of RRDtool make sure you consult the
+L<rrdtutorial>.
+
+=head2 FUNCTIONS
+
+While the man pages talk of command line switches you have to set in
+order to make B<RRDtool> work it is important to note that
+B<RRDtool> can be remotely controlled through a set of pipes. This
+saves a considerable amount of startup time when you plan to make
+B<RRDtool> do a lot of things quickly. Check the section on L<Remote_Control>
+further down. There is also a number of language bindings
+for RRDtool which allow you to use it directly from Perl, python, Tcl,
+PHP, etc.
+
+=over 8
+
+=item B<create>
+
+Set up a new Round Robin Database (RRD). Check L<rrdcreate>.
+
+=item B<update>
+
+Store new data values into an RRD. Check L<rrdupdate>.
+
+=item B<updatev>
+
+Operationally equivalent to B<update> except for output. Check L<rrdupdate>.
+
+=item B<graph>
+
+Create a graph from data stored in one or several RRDs. Apart from
+generating graphs, data can also be extracted to stdout. Check L<rrdgraph>.
+
+=item B<dump>
+
+Dump the contents of an RRD in plain ASCII. In connection with restore
+you can use this to move an RRD from one computer architecture to
+another.  Check L<rrddump>.
+
+=item B<restore>
+
+Restore an RRD in XML format to a binary RRD. Check L<rrdrestore>
+
+=item B<fetch>
+
+Get data for a certain time period from a RRD. The graph function
+uses fetch to retrieve its data from an RRD. Check L<rrdfetch>.
+
+=item B<tune>
+
+Alter setup of an RRD. Check L<rrdtune>.
+
+=item B<last>
+
+Find the last update time of an RRD. Check L<rrdlast>.
+
+=item B<info>
+
+Get information about an RRD. Check L<rrdinfo>.
+
+=item B<rrdresize>
+
+Change the size of individual RRAs. This is dangerous! Check L<rrdresize>.
+
+=item B<xport>
+
+Export data retrieved from one or several RRDs. Check L<rrdxport>.
+
+=item B<flushcached>
+
+Flush the values for a specific RRD file from memory. Check L<rrdflushcached>.
+
+=item B<rrdcgi>
+
+This is a standalone tool for producing RRD graphs on the fly. Check
+L<rrdcgi>.
+
+=back
+
+=head2 HOW DOES RRDTOOL WORK?
+
+=over 8
+
+=item Data Acquisition
+
+When monitoring the state of a system, it is convenient to have the
+data available at a constant time interval. Unfortunately, you may not
+always be able to fetch data at exactly the time you want
+to. Therefore B<RRDtool> lets you update the log file at any time you
+want. It will automatically interpolate the value of the data-source
+(B<DS>) at the latest official time-slot (interval) and write this
+interpolated value to the log. The original value you have supplied is
+stored as well and is also taken into account when interpolating the
+next log entry.
+
+=item Consolidation
+
+You may log data at a 1 minute interval, but you might also be
+interested to know the development of the data over the last year. You
+could do this by simply storing the data in 1 minute intervals for the
+whole year. While this would take considerable disk space it would
+also take a lot of time to analyze the data when you wanted to create
+a graph covering the whole year. B<RRDtool> offers a solution to this
+problem through its data consolidation feature. When setting up an
+Round Robin Database (B<RRD>), you can define at which interval this
+consolidation should occur, and what consolidation function (B<CF>)
+(average, minimum, maximum, total, last) should be used to build the
+consolidated values (see rrdcreate). You can define any number of
+different consolidation setups within one B<RRD>. They will all be
+maintained on the fly when new data is loaded into the B<RRD>.
+
+=item Round Robin Archives
+
+Data values of the same consolidation setup are stored into Round
+Robin Archives (B<RRA>). This is a very efficient manner to store data
+for a certain amount of time, while using a known and constant amount
+of storage space.
+
+It works like this: If you want to store 1'000 values in 5 minute
+interval, B<RRDtool> will allocate space for 1'000 data values and a
+header area. In the header it will store a pointer telling which slots
+(value) in the storage area was last written to. New values are
+written to the Round Robin Archive in, you guessed it, a round robin
+manner. This automatically limits the history to the last 1'000 values
+(in our example). Because you can define several B<RRA>s within a
+single B<RRD>, you can setup another one, for storing 750 data values
+at a 2 hour interval, for example, and thus keep a log for the last
+two months at a lower resolution.
+
+The use of B<RRA>s guarantees that the B<RRD> does not grow over
+time and that old data is automatically eliminated. By using the
+consolidation feature, you can still keep data for a very long time,
+while gradually reducing the resolution of the data along the time
+axis.
+
+Using different consolidation functions (B<CF>) allows you to store
+exactly the type of information that actually interests you: the maximum
+one minute traffic on the LAN, the minimum temperature of your wine cellar,
+the total minutes of down time, etc.
+
+=item Unknown Data
+
+As mentioned earlier, the B<RRD> stores data at a constant
+interval. Sometimes it may happen that no new data is available when a
+value has to be written to the B<RRD>. Data acquisition may not be
+possible for one reason or other. With B<RRDtool> you can handle these
+situations by storing an I<*UNKNOWN*> value into the database. The
+value 'I<*UNKNOWN*>' is supported through all the functions of the
+tool. When consolidating a data set, the amount of I<*UNKNOWN*> data
+values is accounted for and when a new consolidated value is ready to
+be written to its Round Robin Archive (B<RRA>), a validity check is
+performed to make sure that the percentage of unknown values in the
+data point is above a configurable level. If not, an I<*UNKNOWN*> value
+will be written to the B<RRA>.
+
+=item Graphing
+
+B<RRDtool> allows you to generate reports in numerical and
+graphical form based on the data stored in one or several
+B<RRD>s. The graphing feature is fully configurable. Size, color and
+contents of the graph can be defined freely. Check L<rrdgraph>
+for more information on this.
+
+=item Aberrant Behavior Detection
+
+by Jake Brutlag
+
+B<RRDtool> provides the building blocks for near real-time aberrant
+behavior detection. These components include:
+
+=over
+
+=item *
+
+An algorithm for predicting the value of a time series one time step
+into the future.
+
+=item *
+
+A measure of deviation between predicted and observed values.
+
+=item *
+
+A mechanism to decide if and when an observed value or sequence of
+observed values is I<too deviant> from the predicted value(s).
+
+=back
+
+Here is a brief explanation of these components:
+
+The Holt-Winters time series forecasting algorithm is an on-line (or
+incremental) algorithm that adaptively predicts future observations in
+a time series. Its forecast is the sum of three components: a baseline
+(or intercept), a linear trend over time (or slope), and a seasonal
+coefficient (a periodic effect, such as a daily cycle). There is one
+seasonal coefficient for each time point in the period (cycle). After
+a value is observed, each of these components is updated via
+exponential smoothing. This means that the algorithm "learns" from
+past values and uses them to predict the future. The rate of
+adaptation is governed by 3 parameters, alpha (intercept), beta
+(slope), and gamma (seasonal). The prediction can also be viewed as a
+smoothed value for the time series.
+
+The measure of deviation is a seasonal weighted absolute
+deviation. The term I<seasonal> means deviation is measured separately
+for each time point in the seasonal cycle. As with Holt-Winters
+forecasting, deviation is predicted using the measure computed from
+past values (but only at that point in the seasonal cycle). After the
+value is observed, the algorithm learns from the observed value via
+exponential smoothing. Confidence bands for the observed time series
+are generated by scaling the sequence of predicted deviation values
+(we usually think of the sequence as a continuous line rather than a
+set of discrete points).
+
+Aberrant behavior (a potential failure) is reported whenever the
+number of times the observed value violates the confidence bands meets
+or exceeds a specified threshold within a specified temporal window
+(e.g. 5 violations during the past 45 minutes with a value observed
+every 5 minutes).
+
+This functionality is embedded in a set of related B<RRAs>. In
+particular, a FAILURES B<RRA> logs potential failures. With these data
+you could, for example, use a front-end application to B<RRDtool> to
+initiate real-time alerts.
+
+For a detailed description on how to set this up, see L<rrdcreate>.
+
+=back
+
+=head2 REMOTE CONTROL
+
+When you start B<RRDtool> with the command line option 'B<->' it waits
+for input via standard input (STDIN). With this feature you can
+improve performance by attaching B<RRDtool> to another process (MRTG
+is one example) through a set of pipes. Over these pipes B<RRDtool>
+accepts the same arguments as on the command line and some special
+commands like B<quit, cd, mkdir> and B<ls>. For detailed help on the
+server commands type:
+
+   rrdtool help cd|mkdir|pwd|ls|quit
+
+When a command is completed, RRDtool will print the string  'C<OK>',
+followed by timing information of the form B<u:>I<usertime>
+B<s:>I<systemtime>. Both values are the running totals of seconds since
+RRDtool was started. If an error occurs, a line of the form 'C<ERROR:>
+I<Description of error>' will be printed instead. B<RRDtool> will not abort,
+unless something really serious happens. If
+a B<workdir> is specified and the UID is 0, RRDtool will do a chroot to that
+workdir. If the UID is not 0, RRDtool only changes the current directory to
+B<workdir>.
+
+=head2 RRD Server
+
+If you want to create a RRD-Server, you must choose a TCP/IP Service
+number and add them to I</etc/services> like this:
+
+ rrdsrv      13900/tcp                       # RRD server
+
+Attention: the TCP port 13900 isn't officially registered for
+rrdsrv. You can use any unused port in your services file, but the
+server and the client system must use the same port, of course.
+
+With this configuration you can add RRDtool as meta-server to
+I</etc/inetd.conf>. For example:
+
+ rrdsrv stream tcp nowait root /opt/rrd/bin/rrdtool rrdtool - /var/rrd
+
+Don't forget to create the database directory /var/rrd and
+reinitialize your inetd.
+
+If all was setup correctly, you can access the server with Perl
+sockets, tools like netcat, or in a quick interactive test by using
+'telnet localhost rrdsrv'.
+
+B<NOTE:> that there is no authentication with this feature! Do not setup
+such a port unless you are sure what you are doing.
+
+=head1 RRDCACHED, THE CACHING DAEMON
+
+For very big setups, updating thousands of RRD files often becomes a serious IO
+problem. If you run into such problems, you might want to take a look at
+L<rrdcached>, a caching daemon for RRDtool which may help you lessen the
+stress on your disks.
+
+=head1 SEE ALSO
+
+rrdcreate, rrdupdate, rrdgraph, rrddump, rrdfetch, rrdtune, rrdlast, rrdxport,
+rrdflushcached, rrdcached
+
+=head1 BUGS
+
+Bugs? Features!
+
+=head1 AUTHOR
+
+Tobias Oetiker <tobi@oetiker.ch>
+
diff --git a/program/doc/rrdtune.pod b/program/doc/rrdtune.pod
new file mode 100644 (file)
index 0000000..859c143
--- /dev/null
@@ -0,0 +1,183 @@
+=head1 NAME
+
+rrdtune - Modify some basic properties of a Round Robin Database
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<tune> I<filename>
+S<[B<--heartbeat>|B<-h> I<ds-name>:I<heartbeat>]>
+S<[B<--minimum>|B<-i> I<ds-name>:I<min>]>
+S<[B<--maximum>|B<-a> I<ds-name>:I<max>]>
+S<[B<--data-source-type>|B<-d> I<ds-name>:I<DST>]>
+S<[B<--data-source-rename>|B<-r> I<old-name>:I<new-name>]>
+S<[B<--deltapos> I<scale-value>]>
+S<[B<--deltaneg> I<scale-value>]>
+S<[B<--failure-threshold> I<failure-threshold>]>
+S<[B<--window-length> I<window-length>]>
+S<[B<--alpha> I<adaption-parameter>]>
+S<[B<--beta> I<adaption-parameter>]>
+S<[B<--gamma> I<adaption-parameter>]>
+S<[B<--gamma-deviation> I<adaption-parameter>]>
+S<[B<--smoothing-window> I<fraction-of-season>]>
+S<[B<--smoothing-window-deviation> I<fraction-of-season>]>
+S<[B<--aberrant-reset> I<ds-name>]>
+
+=head1 DESCRIPTION
+
+The tune option allows you to alter some of the basic configuration
+values stored in the header area of a Round Robin Database (B<RRD>).
+
+One application of the B<tune> function is to relax the
+validation rules on an B<RRD>. This allows to fill a new B<RRD> with
+data available in larger intervals than what you would normally want
+to permit. Be very careful with tune operations for COMPUTE data sources.
+Setting the I<min>, I<max>, and  I<heartbeat> for a COMPUTE data source
+without changing the data source type to a non-COMPUTE B<DST> WILL corrupt
+the data source header in the B<RRD>.
+
+A second application of the B<tune> function is to set or alter parameters
+used by the specialized function B<RRAs> for aberrant behavior detection.
+
+=over 8
+
+=item I<filename>
+
+The name of the B<RRD> you want to tune.
+
+=item S<B<--heartbeat>|B<-h> I<ds-name>:I<heartbeat>>
+
+modify the I<heartbeat> of a data source. By setting this to a high
+value the RRD will accept things like one value per day.
+
+=item S<B<--minimum>|B<-i> I<ds-name>:I<min>>
+
+alter the minimum value acceptable as input from the data source.
+Setting I<min> to 'U' will disable this limit.
+
+=item S<B<--maximum>|B<-a> I<ds-name>:I<max>>
+
+alter the maximum value acceptable as input from the data source.
+Setting I<max> to 'U' will disable this limit.
+
+=item S<B<--data-source-type>|B<-d> I<ds-name>:I<DST>>
+
+alter the type B<DST> of a data source.
+
+=item S<B<--data-source-rename>|B<-r> I<old-name>:I<new-name>>
+
+rename a data source.
+
+=item S<B<--deltapos> I<scale-value>>
+
+Alter the deviation scaling factor for the upper bound of the
+confidence band used internally to calculate violations for the
+FAILURES B<RRA>. The default value is 2. Note that this parameter is
+not related to graphing confidence bounds which must be specified as a
+CDEF argument to generate a graph with confidence bounds. The graph
+scale factor need not to agree with the value used internally by the
+FAILURES B<RRA>.
+
+=item S<B<--deltaneg> I<scale-value>>
+
+Alter the deviation scaling factor for the lower bound of the confidence band
+used internally to calculate violations for the FAILURES B<RRA>. The default
+value is 2. As with B<--deltapos>, this argument is unrelated to the scale
+factor chosen when graphing confidence bounds.
+
+=item S<B<--failure-threshold> I<failure-threshold>>
+
+Alter the number of confidence bound violations that constitute a failure for
+purposes of the FAILURES B<RRA>. This must be an integer less than or equal to
+the window length of the FAILURES B<RRA>. This restriction is not verified by
+the tune option, so one can reset failure-threshold and window-length
+simultaneously. Setting this option will reset the count of violations to 0.
+
+=item S<B<--window-length> I<window-length>>
+
+Alter the number of time points in the temporal window for determining
+failures. This must be an integer greater than or equal to the window
+length of the FAILURES B<RRA> and less than or equal to 28. Setting
+this option will reset the count of violations to 0.
+
+=item S<B<--alpha> I<adaption-parameter>>
+
+Alter the intercept adaptation parameter for the Holt-Winters
+forecasting algorithm. This parameter must be between 0 and 1.
+
+=item S<B<--beta> I<adaption-parameter>>
+
+Alter the slope adaptation parameter for the Holt-Winters forecasting
+algorithm. This parameter must be between 0 and 1.
+
+=item S<B<--gamma> I<adaption-parameter>>
+
+Alter the seasonal coefficient adaptation parameter for the SEASONAL
+B<RRA>. This parameter must be between 0 and 1.
+
+=item S<B<--gamma-deviation> I<adaption-parameter>>
+
+Alter the seasonal deviation adaptation parameter for the DEVSEASONAL
+B<RRA>. This parameter must be between 0 and 1.
+
+=item S<B<--smoothing-window> I<fraction-of-season>>
+
+Alter the size of the smoothing window for the SEASONAL B<RRA>. This must
+be between 0 and 1.
+
+=item S<B<--smoothing-window-deviation> I<fraction-of-season>>
+
+Alter the size of the smoothing window for the DEVSEASONAL B<RRA>. This must
+be between 0 and 1.
+
+=item S<B<--aberrant-reset> I<ds-name>>
+
+This option causes the aberrant behavior detection algorithm to reset
+for the specified data source; that is, forget all it is has learnt so far.
+Specifically, for the HWPREDICT or MHWPREDICT B<RRA>, it sets the intercept and
+slope coefficients to unknown. For the SEASONAL B<RRA>, it sets all seasonal
+coefficients to unknown. For the DEVSEASONAL B<RRA>, it sets all seasonal
+deviation coefficients to unknown. For the FAILURES B<RRA>, it erases the
+violation history. Note that reset does not erase past predictions
+(the values of the HWPREDICT or MHWPREDICT B<RRA>), predicted deviations (the
+values of the DEVPREDICT B<RRA>), or failure history (the values of the 
+FAILURES B<RRA>).  This option will function even if not all the listed 
+B<RRAs> are present.
+
+Due to the implementation of this option, there is an indirect impact on
+other data sources in the RRD. A smoothing algorithm is applied to
+SEASONAL and DEVSEASONAL values on a periodic basis. During bootstrap
+initialization this smoothing is deferred. For efficiency, the implementation
+of smoothing is not data source specific. This means that utilizing
+reset for one data source will delay running the smoothing algorithm
+for all data sources in the file. This is unlikely to have serious
+consequences, unless the data being collected for the non-reset data sources
+is unusually volatile during the reinitialization period of the reset
+data source.
+
+Use of this tuning option is advised when the behavior of the data source
+time series changes in a drastic and permanent manner.
+
+=back
+
+=head1 EXAMPLE 1
+
+C<rrdtool tune data.rrd -h in:100000 -h out:100000 -h through:100000>
+
+Set the minimum required heartbeat for data sources 'in', 'out'
+and 'through' to 10'000 seconds which is a little over one day in data.rrd.
+This would allow to feed old data from MRTG-2.0 right into
+RRDtool without generating *UNKNOWN* entries.
+
+=head1 EXAMPLE 2
+
+C<rrdtool tune monitor.rrd --window-length 5 --failure-threshold 3>
+
+If the FAILURES B<RRA> is implicitly created, the default
+window-length is 9 and the default failure-threshold is 7. This
+command now defines a failure as 3 or more violations in a temporal
+window of 5 time points.
+
+=head1 AUTHOR
+
+Tobias Oetiker <tobi@oetiker.ch>
+
diff --git a/program/doc/rrdtutorial.es.pod b/program/doc/rrdtutorial.es.pod
new file mode 100644 (file)
index 0000000..8200e41
--- /dev/null
@@ -0,0 +1,1183 @@
+=head1 NAME
+
+rrdtutorial - Tutorial sobre RRDtool por Alex van den Bogaerdt
+(Traducido al castellano por Jesús Couto Fandiño)
+
+=for html <div align="right"><a href="rrdtutorial.html">English</a></div>
+
+=head1 DESCRIPTION / DESCRIPCIÓN
+
+RRDtool es un programa escrito por Tobias Oetiker con la
+colaboración de muchas personas en diversas partes del mundo. Alex van
+den Bogaerdt escribió este documento para ayudarte a entender que es
+RRDtool y que es lo que puede hacer por ti.
+
+La documentación que viene con RRDtool puede ser demasiado técnica
+para algunos. Este tutorial existe para ayudarte a entender las
+funciones básicas de RRdtool. Debe servirte de preparación para leer la
+documentación, y además explica algunas ideas generales sobre
+estadística, con un enfoque particular hacia las redes.
+
+=head1 TUTORIAL
+
+=head2 Importante
+
+¡Por favor, no te adelantes en la lectura de este documento! Esta
+primera parte explica los fundamentos básicos. Puede ser aburrida,
+pero si te saltas los fundamentos, los ejemplos no te van a tener
+mucho sentido.
+
+=head2 Â¿Qué es RRDtool?
+
+RRDtool significa "herramienta de bases de datos en round robin".
+"Round robin" es una técnica que implica un número fijo de datos, y un
+apuntador al elemento más reciente. Piensa en un circulo con unos
+cuantos puntos dibujados alrededor del borde; estos puntos son los
+lugares donde se pueden guardar los datos. Dibuja ahora una flecha
+desde el centro del círculo a uno de los puntos; este es el apuntador.
+Cuando se lee o escribe el dato actualmente apuntado, la flecha se
+mueve al próximo elemento. Como estamos en un círculo, no hay ni
+principio ni fin; siempre puedes seguir, eternamente. Al cabo de un
+tiempo ya se habrán usado todas las posiciones disponibles y el
+proceso empieza a reutilizar las antiguas. De esta forma, la base de datos
+no crece en tamaño y, por lo tanto, no requiere ningún mantenimiento.
+RRDtool trabaja con estas bases de datos en "round-robin", guardando y
+recuperando datos de ellas.
+
+=head2 Â¿Qué datos pueden guardarse en una RRD?
+
+Lo que se te ocurra. Debes poder medir algún valor dado en distintos
+momentos en el tiempo y proveer a RRDtool de estos valores. Si puedes
+hacer esto, RRDtool puede guardar los datos. Los valores tienen que
+ser numéricos, pero no necesariamente enteros, como en MRTG.
+
+Muchos ejemplos mencionan SNMP, que es el acrónimo de
+"Simple Network Management Protocol" (Protocolo Simple de
+Administración de Redes). Lo de "simple" se refiere al protocolo - no
+se supone que sea fácil administrar o monitorizar una red. Cuando
+hayas terminado con este documento, deberás saber lo suficiente para
+entender cuando oigas a otros hablar sobre
+SNMP. Por ahora, simplemente considera a
+SNMP como una forma de preguntarle a los dispositivos
+por los valores de ciertos contadores que mantienen. Son estos valores
+de estos contadores los que vamos a almacenar en la RRD.
+
+=head2 Â¿Qué puedo hacer con esta herramienta?
+
+RRDtool se deriva de MRTG (Multi Router
+Traffic Grapher, Graficador De Tráfico de Múltiples Enrutadores).
+MRTG empezó como un pequeño script para poder
+graficar el uso de una conexión a la Internet. Luego evolucionó,
+permitiendo graficar otras fuentes de datos, como temperatura,
+velocidad, voltajes, cantidad de páginas impresas, etc... Lo más
+probable es que empieces a usar RRDtool para guardar y procesar datos
+conseguidos a través de SNMP, y que los datos
+sean el número de bytes (o bits) transferidos desde y hacia una red u
+ordenador. RRDtool te permite crear una base de datos, guardar los
+datos en ellas, recuperarlos y crear gráficos en formato SVG o PNG,
+para mostrarlos en un navegador web. Esas imágenes dependen de los
+datos que hayas guardado y pueden, por ejemplo, ser un sumario del
+promedio de uso de la red, o los picos de tráfico que ocurrieron.
+También lo puedes usar para mostrar el nivel de las mareas, la
+radiación solar, el consumo de electricidad, el número de visitantes
+en una exposición en un momento dado, los niveles de ruido cerca del
+aeropuerto, la temperatura en tu lugar de vacaciones favorito, o en
+la nevera, o cualquier otra cosa que te puedas imaginar, mientras
+tengas algún sensor con el cual medir los datos y seas capaz de
+pasarle los números a RRDtool.
+
+=head2 Â¿Y si aún tengo problemas después de leer este documento?
+
+Lo primero, Â¡léelo otra vez!. Puede que te hayas perdido de algo.
+Si no puedes compilar el código fuente y usas un sistema operativo
+bastante común, casi seguro que no es la culpa de RRDtool.
+Probablemente consigas versiones pre-compiladas por la Internet. Si
+provienen de una fuente confiable, Ãºsalas. Si, por otro lado, el
+programa funciona, pero no te da los resultados que tu esperabas,
+puede ser un problema con la configuración; revísala y
+compárala con los ejemplos.
+
+Hay una lista de correo electrónico y una archivo de la misma. Lee
+la lista durante unas cuantas semanas, y busca en el archivo. Es
+descortés hacer una pregunta sin haber revisado el archivo; Â¡puede que
+tu problema ya haya sido resuelto antes! Normalmente ocurre así en todas
+las listas de correo, no sólo esta. Examina la documentación que vino
+con RRDtool para ver donde está el archivo y como usarlo.
+
+Te sugiero que te tomes un momento y te subscribas a la lista ahora
+mismo, enviando un mensaje a rrd-users-request@lists.oetiker.ch
+con título C<subscribe>. Si eventualmente deseas salirte de la lista,
+envía otro correo a la misma dirección, con título C<unsubscribe>.
+
+=head2 Â¿Cómo me vas a ayudar?
+
+Dándote descripciones y ejemplos detallados. Asumimos que el seguir
+las instrucciones en el orden en que se presentan aquí te dará
+suficiente conocimiento  de RRDtool como para que experimentes por tu
+cuenta. Si no funciona a la primera, puede que te hallas saltado algo;
+siguiendo los ejemplos obtendrás algo de experiencia práctica y, lo
+que es más importante, un poco de información sobre como funciona el
+programa.
+
+Necesitarás saber algo sobre números hexadecimales. Si no, empieza
+por leer "bin_dec_hex" antes de continuar.
+
+=head2 Tu primera base de datos en round-robin
+
+En mi opinión, la mejor forma de aprender algo es haciéndolo. Â¿Por
+qué no empezamos ya? Vamos a crear una base de datos, poner unos cuantos
+valores en ella y extraerlos después. La salida que obtengas debe ser
+igual a la que aparece en este documento.
+
+Empezaremos con algo fácil, comparando un coche con un enrutador, o
+por decirlo de otra forma, comparando kilómetros con bits y bytes. A
+nosotros nos da lo mismo; son unos números obtenidos en un espacio de tiempo.
+
+Asumamos que tenemos un dispositivo que transfiere bytes desde y
+hacia la Internet. Este dispositivo tiene un contador que empieza en 0
+al encenderse y se incrementa con cada byte transferido. Este contador
+tiene un valor máximo; si ese valor se alcanza y se cuenta un byte
+más, el contador vuelve a empezar desde cero. Esto es exactamente lo
+mismo que pasa con muchos contadores, como el cuentakilómetros del
+coche. En muchas de las disertaciones sobre redes se habla de bits por
+segundo, así que empezaremos por acostumbrarnos a esto. Asumamos que un
+byte son 8 bits y empecemos a pensar en bits y no en bytes. Â¡El
+contador, sin embargo, sigue contando en bytes! En el mundo
+SNMP, la mayoría de los contadores tienen una
+longitud de 32 bits. Esto significa que pueden contar desde 0 hasta
+4294967295. Usaremos estos valores en los ejemplos. El dispositivo, cuando
+le preguntamos, retorna el valor actual del contador. Como sabemos el
+tiempo transcurrido desde la Ãºltima vez que le preguntamos, sabemos
+cuantos bytes se han transferido C<***en promedio***> por
+segundo. Esto no es muy difícil de calcular; primero en palabras,
+luego en operaciones:
+
+=over 4
+
+=item 1.
+
+Toma el valor actual del contador y réstale el valor anterior
+
+=item 2.
+
+Haz lo mismo con la fecha
+
+=item 3.
+
+Divide el resultado del paso (1) por el resultado del paso (2).
+El resultado es la cantidad de bytes por segundo. Si lo
+multiplicas por ocho obtienes la cantidad de bits por segundo
+
+=back
+
+  bps = (contador_actual - contador_anterior) / (fecha_actual - fecha_anterior) * 8
+
+Para algunos será de ayuda traducir esto a un ejemplo automotor.
+No prueben estas velocidades en la práctica, y si lo hacen, no me
+echen la culpa por los resultados.
+
+Usaremos las siguientes abreviaturas:
+
+ M:    metros
+ KM:   kilómetros (= 1000 metros).
+ H:    horas
+ S:    segundos
+ KM/H: kilómetros por hora
+ M/S:  metros por segundo
+
+
+Vas conduciendo un coche. A las 12:05, miras el contador en el
+salpicadero y ves que el coche ha recorrido 12345
+KM. A las 12:10 vuelves a mirar otra vez, y dice
+12357 KM. Quiere decir, que has recorrido 12
+KM en cinco minutos. Un científico convertiría
+esto en metros por segundos; esto es bastante parecido al problema de
+pasar de bytes transferidos en 5 minutos a bits por segundo.
+
+Viajamos 12 kilómetros, que son 12000 metros. Tardamos 5 minutos, o
+sea 300 segundos. Nuestra velocidad es 12000M / 300S igual a 40 M/S.
+
+También podemos calcular la velocidad en KM/H: 12 veces 5 minutos
+es una hora, así que multiplicando los 12 KM por 12 obtenemos 144
+KM/H. No intentes esto en casa, o por donde vivo :-)
+
+Recuerda que estos números son tan sólo promedios. No hay forma de
+deducir, viendo sólo los números, si fuiste a una velocidad constante.
+Hay un ejemplo más adelante en el tutorial que explica esto.
+
+Espero que entiendas que no hay diferencia entre calcular la
+velocidad en M/S o bps; sólo la forma en que
+recogemos los datos es distinta. Inclusive, la K de kilo en este
+caso es exactamente la misma, ya que en redes k es 1000
+
+Ahora vamos a crear una base de datos en la que guardar todos estos
+interesantes valores. El método a usar para arrancar el programa puede
+variar de un sistema de operación a otro, pero asumamos que lo puedes
+resolver tu mismo en caso que se diferente en el sistema que usas.
+Asegúrate de no sobreescribir ningún archivo en tu sistema al
+ejecutarlo y escribe todo como una sola línea (tuve que partirlo para
+que fuera legible), saltándote todos los caracteres '\'
+
+   rrdtool create test.rrd             \
+            --start 920804400          \
+            DS:speed:COUNTER:600:U:U   \
+            RRA:AVERAGE:0.5:1:24       \
+            RRA:AVERAGE:0.5:6:10
+
+(o sea, escribe: C<rrdtool create test.rrd --start 920804400 DS ...>)
+
+=head2 Â¿Qué hemos creado?
+
+Hemos creado una base de datos en round robin llamada test
+(test.rrd), que empieza desde el mediodía del día en que empecé a
+escribir este documento (7 de marzo de 1999). En ella se guarda una
+fuente de datos (DS), llamada "speed", que se
+lee de un contador. En la misma base de datos se guardan dos archivos
+en round robin (RRAs), uno promedia los datos cada vez que se leen (o
+sea, no hay nada que promediar), y mantiene 24 muestras (24 por 5
+minutos = 2 horas de muestras). El otro promedia 6 muestras (media
+hora), y guarda 10 de estos promedios (o sea, 5 horas). Las opciones
+restantes las veremos más adelante.
+
+RRDtool usa un formato de "fecha" especial que viene del mundo de
+UNIX. Estas "fechas" son el número de segundos
+que han pasado desde el primero de enero de 1970, zona UTC. Este
+número de segundos se convierte luego en la fecha local, por lo que
+varia según la franja horaria.
+
+Lo más probable es que tu no vivas en la misma parte del mundo que
+yo, por lo que tu franja horaria será diferente. En los ejemplos,
+cuando mencione horas, puede que no sean las mismas para ti; esto no
+afecta mucho los resultados, sólo tienes que corregir las horas
+mientras lees. Por ejemplo, las 12:05 para mí son las 11:05 para los
+amigos en la Gran Bretaña.
+
+Ahora tenemos que llenar nuestra base de datos con valores. Vamos a
+suponer que leímos estos datos:
+
+ 12:05  12345 KM
+ 12:10  12357 KM
+ 12:15  12363 KM
+ 12:20  12363 KM
+ 12:25  12363 KM
+ 12:30  12373 KM
+ 12:35  12383 KM
+ 12:40  12393 KM
+ 12:45  12399 KM
+ 12:50  12405 KM
+ 12:55  12411 KM
+ 13:00  12415 KM
+ 13:05  12420 KM
+ 13:10  12422 KM
+ 13:15  12423 KM
+
+Llenaremos la base de datos así:
+
+ rrdtool update test.rrd 920804700:12345 920805000:12357 920805300:12363
+ rrdtool update test.rrd 920805600:12363 920805900:12363 920806200:12373
+ rrdtool update test.rrd 920806500:12383 920806800:12393 920807100:12399
+ rrdtool update test.rrd 920807400:12405 920807700:12411 920808000:12415
+ rrdtool update test.rrd 920808300:12420 920808600:12422 920808900:12423
+
+Lo que significa: actualiza nuestra base de datos test con los
+siguientes valores:
+
+ fecha 920804700, valor 12345
+ fecha 920805000, valor 12357
+
+ etcétera.
+
+Como ves, pueden introducirse más de un valor en la base de datos
+por ejecución del comando. Yo los agrupo de tres en tres para hacerlo
+legible, pero en realidad el máximo depende del sistema de operación.
+
+Ahora podemos recuperar los datos usando ``rrdtool fetch'':
+
+ rrdtool fetch test.rrd AVERAGE --start 920804400 --end 920809200
+
+Debes obtener esto como salida:
+
+                    speed
+
+ 920804400:        NaN
+ 920804700:        NaN
+ 920805000: 4.0000000000e-02
+ 920805300: 2.0000000000e-02
+ 920805600: 0.0000000000e+00
+ 920805900: 0.0000000000e+00
+ 920806200: 3.3333333333e-02
+ 920806500: 3.3333333333e-02
+ 920806800: 3.3333333333e-02
+ 920807100: 2.0000000000e-02
+ 920807400: 2.0000000000e-02
+ 920807700: 2.0000000000e-02
+ 920808000: 1.3333333333e-02
+ 920808300: 1.6666666667e-02
+ 920808600: 6.6666666667e-03
+ 920808900: 3.3333333333e-03
+ 920809200:        NaN
+
+Si no, hay algo mal. Probablemente tu sistema de operación muestre ``NaN''
+de otra forma; representa "Not a Number", o sea "No es un número". Si
+aparece ``U'' o ``UNKN'' o algo parecido, es lo mismo. Si hay alguna otra
+diferencia, probablemente te equivocaste al introducir algún P valor
+(asumiendo que mi tutorial está bien, por supuesto :-). En ese caso, borra
+la base de datos y prueba de nuevo.
+
+Lo que representa exactamente esta salida lo vamos más adelante en el tutorial.
+
+=head2 Hora de hacer algunos gráficos
+
+Prueba este comando:
+
+ rrdtool graph speed.png                                 \
+         --start 920804400 --end 920808000               \
+         DEF:myspeed=test.rrd:speed:AVERAGE              \
+         LINE2:myspeed#FF0000
+
+Este comando crea speed.png, un gráfico de los datos desde las
+12:00 hasta las 13:00. Contiene una definición de la variable myspeed
+y define el color como rojo. Notarás que el gráfico no comienza
+exactamente a las 12:00 sino a las 12:05, y es porque no tenemos datos
+suficientes como para calcular el promedio de velocidad antes de ese
+momento. Esto sólo ocurre en caso de que se pierdan algún muestreo, lo
+que esperamos que no debe ocurrir muy a menudo.
+
+Si ha funcionado, Â¡felicitaciones!. Si no, revisa qué puede estar mal.
+
+La definición de colores se construye a partir del rojo, verde y
+azul. Especificas cuanto de cada uno de estos componentes vas a usar
+en hexadecimal: 00 significa "nada de este color" y FF significa
+"este color a máxima intensidad". El "color" blanco es la mezcla
+del rojo, verde y azul a toda intensidad:
+FFFFFF; el negro es la ausencia de todos los colores: 000000.
+
+   rojo    #FF0000
+   verde   #00FF00
+   azul    #0000FF
+   violeta #FF00FF     (mezcla de rojo y azul)
+   gris    #555555     (un tercio de cada uno de los colores)
+
+El archivo PNG que acabas de crear puede
+verse con tu visor de archivos de imagen favorito. Los navegadores lo
+mostrarán usando la URL
+``file://el/camino/de/directorios/hasta/speed.png''
+
+=head2 Gráficos con un poco de matemática
+
+Cuando veas la imagen, notarás que el eje horizontal tiene unas
+etiquetas marcando las 12:10, 12:20, 12:30, 12:40 y 12:50. Los otros
+dos momentos (12:00 y 13:00) no se pueden mostrar bien por falta de datos, así que
+el programa se los salta. El eje vertical muestra el rango de los valores que
+entramos. Introdujimos los kilómetros y luego dividimos entre 300
+segundos, por lo que obtuvimos valores bastante bajos. Para ser
+exactos, el primer valor, 12 (12357-12345), dividido entre 300 da
+0.04, lo que RRDtool muestra como ``40m'', o sea ``40/1000''. Â¡La
+``m''' no tiene nada que ver con metros, kilómetros o milímetros!.
+RRDtool no sabe nada de unidades, el sólo trabaja con números, no con
+metros.
+
+Donde nos equivocamos fue en que debimos medir en metros. Así,
+(12357000-12345000)/300 = 12000/300 = 40.
+
+Vamos a corregirlo. Podríamos recrear la base de datos con los
+valores correctos, pero hay una forma mejor: Â¡haciendo los cálculos
+mientras creamos el archivo png!
+
+   rrdtool graph speed2.png                           \
+      --start 920804400 --end 920808000               \
+      --vertical-label m/s                            \
+      DEF:myspeed=test.rrd:speed:AVERAGE              \
+      CDEF:realspeed=myspeed,1000,*                   \
+      LINE2:realspeed#FF0000
+
+Cuando veas esta imagen, notarás que la ``m'' ha desaparecido, y
+ahora tienes los resultados correctos. Además hemos añadido una
+etiqueta a la imagen. Apartando esto, el archivo PNG es el mismo.
+
+Las operaciones están en la sección del CDEF
+y están escritas en Notación Polaca Inversa (Reverse Polish Notation o
+``RPN''). En palabras, dice: "toma la fuente de
+datos myspeed y el numero 1000, y multiplícalos". No te molestes en
+meterte con RPN todavía, la veremos con más
+detalle más adelante. Además, puede que quieras leer mi tutorial sobre
+los CDEF y el tutorial de Steve Rader sobre RPN, pero primero terminemos con este.
+
+¡Un momento! Si podemos multiplicar los valores por mil, entonces,
+¡también debería ser posible el mostrar la velocidad en kilómetros por
+hora usando los mismos datos!
+
+Para cambiar el valor que medimos en metros por segundo, calculamos
+los metros por hora (valor * 3600) y dividimos entre 1000 para sacar
+los kilómetros por hora. Todo junto hace valor * (3600/1000) == valor
+* 3.6.
+
+Como en nuestra base de datos cometimos un error guardando los
+valores en kilómetros, debemos compensar por ello, multiplicando por
+100, por lo que al aplicar esta corrección nos queda valor * 3600.
+
+Ahora vamos a crear este png, agreándole un poco más de magia...
+
+   rrdtool graph speed3.png                           \
+      --start 920804400 --end 920808000               \
+      --vertical-label km/h                           \
+      DEF:myspeed=test.rrd:speed:AVERAGE              \
+      "CDEF:kmh=myspeed,3600,*"                       \
+      CDEF:fast=kmh,100,GT,kmh,0,IF                   \
+      CDEF:good=kmh,100,GT,0,kmh,IF                   \
+      HRULE:100#0000FF:"Maximum allowed"              \
+      AREA:good#00FF00:"Good speed"                   \
+      AREA:fast#FF0000:"Too fast"
+
+Esto luce mucho mejor. La velocidad en KM/H,
+y además tenemos una línea extra mostrando la velocidad máxima
+permitida (en el camino por donde conduzco). También le cambie los
+colores de la velocidad, y ahora paso de ser una línea a un Ã¡rea.
+
+Los cálculos son más complejos ahora. Para calcular la velocidad "aceptable":
+
+   Verifica si la velocidad en kmh es mayor que 100     ( kmh,100 ) GT
+   Si es así, retorna 0, si no, retorna la velocidad    ((( kmh,100 ) GT ), 0, kmh) IF
+
+Para calcular la parte de velocidad "excesiva":
+
+   Verifica si la velocidad en kmh es mayor que 100     ( kmh,100 ) GT
+   Si es así, retorna la velocidad, si no, retorna 0    ((( kmh,100) GT ), kmh, 0) IF
+
+=head2 Magia gráfica
+
+Me gusta creer que virtualmente no hay limites para lo que RRDtool puede
+hacer con los datos. No voy a explicarlo en detalle, pero mira este PNG:
+
+   rrdtool graph speed4.png                           \
+      --start 920804400 --end 920808000               \
+      --vertical-label km/h                           \
+      DEF:myspeed=test.rrd:speed:AVERAGE              \
+      "CDEF:kmh=myspeed,3600,*"                       \
+      CDEF:fast=kmh,100,GT,100,0,IF                   \
+      CDEF:over=kmh,100,GT,kmh,100,-,0,IF             \
+      CDEF:good=kmh,100,GT,0,kmh,IF                   \
+      HRULE:100#0000FF:"Maximum allowed"              \
+      AREA:good#00FF00:"Good speed"                   \
+      AREA:fast#550000:"Too fast"                     \
+      STACK:over#FF0000:"Over speed"
+
+Vamos a crear una página HTML simple para ver los tres archivos PNG:
+
+   <HTML><HEAD><TITLE>Velocidad</TITLE></HEAD><BODY>
+   <IMG src="speed2.png" alt="Speed in meters per second">
+   <BR>
+   <IMG src="speed3.png" alt="Speed in kilometers per hour">
+   <BR>
+   <IMG src="speed4.png" alt="Traveled too fast?">
+   </BODY></HTML>
+
+Guárdalo como ``speed.html'' o algo parecido, y examínalo con un navegador.
+
+Ahora, todo lo que tienes que hacer es medir los datos regularmente
+y actualizar la base de datos. Cuando quieras verlos, vuelve a crear
+los archivos PNG y asegúrate que se carguen de nuevo en tu navegador
+(Nota: presionar el botón de "refrescar" puede no ser suficiente; en
+particular, Netscape tiene un problema al respecto, por lo que
+necesitaras darle al botón mientras presionas la tecla de mayúsculas.
+
+=head2 Actualizaciones de verdad
+
+Ya hemos usado el comando ``update''; vimos que recibia uno o más
+parámetros en el formato: ``E<lt>fechaE<gt>:E<lt>valorE<gt>''. Para
+facilitarte las cosas, puedes obtener la fecha actual colocando
+``N'' en la fecha. También podrías usar la función
+``time'' de Perl para obtenerla. El ejemplo más corto de todo el
+tutorial :)
+
+   perl -e 'print time, "\n" '
+
+Ahora, la forma de poner a correr un programa a intervalos
+regulares de tiempo depende del sistema de operación. La
+actualización, en pseudo-código, sería:
+
+   Toma el valor, colócalo en la variable "$speed"
+   rrdtool update speed.rrd N:$speed
+
+(Pero no lo hagas sobre nuestra base de datos de pruebas, que aún
+la vamos a usar en otros ejemplos.
+
+Eso es todo. Ejecutando este script cada 5 minutos, lo Ãºnico que
+tienes que hacer para ver los gráficos actuales es correr los ejemplos
+anteriores, que también puedes poner en un script. Luego de correrlo,
+basta con cargar index.html
+
+=head2 Unas palabras sobre SNMP
+
+Me imagino que muy pocas personas serán capaces de obtener en su
+ordenador datos reales de su coche cada 5 minutos; los demás nos
+tendremos que conformar con algún otro contador. Puedes, por ejemplo,
+medir la cantidad de páginas que ha hecho una impresora, cuanto café
+has hecho con la cafetera, el medidor del consumo de electricidad, o
+cualquier otra cosa. Cualquier contador incremental puede
+monitorizarse y graficarse con lo que has aprendido hasta ahora. Más
+adelante, veremos también como monitorizar otro tipo de valores, como
+la temperatura. La mayoría usaremos alguna vez un contador que lleve
+la cuenta de cuantos octetos (bytes) a transferido un dispositivo de
+red, así que vamos a ver como hacer esto. Empezaremos describiendo
+como recoger los datos. Hay quien dirá que hay herramientas que pueden
+recoger estos datos por ti. Â¡Es cierto! Pero, creo que es importante
+darse cuenta de que no son necesarias. Cuando tienes que determinar
+porqué algo no funciona, necesitas saber cómo funciona en primer lugar.
+
+Una herramienta que mencionamos brevemente al principio del
+documento es SNMP. SNMP es una forma de comunicarse con tus equipos.
+La herramienta particular que voy a usar más adelante se llama
+``snmpget'', y funciona así:
+
+   snmpget dispositivo clave OID
+
+En "dispositivo" colocas el nombre o dirección IP del equipo a
+monitorizar. En clave, colocas la "cadena de caracteres de la
+comunidad de lectura", como se le denomina en el mundillo SNMP.
+Muchos dispositivos aceptarán "public" como
+cadena por defecto, pero por razones de privacidad y seguridad esta
+clave puede estar deshabilitada. Consulta la documentación
+correspondiente al dispositivo o programa.
+
+Luego esta el tercer parámetro, llamado OID
+(Object IDentifier, identificador de objeto).
+
+Al principio, cuando empiezas a aprender sobre SNMP, parece muy
+confuso. No lo es tanto cuando le hechas una ojeada a los
+``MIB'' (Manager Information Base, o Base de
+Información Administrativa). Es un Ã¡rbol invertido que describe los
+datos, empezando en un nodo raíz desde el que parten varias ramas.
+Cada rama termina en otro nodo y puede abrir nuevas sub-ramas. Cada
+rama tiene un nombre, y forman un camino que nos lleva hasta el fondo
+del Ã¡rbol. En este ejemplo, las ramas que vamos a tomar se llaman iso,
+org, dod, internet, mgmt y mib-2. También pueden accederse por su
+número relativo; en este caso, estos números son 1, 3, 6, 1, 2 y 1:
+
+   iso.org.dod.internet.mgmt.mib-2 (1.3.6.1.2.1)
+
+En algunos programas se usa un punto al iniciar el OID. Esto puede
+ser confuso; no hay ningún punto inicial en la especificación de los
+OID... sin embargo, algunos programas usan por defecto un prefijo
+inicial. Para indicar la diferencia entre los OID abreviados (o sea, a
+los que se le pondrá el prefijo inicial) y los completos, estos
+programas necesitan que los OID completos empiecen por un punto. Para
+empeorar las cosas, se usan varios prefijos distintos...
+
+De acuerdo, sigamos con el inicio de nuestro OID: teníamos
+1.3.6.1.2.1 . Ahora, nos interesa la rama ``interfaces'', que tiene el
+número dos (o sea, 1.3.6.1.2.1.2, o 1.3.6.1.2.1.interfaces).
+
+Lo primero es hacernos con un programa SNMP. Busca algún
+paquete pre-compilado para tu plataforma, si no, puedes
+buscar el código fuente y compilarlo tu mismo. En Internet encontrarás
+muchos programas, búscalos con un motor de búsqueda o como prefieras.
+Mi sugerencia es que busques el paquete CMU-SNMP, que esta bastante difundido.
+
+Asumamos que ya tienes el programa. Empecemos por tomar ciertos
+datos que están disponibles en la mayoría de los sistemas. Recuerda:
+hay un nombre abreviado para la parte del Ã¡rbol que más nos interesa.
+
+Voy a usar la versión corta, ya que creo que este documento ya es
+lo bastante largo. Si no te funciona, añádele el prefijo .1.3.6.1.2.1
+y prueba de nuevo. O prueba leyendo el manual; sáltate las partes que
+no entiendas aún, y busca las secciones que hablan de como arrancar y
+usar el programa.
+
+   snmpget myrouter public system.sysdescr.0
+
+El dispositivo deberá contestarte con una descripción, probablemente
+vacía, de sí mismo. Si no consigues una respuesta válida, prueba con
+otra "clave" u otro dispositivo; no podemos seguir hasta tener un
+resultado.
+
+   snmpget myrouter public interfaces.ifnumber.0
+
+Con suerte, usando este comando obtendrás un número como resultado:
+el número de interfaces del dispositivo. Si es así, seguiremos
+adelante con otro programa, llamado "snmpwalk"
+
+   snmpwalk myrouter public interfaces.iftable.ifentry.ifdescr
+
+Si obtienes una lista de interfaces, ya casi hemos llegado. Aquí
+tienes un ejemplo del resultado:
+
+   [user@host /home/alex]$ snmpwalk cisco public 2.2.1.2
+   interfaces.ifTable.ifEntry.ifDescr.1 = "BRI0: B-Channel 1"
+   interfaces.ifTable.ifEntry.ifDescr.2 = "BRI0: B-Channel 2"
+   interfaces.ifTable.ifEntry.ifDescr.3 = "BRI0" Hex: 42 52 49 30
+   interfaces.ifTable.ifEntry.ifDescr.4 = "Ethernet0"
+   interfaces.ifTable.ifEntry.ifDescr.5 = "Loopback0"
+
+En este equipo CISCO, quiero monitorizar la interfaz "Ethernet0".
+Viendo que es la cuarta, pruebo con:
+
+   [user@host /home/alex]$ snmpget cisco public 2.2.1.10.4 2.2.1.16.4
+
+   interfaces.ifTable.ifEntry.ifInOctets.4 = 2290729126
+   interfaces.ifTable.ifEntry.ifOutOctets.4 = 1256486519
+
+Entonces, tengo 2 OIDs que monitorizar, y son (en el formato largo, ahora):
+
+   1.3.6.1.2.1.2.2.1.10
+
+        y
+
+   1.3.6.1.2.1.2.2.1.16
+
+, ambas con el número de interfaz de 4
+
+No te engañes, esto no lo logre yo al primer intento. Me tomó un
+tiempo entender lo que significaban todos estos números; ayuda cuando
+se traducen en un texto descriptivo... por lo menos, cuando oigas
+hablar de MIBs y OIDs, ahora sabrás de qué se trata. No te olvides
+del número de interfaz (0 si el valor no depende de una interfaz), y
+prueba con snmpwalk si no obtienes una respuesta clara con snmpget.
+
+Si entendiste todo esto, y obtienes resultados del dispositivo con
+el que estás probando, sigue adelante con el tutorial. Si no, vuelve a
+leer esta sección; es importante
+
+=head2 Un ejemplo real
+
+Ok, empecemos con la diversión. Primero, crea una base de datos
+nueva. Vamos a guardar en ella 2 contadores, "input" y "ouput". Los
+datos los vamos a guardar en archivos que los promediarán, tomando
+grupos de 1, 6, 24 o 288 muestras. También archivaremos los valores
+máximos. Lo explicaremos con más detalle después. El intervalo de
+tiempo entre las muestras será de 300 segundos (5 minutos).
+
+ 1 muestra "promediada" sigue siendo 1 muestra cada 5 minutos
+ 6 muestras promediadas son un promedio de cada 30 minutos
+ 24 muestras promediadas son un promedio de cada 2 horas
+ 288 muestras promediadas son un promedio de cada día
+
+Vamos a tratar de ser compatibles con MRTG, que guarda más o menos
+esta cantidad de datos:
+
+ 600 muestras de 5 minutos:          2 días y 2 horas
+ 600 promedios de 30 minutos:        12.5 días
+ 600 promedios de 2 horas:           50 días
+ 600 promedios de 1 día:             732 días
+
+Uniendo todos estos rangos tenemos que en total guardamos datos de
+unos 797 días. RRDtool guarda los datos de una forma distinta a MRTG;
+no empieza el archivo "semanal" donde acaba el "diario", sino que
+ambos archivos contienen la información más reciente, Â¡por lo que con
+RRDtool archivamos más datos que con MRTG!
+
+Necesitaremos:
+
+ 600 muestras de 5 minutos    (2 días y 2 horas)
+ 700 entradas de 30 minutos   (2 días y 2 horas, más 12.5 días)
+ 775 entradas de 2 horas      (lo anterior + 50 días)
+ 797 entradas de 1 día        (lo anterior + 732 días, redondeando)
+
+   rrdtool create myrouter.rrd         \
+            DS:input:COUNTER:600:U:U   \
+            DS:output:COUNTER:600:U:U  \
+            RRA:AVERAGE:0.5:1:600      \
+            RRA:AVERAGE:0.5:6:700      \
+            RRA:AVERAGE:0.5:24:775     \
+            RRA:AVERAGE:0.5:288:797    \
+            RRA:MAX:0.5:1:600          \
+            RRA:MAX:0.5:6:700          \
+            RRA:MAX:0.5:24:775         \
+            RRA:MAX:0.5:288:797
+
+Lo siguiente es recoger los datos y guardarlos, como en el ejemplo
+siguiente. Esta parcialmente en pseudo-código, por lo que tendrás que
+buscar exactamente como hacerlo funcionar en tu sistema operativo.
+
+   mientras no sea el fin del universo
+   hacer
+      tomar el resultado de
+          snmpget router community 2.2.1.10.4
+      en la variable $in
+      tomar el resultado de
+          snmpget router community 2.2.1.16.4
+      en la variable $out
+      rrdtool update myrouter.rrd N:$in:$out
+      esperar 5 minutos
+   hecho
+
+Luego, tras recoger datos por un día, crea una imagen, usando:
+
+   rrdtool graph myrouter-day.png --start -86400 \
+            DEF:inoctets=myrouter.rrd:input:AVERAGE \
+            DEF:outoctets=myrouter.rrd:output:AVERAGE \
+            AREA:inoctets#00FF00:"In traffic" \
+            LINE1:outoctets#0000FF:"Out traffic"
+
+Este comando debe producir un gráfico del tráfico del día. Un día
+son 24 horas, de 60 minutos, de 60 segundos: 24*60*60=86400, o sea que
+empezamos a "ahora" menos 86400 segundos. Definimos (con los DEFs)
+"inoctets" y "outoctets" como los valores promedio de la base da datos
+myrouter.rrd, dibujando un Ã¡rea para el tráfico de entrada y una línea
+para el tráfico de salida.
+
+Mira la imagen y sigue recogiendo datos por unos cuantos días. Si
+lo deseas, puedes probar con los ejemplos de la base de datos de
+pruebas y ver si puedes hacer trabajar las diversas opciones y
+operaciones.
+
+Sugerencia:
+
+Haz un gráfico que muestre el tráfico en bytes por segundo y en
+bits por segundo. Colorea el tráfico Ethernet rojo si sobrepasa los
+cuatro megabits por segundo.
+
+=head2 Funciones de consolidación
+
+Unos cuantos párrafos atrás hablábamos sobre la posibilidad de
+guardar el valor máximo en vez del promedio. Profundicemos un poco en
+este tema.
+
+Recordemos lo que hablábamos sobre la velocidad de un coche.
+Supongamos que manejamos a 144 KM/H durante 5
+minutos y luego nos detiene la policía durante unos 25 minutos. Al
+finalizar el regaño, tomamos nuestro portátil y creamos una imagen
+desde nuestra base de datos. Si visualizamos la segunda RRA que
+creamos, tendremos el promedio de 6 muestreos. Las velocidades
+registradas serian 144+0+0+0+0+0=144, lo que en promedio nos da una
+velocidad de 24 KM/H., con lo que nos igual nos
+pondrían una multa, sólo que no por exceso de velocidad.
+
+Obviamente, en este caso, no deberíamos tomar en cuenta los
+promedios. Estos son Ãºtiles en varios casos. Por ejemplo, si queremos
+ver cuantos KM hemos viajado, este sería el
+gráfico más indicado. Pero por otro lado, para ver la velocidad ha la
+que hemos viajado, los valores máximos son más adecuados.
+
+Es lo mismo con los datos que recogemos. Si quieres saber la
+cantidad total, mira los promedios. Si quieres ver la velocidad, mira
+los máximos. Con el tiempo, ambas cantidades se separan cada vez más.
+En la Ãºltima base de datos que creamos, había dos archivos que
+guardaban los datos de cada día. El archivo que guarda los promedios
+mostrará valores bajos, mientras que el de máximos mostrará valores más
+altos. Para mi coche, mostraría valores promedio de 96/24=4 KM/H
+(viajo unos 96 kilómetros por día), y máximos de 1220 KM/H (la
+velocidad máxima que alcanzo cada día)
+
+Como ves, una gran diferencia. No mires el segundo gráfico para
+estimar la distancia que recorro, ni al primero para estimar la
+velocidad a la que voy. Esto sólo funciona con muestras muy cercanas,
+pero no si sacas promedios.
+
+Algunas veces, hago un viaje largo. Si hago un recorrido por
+Europa, conduciendo por unas 12 horas, el primer gráfico subirá
+a unos 60 KM/H. El segundo mostrará unos 180 KM/H. Esto significa que
+recorrí unos 60 KM/H por 24 horas = 1440 KM. Muestra además que fui a
+una velocidad promedio mayor a la normal y a un máximo de 180 KM/H,
+¡no que fui 8 horas a una velocidad fija de 180 KM/H! Este es un
+ejemplo real: tengo que seguir la corriente en las autopistas de
+Alemania, detenerme por gasolina y café de vez en cuando, manejar más
+lentamente por Austria y Holanda, e ir con cuidado en las montañas y
+las villas. Si viéramos los gráficos de los promedios de cada 5
+minutos, la imagen sería completamente distinta; veríamos los mismos
+valores de promedio y de máxima. (suponiendo que las mediciones fueran
+cada 300 segundos). Se podría ver cuando paré, cuando iba en
+primera, cuando iba por las autopistas, etc. La granularidad de los
+datos es más alta, por lo que se tiene más información. Sin embargo,
+esto nos lleva unas 12 muestras por hora, o 288 al día, lo cual es
+mucho para guardar por un periodo de tiempo largo. Por lo tanto,
+sacamos el promedio, guardando eventualmente un solo valor por día.
+Con este Ãºnico valor, no podemos ver mucho.
+
+Es importante comprender lo que expuesto en estos Ãºltimos párrafos.
+Unos ejes y unas líneas no tienen ningún valor por si mismos; hay que
+saber que representan e interpretar correctamente los valores
+obtenidos. Sean cuales sean los datos, esto siempre será cierto.
+
+El mayor error que puedes cometer es usar los datos recogidos para
+algo para lo cual no sirven. En ese caso, seria hasta mejor no tener
+gráfico alguno.
+
+=head2 Repasemos lo que sabemos
+
+Ahora ya sabes como crear una base de datos. Puedes guardar valores
+en ella, extraerlos creando un gráfico, hacer operaciones matemáticas
+con ellos desde la base de datos y visualizar los resultados de estas
+en vez de los datos originales. Vimos la diferencia entre los
+promedios y los máximos y cuando debemos usar cada uno (o al menos una
+idea de ello)
+
+RRDtool puede hacer más de lo que hemos visto hasta ahora. Pero
+antes de continuar, te recomiendo que releas el texto desde el
+principio y pruebes a hacerle algunas modificaciones a los ejemplos.
+Asegúrate de entenderlo todo. El esfuerzo valdrá la pena, y te ayudará,
+no sólo con el resto del documento, sino en tu trabajo diario de
+monitorización, mucho después de terminar con esta introducción.
+
+=head2 Tipos de fuentes de datos
+
+De acuerdo, quieres continuar. Bienvenido de vuelta otra vez y
+prepárate; voy a ir más rápido con los ejemplos y explicaciones.
+
+Ya vimos que, para ver el cambio de un contador a lo largo del
+tiempo, tenemos que tomar dos números y dividir la diferencia entre el
+tiempo transcurrido entre las mediciones. Para los ejemplos que hemos
+visto es lo lógico, pero hay otras posibilidades. Por ejemplo, mi
+enrutador me puede dar la temperatura actual en tres puntos distintos,
+la entrada de aire, el llamado "punto caliente" y la salida de
+ventilación. Estos valores no son contadores; si tomo los valores de
+dos muestreos y lo divido entre 300 segundos, obtendré el cambio de
+temperatura por segundo. Â¡Esperemos que sea cero, o tendríamos un
+incendio en el cuarto de ordenadores! :)
+
+Entonces, Â¿que hacemos? Podemos decirle a RRDtool que guarde los
+valores tal como los medimos (esto no es exactamente así, pero se
+aproxima bastante a la verdad). Así, los gráficos se verán mucho
+mejor. Puedo ver cuando el enrutador está trabajando más (en serio,
+funciona; como usa más electricidad, genera más calor y sube la
+temperatura), puedo saber cuando me he dejado las puertas abiertas (el
+cuarto de ordenadores tiene aire acondicionado; con las puertas
+abiertas el aire caliente del resto del edificion entra y sube la
+temperatura en la entrada de aire del enrutador), etc. Antes usamos un
+tipo de datos de "contador", ahora usaremos un tipo de datos
+diferente, con un nombre diferente, GAUGE.
+Tenemos otros tipos:
+
+ - COUNTER este ya lo conocemos
+ - GAUGE   este acabamos de verlo
+ - DERIVE
+ - ABSOLUTE
+
+Los otros dos tipos son DERIVE y ABSOLUTE. ABSOLUTE puede usarse
+igual que COUNTER, con una diferencia; RRDtool asume que el contador
+se reinicia cada vez que se lee. O en otras palabras; el delta entre
+los valores no hay que calcularlo, mientras que con COUNTER RRDtool
+tiene que sacar Ã©l la cuenta. Por ejemplo, nuestro primer ejemplo,
+(12345, 12357, 12363, 12363), sería (unknown, 12, 6, 0) en ABSOLUTE.
+El otro tipo, DERIVE, es como COUNTER, pero al contrario de COUNTER,
+este valor también puede decrecer, por lo que puede tenerse un delta
+negativo.
+
+Vamos a probarlos todos:
+
+   rrdtool create all.rrd --start 978300900 \
+            DS:a:COUNTER:600:U:U \
+            DS:b:GAUGE:600:U:U \
+            DS:c:DERIVE:600:U:U \
+            DS:d:ABSOLUTE:600:U:U \
+            RRA:AVERAGE:0.5:1:10
+   rrdtool update all.rrd \
+            978301200:300:1:600:300    \
+            978301500:600:3:1200:600   \
+            978301800:900:5:1800:900   \
+            978302100:1200:3:2400:1200 \
+            978302400:1500:1:2400:1500 \
+            978302700:1800:2:1800:1800 \
+            978303000:2100:4:0:2100    \
+            978303300:2400:6:600:2400  \
+            978303600:2700:4:600:2700  \
+            978303900:3000:2:1200:3000
+   rrdtool graph all1.png -s 978300600 -e 978304200 -h 400 \
+            DEF:linea=all.rrd:a:AVERAGE LINE3:linea#FF0000:"Line A" \
+            DEF:lineb=all.rrd:b:AVERAGE LINE3:lineb#00FF00:"Line B" \
+            DEF:linec=all.rrd:c:AVERAGE LINE3:linec#0000FF:"Line C" \
+            DEF:lined=all.rrd:d:AVERAGE LINE3:lined#000000:"Line D"
+
+=head2 RRDtool bajo el microscopio
+
+=over 4
+
+=item *
+
+La línea A es un contador, por lo que
+debe incrementarse continuamente y RRDtool tiene que calcular las
+diferencias. Además RRDtool tiene que dividir la diferencia entre
+el tiempo transcurrido. Esto debería terminar con una línea recta
+en 1 (los deltas son 300, y los intervalos son de 300)
+
+=item *
+
+La línea B es de tipo GAUGE. Estos son
+los valores "reales", así que el gráfico debe mostrar lo mismo que
+los valores que introducimos: una especie de onda
+Z<>
+
+=item *
+
+La línea C es de tipo DERIVE. Es un
+contador, y puede decrecer. Va entre 2400 y 0, con 1800 en el medio.
+
+=item *
+
+La línea D es de tipo ABSOLUTE. Esto es,
+es un contador pero no hay que calcular las diferencias. Los
+números son iguales a la línea A, y espero
+que puedas ver la diferencia en los gráficos.
+
+=back
+
+Esto equivale a los valores siguientes, empezando a las 23:10 y
+terminando a las 00:10 (las U significan desconocido).
+
+
+ - Línea  A:  u  u  1  1  1  1  1  1  1  1  1  u
+ - Línea  B:  u  1  3  5  3  1  2  4  6  4  2  u
+ - Línea  C:  u  u  2  2  2  0 -2 -6  2  0  2  u
+ - Línea  D:  u  1  2  3  4  5  6  7  8  9 10  u
+
+Si tu archivo PNG muestra todo esto, has
+entrado los datos correctamente, tu programa RRDtool está funcionando
+bien, el visor de gráficos no te engaña y hemos entrado en el 2000 sin
+problemas :) Puedes probar el mismo ejemplo cuatro veces, una por cada línea.
+
+Revisemos los datos otra vez:
+
+=over 4
+
+=item *
+
+Línea A: 300, 600, 900 , etc.
+La diferencia del contador es siempre 300, igual que el intervalo de
+tiempo transcurrido entre mediciones. Por lo tanto, el promedio
+siempre es 1. Pero, Â¿por qué el primer punto tiene un valor de
+"desconocido"? Â¿Acaso no era conocido el valor que pusimos en la
+base de datos? Â¡Si! Pero no teníamos un valor inicial para
+calcular la diferencia. Sería un error asumir que el contador
+empezaba en 0, así que no conocemos el valor de la diferencia
+
+=item *
+
+Línea B:
+No hay nada que calcular, los valores son los mismos que se
+introdujeron en la base de datos.
+
+=item *
+
+Línea C:
+De nuevo, no conocemos el valor
+inicial antes de la primera medición, así que se aplica el mismo
+razonamiento que para la línea A. En este
+caso las diferencias no son constantes, así que la línea no es
+recta. Si hubiésemos puesto los mismos valores que en la línea
+A, el gráfico sería el mismo. Al contrario
+que COUNTER, el valor puede decrecer, y espero mostrarte más
+adelante el por que de la diferencia entre ambos tipos.
+
+=item *
+
+Línea D: En este caso, el dispositivo nos
+da las diferencias por sí mismo. Por lo tanto, conocemos la
+diferencia inicial, y podemos graficarla. Tenemos los mismos
+valores que en la línea A, pero su
+significado es distinto, por lo que el gráfico también lo es. En
+este caso, las diferencias se incrementan en 300 cada vez,
+mientras que el intervalo de tiempo permanece constante en 300
+segundos, por lo que la división nos da resultados cada vez mayores.
+
+=back
+
+=head2 Reinicialización de los contadores
+
+Todavía nos quedan algunas cosas por ver. Nos quedan algunas
+opciones importantes por cubrir, y aun no hemos hablado de la
+reinicialización de contadores. Empecemos por ahí: Estamos en nuestro
+coche, vemos el contador y muestra 999987. Andamos unos 20 KM, así que
+el contador debe subir a 1000007. Desafortunadamente, el contador
+sólo tiene 6 dígitos, así que en realidad nos muestra 000007. Si
+estuviéramos guardando los valores en un tipo DERIVE, esto
+significaría que el contador retrocedió unos 999980 KM. Por supuesto
+esto no es cierto, por lo que necesitamos alguna protección contra estos
+casos. Esta protección sólo la tenemos para el tipo COUNTER, el cual
+de todas formas era el que teníamos que haber usado para este tipo de
+contador. Â¿Cómo funciona? Los valores tipo COUNTER no deben decrecer
+nunca, Â¡por lo que RRDtool asume en ese caso que el contador se ha
+reinicializado! Si la diferencia es negativa, esto se compensa sumando
+el valor máximo del contador + 1. Para nuestro coche, tendríamos:
+
+ Delta = 7 - 999987 = -999980    (en vez de 1000007-999987=20)
+
+ Delta real= -999980 + 999999 + 1 = 20
+
+Al momento de escribir este documento, RRDtool maneja contadores de
+32 o 64 bits de tamaño. Estos contadores pueden manejar los siguientes
+valores:
+
+ - 32 bits: 0 ..           4294967295
+ - 64 bits: 0 .. 18446744073709551615
+
+Si estos valores te parecen raros, podemos verlos en formato hexadecimal:
+
+ - 32 bits: 0 ..         FFFFFFFF
+ - 64 bits: 0 .. FFFFFFFFFFFFFFFF
+
+RRDtool maneja ambos contadores de la misma manera. Si ocurre un
+desbordamiento y la diferencia es negativa, RRDtool le suma primero
+el máximo del contador "menor" (32 bits) + 1 a la diferencia. Si aún
+así la diferencia es negativa, entonces el contador reinicializado era
+mayor (64 bits), por lo que se le suma el valor máximo del contador
+"largo" + 1 y se le resta el máximo del contador "pequeño" que sumamos
+erróneamente. Hay un problema con esto: supongamos que un contador
+largo se ha reinicializado al sumársele una diferencia muy grande;
+entonces es posible que al añadir el valor máximo del contador pequeño
+la diferencia nos dé positivo. En este caso poco probable, los valores
+resultantes no serian correctos. Para que ocurra esto, el incremento
+tiene que ser casi tan grande como el valor máximo del contador, por
+lo que de ocurrir es muy probable que halla varios problemas más en
+la configuración y no merezca la pena preocuparse sólo por este. Aún
+así, he incluido un ejemplo de este caso para que lo puedas juzgar por
+ti mismo.
+
+A continuación, unos ejemplos de reinicialización de los
+contadores. Prueba de hacer los cálculos por ti mismo, o acepta mis
+resultados si tu calculadora no puede con los números :)
+
+Números de corrección:
+
+ - 32 bits: (4294967295+1) =                                 4294967296
+ - 64 bits: (18446744073709551615+1)-correction1 = 18446744069414584320
+
+ Antes:          4294967200
+ Incremento:            100
+ Debería ser:    4294967300
+ Pero es:                 4
+ Diferencia:    -4294967196
+ Corrección #1: -4294967196 + 4294967296 = 100
+
+ Antes:          18446744073709551000
+ Incremento:                      800
+ Debería ser:    18446744073709551800
+ Pero es:                         184
+ Diferencia:    -18446744073709550816
+ Corrección #1: -18446744073709550816 +4294967296 = -18446744069414583520
+ Corrección #2: -18446744069414583520 +18446744069414584320 = 800
+
+ Antes:          18446744073709551615 ( valor máximo )
+ Incremento:     18446744069414584320 ( incremento absurdo,
+ Debería ser:    36893488143124135935   mínimo para que
+ Pero es:        18446744069414584319   funcione el ejemplo)
+ Diferencia:              -4294967296
+ Corrección #1:  -4294967296 + 4294967296 = 0 (positivo,
+                                               por tanto no se hace
+                                               la segunda corrección)
+
+ Antes:          18446744073709551615 ( valor máximo )
+ Incremento:     18446744069414584319
+ Debería ser:    36893488143124135934
+ Pero es:        18446744069414584318
+ Diferencia:              -4294967297
+ Corrección #1:  -4294967297 +4294967296 = -1
+ Corrección #2:  -1 +18446744069414584320 = 18446744069414584319
+
+Como puede verse en los Ãºltimos ejemplos, necesitas unos valores
+bastante extraños para hacer que RRDtool falle (asumiendo que no tenga
+ningún error el programa, por supuesto), así que esto no debería
+ocurrir. Sin embargo, SNMP o cualquier otro
+método que uses de recogida de datos puede también reportar algún
+valor erróneo ocasionalmente. No podemos prevenir todos los errores,
+pero podemos tomar algunas medidas. El comando "create" de RRDtool
+tiene dos parámetros especialmente para esto, que definen los valores
+mínimo y máximo permitidos. Hasta ahora hemos usado "U",
+"desconocido". Si le pasas valores para uno o ambos parámetros y
+RRDtool recibe un valor fuera de esos límites, los ignorará. Para un
+termómetro en grados Celsius, el mínimo absoluto es -273. Para mi
+enrutador, puedo asumir que ese mínimo es mucho mayor, digamos que 10.
+La temperatura máxima la pondría en unos 80 grados; más alto y
+el aparato no funcionaría. Para mi coche, nunca esperaría obtener
+valores negativos, y tampoco esperaría valores mayores a 230.
+Cualquier otra cosa sería un error. Pero recuerda, lo contrario no es
+cierto: si los valores pasan este examen no quiere decir que sean los
+correctos. Siempre examina bien el gráfico si los valores parecen
+extraños.
+
+
+=head2 Remuestreo de los datos
+
+Hay una funcionalidad importante de RRDtool que no hemos explicado
+todavía: es virtualmente imposible recoger los datos y pasarselos a
+RRDtool a intervalos exactos de tiempo. Por tanto, RRDtool interpola
+los datos a los intervalos exactos. Si no sabes que significa esto o
+como se hace, he aquí la ayuda que necesitas:
+
+Supongamos un contador se incremente exactamente en 1 cada segundo.
+Queremos medirlo cada 300 segundos, por lo que deberíamos tener
+valores separados exactamente en 300. Sin embargo, por varias
+circunstancias llegamos unos segundos tarde y el intervalo es 303. La
+diferencia será por tanto 303. Obviamente, RRDtool no debe colocar 303
+en la base de datos y dar así la impresión de que el contador se
+incrementó 303 en 300 segundos. Aquí es donde RRDtool interpola:
+alterá el valor 303 al valor que tendría 3 segundos antes y guarda 300
+en 300 segundos. Digamos que la próxima vez llegamos justo a tiempo;
+por tanto, el intervalo actual es 297 segundos, por lo que el contador
+debería ser 297. De nuevo, RRDtool altera el valor y guarda 300, como
+debe ser.
+
+         en RRD                     en realidad
+ tiempo+000:   0 delta="U"    tiempo+000:   0 delta="U"
+ tiempo+300: 300 delta=300    tiempo+300: 300 delta=300
+ tiempo+600: 600 delta=300    tiempo+603: 603 delta=303
+ tiempo+900: 900 delta=300    tiempo+900: 900 delta=297
+
+Creemos dos bases de datos idénticas. He escogido el rango de
+tiempo entre 920805000 y 920805900.
+
+   rrdtool create seconds1.rrd   \
+      --start 920804700          \
+      DS:seconds:COUNTER:600:U:U \
+      RRA:AVERAGE:0.5:1:24
+
+   para Unix: cp seconds1.rrd seconds2.rrd
+   para DOS: copy seconds1.rrd seconds2.rrd
+   para VMS:  y yo que sé :)
+
+   rrdtool update seconds1.rrd \
+      920805000:000 920805300:300 920805600:600 920805900:900
+   rrdtool update seconds2.rrd \
+      920805000:000 920805300:300 920805603:603 920805900:900
+
+   rrdtool graph seconds1.png                       \
+      --start 920804700 --end 920806200             \
+      --height 200                                  \
+      --upper-limit 1.05 --lower-limit 0.95 --rigid \
+      DEF:seconds=seconds1.rrd:seconds:AVERAGE      \
+      CDEF:unknown=seconds,UN                       \
+      LINE2:seconds#0000FF                          \
+      AREA:unknown#FF0000
+   rrdtool graph seconds2.png                       \
+      --start 920804700 --end 920806200             \
+      --height 200                                  \
+      --upper-limit 1.05 --lower-limit 0.95 --rigid \
+      DEF:seconds=seconds2.rrd:seconds:AVERAGE      \
+      CDEF:unknown=seconds,UN                       \
+      LINE2:seconds#0000FF                          \
+      AREA:unknown#FF0000
+
+Los dos gráficos debe ser iguales.
+
+=head1 RESUMEN
+
+Es hora de concluir este documento. Ahora debes conocer lo básico
+como para trabajar con RRDtool y leer la documentación. Aún hay mucho
+más por descubrir acerca de RRDtool, y le encontrarás; más y más usos
+para la herramienta. Con los ejemplos y la herramienta puedes crear
+fácilmente muchos gráficos; también puedes usar las interfaces
+disponibles.
+
+=head1 LISTA DE CORREO
+
+Recuerda subscribirte a la lista de correo. Aunque no contestes los
+correos que aparecen en ella, te servirá de ayuda a ti y a los demás.
+Mucho de lo que se sobre MRTG (y por tanto sobre RRDtool), lo aprendí
+tan sólo con leer la lista, sin escribir. No hay por que preguntar las
+preguntas básicas, que ya tienen su respuesta en la FAQ (¡léela!). Con
+miles de usuarios a lo largo del mundo, siempre hay preguntas que tu
+puedes responder con lo aprendido en este y otros documentos.
+
+=head1 VER TAMBIÉN
+
+Las páginas del manual de RRDtool
+
+=head1 AUTOR
+
+Espero que hayas disfrutado con los ejemplos y las descripciones.
+Si es así, ayuda a otros refiriéndolos a este documento cuando te
+hagan preguntas básicas. No sólo obtendrán la respuesta, sino que
+aprenderán muchas otras cosas.
+
+¡No hablo español!
+Alex van den Bogaerdt <alex@vandenbogaerdt.nl>
+
diff --git a/program/doc/rrdtutorial.pod b/program/doc/rrdtutorial.pod
new file mode 100644 (file)
index 0000000..d6ec09d
--- /dev/null
@@ -0,0 +1,1206 @@
+=for changes please consult me first. Thanks, Alex
+
+=head1 NAME
+
+rrdtutorial - Alex van den Bogaerdt's RRDtool tutorial
+
+=head1 DESCRIPTION
+
+RRDtool is written by Tobias Oetiker E<lt>tobi@oetiker.chE<gt> with
+contributions from many people all around the world. This document is
+written by Alex van den Bogaerdt E<lt>alex@vandenbogaerdt.nlE<gt> to help you
+understand what RRDtool is and what it can do for you.
+
+The documentation provided with RRDtool can be too technical for some
+people. This tutorial is here to help you understand the basics of
+RRDtool. It should prepare you to read the documentation yourself.
+It also explains the general things about statistics with a focus on
+networking.
+
+=head1 TUTORIAL
+
+=head2 Important
+
+Please don't skip ahead in this document!  The first part of this
+document explains the basics and may be boring.  But if you don't
+understand the basics, the examples will not be as meaningful to you.
+
+Sometimes things change.  This example used to provide numbers like
+"0.04" in stead of "4.00000e-02".  Those are really the same numbers,
+just written down differently.  Don't be alarmed if a future version
+of rrdtool displays a slightly different form of output. The examples
+in this document are correct for version 1.2.0 of RRDtool.
+
+Also, sometimes bugs do occur. They may also influence the outcome of
+the examples. Example speed4.png was suffering from this (the handling
+of unknown data in an if-statement was wrong). Normal data will be
+just fine (a bug in rrdtool wouldn't last long) but special cases like
+NaN, INF and so on may last a bit longer.  Try another version if you
+can, or just live with it.
+
+I fixed the speed4.png example (and added a note). There may be other
+examples which suffer from the same or a similar bug.  Try to fix it
+yourself, which is a great excercise. But please do not submit your
+result as a fix to the source of this document. Discuss it on the
+user's list, or write to me.
+
+=head2 What is RRDtool?
+
+RRDtool refers to Round Robin Database tool.
+Round robin is a technique that works with a fixed amount of data, and a
+pointer to the current element. Think of a circle with some dots plotted
+on the edge. These dots are the places where data can be stored. Draw an
+arrow from the center of the circle to one of the dots; this is the pointer.
+When the current data is read or written, the pointer moves to the next
+element. As we are on a circle there is neither a beginning nor an end, you can
+go on and on and on. After a while, all the available places will be used and
+the process automatically reuses old locations. This way, the dataset
+will not grow in size and therefore requires no maintenance.
+RRDtool works with with Round Robin Databases (RRDs). It stores and retrieves
+data from them.
+
+=head2 What data can be put into an RRD?
+
+You name it, it will probably fit as long as it is some sort of
+time-series data. This means you have to be able to measure some value
+at several points in time and provide this information to RRDtool. If
+you can do this, RRDtool will be able to store it. The values must be
+numerical but don't have to be integers, as is the case with MRTG (the
+next section will give more details on this more specialized application).
+
+Many examples below talk about SNMP which is an acronym for Simple Network
+Management Protocol. "Simple" refers to the protocol. It does not
+mean it is simple to manage or monitor a network. After working your
+way through this document, you should know enough to be able to
+understand what people are talking about. For now, just realize that
+SNMP can be used to query devices for the values of counters they keep. It
+is the value from those counters that we want to store in the RRD.
+
+=head2 What can I do with this tool?
+
+RRDtool originated from MRTG (Multi Router Traffic Grapher). MRTG
+started as a tiny little script for graphing the use of a university's
+connection to the Internet. MRTG was later (ab-)used as a tool for
+graphing other data sources including temperature, speed, voltage,
+number of printouts and the like.
+
+Most likely you will start to use RRDtool to store and process data
+collected via SNMP. The data will most likely be bytes (or bits)
+transferred from and to a network or a computer.  But it can also be
+used to display tidal waves, solar radiation, power consumption,
+number of visitors at an exhibition, noise levels near an airport,
+temperature on your favorite holiday location, temperature in the
+fridge and whatever your imagination can come up with.
+
+You only need a sensor to measure the data and be able to feed the
+numbers into RRDtool. RRDtool then lets you create a database, store
+data in it, retrieve that data and create graphs in PNG format for
+display on a web browser. Those PNG images are dependent on the data
+you collected and could be, for instance, an overview of the average
+network usage, or the peaks that occurred.
+
+=head2 What if I still have problems after reading this document?
+
+First of all: read it again! You may have missed something.
+If you are unable to compile the sources and you have a fairly common
+OS, it will probably not be the fault of RRDtool. There may be pre-compiled
+versions around on the Internet. If they come from trusted sources, get
+one of those.
+
+If on the other hand the program works but does not give you the
+expected results, it will be a problem with configuring it. Review
+your configuration and compare it with the examples that follow.
+
+There is a mailing list and an archive of it. Read the list for a few
+weeks and search the archive. It is considered rude to just ask
+a question without searching the archives: your problem may already have been
+solved for somebody else!  This is true for most, if not all, mailing lists
+and not only for this particular one. Look in the documentation that
+came with RRDtool for the location and usage of the list.
+
+I suggest you take a moment to subscribe to the mailing list right now
+by sending an email to E<lt>rrd-users-request@lists.oetiker.chE<gt> with a
+subject of "subscribe". If you ever want to leave this list, just write
+an email to the same address but now with a subject of "unsubscribe".
+
+=head2 How will you help me?
+
+By giving you some detailed descriptions with detailed examples.
+I assume that following the instructions in the order presented
+will give you enough knowledge of RRDtool to experiment for yourself.
+If it doesn't work the first time, don't give up. Reread the stuff that
+you did understand, you may have missed something.
+
+By following the examples you get some hands-on experience and, even
+more important, some background information of how it works.
+
+You will need to know something about hexadecimal numbers. If you don't
+then start with reading L<bin_dec_hex> before you continue here.
+
+=head2 Your first Round Robin Database
+
+In my opinion the best way to learn something is to actually do it.
+Why not start right now?  We will create a database, put some values
+in it and extract this data again.  Your output should be the same
+as the output that is included in this document.
+
+We will start with some easy stuff and compare a car with a router,
+or compare kilometers (miles if you wish) with bits and bytes. It's
+all the same: some number over some time.
+
+Assume we have a device that transfers bytes to and from the Internet.
+This device keeps a counter that starts at zero when it is turned on,
+increasing with every byte that is transferred. This counter will probably have
+a maximum value. If this value is reached and an extra byte is counted,
+the counter starts over at zero. This is the same as many counters
+in the world such as the mileage counter in a car.
+
+Most discussions about networking talk about bits per second so lets
+get used to that right away. Assume a byte is eight bits and start to
+think in bits not bytes. The counter, however, still counts bytes!
+In the SNMP world most of the counters are 32 bits. That means they are
+counting from 0 to 4294967295. We will use these values in the examples.
+The device, when asked, returns the current value of the counter. We
+know the time that has passes since we last asked so we now know how
+many bytes have been transferred ***on average*** per second. This is
+not very hard to calculate. First in words, then in calculations:
+
+=over 3
+
+=item 1.
+
+Take the current counter, subtract the previous value from it.
+
+=item 2.
+
+Do the same with the current time and the previous time (in seconds).
+
+=item 3.
+
+Divide the outcome of (1) by the outcome of (2), the result is
+the amount of bytes per second. Multiply by eight to get the
+number of bits per second (bps).
+
+=back
+
+  bps = (counter_now - counter_before) / (time_now - time_before) * 8
+
+For some people it may help to translate this to an automobile example.
+Do not try this example, and if you do, don't blame me for the results!
+
+People who are not used to think in kilometers per hour can translate
+most into miles per hour by dividing km by 1.6 (close enough).
+I will use the following abbreviations:
+
+ m:    meter
+ km:   kilometer (= 1000 meters).
+ h:    hour
+ s:    second
+ km/h: kilometers per hour
+ m/s:  meters per second
+
+You are driving a car. At 12:05 you read the counter in the dashboard
+and it tells you that the car has moved 12345 km until that moment.
+At 12:10 you look again, it reads 12357 km. This means you have
+traveled 12 km in five minutes. A scientist would translate that
+into meters per second and this makes a nice comparison toward the
+problem of (bytes per five minutes) versus (bits per second).
+
+We traveled 12 kilometers which is 12000 meters. We did that in five
+minutes or 300 seconds. Our speed is 12000m / 300s or 40 m/s.
+
+We could also calculate the speed in km/h: 12 times 5 minutes
+is an hour, so we have to multiply 12 km by 12 to get 144 km/h.
+For our native English speaking friends: that's 90 mph so don't
+try this example at home or where I live :)
+
+Remember: these numbers are averages only.  There is no way to figure out
+from the numbers, if you drove at a constant speed.  There is an example
+later on in this tutorial that explains this.
+
+I hope you understand that there is no difference in calculating m/s or
+bps; only the way we collect the data is different. Even the k from kilo
+is the same as in networking terms k also means 1000.
+
+We will now create a database where we can keep all these interesting
+numbers. The method used to start the program may differ slightly from
+OS to OS, but I assume you can figure it out if it works different on
+your's. Make sure you do not overwrite any file on your system when
+executing the following command and type the whole line as one long
+line (I had to split it for readability)
+and skip all of the '\' characters.
+
+   rrdtool create test.rrd             \
+            --start 920804400          \
+            DS:speed:COUNTER:600:U:U   \
+            RRA:AVERAGE:0.5:1:24       \
+            RRA:AVERAGE:0.5:6:10
+
+(So enter: C<rrdtool create test.rrd --start 920804400 DS ...>)
+
+=head2 What has been created?
+
+We created the round robin database called test (test.rrd) which starts at
+noon the day I started writing this document, 7th of March, 1999 (this date
+translates to 920804400 seconds as explained below). Our database holds
+one data source (DS) named "speed" that represents a counter. This counter
+is read every five minutes (this is the default therefore you don't have to
+put C<--step=300>).  In the same database two round robin archives (RRAs)
+are kept, one averages the data every time it is read (e.g., there's nothing
+to average) and keeps 24 samples (24 times 5 minutes is 2 hours). The other
+averages 6 values (half hour) and contains 10 such averages (e.g. 5 hours).
+
+RRDtool works with special time stamps coming from the UNIX world.
+This time stamp is the number of seconds that passed since January
+1st 1970 UTC.  The time stamp value is translated into local time and
+it will therefore look different for different time zones.
+
+Chances are that you are not in the same part of the world as I am.
+This means your time zone is different. In all examples where I talk
+about time, the hours may be wrong for you. This has little effect on
+the results of the examples, just correct the hours while reading.
+As an example: where I will see "12:05" the UK folks will see "11:05".
+
+We now have to fill our database with some numbers. We'll pretend to
+have read the following numbers:
+
+ 12:05 12345 km
+ 12:10 12357 km
+ 12:15 12363 km
+ 12:20 12363 km
+ 12:25 12363 km
+ 12:30 12373 km
+ 12:35 12383 km
+ 12:40 12393 km
+ 12:45 12399 km
+ 12:50 12405 km
+ 12:55 12411 km
+ 13:00 12415 km
+ 13:05 12420 km
+ 13:10 12422 km
+ 13:15 12423 km
+
+We fill the database as follows:
+
+ rrdtool update test.rrd 920804700:12345 920805000:12357 920805300:12363
+ rrdtool update test.rrd 920805600:12363 920805900:12363 920806200:12373
+ rrdtool update test.rrd 920806500:12383 920806800:12393 920807100:12399
+ rrdtool update test.rrd 920807400:12405 920807700:12411 920808000:12415
+ rrdtool update test.rrd 920808300:12420 920808600:12422 920808900:12423
+
+This reads: update our test database with the following numbers
+
+ time 920804700, value 12345
+ time 920805000, value 12357
+
+etcetera.
+
+As you can see, it is possible to feed more than one value into the
+database in one command. I had to stop at three for readability but
+the real maximum per line is OS dependent.
+
+We can now retrieve the data from our database using "rrdtool fetch":
+
+ rrdtool fetch test.rrd AVERAGE --start 920804400 --end 920809200
+
+It should return the following output:
+
+                          speed
+
+ 920804700: nan
+ 920805000: 4.0000000000e-02
+ 920805300: 2.0000000000e-02
+ 920805600: 0.0000000000e+00
+ 920805900: 0.0000000000e+00
+ 920806200: 3.3333333333e-02
+ 920806500: 3.3333333333e-02
+ 920806800: 3.3333333333e-02
+ 920807100: 2.0000000000e-02
+ 920807400: 2.0000000000e-02
+ 920807700: 2.0000000000e-02
+ 920808000: 1.3333333333e-02
+ 920808300: 1.6666666667e-02
+ 920808600: 6.6666666667e-03
+ 920808900: 3.3333333333e-03
+ 920809200: nan
+
+If it doesn't, something may be wrong.  Perhaps your OS will print
+"NaN" in a different form. "NaN" stands for "Not A Number".  If your OS
+writes "U" or "UNKN" or something similar that's okay.  If something
+else is wrong, it will probably be due to an error you made (assuming
+that my tutorial is correct of course :-). In that case: delete the
+database and try again. 
+
+
+The meaning of the above output will become clear below.
+
+=head2 Time to create some graphics
+
+Try the following command:
+
+ rrdtool graph speed.png                                 \
+         --start 920804400 --end 920808000               \
+         DEF:myspeed=test.rrd:speed:AVERAGE              \
+         LINE2:myspeed#FF0000
+
+This will create speed.png which starts at 12:00 and ends at 13:00.
+There is a definition of a variable called myspeed, using the data from RRA
+"speed" out of database "test.rrd". The line drawn is 2 pixels high
+and represents the variable myspeed. The color is red (specified by
+its rgb-representation, see below).
+
+You'll notice that the start of the graph is not at 12:00 but at 12:05.
+This is because we have insufficient data to tell the average before
+that time. This will only happen when you miss some samples, this will
+not happen a lot, hopefully.
+
+If this has worked: congratulations! If not, check what went wrong.
+
+
+The colors are built up from red, green and blue. For each of the
+components, you specify how much to use in hexadecimal where 00 means
+not included and FF means fully included.
+The "color" white is a mixture of red, green and blue: FFFFFF
+The "color" black is all colors off: 000000
+
+   red     #FF0000
+   green   #00FF00
+   blue    #0000FF
+   magenta #FF00FF     (mixed red with blue)
+   gray    #555555     (one third of all components)
+
+Additionally you can (with a recent RRDtool)  add an alpha channel
+(transparency).  The default will be "FF" which means non-transparent.
+
+The PNG you just created can be displayed using your favorite image
+viewer.  Web browsers will display the PNG via the URL
+"file:///the/path/to/speed.png"
+
+=head2 Graphics with some math
+
+When looking at the image, you notice that the horizontal axis is labeled
+12:10, 12:20, 12:30, 12:40 and 12:50. Sometimes a label doesn't fit (12:00
+and 13:00 would be likely candidates) so they are skipped.
+
+The vertical axis displays the range we entered. We provided
+kilometers and when divided by 300 seconds, we get very small
+numbers. To be exact, the first value was 12 (12357-12345) and divided
+by 300 this makes 0.04, which is displayed by RRDtool as "40 m"
+meaning "40/1000". The "m" (milli) has nothing to do with meters (also m),
+kilometers or millimeters! RRDtool doesn't know about the physical
+units of our data, it just works with dimensionless numbers.
+
+If we had measured our distances in meters, this would have been
+(12357000-12345000)/300 = 12000/300 = 40.
+
+As most people have a better feel for numbers in this range, we'll
+correct that. We could recreate our database and store the correct
+data, but there is a better way: we do some calculations while creating
+the png file!
+
+   rrdtool graph speed2.png                           \
+      --start 920804400 --end 920808000               \
+      --vertical-label m/s                            \
+      DEF:myspeed=test.rrd:speed:AVERAGE              \
+      CDEF:realspeed=myspeed,1000,\*                  \
+      LINE2:realspeed#FF0000
+
+Note: I need to escape the multiplication operator * with a backslash.
+If I don't, the operating system may interpret it and use it for file
+name expansion. You could also place the line within quotation marks
+like so:
+
+      "CDEF:realspeed=myspeed,1000,*"                  \
+
+It boils down to: it is RRDtool which should see *, not your shell.
+And it is your shell interpreting \, not RRDtool. You may need to
+adjust examples accordingly if you happen to use an operating
+system or shell which behaves differently.
+
+After viewing this PNG, you notice the "m" (milli) has
+disappeared. This it what the correct result would be. Also, a label
+has been added to the image.  Apart from the things mentioned above,
+the PNG should look the same.
+
+The calculations are specified in the CDEF part above and are in
+Reverse Polish Notation ("RPN"). What we requested RRDtool to do is:
+"take the data source myspeed and the number 1000; multiply
+those". Don't bother with RPN yet, it will be explained later on in
+more detail. Also, you may want to read my tutorial on CDEFs and Steve
+Rader's tutorial on RPN. But first finish this tutorial.
+
+Hang on! If we can multiply values with 1000, it should also be possible
+to display kilometers per hour from the same data!
+
+To change a value that is measured in meters per second:
+
+ Calculate meters per hour:     value * 3600
+ Calculate kilometers per hour: value / 1000
+ Together this makes:           value * (3600/1000) or value * 3.6
+
+In our example database we made a mistake and we need to compensate for
+this by multiplying with 1000. Applying that correction:
+
+ value * 3.6  * 1000 == value * 3600
+
+Now let's create this PNG, and add some more magic ...
+
+ rrdtool graph speed3.png                             \
+      --start 920804400 --end 920808000               \
+      --vertical-label km/h                           \
+      DEF:myspeed=test.rrd:speed:AVERAGE              \
+      "CDEF:kmh=myspeed,3600,*"                       \
+      CDEF:fast=kmh,100,GT,kmh,0,IF                   \
+      CDEF:good=kmh,100,GT,0,kmh,IF                   \
+      HRULE:100#0000FF:"Maximum allowed"              \
+      AREA:good#00FF00:"Good speed"                   \
+      AREA:fast#FF0000:"Too fast"
+
+Note: here we use another means to escape the * operator by enclosing
+the whole string in double quotes.
+
+This graph looks much better. Speed is shown in km/h and there is even
+an extra line with the maximum allowed speed (on the road I travel
+on). I also changed the colors used to display speed and changed it
+from a line into an area.
+
+The calculations are more complex now. For speed measurements within
+the speed limit they are:
+
+   Check if kmh is greater than 100    ( kmh,100 ) GT
+   If so, return 0, else kmh           ((( kmh,100 ) GT ), 0, kmh) IF
+
+For values above the speed limit:
+
+   Check if kmh is greater than 100    ( kmh,100 ) GT
+   If so, return kmh, else return 0    ((( kmh,100) GT ), kmh, 0) IF
+
+=head2 Graphics Magic
+
+I like to believe there are virtually no limits to how RRDtool graph
+can manipulate data. I will not explain how it works, but look at the
+following PNG:
+
+   rrdtool graph speed4.png                           \
+      --start 920804400 --end 920808000               \
+      --vertical-label km/h                           \
+      DEF:myspeed=test.rrd:speed:AVERAGE              \
+      CDEF:nonans=myspeed,UN,0,myspeed,IF             \
+      CDEF:kmh=nonans,3600,*                          \
+      CDEF:fast=kmh,100,GT,100,0,IF                   \
+      CDEF:over=kmh,100,GT,kmh,100,-,0,IF             \
+      CDEF:good=kmh,100,GT,0,kmh,IF                   \
+      HRULE:100#0000FF:"Maximum allowed"              \
+      AREA:good#00FF00:"Good speed"                   \
+      AREA:fast#550000:"Too fast"                     \
+      STACK:over#FF0000:"Over speed"
+
+Remember the note in the beginning?  I had to remove unknown data from
+this example. The 'nonans' CDEF is new, and the 6th line (which used to
+be the 5th line) used to read 'CDEF:kmh=myspeed,3600,*'
+
+Let's create a quick and dirty HTML page to view the three PNGs:
+
+   <HTML><HEAD><TITLE>Speed</TITLE></HEAD><BODY>
+   <IMG src="speed2.png" alt="Speed in meters per second">
+   <BR>
+   <IMG src="speed3.png" alt="Speed in kilometers per hour">
+   <BR>
+   <IMG src="speed4.png" alt="Traveled too fast?">
+   </BODY></HTML>
+
+Name the file "speed.html" or similar, and look at it in your web browser.
+
+Now, all you have to do is measure the values regularly and update the
+database.  When you want to view the data, recreate the PNGs and make
+sure to refresh them in your browser. (Note: just clicking reload may
+not be enough, especially when proxies are involved.  Try shift-reload
+or ctrl-F5).
+
+=head2 Updates in Reality
+
+We've already used the C<update> command: it took one or more
+parameters in the form of "<time>:<value>". You'll be glad to know
+that you can specify the current time by filling in a "N" as the time.
+Or you could use the "time" function in Perl (the shortest example in
+this tutorial):
+
+   perl -e 'print time, "\n" '
+
+How to run a program on regular intervals is OS specific. But here is
+an example in pseudo code:
+
+   - Get the value and put it in variable "$speed"
+   - rrdtool update speed.rrd N:$speed
+
+(do not try this with our test database, we'll use it in further examples)
+
+This is all. Run the above script every five minutes. When you need to know
+what the graphs look like, run the examples above. You could put them
+in a script as well. After running that script, view the page
+index.html we created above.
+
+=head2 Some words on SNMP
+
+I can imagine very few people that will be able to get real data from
+their car every five minutes. All other people will have to settle for
+some other kind of counter. You could measure the number of pages
+printed by a printer, for example, the cups of coffee made by the
+coffee machine, a device that counts the electricity used,
+whatever. Any incrementing counter can be monitored and graphed using
+the stuff you learned so far. Later on we will also be able to monitor
+other types of values like temperature.
+
+Many people interested in RRDtool will use the counter that keeps track
+of octets (bytes) transferred by a network device. So let's do just
+that next. We will start with a description of how to collect data.
+
+Some people will make a remark that there are tools which can do this data
+collection for you. They are right! However, I feel it is important that
+you understand they are not necessary. When you have to determine why
+things went wrong you need to know how they work.
+
+One tool used in the example has been talked about very briefly in the
+beginning of this document, it is called SNMP. It is a way of talking
+to networked equipment. The tool I use below is called "snmpget" and
+this is how it works:
+
+   snmpget device password OID
+
+or
+
+   snmpget -v[version] -c[password] device OID
+
+For device you substitute the name, or the IP address, of your device.
+For password you use the "community read string" as it is called in the
+SNMP world.  For some devices the default of "public" might work, however
+this can be disabled, altered or protected for privacy and security
+reasons.  Read the documentation that comes with your device or program.
+
+Then there is this parameter, called OID, which means "object identifier".
+
+When you start to learn about SNMP it looks very confusing. It isn't
+all that difficult when you look at the Management Information Base
+("MIB").  It is an upside-down tree that describes data, with a single node
+as the root and from there a number of branches.  These branches end
+up in another node, they branch out, etc.  All the branches have a name
+and they form the path that we follow all the way down.  The branches
+that we follow are named: iso, org, dod, internet, mgmt and mib-2.
+These names can also be written down as numbers and are 1 3 6 1 2 1.
+
+   iso.org.dod.internet.mgmt.mib-2 (1.3.6.1.2.1)
+
+There is a lot of confusion about the leading dot that some programs
+use.  There is *no* leading dot in an OID.  However, some programs
+can use the above part of OIDs as a default.  To indicate the difference
+between abbreviated OIDs and full OIDs they need a leading dot when
+you specify the complete OID.  Often those programs will leave out
+the default portion when returning the data to you.  To make things
+worse, they have several default prefixes ...
+
+Ok, lets continue to the start of our OID: we had 1.3.6.1.2.1
+From there, we are especially interested in the branch "interfaces"
+which has number 2 (e.g., 1.3.6.1.2.1.2 or 1.3.6.1.2.1.interfaces).
+
+First, we have to get some SNMP program. First look if there is a
+pre-compiled package available for your OS. This is the preferred way.
+If not, you will have to get the sources yourself and compile those.
+The Internet is full of sources, programs etc. Find information using
+a search engine or whatever you prefer.
+
+Assume you got the program. First try to collect some data that is
+available on most systems. Remember: there is a short name for the
+part of the tree that interests us most in the world we live in!
+
+I will give an example which can be used on Fedora Core 3.  If it
+doesn't work for you, work your way through the manual of snmp and
+adapt the example to make it work.
+
+   snmpget -v2c -c public myrouter system.sysDescr.0
+
+The device should answer with a description of itself, perhaps an
+empty one. Until you got a valid answer from a device, perhaps using a
+different "password", or a different device, there is no point in
+continuing.
+
+   snmpget -v2c -c public myrouter interfaces.ifNumber.0
+
+Hopefully you get a number as a result, the number of interfaces.
+If so, you can carry on and try a different program called "snmpwalk".
+
+   snmpwalk -v2c -c public myrouter interfaces.ifTable.ifEntry.ifDescr
+
+If it returns with a list of interfaces, you're almost there.
+Here's an example:
+   [user@host /home/alex]$ snmpwalk -v2c -c public cisco 2.2.1.2
+
+   interfaces.ifTable.ifEntry.ifDescr.1 = "BRI0: B-Channel 1"
+   interfaces.ifTable.ifEntry.ifDescr.2 = "BRI0: B-Channel 2"
+   interfaces.ifTable.ifEntry.ifDescr.3 = "BRI0" Hex: 42 52 49 30
+   interfaces.ifTable.ifEntry.ifDescr.4 = "Ethernet0"
+   interfaces.ifTable.ifEntry.ifDescr.5 = "Loopback0"
+
+On this cisco equipment, I would like to monitor the "Ethernet0"
+interface and from the above output I see that it is number four. I try:
+
+   [user@host /home/alex]$ snmpget -v2c -c public cisco 2.2.1.10.4 2.2.1.16.4
+
+   interfaces.ifTable.ifEntry.ifInOctets.4 = 2290729126
+   interfaces.ifTable.ifEntry.ifOutOctets.4 = 1256486519
+
+So now I have two OIDs to monitor and they are (in full, this time):
+
+   1.3.6.1.2.1.2.2.1.10
+
+and
+
+   1.3.6.1.2.1.2.2.1.16
+
+both with an interface number of 4.
+
+Don't get fooled, this wasn't my first try. It took some time for me too
+to understand what all these numbers mean. It does help a lot when they
+get translated into descriptive text... At least, when people are talking
+about MIBs and OIDs you know what it's all about.
+Do not forget the interface number (0 if it is not interface dependent)
+and try snmpwalk if you don't get an answer from snmpget.
+
+If you understand the above section and get numbers from your device, continue
+on with this tutorial. If not, then go back and re-read this part.
+
+=head2 A Real World Example
+
+Let the fun begin. First, create a new database. It contains data from
+two counters, called input and output. The data is put into archives
+that average it. They take 1, 6, 24 or 288 samples at a time.
+They also go into archives that keep the maximum numbers. This will be
+explained later on. The time in-between samples is 300 seconds, a good
+starting point, which is the same as five minutes.
+
+ 1 sample "averaged" stays 1 period of 5 minutes
+ 6 samples averaged become one average on 30 minutes
+ 24 samples averaged become one average on 2 hours
+ 288 samples averaged become one average on 1 day
+
+Lets try to be compatible with MRTG which stores about the following
+amount of data:
+
+ 600 5-minute samples:    2   days and 2 hours
+ 600 30-minute samples:  12.5 days
+ 600 2-hour samples:     50   days
+ 732 1-day samples:     732   days
+
+These ranges are appended, so the total amount of data stored in the
+database is approximately 797 days. RRDtool stores the data
+differently, it doesn't start the "weekly" archive where the "daily"
+archive stopped. For both archives the most recent data will be near
+"now" and therefore we will need to keep more data than MRTG does!
+
+We will need:
+
+ 600 samples of 5 minutes  (2 days and 2 hours)
+ 700 samples of 30 minutes (2 days and 2 hours, plus 12.5 days)
+ 775 samples of 2 hours    (above + 50 days)
+ 797 samples of 1 day      (above + 732 days, rounded up to 797)
+
+   rrdtool create myrouter.rrd         \
+            DS:input:COUNTER:600:U:U   \
+            DS:output:COUNTER:600:U:U  \
+            RRA:AVERAGE:0.5:1:600      \
+            RRA:AVERAGE:0.5:6:700      \
+            RRA:AVERAGE:0.5:24:775     \
+            RRA:AVERAGE:0.5:288:797    \
+            RRA:MAX:0.5:1:600          \
+            RRA:MAX:0.5:6:700          \
+            RRA:MAX:0.5:24:775         \
+            RRA:MAX:0.5:288:797
+
+Next thing to do is to collect data and store it. Here is an example.
+It is written partially in pseudo code,  you will have to find out what
+to do exactly on your OS to make it work.
+
+   while not the end of the universe
+   do
+      get result of
+         snmpget router community 2.2.1.10.4
+      into variable $in
+      get result of
+         snmpget router community 2.2.1.16.4
+      into variable $out
+
+      rrdtool update myrouter.rrd N:$in:$out
+
+      wait for 5 minutes
+   done
+
+Then, after collecting data for a day, try to create an image using:
+
+   rrdtool graph myrouter-day.png --start -86400 \
+            DEF:inoctets=myrouter.rrd:input:AVERAGE \
+            DEF:outoctets=myrouter.rrd:output:AVERAGE \
+            AREA:inoctets#00FF00:"In traffic" \
+            LINE1:outoctets#0000FF:"Out traffic"
+
+This should produce a picture with one day worth of traffic.
+One day is 24 hours of 60 minutes of 60 seconds: 24*60*60=86400, we
+start at now minus 86400 seconds. We define (with DEFs) inoctets and
+outoctets as the average values from the database myrouter.rrd and draw
+an area for the "in" traffic and a line for the "out" traffic.
+
+View the image and keep logging data for a few more days.
+If you like, you could try the examples from the test database and
+see if you can get various options and calculations to work.
+
+Suggestion: Display in bytes per second and in bits per second. Make
+the Ethernet graphics go red if they are over four megabits per
+second.
+
+=head2 Consolidation Functions
+
+A few paragraphs back I mentioned the possibility of keeping
+the maximum values instead of the average values. Let's go
+into this a bit more.
+
+Recall all the stuff about the speed of the car. Suppose we drove at 144
+km/h during 5 minutes and then were stopped by the police for 25 minutes.
+At the end of the lecture we would take our laptop and create and view the
+image taken from the database. If we look at the second RRA we did
+create, we would have the average from 6 samples. The samples measured
+would be 144+0+0+0+0+0=144, divided by 30 minutes, corrected for the
+error by 1000, translated into km/h, with a result of 24 km/h.
+I would still get a ticket but not for speeding anymore :)
+
+Obviously, in this case we shouldn't look at the averages. In some
+cases they are handy. If you want to know how many km you had traveled,
+the averaged picture would be the right one to look at. On the other hand, for
+the speed that we traveled at, the maximum numbers seen is much more
+interesting. Later we will see more types.
+
+It is the same for data. If you want to know the amount, look at the
+averages. If you want to know the rate, look at the maximum.
+Over time, they will grow apart more and more. In the last database
+we have created, there are two archives that keep data per day. The
+archive that keeps averages will show low numbers, the archive that
+shows maxima will have higher numbers.
+
+For my car this would translate in averages per day of 96/24=4 km/h
+(as I travel about 94 kilometers on a day) during working days, and
+maxima of 120 km/h (my top speed that I reach every day).
+
+Big difference. Do not look at the second graph to estimate the
+distances that I travel and do not look at the first graph to
+estimate my speed. This will work if the samples are close together,
+as they are in five minutes, but not if you average.
+
+On some days, I go for a long ride. If I go across Europe and travel
+for 12 hours, the first graph will rise to about 60 km/h. The second
+one will show 180 km/h. This means that I traveled a distance of 60
+km/h times 24 h = 1440 km. I did this with a higher speed and a
+maximum around 180 km/h. However, it probably doesn't mean that I
+traveled for 8 hours at a constant speed of 180 km/h!
+
+This is a real example: go with the flow through Germany (fast!) and stop
+a few times for gas and coffee. Drive slowly through Austria and the
+Netherlands. Be careful in the mountains and villages. If you would
+look at the graphs created from the five-minute averages you would
+get a totally different picture. You would see the same values on the
+average and maximum graphs (provided I measured every 300 seconds).
+You would be able to see when I stopped, when I was in top gear, when
+I drove over fast highways etc. The granularity of the data is much
+higher, so you can see more. However, this takes 12 samples per hour,
+or 288 values per day, so it would be a lot of data over a longer
+period of time. Therefore we average it, eventually to one value per
+day. From this one value, we cannot see much detail, of course.
+
+Make sure you understand the last few paragraphs. There is no value
+in only a line and a few axis, you need to know what they mean and
+interpret the data in an appropriate way. This is true for all data.
+
+The biggest mistake you can make is to use the collected data for
+something that it is not suitable for. You would be better off if
+you didn't have the graph at all.
+
+
+=head2 Let's review what you now should know
+
+You know how to create a database and can put data in it. You can get
+the numbers out again by creating an image, do math on the data from
+the database and view the result instead of the raw data.  You know
+about the difference between averages and maximum, and when to use
+which (or at least you should have an idea).
+
+RRDtool can do more than what we have learned up to now. Before you
+continue with the rest of this doc, I recommend that you reread from
+the start and try some modifications on the examples. Make sure you
+fully understand everything. It will be worth the effort and helps
+you not only with the rest of this tutorial, but also in your day to day
+monitoring long after you read this introduction.
+
+=head2 Data Source Types
+
+All right, you feel like continuing. Welcome back and get ready
+for an increased speed in the examples and explanations.
+
+You know that in order to view a counter over time, you have to
+take two numbers and divide the difference of them between the
+time lapsed.  This makes sense for the examples I gave you but there
+are other possibilities.  For instance, I'm able to retrieve the
+temperature from my router in three places namely the inlet, the
+so called hot-spot and the exhaust.  These values are not counters.
+If I take the difference of the two samples and divide that by
+300 seconds I would be asking for the temperature change per second.
+Hopefully this is zero! If not, the computer room is probably on fire :)
+
+So, what can we do?  We can tell RRDtool to store the values we measure
+directly as they are (this is not entirely true but close enough). The
+graphs we make will look much better, they will show a rather constant
+value. I know when the router is busy (it
+works -> it uses more electricity -> it generates more heat -> the
+temperature rises). I know when the doors are left open (the room is
+air conditioned) -> the warm air from the rest of the building flows into the
+computer room -> the inlet temperature rises). Etc. The data type we
+use when creating the database before was counter, we now have a
+different data type and thus a different name for it. It is called
+GAUGE. There are more such data types:
+
+ - COUNTER   we already know this one
+ - GAUGE     we just learned this one
+ - DERIVE
+ - ABSOLUTE
+
+The two additional types are DERIVE and ABSOLUTE. Absolute can be used like
+counter with one difference: RRDtool assumes the counter is reset when
+it's read. That is: its delta is known without calculation by RRDtool
+whereas RRDtool needs to calculate it for the counter type.
+Example: our first example (12345, 12357, 12363, 12363) would read:
+unknown, 12, 6, 0. The rest of the calculations stay the same.
+The other one, derive, is like counter. Unlike counter, it can also
+decrease so it can have a negative delta. Again, the rest of the
+calculations stay the same.
+
+Let's try them all:
+
+   rrdtool create all.rrd --start 978300900 \
+            DS:a:COUNTER:600:U:U \
+            DS:b:GAUGE:600:U:U \
+            DS:c:DERIVE:600:U:U \
+            DS:d:ABSOLUTE:600:U:U \
+            RRA:AVERAGE:0.5:1:10
+   rrdtool update all.rrd \
+            978301200:300:1:600:300    \
+            978301500:600:3:1200:600   \
+            978301800:900:5:1800:900   \
+            978302100:1200:3:2400:1200 \
+            978302400:1500:1:2400:1500 \
+            978302700:1800:2:1800:1800 \
+            978303000:2100:4:0:2100    \
+            978303300:2400:6:600:2400  \
+            978303600:2700:4:600:2700  \
+            978303900:3000:2:1200:3000
+   rrdtool graph all1.png -s 978300600 -e 978304200 -h 400 \
+            DEF:linea=all.rrd:a:AVERAGE LINE3:linea#FF0000:"Line A" \
+            DEF:lineb=all.rrd:b:AVERAGE LINE3:lineb#00FF00:"Line B" \
+            DEF:linec=all.rrd:c:AVERAGE LINE3:linec#0000FF:"Line C" \
+            DEF:lined=all.rrd:d:AVERAGE LINE3:lined#000000:"Line D"
+
+=head2 RRDtool under the Microscope
+
+=over 2
+
+=item *
+
+Line A is a COUNTER type, so it should continuously increment and RRDtool
+must calculate the differences. Also, RRDtool needs to divide the
+difference by the amount of time lapsed. This should end up as a
+straight line at 1 (the deltas are 300, the time is 300).
+
+=item *
+
+Line B is of type GAUGE. These are "real" values so they should match
+what we put in: a sort of a wave.
+
+=item *
+
+Line C is of type DERIVE. It should be a counter that can decrease. It does
+so between 2400 and 0, with 1800 in-between.
+
+=item *
+
+Line D is of type ABSOLUTE. This is like counter but it works on
+values without calculating the difference. The numbers are the same
+and as you can see (hopefully) this has a different result.
+
+=back
+
+This translates in the following values, starting at 23:10 and ending
+at 00:10 the next day (where "u" means unknown/unplotted):
+
+ - Line A:  u  u  1  1  1  1  1  1  1  1  1  u
+ - Line B:  u  1  3  5  3  1  2  4  6  4  2  u
+ - Line C:  u  u  2  2  2  0 -2 -6  2  0  2  u
+ - Line D:  u  1  2  3  4  5  6  7  8  9 10  u
+
+If your PNG shows all this, you know you have entered the data correctly,
+the RRDtool executable is working properly, your viewer doesn't fool you,
+and you successfully entered the year 2000 :)
+
+You could try the same example four times, each time with only one of
+the lines.
+
+Let's go over the data again:
+
+=over 2
+
+=item *
+
+Line A: 300,600,900 and so on. The counter delta is a constant 300 and
+so is the time delta. A number divided by itself is always 1 (except
+when dividing by zero which is undefined/illegal).
+
+Why is it that the first point is unknown? We do know what we put into
+the database, right? True, But we didn't have a value to calculate the delta
+from, so we don't know where we started. It would be wrong to assume we
+started at zero so we don't!
+
+=item *
+
+Line B: There is nothing to calculate. The numbers are as they are.
+
+=item *
+
+Line C: Again, the start-out value is unknown. The same story is holds
+as for line A. In this case the deltas are not constant, therefore the line
+is not either. If we would put the same numbers in the database as we did for
+line A, we would have gotten the same line. Unlike type counter,
+this type can decrease and I hope to show you later on why
+this makes a difference.
+
+=item *
+
+Line D: Here the device calculates the deltas. Therefore we DO know the
+first delta and it is plotted. We had the same input as with line A, but
+the meaning of this input is different and thus the line is different.
+In this case the deltas increase each time with 300. The time delta
+stays at a constant 300 and therefore the division of the two gives
+increasing values.
+
+=back
+
+=head2 Counter Wraps
+
+There are a few more basics to show. Some important options are still to
+be covered and we haven't look at counter wraps yet. First the counter wrap:
+In our car we notice that the counter shows 999987. We travel 20 km and
+the counter should go to 1000007. Unfortunately, there are only six digits
+on our counter so it really shows 000007. If we would plot that on a type
+DERIVE, it would mean that the counter was set back 999980 km. It wasn't,
+and there has to be some protection for this. This protection is only
+available for type COUNTER which should be used for this kind of counter
+anyways. How does it work? Type counter should never decrease and
+therefore RRDtool must assume it wrapped if it does decrease!
+If the delta is negative, this can be compensated for by adding the
+maximum value of the counter + 1. For our car this would be:
+
+ Delta = 7 - 999987 = -999980    (instead of 1000007-999987=20)
+
+ Real delta = -999980 + 999999 + 1 = 20
+
+At the time of writing this document, RRDtool knows of counters that
+are either 32 bits or 64 bits of size. These counters can handle the
+following different values:
+
+ - 32 bits: 0 ..           4294967295
+ - 64 bits: 0 .. 18446744073709551615
+
+If these numbers look strange to you, you can view them in
+their hexadecimal form:
+
+ - 32 bits: 0 ..         FFFFFFFF
+ - 64 bits: 0 .. FFFFFFFFFFFFFFFF
+
+RRDtool handles both counters the same. If an overflow occurs and
+the delta would be negative, RRDtool first adds the maximum of a small
+counter + 1 to the delta. If the delta is still negative, it had to be
+the large counter that wrapped. Add the maximum possible value of the
+large counter + 1 and subtract the erroneously added small value.
+
+There is a risk in this: suppose the large counter wrapped while adding
+a huge delta, it could happen, theoretically, that adding the smaller value
+would make the delta positive. In this unlikely case the results would
+not be correct. The increase should be nearly as high as the maximum
+counter value for that to happen, so chances are you would have several
+other problems as well and this particular problem would not even be
+worth thinking about. Even though, I did include an example, so you
+can judge for yourself.
+
+The next section gives you some numerical examples for counter-wraps.
+Try to do the calculations yourself or just believe me if your calculator
+can't handle the numbers :)
+
+Correction numbers:
+
+ - 32 bits: (4294967295 + 1) =                                4294967296
+ - 64 bits: (18446744073709551615 + 1)
+                                   - correction1 = 18446744069414584320
+
+ Before:        4294967200
+ Increase:                100
+ Should become: 4294967300
+ But really is:             4
+ Delta:        -4294967196
+ Correction1:  -4294967196 + 4294967296 = 100
+
+ Before:        18446744073709551000
+ Increase:                             800
+ Should become: 18446744073709551800
+ But really is:                        184
+ Delta:        -18446744073709550816
+ Correction1:  -18446744073709550816
+                               + 4294967296 = -18446744069414583520
+ Correction2:  -18446744069414583520
+                  + 18446744069414584320 = 800
+
+ Before:        18446744073709551615 ( maximum value )
+ Increase:      18446744069414584320 ( absurd increase, minimum for
+ Should become: 36893488143124135935             this example to work )
+ But really is: 18446744069414584319
+ Delta:                     -4294967296
+ Correction1:  -4294967296 + 4294967296 = 0
+ (not negative -> no correction2)
+
+ Before:        18446744073709551615 ( maximum value )
+ Increase:      18446744069414584319 ( one less increase )
+ Should become: 36893488143124135934
+ But really is: 18446744069414584318
+ Delta:                     -4294967297
+ Correction1:  -4294967297 + 4294967296 = -1
+ Correction2:  -1 + 18446744069414584320 = 18446744069414584319
+
+As you can see from the last two examples, you need strange numbers
+for RRDtool to fail (provided it's bug free of course), so this should
+not happen. However, SNMP or whatever method you choose to collect the
+data, might also report wrong numbers occasionally.  We can't prevent all
+errors, but there are some things we can do. The RRDtool "create" command
+takes two special parameters for this. They define
+the minimum and maximum allowed values. Until now, we used "U", meaning
+"unknown". If you provide values for one or both of them and if RRDtool
+receives data points that are outside these limits, it will ignore those
+values. For a thermometer in degrees Celsius, the absolute minimum is
+just under -273. For my router, I can assume this minimum is much higher
+so I would set it to 10, where as the maximum temperature I would
+set to 80. Any higher and the device would be out of order.
+
+For the speed of my car, I would never expect negative numbers and
+also I would not expect a speed  higher than 230. Anything else,
+and there must have been an error. Remember: the opposite is not true,
+if the numbers pass this check, it doesn't mean that they are
+correct. Always judge the graph with a healthy dose of suspicion if it
+seems weird to you.
+
+=head2 Data Resampling
+
+One important feature of RRDtool has not been explained yet: it is
+virtually impossible to collect data and feed it into RRDtool on exact
+intervals. RRDtool therefore interpolates the data, so they are stored
+on exact intervals. If you do not know what this means or how it
+works, then here's the help you seek:
+
+Suppose a counter increases by exactly one for every second. You want
+to measure it in 300 seconds intervals. You should retrieve values
+that are exactly 300 apart. However, due to various circumstances you
+are a few seconds late and the interval is 303. The delta will also be
+303 in that case. Obviously, RRDtool should not put 303 in the database
+and make you believe that the counter increased by 303 in 300 seconds.
+This is where RRDtool interpolates: it alters the 303 value as if it
+would have been stored earlier and it will be 300 in 300 seconds.
+Next time you are at exactly the right time. This means that the current
+interval is 297 seconds and also the counter increased by 297. Again,
+RRDtool interpolates and stores 300 as it should be.
+
+      in the RRD                 in reality
+
+ time+000:   0 delta="U"   time+000:    0 delta="U"
+ time+300: 300 delta=300   time+300:  300 delta=300
+ time+600: 600 delta=300   time+603:  603 delta=303
+ time+900: 900 delta=300   time+900:  900 delta=297
+
+Let's create two identical databases. I've chosen the time range 920805000
+to 920805900 as this goes very well with the example numbers.
+
+   rrdtool create seconds1.rrd   \
+      --start 920804700          \
+      DS:seconds:COUNTER:600:U:U \
+      RRA:AVERAGE:0.5:1:24
+
+Make a copy
+
+   for Unix: cp seconds1.rrd seconds2.rrd
+   for Dos:  copy seconds1.rrd seconds2.rrd
+   for vms:  how would I know :)
+
+Put in some data
+
+   rrdtool update seconds1.rrd \
+      920805000:000 920805300:300 920805600:600 920805900:900
+   rrdtool update seconds2.rrd \
+      920805000:000 920805300:300 920805603:603 920805900:900
+
+Create output
+
+   rrdtool graph seconds1.png                       \
+      --start 920804700 --end 920806200             \
+      --height 200                                  \
+      --upper-limit 1.05 --lower-limit 0.95 --rigid \
+      DEF:seconds=seconds1.rrd:seconds:AVERAGE      \
+      CDEF:unknown=seconds,UN                       \
+      LINE2:seconds#0000FF                          \
+      AREA:unknown#FF0000
+   rrdtool graph seconds2.png                       \
+      --start 920804700 --end 920806200             \
+      --height 200                                  \
+      --upper-limit 1.05 --lower-limit 0.95 --rigid \
+      DEF:seconds=seconds2.rrd:seconds:AVERAGE      \
+      CDEF:unknown=seconds,UN                       \
+      LINE2:seconds#0000FF                          \
+      AREA:unknown#FF0000
+
+View both images together (add them to your index.html file)
+and compare. Both graphs should show the same, despite the
+input being different.
+
+=head1 WRAPUP
+
+It's time now to wrap up this tutorial. We covered all the basics for
+you to be able to work with RRDtool and to read the additional
+documentation available. There is plenty more to discover about
+RRDtool and you will find more and more uses for this package. You can
+easily create graphs using just the examples provided and using only
+RRDtool. You can also use one of the front ends to RRDtool that are
+available.
+
+=head1 MAILINGLIST
+
+Remember to subscribe to the RRDtool mailing list. Even if you are not
+answering to mails that come by, it helps both you and the rest of the
+users. A lot of the stuff that I know about MRTG (and therefore about
+RRDtool) I've learned while just reading the list without posting to
+it. I did not need to ask the basic questions as they are answered in
+the FAQ (read it!) and in various mails by other users. With
+thousands of users all over the world, there will always be people who
+ask questions that you can answer because you read this and other
+documentation and they didn't.
+
+=head1 SEE ALSO
+
+The RRDtool manpages
+
+=head1 AUTHOR
+
+I hope you enjoyed the examples and their descriptions. If you do, help
+other people by pointing them to this document when they are asking
+basic questions. They will not only get their answers, but at the same
+time learn a whole lot more.
+
+Alex van den Bogaerdt
+E<lt>alex@vandenbogaerdt.nlE<gt>
+
diff --git a/program/doc/rrdupdate.pod b/program/doc/rrdupdate.pod
new file mode 100644 (file)
index 0000000..67908d0
--- /dev/null
@@ -0,0 +1,174 @@
+=head1 NAME
+
+rrdupdate - Store a new set of values into the RRD
+
+=head1 SYNOPSIS
+
+B<rrdtool> {B<update> | B<updatev>} I<filename>
+S<[B<--template>|B<-t> I<ds-name>[B<:>I<ds-name>]...]>
+S<[B<--daemon> I<address>]> [B<-->]
+S<B<N>|I<timestamp>B<:>I<value>[B<:>I<value>...]>
+S<I<at-timestamp>B<@>I<value>[B<:>I<value>...]>
+S<[I<timestamp>B<:>I<value>[B<:>I<value>...] ...]>
+
+=head1 DESCRIPTION
+
+The B<update> function feeds new data values into an B<RRD>. The data
+is time aligned (interpolated) according to the properties of the
+B<RRD> to which the data is written.
+
+=over 8
+
+=item B<updatev>
+
+This alternate version of B<update> takes the same arguments and
+performs the same function. The I<v> stands for I<verbose>, which
+describes the output returned. B<updatev> returns a list of any and all
+consolidated data points (CDPs) written to disk as a result of the
+invocation of update. The values are indexed by timestamp (time_t),
+RRA (consolidation function and PDPs per CDP), and data source (name).
+Note that depending on the arguments of the current and previous call to
+update, the list may have no entries or a large number of entries.
+
+Since B<updatev> requires direct disk access, the B<--daemon> option cannot be
+used with this command.
+
+=item I<filename>
+
+The name of the B<RRD> you want to update.
+
+=item B<--template>|B<-t> I<ds-name>[B<:>I<ds-name>]...
+
+By default, the B<update> function expects its data input in the order
+the data sources are defined in the RRD, excluding any COMPUTE data
+sources (i.e. if the third data source B<DST> is COMPUTE, the third
+input value will be mapped to the fourth data source in the B<RRD> and
+so on). This is not very error resistant, as you might be sending the
+wrong data into an RRD.
+
+The template switch allows you to specify which data sources you are
+going to update and in which order. If the data sources specified in
+the template are not available in the RRD file, the update process
+will abort with an error message.
+
+While it appears possible with the template switch to update data sources
+asynchronously, B<RRDtool> implicitly assigns non-COMPUTE data sources missing
+from the template the I<*UNKNOWN*> value.
+
+Do not specify a value for a COMPUTE B<DST> in the B<update>
+function. If this is done accidentally (and this can only be done
+using the template switch), B<RRDtool> will ignore the value specified
+for the COMPUTE B<DST>.
+
+=item B<--daemon> I<address>
+
+If given, B<RRDTool> will try to connect to the caching daemon L<rrdcached>
+at I<address> and will fail if the connection cannot be established. If the
+connection is successfully established the values will be sent to the daemon
+instead of accessing the files directly.
+
+For a list of accepted formats, see the B<-l> option in the L<rrdcached> manual.
+
+=item B<N>|I<timestamp>B<:>I<value>[B<:>I<value>...]
+
+The data used for updating the RRD was acquired at a certain
+time. This time can either be defined in seconds since 1970-01-01 or
+by using the letter 'N', in which case the update time is set to be
+the current time. Negative time values are subtracted from the current
+time. An AT_STYLE TIME SPECIFICATION (see the I<rrdfetch>
+documentation) may also be used by delimiting the end of the time
+specification with the '@' character instead of a ':'. Getting the
+timing right to the second is especially important when you are
+working with data-sources of type B<COUNTER>, B<DERIVE> or
+B<ABSOLUTE>.
+
+When using negative time values, options and data have to be separated
+by two dashes (B<-->), else the time value would be parsed as an option.
+See below for an example.
+
+When using negative time values, options and data have to be separated
+by two dashes (B<-->), else the time value would be parsed as an option.
+See below for an example.
+
+The remaining elements of the argument are DS updates. The order of
+this list is the same as the order the data sources were defined in
+the RRA. If there is no data for a certain data-source, the letter
+B<U> (e.g., N:0.1:U:1) can be specified.
+
+The format of the value acquired from the data source is dependent on
+the data source type chosen. Normally it will be numeric, but the data
+acquisition modules may impose their very own parsing of this
+parameter as long as the colon (B<:>) remains the data source value
+separator.
+
+=back
+
+=head1 ENVIRONMENT VARIABLES
+
+The following environment variables may be used to change the behavior of
+C<rrdtoolE<nbsp>update>:
+
+=over
+
+=item B<RRDCACHED_ADDRESS>
+
+If this environment variable is set it will have the same effect as specifying
+the C<--daemon> option on the command line. If both are present, the command
+line argument takes precedence.
+
+=back
+
+=head1 EXAMPLES
+
+=over
+
+=item *
+
+C<rrdtool update demo1.rrd N:3.44:3.15:U:23>
+
+Update the database file demo1.rrd with 3 known and one I<*UNKNOWN*>
+value. Use the current time as the update time.
+
+=item *
+
+C<rrdtool update demo2.rrd 887457267:U 887457521:22 887457903:2.7>
+
+Update the database file demo2.rrd which expects data from a single
+data-source, three times. First with an I<*UNKNOWN*> value then with two
+regular readings. The update interval seems to be around 300 seconds.
+
+=item *
+
+C<rrdtool update demo3.rrd -- -5:21 N:42>
+
+Update the database file demo3.rrd two times, using five seconds in the
+past and the current time as the update times.
+
+=item *
+
+C<rrdtool update --cache /var/lib/rrd/demo3.rrd N:42>
+
+Update the file C</var/lib/rrd/demo3.rrd> with a single data source, using the
+current time. If the caching daemon cannot be reached, do B<not> fall back to
+direct file access.
+
+=item *
+
+C<rrdtool update --daemon unix:/tmp/rrdd.sock demo4.rrd N:23>
+
+Use the UNIX domain socket C</tmp/rrdd.sock> to contact the caching daemon. If
+the caching daemon is not available, update the file C<demo4.rrd> directly.
+B<WARNING:> Since a relative path is specified, the following disturbing effect
+may occur: If the daemon is available, the file relative to the working
+directory B<of the daemon> is used. If the daemon is not available, the file
+relative to the current working directory of the invoking process is used.
+B<This may update two different files depending on whether the daemon could be
+reached or not.> Don't do relative paths, kids!
+
+=back
+
+=head1 AUTHORS
+
+Tobias Oetiker <tobi@oetiker.ch>,
+Florian Forster <octoE<nbsp>atE<nbsp>verplant.org>
+
diff --git a/program/doc/rrdxport.pod b/program/doc/rrdxport.pod
new file mode 100644 (file)
index 0000000..d7e6bb3
--- /dev/null
@@ -0,0 +1,168 @@
+=head1 NAME
+
+rrdxport - Export data in XML format based on data from one or several RRD
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<xport>
+S<[B<-s>|B<--start> I<seconds>]>
+S<[B<-e>|B<--end> I<seconds>]>
+S<[B<-m>|B<--maxrows> I<rows>]>
+S<[B<--step> I<value>]>
+S<[B<--daemon> I<address>]>
+S<[B<DEF:>I<vname>B<=>I<rrd>B<:>I<ds-name>B<:>I<CF>]>
+S<[B<CDEF:>I<vname>B<=>I<rpn-expression>]>
+S<[B<XPORT>B<:>I<vname>[B<:>I<legend>]]>
+
+=head1 DESCRIPTION
+
+The B<xport> function's main purpose is to write an XML formatted
+representation of the data stored in one or several B<RRD>s. It
+can also extract numerical reports.
+
+If no I<XPORT> statements are found, there will be no output.
+
+=over
+
+=item B<-s>|B<--start> I<seconds> (default end-1day)
+
+The time when the exported range should begin. Time in seconds since
+epoch (1970-01-01) is required. Negative numbers are relative to the
+current time. By default one day worth of data will be printed.
+See also AT-STYLE TIME SPECIFICATION section in the I<rrdfetch>
+documentation for a detailed explanation on how to specify time.
+
+=item B<-e>|B<--end> I<seconds> (default now)
+
+The time when the exported range should end. Time in seconds since epoch.
+See also AT-STYLE TIME SPECIFICATION section in the I<rrdfetch>
+documentation for a detailed explanation of ways to specify time.
+
+=item B<-m>|B<--maxrows> I<rows> (default 400 rows)
+
+This works like the B<-w>|B<--width> parameter of I<rrdgraph>.
+In fact it is exactly the same, but the parameter was renamed to
+describe its purpose in this module. See I<rrdgraph> documentation
+for details.
+
+=item B<--step> I<value> (default automatic)
+
+See L<rrdgraph> documentation.
+
+=item B<--daemon> I<address>
+
+Address of the L<rrdcached> daemon. If specified, a C<flush> command is sent
+to the server before reading the RRD files. This allows B<rrdtool> to return
+fresh data even if the daemon is configured to cache values for a long time.
+For a list of accepted formats, see the B<-l> option in the L<rrdcached> manual.
+
+  rrdtool xport --daemon unix:/var/run/rrdcached.sock ...
+
+=item B<--enumds>
+
+The generated xml should contain the data values in enumerated tags.
+
+ <v0>val</v0><v1>val</v1>
+
+=item B<DEF:>I<vname>B<=>I<rrd>B<:>I<ds-name>B<:>I<CF>
+
+See I<rrdgraph> documentation.
+
+=item B<CDEF:>I<vname>B<=>I<rpn-expression>
+
+See I<rrdgraph> documentation.
+
+=item B<XPORT:>I<vname>B<:>B<:>I<legend>
+
+At least one I<XPORT> statement should be present. The values
+referenced by I<vname> are printed. Optionally add a legend.
+
+=back
+
+=head1 Output format
+
+The output is enclosed in an B<xport> element and contains two
+blocks. The first block is enclosed by a B<meta> element and
+contains some meta data. The second block is enclosed by a
+B<data> element and contains the data rows.
+
+Let's assume that the I<xport> command looks like this:
+
+  rrdtool xport \
+          --start now-1h --end now \
+          DEF:xx=host-inout.lo.rrd:output:AVERAGE \
+         DEF:yy=host-inout.lo.rrd:input:AVERAGE \
+          CDEF:aa=xx,yy,+,8,* \
+          XPORT:xx:"out bytes" \
+          XPORT:aa:"in and out bits"
+
+The resulting meta data section is (the values will depend on the
+RRD characteristics):
+
+  <meta>
+    <start>1020611700</start>
+    <step>300</step>
+    <end>1020615600</end>
+    <rows>14</rows>
+    <columns>2</columns>
+    <legend>
+      <entry>out bytes</entry>
+      <entry>in and out bits</entry>
+    </legend>
+  </meta>
+
+The resulting data section is:
+
+  <data>
+    <row><t>1020611700</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020612000</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020612300</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020612600</t><v>3.4113333333e+00</v><v>5.4581333333e+01</v></row>
+    <row><t>1020612900</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020613200</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020613500</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020613800</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020614100</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020614400</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020614700</t><v>3.7333333333e+00</v><v>5.9733333333e+01</v></row>
+    <row><t>1020615000</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020615300</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020615600</t><v>NaN</v><v>NaN</v></row>
+  </data>
+
+
+=head1 EXAMPLE 1
+
+  rrdtool xport \
+          DEF:out=if1-inouts.rrd:outoctets:AVERAGE \
+          XPORT:out:"out bytes"
+
+=head1 EXAMPLE 2
+
+  rrdtool xport \
+          DEF:out1=if1-inouts.rrd:outoctets:AVERAGE \
+          DEF:out2=if2-inouts.rrd:outoctets:AVERAGE \
+         CDEF:sum=out1,out2,+ \
+          XPORT:out1:"if1 out bytes" \
+          XPORT:out2:"if2 out bytes" \
+         XPORT:sum:"output sum"
+
+=head1 ENVIRONMENT VARIABLES
+
+The following environment variables may be used to change the behavior of
+C<rrdtoolE<nbsp>xport>:
+
+=over 4
+
+=item B<RRDCACHED_ADDRESS>
+
+If this environment variable is set it will have the same effect as specifying
+the C<--daemon> option on the command line. If both are present, the command
+line argument takes precedence.
+
+=back
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
diff --git a/program/etc/rrdcached-default b/program/etc/rrdcached-default
new file mode 100644 (file)
index 0000000..d8d4d59
--- /dev/null
@@ -0,0 +1,14 @@
+
+RUN_RRDCACHED=0
+
+RRDCACHED_USER="rrdcached"
+
+OPTS="-w 300 -z 300"
+
+PIDFILE="/var/run/rrdcached/rrdcached.pid"
+
+SOCKFILE="/var/run/rrdcached/rrdcached.sock"
+
+SOCKPERMS=0660
+
+
diff --git a/program/etc/rrdcached-init b/program/etc/rrdcached-init
new file mode 100644 (file)
index 0000000..45b5cdf
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/sh
+#
+# chkconfig: 2345 19 81
+# description: rrdcached startup script
+#
+
+RRDCACHED=/usr/bin/rrdcached
+
+. /etc/rc.d/init.d/functions
+
+. /etc/default/rrdcached
+
+RETVAL=0
+
+case "$1" in
+   start)
+      if [ ${RUN_RRDCACHED} -eq 0 ];
+      then
+        echo "Please enable rrdcached in /etc/default/rrdcached"
+        exit 0
+      fi
+
+      echo -n "Starting rrdcached: "
+      [ -f $RRDCACHED ] || exit 1
+
+      daemon --user=$RRDCACHED_USER \
+        $RRDCACHED $OPTS -p $PIDFILE -l $SOCKFILE
+      RETVAL=$?
+      echo
+      [ $RETVAL -eq 0 ] && touch /var/lock/subsys/rrdcached
+      [ $RETVAL -eq 0 ] && chmod $SOCKPERMS "${SOCKFILE}"
+      ;;
+
+  stop)
+      echo -n "Shutting down rrdcached: "
+      killproc rrdcached
+      RETVAL=$?
+      echo
+      [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/rrdcached && rm -rf $PIDFILE
+      ;;
+
+  restart|reload)
+      $0 stop
+      $0 start
+      RETVAL=$?
+      ;;
+  status)
+      status rrdcached
+      RETVAL=$?
+      ;;
+  *)
+      echo "Usage: $0 {start|stop|restart|status}"
+      exit 1
+esac
+
+exit $RETVAL
+
diff --git a/program/examples/4charts.pl.in b/program/examples/4charts.pl.in
new file mode 100755 (executable)
index 0000000..a11b944
--- /dev/null
@@ -0,0 +1,124 @@
+#! @PERL@
+
+#makes things work when run without install
+use lib qw( @prefix@/lib/perl );
+
+use RRDs;
+
+my $start=time;
+my $rrd="randome.rrd";
+my $name = $0;
+$name =~ s/.*\///g;
+$name =~ s/\.pl.*//g;
+
+RRDs::create ($rrd, "--start",$start-1, "--step",300,
+             "DS:a:GAUGE:600:U:U",
+             "DS:b:GAUGE:600:U:U",
+             "RRA:AVERAGE:0.5:1:300",
+             "RRA:MIN:0.5:12:300",
+             "RRA:MAX:0.5:12:300",
+);
+
+my $ERROR = RRDs::error;
+die "$0: unable to create `$rrd': $ERROR\n" if $ERROR;
+
+# dropt some data into the rrd
+my $t;
+for ($t=$start; $t<$start+300*300; $t+=300){
+  RRDs::update $rrd, "$t:".(sin($t/3000)*50+50).":".(sin($t/2500)*50+50);
+  if ($ERROR = RRDs::error) {
+    die "$0: unable to update `$rrd': $ERROR\n";
+  }
+}
+
+my $c1="f57912a0";
+my $c2="2a79e9a0";
+my $w=300;
+my $h=140;
+
+RRDs::graph "$name-L.png",
+  "--title", "2 LINES", 
+  "--start", "now",
+  "--end", "start+15h",
+  "--lower-limit=0",
+  "--interlace", 
+  "--imgformat","PNG",
+  "--width=$w",
+  "--height=$h",
+  "DEF:a=$rrd:a:AVERAGE",
+  "DEF:b=$rrd:b:AVERAGE",
+  "LINE1:a#$c1:Value A",
+  "LINE3:b#$c2:Value B",
+;
+
+RRDs::graph "$name-A.png",
+  "--title", "LINE and AREA", 
+  "--start", "now",
+  "--end", "start+15h",
+  "--lower-limit=0",
+  "--interlace", 
+  "--imgformat","PNG",
+  "--width=$w",
+  "--height=$h",
+  "DEF:a=$rrd:a:AVERAGE",
+  "DEF:b=$rrd:b:AVERAGE",
+  "AREA:a#$c1:Value A",
+  "LINE2:b#$c2:Value B",
+;
+
+RRDs::graph "$name-S.png",
+  "--title", "STACKED AREAS", 
+  "--start", "now",
+  "--end", "start+15h",
+  "--lower-limit=0",
+  "--interlace", 
+  "--imgformat","PNG",
+  "--width=$w",
+  "--height=$h",
+  "DEF:a=$rrd:a:AVERAGE",
+  "DEF:b=$rrd:b:AVERAGE",
+  "AREA:a#$c1:Value A",
+  "STACK:b#$c2:Value B",
+;
+
+
+RRDs::graph "$name-M.png",
+  "--title", "RPN Magic", 
+  "--start", "now",
+  "--end", "start+15h",
+  "--lower-limit=0",
+  "--interlace", 
+  "--imgformat","PNG",
+  "--width=$w",
+  "--height=$h",
+  "DEF:a=$rrd:a:AVERAGE",
+  "DEF:b=$rrd:b:AVERAGE",
+  "CDEF:alpha=TIME,3600,%,1800,LT,a,UNKN,IF",
+  "CDEF:beta=TIME,3600,%,1800,GE,b,UNKN,IF",
+  "AREA:alpha#$c1:Value A",
+  "LINE1:a#$c1",
+  "AREA:beta#$c2:Value B",
+  "LINE1:b#$c2",
+;
+
+RRDs::graph "$name-sample.png",
+  "--title", "Sample", 
+  "--start", "now",
+  "--end", "start+15h",
+  "--lower-limit=0",
+  "--interlace", 
+  "--imgformat","PNG",
+  "--width=600",
+  "--height=50",
+  "DEF:a=$rrd:a:AVERAGE",
+  "DEF:b=$rrd:a:MAX",
+  "AREA:a#00ff00:Incoming",
+  "LINE1:b#ff0000:Max Incoming",
+;
+
+if ($ERROR = RRDs::error) {
+  die "ERROR: $ERROR\n";
+};
+
+print "This script has created $name.png in the current directory\n";
+print "This demonstrates the use of the TIME and % RPN operators\n";
diff --git a/program/examples/Makefile.am b/program/examples/Makefile.am
new file mode 100644 (file)
index 0000000..a91db8c
--- /dev/null
@@ -0,0 +1,18 @@
+## Process this file with automake to produce Makefile.in
+
+#AUTOMAKE_OPTIONS        =  foreign
+
+#ACLOCAL_M4 = $(top_srcdir)/config/aclocal.m4
+
+SUBDIRS = rrdcached
+
+EXTRA_DIST = cgi-demo.cgi.in
+
+examplesdir = $(pkgdatadir)/examples
+examples_SCRIPTS = cgi-demo.cgi piped-demo.pl shared-demo.pl \
+       stripes.pl bigtops.pl minmax.pl 4charts.pl perftest.pl
+
+cgi-demo.cgi: @srcdir@/cgi-demo.cgi.in $(top_builddir)/config.status
+       sed 's,@''exec_prefix@,$(exec_prefix),' @srcdir@/cgi-demo.cgi.in > $@
+       chmod a+x $@
+
diff --git a/program/examples/bigtops.pl.in b/program/examples/bigtops.pl.in
new file mode 100755 (executable)
index 0000000..997386c
--- /dev/null
@@ -0,0 +1,50 @@
+#! @PERL@
+# this is for after install
+use lib qw( @prefix@/lib/perl );
+
+use RRDs;
+my $start=time;
+my $rrd="randome.rrd";
+my $name = $0;
+$name =~ s/.*\///g;
+$name =~ s/\.pl.*//g;
+
+RRDs::create ($rrd, "--start",$start-1, "--step",300,
+             "DS:a:GAUGE:600:U:U",
+             "DS:b:GAUGE:600:U:U",
+             "RRA:AVERAGE:0.5:1:300");
+my $ERROR = RRDs::error;
+die "$0: unable to create `$rrd': $ERROR\n" if $ERROR;
+
+# dropt some data into the rrd
+my $t;
+for ($t=$start; $t<$start+300*300; $t+=300){
+  RRDs::update $rrd, "$t:".rand(100).":".(sin($t/800)*50+50);
+  if ($ERROR = RRDs::error) {
+    die "$0: unable to update `$rrd': $ERROR\n";
+  }
+}
+
+RRDs::graph "$name.png",
+  "--title", uc($name)." Demo", 
+  "--start", "$start + 1 h",
+  "--end", "start + 1000 min",
+  "--interlace", 
+  "--imgformat","PNG",
+  "--width=450",
+  "DEF:a=$rrd:a:AVERAGE",
+  "DEF:b=$rrd:b:AVERAGE",
+  "CDEF:line=TIME,2400,%,300,LT,a,UNKN,IF",
+  "AREA:b#00b6e4:beta",
+  "AREA:line#0022e9:alpha",
+  "LINE3:line#ff0000",
+
+;
+
+if ($ERROR = RRDs::error) {
+  die "ERROR: $ERROR\n";
+};
+
+
+print "This script has created $name.png in the current directory\n";
+print "This demonstrates the use of the TIME and % RPN operators\n";
diff --git a/program/examples/cgi-demo.cgi.in b/program/examples/cgi-demo.cgi.in
new file mode 100755 (executable)
index 0000000..c5c5a11
--- /dev/null
@@ -0,0 +1,40 @@
+#! @exec_prefix@/bin/rrdcgi 
+
+<HTML>
+<HEAD>
+<TITLE>RRDCGI Demo</TITLE>
+</HEAD>
+<BODY>
+Note: This Demo will only work if have previously run
+the <TT>shared-demo.pl</TT>.
+
+<H1>This is NOT traffic</H1>
+
+
+<P><RRD::GRAPH cgi-demo1.png 
+           --lower-limit 0
+           --start 'end-10h'
+           --title "Graph in Localtime <RRD::TIME::NOW %c>"
+            DEF:alpha=shared-demo.rrd:a:AVERAGE
+            DEF:beta=shared-demo.rrd:b:AVERAGE
+            AREA:alpha#0022e9:"Trees on Mars"
+            STACK:beta#00b871:"Elchs in Norway">
+</P>
+
+<P><RRD::SETENV TZ UTC>
+   <RRD::GRAPH cgi-demo2.png 
+           --lower-limit 0
+           --start 'end-10h'
+           --title "Graph in UTC"
+            DEF:alpha=shared-demo.rrd:a:AVERAGE
+            DEF:beta=shared-demo.rrd:b:AVERAGE
+            AREA:alpha#0022e9:"Trees on Mars"
+            STACK:beta#00b871:"Elchs in Norway">
+</P>
+
+</BODY>
+</HTML>
+
+
+
+
diff --git a/program/examples/minmax.pl.in b/program/examples/minmax.pl.in
new file mode 100755 (executable)
index 0000000..3fd0e65
--- /dev/null
@@ -0,0 +1,52 @@
+#! @PERL@
+
+use lib qw( @prefix@/lib/perl );
+
+use RRDs;
+my $start=time;
+my $rrd="randome.rrd";
+my $name = $0;
+$name =~ s/.*\///g;
+$name =~ s/\.pl.*//g;
+
+RRDs::create ($rrd, "--start",$start-1, "--step",300,
+             "DS:a:GAUGE:600:U:U",
+             "RRA:AVERAGE:0.5:1:300",
+             "RRA:MIN:0.5:12:300",
+             "RRA:MAX:0.5:12:300",
+);
+my $ERROR = RRDs::error;
+die "$0: unable to create `$rrd': $ERROR\n" if $ERROR;
+
+# dropt some data into the rrd
+my $t;
+for ($t=$start; $t<$start+300*300; $t+=300){
+  RRDs::update $rrd, "$t:".(sin($t/3000)*50+50);
+  if ($ERROR = RRDs::error) {
+    die "$0: unable to update `$rrd': $ERROR\n";
+  }
+}
+
+RRDs::graph "$name.png",
+  "--title", uc($name)." Demo", 
+  "--start", "now",
+  "--end", "start+1d",
+  "--lower-limit=0",
+  "--interlace", 
+  "--imgformat","PNG",
+  "--width=450",
+  "DEF:a=$rrd:a:AVERAGE",
+  "DEF:b=$rrd:a:MIN",
+  "DEF:c=$rrd:a:MAX",
+  "AREA:a#00b6e4:real",
+  "LINE1:b#0022e9:min",
+  "LINE1:c#00ee22:max",
+;
+
+if ($ERROR = RRDs::error) {
+  die "ERROR: $ERROR\n";
+};
+
+
+print "This script has created $name.png in the current directory\n";
+print "This demonstrates the use of MIN and MAX archives\n";
diff --git a/program/examples/perftest.pl.in b/program/examples/perftest.pl.in
new file mode 100755 (executable)
index 0000000..022a954
--- /dev/null
@@ -0,0 +1,214 @@
+#! @PERL@
+#
+# $Id:$
+#
+# Created By Tobi Oetiker <tobi@oetiker.ch>
+# Date 2006-10-27
+#
+#makes programm work AFTER install
+
+my $Chunk = shift @ARGV || 10000;
+
+use lib qw( ../bindings/perl-shared/blib/lib ../bindings/perl-shared/blib/arch @prefix@/lib/perl );
+
+print <<NOTE;
+
+RRDtool Performance Tester
+--------------------------
+Running on $RRDs::VERSION;
+
+RRDtool update performance is ultimately disk-bound. Since very little data
+does actually get written to disk in a single update, the performance
+is highly dependent on the cache situation of your machine.
+
+This test tries to cater for this. It works like this:
+
+1) Create $Chunk RRD files in a tree
+
+2) For $Chunk -> Update RRD file, Sync
+
+3) goto 1)
+
+The numbers at the start of the row, show which
+RRA is being updated. So if several RRAs are being updated,
+you should see a slowdown as data has to be read from disk.
+
+The growning number in the second column shows how many RRD have been
+updated ... If everything is in cache, the number will Jump to $Chunk almost
+immediately. Then the system will seem to hang as 'sync' runs, to make sure
+all data has been written to disk prior to the next perftest run. This may
+not be 100% real-life, so you may want to remove the sync just for fun
+(then it is even less real-life, but different)
+
+NOTE
+
+use strict;
+use Time::HiRes qw(time);
+use RRDs;
+use IO::File;
+use Time::HiRes qw( usleep );
+
+sub create($$){
+  my $file = shift;
+  my $time = shift;
+  my $start = time; #since we loaded HiRes
+  RRDs::create  ( $file.".rrd", "-b$time", qw(
+                       -s300                        
+                       DS:in:GAUGE:400:U:U
+                       DS:out:GAUGE:400:U:U
+                       RRA:AVERAGE:0.5:1:600
+                       RRA:AVERAGE:0.5:6:600
+                       RRA:MAX:0.5:6:600
+                       RRA:AVERAGE:0.5:24:600
+                       RRA:MAX:0.5:24:600
+                       RRA:AVERAGE:0.5:144:600
+                       RRA:MAX:0.5:144:600
+               ));
+   my $total = time - $start;
+   my $error =  RRDs::error;
+   die $error if $error;
+   return $total;
+}
+
+sub update($$){
+  my $file = shift;
+  my $time = shift;
+  my $in = rand(1000);
+  my $out = rand(1000);
+  my $start = time;
+  my $ret = RRDs::updatev($file.".rrd", $time.":$in:$out");
+  my $total = time - $start;
+  my $error =  RRDs::error;
+  die $error if $error;
+  return $total;
+}
+
+sub tune($){
+  my $file = shift;
+  my $start = time;
+  RRDs::tune ($file.".rrd", "-a","in:U","-a","out:U","-d","in:GAUGE","-d","out:GAUGE");
+  my $total = time - $start;
+  my $error =  RRDs::error;
+  die $error if $error;
+  return $total;
+}
+
+sub infofetch($){
+  my $file = shift;
+  my $start = time;
+  my $info = RRDs::info ($file.".rrd");
+  my $error =  RRDs::error;
+  die $error if $error;
+  my $lasttime =  $info->{last_update} - $info->{last_update} % $info->{step};           
+  my $fetch = RRDs::fetch ($file.".rrd",'AVERAGE','-s',$lasttime-1,'-e',$lasttime);
+  my $total = time - $start;
+  my $error =  RRDs::error;
+  die $error if $error;
+  return $total;
+}
+
+sub stddev ($$$){ #http://en.wikipedia.org/wiki/Standard_deviation
+  my $sum = shift;
+  my $squaresum = shift;
+  my $count = shift;
+  return sqrt( 1 / $count * ( $squaresum - $sum*$sum / $count ))
+}
+
+sub makerrds($$$$){
+    my $count = shift;
+    my $total = shift;
+    my $list = shift;
+    my $time = shift;
+    my @files;
+    my $now = int(time);
+    for (1..$count){
+        my $id = sprintf ("%07d",$total);
+        $id =~ s/^(.)(.)(.)(.)(.)//;
+        push @$list, "$1/$2/$3/$4/$5/$id";    
+        -d "$1" or mkdir "$1";
+        -d "$1/$2" or mkdir "$1/$2";
+        -d "$1/$2/$3" or mkdir "$1/$2/$3";
+        -d "$1/$2/$3/$4" or mkdir "$1/$2/$3/$4";
+        -d "$1/$2/$3/$4/$5" or mkdir "$1/$2/$3/$4/$5";
+       push @files, $list->[$total];
+        create $list->[$total++],$time-2;
+       if ($now < int(time)){
+         $now = int(time);
+         print STDERR "Creating RRDs: ", $count - $_," rrds to go. \r";
+        }
+    }
+    return $count;
+}
+sub main (){
+    mkdir "db-$$" or die $!;
+    chdir "db-$$";
+
+    my $step = $Chunk; # number of rrds to creat for every round
+    
+    my @path;
+    my $time=int(time);
+
+    my $tracksize = 0;
+    my $uppntr = 0;
+
+    
+    my %squaresum = ( cr => 0, up => 0 );
+    my %sum = ( cr => 0, up => 0 );
+    my %count =( cr => 0, up => 0 );
+
+    my $printtime = time;
+    my %step;
+    for (qw(1 6 24 144)){
+          $step{$_} = int($time / 300 / $_);
+    }
+    
+    for (0..2) {
+        # enhance the track
+        $time += 300;
+        $tracksize += makerrds $step,$tracksize,\@path,$time;            
+        # run benchmark
+    
+        for (0..50){
+           $time += 300;
+            my $count = 0;
+            my $sum = 0;
+            my $squaresum = 0;
+            my $prefix = "";
+            for (qw(1 6 24 144)){
+                if (int($time / 300 / $_) > $step{$_})  {
+                    $prefix .= "$_  ";
+                    $step{$_} = int($time / 300 / $_);
+                 }
+                 else {
+                    $prefix .= (" " x length("$_")) . "  ";
+                 }   
+            }
+            my $now = int(time);
+            for (my $i = 0; $i<$tracksize;$i ++){
+               my $ntime = int(time);
+               if ($now < $ntime or $i == $tracksize){
+                   printf STDERR "$prefix %7d \r",$i;
+                   $now = $ntime;
+               }
+               my $elapsed = update($path[$i],$time);                
+               $sum += $elapsed;
+               $squaresum += $elapsed**2;
+               $count++;
+            };
+            my $startsync = time;
+            print STDERR 's';
+            system "sync";
+            print STDERR "\h";
+            my $synctime = time-$startsync;     
+            $sum += $synctime;
+            $squaresum += $synctime**2;
+            my $ups = $count/$sum;
+            my $sdv = stddev($sum,$squaresum,$count);
+            printf STDERR "$prefix %7d %6.0f Up/s (%6.5f sdv)\n",$count,$ups,$sdv;
+        }
+       print STDERR "\n";
+    }
+}
+
+main;
diff --git a/program/examples/piped-demo.pl.in b/program/examples/piped-demo.pl.in
new file mode 100755 (executable)
index 0000000..cafb0f5
--- /dev/null
@@ -0,0 +1,148 @@
+#! @PERL@ 
+
+use lib qw( @prefix@/lib/perl );
+
+use RRDp;
+
+# this simpulates a standard mrtg-2.x setup ... we can use this to
+# compare performance ...
+
+$main::DEBUG=0;
+$STEP = 300;
+$RUNS = 12*24*30*6;
+$GRUNS = 20;
+$RRD = "piped-demo.rrd";
+$SVG = "piped-demo.svg";
+$PNG = "piped-demo.png";
+
+# some magic to find the correct rrdtol executable
+$prefix="@prefix@";
+
+if ( -x "@exec_prefix@/bin/rrdtool") {
+   RRDp::start "@exec_prefix@/bin/rrdtool";
+} elsif ( -x "../../../bin/rrdtool") {
+   RRDp::start "../../../bin/rrdtool";
+} else {
+   RRDp::start "../src/rrdtool";
+}
+
+print "* Creating RRD with properties equivalent to mrtg-2.x logfile\n\n";
+
+$START = time()-$RUNS*$STEP;
+
+RRDp::cmd "create $RRD -b $START -s $STEP 
+       DS:in:GAUGE:400:U:U
+       DS:out:GAUGE:400:U:U
+       RRA:AVERAGE:0.5:1:600
+       RRA:AVERAGE:0.5:6:600
+       RRA:MAX:0.5:6:600
+       RRA:AVERAGE:0.5:24:600
+       RRA:MAX:0.5:24:600
+       RRA:AVERAGE:0.5:144:600
+       RRA:MAX:0.5:144:600";
+
+$answer = RRDp::read;
+($user,$sys,$real) =  ($RRDp::user,$RRDp::sys,$RRDp::real);
+    
+print "* Filling RRD with $RUNS Values. One moment please ...\n";
+print "  If you are running over NFS this will take *MUCH* longer\n\n"; 
+
+for ($i=$START+1;
+     $i<$START+$STEP*$RUNS;
+     $i+=$STEP+int((rand()-0.5)*7)){
+
+  $line = "update $RRD $i:".int(rand(100000)).":".int(rand(100000));
+  RRDp::cmd $line;
+  $answer = RRDp::read;
+}
+
+($user1,$sys1,$real1) =  ($RRDp::user,$RRDp::sys,$RRDp::real);
+
+printf "-- performance analysis Update test\n".
+       "   usr/upd: %1.5fs sys/upd: %1.5fs real/upd: %1.5fs upd/sec: %1.0f\n",
+  ($user1-$user)/($RUNS), ($sys1-$sys)/($RUNS), 
+  ($real1-$real)/($RUNS), ($RUNS)/($real1-$real);
+print "\n";
+# creating some graphs
+
+print "* Creating $GRUNS SVG graphs: $SVG\n\n";
+$now = time;
+$localtime = scalar localtime(time);
+$localtime = s/:/\\:/g;
+for ($i=0;$i<$GRUNS;$i++) {
+RRDp::cmd "graph $SVG ", "--title 'Test GRAPH' ",
+       "--imgformat SVG --height 150 --vertical-label 'Dummy Units' ".
+       "--start now".(-$RUNS*$STEP),
+       "--color ARROW#bfbfbf",
+        "DEF:alpha=$RRD:in:AVERAGE",
+        "DEF:beta=$RRD:out:AVERAGE",
+        "CDEF:calc=alpha,beta,+,1.5,/",
+        "AREA:alpha#0022e9:Alpha",
+        "STACK:beta#00b871:Beta",
+        "STACK:calc#ff0091:Calc\\j",
+       "PRINT:alpha:AVERAGE:'Average Alpha\\: %1.2lf %S'",
+       "PRINT:alpha:MIN:'Min Alpha\\: %1.2lf %S'",
+       "PRINT:alpha:MAX:'Max Alpha\\: %1.2lf %S'",
+       "GPRINT:calc:AVERAGE:'Average calc\\: %1.2lf %S\\r'",
+       "GPRINT:calc:MIN:'Min calc\\: %1.2lf %S'",
+       "GPRINT:calc:MAX:'Max calc\\: %1.2lf %S'",
+        "VRULE:".($now-3600)."#008877:'60 Minutes ago'",
+        "COMMENT:'\\s'",
+        "COMMENT:'Graph created on\\: ".$localtime."\\c'";
+
+$answer = RRDp::read;
+}
+($user2,$sys2,$real2) =  ($RRDp::user,$RRDp::sys,$RRDp::real);
+
+print "ANSWER:\n$$answer";
+
+printf "\n-- average Time for one Graph\n".
+       "   usr/grf: %1.5fs sys/grf: %1.5fs real/grf: %1.5fs   graphs/sec: %1.2f\n",
+  ($user2-$user1)/$GRUNS, 
+  ($sys2-$sys1)/$GRUNS, 
+  ($real2-$real1)/$GRUNS, 
+  $GRUNS/($real2-$real1);
+
+print "\n\n* Creating $GRUNS PNG graphs: $PNG\n\n";
+
+$now = time;
+($user1,$sys1,$real1) =  ($RRDp::user,$RRDp::sys,$RRDp::real);
+my $local = "".localtime(time());
+$local =~ s/:/\\:/g;
+
+for ($i=0;$i<$GRUNS;$i++) {
+RRDp::cmd "graph $PNG ", "--title 'Test GRAPH' ",
+       "--imgformat PNG --height 150 --vertical-label 'Dummy Units' ".
+       "--start now".(-$RUNS*$STEP),
+       "--color ARROW#bfbfbf",
+        "DEF:alpha=$RRD:in:AVERAGE",
+        "DEF:beta=$RRD:out:AVERAGE",
+        "CDEF:calc=alpha,beta,+,1.5,/",
+        "AREA:alpha#0022e9:Alpha",
+        "STACK:beta#00b871:Beta",
+        "STACK:calc#ff0091:Calc\\j",
+       "PRINT:alpha:AVERAGE:'Average Alpha\\: %1.2lf %S'",
+       "PRINT:alpha:MIN:'Min Alpha\\: %1.2lf %S'",
+       "PRINT:alpha:MAX:'Max Alpha\\: %1.2lf %S'",
+       "GPRINT:calc:AVERAGE:'Average calc\\: %1.2lf %S\\r'",
+       "GPRINT:calc:MIN:'Min calc\\: %1.2lf %S'",
+       "GPRINT:calc:MAX:'Max calc\\: %1.2lf %S'",
+        "VRULE:".($now-3600)."#008877:'60 Minutes ago'",
+        "COMMENT:'\\s'",
+        "COMMENT:'Graph created on\\: $local\\c'";
+
+$answer = RRDp::read;
+}
+($user2,$sys2,$real2) =  ($RRDp::user,$RRDp::sys,$RRDp::real);
+
+print "ANSWER:\n$$answer";
+
+printf "\n-- average Time for one PNG Graph\n".
+       "   usr/grf: %1.5fs sys/grf: %1.5fs real/grf: %1.5fs".
+       "  graphs/sec: %1.2f\n\n",
+  ($user2-$user1)/$GRUNS, 
+  ($sys2-$sys1)/$GRUNS, 
+  ($real2-$real1)/$GRUNS, 
+  $GRUNS/($real2-$real1);
+
+RRDp::end;
diff --git a/program/examples/rrdcached/Makefile.am b/program/examples/rrdcached/Makefile.am
new file mode 100644 (file)
index 0000000..51b1037
--- /dev/null
@@ -0,0 +1,10 @@
+## Process this file with automake to produce Makefile.in
+
+#AUTOMAKE_OPTIONS        =  foreign
+
+#ACLOCAL_M4 = $(top_srcdir)/config/aclocal.m4
+
+examplesdir = $(pkgdatadir)/examples/rrdcached
+examples_SCRIPTS = RRDCached.pm rrdcached-size.pl
+
+EXTRA_DIST = RRDCached.pm rrdcached-size.pl
diff --git a/program/examples/rrdcached/RRDCached.pm b/program/examples/rrdcached/RRDCached.pm
new file mode 100644 (file)
index 0000000..d776e32
--- /dev/null
@@ -0,0 +1,90 @@
+
+use strict;
+use warnings;
+
+package RRDCached;
+
+=head1 RRDCached
+
+This module implements the B<RRDCached> client protocol for bulk updates.
+
+=head1 SYNOPSIS
+
+    my $cache = RRDCached->new('unix:/var/run/rrdcached.sock')
+        or die "Cannot connect to RRDCached";
+
+    $cache->update('file1.rrd', 'N:10:2:78');
+    $cache->update('file2.rrd', '1222973760:30:0:9', 'N:68:1:55');
+    ...
+
+    $cache->done();
+
+=cut
+
+use IO::Socket;
+
+#################################################################
+
+sub new {
+    my ($class, $daemon) = @_;
+    my $this = {};
+
+    $daemon ||= $ENV{RRDCACHED_ADDRESS};
+    defined $daemon or return undef;
+
+    my $sock_family = "INET";
+
+    if ($daemon =~ m{^unix: | ^/ }x)
+    {
+        $sock_family = "UNIX";
+        $daemon =~ s/^unix://;
+    }
+
+    my $sock = "IO::Socket::$sock_family"->new($daemon)
+        or die "Cannot connect to daemon";
+
+    $sock->printflush("BATCH\n");
+
+    my $go = $sock->getline;
+    warn "We didn't get go-ahead from rrdcached" unless $go =~ /^0/;
+
+    $sock->autoflush(0);
+
+    bless { sock => $sock,
+            daemon => $daemon,
+        }, $class;
+}
+
+sub update {
+    my $this = shift;
+    my $file = shift;
+    ## @updates = @_;
+
+    @_ or warn "No updates for $file!";
+
+    ## rrdcached doesn't handle N: timestamps
+    my $now = time();
+    s/^N(?=:)/$now/ for (@_);
+
+    $this->{sock}->print("update $file @_\n");
+}
+
+sub done {
+    my ($this) = @_;
+
+    my $sock = delete $this->{sock};
+
+    $sock->printflush(".\n");
+    my $errs = $sock->getline;
+
+    my ($num_err) = $errs =~ /^(\d+)/;
+    return unless $num_err;
+
+    $sock->getline for (1..$num_err);
+
+    $sock->close;
+}
+
+#################################################################
+
+1;
diff --git a/program/examples/rrdcached/rrdcached-size.pl b/program/examples/rrdcached/rrdcached-size.pl
new file mode 100644 (file)
index 0000000..df6aaf6
--- /dev/null
@@ -0,0 +1,153 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+rrdcached-size.pl - estimate the IO and memory requirements for rrdcached
+
+=head1 SYNOPSIS
+
+B<rrdcached-size.pl>
+[B<-rrds>E<nbsp>I<file_count>]
+[B<-step>E<nbsp>I<seconds>]
+[B<-update>E<nbsp>I<length>]
+[B<-file>E<nbsp>I<length>]
+[B<-io>E<nbsp>I<files/sec>]
+[B<-w>E<nbsp>I<seconds>]
+[B<-f>E<nbsp>I<seconds>]
+[B<-pagesize>E<nbsp>I<bytes>]
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-rrds> I<file_count>
+
+Specify the number of RRDs in the working set.
+
+=item B<-step> I<seconds>
+
+Specify the RRD step value for each file.
+
+=item B<-update> I<length>
+
+Average update string length.  For this calculation, the time value must
+be specified as a C<time_t>, not C<N>.  For example, this update string
+would lead to B<-update>E<nbsp>I<43> :
+
+  1226936851:0:0:101113914:0:0:0:25814373:0:0
+
+=item B<-file> I<length>
+
+Specify the average file name length.  For this calculation, use the full
+path of the file.
+
+=item B<-io> I<files/sec>
+
+Specify the number of RRD files that your system can write per second.
+
+=item B<-w> I<timer>
+
+Specifies the B<-w> timer used with rrdcached.  For more information, see
+the B<rrdcached> documentation.
+
+=item B<-f> I<timer>
+
+Specifies the B<-f> timer used with rrdcached.  For more information, see
+the B<rrdcached> documentation.
+
+=item B<-pagesize> I<bytes>
+
+Manually specify the system page size, in case it is not detected
+properly.
+
+=back
+
+=cut
+
+use strict;
+use warnings;
+
+my $filename_len = 60;
+my $update_len = 128;
+my $rrds = 100;
+my $step = 300;
+my $rrd_per_sec = 200;
+my $rrdc_write = 300;
+my $rrdc_flush = 3600;
+my $pagesize = `pagesize` || 4096;
+
+#################################################################
+
+use Getopt::Long;
+GetOptions('rrds=i' => \$rrds,
+           'step=i' => \$step,
+           'update=i' => \$update_len,
+           'file=i' => \$filename_len,
+           'io=i' => \$rrd_per_sec,
+           'w=i'    => \$rrdc_write,
+           'f=i'    => \$rrdc_flush,
+           'pagesize=i' => \$pagesize,
+           'h' => \&usage,
+           )
+    or die "Options failure";
+
+@ARGV and die "Extra args: @ARGV\n";
+
+#################################################################
+
+my $MEG = 1024*1024;
+
+my $write_time = int($rrds / $rrd_per_sec);
+my $write_busy = int(100 * $write_time / $rrdc_write);
+my $buffered_pdp = $rrdc_write / $step;
+
+my $max_ram
+    = $rrds
+    * ($filename_len
+           + ( $rrdc_write / $step ) * $update_len)
+    / $MEG;
+
+my $journal_size
+    = $rrds
+    * (length("update") + $filename_len + $update_len + 3)
+    * ($rrdc_flush/$step)
+    * 2  # 2 logs
+    / $MEG;
+
+my $journal_rate = (($journal_size*$MEG/2))/$rrdc_flush;
+my $journal_page_rate = $journal_rate / $pagesize;
+
+$_ = sprintf("%.1f", $_)
+    for ($write_time,
+         $write_busy,
+         $buffered_pdp,
+         $max_ram,
+         $journal_size,
+         $journal_rate,
+         $journal_page_rate,
+     );
+
+print <<"EOF";
+RRD files     : $rrds files
+RRD step      : $step seconds
+Update length : $update_len bytes
+IO writes/sec : $rrd_per_sec rrd/sec
+write timer   : $rrdc_write seconds
+flush timer   : $rrdc_flush seconds
+-----------------------------------------------------------------
+
+Time to write all RRDs: $write_time sec ($write_busy\% busy)
+
+$buffered_pdp PDPs will be buffered per file
+
+RAM usage: $max_ram MB
+
+Journal size: $journal_size MB (total size for two journals)
+
+Journal write rate: $journal_page_rate page/sec ($journal_rate byte/sec)
+EOF
+
+sub usage {
+    system("perldoc $0");
+    exit(1);
+}
diff --git a/program/examples/shared-demo.pl.in b/program/examples/shared-demo.pl.in
new file mode 100755 (executable)
index 0000000..f01c1ae
--- /dev/null
@@ -0,0 +1,215 @@
+#! @PERL@ 
+
+
+END {
+  print "not ok 1\n" unless $loaded;
+  unlink "demo.rrd";
+}
+
+sub ok
+{
+    my($what, $result) = @_ ;
+    $ok_count++;
+    print "not " unless $result;
+    print "ok $ok_count $what\n";
+}
+
+#makes programm work AFTER install
+use lib qw( @prefix@/lib/perl );
+
+use strict;
+use vars qw(@ISA $loaded);
+
+use RRDs;
+$loaded = 1;
+my $ok_count = 1;
+
+ok("loading",1);
+
+######################### End of black magic.
+
+my $STEP  = 100;
+my $RUNS  = 500;
+my $GRUNS = 4;
+my $RRD1  = "shared-demo.rrd";
+my $RRD2  = "shared-demob.rrd";
+my $PNG1  = "shared-demo1.png";
+my $PNG2  = "shared-demo2.png";
+my $time  = 30*int(time/30);
+my $START = $time-$RUNS*$STEP;
+
+my @options = ("-b", $START, "-s", $STEP,
+ "DS:a:GAUGE:2000:U:U",
+ "DS:b:GAUGE:200:U:U",
+ "DS:c:GAUGE:200:U:U",
+ "DS:d:GAUGE:200:U:U",
+ "DS:e:DERIVE:200:U:U",
+ "RRA:AVERAGE:0.5:1:5000",
+ "RRA:AVERAGE:0.5:10:500");
+
+print "* Creating RRD $RRD1 starting at $time.\n\n";
+RRDs::create $RRD1, @options;
+
+my $ERROR = RRDs::error;
+ok("create A", !$ERROR);                                                               #  2
+if ($ERROR) {
+  die "$0: unable to create `$RRD1': $ERROR\n";
+}
+
+print "* Creating RRD $RRD2 starting at $time.\n\n";
+RRDs::create $RRD2, @options;
+
+$ERROR= RRDs::error;
+ok("create B",!$ERROR);                                                                #  3
+if ($ERROR) {
+  die "$0: unable to create `$RRD2': $ERROR\n";
+}
+
+my $last = RRDs::last $RRD1;
+if ($ERROR = RRDs::error) {
+  die "$0: unable to get last `$RRD1': $ERROR\n";
+}
+ok("last A", $last == $START);                                                 #  4
+
+$last = RRDs::last $RRD2;
+if ($ERROR = RRDs::error) {
+  die "$0: unable to get last `$RRD2': $ERROR\n";
+}
+ok("last B", $last == $START);                                                 #  5
+
+print "* Filling $RRD1 and $RRD2 with $RUNS*5 values. One moment please ...\n";
+print "* If you are running over NFS this will take *MUCH* longer\n\n";
+
+srand(int($time / 100));
+
+@options = ();
+
+my $counter = 1e7;
+for (my $t=$START+1;
+     $t<$START+$STEP*$RUNS;
+     $t+=$STEP+int((rand()-0.5)*7)){
+  $counter += int(2500*sin($t/2000)*$STEP);
+  my $data = (1000+500*sin($t/1000)).":".
+      (1000+900*sin($t/2330)).":".
+      (2000*cos($t/1550)).":".
+      (3220*sin($t/3420)).":$counter";
+  push(@options, "$t:$data");
+  RRDs::update $RRD1, "$t:$data";
+  if ($ERROR = RRDs::error) {
+    die "$0: unable to update `$RRD1': $ERROR\n";
+  }
+}
+
+RRDs::update $RRD2, @options;
+
+if ($ERROR = RRDs::error) {
+  die "$0: unable to update `$RRD2': $ERROR\n";
+}
+
+print "* Creating $GRUNS graphs: $PNG1 & $PNG2\n\n";
+my $now = $time;
+for (my $i=0;$i<$GRUNS;$i++) {
+  my @rrd_pngs = ($RRD1, $PNG1, $RRD2, $PNG2);
+  while (@rrd_pngs) {
+    my $RRD = shift(@rrd_pngs);
+    my $PNG = shift(@rrd_pngs);
+    my ($graphret,$xs,$ys) = RRDs::graph $PNG, "--title", 'Test GRAPH', 
+    '--base', '1024',
+          "--vertical-label", 'Dummy Units', "--start", (-$RUNS*$STEP),
+          "--end", $time,
+         "--interlace", "--imgformat","PNG",
+          "DEF:alpha=$RRD:a:AVERAGE",
+          "DEF:beta=$RRD:b:AVERAGE",
+          "DEF:gamma=$RRD:c:AVERAGE",
+          "DEF:delta=$RRD:d:AVERAGE",
+          "DEF:epsilon=$RRD:e:AVERAGE",
+          "CDEF:calc=alpha,beta,+,2,/,100,*,102,/",
+          "AREA:alpha#0022e9:Short",
+          "GPRINT:calc:MAX:Max calc %1.2lf",
+          "STACK:beta#00b871:Demo Text",
+          "GPRINT:calc:AVERAGE:Average calc %1.2lf",
+          "STACK:beta#0ad871:Demo Text 2",
+          "LINE1:gamma#ff0000:Line 1",
+          "LINE2:delta#888800:Line 2",
+          "LINE3:calc#00ff44:Line 3",
+          "LINE3:epsilon#000000:Line 4",
+          "HRULE:1500#ff8800:Horizontal Line at 1500",
+          "PRINT:alpha:AVERAGE:Average Alpha %1.2lf",
+          "PRINT:alpha:MIN:Min Alpha %1.2lf",
+          "PRINT:alpha:MAX:Max Alpha %1.2lf",
+          "GPRINT:calc:MIN:Min calc %1.2lf",
+          "VRULE:".($now-3600)."#008877:60 Minutes ago",
+          "VRULE:".($now-7200)."#008877:120 Minutes ago";
+
+    if ($ERROR = RRDs::error) {
+      die "ERROR: $ERROR\n";
+    } else {
+      print "Image Size: ${xs}x${ys}\n";
+      print "Graph Return:\n",(join "\n", @$graphret),"\n\n";
+    }
+  }
+}
+
+
+
+my ($start,$step,$names,$array) = RRDs::fetch $RRD1, "AVERAGE";
+$ERROR = RRDs::error;
+die "ERROR: $ERROR\n" if $ERROR ;
+print "start=$start, step=$step\n";
+print "                    "; 
+map {printf("%12s",$_)} @$names ;
+print "\n";
+foreach my $line (@$array){
+  print "".localtime($start),"   ";
+  $start += $step; 
+  foreach my $val (@$line) {           
+    printf "%12.1f", $val;
+  }
+  print "\n";
+}
+
+
+
+my ($start,$end,$step,$col_cnt,$legend,$data) = 
+  RRDs::xport ("-m", 400,
+              "--start", "now-1day",
+              "--end", "now",
+              "DEF:alpha=$RRD1:a:AVERAGE",
+              "DEF:beta=$RRD1:d:AVERAGE",
+              "CDEF:calc=alpha,beta,+,2,/,100,*,102,/",
+              "XPORT:alpha:original ds",
+              "XPORT:calc:calculated values",
+              );
+
+my $ERROR = RRDs::error;
+die "$0: unable to xport: $ERROR\n" if $ERROR;
+
+print "\nrrdxport test:\n\n";
+print "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n\n";
+print "<xport>\n";
+print "  <meta>\n";
+print "    <start>$start</start>\n";
+print "    <step>$step</step>\n";
+print "    <end>$end</end>\n";
+print "    <rows>", $#$data + 1, "</rows>\n";
+print "    <columns>$col_cnt</columns>\n";
+print "    <legend>\n";
+foreach my $entry (@$legend) {
+    print "      <entry>$entry</entry>\n";
+}
+print "    </legend>\n";
+print "  </meta>\n";
+print "  <data>\n";
+my $row_counter = 0;
+foreach my $row (@$data) {
+    $row_counter++;
+    print "    <row id=\"$row_counter\"><t is=\"", scalar localtime($start), "\">$start</t>";
+    $start += $step;
+    foreach my $val (@$row) {
+       printf ("<v>%1.10e</v>",$val) if $val ne '';
+       print "<v>NaN</v>" if  $val eq '';
+    }
+    print "</row>\n";
+}
+print "  </data>\n";
+print "</xport>\n";
diff --git a/program/examples/stripes.pl.in b/program/examples/stripes.pl.in
new file mode 100755 (executable)
index 0000000..362f499
--- /dev/null
@@ -0,0 +1,46 @@
+#! @PERL@
+use lib qw( @prefix@/lib/perl );
+
+use strict;
+use vars qw(@ISA $loaded);
+
+use RRDs;
+my $start=time;
+my $rrd="random.rrd";
+RRDs::create ($rrd, "--start",$start-1, "--step",300,
+             "DS:a:GAUGE:600:U:U",
+             "DS:b:GAUGE:600:U:U",
+             "RRA:AVERAGE:0.5:1:200");
+my $ERROR = RRDs::error;
+die "$0: unable to create `$rrd': $ERROR\n" if $ERROR;
+my $t;
+for ($t=$start; $t<$start+200*300; $t+=300){
+  RRDs::update $rrd, "$t:".rand(100).":".(sin($t/800)*50+50);
+  if ($ERROR = RRDs::error) {
+    die "$0: unable to update `$rrd': $ERROR\n";
+  }
+}
+RRDs::graph "stripes.png",
+  "--title", "Stripes Demo", 
+  "--start", $start,
+  "--end", "start + 400 min",
+  "--interlace", 
+  "--imgformat","PNG",
+  "--width=450",
+  "DEF:a=$rrd:a:AVERAGE",
+  "DEF:b=$rrd:b:AVERAGE",
+  "CDEF:alpha=TIME,1200,%,600,LT,a,UNKN,IF",
+  "CDEF:beta=TIME,1200,%,600,GE,b,UNKN,IF",
+  "AREA:alpha#0022e9:alpha",
+  "AREA:beta#00b674:beta",
+  "LINE1:b#ff4400:beta envelope\\c",
+  "COMMENT:\\s",
+  "COMMENT:alpha=TIME,1200,%,600,LT,a,UNKN,IF",
+  "COMMENT:beta=TIME,1200,%,600,GE,b,UNKN,IF\\j";
+if ($ERROR = RRDs::error) {
+  die "ERROR: $ERROR\n";
+};
+
+
+print "This script has created stripes.png in the current directory\n";
+print "This demonstrates the use of the TIME and % RPN operators\n";
diff --git a/program/favicon.ico b/program/favicon.ico
new file mode 100644 (file)
index 0000000..7d08dd4
Binary files /dev/null and b/program/favicon.ico differ
diff --git a/program/m4/acinclude.m4 b/program/m4/acinclude.m4
new file mode 100644 (file)
index 0000000..ee2fc1b
--- /dev/null
@@ -0,0 +1,601 @@
+dnl Helper Functions for the RRDtool configure.ac script
+dnl 
+dnl this file gets included into aclocal.m4 when runnning aclocal
+dnl
+dnl
+dnl
+dnl Check for the presence of a particular library and its header files
+dnl if this check fails set the environment variable EX_CHECK_ALL_ERR to YES
+dnl and prints out a helful message
+dnl
+dnl
+dnl EX_CHECK_ALL(library, function, header, pkgconf name, tested-version, homepage, cppflags)
+dnl              $1       $2        $3      $4            $5              $6        $7
+dnl
+dnl
+AC_DEFUN([EX_CHECK_ALL],
+[
+ AC_LANG_PUSH(C)
+ EX_CHECK_STATE=NO
+ ex_check_save_LIBS=${LIBS}
+ ex_check_save_CPPFLAGS=${CPPFLAGS}
+ ex_check_save_LDFLAGS=${LDFLAGS}
+ if test "x$7" != "x"; then
+   CPPFLAGS="$CPPFLAGS -I$7"
+ fi
+ dnl try compiling naked first
+ AC_CHECK_LIB($1,$2, [
+    AC_CHECK_HEADER($3,[LIBS="-l$1 ${LIBS}";EX_CHECK_STATE=YES],[])],[])
+ if test $EX_CHECK_STATE = NO; then
+    dnl now asking pkg-config for help
+    AC_CHECK_PROGS(PKGCONFIG,[pkg-config],no)
+    if test "$PKGCONFIG" != "no"; then
+          if $PKGCONFIG --exists $4; then
+             CPPFLAGS=${CPPFLAGS}" "`$PKGCONFIG --cflags $4`
+             LDFLAGS=${LDFLAGS}" "`$PKGCONFIG --libs-only-L $4`
+             LDFLAGS=${LDFLAGS}" "`$PKGCONFIG --libs-only-other $4`
+             LIBS=${LIBS}" "`$PKGCONFIG --libs-only-l $4`
+            dnl remove the cached value and test again
+            unset ac_cv_lib_`echo $1 | sed ['s/[^_a-zA-Z0-9]/_/g;s/^[0-9]/_/']`_$2
+             AC_CHECK_LIB($1,$2,[
+                unset ac_cv_header_`echo $3 | sed ['s/[^_a-zA-Z0-9]/_/g;s/^[0-9]/_/']`
+                AC_CHECK_HEADER($3,[EX_CHECK_STATE=YES],[])
+            ],[])
+          else
+             AC_MSG_WARN([
+----------------------------------------------------------------------------
+* I found a copy of pkgconfig, but there is no $4.pc file around.
+  You may want to set the PKG_CONFIG_PATH variable to point to its
+  location.
+----------------------------------------------------------------------------
+                       ])
+           fi
+     fi
+  fi  
+
+  if test ${EX_CHECK_STATE} = NO; then
+     AC_MSG_WARN([
+----------------------------------------------------------------------------
+* I could not find a working copy of $4. Check config.log for hints on why
+  this is the case. Maybe you need to set LDFLAGS and CPPFLAGS appropriately
+  so that compiler and the linker can find lib$1 and its header files. If
+  you have not installed $4, you can get it either from its original home on
+
+     $6
+
+  You can find also find an archive copy on
+
+     http://oss.oetiker.ch/rrdtool/pub/libs
+
+  The last tested version of $4 is $5.
+
+       LIBS=$LIBS
+   LDFLAGS=$LDFLAGS
+  CPPFLAGS=$CPPFLAGS
+
+----------------------------------------------------------------------------
+                ])
+       EX_CHECK_ALL_ERR=YES
+       LIBS="${ex_check_save_LIBS}"
+       CPPFLAGS="${ex_check_save_CPPFLAGS}"
+       LDFLAGS="${ex_check_save_LDFLAGS}"
+    fi
+    AC_LANG_POP(C)
+]
+)
+
+dnl
+dnl  Ptherad check from http://autoconf-archive.cryp.to/acx_pthread.m4
+dnl
+dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+dnl
+dnl This macro figures out how to build C programs using POSIX threads.
+dnl It sets the PTHREAD_LIBS output variable to the threads library and
+dnl linker flags, and the PTHREAD_CFLAGS output variable to any special
+dnl C compiler flags that are needed. (The user can also force certain
+dnl compiler flags/libs to be tested by setting these environment
+dnl variables.)
+dnl
+dnl Also sets PTHREAD_CC to any special C compiler that is needed for
+dnl multi-threaded programs (defaults to the value of CC otherwise).
+dnl (This is necessary on AIX to use the special cc_r compiler alias.)
+dnl
+dnl NOTE: You are assumed to not only compile your program with these
+dnl flags, but also link it with them as well. e.g. you should link
+dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS
+dnl $LIBS
+dnl
+dnl If you are only building threads programs, you may wish to use
+dnl these variables in your default LIBS, CFLAGS, and CC:
+dnl
+dnl        LIBS="$PTHREAD_LIBS $LIBS"
+dnl        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+dnl        CC="$PTHREAD_CC"
+dnl
+dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute
+dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to
+dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+dnl
+dnl ACTION-IF-FOUND is a list of shell commands to run if a threads
+dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to
+dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the
+dnl default action will define HAVE_PTHREAD.
+dnl
+dnl Please let the authors know if this macro fails on any platform, or
+dnl if you have any other suggestions or comments. This macro was based
+dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with
+dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros
+dnl posted by Alejandro Forero Cuervo to the autoconf macro repository.
+dnl We are also grateful for the helpful feedback of numerous users.
+dnl
+dnl @category InstalledPackages
+dnl @author Steven G. Johnson <stevenj@alum.mit.edu>
+dnl @version 2005-01-14
+dnl @license GPLWithACException
+
+AC_DEFUN([ACX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_PUSH(C)
+acx_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+        AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
+        AC_MSG_RESULT($acx_pthread_ok)
+        if test x"$acx_pthread_ok" = xno; then
+                PTHREAD_LIBS=""
+                PTHREAD_CFLAGS=""
+        fi
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try.  Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important.  Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+#       other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+#      doesn't hurt to check since this sometimes defines pthreads too;
+#      also defines -D_REENTRANT)
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case "${host_cpu}-${host_os}" in
+        *solaris*)
+
+        # On Solaris (at least, for some versions), libc contains stubbed
+        # (non-functional) versions of the pthreads routines, so link-based
+        # tests will erroneously succeed.  (We need to link with -pthread or
+        # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
+        # a function called by this macro, so we could check for that, but
+        # who knows whether they'll stub that too in a future libc.)  So,
+        # we'll just look for -pthreads and -lpthread first:
+
+        acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags"
+        ;;
+esac
+
+if test x"$acx_pthread_ok" = xno; then
+for flag in $acx_pthread_flags; do
+
+        case $flag in
+                none)
+                AC_MSG_CHECKING([whether pthreads work without any flags])
+                ;;
+
+                -*)
+                AC_MSG_CHECKING([whether pthreads work with $flag])
+                PTHREAD_CFLAGS="$flag"
+                ;;
+
+               pthread-config)
+               AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no)
+               if test x"$acx_pthread_config" = xno; then continue; fi
+               PTHREAD_CFLAGS="`pthread-config --cflags`"
+               PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+               ;;
+
+                *)
+                AC_MSG_CHECKING([for the pthreads library -l$flag])
+                PTHREAD_LIBS="-l$flag"
+                ;;
+        esac
+
+        save_LIBS="$LIBS"
+        save_CFLAGS="$CFLAGS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Check for various functions.  We must include pthread.h,
+        # since some functions may be macros.  (On the Sequent, we
+        # need a special flag -Kthread to make this header compile.)
+        # We check for pthread_join because it is in -lpthread on IRIX
+        # while pthread_create is in libc.  We check for pthread_attr_init
+        # due to DEC craziness with -lpthreads.  We check for
+        # pthread_cleanup_push because it is one of the few pthread
+        # functions on Solaris that doesn't have a non-functional libc stub.
+        # We try pthread_create on general principles.
+        AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]], [[pthread_t th; pthread_join(th, 0);
+                     pthread_attr_init(0); pthread_cleanup_push(0, 0);
+                     pthread_create(0,0,0,0); pthread_cleanup_pop(0); ]])],[acx_pthread_ok=yes],[])
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        AC_MSG_RESULT($acx_pthread_ok)
+        if test "x$acx_pthread_ok" = xyes; then
+                break;
+        fi
+
+        PTHREAD_LIBS=""
+        PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$acx_pthread_ok" = xyes; then
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+       AC_MSG_CHECKING([for joinable pthread attribute])
+       attr_name=unknown
+       for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+           AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]], [[int attr=$attr;]])],[attr_name=$attr; break],[])
+       done
+        AC_MSG_RESULT($attr_name)
+        if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+            AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+                               [Define to necessary symbol if this constant
+                                uses a non-standard name on your system.])
+        fi
+
+        AC_MSG_CHECKING([if more special flags are required for pthreads])
+        x_rflag=no
+        case "${host_cpu}-${host_os}" in
+            *-aix* | *-freebsd* | *-darwin*) x_rflag="-D_THREAD_SAFE";;
+            *solaris* | *-osf* | *-hpux*) x_rflag="-D_REENTRANT";;
+            *-linux* | *-k*bsd*-gnu*)                
+            if test x"$PTHREAD_CFLAGS" = "x-pthread"; then
+                # For Linux/gcc "-pthread" implies "-lpthread". We need, however, to make this explicit
+                # in PTHREAD_LIBS such that a shared library to be built properly depends on libpthread.
+                PTHREAD_LIBS="-lpthread $PTHREAD_LIBS"
+            fi;;
+        esac
+        AC_MSG_RESULT(${x_rflag})
+        if test "x$x_rflag" != xno; then
+            PTHREAD_CFLAGS="$x_rflag $PTHREAD_CFLAGS"
+        fi
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        # More AIX lossage: must compile with cc_r
+        AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC})
+else
+        PTHREAD_CC="$CC"
+fi
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_CC)
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$acx_pthread_ok" = xyes; then
+        ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+        :
+else
+        acx_pthread_ok=no
+        $2
+fi
+AC_LANG_POP(C)
+])dnl ACX_PTHREAD
+
+
+dnl
+dnl determine how to get IEEE math working
+dnl AC_IEEE(MESSAGE, set rd_cv_ieee_[var] variable, INCLUDES,
+dnl   FUNCTION-BODY, [ACTION-IF-FOUND [,ACTION-IF-NOT-FOUND]])
+dnl
+
+dnl substitute them in all the files listed in AC_OUTPUT
+AC_SUBST(PERLFLAGS)
+
+AC_DEFUN([AC_IEEE], [
+AC_MSG_CHECKING([if IEEE math works $1])
+AC_CACHE_VAL([rd_cv_ieee_$2],
+[AC_RUN_IFELSE([AC_LANG_SOURCE([[$3
+
+#if HAVE_MATH_H
+#  include <math.h>
+#endif
+
+#if HAVE_FLOAT_H
+#  include <float.h>  
+#endif
+
+#if HAVE_IEEEFP_H
+#  include <ieeefp.h>
+#endif
+
+#if HAVE_FP_CLASS_H
+#  include <fp_class.h>
+#endif
+
+/* Solaris */
+#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASS))
+#  define HAVE_ISINF 1
+#  define isinf(a) (fpclass(a) == FP_NINF || fpclass(a) == FP_PINF)
+#endif
+
+/* solaris 10 it defines isnan such that only forte can compile it ... bad bad  */
+#if (defined(HAVE_ISNAN) && defined(isnan) && defined(HAVE_FPCLASS) && defined(FP_SNAN) && defined(FP_QNAN))
+#  undef isnan
+#  define isnan(a) (fpclass(a) == FP_SNAN || fpclass(a) == FP_QNAN)
+#endif
+
+/* Digital UNIX */
+#if (! defined(HAVE_ISINF) && defined(HAVE_FP_CLASS) && defined(HAVE_FP_CLASS_H) && defined(FP_NEG_INF) && defined( FP_POS_INF) )
+#  define HAVE_ISINF 1
+#  define isinf(a) (fp_class(a) == FP_NEG_INF || fp_class(a) == FP_POS_INF)
+#endif 
+
+/* AIX */
+#if (! defined(HAVE_ISINF) && defined(HAVE_CLASS))
+#  define HAVE_ISINF 1
+#  define isinf(a) (class(a) == FP_MINUS_INF || class(a) == FP_PLUS_INF)
+#endif
+
+#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASSIFY) && defined(FP_PLUS_INF) && defined(FP_MINUS_INF))
+#  define HAVE_ISINF 1
+#  define isinf(a) (fpclassify(a) == FP_MINUS_INF || fpclassify(a) == FP_PLUS_INF)
+#endif
+
+#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASSIFY) && defined(FP_INFINITE))
+#  define HAVE_ISINF 1
+#  define isinf(a) (fpclassify(a) == FP_INFINITE)
+#endif
+
+#if HAVE_MATH_H
+#include <math.h>
+#endif
+
+#include <stdio.h>
+int main(void){
+    double rrdnan,rrdinf,rrdc,rrdzero;
+    $4;
+    /* some math to see if we get a floating point exception */
+    rrdzero=sin(0.0); /* don't let the compiler optimize us away */
+    rrdnan=0.0/rrdzero; /* especially here */
+    rrdinf=1.0/rrdzero; /* and here. I want to know if it can do the magic */
+                 /* at run time without sig fpe */
+    rrdc = rrdinf + rrdnan;
+    rrdc = rrdinf / rrdnan;
+    if (! isnan(rrdnan)) {printf ("not isnan(NaN) ... "); return 1;}
+    if (rrdnan == rrdnan) {printf ("nan == nan ... "); return 1;}
+    if (! isinf(rrdinf)) {printf ("not isinf(oo) ... "); return 1;}
+    if (! isinf(-rrdinf)) {printf ("not isinf(-oo) ... "); return 1;}
+    if (! rrdinf > 0) {printf ("not inf > 0 ... "); return 1;}
+    if (! -rrdinf < 0) {printf ("not -inf < 0 ... "); return 1;}
+    return 0;
+ }]])],[rd_cv_ieee_$2=yes],[rd_cv_ieee_$2=no],[:])])
+dnl these we run regardles is cached or not
+if test x${rd_cv_ieee_$2} = "xyes"; then
+ AC_MSG_RESULT(yes)
+ $5
+else
+ AC_MSG_RESULT(no)
+ $6
+fi
+
+])
+
+AC_DEFUN([AC_FULL_IEEE],[
+AC_LANG_PUSH(C)
+_cflags=${CFLAGS}
+AC_IEEE([out of the box], works, , , ,
+  [CFLAGS="$_cflags -ieee"
+  AC_IEEE([with the -ieee switch], switch, , , ,
+    [CFLAGS="$_cflags -qfloat=nofold"
+    AC_IEEE([with the -qfloat=nofold switch], nofold, , , ,
+      [CFLAGS="$_cflags -w -qflttrap=enable:zerodivide"
+      AC_IEEE([with the -w -qflttrap=enable:zerodivide], flttrap, , , ,
+       [CFLAGS="$_cflags -mieee"
+       AC_IEEE([with the -mieee switch], mswitch, , , ,
+         [CFLAGS="$_cflags -q float=rndsngl"
+         AC_IEEE([with the -q float=rndsngl switch], qswitch, , , ,
+           [CFLAGS="$_cflags -OPT:IEEE_NaN_inf=ON"
+           AC_IEEE([with the -OPT:IEEE_NaN_inf=ON switch], ieeenaninfswitch, , , ,
+             [CFLAGS="$_cflags -OPT:IEEE_comparisons=ON"
+             AC_IEEE([with the -OPT:IEEE_comparisons=ON switch], ieeecmpswitch, , , ,
+               [CFLAGS=$_cflags
+               AC_IEEE([with fpsetmask(0)], mask,
+                 [#include <floatingpoint.h>], [fpsetmask(0)],
+                 [AC_DEFINE(MUST_DISABLE_FPMASK)
+                PERLFLAGS="CCFLAGS=-DMUST_DISABLE_FPMASK"],
+                 [AC_IEEE([with signal(SIGFPE,SIG_IGN)], sigfpe,
+                   [#include <signal.h>], [signal(SIGFPE,SIG_IGN)],
+                   [AC_DEFINE(MUST_DISABLE_SIGFPE)
+                   PERLFLAGS="CCFLAGS=-DMUST_DISABLE_SIGFPE"],         
+                   AC_MSG_ERROR([
+Your Compiler does not do propper IEEE math ... Please find out how to
+make IEEE math work with your compiler and let me know (tobi@oetiker.ch).
+Check config.log to see what went wrong ...
+]))])])])])])])])])])
+
+AC_LANG_POP(C)
+
+])
+
+
+dnl a macro to check for ability to create python extensions
+dnl  AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE])
+dnl function also defines PYTHON_INCLUDES
+AC_DEFUN([AM_CHECK_PYTHON_HEADERS],
+[AC_REQUIRE([AM_PATH_PYTHON])
+AC_MSG_CHECKING(for headers required to compile python extensions)
+dnl deduce PYTHON_INCLUDES
+py_prefix=`$PYTHON -c "import sys; print sys.prefix"`
+py_exec_prefix=`$PYTHON -c "import sys; print sys.exec_prefix"`
+PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}"
+if test "$py_prefix" != "$py_exec_prefix"; then
+  PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}"
+fi
+AC_SUBST(PYTHON_INCLUDES)
+dnl check if the headers exist:
+save_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES"
+AC_TRY_CPP([#include <Python.h>],dnl
+[AC_MSG_RESULT(found)
+$1],dnl
+[AC_MSG_RESULT(not found)
+$2])
+CPPFLAGS="$save_CPPFLAGS"
+])
+
+dnl a macro to add some color to the build process.
+dnl CONFIGURE_PART(MESSAGE)
+
+AC_DEFUN([CONFIGURE_PART],[
+case $TERM in
+       #   for the most important terminal types we directly know the sequences
+       xterm|xterm*|vt220|vt220*)
+               T_MD=`awk 'BEGIN { printf("%c%c%c%c", 27, 91, 49, 109); }' </dev/null 2>/dev/null`
+               T_ME=`awk 'BEGIN { printf("%c%c%c", 27, 91, 109); }' </dev/null 2>/dev/null`
+       ;;
+       vt100|vt100*|cygwin)
+               T_MD=`awk 'BEGIN { printf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0); }' </dev/null 2>/dev/null`
+               T_ME=`awk 'BEGIN { printf("%c%c%c%c%c", 27, 91, 109, 0, 0); }' </dev/null 2>/dev/null`
+       ;;
+       *)
+               T_MD=''
+               T_ME=''
+       ;;
+esac
+  AC_MSG_RESULT()
+  AC_MSG_RESULT([${T_MD}$1${T_ME}])
+])
+
+dnl check 
+
+AC_DEFUN([CHECK_FOR_WORKING_MS_ASYNC], [
+AC_MSG_CHECKING([if msync with MS_ASYNC updates the files mtime])
+AC_CACHE_VAL([rd_cv_ms_async],
+[AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <utime.h>
+#include <signal.h>
+void timeout (int i) { exit (1); }
+int main(void){
+        int fd;
+        struct stat stbuf;
+        char *addr;
+        int res;
+        char temp[] = "mmaptestXXXXXX";
+        struct utimbuf newtime;
+        time_t create_ts;
+        fd = mkstemp(temp);
+        if (fd == -1){
+            perror(temp);
+            return 1;
+        }
+        write(fd,"12345\n", 6);        
+        stat(temp, &stbuf);
+        create_ts = stbuf.st_mtime;
+        newtime.actime = 0;
+        newtime.modtime = 0;
+        utime(temp,&newtime);
+        addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+        if (addr == MAP_FAILED) {
+            perror("mmap");
+            goto bad_exit;
+        }
+        addr[0]='x';
+        res = msync(addr, 4, MS_ASYNC);
+        if (res == -1) {
+           perror("msync");
+           goto bad_exit;
+        }
+        res = close(fd);        
+        if (res == -1) {
+           perror("close");
+           goto bad_exit;
+        }
+        /* there were reports of sync hanging
+           so we better set an alarm */
+        signal(SIGALRM,&timeout);
+        alarm(5);
+        /* The ASYNC means that we schedule the msync and return immediately.
+           Since we want to see if the modification time is updated upon
+           msync(), we have to make sure that our asynchronous request
+           completes before we stat below. In a real application, the
+           request would be completed at a random time in the future
+           but for this test we do not want to wait an arbitrary amount of
+           time, so force a commit now.  */        
+        sync();
+        stat(temp, &stbuf);
+        if (create_ts > stbuf.st_mtime){
+           goto bad_exit;
+        }      
+        unlink(temp);  
+        return 0;
+     bad_exit:
+        unlink(temp);
+        return 1;
+}
+]])],[rd_cv_ms_async=ok],[rd_cv_ms_async=broken],[:])])
+
+
+if test "${rd_cv_ms_async}" = "ok"; then
+ AC_MSG_RESULT(yes)
+else
+ AC_DEFINE_UNQUOTED(HAVE_BROKEN_MS_ASYNC, 1 , [set to 1 if msync with MS_ASYNC fails to update mtime])
+ AC_MSG_RESULT(no)
+ AC_MSG_WARN([With mmap access, your platform fails to update the files])
+ AC_MSG_WARN([mtime. RRDtool will work around this problem by calling utime on each])
+ AC_MSG_WARN([file it opens for rw access.])
+ sleep 2
+fi
+
+])
+
diff --git a/program/netware/Makefile b/program/netware/Makefile
new file mode 100644 (file)
index 0000000..ec4e287
--- /dev/null
@@ -0,0 +1,555 @@
+# Gnu Makefile for NetWare target
+# for use with gcc/nlmconv or Metrowerks CodeWarrior compiler
+# use with: make -f Makefile [help|all|clean|dev|devclean|dist|distclean]
+#
+# $id: $
+#
+
+DESCR  = Round Robin Database Tool $(RRD_VERSION_STR)
+COPYR  = Copyright (c) 1997-2007 by Tobias Oetiker
+WWWURL = http://www.rrdtool.org/
+MTSAFE = YES
+#SCREEN        = $(DESCR)
+STACK  = 65535
+# Comment the line below if you dont want to load protected automatically.
+#LDRING        = 3
+
+# You can set the default font used in graphs.
+# If not set here RRD defaults to DejaVuSansMono-Roman.ttf
+#RRD_DEFAULT_FONT = "sys:/java/nwgfx/lib/x11/fonts/ttf/tt0003m_.ttf"
+#RRD_DEFAULT_FONT = "VeraMono.ttf"
+
+# Vertical label angle: 90.0 (default) or 270.0
+RRDGRAPH_YLEGEND_ANGLE = 90.0
+
+# Set to one if you want to have piecharts.
+WITH_PIECHART = 0
+
+# Set the extension used for rrdcgi.
+ifndef CGIEXT
+CGIEXT = nlm
+endif
+
+# Edit the path below to point to your Novell NDK.
+ifndef NDKBASE
+NDKBASE        = c:/novell
+endif
+
+# Base for the lib sources
+ifndef LIBBASE
+LIBBASE        = ../..
+endif
+# All library code is statically linked to avoid problems with other lib NLMs.
+# Edit the path below to point to your libpng sources or set environment var.
+ifndef LIBPNG
+LIBPNG = $(LIBBASE)/libpng-1.2.16
+endif
+# Edit the path below to point to your freetype sources or set environment var.
+ifndef LIBFT2
+LIBFT2 = $(LIBBASE)/freetype-2.3.4
+endif
+# Edit the path below to point to your libart sources or set environment var.
+ifndef LIBART
+LIBART = $(LIBBASE)/libart_lgpl-2.3.17
+endif
+# Edit the path below to point to your zlib sources or set environment var.
+ifndef ZLIBSDK
+ZLIBSDK        = $(LIBBASE)/zlib-1.2.3
+endif
+
+# Edit the path below to point to your distribution folder.
+ifndef DISTDIR
+DISTDIR        = rrdtool-$(RRD_VERSION_STR)-nw
+endif
+DISTARC = $(DISTDIR).zip
+
+# Edit the path below to point to your distribution folder.
+ifndef DEVLDIR
+DEVLDIR        = rrdtool-$(RRD_VERSION_STR)-sdk-nw
+endif
+DEVLARC = $(DEVLDIR).zip
+
+# whatever...
+# NO_NULL_REALLOC = 1
+
+# Edit the var below to point to your lib architecture.
+ifndef LIBARCH
+# LIBARCH = CLIB
+LIBARCH = LIBC
+endif
+
+# The following line defines your compiler.
+ifdef METROWERKS
+       CC = mwccnlm
+else
+       CC = gcc
+endif
+# RM   = rm -f
+CP     = cp -afv
+# if you want to mark the target as MTSAFE you will need a tool for
+# generating the xdc data for the linker; here's a minimal tool:
+# http://www.gknw.net/development/prgtools/mkxdc.zip
+MPKXDC = mkxdc
+# CodeWarrior is too stupid to set the internal name properly when
+# the extension is not a NLM and not a registered type. So we need
+# to fix that after linking (since CGI isnt a known type - argh!):
+# http://www.gknw.net/development/prgtools/fixnlmname.zip
+FIXNLMN        = fixnlmname #-q
+# Here you can find a native Win32 binary of the original awk:
+# http://www.gknw.net/development/prgtools/awk.zip
+AWK    = awk
+ZIP    = zip -qzr9
+MV     = mv -fv
+
+# must be equal to DEBUG or NDEBUG
+DB     = NDEBUG
+# DB   = DEBUG
+# Optimization: -O<n> or debugging: -g
+ifeq ($(DB),NDEBUG)
+       OPT     = -O2
+       OBJDIR  = release
+else
+       OPT     = -g
+       OBJDIR  = debug
+endif
+
+# Project root
+PROOT  = ..
+
+# Include the version info retrieved from source.
+-include $(OBJDIR)/version.inc
+
+# Global flags for all compilers
+CFLAGS = $(OPT) -D$(DB) -nostdinc -DNETWARE -DN_PLAT_NLM -D_POSIX_SOURCE
+CFLAGS += -DHAVE_CONFIG_H
+
+ifeq ($(CC),mwccnlm)
+LD     = mwldnlm
+LDFLAGS        = -nostdlib $^ $(PRELUDE) $(LDLIBS) -o $@ -commandfile
+AR     = $(LD)
+ARFLAGS        = -nostdlib -type library -o
+LIBEXT = lib
+#RANLIB        =
+CFLAGS += -gccinc -inline off -opt nointrinsics -proc 586
+CFLAGS += -relax_pointers
+#CFLAGS        += -w on,nounused,nounusedexpr # -ansi strict
+ifeq ($(LIBARCH),LIBC)
+       PRELUDE = $(SDK_LIBC)/imports/libcpre.o
+       CFLAGS += -align 4
+else
+       PRELUDE = "$(METROWERKS)/Novell Support/libraries/runtime/prelude.obj"
+       CFLAGS += -include "$(METROWERKS)/Novell Support/headers/nlm_prefix.h"
+       CFLAGS += -align 1
+endif
+else
+LD     = nlmconv
+LDFLAGS        = -T
+AR     = ar
+ARFLAGS        = -cq
+LIBEXT = a
+RANLIB = ranlib
+CFLAGS += -fno-builtin -fpcc-struct-return -fno-strict-aliasing
+CFLAGS += -Wall -Wno-unused # -pedantic
+ifeq ($(LIBARCH),LIBC)
+       PRELUDE = $(SDK_LIBC)/imports/libcpre.gcc.o
+else
+       PRELUDE = $(NDK_ROOT)/pre/prelude.o
+       CFLAGS += -include $(NDKBASE)/nlmconv/genlm.h
+endif
+endif
+
+ifeq ($(findstring linux,$(OSTYPE)),linux)
+#include $(NDKBASE)/nlmconv/ncpfs.inc
+DL     = '
+DS     = /
+else
+DS     = \\
+endif
+
+ifeq ($(MTSAFE),YES)
+       XDCOPT = -n
+endif
+ifeq ($(MTSAFE),NO)
+       XDCOPT = -u
+endif
+ifndef DESCR
+       DESCR = $(notdir $(@:.def=)) Command Extension
+endif
+DESCR += ($(LIBARCH)) - $(CC) build
+
+NDK_ROOT = $(NDKBASE)/ndk
+SDK_CLIB = $(NDK_ROOT)/nwsdk
+SDK_LIBC = $(NDK_ROOT)/libc
+
+INCLUDES += -I$(PROOT) -I$(PROOT)/src -I$(LIBPNG) -I$(LIBFT2)/include -I$(LIBART) -I$(ZLIBSDK)
+
+ifeq ($(LIBARCH),LIBC)
+       INCLUDES += -I$(SDK_LIBC)/include -I$(SDK_LIBC)/include/nks
+else
+       INCLUDES += -I$(SDK_CLIB)/include/nlm -I$(SDK_CLIB)/include
+endif
+
+CFLAGS += $(INCLUDES)
+
+vpath %.c $(PROOT)/src $(LIBPNG) $(LIBART)/libart_lgpl $(ZLIBSDK)
+
+RRDLIBOBJS     = \
+       $(OBJDIR)/rrd_afm.o \
+       $(OBJDIR)/rrd_afm_data.o \
+       $(OBJDIR)/rrd_create.o \
+       $(OBJDIR)/rrd_diff.o \
+       $(OBJDIR)/rrd_dump.o \
+       $(OBJDIR)/rrd_error.o \
+       $(OBJDIR)/rrd_fetch.o \
+       $(OBJDIR)/rrd_first.o \
+       $(OBJDIR)/rrd_format.o \
+       $(OBJDIR)/rrd_gfx.o \
+       $(OBJDIR)/rrd_graph.o \
+       $(OBJDIR)/rrd_graph_helper.o \
+       $(OBJDIR)/rrd_hw.o \
+       $(OBJDIR)/rrd_info.o \
+       $(OBJDIR)/rrd_last.o \
+       $(OBJDIR)/rrd_lastupdate.o \
+       $(OBJDIR)/rrd_nan_inf.o \
+       $(OBJDIR)/rrd_open.o \
+       $(OBJDIR)/rrd_resize.o \
+       $(OBJDIR)/rrd_restore.o \
+       $(OBJDIR)/rrd_rpncalc.o \
+       $(OBJDIR)/rrd_tune.o \
+       $(OBJDIR)/rrd_update.o \
+       $(OBJDIR)/rrd_version.o \
+       $(OBJDIR)/rrd_xport.o \
+       $(OBJDIR)/rrd_thread_safe.o \
+       $(EOLIST)
+
+XLIBOBJS       = \
+       $(OBJDIR)/rrd_getopt.o \
+       $(OBJDIR)/rrd_getopt1.o \
+       $(OBJDIR)/art_rgba_svp.o \
+       $(OBJDIR)/hash_32.o \
+       $(OBJDIR)/rrd_parsetime.o \
+       $(OBJDIR)/pngsize.o \
+       $(EOLIST)
+
+PNGLIBOBJS     = \
+       $(OBJDIR)/png.o \
+       $(OBJDIR)/pngerror.o \
+       $(OBJDIR)/pngget.o \
+       $(OBJDIR)/pngmem.o \
+       $(OBJDIR)/pngpread.o \
+       $(OBJDIR)/pngread.o \
+       $(OBJDIR)/pngrio.o \
+       $(OBJDIR)/pngrtran.o \
+       $(OBJDIR)/pngrutil.o \
+       $(OBJDIR)/pngset.o \
+       $(OBJDIR)/pngtrans.o \
+       $(OBJDIR)/pngwio.o \
+       $(OBJDIR)/pngwrite.o \
+       $(OBJDIR)/pngwtran.o \
+       $(OBJDIR)/pngwutil.o \
+       $(EOLIST)
+ifeq "$(wildcard $(LIBPNG)/pnggccrd.c)" "$(LIBPNG)/pnggccrd.c"
+PNGLIBOBJS     += \
+       $(OBJDIR)/pnggccrd.o \
+       $(OBJDIR)/pngvcrd.o \
+       $(EOLIST)
+endif
+
+ZLIBOBJS       = \
+       $(OBJDIR)/adler32.o \
+       $(OBJDIR)/compress.o \
+       $(OBJDIR)/crc32.o \
+       $(OBJDIR)/deflate.o \
+       $(OBJDIR)/inflate.o \
+       $(OBJDIR)/inffast.o \
+       $(OBJDIR)/inftrees.o \
+       $(OBJDIR)/trees.o \
+       $(OBJDIR)/zutil.o \
+       $(EOLIST)
+ifeq "$(wildcard $(ZLIBSDK)/infblock.c)" "$(ZLIBSDK)/infblock.c"
+ZLIBOBJS       += \
+       $(OBJDIR)/infblock.o \
+       $(OBJDIR)/infcodes.o \
+       $(OBJDIR)/infutil.o \
+       $(EOLIST)
+endif
+
+ARTLIBOBJS     = \
+       $(patsubst $(LIBART)/libart_lgpl/%.c,$(OBJDIR)/%.o,$(wildcard $(LIBART)/libart_lgpl/art_*.c))
+
+OBJS   := $(RRDLIBOBJS) $(XLIBOBJS) $(PNGLIBOBJS) $(ARTLIBOBJS) $(ZLIBOBJS)
+OBJCGI := $(OBJS) $(OBJDIR)/rrd_cgi.o
+OBJTOOL        := $(OBJS) $(OBJDIR)/rrd_tool.o
+
+LDLIBS += $(LIBFT2)/builds/netware/libc/libft2.$(LIBEXT)
+
+
+all: rrdtool rrdcgi
+
+rrdtool: $(OBJDIR) $(PROOT)/rrd_config.h $(OBJDIR)/rrdtool.nlm
+rrdcgi: $(OBJDIR) $(PROOT)/rrd_config.h $(OBJDIR)/rrdcgi.$(CGIEXT)
+librrd: $(OBJDIR) $(PROOT)/rrd_config.h $(OBJDIR)/librrd.$(LIBEXT)
+
+FORCE: ;
+
+dist: all $(DISTDIR) $(DISTDIR)/readme.txt
+       @-$(CP) $(OBJDIR)/rrdcgi.$(CGIEXT) $(DISTDIR)
+       @-$(CP) $(OBJDIR)/rrdtool.nlm $(DISTDIR)
+       @-$(CP) $(PROOT)/src/*.ttf $(DISTDIR)
+       @-$(CP) $(PROOT)/CHANGES $(DISTDIR)
+       @-$(CP) $(PROOT)/COPYING $(DISTDIR)
+       @-$(CP) $(PROOT)/COPYRIGHT $(DISTDIR)
+       @-$(CP) $(PROOT)/NEWS $(DISTDIR)
+       @-$(CP) $(PROOT)/README $(DISTDIR)
+       @echo Creating $(DISTARC)
+       @$(ZIP) $(DISTARC) $(DISTDIR)/* < $(DISTDIR)/readme.txt
+
+dev: librrd $(DEVLDIR) $(DEVLDIR)/readme.txt
+       @-mkdir $(DEVLDIR)$(DS)include
+       @-mkdir $(DEVLDIR)$(DS)lib
+       @-mkdir $(DEVLDIR)$(DS)src
+       @-$(CP) $(OBJDIR)/librrd.$(LIBEXT) $(DEVLDIR)/lib
+       @-$(CP) $(PROOT)/rrd_config.h $(DEVLDIR)/include
+       @-$(CP) $(PROOT)/src/rrd.h $(DEVLDIR)/include
+       @-$(CP) $(PROOT)/src/*.ttf $(DEVLDIR)/src
+       @-$(CP) $(PROOT)/CHANGES $(DEVLDIR)
+       @-$(CP) $(PROOT)/COPYING $(DEVLDIR)
+       @-$(CP) $(PROOT)/COPYRIGHT $(DEVLDIR)
+       @-$(CP) $(PROOT)/NEWS $(DEVLDIR)
+       @-$(CP) $(PROOT)/README $(DEVLDIR)
+       @echo Creating $(DEVLARC)
+       @$(ZIP) $(DEVLARC) $(DEVLDIR)/* < $(DEVLDIR)/readme.txt
+
+clean:
+       -$(RM) -r $(OBJDIR)
+       -$(RM) $(PROOT)/rrd_config.h
+
+distclean: clean
+       -$(RM) -r $(DISTDIR)
+       -$(RM) $(DISTARC)
+
+devclean: clean
+       -$(RM) -r $(DEVLDIR)
+       -$(RM) $(DEVLARC)
+
+$(OBJDIR):
+       @mkdir $@
+
+$(DISTDIR):
+       @mkdir $@
+
+$(DEVLDIR):
+       @mkdir $@
+
+$(OBJDIR)/version.inc: $(PROOT)/configure.ac $(OBJDIR) $(PROOT)/src/get_ver.awk
+       @echo Creating $@
+       @$(AWK) -f $(PROOT)/src/get_ver.awk $< > $@
+
+$(OBJDIR)/%.o: %.c
+       @echo Compiling $<
+       @$(CC) $(CFLAGS) -c $< -o $@
+
+$(OBJDIR)/rrdcgi.$(CGIEXT): $(OBJCGI) $(OBJDIR)/rrdcgi.def $(OBJDIR)/rrdcgi.xdc
+       @echo Linking $@
+       @-$(RM) $@
+       @$(LD) $(LDFLAGS) $(@:.$(CGIEXT)=.def)
+ifeq ($(LD),nlmconv)
+       @$(MV) $(notdir $@) $@
+endif
+ifneq ($(CGIEXT),nlm)
+ifeq ($(LD),mwldnlm)
+       @$(FIXNLMN) $@
+endif
+endif
+
+$(OBJDIR)/rrdtool.nlm: $(OBJTOOL) $(OBJDIR)/rrdtool.def $(OBJDIR)/rrdtool.xdc
+       @echo Linking $@
+       @-$(RM) $@
+       @$(LD) $(LDFLAGS) $(@:.nlm=.def)
+ifeq ($(LD),nlmconv)
+       @$(MV) $(notdir $@) $@
+endif
+
+$(OBJDIR)/librrd.$(LIBEXT): $(OBJS)
+       @echo Creating $@
+       @-$(RM) $@
+       @$(AR) $(ARFLAGS) $@ $^
+ifdef RANLIB
+       @$(RANLIB) $@
+endif
+
+$(OBJDIR)/%.xdc: Makefile
+       @echo Creating $@
+       @$(MPKXDC) $(XDCOPT) $@
+
+$(OBJDIR)/%.def: Makefile $(OBJDIR)/version.inc
+       @echo $(DL)# DEF Linker File for use with gcc and nlmconv$(DL) > $@
+       @echo $(DL)# or with Codewarrior command line compiler.$(DL) >> $@
+       @echo $(DL)# Do not edit this file - it is created by make!$(DL) >> $@
+       @echo $(DL)# All your changes will be lost!!$(DL) >> $@
+       @echo $(DL)#$(DL) >> $@
+       @echo $(DL)copyright "$(COPYR)"$(DL) >> $@
+       @echo $(DL)description "$(DESCR)"$(DL) >> $@
+       @echo $(DL)version $(RRD_VERSION)$(DL) >> $@
+ifdef NLMTYPE
+       @echo $(DL)type $(NLMTYPE)$(DL) >> $@
+else
+       @echo $(DL)type 0$(DL) >> $@
+endif
+ifdef STACK
+       @echo $(DL)stack $(STACK)$(DL) >> $@
+endif
+       @echo $(DL)threadname "$(notdir $(@:.def=))"$(DL) >> $@
+ifdef SCREEN
+       @echo $(DL)screenname "$(SCREEN)"$(DL) >> $@
+else
+       @echo $(DL)screenname "DEFAULT"$(DL) >> $@
+endif
+ifeq ($(DB),DEBUG)
+       @echo $(DL)debug$(DL) >> $@
+endif
+ifeq ($(LIBARCH),CLIB)
+       @echo $(DL)start _Prelude$(DL) >> $@
+       @echo $(DL)exit _Stop$(DL) >> $@
+       @echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/clib.imp$(DL) >> $@
+       @echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/threads.imp$(DL) >> $@
+       @echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/nlmlib.imp$(DL) >> $@
+       @echo $(DL)module clib$(DL) >> $@
+else
+       @echo $(DL)start _LibCPrelude$(DL) >> $@
+       @echo $(DL)exit _LibCPostlude$(DL) >> $@
+       @echo $(DL)check _LibCCheckUnload$(DL) >> $@
+       @echo $(DL)import @$(NDK_ROOT)/libc/imports/libc.imp$(DL) >> $@
+       @echo $(DL)import @$(NDK_ROOT)/libc/imports/netware.imp$(DL) >> $@
+       @echo $(DL)module libc$(DL) >> $@
+       @echo $(DL)pseudopreemption$(DL) >> $@
+       @echo $(DL)flag_on 64$(DL) >> $@
+endif
+ifeq ($(LDRING),0)
+       @echo $(DL)flag_on 16$(DL) >> $@
+endif
+ifeq ($(LDRING),3)
+       @echo $(DL)flag_on 512$(DL) >> $@
+endif
+ifdef XDCOPT
+       @echo $(DL)xdcdata $(@:.def=.xdc)$(DL) >> $@
+endif
+ifeq ($(LD),nlmconv)
+       @echo $(DL)input $(OBJS) $(LDLIBS)$(DL) >> $@
+       @echo $(DL)input $(PRELUDE)$(DL) >> $@
+       @echo $(DL)output $(notdir $(@:.def=.nlm))$(DL) >> $@
+endif
+
+$(PROOT)/rrd_config.h: FORCE Makefile $(OBJDIR)/version.inc
+       @echo Creating $@
+       @echo $(DL)/* $(notdir $@) for NetWare target.$(DL) > $@
+       @echo $(DL)** Do not edit this file - it is created by make!$(DL) >> $@
+       @echo $(DL)** All your changes will be lost!!$(DL) >> $@
+       @echo $(DL)*/$(DL) >> $@
+       @echo $(DL)#ifndef NETWARE$(DL) >> $@
+       @echo $(DL)#error This $(notdir $@) is created for NetWare platform!$(DL) >> $@
+       @echo $(DL)#endif$(DL) >> $@
+       @echo $(DL)#ifndef RRD_CONFIG_H$(DL) >> $@
+       @echo $(DL)#define RRD_CONFIG_H$(DL) >> $@
+       @echo $(DL)#define OS "i586-pc-NetWare"$(DL) >> $@
+       @echo $(DL)#define PACKAGE_VERSION "$(RRD_VERSION_STR)"$(DL) >> $@
+       @echo $(DL)#define PACKAGE_BUGREPORT "tobi@oetiker.ch"$(DL) >> $@
+       @echo $(DL)#define NUMVERS $(RRD_NUMVERS)$(DL) >> $@
+       @echo $(DL)#define HAVE_ASSERT_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_DLFCN_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_DLOPEN 1$(DL) >> $@
+       @echo $(DL)#define HAVE_ERR_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_ERRNO_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_FCNTL_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_FIONBIO 1$(DL) >> $@
+       @echo $(DL)#define HAVE_FLOAT_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_GETTIMEOFDAY 1$(DL) >> $@
+       @echo $(DL)#define HAVE_INTTYPES_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_LIMITS_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_LONGLONG 1$(DL) >> $@
+       @echo $(DL)#define HAVE_LOCALE_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_MALLOC_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_MATH_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_MBSTOWCS 1$(DL) >> $@
+       @echo $(DL)#define HAVE_MEMMOVE 1$(DL) >> $@
+       @echo $(DL)#define HAVE_MKTIME 1$(DL) >> $@
+       @echo $(DL)#define HAVE_SELECT 1$(DL) >> $@
+       @echo $(DL)#define HAVE_SETLOCALE 1$(DL) >> $@
+       @echo $(DL)#define HAVE_SETJMP_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_SNPRINTF 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STDARG_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STDDEF_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STDINT_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STDLIB_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STRCASECMP 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STRDUP 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STRFTIME 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STRING_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STRLCAT 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STRLCPY 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STRSTR 1$(DL) >> $@
+       @echo $(DL)#define HAVE_SYS_PARAM_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_SYS_SELECT_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_SYS_STAT_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_SYS_TIME_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_SYS_TYPES_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_TIME_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_TZSET 1$(DL) >> $@
+       @echo $(DL)#define HAVE_UNAME 1$(DL) >> $@
+       @echo $(DL)#define HAVE_VSNPRINTF 1$(DL) >> $@
+       @echo $(DL)#define STDC_HEADERS 1$(DL) >> $@
+       @echo $(DL)#define TIME_WITH_SYS_TIME 1$(DL) >> $@
+       @echo $(DL)#define HAVE_ZLIB_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_LIBZ 1$(DL) >> $@
+ifdef NO_NULL_REALLOC
+       @echo $(DL)#define NO_NULL_REALLOC 1$(DL) >> $@
+       @echo $(DL)#define rrd_realloc(a,b) ( (a) == NULL ? malloc( (b) ) : realloc( (a) , (b) ))$(DL) >> $@
+else
+       @echo $(DL)#define rrd_realloc(a,b) realloc((a), (b))$(DL) >> $@
+endif
+ifdef RRD_DEFAULT_FONT
+       @echo $(DL)#define RRD_DEFAULT_FONT $(RRD_DEFAULT_FONT)$(DL) >> $@
+endif
+       @echo $(DL)#define RRDGRAPH_YLEGEND_ANGLE $(RRDGRAPH_YLEGEND_ANGLE)$(DL) >> $@
+ifdef WITH_PIECHART
+       @echo $(DL)#define WITH_PIECHART $(WITH_PIECHART)$(DL) >> $@
+endif
+       @echo $(DL)#endif /* RRD_CONFIG_H */$(DL) >> $@
+
+$(DISTDIR)/readme.txt: Makefile
+       @echo Creating $@
+       @echo $(DL)This is a binary distribution for NetWare platform.$(DL) > $@
+       @echo $(DL)RRDTool version $(RRD_VERSION_STR)$(DL) >> $@
+       @echo $(DL)Please download the complete RRDTool package for$(DL) >> $@
+       @echo $(DL)any further documentation:$(DL) >> $@
+       @echo $(DL)$(WWWURL)$(DL) >> $@
+
+$(DEVLDIR)/readme.txt: Makefile
+       @echo Creating $@
+       @echo $(DL)This is a development distribution for NetWare platform.$(DL) > $@
+       @echo $(DL)RRDTool version $(RRD_VERSION_STR)$(DL) >> $@
+       @echo $(DL)Please download the complete RRDTool package for$(DL) >> $@
+       @echo $(DL)any further documentation:$(DL) >> $@
+       @echo $(DL)$(WWWURL)$(DL) >> $@
+
+help:
+       @echo $(DL)===========================================================$(DL)
+       @echo $(DL)Novell NDK Base = $(NDKBASE)$(DL)
+       @echo $(DL)libpng Source   = $(LIBPNG)$(DL)
+       @echo $(DL)libart Source   = $(LIBART)$(DL)
+       @echo $(DL)Freetype 2 SDK  = $(LIBFT2)$(DL)
+       @echo $(DL)Zlib SDK        = $(ZLIBSDK)$(DL)
+       @echo $(DL)===========================================================$(DL)
+       @echo $(DL)RRDTool $(RRD_VERSION_STR) - available targets are:$(DL)
+       @echo $(DL)$(MAKE) all$(DL)
+       @echo $(DL)$(MAKE) rrdtool$(DL)
+       @echo $(DL)$(MAKE) rrdcgi$(DL)
+       @echo $(DL)$(MAKE) librrd$(DL)
+       @echo $(DL)$(MAKE) clean$(DL)
+       @echo $(DL)$(MAKE) dev$(DL)
+       @echo $(DL)$(MAKE) devclean$(DL)
+       @echo $(DL)$(MAKE) dist$(DL)
+       @echo $(DL)$(MAKE) distclean$(DL)
+       @echo $(DL)===========================================================$(DL)
+
+
diff --git a/program/po/ChangeLog b/program/po/ChangeLog
new file mode 100644 (file)
index 0000000..f738a7b
--- /dev/null
@@ -0,0 +1,12 @@
+2009-05-26  gettextize  <bug-gnu-gettext@gnu.org>
+
+       * Makefile.in.in: New file, from gettext-0.17.
+       * Rules-quot: New file, from gettext-0.17.
+       * boldquot.sed: New file, from gettext-0.17.
+       * en@boldquot.header: New file, from gettext-0.17.
+       * en@quot.header: New file, from gettext-0.17.
+       * insert-header.sin: New file, from gettext-0.17.
+       * quot.sed: New file, from gettext-0.17.
+       * remove-potcdate.sin: New file, from gettext-0.17.
+       * POTFILES.in: New file.
+
diff --git a/program/po/LINGUAS b/program/po/LINGUAS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/program/po/Makevars b/program/po/Makevars
new file mode 100644 (file)
index 0000000..c450650
--- /dev/null
@@ -0,0 +1,41 @@
+# Makefile variables for PO directory in any package using GNU gettext.
+
+# Usually the message domain is the same as the package name.
+DOMAIN = $(PACKAGE)
+
+# These two variables depend on the location of this directory.
+subdir = po
+top_builddir = ..
+
+# These options get passed to xgettext.
+XGETTEXT_OPTIONS = --keyword=_ --keyword=N_
+
+# This is the copyright holder that gets inserted into the header of the
+# $(DOMAIN).pot file.  Set this to the copyright holder of the surrounding
+# package.  (Note that the msgstr strings, extracted from the package's
+# sources, belong to the copyright holder of the package.)  Translators are
+# expected to transfer the copyright for their translations to this person
+# or entity, or to disclaim their copyright.  The empty string stands for
+# the public domain; in this case the translators are expected to disclaim
+# their copyright.
+COPYRIGHT_HOLDER = Tobias Oetiker
+
+# This is the email address or URL to which the translators shall report
+# bugs in the untranslated strings:
+# - Strings which are not entire sentences, see the maintainer guidelines
+#   in the GNU gettext documentation, section 'Preparing Strings'.
+# - Strings which use unclear terms or require additional context to be
+#   understood.
+# - Strings which make invalid assumptions about notation of date, time or
+#   money.
+# - Pluralisation problems.
+# - Incorrect English spelling.
+# - Incorrect formatting.
+# It can be your email address, or a mailing list address where translators
+# can write to without being subscribed, or the URL of a web page through
+# which the translators can contact you.
+MSGID_BUGS_ADDRESS = http://oss.oetiker.ch/rrdtool-trac/newticket
+
+# This is the list of locale categories, beyond LC_MESSAGES, for which the
+# message catalogs shall be used.  It is usually empty.
+EXTRA_LOCALE_CATEGORIES =
diff --git a/program/po/POTFILES.in b/program/po/POTFILES.in
new file mode 100644 (file)
index 0000000..eeacfcb
--- /dev/null
@@ -0,0 +1,2 @@
+src/rrd_getopt.c
+src/rrd_tool.c
diff --git a/program/rrdtool-1.2-release b/program/rrdtool-1.2-release
new file mode 100755 (executable)
index 0000000..b364a3f
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+set -e
+VERSION=`perl -n -e 'm/\QAC_INIT([rrdtool],[\E(.+?)\Q])\E/ && print $1' configure.ac`
+PERLVERS=`perl -n -e 'm/NUMVERS=(\d+\.\d+)/ && print $1' configure.ac`
+set -x
+perl -i -p -e 's/^\$VERSION.+/\$VERSION='$PERLVERS';/' bindings/perl-*/*.pm
+perl -i -p -e 's/RRDtool 1\S+/RRDtool '$VERSION'/ && s/Copyright.+?Oetiker.+\d{4}/Copyright by Tobi Oetiker, 1997-2007/' src/*.h src/*.c
+perl -i -p -e 's/^Version:.+/Version: '$VERSION'/' rrdtool.spec
+perl -i -p -e 's/rrdtool-[\.\d]+\d(pre\d+)?(rc\d+)?/rrdtool-'$VERSION'/g' doc/rrdbuild.pod
+svn diff 
+echo "Tagging and releasing rrdtool $VERSION ($PERLVERS). Press any Key to continue."
+read somekey
+svn commit -m "prepare for the release of rrdtool-$VERSION"
+mkdir /tmp/rrdtool-$$
+OPWD=`pwd`
+cd /tmp/rrdtool-$$
+svn checkout svn://svn.oetiker.ch/rrdtool/branches/1.2/program .
+svn log --stop-on-copy --xml --verbose svn://svn.oetiker.ch/rrdtool/branches/1.2/program | \
+    xsltproc --stringparam strip-prefix branches/1.2/program $OPWD/svn2cl.xsl -  >CHANGES
+sh MakeMakefile
+PKG_CONFIG_PATH=/usr/pack/rrdtool-1.2svn-to/i686-debian-linux3.1/lib/pkgconfig/
+export PKG_CONFIG_PATH
+./configure
+make dist
+# do a test build
+tar zxvf rrdtool*.tar.gz
+cd rrdtool-$VERSION
+./configure
+make
+src/rrdtool
+cd ..
+scp CHANGES rrdtool*.tar.gz  oposs@james:public_html/rrdtool/pub/
+ssh oposs@james "cd public_html/rrdtool/pub/;rm rrdtool.tar.gz;ln -s rrdtool-$VERSION.tar.gz rrdtool.tar.gz"
+cd ..
+rm -rf rrdtool-$$
+svn copy -m "tagging version $VERSION" svn://svn.oetiker.ch/rrdtool/branches/1.2/program svn://svn.oetiker.ch/rrdtool/tags/$VERSION
+
diff --git a/program/rrdtool-1.4-release b/program/rrdtool-1.4-release
new file mode 100755 (executable)
index 0000000..caccc92
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/sh
+set -e
+VERSION=`perl -n -e 'm/\QAC_INIT([rrdtool],[\E(.+?)\Q])\E/ && print $1' configure.ac`
+PERLVERS=`perl -n -e 'm/NUMVERS=(\d+\.\d+)/ && print $1' configure.ac`
+set -x
+perl -i -p -e 's/^\$VERSION.+/\$VERSION='$PERLVERS';/' bindings/perl-*/*.pm
+perl -i -p -e 's/RRDtool 1\S+/RRDtool '$VERSION'/ && s/Copyright.+?Oetiker.+\d{4}/Copyright by Tobi Oetiker, 1997-2010/' src/*.h src/*.c
+perl -i -p -e 's/^Version:.+/Version: '$VERSION'/' rrdtool.spec
+perl -i -p -e 's/rrdtool-[\.\d]+\d(pre\d+)?(rc\d+)?/rrdtool-'$VERSION'/g' doc/rrdbuild.pod
+svn diff 
+echo "Tagging and releasing rrdtool $VERSION ($PERLVERS). Press any Key to continue."
+read somekey
+svn commit -m "prepare for the release of rrdtool-$VERSION"
+OPWD=`pwd`
+cd /tmp
+svn export svn://svn.oetiker.ch/rrdtool/trunk/program rrdtool-$$
+cd rrdtool-$$
+#svn log --stop-on-copy --xml --verbose svn://svn.oetiker.ch/rrdtool/trunk | \
+#    xsltproc --stringparam strip-prefix trunk/program $OPWD/svn2cl.xsl -  >CHANGES
+sh MakeMakefile
+#PKG_CONFIG_PATH=/usr/pack/rrdtool-1.3svn-to/i686-debian-linux3.1/lib/pkgconfig/
+#export PKG_CONFIG_PATH
+./configure --enable-maintainer-mode
+make dist
+# do a test build
+tar zxvf rrdtool*.tar.gz
+cd rrdtool-$VERSION
+./configure
+make
+src/rrdtool
+cd ..
+echo READY TO DIST ... press ENTER
+read x
+
+scp CHANGES rrdtool*.tar.gz  oposs@james:public_html/rrdtool/pub
+ssh oposs@james "cd public_html/rrdtool/pub/;rm rrdtool.tar.gz;ln -s rrdtool-$VERSION.tar.gz rrdtool.tar.gz"
+cd ..
+rm -rf rrdtool-$$
+svn copy -m "tagging version $VERSION" svn://svn.oetiker.ch/rrdtool/trunk/program svn://svn.oetiker.ch/rrdtool/tags/$VERSION
+
diff --git a/program/rrdtool.spec b/program/rrdtool.spec
new file mode 100644 (file)
index 0000000..37fddbb
--- /dev/null
@@ -0,0 +1,647 @@
+%define with_python %{?_without_python: 0} %{?!_without_python: 1}
+%define with_php %{?_without_php: 0} %{?!_without_php: 1}
+%define with_tcl %{?_without_tcl: 0} %{?!_without_tcl: 1}
+%define with_ruby %{?_without_ruby: 0} %{?!_without_ruby: 1}
+%define php_extdir %(php-config --extension-dir 2>/dev/null || echo %{_libdir}/php4)
+%define svnrev r1190
+#define pretag 1.2.99908020600
+%define rrdcached_user rrdcached
+
+Summary: Round Robin Database Tool to store and display time-series data
+Name: rrdtool
+Version: 1.4.3
+Release: 0.20%{?dist}
+License: GPLv2+ with exceptions
+Group: Applications/Databases
+URL: http://oss.oetiker.ch/rrdtool/
+#Source0: http://oss.oetiker.ch/%{name}/pub/%{name}-%{version}.tar.gz
+Source0: http://oss.oetiker.ch/rrdtool/pub/beta/%{name}-%{version}.tar.gz
+%if %{with_php}
+Source1: php4-%{svnrev}.tar.gz
+Patch1: rrdtool-1.3.0-beta4-fix-rrd_update-in-php-bindings.patch
+%endif
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+Requires: dejavu-lgc-fonts
+BuildRequires: gcc-c++, openssl-devel, freetype-devel
+BuildRequires: libpng-devel, zlib-devel, intltool >= 0.35.0
+BuildRequires: cairo-devel >= 1.2, pango-devel >= 1.14
+BuildRequires: libtool, groff
+BuildRequires: gettext, libxml2-devel
+%if 0%{?fedora} >= 7
+BuildRequires: perl-ExtUtils-MakeMaker perl-devel
+%endif
+
+%description
+RRD is the Acronym for Round Robin Database. RRD is a system to store and
+display time-series data (i.e. network bandwidth, machine-room temperature,
+server load average). It stores the data in a very compact way that will not
+expand over time, and it presents useful graphs by processing the data to
+enforce a certain data density. It can be used either via simple wrapper
+scripts (from shell or Perl) or via frontends that poll network devices and
+put a friendly user interface on it.
+
+%package devel
+Summary: RRDtool libraries and header files
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description devel
+RRD is the Acronym for Round Robin Database. RRD is a system to store and
+display time-series data (i.e. network bandwidth, machine-room temperature,
+server load average). This package allow you to use directly this library.
+
+%package doc
+Summary: RRDtool documentation
+Group: Documentation
+
+%description doc
+RRD is the Acronym for Round Robin Database. RRD is a system to store and
+display time-series data (i.e. network bandwidth, machine-room temperature,
+server load average). This package contains documentation on using RRD.
+
+%package perl
+Summary: Perl RRDtool bindings
+Group: Development/Languages
+Requires: %{name} = %{version}-%{release}
+Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))
+Obsoletes: perl-%{name} < %{version}-%{release}
+Provides: perl-%{name} = %{version}-%{release}
+
+%description perl
+The Perl RRDtool bindings
+
+%if %{with_python}
+%{!?python_sitearch: %define python_sitearch %(%{__python} -c 'from distutils import sysconfig; print sysconfig.get_python_lib(1)')}
+# eval to 2.3 if python isn't yet present, workaround for no python in fc4 minimal buildroot
+%{!?python_version: %define python_version %(%{__python} -c 'import sys; print sys.version.split(" ")[0]' || echo "2.3")}
+
+%package python
+Summary: Python RRDtool bindings
+Group: Development/Languages
+BuildRequires: python-devel >= 2.3
+Requires: python >= %{python_version}
+Requires: %{name} = %{version}-%{release}
+Obsoletes: python-%{name} < %{version}-%{release}
+Provides: python-%{name} = %{version}-%{release}
+
+%description python
+Python RRDtool bindings.
+%endif
+
+%ifarch ppc64
+# php bits busted on ppc64 at the moment
+%define with_php 0
+%endif
+
+%if %{with_php}
+%package php
+Summary: PHP RRDtool bindings
+Group: Development/Languages
+BuildRequires: php-devel >= 4.0
+Requires: php >= 4.0
+Requires: %{name} = %{version}-%{release}
+%if 0%{?php_zend_api}
+Requires: php(zend-abi) = %{php_zend_api}
+Requires: php(api) = %{php_core_api}
+%else
+Requires: php-api = %{php_apiver}
+%endif
+Obsoletes: php-%{name} < %{version}-%{release}
+Provides: php-%{name} = %{version}-%{release}
+Provides: php-pecl(rrdtool)
+
+%description php
+The %{name}-php package includes a dynamic shared object (DSO) that adds
+RRDtool bindings to the PHP HTML-embedded scripting language.
+%endif
+
+%if %{with_tcl}
+%package tcl
+Summary: Tcl RRDtool bindings
+Group: Development/Languages
+BuildRequires: tcl-devel >= 8.0
+Requires: tcl >= 8.0
+Requires: %{name} = %{version}-%{release}
+Obsoletes: tcl-%{name} < %{version}-%{release}
+Provides: tcl-%{name} = %{version}-%{release}
+
+%description tcl
+The %{name}-tcl package includes RRDtool bindings for Tcl.
+%endif
+
+%if %{with_ruby}
+%{!?ruby_sitearch: %define ruby_sitearch %(ruby -rrbconfig -e 'puts Config::CONFIG["sitearchdir"]')}
+
+%package ruby
+Summary: Ruby RRDtool bindings
+Group: Development/Languages
+BuildRequires: ruby, ruby-devel
+Requires: ruby(abi) = 1.8
+Requires: %{name} = %{version}-%{release}
+
+%description ruby
+The %{name}-ruby package includes RRDtool bindings for Ruby.
+%endif
+
+%package cached
+Summary: Data caching daemon for RRDtool
+Group: Applications/Databases
+Requires: %{name} = %{version}-%{release}
+
+%description cached
+rrdcached is a daemon that receives updates to existing RRD files,
+accumulates them and, if enough have been received or a defined time has
+passed, writes the updates to the RRD file.  The daemon was written with
+big setups in mind which usually runs into I/O related problems.  This
+daemon was written to alleviate these problems.
+
+%prep
+%if %{with_php}
+%setup -q -n %{name}-%{version} -a 1
+%patch1 -p1
+%else
+%setup -q -n %{name}-%{version}
+%endif
+
+# Fix to find correct python dir on lib64
+%{__perl} -pi -e 's|get_python_lib\(0,0,prefix|get_python_lib\(1,0,prefix|g' \
+    configure
+
+# Most edits shouldn't be necessary when using --libdir, but
+# w/o, some introduce hardcoded rpaths where they shouldn't
+%{__perl} -pi.orig -e 's|/lib\b|/%{_lib}|g' configure Makefile.in*
+%if %{with_php}
+%{__perl} -pi.orig -e 's|/lib\b|/%{_lib}|g' php4/configure php4/ltconfig*
+%endif
+
+# Perl 5.10 seems to not like long version strings, hack around it
+%{__perl} -pi.orig -e 's|1.299907080300|1.29990708|' \
+    bindings/perl-shared/RRDs.pm bindings/perl-piped/RRDp.pm
+
+#
+# fix config files for php4 bindings
+# workaround needed due to https://bugzilla.redhat.com/show_bug.cgi?id=211069
+%if %{with_php}
+cp -p /usr/lib/rpm/config.{guess,sub} php4/
+%endif
+
+
+%build
+%configure \
+    CFLAGS="-g -O0" \
+    --with-perl-options='INSTALLDIRS="vendor"' \
+%if %{with_tcl}
+    --enable-tcl-site \
+    --with-tcllib=%{_libdir} \
+%else
+    --disable-tcl \
+%endif
+%if %{with_python}
+    --enable-python \
+%else
+    --disable-python \
+%endif
+%if %{with_ruby}
+    --enable-ruby \
+%endif
+    --disable-static \
+    --with-pic
+
+# Fix another rpath issue
+%{__perl} -pi.orig -e 's|-Wl,--rpath -Wl,\$rp||g' \
+    bindings/perl-shared/Makefile.PL
+
+# Force RRDp bits where we want 'em, not sure yet why the
+# --with-perl-options and --libdir don't take
+pushd bindings/perl-piped/
+%{__perl} Makefile.PL INSTALLDIRS=vendor
+%{__perl} -pi.orig -e 's|/lib/perl|/%{_lib}/perl|g' Makefile
+popd
+
+#{__make} %{?_smp_mflags}
+make
+
+# Build the php module, the tmp install is required
+%if %{with_php}
+%define rrdtmp %{_tmppath}/%{name}-%{version}-tmpinstall
+%{__make} install DESTDIR="%{rrdtmp}"
+pushd php4/
+%configure \
+    --with-rrdtool="%{rrdtmp}%{_prefix}" \
+    --disable-static
+#{__make} %{?_smp_mflags}
+make
+popd
+%{__rm} -rf %{rrdtmp}
+%endif
+
+# Fix @perl@ and @PERL@
+find examples/ -type f \
+    -exec %{__perl} -pi -e 's|^#! \@perl\@|#!%{__perl}|gi' {} \;
+find examples/ -name "*.pl" \
+    -exec %{__perl} -pi -e 's|\015||gi' {} \;
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make DESTDIR="$RPM_BUILD_ROOT" install
+
+# Install the php module
+%if %{with_php}
+%{__install} -D -m0755 php4/modules/rrdtool.so \
+    %{buildroot}%{php_extdir}/rrdtool.so
+# Clean up the examples for inclusion as docs
+%{__rm} -rf php4/examples/.svn
+# Put the php config bit into place
+%{__mkdir_p} %{buildroot}%{_sysconfdir}/php.d
+%{__cat} << __EOF__ > %{buildroot}%{_sysconfdir}/php.d/rrdtool.ini
+; Enable rrdtool extension module
+extension=rrdtool.so
+__EOF__
+%endif
+
+# Pesky RRDp.pm...
+%{__mv} $RPM_BUILD_ROOT%{perl_vendorarch}/../RRDp.pm $RPM_BUILD_ROOT%{perl_vendorarch}/
+
+# Dunno why this is getting installed here...
+%{__rm} -f $RPM_BUILD_ROOT%{perl_vendorarch}/../leaktest.pl
+
+# We only want .txt and .html files for the main documentation
+%{__mkdir_p} doc2/html doc2/txt
+%{__cp} -a doc/*.txt doc2/txt/
+%{__cp} -a doc/*.html doc2/html/
+
+# Put perl docs in perl package
+%{__mkdir_p} doc3/html
+%{__mv} doc2/html/RRD*.html doc3/html/
+
+# Clean up the examples
+%{__rm} -f examples/Makefile* examples/*.in
+
+# This is so rpm doesn't pick up perl module dependencies automatically
+find examples/ -type f -exec chmod 0644 {} \;
+
+# Clean up the buildroot
+%{__rm} -rf $RPM_BUILD_ROOT%{_docdir}/%{name}-* \
+       $RPM_BUILD_ROOT%{perl_vendorarch}/ntmake.pl \
+       $RPM_BUILD_ROOT%{perl_archlib}/perllocal.pod \
+        $RPM_BUILD_ROOT%{_datadir}/%{name}/examples \
+        $RPM_BUILD_ROOT%{perl_vendorarch}/auto/*/{.packlist,*.bs}
+
+# Set up rrdcached
+%__install -d -m 0755 $RPM_BUILD_ROOT/%{_sysconfdir}/default
+%__install -d -m 0755 $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d
+%__install -m 0644 etc/rrdcached-default $RPM_BUILD_ROOT/%{_sysconfdir}/default/rrdcached
+%__install -m 0755 etc/rrdcached-init $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d/rrdcached
+%__install -d -m 0755 -o nobody -g nobody $RPM_BUILD_ROOT/%{_localstatedir}/run/rrdcached
+
+%clean
+%{__rm} -rf $RPM_BUILD_ROOT
+
+%pre cached
+/usr/sbin/groupadd %rrdcached_user &>/dev/null ||:
+/usr/sbin/useradd -s /sbin/nologin -g %rrdcached_user -c %rrdcached_user -d %{_localstatedir}/run/rrdcached  %rrdcached_user &>/dev/null || :
+
+%post -p /sbin/ldconfig
+
+%post cached
+/sbin/chkconfig --add rrdcached
+/sbin/service rrdcached start
+
+%preun cached
+/sbin/service rrdcached stop
+
+%postun -p /sbin/ldconfig
+
+%postun cached
+/sbin/chkconfig --del rrdcached
+test "$1" != 0 || /usr/sbin/userdel %rrdcached_user &>/dev/null || :
+#test "$1" != 0 || /usr/sbin/groupdel %rrdcached_user &>/dev/null || :
+
+%files
+%defattr(-,root,root,-)
+%{_bindir}/*
+%exclude %{_bindir}/rrdcached
+%{_libdir}/*.so.*
+%{_datadir}/%{name}
+%{_mandir}/man1/*
+%exclude %{_mandir}/man1/rrdcached*
+
+%files devel
+%defattr(-,root,root,-)
+%{_includedir}/*.h
+%exclude %{_libdir}/*.la
+%{_libdir}/*.so
+%{_libdir}/pkgconfig/librrd.pc
+
+%files doc
+%defattr(-,root,root,-)
+%doc CONTRIBUTORS COPYING COPYRIGHT README TODO NEWS THREADS
+%doc examples doc2/html doc2/txt
+
+%files perl
+%defattr(-,root,root,-)
+%doc doc3/html
+%{_mandir}/man3/*
+%{perl_vendorarch}/*.pm
+%attr(0755,root,root) %{perl_vendorarch}/auto/RRDs/
+
+%if %{with_python}
+%files python
+%defattr(-,root,root,-)
+%doc bindings/python/AUTHORS bindings/python/COPYING bindings/python/README
+%{python_sitearch}/*
+%endif
+
+%if %{with_php}
+%files php
+%defattr(-,root,root,0755)
+%doc php4/examples php4/README
+%config(noreplace) %{_sysconfdir}/php.d/rrdtool.ini
+%{php_extdir}/rrdtool.so
+%endif
+
+%if %{with_tcl}
+%files tcl
+%defattr(-,root,root,-)
+%doc bindings/tcl/README
+%{_libdir}/tclrrd*.so
+%{_libdir}/rrdtool/*.tcl
+%endif
+
+%if %{with_ruby}
+%files ruby
+%defattr(-,root,root,-)
+%doc bindings/ruby/README
+%{ruby_sitearch}/RRD.so
+%endif
+
+%files cached
+%{_bindir}/rrdcached
+%config %{_sysconfdir}/default/*
+%config %{_sysconfdir}/rc.d/init.d/*
+%{_mandir}/man1/rrdcached*
+%attr(0775 %rrdcached_user %rrdcached_user) %dir %{_localstatedir}/run/rrdcached
+
+%changelog
+* Wed Oct 08 2008 Bernard Li <bernard@vanhpc.org>
+- Split rrdcached related files to -cached subpackage
+- Create rrdcached user and make rrdcached related files owned by it
+
+* Tue Oct 07 2008 Bernard Li <bernard@vanhpc.org>
+- Include librrd.pc file in -devel package
+
+* Sun Jun 08 2008 Jarod Wilson <jwilson@redhat.com> 1.3-0.20.rc9
+- Update to rrdtool 1.3 rc9
+- Minor spec tweaks to permit building on older EL
+
+* Wed Jun 04 2008 Chris Ricker <kaboom@oobleck.net> 1.3-0.19.rc7
+- Update to rrdtool 1.3 rc7
+
+* Tue May 27 2008 Chris Ricker <kaboom@oobleck.net> 1.3-0.18.rc6
+- Update to rrdtool 1.3 rc6
+
+* Wed May 21 2008 Chris Ricker <kaboom@oobleck.net> 1.3-0.17.rc4
+- Bump version and rebuild
+
+* Wed May 21 2008 Chris Ricker <kaboom@oobleck.net> 1.3-0.16.rc4
+- Fix php bindings compile on x86_64
+
+* Mon May 19 2008 Chris Ricker <kaboom@oobleck.net> 1.3-0.15.rc4
+- Update to rrdtool 1.3 rc4
+
+* Tue May 13 2008 Jarod Wilson <jwilson@redhat.com> 1.3-0.15.rc1
+- Update to rrdtool 1.3 rc1
+- Fix versioning in changelog entries, had an extra 0 in there...
+- Drop cairo and python patches, they're in 1.3 rc1
+- Add Requires: gettext and libxml2-devel for new translations
+
+* Wed Apr 30 2008 Jarod Wilson <jwilson@redhat.com> 1.3-0.14.beta4
+- Drop some conditional flags, they're not working at the moment...
+
+* Wed Apr 30 2008 Jarod Wilson <jwilson@redhat.com> 1.3-0.13.beta4
+- Fix problem with cairo_save/cairo_restore (#444827)
+
+* Wed Apr 23 2008 Jarod Wilson <jwilson@redhat.com> 1.3-0.12.beta4
+- Fix python bindings rrdtool info implementation (#435468)
+
+* Tue Apr 08 2008 Jarod Wilson <jwilson@redhat.com> 1.3-0.11.beta4
+- Work around apparent version string length issue w/perl 5.10 (#441359)
+
+* Sat Apr 05 2008 Jarod Wilson <jwilson@redhat.com> 1.3-0.10.beta4
+- Fix use of rrd_update in php bindings (#437558)
+
+* Mon Mar  3 2008 Tom "spot" Callaway <tcallawa@redhat.com> 1.3-0.9.beta4
+- rebuild for new perl (again)
+
+* Wed Feb 13 2008 Jarod Wilson <jwilson@redhat.com> 1.3-0.8.beta4
+- Update to rrdtool 1.3 beta4
+
+* Tue Feb 05 2008 Tom "spot" Callaway <tcallawa@redhat.com> 1.3-0.7.beta3
+- rebuild for new perl (and fix license tag)
+
+* Mon Feb 04 2008 Jarod Wilson <jwilson@redhat.com> 1.3-0.6.beta3
+- Plug memory leak (#430879)
+
+* Mon Jan 07 2008 Jarod Wilson <jwilson@redhat.com> 1.3-0.5.beta3
+- Fix right-aligned text alignment and scaling (Resolves: #427609)
+
+* Wed Jan 02 2008 Jarod Wilson <jwilson@redhat.com> 1.3-0.4.beta3
+- Add newly built python egg to %%files
+
+* Wed Jan 02 2008 Jarod Wilson <jwilson@redhat.com> 1.3-0.3.beta3
+- Update to rrdtool 1.3 beta3
+- Return properly from errors in RRDp.pm (Resolves: #427040)
+- Requires: dejavu-lgc-fonts (Resolves: #426935)
+
+* Thu Dec 06 2007 Jarod Wilson <jwilson@redhat.com> 1.3-0.2.beta2
+- Update to rrdtool 1.3 beta2
+
+* Wed Aug 08 2007 Jarod Wilson <jwilson@redhat.com> 1.3-0.1.beta1
+- Update to rrdtool 1.3 beta1
+
+* Tue Jul 10 2007 Jarod Wilson <jwilson@redhat.com> 1.2.999-0.3.r1144
+- Update to latest rrdtool pre-1.3 svn snapshot (svn r1144)
+- Add php abi check (Resolves: #247339)
+
+* Fri Jun 15 2007 Jarod Wilson <jwilson@redhat.com> 1.2.999-0.2.r1127
+- Fix up BuildRequires
+
+* Fri Jun 15 2007 Jarod Wilson <jwilson@redhat.com> 1.2.999-0.1.r1127
+- Update to rrdtool pre-1.3 svn snapshot (svn r1127)
+
+* Mon May 21 2007 Jarod Wilson <jwilson@redhat.com> 1.2.23-5
+- BR: ruby so %%ruby_sitearch gets set
+
+* Mon May 21 2007 Jarod Wilson <jwilson@redhat.com> 1.2.23-4
+- Build ruby bindings
+
+* Thu May 03 2007 Jarod Wilson <jwilson@redhat.com> 1.2.23-3
+- Disable php bits on ppc64 for now, they fail to build
+
+* Thu May 03 2007 Jarod Wilson <jwilson@redhat.com> 1.2.23-2
+- Add BR: perl-devel for Fedora 7 and later
+
+* Tue May 01 2007 Jarod Wilson <jwilson@redhat.com> 1.2.23-1
+- New upstream release
+
+* Tue May 01 2007 Jarod Wilson <jwilson@redhat.com> 1.2.21-1
+- New upstream release
+
+* Wed Apr 25 2007 Jarod Wilson <jwilson@redhat.com> 1.2.19-2
+- Define %%python_version *before* its needed (#237826)
+
+* Mon Apr 09 2007 Jarod Wilson <jwilson@redhat.com> 1.2.19-1
+- New upstream release
+
+* Tue Jan 23 2007 Jarod Wilson <jwilson@redhat.com> 1.2.18-1
+- New upstream release
+
+* Mon Jan 22 2007 Jarod Wilson <jwilson@redhat.com> 1.2.17-1
+- New upstream release
+
+* Tue Jan 02 2007 Jarod Wilson <jwilson@redhat.com> 1.2.15-9
+- Fix crash with long error strings (upstream
+  changesets 929 and 935)
+
+* Thu Dec 14 2006 Jarod Wilson <jwilson@redhat.com> 1.2.15-8
+- Fix for log grid memory leak (#201241)
+
+* Tue Dec 12 2006 Jarod Wilson <jwilson@redhat.com> 1.2.15-7
+- Rebuild for python 2.5
+
+* Tue Nov 14 2006 Jarod Wilson <jwilson@redhat.com> 1.2.15-6
+- Conditionalize python, php and tcl bits (Resolves #203275)
+
+* Wed Oct 25 2006 Jarod Wilson <jwilson@redhat.com> 1.2.15-5
+- Add tcl sub-package (#203275)
+
+* Tue Sep 05 2006 Jarod Wilson <jwilson@redhat.com> 1.2.15-4
+- Rebuild for new glibc
+
+* Wed Aug 02 2006 Jarod Wilson <jwilson@redhat.com> 1.2.15-3
+- One more addition to initrrdtool patch, to fully revert
+  and correct upstream changeset 839
+- Fix for no python in minimal fc4 buildroots
+
+* Tue Aug  1 2006 Mihai Ibanescu <misa@redhat.com> 1.2.15-2
+- Fixed rrdtool-python to import the module properly (patch
+  rrdtool-1.2.15-initrrdtool.patch)
+
+* Mon Jul 17 2006 Jarod Wilson <jwilson@redhat.com> 1.2.15-1
+- Update to 1.2.15
+- Minor spec cleanups
+
+* Sat Jun 24 2006 Jarod Wilson <jwilson@redhat.com> 1.2.13-7
+- Fix up Obsoletes
+
+* Mon Jun 19 2006 Jarod Wilson <jwilson@redhat.com> 1.2.13-6
+- Flip perl, php and python sub-package names around to 
+  conform with general practices
+
+* Sat Jun 10 2006 Jarod Wilson <jwilson@redhat.com> 1.2.13-5
+- Minor fixes to make package own created directories
+
+* Wed Jun 07 2006 Jarod Wilson <jwilson@redhat.com> 1.2.13-4
+- Add php bits back into the mix
+
+* Mon Jun 05 2006 Jarod Wilson <jwilson@redhat.com> 1.2.13-3
+- Merge spec fixes from bz 185909
+
+* Sun Jun 04 2006 Jarod Wilson <jwilson@redhat.com> 1.2.13-2
+- Remove explicit perl dep, version grabbing using rpm during
+  rpmbuild not guaranteed to work (fails on ppc in plague),
+  and auto-gen perl deps are sufficient
+
+* Sat Jun 03 2006 Jarod Wilson <jwilson@redhat.com> 1.2.13-1
+- Update to release 1.2.13
+- Merge spec changes from dag, atrpms and mdk builds
+- Additional hacktastic contortions for lib64 & rpath messiness
+- Add missing post/postun ldconfig
+- Fix a bunch of rpmlint errors
+- Disable static libs, per FE guidelines
+- Split off docs
+
+* Wed Apr 19 2006 Chris Ricker <kaboom@oobleck.net> 1.2.12-1
+- Rev to 1.2
+
+* Fri May 20 2005 Matthias Saou <http://freshrpms.net/> 1.0.49-5
+- Include patch from Michael to fix perl module compilation on FC4 (#156242).
+
+* Fri May 20 2005 Matthias Saou <http://freshrpms.net/> 1.0.49-4
+- Fix for the php module patch (Joe Pruett, Dag Wieers), #156716.
+- Update source URL to new location since 1.2 is now the default stable.
+- Don't (yet) update to 1.0.50, as it introduces some changes in the perl
+  modules install.
+
+* Mon Jan 31 2005 Matthias Saou <http://freshrpms.net/> 1.0.49-3
+- Put perl modules in vendor_perl and not site_perl. #146513
+
+* Thu Jan 13 2005 Matthias Saou <http://freshrpms.net/> 1.0.49-2
+- Minor cleanups.
+
+* Thu Aug 25 2004 Dag Wieers <dag@wieers.com> - 1.0.49-1
+- Updated to release 1.0.49.
+
+* Wed Aug 25 2004 Dag Wieers <dag@wieers.com> - 1.0.48-3
+- Fixes for x86_64. (Garrick Staples)
+
+* Fri Jul  2 2004 Matthias Saou <http://freshrpms.net/> 1.0.48-3
+- Actually apply the patch for fixing the php module, doh!
+
+* Thu May 27 2004 Matthias Saou <http://freshrpms.net/> 1.0.48-2
+- Added php.d config entry to load the module once installed.
+
+* Thu May 13 2004 Dag Wieers <dag@wieers.com> - 1.0.48-1
+- Updated to release 1.0.48.
+
+* Tue Apr 06 2004 Dag Wieers <dag@wieers.com> - 1.0.47-1
+- Updated to release 1.0.47.
+
+* Thu Mar  4 2004 Matthias Saou <http://freshrpms.net/> 1.0.46-2
+- Change the strict dependency on perl to fix problem with the recent
+  update.
+
+* Mon Jan  5 2004 Matthias Saou <http://freshrpms.net/> 1.0.46-1
+- Update to 1.0.46.
+- Use system libpng and zlib instead of bundled ones.
+- Added php-rrdtool sub-package for the php4 module.
+
+* Fri Dec  5 2003 Matthias Saou <http://freshrpms.net/> 1.0.45-4
+- Added epoch to the perl dependency to work with rpm > 4.2.
+- Fixed the %% escaping in the perl dep.
+
+* Mon Nov 17 2003 Matthias Saou <http://freshrpms.net/> 1.0.45-2
+- Rebuild for Fedora Core 1.
+
+* Sun Aug  3 2003 Matthias Saou <http://freshrpms.net/>
+- Update to 1.0.45.
+
+* Wed Apr 16 2003 Matthias Saou <http://freshrpms.net/>
+- Update to 1.0.42.
+
+* Mon Mar 31 2003 Matthias Saou <http://freshrpms.net/>
+- Rebuilt for Red Hat Linux 9.
+
+* Wed Mar  5 2003 Matthias Saou <http://freshrpms.net/>
+- Added explicit perl version dependency.
+
+* Sun Feb 23 2003 Matthias Saou <http://freshrpms.net/>
+- Update to 1.0.41.
+
+* Fri Jan 31 2003 Matthias Saou <http://freshrpms.net/>
+- Update to 1.0.40.
+- Spec file cleanup.
+
+* Fri Jul 05 2002 Henri Gomez <hgomez@users.sourceforge.net>
+- 1.0.39
+
+* Mon Jun 03 2002 Henri Gomez <hgomez@users.sourceforge.net>
+- 1.0.38
+
+* Fri Apr 19 2002 Henri Gomez <hgomez@users.sourceforge.net>
+- 1.0.37
+
+* Tue Mar 12 2002 Henri Gomez <hgomez@users.sourceforge.net>
+- 1.0.34
+- rrdtools include zlib 1.1.4 which fix vulnerabilities in 1.1.3
+
diff --git a/program/src/Makefile.am b/program/src/Makefile.am
new file mode 100644 (file)
index 0000000..9998e4a
--- /dev/null
@@ -0,0 +1,128 @@
+## Process this file with automake to produce Makefile.in
+
+#AUTOMAKE_OPTIONS   = foreign
+#
+#ACLOCAL_M4       = $(top_srcdir)/config/aclocal.m4
+#AUTOHEADER = @AUTOHEADER@ --localdir=$(top_srcdir)/config
+
+if STATIC_PROGRAMS
+AM_LDFLAGS = -all-static
+endif
+
+INCLUDES = -DLOCALEDIR="\"$(datadir)/locale\""
+RRD_DEFAULT_FONT=@RRD_DEFAULT_FONT@
+AM_CPPFLAGS = -DRRD_DEFAULT_FONT=\"$(RRD_DEFAULT_FONT)\" -DNUMVERS=@NUMVERS@
+AM_CFLAGS = @CFLAGS@
+## no including this by default @WERROR@
+
+UPD_C_FILES =          \
+       rrd_parsetime.c \
+       rrd_hw.c        \
+       rrd_hw_math.c   \
+       rrd_hw_update.c \
+       rrd_diff.c      \
+       rrd_format.c    \
+       rrd_info.c      \
+       rrd_error.c     \
+       rrd_open.c      \
+       rrd_client.c    \
+       rrd_nan_inf.c   \
+       rrd_rpncalc.c   \
+       rrd_utils.c     \
+       rrd_update.c
+
+RRD_C_FILES =          \
+       hash_32.c       \
+       pngsize.c       \
+       rrd_create.c    \
+       rrd_graph.c     \
+       rrd_graph_helper.c      \
+       rrd_version.c   \
+       rrd_last.c      \
+       rrd_lastupdate.c        \
+       rrd_first.c     \
+       rrd_restore.c   \
+       rrd_xport.c     \
+       rrd_gfx.c \
+       rrd_dump.c      \
+       rrd_flushcached.c \
+       rrd_fetch.c     \
+       rrd_resize.c \
+       rrd_tune.c
+
+noinst_HEADERS = \
+       unused.h \
+        gettext.h \
+       rrd_getopt.h rrd_parsetime.h \
+       rrd_config_bottom.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 \
+       fnv.h rrd_graph.h \
+       rrd_is_thread_safe.h
+
+if BUILD_LIBDBI
+RRD_C_FILES += rrd_fetch_libdbi.c
+endif
+
+if BUILD_GETOPT
+noinst_HEADERS += rrd_getopt.h
+UPD_C_FILES += rrd_getopt.c rrd_getopt1.c
+endif
+
+noinst_LTLIBRARIES        = librrdupd.la
+
+lib_LTLIBRARIES           = librrd.la 
+if BUILD_MULTITHREAD
+lib_LTLIBRARIES           += librrd_th.la
+endif
+
+librrdupd_la_SOURCES      = $(UPD_C_FILES) rrd_not_thread_safe.c
+librrdupd_la_LIBADD       = $(CORE_LIBS)
+
+librrd_la_SOURCES         = $(RRD_C_FILES)
+librrd_la_DEPENDENCIES    = librrdupd.la librrd.sym
+librrd_la_LIBADD          = librrdupd.la $(ALL_LIBS)
+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          = $(AM_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 rrd_format.h rrd_client.h
+
+bin_PROGRAMS   = rrdtool rrdupdate rrdcached
+
+if BUILD_RRDCGI
+bin_PROGRAMS += rrdcgi
+endif
+
+rrdcgi_SOURCES = rrd_cgi.c
+rrdcgi_LDADD   = librrd.la
+
+rrdupdate_SOURCES = rrdupdate.c
+rrdupdate_LDADD          = librrdupd.la
+
+rrdtool_SOURCES = rrd_tool.c
+rrdtool_DEPENDENCIES = librrd.la
+rrdtool_LDADD  = librrd.la
+
+rrdcached_SOURCES = rrd_daemon.c
+rrdcached_DEPENDENCIES = librrd_th.la
+rrdcached_CPPFLAGS = -DVERSION='"$(VERSION)"' -DLOCALSTATEDIR='"$(localstatedir)"'
+rrdcached_LDADD = librrd_th.la
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = librrd.pc
+
+# strftime is here because we do not usually need it. unices have propper
+# iso date support
+EXTRA_DIST= librrd.pc.in strftime.c strftime.h  rrd_getopt.c rrd_getopt1.c rrd_getopt.h \
+       win32comp.c  rrd_thread_safe_nt.c get_ver.awk librrd.sym.in.in plbasename.c   plbasename.h   
+
+
+librrd.sym: librrd.sym.in
+       $(AM_V_GEN)grep -v LIBC_HAS_GETOPT_LONG librrd.sym.in >$@
diff --git a/program/src/compile_afm.pl b/program/src/compile_afm.pl
new file mode 100644 (file)
index 0000000..3101b31
--- /dev/null
@@ -0,0 +1,479 @@
+#!/usr/local/bin/perl -w
+
+require 5.005;
+use strict;
+
+# The glyps list can be downloaded from
+# http://partners.adobe.com/asn/developer/type/glyphlist.txt
+# This URL is from this page:
+# http://partners.adobe.com/asn/developer/type/unicodegn.html
+# which is refered from
+# http://partners.adobe.com/asn/developer/technotes/fonts.html
+
+my $onlyHelvetica = 0;
+
+my %globalName2Unicode;
+my %font_code = ();
+
+my $indent0 = "";
+my $indent1 = "  ";
+my $indent2 = $indent1 x 3;
+
+my $q = 0;
+my $qU = 0;
+
+sub read_glyphlist
+{
+  my $fn ="glyphlist.txt";
+  open(FH, $fn)
+  || die "Can't read $fn\n";
+  my %seen = ();
+  while (<FH>) {
+    next if /^\s*#/;
+    next unless /^([0-9A-F]{4});(\w+);/;
+    my $unicode = 0 + hex($1);
+    my $name = $2;
+    next if ($globalName2Unicode{$name});
+    $globalName2Unicode{$name} = $unicode;
+  }
+  close(FH);
+}
+
+sub process_all_fonts
+{
+  my $dir = ".";
+  my $wc = "*.afm";
+  $wc = "Helvetica.afm" if $onlyHelvetica;
+  $wc = "ZapfDin.afm" if 0;
+  $wc = "Helve*.afm" if 0;
+  $wc = "Times-BoldItalic.afm" if 0;
+  foreach my $fn (glob("$dir/$wc")) {
+    process_font($fn);
+  }
+}
+
+sub process_font
+{
+  my ($fn) = @_;
+  print STDERR "Compiling afm file: $fn\n";
+  my %fi = (); # font info
+  my $c = "";
+  $fi{C} = \$c;
+  $fi{ligaturesR} = {};
+  $fi{FontSpecificUnicodeNameToChar} = {};
+  $fi{filename} = $fn;
+  $fi{filename} =~ s/.*\///;
+  open(FH, $fn) || die "Can't open $fn\n";
+  print STDERR "Reads global font info\n" if $q;
+  while (<FH>) {
+    chomp;
+    next if /^\s*$/ || /^\s*#/;
+    last if /^StartCharMetrics/;
+    next unless (/^(\S+)\s+(\S(.*\S)?)/);
+    my $id = $1;
+    my $value = $2;
+    $value =~ s/\s+/ /g;
+    $fi{"Afm$id"} = $value;
+  }
+  my $fontName = $fi{AfmFontName};
+  $c .= "\n\n/* ". ("-" x 66) . "*/\n";
+  $c .= "/* FontName: $fontName */\n";
+  $c .= "/* FullName: $fi{AfmFullName} */\n";
+  $c .= "/* FamilyName: $fi{AfmFamilyName} */\n";
+  $fi{cName} = $fontName;
+  $fi{cName} =~ s/\W/_/g;
+  my %charMetrics = ();
+  my %kerning = ();
+  read_charmetrics(\%fi, \%charMetrics);
+  while (<FH>) {
+    read_kerning(\%fi, \%kerning) if /^StartKernPairs/;
+  }
+  if (0) {
+    my @names = keys %charMetrics;
+    print STDERR "Did read ", ($#names + 1), " font metrics\n";
+  }
+  write_font(\%fi, \%charMetrics, \%kerning);
+}
+
+sub read_charmetrics
+{
+  my ($fiR, $charMetricsR) = @_;
+  print STDERR "Reads char metric info\n" if $q;
+  my $isZapfDingbats = $$fiR{AfmFontName} eq "ZapfDingbats";
+  my $ligaturesR = $$fiR{ligaturesR};
+  my %ligatures = ();
+  my %seenUnicodes = ();
+  while (<FH>) {
+    chomp;
+    next if /^\s*$/ || /^\s*#/;
+    last if /^EndCharMetrics/;
+#next unless /N S / || /N comma /;
+#next unless /N ([sfil]|fi) /;
+#print "$_\n";
+    my $line = $_;
+# C 102 ; WX 333 ; N f ; B -169 -205 446 698 ; L i fi ; L l fl ;
+    my ($width, $unicode, $name, @charLigatures);
+    foreach (split/\s*;\s*/, $line) {
+      if (/^C\s+(-?\d+)/) {
+        $unicode = 0 + $1;
+      } elsif (/^N\s+(\w+)/) {
+        $name = $1;
+      } elsif (/^WX?\s+(-?\d+)/) {
+        $width = normalize_width($1, 0);
+      } elsif (/^L\s+(\w+)\s+(\w+)/) {
+        push(@charLigatures, $1, $2);
+      }
+    }
+    if ($unicode < 0) {
+      unless (defined $name) {
+        print STDERR "Glyph missing name and code: $_\n";
+        next;
+      }
+      $unicode = name2uni($fiR, $name);
+      print STDERR "name2uni: $name -> $unicode\n" if $qU && 0;
+    } elsif (defined $name) {
+      my $std = $globalName2Unicode{$name};
+      if (!defined $std) {
+        print STDERR "Adds unicode mapping: ",
+          "$name -> $unicode\n" if $qU;
+        ${$$fiR{FontSpecificUnicodeNameToChar}}{$name} = $unicode;
+      } else {
+        $unicode = $std;
+      }
+    }
+    if (!defined($unicode) || $unicode <= 0) {
+      next if $isZapfDingbats && $name =~ /^a(\d+)$/;
+      next if $$fiR{AfmFontName} eq "Symbol" && $name eq "apple";
+      print STDERR "Glyph '$name' has unknown unicode: $_\n";
+      next;
+    }
+    unless (defined $width) {
+      print STDERR "Glyph '$name' missing width: $_\n";
+      next;
+    }
+    if ($seenUnicodes{$unicode}) {
+      print STDERR "Duplicate character: unicode = $unicode, ",
+        "$name and ", $seenUnicodes{$unicode},
+        " (might be due to Adobe charset remapping)\n";
+      next;
+    }
+    $seenUnicodes{$unicode} = $name;
+    my %c = ();
+    $c{name} = $name;
+    $c{unicode} = $unicode;
+    $c{width} = $width;
+    $$charMetricsR{$unicode} = \%c;
+    $ligatures{$unicode} = \@charLigatures if $#charLigatures >= 0;
+  }
+  foreach my $unicode (keys %ligatures) {
+    my $aR = $ligatures{$unicode};
+    my $unicode2 = name2uni($fiR, $$aR[0]);
+    my $unicode3 = name2uni($fiR, $$aR[1]);
+    unless ($unicode2) {
+      print STDERR "Missing ligature char 1: $$aR[0]\n";
+      next;
+    }
+    unless ($unicode3) {
+      print STDERR "Missing ligature char 2: $$aR[1]\n";
+      next;
+    }
+    my $key = sprintf("%04d;%04d", $unicode, $unicode2);
+    $$ligaturesR{$key} = $unicode3;
+  }
+}
+
+sub name2uni
+{
+  my ($fiR, $name) = @_;
+  my $fontMapR = $$fiR{FontSpecificUnicodeNameToChar};
+  return $globalName2Unicode{$name} || $$fontMapR{$name};
+}
+
+sub read_kerning
+{
+  my ($fiR, $kerningR) = @_;
+  print STDERR "Reads kerning info\n" if $q;
+  while (<FH>) {
+    chomp;
+    next if /^\s*$/ || /^\s*#/;
+    last if /^EndKernPairs/;
+    unless (/^KPX\s+(\w+)\s+(\w+)\s+(-?\d+)\s*$/) {
+      print STDERR "Can't parse kern spec: $_\n";
+      next;
+    }
+    my $name1 = $1;
+    my $name2 = $2;
+    my $delta = normalize_width($3, 1);
+    next unless $delta;
+    my $unicode1 = name2uni($fiR, $name1);
+    my $unicode2 = name2uni($fiR, $name2);
+    unless ($unicode1 && $unicode2) {
+      print "Unknown kern pair: $name1 and $name2\n";
+      next;
+    }
+    my $charR = $$kerningR{$unicode1};
+    unless (defined $charR) {
+      my %c = ();
+      $charR = \%c;
+      $$kerningR{$unicode1} = $charR;
+    }
+    $$charR{$unicode2} = $delta;
+  }
+}
+
+sub write_font
+{
+  my ($fiR, $charMetricsR, $kerningR) = @_;
+  print STDERR "Writes font\n" if $q;
+  my $cR = $$fiR{C};
+  $$fiR{widthsA} = make_array();
+  $$fiR{kerning_indexA} = make_array();
+  $$fiR{kerning_dataA} = make_array();
+  $$fiR{highchars_indexA} = make_array();
+  $$fiR{ligaturesA} = make_array();
+  write_font_metrics($fiR, $charMetricsR, $kerningR);
+  write_ligatures($fiR);
+  my $widths_count = array_size($$fiR{widthsA});
+  my $kerning_index_count = array_size($$fiR{kerning_indexA});
+  my $kerning_data_count = array_size($$fiR{kerning_dataA});
+  my $highchars_count = array_size($$fiR{highchars_indexA});
+  my $ligatures_count = array_size($$fiR{ligaturesA}) / 3;
+  my $info_code = "";
+  my $i2 = $indent2;
+  my $packedSize = $widths_count + 2 * $kerning_index_count +
+     $kerning_data_count + 2 * $highchars_count +
+     3 * 2 * $ligatures_count;
+  $info_code .= $indent1 . "{ /* $$fiR{filename}   $packedSize bytes */\n";
+    $info_code .= $i2 . "\"$$fiR{AfmFontName}\",";
+    $info_code .= " \"$$fiR{AfmFullName}\",\n";
+    $info_code .= $i2 . $$fiR{widthsACName} . ",\n";
+    $info_code .= $i2 . $$fiR{kerning_indexACName} . ",\n";
+    $info_code .= $i2 . $$fiR{kerning_dataACName} . ",\n";
+    $info_code .= $i2 . $$fiR{highchars_indexACName} . ", ";
+    $info_code .= $highchars_count . ",\n";
+    $info_code .= $i2 . $$fiR{ligaturesACName} . ", ";
+    $info_code .= $ligatures_count;
+    $info_code .= "},\n";
+  $font_code{$$fiR{AfmFullName}} = { TABLES => $$cR, INFO => $info_code};
+}
+
+sub write_font_metrics
+{
+  my ($fiR, $charMetricsR, $kerningR) = @_;
+  print STDERR "Writes font metrics\n" if $q;
+  my $lastUnicode = 31;
+  my $cR = $$fiR{C};
+  my $widthsA = $$fiR{widthsA};
+  my $kerning_indexA = $$fiR{kerning_indexA};
+  my $kerning_dataA = $$fiR{kerning_dataA};
+  my $highchars_indexA = $$fiR{highchars_indexA};
+  my @uniArray = sort { $a <=> $b } keys %$charMetricsR;
+  my $highchars_count = 0;
+  my $had_kerning = 0;
+  while (1) {
+    my $fill = 0;
+    if ($#uniArray < 0) {
+      last if $lastUnicode > 126;
+      $fill = 1;
+    } elsif ($lastUnicode < 126 && $uniArray[0] > $lastUnicode + 1) {
+      $fill = 1;
+    }
+    if ($fill) {
+      $lastUnicode++;
+#print STDERR "fill for $lastUnicode, $#uniArray, $uniArray[0]\n";
+      append_to_array($widthsA, 0);
+      append_to_array($kerning_indexA, 0);
+      next;
+    }
+    my $unicode = shift @uniArray;
+    next if $unicode < 32;
+    $lastUnicode = $unicode;
+    my $metricsR = $$charMetricsR{$unicode};
+    if ($unicode > 126) {
+      append_to_array($highchars_indexA, $unicode);
+      $highchars_count++;
+    }
+    my $m = $$metricsR{width};
+    $m = "/* ".array_size($widthsA)."=$unicode */". $m if 0;
+    append_to_array($widthsA, $m);
+    my $kerningInfoR = $$kerningR{$unicode};
+    my $kerning_index = 0;
+    if (defined $kerningInfoR) {
+      my @kerns = ();
+      foreach my $unicode2 (sort { $a <=> $b } keys %$kerningInfoR) {
+        my $delta = $$kerningInfoR{$unicode2};
+        append_escaped_16bit_int(\@kerns, $unicode2);
+        push(@kerns, $delta);
+        $had_kerning = 1;
+      }
+      $kerning_index = append_8bit_subarray($kerning_dataA, 2, @kerns);
+    }
+    append_to_array($kerning_indexA, $kerning_index);
+  }
+  $$fiR{kerning_indexA} = make_array() if !$had_kerning;
+  write_array($fiR, "widths", "afm_cuint8");
+  write_array($fiR, "kerning_index", "afm_sint16");
+  write_array($fiR, "kerning_data", "afm_cuint8");
+  write_array($fiR, "highchars_index", "afm_cuint16");
+}
+
+sub write_ligatures
+{
+  my ($fiR) = @_;
+  print STDERR "Writes font ligatures\n" if $q;
+  my $ligaturesA = $$fiR{ligaturesA};
+  my $ligaturesR = $$fiR{ligaturesR};
+  foreach (sort keys %$ligaturesR) {
+    unless (/^(\w{4});(\w{4})$/) {
+      die "Invalid ligature key: $_";
+    }
+    append_to_array($ligaturesA, $1 + 0, $2 + 0, $$ligaturesR{$_});
+  }
+  write_array($fiR, "ligatures", "afm_cunicode");
+}
+
+sub indent
+{
+  my ($num) = @_;
+  return "  " x $num;
+}
+
+sub make_array
+{
+  my @a = ();
+  return \@a;
+}
+
+sub append_to_array
+{
+  my ($aR, @newElements) = @_;
+  my $z1 = array_size($aR);
+  push(@$aR, @newElements);
+  my $z2 = array_size($aR);
+  my $zz = $#newElements +1;
+}
+
+sub append_8bit_subarray
+{
+  my ($aR, $elementsPerItem, @newElements) = @_;
+  push(@$aR, 42) if !array_size($aR); # initial dummy value
+  my $idx = $#{$aR} + 1;
+#print "append_8bit_subarray ", ($#newElements+1), " = (", join(", ", @newElements), ") -> $idx\n";
+  append_escaped_16bit_int($aR, ($#newElements + 1) / $elementsPerItem);
+  push(@$aR, @newElements);
+  die "Can't handle that big sub array, sorry...\n" if $idx > 50000;
+  return $idx;
+}
+
+sub append_escaped_16bit_int
+{
+  my ($aR, $count) = @_;
+  die "Invalid count = 0\n" unless $count;
+  if ($count >= 510) {
+    push(@$aR, 1, int($count / 256), int($count % 256));
+    print STDERR "full: $count\n" if 0;
+  } elsif ($count >= 254) {
+    push(@$aR, 0, $count - 254);
+    print STDERR "semi: $count\n" if 0;
+  } else {
+    push(@$aR, $count + 1);
+  }
+}
+
+sub array_size
+{
+  my ($aR) = @_;
+  return $#{$aR} + 1;
+}
+
+sub write_array
+{
+  my ($fiR, $name, $type) = @_;
+  my $aR = $$fiR{$name."A"};
+  my $cName = $$fiR{cName};
+  my $num = $#{$aR} + 1;
+  my $array_name_key = $name."ACName";
+  if ($num == 0) {
+    $$fiR{$array_name_key} = "NULL";
+    return;
+  }
+  my $cR = $$fiR{C};
+  my $array_name = "afm_" . $cName . "_" . $name;
+  $$fiR{$array_name_key} = $array_name;
+  $$cR .= "static $type $array_name" . "[] = { /* $num */\n";
+  my $line = $indent1;
+  for (my $i = 0; $i < $num; $i++) {
+    $line .= "," if $i > 0;
+    if (length($line) > 65) {
+      $line .= "\n";
+      $$cR .= $line;
+      $line = $indent1;
+    }
+    $line .= $$aR[$i];
+  }
+  $line .= "\n";
+  $$cR .= $line;
+  $$cR .= "};\n";
+}
+
+sub normalize_width
+{
+  my ($w, $signed) = @_;
+  my $n = int(($w + 3) / 6);
+  if ($signed) {
+    $n = -128 if $n < -128;
+    $n =  127 if $n >  127;
+    $n =  256 + $n if $n < 0; # make unsigned.
+  } else {
+    $n =    0 if $n <    0;
+    $n =  255 if $n >  255;
+  }
+  return $n;
+}
+
+sub main
+{
+  my $cfn = "../../src/rrd_afm_data.c";
+  read_glyphlist();
+  process_all_fonts();
+  my @fonts = sort keys %font_code;
+  unless ($#fonts >= 0) {
+    die "You must have at least 1 font.\n";
+  }
+  open(CFILE, ">$cfn") || die "Can't create $cfn\n";
+  print CFILE header($cfn);
+  print CFILE ${$font_code{$_}}{TABLES} foreach @fonts;
+  print CFILE "const afm_fontinfo afm_fontinfolist[] = {\n";
+  print CFILE ${$font_code{$_}}{INFO} foreach @fonts;
+  print CFILE $indent1 . "{ 0, 0, 0 }\n";
+  print CFILE $indent0 . "};\n";
+  print CFILE $indent0 . "const int afm_fontinfo_count = ",
+    ($#fonts + 1), ";\n";
+  close(CFILE);
+  print STDERR "Compiled ", ($#fonts+1), " fonts.\n";
+}
+
+sub header
+{
+  my ($fn) = @_;
+  $fn =~ s/.*\///;
+  my $h = $fn;
+  $h =~ s/\.c$/.h/;
+  return <<"END";
+/****************************************************************************
+ * RRDtool 1.1.x  Copyright Tobias Oetiker, 1997 - 2002
+ ****************************************************************************
+ * $fn  Encoded afm (Adobe Font Metrics) for selected fonts.
+ ****************************************************************************
+ *
+ * THIS FILE IS AUTOGENERATED BY PERL. DO NOT EDIT.
+ *
+ ****************************************************************************/
+
+#include "$h"
+#include <stdlib.h>
+
+END
+}
+
+main();
diff --git a/program/src/fnv.h b/program/src/fnv.h
new file mode 100644 (file)
index 0000000..eaea66b
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * fnv - Fowler/Noll/Vo- hash code
+ *
+ * @(#) $Revision$
+ * @(#) $Id$
+ * @(#) $Source$
+ *
+ ***
+ *
+ * Fowler/Noll/Vo- hash
+ *
+ * The basis of this hash algorithm was taken from an idea sent
+ * as reviewer comments to the IEEE POSIX P1003.2 committee by:
+ *
+ *      Phong Vo (http://www.research.att.com/info/kpv/)
+ *      Glenn Fowler (http://www.research.att.com/~gsf/)
+ *
+ * In a subsequent ballot round:
+ *
+ *      Landon Curt Noll (http://reality.sgi.com/chongo/)
+ *
+ * improved on their algorithm.  Some people tried this hash
+ * and found that it worked rather well.  In an EMail message
+ * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.
+ *
+ * FNV hashes are architected to be fast while maintaining a low
+ * collision rate. The FNV speed allows one to quickly hash lots
+ * of data while maintaining a reasonable collision rate.  See:
+ *
+ *      http://reality.sgi.com/chongo/tech/comp/fnv/
+ *
+ * for more details as well as other forms of the FNV hash.
+ *
+ ***
+ *
+ * NOTE: The FNV-0 historic hash is not recommended.  One should use
+ *      the FNV-1 hash instead.
+ *
+ * To use the 32 bit FNV-0 historic hash, pass FNV0_32_INIT as the
+ * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().
+ *
+ * To use the 64 bit FNV-0 historic hash, pass FNV0_64_INIT as the
+ * Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str().
+ *
+ * To use the recommended 32 bit FNV-1 hash, pass FNV1_32_INIT as the
+ * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().
+ *
+ * To use the recommended 64 bit FNV-1 hash, pass FNV1_64_INIT as the
+ * Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str().
+ *
+ ***
+ *
+ * Please do not copyright this code.  This code is in the public domain.
+ *
+ * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
+ * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * By:
+ *     chongo <Landon Curt Noll> /\oo/\
+ *     http://reality.sgi.com/chongo/
+ *     EMail: chongo_fnv at prime dot engr dot sgi dot com
+ *
+ * Share and Enjoy!    :-)
+ */
+
+#if !defined(__FNV_H__)
+#define __FNV_H__
+
+
+/*
+ * 32 bit FNV-0 hash type
+ */
+typedef unsigned long Fnv32_t;
+
+
+/* 
+ * 32 bit FNV-0 zero initial basis
+ *
+ * This historic hash is not recommended.  One should use
+ * the FNV-1 hash and inital basis instead.
+ */
+#define FNV0_32_INIT ((Fnv32_t)0)
+
+
+/*
+ * 32 bit FNV-1 non-zero initial basis
+ *
+ * The FNV-1 initial basis is the FNV-0 hash of the following 32 octets:
+ *
+ *              chongo <Landon Curt Noll> /\../\
+ *
+ * Note that the \'s above are not back-slashing escape characters.
+ * They are literal ASCII  backslash 0x5c characters.
+ */
+#define FNV1_32_INIT ((Fnv32_t)0x811c9dc5)
+
+Fnv32_t   fnv_32_buf(
+    const void *,
+    size_t,
+    Fnv32_t);
+
+Fnv32_t   fnv_32_str(
+    const char *,
+    Fnv32_t);
+
+unsigned long FnvHash(
+    const char *);
+
+#endif                          /* __FNV_H__ */
diff --git a/program/src/get_ver.awk b/program/src/get_ver.awk
new file mode 100644 (file)
index 0000000..e8c4552
--- /dev/null
@@ -0,0 +1,40 @@
+# ****************************************************************************
+# RRDtool 1.2.19  Copyright by Tobi Oetiker, 1997-2007
+# ****************************************************************************
+# get_ver.awk   AWK Script for non-configure builds
+# ****************************************************************************
+# $Id: get_ver.awk 1000 2007-14-02 05:51:34Z oetiker $
+# ****************************************************************************
+BEGIN {
+  # fetch rrdtool version number from input file and write them to STDOUT
+  while ((getline < ARGV[1]) > 0) {
+    if (match ($0, /^AC_INIT/)) {
+      split($1, t, ",");
+      my_ver_str = substr(t[2],2,length(t[2])-3);
+      split(my_ver_str, v, ".");
+      gsub("[^0-9].*$", "", v[3]);
+      my_ver = v[1] "," v[2] "," v[3];
+    }
+    if (match ($0, /^NUMVERS=/)) {
+      split($1, t, "=");
+      my_ver_num = t[2];
+    }
+  }
+  # read from from input file, replace placeholders, and write to STDOUT
+  if (ARGV[2]) {
+    while ((getline < ARGV[2]) > 0) {
+      if (match ($0, /@@NUMVERS@@/)) {
+        gsub("@@NUMVERS@@", my_ver_num, $0);
+      }
+      if (match ($0, /@@PACKAGE_VERSION@@/)) {
+        gsub("@@PACKAGE_VERSION@@", "" my_ver_str "", $0);
+      }
+      print;
+    }
+  } else {
+    print "RRD_VERSION = " my_ver "";
+    print "RRD_VERSION_STR = " my_ver_str "";
+    print "RRD_NUMVERS = " my_ver_num "";
+  }
+}
+
diff --git a/program/src/gettext.h b/program/src/gettext.h
new file mode 100644 (file)
index 0000000..209921e
--- /dev/null
@@ -0,0 +1,271 @@
+/* Convenience header for conditional use of GNU <libintl.h>.
+   Copyright (C) 1995-1998, 2000-2002, 2004-2006 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published
+   by the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU General Public
+   License along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+   USA.  */
+
+#ifndef _LIBGETTEXT_H
+#define _LIBGETTEXT_H 1
+
+/* NLS can be disabled through the configure --disable-nls option.  */
+#if ENABLE_NLS
+
+/* Get declarations of GNU message catalog functions.  */
+# include <libintl.h>
+
+/* You can set the DEFAULT_TEXT_DOMAIN macro to specify the domain used by
+   the gettext() and ngettext() macros.  This is an alternative to calling
+   textdomain(), and is useful for libraries.  */
+# ifdef DEFAULT_TEXT_DOMAIN
+#  undef gettext
+#  define gettext(Msgid) \
+     dgettext (DEFAULT_TEXT_DOMAIN, Msgid)
+#  undef ngettext
+#  define ngettext(Msgid1, Msgid2, N) \
+     dngettext (DEFAULT_TEXT_DOMAIN, Msgid1, Msgid2, N)
+# endif
+
+#else
+
+/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
+   chokes if dcgettext is defined as a macro.  So include it now, to make
+   later inclusions of <locale.h> a NOP.  We don't include <libintl.h>
+   as well because people using "gettext.h" will not include <libintl.h>,
+   and also including <libintl.h> would fail on SunOS 4, whereas <locale.h>
+   is OK.  */
+#if defined(__sun)
+# include <locale.h>
+#endif
+
+/* Many header files from the libstdc++ coming with g++ 3.3 or newer include
+   <libintl.h>, which chokes if dcgettext is defined as a macro.  So include
+   it now, to make later inclusions of <libintl.h> a NOP.  */
+#if defined(__cplusplus) && defined(__GNUG__) && (__GNUC__ >= 3)
+# include <cstdlib>
+# if (__GLIBC__ >= 2) || _GLIBCXX_HAVE_LIBINTL_H
+#  include <libintl.h>
+# endif
+#endif
+
+/* Disabled NLS.
+   The casts to 'const char *' serve the purpose of producing warnings
+   for invalid uses of the value returned from these functions.
+   On pre-ANSI systems without 'const', the config.h file is supposed to
+   contain "#define const".  */
+# define gettext(Msgid) ((const char *) (Msgid))
+# define dgettext(Domainname, Msgid) ((void) (Domainname), gettext (Msgid))
+# define dcgettext(Domainname, Msgid, Category) \
+    ((void) (Category), dgettext (Domainname, Msgid))
+# define ngettext(Msgid1, Msgid2, N) \
+    ((N) == 1 \
+     ? ((void) (Msgid2), (const char *) (Msgid1)) \
+     : ((void) (Msgid1), (const char *) (Msgid2)))
+# define dngettext(Domainname, Msgid1, Msgid2, N) \
+    ((void) (Domainname), ngettext (Msgid1, Msgid2, N))
+# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
+    ((void) (Category), dngettext(Domainname, Msgid1, Msgid2, N))
+# define textdomain(Domainname) ((const char *) (Domainname))
+# define bindtextdomain(Domainname, Dirname) \
+    ((void) (Domainname), (const char *) (Dirname))
+# define bind_textdomain_codeset(Domainname, Codeset) \
+    ((void) (Domainname), (const char *) (Codeset))
+
+#endif
+
+/* A pseudo function call that serves as a marker for the automated
+   extraction of messages, but does not call gettext().  The run-time
+   translation is done at a different place in the code.
+   The argument, String, should be a literal string.  Concatenated strings
+   and other string expressions won't work.
+   The macro's expansion is not parenthesized, so that it is suitable as
+   initializer for static 'char[]' or 'const char[]' variables.  */
+#define gettext_noop(String) String
+
+/* The separator between msgctxt and msgid in a .mo file.  */
+#define GETTEXT_CONTEXT_GLUE "\004"
+
+/* Pseudo function calls, taking a MSGCTXT and a MSGID instead of just a
+   MSGID.  MSGCTXT and MSGID must be string literals.  MSGCTXT should be
+   short and rarely need to change.
+   The letter 'p' stands for 'particular' or 'special'.  */
+#ifdef DEFAULT_TEXT_DOMAIN
+# define pgettext(Msgctxt, Msgid) \
+   pgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES)
+#else
+# define pgettext(Msgctxt, Msgid) \
+   pgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES)
+#endif
+#define dpgettext(Domainname, Msgctxt, Msgid) \
+  pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES)
+#define dcpgettext(Domainname, Msgctxt, Msgid, Category) \
+  pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, Category)
+#ifdef DEFAULT_TEXT_DOMAIN
+# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \
+   npgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES)
+#else
+# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \
+   npgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES)
+#endif
+#define dnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N) \
+  npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES)
+#define dcnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N, Category) \
+  npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, Category)
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static const char *
+pgettext_aux (const char *domain,
+             const char *msg_ctxt_id, const char *msgid,
+             int category)
+{
+  const char *translation = dcgettext (domain, msg_ctxt_id, category);
+  if (translation == msg_ctxt_id)
+    return msgid;
+  else
+    return translation;
+}
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static const char *
+npgettext_aux (const char *domain,
+              const char *msg_ctxt_id, const char *msgid,
+              const char *msgid_plural, unsigned long int n,
+              int category)
+{
+  const char *translation =
+    dcngettext (domain, msg_ctxt_id, msgid_plural, n, category);
+  if (translation == msg_ctxt_id || translation == msgid_plural)
+    return (n == 1 ? msgid : msgid_plural);
+  else
+    return translation;
+}
+
+/* The same thing extended for non-constant arguments.  Here MSGCTXT and MSGID
+   can be arbitrary expressions.  But for string literals these macros are
+   less efficient than those above.  */
+
+#include <string.h>
+
+#define _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS \
+  (((__GNUC__ >= 3 || __GNUG__ >= 2) && !__STRICT_ANSI__) \
+   /* || __STDC_VERSION__ >= 199901L */ )
+
+#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS
+#include <stdlib.h>
+#endif
+
+#define pgettext_expr(Msgctxt, Msgid) \
+  dcpgettext_expr (NULL, Msgctxt, Msgid, LC_MESSAGES)
+#define dpgettext_expr(Domainname, Msgctxt, Msgid) \
+  dcpgettext_expr (Domainname, Msgctxt, Msgid, LC_MESSAGES)
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static const char *
+dcpgettext_expr (const char *domain,
+                const char *msgctxt, const char *msgid,
+                int category)
+{
+  size_t msgctxt_len = strlen (msgctxt) + 1;
+  size_t msgid_len = strlen (msgid) + 1;
+  const char *translation;
+#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS
+  char msg_ctxt_id[msgctxt_len + msgid_len];
+#else
+  char buf[1024];
+  char *msg_ctxt_id =
+    (msgctxt_len + msgid_len <= sizeof (buf)
+     ? buf
+     : (char *) malloc (msgctxt_len + msgid_len));
+  if (msg_ctxt_id != NULL)
+#endif
+    {
+      memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1);
+      msg_ctxt_id[msgctxt_len - 1] = '\004';
+      memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len);
+      translation = dcgettext (domain, msg_ctxt_id, category);
+#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS
+      if (msg_ctxt_id != buf)
+       free (msg_ctxt_id);
+#endif
+      if (translation != msg_ctxt_id)
+       return translation;
+    }
+  return msgid;
+}
+
+#define npgettext_expr(Msgctxt, Msgid, MsgidPlural, N) \
+  dcnpgettext_expr (NULL, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES)
+#define dnpgettext_expr(Domainname, Msgctxt, Msgid, MsgidPlural, N) \
+  dcnpgettext_expr (Domainname, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES)
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static const char *
+dcnpgettext_expr (const char *domain,
+                 const char *msgctxt, const char *msgid,
+                 const char *msgid_plural, unsigned long int n,
+                 int category)
+{
+  size_t msgctxt_len = strlen (msgctxt) + 1;
+  size_t msgid_len = strlen (msgid) + 1;
+  const char *translation;
+#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS
+  char msg_ctxt_id[msgctxt_len + msgid_len];
+#else
+  char buf[1024];
+  char *msg_ctxt_id =
+    (msgctxt_len + msgid_len <= sizeof (buf)
+     ? buf
+     : (char *) malloc (msgctxt_len + msgid_len));
+  if (msg_ctxt_id != NULL)
+#endif
+    {
+      memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1);
+      msg_ctxt_id[msgctxt_len - 1] = '\004';
+      memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len);
+      translation = dcngettext (domain, msg_ctxt_id, msgid_plural, n, category);
+#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS
+      if (msg_ctxt_id != buf)
+       free (msg_ctxt_id);
+#endif
+      if (!(translation == msg_ctxt_id || translation == msgid_plural))
+       return translation;
+    }
+  return (n == 1 ? msgid : msgid_plural);
+}
+
+#endif /* _LIBGETTEXT_H */
diff --git a/program/src/hash_32.c b/program/src/hash_32.c
new file mode 100644 (file)
index 0000000..7885c3e
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * hash_32 - 32 bit Fowler/Noll/Vo hash code
+ *
+ *
+ ***
+ *
+ * Fowler/Noll/Vo hash
+ *
+ * The basis of this hash algorithm was taken from an idea sent
+ * as reviewer comments to the IEEE POSIX P1003.2 committee by:
+ *
+ *      Phong Vo (http://www.research.att.com/info/kpv/)
+ *      Glenn Fowler (http://www.research.att.com/~gsf/)
+ *
+ * In a subsequent ballot round:
+ *
+ *      Landon Curt Noll (http://reality.sgi.com/chongo/)
+ *
+ * improved on their algorithm.  Some people tried this hash
+ * and found that it worked rather well.  In an EMail message
+ * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.
+ *
+ * FNV hashes are architected to be fast while maintaining a low
+ * collision rate. The FNV speed allows one to quickly hash lots
+ * of data while maintaining a reasonable collision rate.  See:
+ *
+ *      http://reality.sgi.com/chongo/tech/comp/fnv/
+ *
+ * for more details as well as other forms of the FNV hash.
+ ***
+ *
+ * NOTE: The FNV-0 historic hash is not recommended.  One should use
+ *      the FNV-1 hash instead.
+ *
+ * To use the 32 bit FNV-0 historic hash, pass FNV0_32_INIT as the
+ * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().
+ *
+ * To use the recommended 32 bit FNV-1 hash, pass FNV1_32_INIT as the
+ * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().
+ *
+ ***
+ *
+ * Please do not copyright this code.  This code is in the public domain.
+ *
+ * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
+ * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * By:
+ *     chongo <Landon Curt Noll> /\oo/\
+ *     http://reality.sgi.com/chongo/
+ *     EMail: chongo_fnv at prime dot engr dot sgi dot com
+ *
+ * Share and Enjoy!    :-)
+ */
+
+#include <stdlib.h>
+#include "fnv.h"
+
+
+/* 
+ * 32 bit magic FNV-0 and FNV-1 prime 
+ */
+#define FNV_32_PRIME ((Fnv32_t)0x01000193)
+
+
+/*
+ * fnv_32_buf - perform a 32 bit Fowler/Noll/Vo hash on a buffer
+ *
+ * input:
+ *     buf     - start of buffer to hash
+ *     len     - length of buffer in octets
+ *     hval    - previous hash value or 0 if first call
+ *
+ * returns:
+ *     32 bit hash as a static hash type
+ *
+ * NOTE: To use the 32 bit FNV-0 historic hash, use FNV0_32_INIT as the hval
+ *      argument on the first call to either fnv_32_buf() or fnv_32_str().
+ *
+ * NOTE: To use the recommended 32 bit FNV-1 hash, use FNV1_32_INIT as the hval
+ *      argument on the first call to either fnv_32_buf() or fnv_32_str().
+ */
+Fnv32_t fnv_32_buf(
+    const void *buf,
+    size_t len,
+    Fnv32_t hval)
+{
+    const unsigned char *bp = (const unsigned char *) buf;  /* start of buffer */
+    const unsigned char *be = bp + len; /* beyond end of buffer */
+
+    /*
+     * FNV-1 hash each octet in the buffer
+     */
+    while (bp < be) {
+
+        /* multiply by the 32 bit FNV magic prime mod 2^64 */
+        hval *= FNV_32_PRIME;
+
+        /* xor the bottom with the current octet */
+        hval ^= (Fnv32_t) *bp++;
+    }
+
+    /* return our new hash value */
+    return hval;
+}
+
+
+/*
+ * fnv_32_str - perform a 32 bit Fowler/Noll/Vo hash on a string
+ *
+ * input:
+ *     str     - string to hash
+ *     hval    - previous hash value or 0 if first call
+ *
+ * returns:
+ *     32 bit hash as a static hash type
+ *
+ * NOTE: To use the 32 bit FNV-0 historic hash, use FNV0_32_INIT as the hval
+ *      argument on the first call to either fnv_32_buf() or fnv_32_str().
+ *
+ * NOTE: To use the recommended 32 bit FNV-1 hash, use FNV1_32_INIT as the hval
+ *      argument on the first call to either fnv_32_buf() or fnv_32_str().
+ */
+Fnv32_t fnv_32_str(
+    const char *str,
+    Fnv32_t hval)
+{
+    const unsigned char *s = (const unsigned char *) str;   /* unsigned string */
+
+    /*
+     * FNV-1 hash each octet in the buffer
+     */
+    while (*s) {
+
+        /* multiply by the 32 bit FNV magic prime mod 2^64 */
+        hval *= FNV_32_PRIME;
+
+        /* xor the bottom with the current octet */
+        hval ^= (Fnv32_t) *s++;
+    }
+
+    /* return our new hash value */
+    return hval;
+}
+
+/* a wrapper function for fnv_32_str */
+unsigned long FnvHash(
+    const char *str)
+{
+    return fnv_32_str(str, FNV1_32_INIT);
+}
diff --git a/program/src/librrd.pc.in b/program/src/librrd.pc.in
new file mode 100644 (file)
index 0000000..927a2b6
--- /dev/null
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: librrd
+Description: Library for the RRDtool
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lrrd
+Cflags: -I${includedir}
diff --git a/program/src/librrd.sym.in.in b/program/src/librrd.sym.in.in
new file mode 100644 (file)
index 0000000..a4b9a08
--- /dev/null
@@ -0,0 +1,67 @@
+rrd_add_ptr
+rrd_add_strdup
+rrd_clear_error
+rrd_close
+rrd_create
+rrd_create_r
+rrd_dontneed
+rrd_dump
+rrd_dump_r
+rrd_dump_cb_r
+rrd_fetch
+rrd_fetch_r
+rrd_flushcached
+rrd_first
+rrd_first_r
+rrd_flush
+rrd_free
+rrd_free_context
+rrd_free_ptrs
+rrd_freemem
+rrd_get_context
+rrd_get_error
+rrd_graph
+rrd_graph_v
+rrd_info
+rrd_info_free
+rrd_info_print
+rrd_info_push
+rrd_info_r
+rrd_init
+rrd_last
+rrd_last_r
+rrd_lastupdate
+rrd_lastupdate_r
+rrd_lock
+rrd_mkdir_p
+rrd_new_context
+rrd_open
+rrd_parsetime
+rrd_proc_start_end
+rrd_random
+rrd_read
+rrd_resize
+rrd_restore
+rrd_seek
+rrd_set_error
+rrd_set_to_DINF
+rrd_set_to_DNAN
+rrd_strerror
+rrd_strversion
+rrd_tell
+rrd_test_error
+rrd_tune
+rrd_update
+rrd_update_r
+rrd_update_v
+rrd_version
+rrd_write
+rrd_xport
+rrdc_connect
+rrdc_is_connected
+rrdc_disconnect
+rrdc_flush
+rrdc_stats_free
+rrdc_stats_get
+rrdc_update
+@RRD_GETOPT_LONG@
diff --git a/program/src/plbasename.c b/program/src/plbasename.c
new file mode 100644 (file)
index 0000000..dd695bb
--- /dev/null
@@ -0,0 +1,211 @@
+#ifdef WIN32
+/*
+ *
+ * Cross-platform basename/dirname 
+ *
+ * Copyright 2005 Syd Logan, All Rights Reserved
+ *
+ * This code is distributed without warranty. You are free to use this
+ * code for any purpose, however, if this code is republished or
+ * redistributed in its original form, as hardcopy or electronically,
+ * then you must include this copyright notice along with the code.
+ *
+ */
+
+// minor changes 2008 by Stefan Ludewig stefan.ludewig@exitgames.com for WIN32 version RRDtool
+
+#include <memory.h>
+#include <stdlib.h>
+#include "plbasename.h"
+#include <string.h>
+#if defined(TEST)
+#include <stdio.h>
+#endif
+
+#if defined(__cplusplus)
+
+extern "C" {
+
+#endif
+
+const char *
+PL_basename(const char *name)
+{
+    const char *base;
+    char *p;
+    static char *tmp = NULL;
+    int len; 
+
+    if (tmp) {
+        free(tmp);
+        tmp = NULL;
+    }
+
+    if (!name || !strcmp(name, ""))
+        return "";
+
+    if (!strcmp(name, "/"))
+        return "/";
+
+    len = strlen(name);
+    if (name[len - 1] == '/') {
+        // ditch the trailing '/'
+        p = tmp = (char*)malloc(len);
+        strncpy(p, name, len - 1); 
+    } else {
+        p = (char *) name;
+    }
+
+    for (base = p; *p; p++) 
+        if (*p == '/') 
+            base = p + 1;
+    
+    return base;
+}
+
+const char *
+PL_dirname(const char *name)
+{
+    static char *ret = NULL;
+    int len;
+    int size = 0;
+    const char *p;
+
+    if (ret) {
+        free(ret);
+        ret = NULL;
+    }
+
+    if (!name || !strcmp(name, "") || !strstr(name, "/"))
+        return(".");
+
+    if (!strcmp(name, "/"))
+        return(name);
+
+    // find the last slash in the string
+
+    len = strlen(name);
+    p = &name[len - 1];
+
+    if (*p == '/') p--;  // skip the trailing /
+
+    while (p != name && *p != '/') p--;
+
+    size = p - name;
+    if (size) {
+        ret = (char*)malloc(size + 1);
+        memcpy(ret, name, size);
+        ret[size] = '\0';
+    } else if (*p == '/')
+        return "/";
+    else
+        return "";
+    
+    return (const char *) ret;
+}
+
+#if defined(__cplusplus)
+
+}
+
+#endif 
+
+#if defined(TEST)
+
+int
+main(int argc, char *argv[])
+{
+/*     run the following tests:
+
+       path           dirname        basename
+       "/usr/lib"     "/usr"         "lib"
+       "/usr/"        "/"            "usr"
+       "usr"          "."            "usr"
+       "/"            "/"            "/"
+       "."            "."            "."
+       ".."           "."            ".."
+       NULL           "."            ""
+       ""             "."            ""
+       "./.."         "."            ".."
+
+      These results can be verified by running the unix commands
+      basename(1) and dirname(1). One tweek to the test strategy
+      used here would be, on darwin and linux, to shell out to 
+      get the expected results vs hardcoding. 
+*/
+    if (!strcmp(PL_basename("/usr/lib"), "lib"))
+        printf("PL_basename /usr/lib passed\n");
+    else
+        printf("PL_basename /usr/lib failed expected lib\n");
+    if (!strcmp(PL_dirname("/usr/lib"), "/usr"))
+        printf("PL_dirname /usr/lib passed\n");
+    else
+        printf("PL_dirname /usr/lib failed expected /usr\n");
+    if (!strcmp(PL_basename("/usr/"), "usr"))
+        printf("PL_basename /usr/ passed\n");
+    else
+        printf("PL_basename /usr/ failed expected usr\n");
+    if (!strcmp(PL_dirname("/usr/"), "/"))
+        printf("PL_dirname /usr/ passed\n");
+    else
+        printf("PL_dirname /usr/ failed expected /\n");
+    if (!strcmp(PL_basename("usr"), "usr"))
+        printf("PL_basename usr passed\n");
+    else
+        printf("PL_basename usr failed expected usr\n");
+    if (!strcmp(PL_dirname("usr"), "."))
+        printf("PL_dirname usr passed\n");
+    else
+        printf("PL_dirname usr failed expected .\n");
+    if (!strcmp(PL_basename("/"), "/"))
+        printf("PL_basename / passed\n");
+    else
+        printf("PL_basename / failed expected /\n");
+    if (!strcmp(PL_dirname("/"), "/"))
+        printf("PL_dirname / passed\n");
+    else
+        printf("PL_dirname / failed expected /\n");
+    if (!strcmp(PL_basename("."), "."))
+        printf("PL_basename . passed\n");
+    else
+        printf("PL_basename . failed\n");
+    if (!strcmp(PL_dirname("."), "."))
+        printf("PL_dirname . passed\n");
+    else
+        printf("PL_dirname . failed expected .\n");
+    if (!strcmp(PL_basename(".."), ".."))
+        printf("PL_basename .. passed\n");
+    else
+        printf("PL_basename .. failed expected  ..\n");
+    if (!strcmp(PL_dirname(".."), "."))
+        printf("PL_dirname .. passed\n");
+    else
+        printf("PL_dirname .. failed expected .\n");
+    if (!strcmp(PL_basename(NULL), ""))
+        printf("PL_basename NULL passed\n");
+    else
+        printf("PL_basename NULL failed expected \"\"\n");
+    if (!strcmp(PL_dirname(NULL), "."))
+        printf("PL_dirname NULL passed\n");
+    else
+        printf("PL_dirname NULL failed expected .\n");
+    if (!strcmp(PL_basename(""), ""))
+        printf("PL_basename \"\" passed\n");
+    else
+        printf("PL_basename \"\" failed expected \"\"\n");
+    if (!strcmp(PL_dirname(""), "."))
+        printf("PL_dirname \"\" passed\n");
+    else
+        printf("PL_dirname \"\" failed expected .\n");
+
+    if (!strcmp(PL_basename("./.."), ".."))
+        printf("PL_basename ./.. passed\n");
+    else
+        printf("PL_basename ./.. failed expected ..\n");
+    if (!strcmp(PL_dirname("./.."), "."))
+        printf("PL_dirname ./.. passed\n");
+    else
+        printf("PL_dirname ./.. failed expected .\n");
+}
+#endif
+#endif // WIN32
diff --git a/program/src/plbasename.h b/program/src/plbasename.h
new file mode 100644 (file)
index 0000000..28786a1
--- /dev/null
@@ -0,0 +1,45 @@
+#ifdef WIN32
+/*
+ *
+ * Cross-platform basename/dirname
+ *
+ * Copyright 2005 Syd Logan, All Rights Reserved
+ *
+ * This code is distributed without warranty. You are free to use this
+ * code for any purpose, however, if this code is republished or
+ * redistributed in its original form, as hardcopy or electronically,
+ * then you must include this copyright notice along with the code.
+ *
+ */
+
+// minor changes 2008 by Stefan Ludewig stefan.ludewig@exitgames.com for WIN32 version RRDtool
+
+#if !defined(__PL_BASENAME_H__)
+#define __PL_BASENAME_H__
+
+/*
+       path           dirname        basename
+       "/usr/lib"     "/usr"         "lib"
+       "/usr/"        "/"            "usr"
+       "usr"          "."            "usr"
+       "/"            "/"            "/"
+       "."            "."            "."
+       ".."           "."            ".."
+*/
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+const char *PL_basename(const char *name);
+const char *PL_dirname(const char *name);
+
+#define basename(name) ((char*)PL_basename(name))
+#define dirname(name) ((char*)PL_dirname(name))
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+#endif // WIN32
diff --git a/program/src/pngsize.c b/program/src/pngsize.c
new file mode 100644 (file)
index 0000000..b937a24
--- /dev/null
@@ -0,0 +1,56 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * pngsize.c  determine the size of a PNG image
+ *****************************************************************************/
+
+#include <png.h>
+#include "rrd_tool.h"
+
+int PngSize(
+    FILE * fd,
+    long *width,
+    long *height)
+{
+    png_structp png_read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+                                                      (png_voidp) NULL,
+                                                      /* we would need to point to error handlers
+                                                         here to do it properly */
+                                                      (png_error_ptr) NULL,
+                                                      (png_error_ptr) NULL);
+
+    png_infop info_ptr = png_create_info_struct(png_read_ptr);
+
+    (*width) = 0;
+    (*height) = 0;
+
+/* this is to make compile on aix work since they seem to define jmpbuf
+   to be _jmpbuf which breaks compilation */
+
+#ifndef png_jmpbuf
+#ifdef PNG_SETJMP_SUPPORTED
+#  define png_jmpbuf(png_ptr)   ((png_ptr)->PNG_jmpbuf)
+#else
+#ifdef jmpbuf
+#undef jmpbuf
+#endif
+#  define png_jmpbuf(png_ptr)   ((png_ptr)->jmpbuf)
+#endif
+#endif
+
+    if (setjmp(png_jmpbuf(png_read_ptr))) {
+        png_destroy_read_struct(&png_read_ptr, &info_ptr, (png_infopp) NULL);
+        return 0;
+    }
+
+    png_init_io(png_read_ptr, fd);
+    png_read_info(png_read_ptr, info_ptr);
+    (*width) = png_get_image_width(png_read_ptr, info_ptr);
+    (*height) = png_get_image_height(png_read_ptr, info_ptr);
+
+    png_destroy_read_struct(&png_read_ptr, &info_ptr, NULL);
+    if (*width > 0 && *height > 0)
+        return 1;
+    else
+        return 0;
+}
diff --git a/program/src/rrd.h b/program/src/rrd.h
new file mode 100644 (file)
index 0000000..31148c8
--- /dev/null
@@ -0,0 +1,429 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrdlib.h   Public header file for librrd
+ *****************************************************************************
+ * $Id$
+ * $Log$
+ * Revision 1.9  2005/02/13 16:13:33  oetiker
+ * let rrd_graph return the actual value range it picked ...
+ * -- Henrik Stoerner <henrik@hswn.dk>
+ *
+ * Revision 1.8  2004/05/26 22:11:12  oetiker
+ * reduce compiler warnings. Many small fixes. -- Mike Slifcak <slif@bellsouth.net>
+ *
+ * Revision 1.7  2003/11/12 22:14:26  oetiker
+ * allow to pass an open filehandle into rrd_graph as an extra argument
+ *
+ * Revision 1.6  2003/11/11 19:46:21  oetiker
+ * replaced time_value with rrd_time_value as MacOS X introduced a struct of that name in their standard headers
+ *
+ * Revision 1.5  2003/04/25 18:35:08  jake
+ * Alternate update interface, updatev. Returns info about CDPs written to disk as result of update. Output format is similar to rrd_info, a hash of key-values.
+ *
+ * Revision 1.4  2003/04/01 22:52:23  jake
+ * Fix Win32 build. VC++ 6.0 and 7.0 now use the thread-safe code.
+ *
+ * Revision 1.3  2003/02/13 07:05:27  oetiker
+ * Find attached the patch I promised to send to you. Please note that there
+ * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c
+ * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This
+ * library is identical to librrd, but it contains support code for per-thread
+ * global variables currently used for error information only. This is similar
+ * to how errno per-thread variables are implemented.  librrd_th must be linked
+ * alongside of libpthred
+ *
+ * There is also a new file "THREADS", holding some documentation.
+ *
+ * -- Peter Stamfest <peter@stamfest.at>
+ *
+ * Revision 1.2  2002/05/07 21:58:32  oetiker
+ * new command rrdtool xport integrated
+ * --  Wolfgang Schrimm <Wolfgang.Schrimm@urz.uni-heidelberg.de>
+ *
+ * Revision 1.1.1.1  2001/02/25 22:25:05  oetiker
+ * checkin
+ *
+ *****************************************************************************/
+#ifdef  __cplusplus
+extern    "C" {
+#endif
+
+#ifndef _RRDLIB_H
+#define _RRDLIB_H
+
+#include <sys/types.h>  /* for off_t */
+
+#ifndef WIN32
+#include <unistd.h>     /* for off_t */
+#else
+       typedef size_t ssize_t;
+       typedef long off_t;
+#endif 
+
+#include <time.h>
+#include <stdio.h>      /* for FILE */
+#include <string.h>
+
+/* Formerly rrd_nan_inf.h */
+#ifndef DNAN
+# define DNAN rrd_set_to_DNAN()
+#endif
+
+#ifndef DINF
+# define DINF rrd_set_to_DINF()
+#endif
+    double    rrd_set_to_DNAN(
+    void);
+    double    rrd_set_to_DINF(
+    void);
+/* end of rrd_nan_inf.h */
+
+/* Transplanted from rrd_format.h */
+    typedef double rrd_value_t; /* the data storage type is
+                                 * double */
+/* END rrd_format.h */
+
+/* information about an rrd file */
+    typedef struct rrd_file_t {
+        size_t     header_len;   /* length of the header of this rrd file */
+        size_t     file_len; /* total size of the rrd file */
+        size_t     pos;  /* current pos in file */
+        void      *pvt;
+    } rrd_file_t;
+
+/* information used for the conventional file access methods */
+    typedef struct rrd_simple_file_t {
+        int       fd;  /* file descriptor of this rrd file */
+#ifdef HAVE_MMAP
+        char     *file_start;   /* start address of an open rrd file */
+        int       mm_prot;
+        int       mm_flags;
+#endif
+    } rrd_simple_file_t;
+
+/* rrd info interface */
+    typedef struct rrd_blob_t {
+        unsigned long size; /* size of the blob */
+        unsigned char *ptr; /* pointer */
+    } rrd_blob_t;
+
+    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 rrd_infoval {
+        unsigned long u_cnt;
+        rrd_value_t u_val;
+        char     *u_str;
+        int       u_int;
+        rrd_blob_t u_blo;
+    } rrd_infoval_t;
+
+    typedef struct rrd_info_t {
+        char     *key;
+        rrd_info_type_t type;
+        rrd_infoval_t value;
+        struct rrd_info_t *next;
+    } rrd_info_t;
+
+    typedef size_t (* rrd_output_callback_t)(
+    const void *,
+    size_t,
+    void *);
+
+/* 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 **,
+    char ***,
+    int *,
+    int *,
+    FILE *,
+    double *,
+    double *);
+    rrd_info_t *rrd_graph_v(
+    int,
+    char **);
+
+    int       rrd_fetch(
+    int,
+    char **,
+    time_t *,
+    time_t *,
+    unsigned long *,
+    unsigned long *,
+    char ***,
+    rrd_value_t **);
+    int       rrd_restore(
+    int,
+    char **);
+    int       rrd_dump(
+    int,
+    char **);
+    int       rrd_tune(
+    int,
+    char **);
+    time_t    rrd_last(
+    int,
+    char **);
+    int rrd_lastupdate(int argc, char **argv);
+    time_t    rrd_first(
+    int,
+    char **);
+    int       rrd_resize(
+    int,
+    char **);
+    char     *rrd_strversion(
+    void);
+    double    rrd_version(
+    void);
+    int       rrd_xport(
+    int,
+    char **,
+    int *,
+    time_t *,
+    time_t *,
+    unsigned long *,
+    unsigned long *,
+    char ***,
+    rrd_value_t **);
+    int       rrd_flushcached (int argc, char **argv);
+
+    void      rrd_freemem(
+    void *mem);
+
+/* thread-safe (hopefully) */
+    int       rrd_create_r(
+    const char *filename,
+    unsigned long pdp_step,
+    time_t last_up,
+    int argc,
+    const char **argv);
+    rrd_info_t *rrd_info_r(
+    char *);
+/* NOTE: rrd_update_r are only thread-safe if no at-style time
+   specifications get used!!! */
+
+    int       rrd_update_r(
+    const char *filename,
+    const char *_template,
+    int argc,
+    const char **argv);
+    int rrd_fetch_r (
+            const char *filename,
+            const char *cf,
+            time_t *start,
+            time_t *end,
+            unsigned long *step,
+            unsigned long *ds_cnt,
+            char ***ds_namv,
+            rrd_value_t **data);
+    int       rrd_dump_r(
+    const char *filename,
+    char *outname);
+    time_t    rrd_last_r (const char *filename);
+    int rrd_lastupdate_r (const char *filename,
+            time_t *ret_last_update,
+            unsigned long *ret_ds_count,
+            char ***ret_ds_names,
+            char ***ret_last_ds);
+    time_t    rrd_first_r(
+    const char *filename,
+    int rraindex);
+
+    int rrd_dump_cb_r(
+    const char *filename,
+    int opt_header,
+    rrd_output_callback_t cb,
+    void *user);
+
+/* Transplanted from rrd_parsetime.h */
+    typedef enum {
+        ABSOLUTE_TIME,
+        RELATIVE_TO_START_TIME,
+        RELATIVE_TO_END_TIME
+    } rrd_timetype_t;
+
+#define TIME_OK NULL
+
+    typedef struct rrd_time_value {
+        rrd_timetype_t type;
+        long      offset;
+        struct tm tm;
+    } rrd_time_value_t;
+
+    char     *rrd_parsetime(
+    const char *spec,
+    rrd_time_value_t * ptv);
+/* END rrd_parsetime.h */
+
+    typedef struct rrd_context {
+        char      lib_errstr[256];
+        char      rrd_error[4096];
+    } rrd_context_t;
+
+/* returns the current per-thread rrd_context */
+    rrd_context_t *rrd_get_context(void);
+
+#ifdef WIN32
+/* this was added by the win32 porters Christof.Wegmann@exitgames.com */
+    rrd_context_t *rrd_force_new_context(void);
+#endif
+
+int       rrd_proc_start_end(
+    rrd_time_value_t *,
+    rrd_time_value_t *,
+    time_t *,
+    time_t *);
+
+/* HELPER FUNCTIONS */
+    void      rrd_set_error(
+    char *,
+    ...);
+    void      rrd_clear_error(
+    void);
+    int       rrd_test_error(
+    void);
+    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 */
+    rrd_context_t *rrd_new_context(
+    void);
+    void      rrd_free_context(
+    rrd_context_t * buf);
+
+/* 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 *); */
+
+/** UTILITY FUNCTIONS */
+
+    long rrd_random(void);
+
+    int rrd_add_ptr(void ***dest, size_t *dest_size, void *src);
+    int rrd_add_strdup(char ***dest, size_t *dest_size, char *src);
+    void rrd_free_ptrs(void ***src, size_t *cnt);
+
+    int rrd_mkdir_p(const char *pathname, mode_t mode);
+
+/*
+ * The following functions are _internal_ functions needed to read the raw RRD
+ * files. Since they are _internal_ they may change with the file format and
+ * will be replaced with a more general interface in RRDTool 1.4. Don't use
+ * these functions unless you have good reasons to do so. If you do use these
+ * functions you will have to adapt your code for RRDTool 1.4!
+ *
+ * To enable the deprecated functions define `RRD_EXPORT_DEPRECATED' before
+ * including <rrd_test.h>. You have been warned! If you come back to the
+ * RRDTool mailing list and whine about your broken application, you will get
+ * hit with something smelly!
+ */
+#if defined(_RRD_TOOL_H) || defined(RRD_EXPORT_DEPRECATED)
+
+# if defined(_RRD_TOOL_H)
+#  include "rrd_format.h"
+# else
+#  include <rrd_format.h>
+# endif
+
+#if defined(__GNUC__) && defined (RRD_EXPORT_DEPRECATED)
+# define RRD_DEPRECATED __attribute__((deprecated))
+#else
+# define RRD_DEPRECATED          /**/
+#endif
+     void     rrd_free(
+    rrd_t *rrd)
+              RRD_DEPRECATED;
+    void      rrd_init(
+    rrd_t *rrd)
+              RRD_DEPRECATED;
+
+    rrd_file_t *rrd_open(
+    const char *const file_name,
+    rrd_t *rrd,
+    unsigned rdwr)
+              RRD_DEPRECATED;
+
+    void      rrd_dontneed(
+    rrd_file_t *rrd_file,
+    rrd_t *rrd)
+              RRD_DEPRECATED;
+    int       rrd_close(
+    rrd_file_t *rrd_file)
+              RRD_DEPRECATED;
+    ssize_t   rrd_read(
+    rrd_file_t *rrd_file,
+    void *buf,
+    size_t count)
+              RRD_DEPRECATED;
+    ssize_t   rrd_write(
+    rrd_file_t *rrd_file,
+    const void *buf,
+    size_t count)
+              RRD_DEPRECATED;
+    void      rrd_flush(
+    rrd_file_t *rrd_file)
+              RRD_DEPRECATED;
+    off_t     rrd_seek(
+    rrd_file_t *rrd_file,
+    off_t off,
+    int whence)
+              RRD_DEPRECATED;
+    off_t     rrd_tell(
+    rrd_file_t *rrd_file)
+              RRD_DEPRECATED;
+    int       rrd_lock(
+    rrd_file_t *file)
+              RRD_DEPRECATED;
+    void      rrd_notify_row(
+    rrd_file_t *rrd_file,
+    int rra_idx,
+    unsigned long rra_row,
+    time_t rra_time)
+              RRD_DEPRECATED;
+    unsigned long rrd_select_initial_row(
+    rrd_file_t *rrd_file,
+    int rra_idx,
+    rra_def_t *rra
+    )
+              RRD_DEPRECATED;
+#endif                  /* defined(_RRD_TOOL_H) || defined(RRD_EXPORT_DEPRECATED) */
+
+#endif                  /* _RRDLIB_H */
+
+#ifdef  __cplusplus
+}
+#endif
diff --git a/program/src/rrd_cgi.c b/program/src/rrd_cgi.c
new file mode 100644 (file)
index 0000000..8ce7fba
--- /dev/null
@@ -0,0 +1,1636 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_cgi.c  RRD Web Page Generator
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#define MEMBLK 1024
+/*#define DEBUG_PARSER
+#define DEBUG_VARS*/
+
+typedef struct var_s {
+    char     *name, *value;
+} s_var;
+
+typedef struct cgi_s {
+    s_var   **vars;
+} s_cgi;
+
+/* in arg[0] find tags beginning with arg[1] call arg[2] on them
+   and replace by result of arg[2] call */
+int       parse(
+    char **,
+    long,
+    char *,
+    char *    (*)(long,
+                  const char **));
+
+/**************************************************/
+/* tag replacers ... they are called from parse   */
+/* through function pointers                      */
+/**************************************************/
+
+/* return cgi var named arg[0] */
+char     *cgiget(
+    long,
+    const char **);
+
+/* return a quoted cgi var named arg[0] */
+char     *cgigetq(
+    long,
+    const char **);
+
+/* return a quoted and sanitized cgi variable */
+char     *cgigetqp(
+    long,
+    const char **);
+
+/* call rrd_graph and insert appropriate image tag */
+char     *drawgraph(
+    long,
+    const char **);
+
+/* return PRINT functions from last rrd_graph call */
+char     *drawprint(
+    long,
+    const char **);
+
+/* pretty-print the <last></last> value for some.rrd via strftime() */
+char     *printtimelast(
+    long,
+    const char **);
+
+/* pretty-print current time */
+char     *printtimenow(
+    long,
+    const char **);
+
+/* set an environment variable */
+char     *rrdsetenv(
+    long,
+    const char **);
+
+/* get an environment variable */
+char     *rrdgetenv(
+    long,
+    const char **);
+
+/* include the named file at this point */
+char     *includefile(
+    long,
+    const char **);
+
+/* for how long is the output of the cgi valid ? */
+char     *rrdgoodfor(
+    long,
+    const char **);
+
+/* return rrdcgi version string */
+char     *rrdgetinternal(
+    long,
+    const char **);
+
+char     *rrdstrip(
+    char *buf);
+char     *scanargs(
+    char *line,
+    int *argc,
+    char ***args);
+
+/* format at-time specified times using strftime */
+char     *printstrftime(
+    long,
+    const char **);
+
+/** HTTP protocol needs special format, and GMT time **/
+char     *http_time(
+    time_t *);
+
+/* return a pointer to newly allocated copy of this string */
+char     *stralloc(
+    const char *);
+
+/* global variable for rrdcgi */
+s_cgi    *rrdcgiArg;
+
+/* rrdcgiHeader
+ * 
+ *  Prints a valid CGI Header (Content-type...) etc.
+ */
+void      rrdcgiHeader(
+    void);
+
+/* rrdcgiDecodeString
+ * decode html escapes
+ */
+
+char     *rrdcgiDecodeString(
+    char *text);
+
+/* rrdcgiDebug
+ * 
+ *  Set/unsets debugging
+ */
+void      rrdcgiDebug(
+    int level,
+    int where);
+
+/* rrdcgiInit
+ *
+ *  Reads in variables set via POST or stdin.
+ */
+s_cgi    *rrdcgiInit(
+    void);
+
+/* rrdcgiGetValue
+ *
+ *  Returns the value of the specified variable or NULL if it's empty
+ *  or doesn't exist.
+ */
+char     *rrdcgiGetValue(
+    s_cgi * parms,
+    const char *name);
+
+/* rrdcgiFreeList
+ *
+ * Frees a list as returned by rrdcgiGetVariables()
+ */
+void      rrdcgiFreeList(
+    char **list);
+
+/* rrdcgiFree
+ *
+ * Frees the internal data structures
+ */
+void      rrdcgiFree(
+    s_cgi * parms);
+
+/*  rrdcgiReadVariables()
+ *
+ *  Read from stdin if no string is provided via CGI.  Variables that
+ *  doesn't have a value associated with it doesn't get stored.
+ */
+s_var   **rrdcgiReadVariables(
+    void);
+
+
+int       rrdcgiDebugLevel = 0;
+int       rrdcgiDebugStderr = 1;
+char     *rrdcgiHeaderString = NULL;
+char     *rrdcgiType = NULL;
+
+/* rrd interface to the variable functions {put,get}var() */
+char     *rrdgetvar(
+    long argc,
+    const char **args);
+char     *rrdsetvar(
+    long argc,
+    const char **args);
+char     *rrdsetvarconst(
+    long argc,
+    const char **args);
+
+
+/* variable store: put/get key-value pairs */
+static int initvar(
+    );
+static void donevar(
+    );
+static const char *getvar(
+    const char *varname);
+static const char *putvar(
+    const char *name,
+    const char *value,
+    int is_const);
+
+/* key value pair that makes up an entry in the variable store */
+typedef struct {
+    int       is_const; /* const variable or not */
+    const char *name;   /* variable name */
+    const char *value;  /* variable value */
+} vardata;
+
+/* the variable heap: 
+   start with a heapsize of 10 variables */
+#define INIT_VARSTORE_SIZE     10
+static vardata *varheap = NULL;
+static size_t varheap_size = 0;
+
+/* allocate and initialize variable heap */
+static int initvar(
+    void)
+{
+    varheap = (vardata *) malloc(sizeof(vardata) * INIT_VARSTORE_SIZE);
+    if (varheap == NULL) {
+        fprintf(stderr, "ERROR: unable to initialize variable store\n");
+        return -1;
+    }
+    memset(varheap, 0, sizeof(vardata) * INIT_VARSTORE_SIZE);
+    varheap_size = INIT_VARSTORE_SIZE;
+    return 0;
+}
+
+/* cleanup: free allocated memory */
+static void donevar(
+    void)
+{
+    int       i;
+
+    if (varheap) {
+        for (i = 0; i < (int) varheap_size; i++) {
+            if (varheap[i].name) {
+                free((char *) varheap[i].name);
+            }
+            if (varheap[i].value) {
+                free((char *) varheap[i].value);
+            }
+        }
+        free(varheap);
+    }
+}
+
+/* Get a variable from the variable store.
+   Return NULL in case the requested variable was not found. */
+static const char *getvar(
+    const char *name)
+{
+    int       i;
+
+    for (i = 0; i < (int) varheap_size && varheap[i].name; i++) {
+        if (0 == strcmp(name, varheap[i].name)) {
+#ifdef         DEBUG_VARS
+            printf("<!-- getvar(%s) -> %s -->\n", name, varheap[i].value);
+#endif
+            return varheap[i].value;
+        }
+    }
+#ifdef DEBUG_VARS
+    printf("<!-- getvar(%s) -> Not found-->\n", name);
+#endif
+    return NULL;
+}
+
+/* Put a variable into the variable store. If a variable by that
+   name exists, it's value is overwritten with the new value unless it was
+   marked as 'const' (initialized by RRD::SETCONSTVAR).
+   Returns a copy the newly allocated value on success, NULL on error. */
+static const char *putvar(
+    const char *name,
+    const char *value,
+    int is_const)
+{
+    int       i;
+
+    for (i = 0; i < (int) varheap_size && varheap[i].name; i++) {
+        if (0 == strcmp(name, varheap[i].name)) {
+            /* overwrite existing entry */
+            if (varheap[i].is_const) {
+#ifdef DEBUG_VARS
+                printf("<!-- setver(%s, %s): not assigning: "
+                       "const variable -->\n", name, value);
+#endif
+                return varheap[i].value;
+            }
+#ifdef DEBUG_VARS
+            printf("<!-- setvar(%s, %s): overwriting old value (%s) -->\n",
+                   name, value, varheap[i].value);
+#endif
+            /* make it possible to promote a variable to readonly */
+            varheap[i].is_const = is_const;
+            free((char *) varheap[i].value);
+            varheap[i].value = stralloc(value);
+            return varheap[i].value;
+        }
+    }
+
+    /* no existing variable found by that name, add it */
+    if (i == (int) varheap_size) {
+        /* ran out of heap: resize heap to double size */
+        size_t    new_size = varheap_size * 2;
+
+        varheap = (vardata *) (realloc(varheap, sizeof(vardata) * new_size));
+        if (!varheap) {
+            fprintf(stderr, "ERROR: Unable to realloc variable heap\n");
+            return NULL;
+        }
+        /* initialize newly allocated memory */ ;
+        memset(&varheap[varheap_size], 0, sizeof(vardata) * varheap_size);
+        varheap_size = new_size;
+    }
+    varheap[i].is_const = is_const;
+    varheap[i].name = stralloc(name);
+    varheap[i].value = stralloc(value);
+
+#ifdef         DEBUG_VARS
+    printf("<!-- setvar(%s, %s): adding new variable -->\n", name, value);
+#endif
+    return varheap[i].value;
+}
+
+/* expand those RRD:* directives that can be used recursivly */
+static char *rrd_expand_vars(
+    char *buffer)
+{
+    int       i;
+
+#ifdef DEBUG_PARSER
+    printf("expanding variables in '%s'\n", buffer);
+#endif
+
+    for (i = 0; buffer[i]; i++) {
+        if (buffer[i] != '<')
+            continue;
+        parse(&buffer, i, "<RRD::CV", cgiget);
+        parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
+        parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
+        parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
+        parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
+        parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
+        parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
+        parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
+        parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
+    }
+    return buffer;
+}
+
+static long goodfor = 0;
+static char **calcpr = NULL;
+static void calfree(
+    void)
+{
+    if (calcpr) {
+        long      i;
+
+        for (i = 0; calcpr[i]; i++) {
+            if (calcpr[i]) {
+                free(calcpr[i]);
+            }
+        }
+        if (calcpr) {
+            free(calcpr);
+        }
+        calcpr = NULL;
+    }
+}
+
+/* create freeable version of the string */
+char     *stralloc(
+    const char *str)
+{
+    char     *nstr;
+
+    if (!str) {
+        return NULL;
+    }
+    nstr = malloc((strlen(str) + 1));
+    strcpy(nstr, str);
+    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[])
+{
+    long      length;
+    char     *buffer;
+    char     *server_url = NULL;
+    long      i;
+    long      filter = 0;
+    struct option long_options[] = {
+        {"filter", no_argument, 0, 'f'},
+        {0, 0, 0, 0}
+    };
+
+#ifdef MUST_DISABLE_SIGFPE
+    signal(SIGFPE, SIG_IGN);
+#endif
+#ifdef MUST_DISABLE_FPMASK
+    fpsetmask(0);
+#endif
+    optind = 0;
+    opterr = 0;         /* initialize getopt */
+
+    /* what do we get for cmdline arguments?
+       for (i=0;i<argc;i++)
+       printf("%d-'%s'\n",i,argv[i]); */
+    while (1) {
+        int       option_index = 0;
+        int       opt;
+
+        opt = getopt_long(argc, argv, "f", long_options, &option_index);
+        if (opt == EOF) {
+            break;
+        }
+
+        switch (opt) {
+        case 'f':
+            filter = 1;
+            break;
+        case '?':
+            printf("unknown commandline option '%s'\n", argv[optind - 1]);
+            return -1;
+        }
+    }
+
+    if (!filter) {
+        rrdcgiDebug(0, 0);
+        rrdcgiArg = rrdcgiInit();
+        server_url = getenv("SERVER_URL");
+    }
+
+    /* make sure we have one extra argument, 
+       if there are others, we do not care Apache gives several */
+
+    /* if ( (optind != argc-2 
+       && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL) 
+       && optind != argc-1) { */
+
+    if (optind >= argc) {
+        fprintf(stderr, "ERROR: expected a filename\n");
+        exit(1);
+    } else {
+        length = readfile(argv[optind], &buffer, 1);
+    }
+
+    if (rrd_test_error()) {
+        fprintf(stderr, "ERROR: %s\n", rrd_get_error());
+        exit(1);
+    }
+
+    /* initialize variable heap */
+    initvar();
+
+#ifdef DEBUG_PARSER
+    /* some fake header for testing */
+    printf("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
+#endif
+
+
+    /* expand rrd directives in buffer recursivly */
+    for (i = 0; buffer[i]; i++) {
+        if (buffer[i] != '<')
+            continue;
+        if (!filter) {
+            parse(&buffer, i, "<RRD::CV", cgiget);
+            parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
+            parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
+            parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
+        }
+        parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
+        parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
+        parse(&buffer, i, "<RRD::GRAPH", drawgraph);
+        parse(&buffer, i, "<RRD::INCLUDE", includefile);
+        parse(&buffer, i, "<RRD::PRINT", drawprint);
+        parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
+        parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
+        parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
+        parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
+        parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
+        parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
+        parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
+    }
+
+    if (!filter) {
+        printf("Content-Type: text/html\n"
+               "Content-Length: %zd\n", strlen(buffer));
+
+        if (labs(goodfor) > 0) {
+            time_t    now;
+
+            now = time(NULL);
+            printf("Last-Modified: %s\n", http_time(&now));
+            now += labs(goodfor);
+            printf("Expires: %s\n", http_time(&now));
+            if (goodfor < 0) {
+                printf("Refresh: %ld\n", labs(goodfor));
+            }
+        }
+        printf("\n");
+    }
+
+    /* output result */
+    printf("%s", buffer);
+
+    /* cleanup */
+    calfree();
+    if (buffer) {
+        free(buffer);
+    }
+    donevar();
+    exit(0);
+}
+
+/* remove occurrences of .. this is a general measure to make
+   paths which came in via cgi do not go UP ... */
+
+char     *rrdsetenv(
+    long argc,
+    const char **args)
+{
+    if (argc >= 2) {
+        char     *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
+
+        if (xyz == NULL) {
+            return stralloc("[ERROR: allocating setenv buffer]");
+        };
+        sprintf(xyz, "%s=%s", args[0], args[1]);
+        if (putenv(xyz) == -1) {
+            free(xyz);
+            return stralloc("[ERROR: failed to do putenv]");
+        };
+        return stralloc("");
+    }
+    return stralloc("[ERROR: setenv failed because not enough "
+                    "arguments were defined]");
+}
+
+/* rrd interface to the variable function putvar() */
+char     *rrdsetvar(
+    long argc,
+    const char **args)
+{
+    if (argc >= 2) {
+        const char *result = putvar(args[0], args[1], 0 /* not const */ );
+
+        if (result) {
+            /* setvar does not return the value set */
+            return stralloc("");
+        }
+        return stralloc("[ERROR: putvar failed]");
+    }
+    return stralloc("[ERROR: putvar failed because not enough arguments "
+                    "were defined]");
+}
+
+/* rrd interface to the variable function putvar() */
+char     *rrdsetvarconst(
+    long argc,
+    const char **args)
+{
+    if (argc >= 2) {
+        const char *result = putvar(args[0], args[1], 1 /* const */ );
+
+        if (result) {
+            /* setvar does not return the value set */
+            return stralloc("");
+        }
+        return stralloc("[ERROR: putvar failed]");
+    }
+    return stralloc("[ERROR: putvar failed because not enough arguments "
+                    "were defined]");
+}
+
+char     *rrdgetenv(
+    long argc,
+    const char **args)
+{
+    char      buf[128];
+    const char *envvar;
+
+    if (argc != 1) {
+        return stralloc("[ERROR: getenv failed because it did not "
+                        "get 1 argument only]");
+    };
+    envvar = getenv(args[0]);
+    if (envvar) {
+        return stralloc(envvar);
+    } else {
+        snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
+        return stralloc(buf);
+    }
+}
+
+char     *rrdgetvar(
+    long argc,
+    const char **args)
+{
+    char      buf[128];
+    const char *value;
+
+    if (argc != 1) {
+        return stralloc("[ERROR: getvar failed because it did not "
+                        "get 1 argument only]");
+    };
+    value = getvar(args[0]);
+    if (value) {
+        return stralloc(value);
+    } else {
+        snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
+        return stralloc(buf);
+    }
+}
+
+char     *rrdgoodfor(
+    long argc,
+    const char **args)
+{
+    if (argc == 1) {
+        goodfor = atol(args[0]);
+    } else {
+        return stralloc("[ERROR: goodfor expected 1 argument]");
+    }
+
+    if (goodfor == 0) {
+        return stralloc("[ERROR: goodfor value must not be 0]");
+    }
+
+    return stralloc("");
+}
+
+char     *rrdgetinternal(
+    long argc,
+    const char **args)
+{
+    if (argc == 1) {
+        if (strcasecmp(args[0], "VERSION") == 0) {
+            return stralloc(PACKAGE_VERSION);
+        } else if (strcasecmp(args[0], "COMPILETIME") == 0) {
+            return stralloc(__DATE__ " " __TIME__);
+        } else {
+            return stralloc("[ERROR: internal unknown argument]");
+        }
+    } else {
+        return stralloc("[ERROR: internal expected 1 argument]");
+    }
+}
+
+/* Format start or end times using strftime.  We always need both the
+ * start and end times, because, either might be relative to the other.
+ * */
+#define MAX_STRFTIME_SIZE 256
+char     *printstrftime(
+    long argc,
+    const char **args)
+{
+    rrd_time_value_t start_tv, end_tv;
+    char     *parsetime_error = NULL;
+    char      formatted[MAX_STRFTIME_SIZE];
+    struct tm *the_tm;
+    time_t    start_tmp, end_tmp;
+
+    /* Make sure that we were given the right number of args */
+    if (argc != 4) {
+        rrd_set_error("wrong number of args %d", argc);
+        return stralloc("");
+    }
+
+    /* Init start and end time */
+    rrd_parsetime("end-24h", &start_tv);
+    rrd_parsetime("now", &end_tv);
+
+    /* Parse the start and end times we were given */
+    if ((parsetime_error = rrd_parsetime(args[1], &start_tv))) {
+        rrd_set_error("start time: %s", parsetime_error);
+        return stralloc("");
+    }
+    if ((parsetime_error = rrd_parsetime(args[2], &end_tv))) {
+        rrd_set_error("end time: %s", parsetime_error);
+        return stralloc("");
+    }
+    if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+        return stralloc("");
+    }
+
+    /* Do we do the start or end */
+    if (strcasecmp(args[0], "START") == 0) {
+        the_tm = localtime(&start_tmp);
+    } else if (strcasecmp(args[0], "END") == 0) {
+        the_tm = localtime(&end_tmp);
+    } else {
+        rrd_set_error("start/end not found in '%s'", args[0]);
+        return stralloc("");
+    }
+
+    /* now format it */
+    if (strftime(formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
+        return (stralloc(formatted));
+    } else {
+        rrd_set_error("strftime failed");
+        return stralloc("");
+    }
+}
+
+char     *includefile(
+    long argc,
+    const char **args)
+{
+    char     *buffer;
+
+    if (argc >= 1) {
+        const char *filename = args[0];
+
+        readfile(filename, &buffer, 0);
+        if (rrd_test_error()) {
+            char     *err = malloc((strlen(rrd_get_error()) + DS_NAM_SIZE));
+
+            sprintf(err, "[ERROR: %s]", rrd_get_error());
+            rrd_clear_error();
+            return err;
+        } else {
+            return buffer;
+        }
+    } else {
+        return stralloc("[ERROR: No Inclue file defined]");
+    }
+}
+
+/* make a copy of buf and replace open/close brackets with '_' */
+char     *rrdstrip(
+    char *buf)
+{
+    char     *p;
+
+    if (buf == NULL) {
+        return NULL;
+    }
+    /* make a copy of the buffer */
+    buf = stralloc(buf);
+    if (buf == NULL) {
+        return NULL;
+    }
+
+    p = buf;
+    while (*p) {
+        if (*p == '<' || *p == '>') {
+            *p = '_';
+        }
+        p++;
+    }
+    return buf;
+}
+
+char     *cgigetq(
+    long argc,
+    const char **args)
+{
+    if (argc >= 1) {
+        char     *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
+        char     *buf2;
+        char     *c, *d;
+        int       qc = 0;
+
+        if (buf == NULL)
+            return NULL;
+
+        for (c = buf; *c != '\0'; c++)
+            if (*c == '"')
+                qc++;
+        if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
+            perror("Malloc Buffer");
+            exit(1);
+        };
+        c = buf;
+        d = buf2;
+        *(d++) = '"';
+        while (*c != '\0') {
+            if (*c == '"') {
+                *(d++) = '"';
+                *(d++) = '\'';
+                *(d++) = '"';
+                *(d++) = '\'';
+            }
+            *(d++) = *(c++);
+        }
+        *(d++) = '"';
+        *(d) = '\0';
+        free(buf);
+        return buf2;
+    }
+
+    return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
+}
+
+/* remove occurrences of .. this is a general measure to make
+   paths which came in via cgi do not go UP ... */
+
+char     *cgigetqp(
+    long argc,
+    const char **args)
+{
+    char     *buf;
+    char     *buf2;
+    char     *p;
+    char     *d;
+
+    if (argc < 1) {
+        return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
+    }
+
+    buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
+    if (!buf) {
+        return NULL;
+    }
+
+    buf2 = malloc(strlen(buf) + 1);
+    if (!buf2) {
+        perror("cgigetqp(): Malloc Path Buffer");
+        exit(1);
+    };
+
+    p = buf;
+    d = buf2;
+
+    while (*p) {
+        /* prevent mallicious paths from entering the system */
+        if (p[0] == '.' && p[1] == '.') {
+            p += 2;
+            *d++ = '_';
+            *d++ = '_';
+        } else {
+            *d++ = *p++;
+        }
+    }
+
+    *d = 0;
+    free(buf);
+
+    /* Make sure the path is relative, e.g. does not start with '/' */
+    p = buf2;
+    while ('/' == *p) {
+        *p++ = '_';
+    }
+
+    return buf2;
+}
+
+
+char     *cgiget(
+    long argc,
+    const char **args)
+{
+    if (argc >= 1)
+        return rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
+    else
+        return stralloc("[ERROR: not enough arguments for RRD::CV]");
+}
+
+
+
+char     *drawgraph(
+    long argc,
+    const char **args)
+{
+    int       i, xsize, ysize;
+    double    ymin, ymax;
+
+    for (i = 0; i < argc; i++)
+        if (strcmp(args[i], "--imginfo") == 0 || strcmp(args[i], "-g") == 0)
+            break;
+    if (i == argc) {
+        args[argc++] = "--imginfo";
+        args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
+    }
+    calfree();
+    if (rrd_graph
+        (argc + 1, (char **) args - 1, &calcpr, &xsize, &ysize, NULL, &ymin,
+         &ymax) != -1) {
+        return stralloc(calcpr[0]);
+    } else {
+        if (rrd_test_error()) {
+            char     *err =
+                malloc((strlen(rrd_get_error()) +
+                        DS_NAM_SIZE) * sizeof(char));
+            sprintf(err, "[ERROR: %s]", rrd_get_error());
+            rrd_clear_error();
+            return err;
+        }
+    }
+    return NULL;
+}
+
+char     *drawprint(
+    long argc,
+    const char **args)
+{
+    if (argc == 1 && calcpr) {
+        long      i = 0;
+
+        while (calcpr[i] != NULL)
+            i++;        /*determine number lines in calcpr */
+        if (atol(args[0]) < i - 1)
+            return stralloc(calcpr[atol(args[0]) + 1]);
+    }
+    return stralloc("[ERROR: RRD::PRINT argument error]");
+}
+
+char     *printtimelast(
+    long argc,
+    const char **args)
+{
+    time_t    last;
+    struct tm tm_last;
+    char     *buf;
+
+    if (argc == 2) {
+        buf = malloc(255);
+        if (buf == NULL) {
+            return stralloc("[ERROR: allocating strftime buffer]");
+        };
+        /* not raising argc in step with args - 1 since the last argument
+           will be used below for strftime  */
+
+        last = rrd_last(argc, (char **) args - 1);
+        if (rrd_test_error()) {
+            char     *err =
+                malloc((strlen(rrd_get_error()) +
+                        DS_NAM_SIZE) * sizeof(char));
+            sprintf(err, "[ERROR: %s]", rrd_get_error());
+            rrd_clear_error();
+            return err;
+        }
+        tm_last = *localtime(&last);
+        strftime(buf, 254, args[1], &tm_last);
+        return buf;
+    }
+    return stralloc("[ERROR: expected <RRD::TIME::LAST file.rrd strftime-format>]");
+}
+
+char     *printtimenow(
+    long argc,
+    const char **args)
+{
+    time_t    now = time(NULL);
+    struct tm tm_now;
+    char     *buf;
+
+    if (argc == 1) {
+        buf = malloc(255);
+        if (buf == NULL) {
+            return stralloc("[ERROR: allocating strftime buffer]");
+        };
+        tm_now = *localtime(&now);
+        strftime(buf, 254, args[0], &tm_now);
+        return buf;
+    }
+    if (argc < 1) {
+        return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
+    }
+    return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
+}
+
+/* Scan buffer until an unescaped '>' arives.
+ * Update argument array with arguments found.
+ * Return end cursor where parsing stopped, or NULL in case of failure.
+ *
+ * FIXME:
+ * To allow nested constructs, we call rrd_expand_vars() for arguments
+ * that contain RRD::x directives. These introduce a small memory leak
+ * since we have to stralloc the arguments the way parse() works.
+ */
+char     *scanargs(
+    char *line,
+    int *argument_count,
+    char ***arguments)
+{
+    char     *getP;     /* read cursor */
+    char     *putP;     /* write cursor */
+    char      Quote;    /* type of quote if in quoted string, 0 otherwise */
+    int       tagcount; /* open tag count */
+    int       in_arg;   /* if we currently are parsing an argument or not */
+    int       argsz;    /* argument array size */
+    int       curarg_contains_rrd_directives;
+
+    /* local array of arguments while parsing */
+    int       argc = 1;
+    char    **argv;
+
+#ifdef DEBUG_PARSER
+    printf("<-- scanargs(%s) -->\n", line);
+#endif
+
+    *arguments = NULL;
+    *argument_count = 0;
+
+    /* create initial argument array of char pointers */
+    argsz = 32;
+    argv = (char **) malloc(argsz * sizeof(char *));
+    if (!argv) {
+        return NULL;
+    }
+    argv[0] = "rrdcgi";
+
+    /* skip leading blanks */
+    while (isspace((int) *line)) {
+        line++;
+    }
+
+    getP = line;
+    putP = line;
+
+    Quote = 0;
+    in_arg = 0;
+    tagcount = 0;
+
+    curarg_contains_rrd_directives = 0;
+
+    /* start parsing 'line' for arguments */
+    while (*getP) {
+        unsigned char c = *getP++;
+
+        if (c == '>' && !Quote && !tagcount) {
+            /* this is our closing tag, quit scanning */
+            break;
+        }
+
+        /* remove all special chars */
+        if (c < ' ') {
+            c = ' ';
+        }
+
+        switch (c) {
+        case ' ':
+            if (Quote || tagcount) {
+                /* copy quoted/tagged (=RRD expanded) string */
+                *putP++ = c;
+            } else if (in_arg) {
+                /* end argument string */
+                *putP++ = 0;
+                in_arg = 0;
+                if (curarg_contains_rrd_directives) {
+                    argv[argc - 1] =
+                        rrd_expand_vars(stralloc(argv[argc - 1]));
+                    curarg_contains_rrd_directives = 0;
+                }
+            }
+            break;
+
+        case '"':      /* Fall through */
+        case '\'':
+            if (Quote != 0) {
+                if (Quote == c) {
+                    Quote = 0;
+                } else {
+                    /* copy quoted string */
+                    *putP++ = c;
+                }
+            } else {
+                if (!in_arg) {
+                    /* reference start of argument string in argument array */
+                    argv[argc++] = putP;
+                    in_arg = 1;
+                }
+                Quote = c;
+            }
+            break;
+
+        default:
+            if (!in_arg) {
+                /* start new argument */
+                argv[argc++] = putP;
+                in_arg = 1;
+            }
+            if (c == '>') {
+                if (tagcount) {
+                    tagcount--;
+                }
+            }
+            if (c == '<') {
+                tagcount++;
+                if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
+                    curarg_contains_rrd_directives = 1;
+                }
+            }
+            *putP++ = c;
+            break;
+        }
+
+        /* check if our argument array is still large enough */
+        if (argc == argsz) {
+            /* resize argument array */
+            argsz *= 2;
+            argv = rrd_realloc(argv, argsz * sizeof(char *));
+            if (*argv == NULL) {
+                return NULL;
+            }
+        }
+    }
+
+    /* terminate last argument found */
+    *putP = '\0';
+    if (curarg_contains_rrd_directives) {
+        argv[argc - 1] = rrd_expand_vars(stralloc(argv[argc - 1]));
+    }
+#ifdef DEBUG_PARSER
+    if (argc > 1) {
+        int       n;
+
+        printf("<-- arguments found [%d]\n", argc);
+        for (n = 0; n < argc; n++) {
+            printf("arg %02d: '%s'\n", n, argv[n]);
+        }
+        printf("-->\n");
+    } else {
+        printf("<!-- No arguments found -->\n");
+    }
+#endif
+
+    /* update caller's notion of the argument array and it's size */
+
+    /* note this is a bit of a hack since the rrd_cgi code used to just put
+       its arguments into a normal array starting at 0 ... since the rrd_*
+       commands expect and argc/argv array we used to just shift everything
+       by -1 ... this in turn exploded when a rrd_* function tried to print
+       argv[0] ... hence we are now doing everything in argv style but hand
+       over seemingly the old array ... but doing argv-1 will actually end
+       up in a 'good' place now. */
+
+    *arguments = argv+1;
+    *argument_count = argc-1;
+
+    if (Quote) {
+        return NULL;
+    }
+
+    /* Return new scanning cursor:
+       pointer to char after closing bracket */
+    return getP;
+}
+
+
+/*
+ * Parse(): scan current portion of buffer for given tag.
+ * If found, parse tag arguments and call 'func' for it.
+ * The result of func is inserted at the current position
+ * in the buffer.
+ */
+int parse(
+    char **buf,         /* buffer */
+    long i,             /* offset in buffer */
+    char *tag,          /* tag to handle  */
+    char *    (*func) (long,
+                       const char **)   /* function to call for 'tag' */
+    )
+{
+    /* the name of the vairable ... */
+    char     *val;
+    long      valln;
+    char    **args;
+    char     *end;
+    long      end_offset;
+    int       argc;
+    size_t    taglen = strlen(tag);
+
+    /* Current position in buffer should start with 'tag' */
+    if (strncmp((*buf) + i, tag, taglen) != 0) {
+        return 0;
+    }
+    /* .. and match exactly (a whitespace following 'tag') */
+    if (!isspace(*((*buf) + i + taglen))) {
+        return 0;
+    }
+#ifdef DEBUG_PARSER
+    printf("parse(): handling tag '%s'\n", tag);
+#endif
+
+    /* Scan for arguments following the tag;
+       scanargs() puts \0 into *buf ... so after scanargs it is probably
+       not a good time to use strlen on buf */
+    end = scanargs((*buf) + i + taglen, &argc, &args);
+    if (end) {
+        /* got arguments, call function for 'tag' with arguments */
+        val = func(argc, (const char **) args);
+        free(args-1);
+    } else {
+        /* unable to parse arguments, undo 0-termination by scanargs */
+        for (; argc > 0; argc--) {
+            *((args[argc - 1]) - 1) = ' ';
+        }
+
+        /* next call, try parsing at current offset +1 */
+        end = (*buf) + i + 1;
+
+        val = stralloc("[ERROR: Parsing Problem with the following text\n"
+                       " Check original file. This may have been altered "
+                       "by parsing.]\n\n");
+    }
+
+    /* remember offset where we have to continue parsing */
+    end_offset = end - (*buf);
+
+    valln = 0;
+    if (val) {
+        valln = strlen(val);
+    }
+
+    /* Optionally resize buffer to hold the replacement value:
+       Calculating the new length of the buffer is simple. add current
+       buffer pos (i) to length of string after replaced tag to length
+       of replacement string and add 1 for the final zero ... */
+    if (end - (*buf) < (i + valln)) {
+        /* make sure we do not shrink the mallocd block */
+        size_t    newbufsize = i + strlen(end) + valln + 1;
+
+        *buf = rrd_realloc(*buf, newbufsize);
+
+        if (*buf == NULL) {
+            perror("Realoc buf:");
+            exit(1);
+        };
+    }
+
+    /* Update new end pointer:
+       make sure the 'end' pointer gets moved along with the 
+       buf pointer when realloc moves memory ... */
+    end = (*buf) + end_offset;
+
+    /* splice the variable:
+       step 1. Shift pending data to make room for 'val' */
+    memmove((*buf) + i + valln, end, strlen(end) + 1);
+
+    /* step 2. Insert val */
+    if (val) {
+        memmove((*buf) + i, val, valln);
+        free(val);
+    }
+    return (valln > 0 ? valln - 1 : valln);
+}
+
+char     *http_time(
+    time_t *now)
+{
+    struct tm *tmptime;
+    static char buf[60];
+
+    tmptime = gmtime(now);
+    strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tmptime);
+    return (buf);
+}
+
+void rrdcgiHeader(
+    void)
+{
+    if (rrdcgiType)
+        printf("Content-type: %s\n", rrdcgiType);
+    else
+        printf("Content-type: text/html\n");
+    if (rrdcgiHeaderString)
+        printf("%s", rrdcgiHeaderString);
+    printf("\n");
+}
+
+void rrdcgiDebug(
+    int level,
+    int where)
+{
+    if (level > 0)
+        rrdcgiDebugLevel = level;
+    else
+        rrdcgiDebugLevel = 0;
+    if (where)
+        rrdcgiDebugStderr = 0;
+    else
+        rrdcgiDebugStderr = 1;
+}
+
+char     *rrdcgiDecodeString(
+    char *text)
+{
+    char     *cp, *xp;
+
+    for (cp = text, xp = text; *cp; cp++) {
+        if (*cp == '%') {
+            if (strchr("0123456789ABCDEFabcdef", *(cp + 1))
+                && strchr("0123456789ABCDEFabcdef", *(cp + 2))) {
+                if (islower(*(cp + 1)))
+                    *(cp + 1) = toupper(*(cp + 1));
+                if (islower(*(cp + 2)))
+                    *(cp + 2) = toupper(*(cp + 2));
+                *(xp) =
+                    (*(cp + 1) >=
+                     'A' ? *(cp + 1) - 'A' + 10 : *(cp + 1) - '0') * 16 +
+                    (*(cp + 2) >=
+                     'A' ? *(cp + 2) - 'A' + 10 : *(cp + 2) - '0');
+                xp++;
+                cp += 2;
+            }
+        } else {
+            *(xp++) = *cp;
+        }
+    }
+    memset(xp, 0, cp - xp);
+    return text;
+}
+
+/*  rrdcgiReadVariables()
+ *
+ *  Read from stdin if no string is provided via CGI.  Variables that
+ *  doesn't have a value associated with it doesn't get stored.
+ */
+s_var   **rrdcgiReadVariables(
+    void)
+{
+    int       length;
+    char     *line = NULL;
+    int       numargs;
+    char     *cp, *ip, *esp, *sptr;
+    s_var   **result;
+    int       i, k, len;
+    char      tmp[101];
+
+    cp = getenv("REQUEST_METHOD");
+    ip = getenv("CONTENT_LENGTH");
+
+    if (cp && !strcmp(cp, "POST")) {
+        if (ip) {
+            length = atoi(ip);
+            if ((line = (char *) malloc(length + 2)) == NULL)
+                return NULL;
+            if (fgets(line, length + 1, stdin) == NULL)
+                return NULL;
+        } else
+            return NULL;
+    } else if (cp && !strcmp(cp, "GET")) {
+        esp = getenv("QUERY_STRING");
+        if (esp && strlen(esp)) {
+            if ((line = (char *) malloc(strlen(esp) + 2)) == NULL)
+                return NULL;
+            sprintf(line, "%s", esp);
+        } else
+            return NULL;
+    } else {
+        length = 0;
+        printf("(offline mode: enter name=value pairs on standard input)\n");
+        memset(tmp, 0, sizeof(tmp));
+        while ((cp = fgets(tmp, 100, stdin)) != NULL) {
+            if (strlen(tmp)) {
+                if (tmp[strlen(tmp) - 1] == '\n')
+                    tmp[strlen(tmp) - 1] = '&';
+                if (length) {
+                    length += strlen(tmp);
+                    len = (length + 1) * sizeof(char);
+                    if ((line = (char *) realloc(line, len)) == NULL)
+                        return NULL;
+                    strcat(line, tmp);
+                } else {
+                    length = strlen(tmp);
+                    len = (length + 1) * sizeof(char);
+                    if ((line = (char *) malloc(len)) == NULL)
+                        return NULL;
+                    memset(line, 0, len);
+                    strcpy(line, tmp);
+                }
+            }
+            memset(tmp, 0, sizeof(tmp));
+        }
+        if (!line)
+            return NULL;
+        if (line[strlen(line) - 1] == '&')
+            line[strlen(line) - 1] = '\0';
+    }
+
+    /*
+     *  From now on all cgi variables are stored in the variable line
+     *  and look like  foo=bar&foobar=barfoo&foofoo=
+     */
+
+    if (rrdcgiDebugLevel > 0) {
+        if (rrdcgiDebugStderr)
+            fprintf(stderr, "Received cgi input: %s\n", line);
+        else
+            printf
+                ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n",
+                 line);
+    }
+
+    for (cp = line; *cp; cp++)
+        if (*cp == '+')
+            *cp = ' ';
+
+    if (strlen(line)) {
+        for (numargs = 1, cp = line; *cp; cp++)
+            if (*cp == '&')
+                numargs++;
+    } else
+        numargs = 0;
+    if (rrdcgiDebugLevel > 0) {
+        if (rrdcgiDebugStderr)
+            fprintf(stderr, "%d cgi variables found.\n", numargs);
+        else
+            printf("%d cgi variables found.<br>\n", numargs);
+    }
+
+    len = (numargs + 1) * sizeof(s_var *);
+    if ((result = (s_var **) malloc(len)) == NULL)
+        return NULL;
+    memset(result, 0, len);
+
+    cp = line;
+    i = 0;
+    while (*cp) {
+        if ((ip = (char *) strchr(cp, '&')) != NULL) {
+            *ip = '\0';
+        } else
+            ip = cp + strlen(cp);
+
+        if ((esp = (char *) strchr(cp, '=')) == NULL) {
+            cp = ++ip;
+            continue;
+        }
+
+        if (!strlen(esp)) {
+            cp = ++ip;
+            continue;
+        }
+
+        if (i < numargs) {
+
+            /* try to find out if there's already such a variable */
+            for (k = 0; k < i && (strncmp(result[k]->name, cp, esp - cp)
+                                  || !(strlen(result[k]->name) ==
+                                       (size_t) (esp - cp))); k++);
+
+            if (k == i) {   /* No such variable yet */
+                if ((result[i] = (s_var *) malloc(sizeof(s_var))) == NULL)
+                    return NULL;
+                if ((result[i]->name =
+                     (char *) malloc((esp - cp + 1) * sizeof(char))) == NULL)
+                    return NULL;
+                memset(result[i]->name, 0, esp - cp + 1);
+                strncpy(result[i]->name, cp, esp - cp);
+                cp = ++esp;
+                if ((result[i]->value =
+                     (char *) malloc((ip - esp + 1) * sizeof(char))) == NULL)
+                    return NULL;
+                memset(result[i]->value, 0, ip - esp + 1);
+                strncpy(result[i]->value, cp, ip - esp);
+                result[i]->value = rrdcgiDecodeString(result[i]->value);
+                if (rrdcgiDebugLevel) {
+                    if (rrdcgiDebugStderr)
+                        fprintf(stderr, "%s: %s\n", result[i]->name,
+                                result[i]->value);
+                    else
+                        printf("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n",
+                               result[i]->name, result[i]->value);
+                }
+                i++;
+            } else {    /* There is already such a name, suppose a mutiple field */
+                cp = ++esp;
+                len =
+                    (strlen(result[k]->value) + (ip - esp) +
+                     2) * sizeof(char);
+                if ((sptr = (char *) malloc(len)) == NULL)
+                    return NULL;
+                memset(sptr, 0, len);
+                sprintf(sptr, "%s\n", result[k]->value);
+                strncat(sptr, cp, ip - esp);
+                free(result[k]->value);
+                result[k]->value = rrdcgiDecodeString(sptr);
+            }
+        }
+        cp = ++ip;
+    }
+    return result;
+}
+
+/*  rrdcgiInit()
+ *
+ *  Read from stdin if no string is provided via CGI.  Variables that
+ *  doesn't have a value associated with it doesn't get stored.
+ */
+s_cgi    *rrdcgiInit(
+    void)
+{
+    s_cgi    *res;
+    s_var   **vars;
+
+    vars = rrdcgiReadVariables();
+
+    if (!vars)
+        return NULL;
+
+    if ((res = (s_cgi *) malloc(sizeof(s_cgi))) == NULL)
+        return NULL;
+    res->vars = vars;
+
+    return res;
+}
+
+char     *rrdcgiGetValue(
+    s_cgi * parms,
+    const char *name)
+{
+    int       i;
+
+    if (!parms || !parms->vars)
+        return NULL;
+    for (i = 0; parms->vars[i]; i++)
+        if (!strcmp(name, parms->vars[i]->name)) {
+            if (rrdcgiDebugLevel > 0) {
+                if (rrdcgiDebugStderr)
+                    fprintf(stderr, "%s found as %s\n", name,
+                            parms->vars[i]->value);
+                else
+                    printf("%s found as %s<br>\n", name,
+                           parms->vars[i]->value);
+            }
+            return parms->vars[i]->value;
+        }
+    if (rrdcgiDebugLevel) {
+        if (rrdcgiDebugStderr)
+            fprintf(stderr, "%s not found\n", name);
+        else
+            printf("%s not found<br>\n", name);
+    }
+    return NULL;
+}
+
+void rrdcgiFreeList(
+    char **list)
+{
+    int       i;
+
+    for (i = 0; list[i] != NULL; i++)
+        free(list[i]);
+    free(list);
+}
+
+void rrdcgiFree(
+    s_cgi * parms)
+{
+    int       i;
+
+    if (!parms)
+        return;
+    if (parms->vars) {
+        for (i = 0; parms->vars[i]; i++) {
+            if (parms->vars[i]->name)
+                free(parms->vars[i]->name);
+            if (parms->vars[i]->value)
+                free(parms->vars[i]->value);
+            free(parms->vars[i]);
+        }
+        free(parms->vars);
+    }
+    free(parms);
+
+    if (rrdcgiHeaderString) {
+        free(rrdcgiHeaderString);
+        rrdcgiHeaderString = NULL;
+    }
+    if (rrdcgiType) {
+        free(rrdcgiType);
+        rrdcgiType = NULL;
+    }
+}
diff --git a/program/src/rrd_client.c b/program/src/rrd_client.c
new file mode 100644 (file)
index 0000000..0b69000
--- /dev/null
@@ -0,0 +1,848 @@
+/**
+ * RRDTool - src/rrd_client.c
+ * Copyright (C) 2008 Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ *   Sebastian tokkee Harl <sh at tokkee.org>
+ **/
+
+#include "rrd.h"
+#include "rrd_tool.h"
+#include "rrd_client.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <assert.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netdb.h>
+#include <limits.h>
+
+#ifndef ENODATA
+#define ENODATA ENOENT
+#endif
+
+struct rrdc_response_s
+{
+  int status;
+  char *message;
+  char **lines;
+  size_t lines_num;
+};
+typedef struct rrdc_response_s rrdc_response_t;
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+static int sd = -1;
+static FILE *sh = NULL;
+static char *sd_path = NULL; /* cache the path for sd */
+
+/* get_path: Return a path name appropriate to be sent to the daemon.
+ *
+ * When talking to a local daemon (thru a UNIX socket), relative path names
+ * are resolved to absolute path names to allow for transparent integration
+ * into existing solutions (as requested by Tobi). Else, absolute path names
+ * are not allowed, since path name translation is done by the server.
+ *
+ * One must hold `lock' when calling this function. */
+static const char *get_path (const char *path, char *resolved_path) /* {{{ */
+{
+  const char *ret = path;
+  int is_unix = 0;
+
+  if ((*sd_path == '/')
+      || (strncmp ("unix:", sd_path, strlen ("unix:")) == 0))
+    is_unix = 1;
+
+  if (*path == '/') /* absolute path */
+  {
+    if (! is_unix)
+    {
+      rrd_set_error ("absolute path names not allowed when talking "
+          "to a remote daemon");
+      return (NULL);
+    }
+    /* else: nothing to do */
+  }
+  else /* relative path */
+  {
+    if (is_unix)
+    {
+      realpath (path, resolved_path);
+      ret = resolved_path;
+    }
+    /* else: nothing to do */
+  }
+  return (ret);
+} /* }}} char *get_path */
+
+/* One must hold `lock' when calling `close_connection'. */
+static void close_connection (void) /* {{{ */
+{
+  if (sh != NULL)
+  {
+    fclose (sh);
+    sh = NULL;
+    sd = -1;
+  }
+  else if (sd >= 0)
+  {
+    close (sd);
+    sd = -1;
+  }
+
+  if (sd_path != NULL)
+    free (sd_path);
+  sd_path = NULL;
+} /* }}} void close_connection */
+
+static int buffer_add_string (const char *str, /* {{{ */
+    char **buffer_ret, size_t *buffer_size_ret)
+{
+  char *buffer;
+  size_t buffer_size;
+  size_t buffer_pos;
+  size_t i;
+  int status;
+
+  buffer = *buffer_ret;
+  buffer_size = *buffer_size_ret;
+  buffer_pos = 0;
+
+  i = 0;
+  status = -1;
+  while (buffer_pos < buffer_size)
+  {
+    if (str[i] == 0)
+    {
+      buffer[buffer_pos] = ' ';
+      buffer_pos++;
+      status = 0;
+      break;
+    }
+    else if ((str[i] == ' ') || (str[i] == '\\'))
+    {
+      if (buffer_pos >= (buffer_size - 1))
+        break;
+      buffer[buffer_pos] = '\\';
+      buffer_pos++;
+      buffer[buffer_pos] = str[i];
+      buffer_pos++;
+    }
+    else
+    {
+      buffer[buffer_pos] = str[i];
+      buffer_pos++;
+    }
+    i++;
+  } /* while (buffer_pos < buffer_size) */
+
+  if (status != 0)
+    return (-1);
+
+  *buffer_ret = buffer + buffer_pos;
+  *buffer_size_ret = buffer_size - buffer_pos;
+
+  return (0);
+} /* }}} int buffer_add_string */
+
+static int buffer_add_value (const char *value, /* {{{ */
+    char **buffer_ret, size_t *buffer_size_ret)
+{
+  char temp[4096];
+
+  if (strncmp (value, "N:", 2) == 0)
+    snprintf (temp, sizeof (temp), "%lu:%s",
+        (unsigned long) time (NULL), value + 2);
+  else
+    strncpy (temp, value, sizeof (temp));
+  temp[sizeof (temp) - 1] = 0;
+
+  return (buffer_add_string (temp, buffer_ret, buffer_size_ret));
+} /* }}} int buffer_add_value */
+
+/* Remove trailing newline (NL) and carriage return (CR) characters. Similar to
+ * the Perl function `chomp'. Returns the number of characters that have been
+ * removed. */
+static int chomp (char *str) /* {{{ */
+{
+  size_t len;
+  int removed;
+
+  if (str == NULL)
+    return (-1);
+
+  len = strlen (str);
+  removed = 0;
+  while ((len > 0) && ((str[len - 1] == '\n') || (str[len - 1] == '\r')))
+  {
+    str[len - 1] = 0;
+    len--;
+    removed++;
+  }
+
+  return (removed);
+} /* }}} int chomp */
+
+static void response_free (rrdc_response_t *res) /* {{{ */
+{
+  if (res == NULL)
+    return;
+
+  if (res->lines != NULL)
+  {
+    size_t i;
+
+    for (i = 0; i < res->lines_num; i++)
+      if (res->lines[i] != NULL)
+        free (res->lines[i]);
+    free (res->lines);
+  }
+
+  free (res);
+} /* }}} void response_free */
+
+static int response_read (rrdc_response_t **ret_response) /* {{{ */
+{
+  rrdc_response_t *ret;
+
+  char buffer[4096];
+  char *buffer_ptr;
+
+  size_t i;
+
+  if (sh == NULL)
+    return (-1);
+
+  ret = (rrdc_response_t *) malloc (sizeof (rrdc_response_t));
+  if (ret == NULL)
+    return (-2);
+  memset (ret, 0, sizeof (*ret));
+  ret->lines = NULL;
+  ret->lines_num = 0;
+
+  buffer_ptr = fgets (buffer, sizeof (buffer), sh);
+  if (buffer_ptr == NULL)
+    return (-3);
+  chomp (buffer);
+
+  ret->status = strtol (buffer, &ret->message, 0);
+  if (buffer == ret->message)
+  {
+    response_free (ret);
+    return (-4);
+  }
+  /* Skip leading whitespace of the status message */
+  ret->message += strspn (ret->message, " \t");
+
+  if (ret->status <= 0)
+  {
+    if (ret->status < 0)
+      rrd_set_error("rrdcached: %s", ret->message);
+    *ret_response = ret;
+    return (0);
+  }
+
+  ret->lines = (char **) malloc (sizeof (char *) * ret->status);
+  if (ret->lines == NULL)
+  {
+    response_free (ret);
+    return (-5);
+  }
+  memset (ret->lines, 0, sizeof (char *) * ret->status);
+  ret->lines_num = (size_t) ret->status;
+
+  for (i = 0; i < ret->lines_num; i++)
+  {
+    buffer_ptr = fgets (buffer, sizeof (buffer), sh);
+    if (buffer_ptr == NULL)
+    {
+      response_free (ret);
+      return (-6);
+    }
+    chomp (buffer);
+
+    ret->lines[i] = strdup (buffer);
+    if (ret->lines[i] == NULL)
+    {
+      response_free (ret);
+      return (-7);
+    }
+  }
+
+  *ret_response = ret;
+  return (0);
+} /* }}} rrdc_response_t *response_read */
+
+static int request (const char *buffer, size_t buffer_size, /* {{{ */
+    rrdc_response_t **ret_response)
+{
+  int status;
+  rrdc_response_t *res;
+
+  if (sh == NULL)
+    return (ENOTCONN);
+
+  status = (int) fwrite (buffer, buffer_size, /* nmemb = */ 1, sh);
+  if (status != 1)
+  {
+    close_connection ();
+    rrd_set_error("request: socket error (%d) while talking to rrdcached",
+                  status);
+    return (-1);
+  }
+  fflush (sh);
+
+  res = NULL;
+  status = response_read (&res);
+
+  if (status != 0)
+  {
+    if (status < 0)
+      rrd_set_error("request: internal error while talking to rrdcached");
+    return (status);
+  }
+
+  *ret_response = res;
+  return (0);
+} /* }}} int request */
+
+/* determine whether we are connected to the specified daemon_addr if
+ * NULL, return whether we are connected at all
+ */
+int rrdc_is_connected(const char *daemon_addr) /* {{{ */
+{
+  if (sd < 0)
+    return 0;
+  else if (daemon_addr == NULL)
+  {
+    /* here we have to handle the case i.e.
+     *   UPDATE --daemon ...; UPDATEV (no --daemon) ...
+     * In other words: we have a cached connection,
+     * but it is not specified in the current command.
+     * Daemon is only implied in this case if set in ENV
+     */
+    if (getenv(ENV_RRDCACHED_ADDRESS) != NULL)
+      return 1;
+    else
+      return 0;
+  }
+  else if (strcmp(daemon_addr, sd_path) == 0)
+    return 1;
+  else
+    return 0;
+
+} /* }}} int rrdc_is_connected */
+
+static int rrdc_connect_unix (const char *path) /* {{{ */
+{
+  struct sockaddr_un sa;
+  int status;
+
+  assert (path != NULL);
+  assert (sd == -1);
+
+  sd = socket (PF_UNIX, SOCK_STREAM, /* protocol = */ 0);
+  if (sd < 0)
+  {
+    status = errno;
+    return (status);
+  }
+
+  memset (&sa, 0, sizeof (sa));
+  sa.sun_family = AF_UNIX;
+  strncpy (sa.sun_path, path, sizeof (sa.sun_path) - 1);
+
+  status = connect (sd, (struct sockaddr *) &sa, sizeof (sa));
+  if (status != 0)
+  {
+    status = errno;
+    close_connection ();
+    return (status);
+  }
+
+  sh = fdopen (sd, "r+");
+  if (sh == NULL)
+  {
+    status = errno;
+    close_connection ();
+    return (status);
+  }
+
+  return (0);
+} /* }}} int rrdc_connect_unix */
+
+static int rrdc_connect_network (const char *addr_orig) /* {{{ */
+{
+  struct addrinfo ai_hints;
+  struct addrinfo *ai_res;
+  struct addrinfo *ai_ptr;
+  char addr_copy[NI_MAXHOST];
+  char *addr;
+  char *port;
+
+  assert (addr_orig != NULL);
+  assert (sd == -1);
+
+  strncpy(addr_copy, addr_orig, sizeof(addr_copy));
+  addr_copy[sizeof(addr_copy) - 1] = '\0';
+  addr = addr_copy;
+
+  int status;
+  memset (&ai_hints, 0, sizeof (ai_hints));
+  ai_hints.ai_flags = 0;
+#ifdef AI_ADDRCONFIG
+  ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+  ai_hints.ai_family = AF_UNSPEC;
+  ai_hints.ai_socktype = SOCK_STREAM;
+
+  port = NULL;
+  if (*addr == '[') /* IPv6+port format */
+  {
+    /* `addr' is something like "[2001:780:104:2:211:24ff:feab:26f8]:12345" */
+    addr++;
+
+    port = strchr (addr, ']');
+    if (port == NULL)
+    {
+      rrd_set_error("malformed address: %s", addr_orig);
+      return (-1);
+    }
+    *port = 0;
+    port++;
+
+    if (*port == ':')
+      port++;
+    else if (*port == 0)
+      port = NULL;
+    else
+    {
+      rrd_set_error("garbage after address: %s", port);
+      return (-1);
+    }
+  } /* if (*addr == '[') */
+  else
+  {
+    port = rindex(addr, ':');
+    if (port != NULL)
+    {
+      *port = 0;
+      port++;
+    }
+  }
+
+  ai_res = NULL;
+  status = getaddrinfo (addr,
+                        port == NULL ? RRDCACHED_DEFAULT_PORT : port,
+                        &ai_hints, &ai_res);
+  if (status != 0)
+  {
+    rrd_set_error ("failed to resolve address `%s' (port %s): %s",
+        addr, port == NULL ? RRDCACHED_DEFAULT_PORT : port,
+        gai_strerror (status));
+    return (-1);
+  }
+
+  for (ai_ptr = ai_res; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+  {
+    sd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
+    if (sd < 0)
+    {
+      status = errno;
+      sd = -1;
+      continue;
+    }
+
+    status = connect (sd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+    if (status != 0)
+    {
+      status = errno;
+      close_connection();
+      continue;
+    }
+
+    sh = fdopen (sd, "r+");
+    if (sh == NULL)
+    {
+      status = errno;
+      close_connection ();
+      continue;
+    }
+
+    assert (status == 0);
+    break;
+  } /* for (ai_ptr) */
+
+  return (status);
+} /* }}} int rrdc_connect_network */
+
+int rrdc_connect (const char *addr) /* {{{ */
+{
+  int status = 0;
+
+  if (addr == NULL)
+    addr = getenv (ENV_RRDCACHED_ADDRESS);
+
+  if (addr == NULL)
+    return 0;
+
+  pthread_mutex_lock(&lock);
+
+  if (sd >= 0 && sd_path != NULL && strcmp(addr, sd_path) == 0)
+  {
+    /* connection to the same daemon; use cached connection */
+    pthread_mutex_unlock (&lock);
+    return (0);
+  }
+  else
+  {
+    close_connection();
+  }
+
+  rrd_clear_error ();
+  if (strncmp ("unix:", addr, strlen ("unix:")) == 0)
+    status = rrdc_connect_unix (addr + strlen ("unix:"));
+  else if (addr[0] == '/')
+    status = rrdc_connect_unix (addr);
+  else
+    status = rrdc_connect_network(addr);
+
+  if (status == 0 && sd >= 0)
+    sd_path = strdup(addr);
+  else
+  {
+    char *err = rrd_test_error () ? rrd_get_error () : "Internal error";
+    /* err points the string that gets written to by rrd_set_error(), thus we
+     * cannot pass it to that function */
+    err = strdup (err);
+    rrd_set_error("Unable to connect to rrdcached: %s",
+                  (status < 0)
+                  ? (err ? err : "Internal error")
+                  : rrd_strerror (status));
+    if (err != NULL)
+      free (err);
+  }
+
+  pthread_mutex_unlock (&lock);
+  return (status);
+} /* }}} int rrdc_connect */
+
+int rrdc_disconnect (void) /* {{{ */
+{
+  pthread_mutex_lock (&lock);
+
+  close_connection();
+
+  pthread_mutex_unlock (&lock);
+
+  return (0);
+} /* }}} int rrdc_disconnect */
+
+int rrdc_update (const char *filename, int values_num, /* {{{ */
+               const char * const *values)
+{
+  char buffer[4096];
+  char *buffer_ptr;
+  size_t buffer_free;
+  size_t buffer_size;
+  rrdc_response_t *res;
+  int status;
+  int i;
+  char file_path[PATH_MAX];
+
+  memset (buffer, 0, sizeof (buffer));
+  buffer_ptr = &buffer[0];
+  buffer_free = sizeof (buffer);
+
+  status = buffer_add_string ("update", &buffer_ptr, &buffer_free);
+  if (status != 0)
+    return (ENOBUFS);
+
+  pthread_mutex_lock (&lock);
+  filename = get_path (filename, file_path);
+  if (filename == NULL)
+  {
+    pthread_mutex_unlock (&lock);
+    return (-1);
+  }
+
+  status = buffer_add_string (filename, &buffer_ptr, &buffer_free);
+  if (status != 0)
+  {
+    pthread_mutex_unlock (&lock);
+    return (ENOBUFS);
+  }
+
+  for (i = 0; i < values_num; i++)
+  {
+    status = buffer_add_value (values[i], &buffer_ptr, &buffer_free);
+    if (status != 0)
+    {
+      pthread_mutex_unlock (&lock);
+      return (ENOBUFS);
+    }
+  }
+
+  assert (buffer_free < sizeof (buffer));
+  buffer_size = sizeof (buffer) - buffer_free;
+  assert (buffer[buffer_size - 1] == ' ');
+  buffer[buffer_size - 1] = '\n';
+
+  res = NULL;
+  status = request (buffer, buffer_size, &res);
+  pthread_mutex_unlock (&lock);
+
+  if (status != 0)
+    return (status);
+
+  status = res->status;
+  response_free (res);
+
+  return (status);
+} /* }}} int rrdc_update */
+
+int rrdc_flush (const char *filename) /* {{{ */
+{
+  char buffer[4096];
+  char *buffer_ptr;
+  size_t buffer_free;
+  size_t buffer_size;
+  rrdc_response_t *res;
+  int status;
+  char file_path[PATH_MAX];
+
+  if (filename == NULL)
+    return (-1);
+
+  memset (buffer, 0, sizeof (buffer));
+  buffer_ptr = &buffer[0];
+  buffer_free = sizeof (buffer);
+
+  status = buffer_add_string ("flush", &buffer_ptr, &buffer_free);
+  if (status != 0)
+    return (ENOBUFS);
+
+  pthread_mutex_lock (&lock);
+  filename = get_path (filename, file_path);
+  if (filename == NULL)
+  {
+    pthread_mutex_unlock (&lock);
+    return (-1);
+  }
+
+  status = buffer_add_string (filename, &buffer_ptr, &buffer_free);
+  if (status != 0)
+  {
+    pthread_mutex_unlock (&lock);
+    return (ENOBUFS);
+  }
+
+  assert (buffer_free < sizeof (buffer));
+  buffer_size = sizeof (buffer) - buffer_free;
+  assert (buffer[buffer_size - 1] == ' ');
+  buffer[buffer_size - 1] = '\n';
+
+  res = NULL;
+  status = request (buffer, buffer_size, &res);
+  pthread_mutex_unlock (&lock);
+
+  if (status != 0)
+    return (status);
+
+  status = res->status;
+  response_free (res);
+
+  return (status);
+} /* }}} int rrdc_flush */
+
+
+/* convenience function; if there is a daemon specified, or if we can
+ * detect one from the environment, then flush the file.  Otherwise, no-op
+ */
+int rrdc_flush_if_daemon (const char *opt_daemon, const char *filename) /* {{{ */
+{
+  int status = 0;
+
+  rrdc_connect(opt_daemon);
+
+  if (rrdc_is_connected(opt_daemon))
+  {
+    rrd_clear_error();
+    status = rrdc_flush (filename);
+
+    if (status != 0 && !rrd_test_error())
+    {
+      if (status > 0)
+      {
+        rrd_set_error("rrdc_flush (%s) failed: %s",
+                      filename, rrd_strerror(status));
+      }
+      else if (status < 0)
+      {
+        rrd_set_error("rrdc_flush (%s) failed with status %i.",
+                      filename, status);
+      }
+    }
+  } /* if (rrdc_is_connected(..)) */
+
+  return status;
+} /* }}} int rrdc_flush_if_daemon */
+
+
+int rrdc_stats_get (rrdc_stats_t **ret_stats) /* {{{ */
+{
+  rrdc_stats_t *head;
+  rrdc_stats_t *tail;
+
+  rrdc_response_t *res;
+
+  int status;
+  size_t i;
+
+  /* Protocol example: {{{
+   * ->  STATS
+   * <-  5 Statistics follow
+   * <-  QueueLength: 0
+   * <-  UpdatesWritten: 0
+   * <-  DataSetsWritten: 0
+   * <-  TreeNodesNumber: 0
+   * <-  TreeDepth: 0
+   * }}} */
+
+  res = NULL;
+  pthread_mutex_lock (&lock);
+  status = request ("STATS\n", strlen ("STATS\n"), &res);
+  pthread_mutex_unlock (&lock);
+
+  if (status != 0)
+    return (status);
+
+  if (res->status <= 0)
+  {
+    response_free (res);
+    return (EIO);
+  }
+
+  head = NULL;
+  tail = NULL;
+  for (i = 0; i < res->lines_num; i++)
+  {
+    char *key;
+    char *value;
+    char *endptr;
+    rrdc_stats_t *s;
+
+    key = res->lines[i];
+    value = strchr (key, ':');
+    if (value == NULL)
+      continue;
+    *value = 0;
+    value++;
+
+    while ((value[0] == ' ') || (value[0] == '\t'))
+      value++;
+
+    s = (rrdc_stats_t *) malloc (sizeof (rrdc_stats_t));
+    if (s == NULL)
+      continue;
+    memset (s, 0, sizeof (*s));
+
+    s->name = strdup (key);
+
+    endptr = NULL;
+    if ((strcmp ("QueueLength", key) == 0)
+        || (strcmp ("TreeDepth", key) == 0)
+        || (strcmp ("TreeNodesNumber", key) == 0))
+    {
+      s->type = RRDC_STATS_TYPE_GAUGE;
+      s->value.gauge = strtod (value, &endptr);
+    }
+    else if ((strcmp ("DataSetsWritten", key) == 0)
+        || (strcmp ("FlushesReceived", key) == 0)
+        || (strcmp ("JournalBytes", key) == 0)
+        || (strcmp ("JournalRotate", key) == 0)
+        || (strcmp ("UpdatesReceived", key) == 0)
+        || (strcmp ("UpdatesWritten", key) == 0))
+    {
+      s->type = RRDC_STATS_TYPE_COUNTER;
+      s->value.counter = (uint64_t) strtoll (value, &endptr, /* base = */ 0);
+    }
+    else
+    {
+      free (s);
+      continue;
+    }
+
+    /* Conversion failed */
+    if (endptr == value)
+    {
+      free (s);
+      continue;
+    }
+
+    if (head == NULL)
+    {
+      head = s;
+      tail = s;
+      s->next = NULL;
+    }
+    else
+    {
+      tail->next = s;
+      tail = s;
+    }
+  } /* for (i = 0; i < res->lines_num; i++) */
+
+  response_free (res);
+
+  if (head == NULL)
+    return (EPROTO);
+
+  *ret_stats = head;
+  return (0);
+} /* }}} int rrdc_stats_get */
+
+void rrdc_stats_free (rrdc_stats_t *ret_stats) /* {{{ */
+{
+  rrdc_stats_t *this;
+
+  this = ret_stats;
+  while (this != NULL)
+  {
+    rrdc_stats_t *next;
+
+    next = this->next;
+
+    if (this->name != NULL)
+    {
+      free ((char *)this->name);
+      this->name = NULL;
+    }
+    free (this);
+
+    this = next;
+  } /* while (this != NULL) */
+} /* }}} void rrdc_stats_free */
+
+/*
+ * vim: set sw=2 sts=2 ts=8 et fdm=marker :
+ */
diff --git a/program/src/rrd_client.h b/program/src/rrd_client.h
new file mode 100644 (file)
index 0000000..6c48dec
--- /dev/null
@@ -0,0 +1,99 @@
+/**
+ * RRDTool - src/rrd_client.h
+ * Copyright (C) 2008 Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#ifndef __RRD_CLIENT_H
+#define __RRD_CLIENT_H 1
+
+#ifndef WIN32
+# ifdef HAVE_STDINT_H
+#  include <stdint.h>
+# else
+#   ifdef HAVE_INTTYPES_H
+#      include <inttypes.h>
+#   else
+#      error "you should have stdint.h or inttypes.h to compile this"
+#   endif
+# endif
+#else
+#      include <stdlib.h>
+       typedef signed char     int8_t;
+       typedef unsigned char   uint8_t;
+       typedef signed int      int16_t;
+       typedef unsigned int    uint16_t;
+       typedef signed long int         int32_t;
+       typedef unsigned long int       uint32_t;
+       typedef signed long long int    int64_t;
+       typedef unsigned long long int  uint64_t;
+#endif
+
+
+#ifndef RRDCACHED_DEFAULT_ADDRESS
+# define RRDCACHED_DEFAULT_ADDRESS "unix:/tmp/rrdcached.sock"
+#endif
+
+#define RRDCACHED_DEFAULT_PORT "42217"
+#define ENV_RRDCACHED_ADDRESS "RRDCACHED_ADDRESS"
+
+
+// Windows version has no daemon/client support
+
+#ifndef WIN32
+int rrdc_connect (const char *addr);
+int rrdc_is_connected(const char *daemon_addr);
+int rrdc_disconnect (void);
+
+int rrdc_update (const char *filename, int values_num,
+        const char * const *values);
+
+int rrdc_flush (const char *filename);
+int rrdc_flush_if_daemon (const char *opt_daemon, const char *filename);
+
+#else
+#      define rrdc_flush_if_daemon(a,b) 0
+#      define rrdc_connect(a) 0
+#      define rrdc_is_connected(a) 0
+#      define rrdc_flush(a) 0
+#      define rrdc_update(a,b,c) 0
+#endif
+
+struct rrdc_stats_s
+{
+  const char *name;
+  uint16_t type;
+#define RRDC_STATS_TYPE_GAUGE   0x0001
+#define RRDC_STATS_TYPE_COUNTER 0x0002
+  uint16_t flags;
+  union
+  {
+    uint64_t counter;
+    double   gauge;
+  } value;
+  struct rrdc_stats_s *next;
+};
+typedef struct rrdc_stats_s rrdc_stats_t;
+
+int rrdc_stats_get (rrdc_stats_t **ret_stats);
+void rrdc_stats_free (rrdc_stats_t *ret_stats);
+
+#endif /* __RRD_CLIENT_H */
+/*
+ * vim: set sw=2 sts=2 ts=8 et fdm=marker :
+ */
diff --git a/program/src/rrd_config_bottom.h b/program/src/rrd_config_bottom.h
new file mode 100644 (file)
index 0000000..a569197
--- /dev/null
@@ -0,0 +1,237 @@
+#ifndef RRD_CONFIG_BOTTOM_H
+#define RRD_CONFIG_BOTTOM_H
+
+/* make sure that we pickup the correct stuff from all headers */
+#ifdef HAVE_FEATURES_H
+# ifdef _XOPEN_SOURCE
+#   undef _XOPEN_SOURCE
+# endif
+# ifdef _BSD_SOURCE
+#  undef _BSD_SOURCE
+# endif
+# define _XOPEN_SOURCE 600
+# define _BSD_SOURCE 1
+# include <features.h>
+#endif
+
+/* FreeBSD 4.8 wants this included BEFORE sys/types.h */
+#ifdef HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+#ifndef MAXPATH
+# ifdef PATH_MAX
+#  define MAXPATH PATH_MAX
+# endif
+#endif
+#ifndef MAXPATH
+/* else try the BSD variant */
+# ifdef MAXPATHLEN
+#  define MAXPATH MAXPATHLEN
+# endif
+#endif
+
+#ifdef HAVE_ERRNO_H
+# include <errno.h>
+#endif
+
+#if !defined HAVE_MADVISE && defined HAVE_POSIX_MADVISE
+/* use posix_madvise family */
+# define madvise posix_madvise
+# define MADV_NORMAL POSIX_MADV_NORMAL
+# define MADV_RANDOM POSIX_MADV_RANDOM
+# define MADV_SEQUENTIAL POSIX_MADV_SEQUENTIAL
+# define MADV_WILLNEED POSIX_MADV_WILLNEED
+# define MADV_DONTNEED POSIX_MADV_DONTNEED
+#endif
+#if defined HAVE_MADVISE || defined HAVE_POSIX_MADVISE
+# define USE_MADVISE 1
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#ifdef HAVE_SYS_TIMES_H
+# include <sys/times.h>
+#endif
+
+#ifdef HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+#if (defined(__svr4__) && defined(__sun__))
+/* Solaris headers (pre 2.6) do not have a getrusage prototype. 
+   Use this instead. */
+extern int getrusage(int, struct rusage *);
+#endif /* __svr4__ && __sun__ */
+#endif
+
+
+/* define strrchr, strchr and memcpy, memmove in terms of bsd funcs
+   make sure you are NOT using bcopy, index or rindex in the code */
+      
+#ifdef STDC_HEADERS
+# include <string.h>
+#else
+# ifndef HAVE_STRCHR
+#  define strchr index
+#  define strrchr rindex
+# endif
+char *strchr (), *strrchr ();
+# ifndef HAVE_MEMMOVE
+#  define memcpy(d, s, n) bcopy ((s), (d), (n))
+#  define memmove(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#ifdef NO_NULL_REALLOC
+# define rrd_realloc(a,b) ( (a) == NULL ? malloc( (b) ) : realloc( (a) , (b) ))
+#else
+# define rrd_realloc(a,b) realloc((a), (b))
+#endif
+
+#ifdef HAVE_STDIO_H
+# include <stdio.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#ifdef HAVE_CTYPE_H
+# include <ctype.h>
+#endif
+
+#ifdef HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# ifdef HAVE_SYS_NDIR_H
+#  include <sys/ndir.h>
+# endif
+# ifdef HAVE_SYS_DIR_H
+#  include <sys/dir.h>
+# endif
+# ifdef HAVE_NDIR_H
+#  include <ndir.h>
+# endif
+#endif
+
+#ifdef MUST_DISABLE_SIGFPE
+# include <signal.h>
+#endif
+
+#ifdef MUST_DISABLE_FPMASK
+# include <floatingpoint.h>
+#endif
+
+
+#ifdef HAVE_MATH_H
+# include <math.h>
+#endif
+
+#ifdef HAVE_FLOAT_H
+# include <float.h>
+#endif
+
+#ifdef HAVE_IEEEFP_H
+# include <ieeefp.h>
+#endif
+
+#ifdef HAVE_FP_CLASS_H
+# include <fp_class.h>
+#endif
+
+/* for Solaris */
+#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASS))
+#  define HAVE_ISINF 1
+#  ifdef isinf
+#  undef isinf
+#  endif
+#  define isinf(a) (!!(fpclass(a) & (FP_SNAN|FP_QNAN)))
+
+#endif
+
+/* solaris 10 it defines isnan such that only forte can compile it ... bad bad  */
+#if (defined(HAVE_ISNAN) && defined(isnan) && defined(HAVE_FPCLASS) && defined(FP_SNAN) && defined(FP_QNAN))
+#  undef isnan
+#  define isnan(a) (fpclass(a) == FP_SNAN || fpclass(a) == FP_QNAN)
+#endif
+
+/* for OSF1 Digital Unix */
+#if (! defined(HAVE_ISINF) && defined(HAVE_FP_CLASS) && defined(HAVE_FP_CLASS_H) && defined(FP_NEG_INF) && defined( FP_POS_INF))
+#  define HAVE_ISINF 1
+#  define isinf(a) (fp_class(a) == FP_NEG_INF || fp_class(a) == FP_POS_INF)
+#endif
+
+#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASSIFY) && defined(FP_PLUS_INF) && defined(FP_MINUS_INF))
+#  define HAVE_ISINF 1
+#  define isinf(a) (fpclassify(a) == FP_MINUS_INF || fpclassify(a) == FP_PLUS_INF)
+#endif
+
+#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASSIFY) && defined(FP_INFINITE))
+#  define HAVE_ISINF 1
+#  define isinf(a) (fpclassify(a) == FP_INFINITE)
+#endif
+
+/* for AIX */
+#if (! defined(HAVE_ISINF) && defined(HAVE_CLASS))
+#  define HAVE_ISINF 1
+#  define isinf(a) (class(a) == FP_MINUS_INF || class(a) == FP_PLUS_INF)
+#endif
+
+#if (! defined (HAVE_FINITE) && defined (HAVE_ISFINITE))
+#  define HAVE_FINITE 1
+#  define finite(a) isfinite(a)
+#endif
+
+#if (! defined(HAVE_FINITE) && defined(HAVE_ISNAN) && defined(HAVE_ISINF))
+#  define HAVE_FINITE 1
+#  define finite(a) (! isnan(a) && ! isinf(a))
+#endif
+
+#ifndef HAVE_FINITE
+#error "Can't compile without finite function"
+#endif
+
+#ifndef HAVE_ISINF
+#error "Can't compile without isinf function"
+#endif
+
+#if (! defined(HAVE_FDATASYNC) && defined(HAVE_FSYNC))
+#define fdatasync fsync
+#endif
+
+#if (!defined(HAVE_FDATASYNC) && !defined(HAVE_FSYNC))
+#error "Can't compile with without fsync and fdatasync"
+#endif
+
+#endif /* RRD_CONFIG_BOTTOM_H */
+
diff --git a/program/src/rrd_create.c b/program/src/rrd_create.c
new file mode 100644 (file)
index 0000000..2a0a143
--- /dev/null
@@ -0,0 +1,839 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_create.c  creates new rrds
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <time.h>
+#include <locale.h>
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include "rrd_hw.h"
+
+#include "rrd_is_thread_safe.h"
+static int opt_no_overwrite = 0;
+
+#ifdef WIN32
+# include <process.h>
+#endif
+
+unsigned long FnvHash(
+    const char *str);
+int       create_hw_contingent_rras(
+    rrd_t *rrd,
+    unsigned short period,
+    unsigned long hashed_name);
+void      parseGENERIC_DS(
+    const char *def,
+    rrd_t *rrd,
+    int ds_idx);
+
+static void rrd_free2(
+    rrd_t *rrd);        /* our onwn copy, immmune to mmap */
+
+int rrd_create(
+    int argc,
+    char **argv)
+{
+    struct option long_options[] = {
+        {"start", required_argument, 0, 'b'},
+        {"step", required_argument, 0, 's'},
+        {"no-overwrite", no_argument, 0, 'O'},
+        {0, 0, 0, 0}
+    };
+    int       option_index = 0;
+    int       opt;
+    time_t    last_up = time(NULL) - 10;
+    unsigned long pdp_step = 300;
+    rrd_time_value_t last_up_tv;
+    char     *parsetime_error = NULL;
+    long      long_tmp;
+    int       rc;
+
+    optind = 0;
+    opterr = 0;         /* initialize getopt */
+
+    while (1) {
+        opt = getopt_long(argc, argv, "Ob:s:", long_options, &option_index);
+
+        if (opt == EOF)
+            break;
+
+        switch (opt) {
+        case 'b':
+            if ((parsetime_error = rrd_parsetime(optarg, &last_up_tv))) {
+                rrd_set_error("start time: %s", parsetime_error);
+                return (-1);
+            }
+            if (last_up_tv.type == RELATIVE_TO_END_TIME ||
+                last_up_tv.type == RELATIVE_TO_START_TIME) {
+                rrd_set_error("specifying time relative to the 'start' "
+                              "or 'end' makes no sense here");
+                return (-1);
+            }
+
+            last_up = mktime(&last_up_tv.tm) +last_up_tv.offset;
+
+            if (last_up < 3600 * 24 * 365 * 10) {
+                rrd_set_error
+                    ("the first entry to the RRD should be after 1980");
+                return (-1);
+            }
+            break;
+
+        case 's':
+            long_tmp = atol(optarg);
+            if (long_tmp < 1) {
+                rrd_set_error("step size should be no less than one second");
+                return (-1);
+            }
+            pdp_step = long_tmp;
+            break;
+
+        case 'O':
+            opt_no_overwrite = 1;
+           break;
+
+        case '?':
+            if (optopt != 0)
+                rrd_set_error("unknown option '%c'", optopt);
+            else
+                rrd_set_error("unknown option '%s'", argv[optind - 1]);
+            return (-1);
+        }
+    }
+    if (optind == argc) {
+        rrd_set_error("need name of an rrd file to create");
+        return -1;
+    }
+    rc = rrd_create_r(argv[optind],
+                      pdp_step, last_up,
+                      argc - optind - 1, (const char **) (argv + optind + 1));
+
+    return rc;
+}
+
+/* #define DEBUG */
+int rrd_create_r(
+    const char *filename,
+    unsigned long pdp_step,
+    time_t last_up,
+    int argc,
+    const char **argv)
+{
+    rrd_t     rrd;
+    long      i;
+    int       offset;
+    char     *token;
+    char      dummychar1[2], dummychar2[2];
+    unsigned short token_idx, error_flag, period = 0;
+    unsigned long hashed_name;
+
+    /* init rrd clean */
+    rrd_init(&rrd);
+    /* static header */
+    if ((rrd.stat_head = (stat_head_t*)calloc(1, sizeof(stat_head_t))) == NULL) {
+        rrd_set_error("allocating rrd.stat_head");
+        rrd_free2(&rrd);
+        return (-1);
+    }
+
+    /* live header */
+    if ((rrd.live_head = (live_head_t*)calloc(1, sizeof(live_head_t))) == NULL) {
+        rrd_set_error("allocating rrd.live_head");
+        rrd_free2(&rrd);
+        return (-1);
+    }
+
+    /* set some defaults */
+    strcpy(rrd.stat_head->cookie, RRD_COOKIE);
+    strcpy(rrd.stat_head->version, RRD_VERSION3);   /* by default we are still version 3 */
+    rrd.stat_head->float_cookie = FLOAT_COOKIE;
+    rrd.stat_head->ds_cnt = 0;  /* this will be adjusted later */
+    rrd.stat_head->rra_cnt = 0; /* ditto */
+    rrd.stat_head->pdp_step = pdp_step; /* 5 minute default */
+
+    /* a default value */
+    rrd.ds_def = NULL;
+    rrd.rra_def = NULL;
+
+    rrd.live_head->last_up = last_up;
+
+    /* optind points to the first non-option command line arg,
+     * in this case, the file name. */
+    /* Compute the FNV hash value (used by SEASONAL and DEVSEASONAL
+     * arrays. */
+    hashed_name = FnvHash(filename);
+    for (i = 0; i < argc; i++) {
+        unsigned int ii;
+
+        if (strncmp(argv[i], "DS:", 3) == 0) {
+            size_t    old_size = sizeof(ds_def_t) * (rrd.stat_head->ds_cnt);
+
+            if ((rrd.ds_def = (ds_def_t*)rrd_realloc(rrd.ds_def,
+                                          old_size + sizeof(ds_def_t))) ==
+                NULL) {
+                rrd_set_error("allocating rrd.ds_def");
+                rrd_free2(&rrd);
+                return (-1);
+            }
+            memset(&rrd.ds_def[rrd.stat_head->ds_cnt], 0, sizeof(ds_def_t));
+            /* extract the name and type */
+            switch (sscanf(&argv[i][3],
+                           DS_NAM_FMT "%1[:]" DST_FMT "%1[:]%n",
+                           rrd.ds_def[rrd.stat_head->ds_cnt].ds_nam,
+                           dummychar1,
+                           rrd.ds_def[rrd.stat_head->ds_cnt].dst,
+                           dummychar2, &offset)) {
+            case 0:
+            case 1:
+                rrd_set_error("Invalid DS name");
+                break;
+            case 2:
+            case 3:
+                rrd_set_error("Invalid DS type");
+                break;
+            case 4:    /* (%n may or may not be counted) */
+            case 5:    /* check for duplicate datasource names */
+                for (ii = 0; ii < rrd.stat_head->ds_cnt; ii++)
+                    if (strcmp(rrd.ds_def[rrd.stat_head->ds_cnt].ds_nam,
+                               rrd.ds_def[ii].ds_nam) == 0)
+                        rrd_set_error("Duplicate DS name: %s",
+                                      rrd.ds_def[ii].ds_nam);
+                /* DS_type may be valid or not. Checked later */
+                break;
+            default:
+                rrd_set_error("invalid DS format");
+            }
+            if (rrd_test_error()) {
+                rrd_free2(&rrd);
+                return -1;
+            }
+
+            /* parse the remainder of the arguments */
+            switch (dst_conv(rrd.ds_def[rrd.stat_head->ds_cnt].dst)) {
+            case DST_COUNTER:
+            case DST_ABSOLUTE:
+            case DST_GAUGE:
+            case DST_DERIVE:
+                parseGENERIC_DS(&argv[i][offset + 3], &rrd,
+                                rrd.stat_head->ds_cnt);
+                break;
+            case DST_CDEF:
+                parseCDEF_DS(&argv[i][offset + 3], &rrd,
+                             rrd.stat_head->ds_cnt);
+                break;
+            default:
+                rrd_set_error("invalid DS type specified");
+                break;
+            }
+
+            if (rrd_test_error()) {
+                rrd_free2(&rrd);
+                return -1;
+            }
+            rrd.stat_head->ds_cnt++;
+        } else if (strncmp(argv[i], "RRA:", 4) == 0) {
+            char     *argvcopy;
+            char     *tokptr = "";
+            size_t    old_size = sizeof(rra_def_t) * (rrd.stat_head->rra_cnt);
+            int       row_cnt;
+
+            if ((rrd.rra_def = (rra_def_t*)rrd_realloc(rrd.rra_def,
+                                           old_size + sizeof(rra_def_t))) ==
+                NULL) {
+                rrd_set_error("allocating rrd.rra_def");
+                rrd_free2(&rrd);
+                return (-1);
+            }
+            memset(&rrd.rra_def[rrd.stat_head->rra_cnt], 0,
+                   sizeof(rra_def_t));
+
+            argvcopy = strdup(argv[i]);
+            token = strtok_r(&argvcopy[4], ":", &tokptr);
+            token_idx = error_flag = 0;
+            while (token != NULL) {
+                switch (token_idx) {
+                case 0:
+                    if (sscanf(token, CF_NAM_FMT,
+                               rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam) !=
+                        1)
+                        rrd_set_error("Failed to parse CF name");
+                    switch (cf_conv
+                            (rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam)) {
+                    case CF_MHWPREDICT:
+                        strcpy(rrd.stat_head->version, RRD_VERSION);    /* MHWPREDICT causes Version 4 */
+                    case CF_HWPREDICT:
+                        /* initialize some parameters */
+                        rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_hw_alpha].
+                            u_val = 0.1;
+                        rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_hw_beta].
+                            u_val = 1.0 / 288;
+                        rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_dependent_rra_idx].u_cnt =
+                            rrd.stat_head->rra_cnt;
+                        break;
+                    case CF_DEVSEASONAL:
+                    case CF_SEASONAL:
+                        /* initialize some parameters */
+                        rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_seasonal_gamma].u_val = 0.1;
+                        rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_seasonal_smoothing_window].u_val = 0.05;
+                        /* fall through */
+                    case CF_DEVPREDICT:
+                        rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_dependent_rra_idx].u_cnt = -1;
+                        break;
+                    case CF_FAILURES:
+                        rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_delta_pos].u_val = 2.0;
+                        rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_delta_neg].u_val = 2.0;
+                        rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_window_len].u_cnt = 3;
+                        rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_failure_threshold].u_cnt = 2;
+                        rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_dependent_rra_idx].u_cnt = -1;
+                        break;
+                        /* invalid consolidation function */
+                    case -1:
+                        rrd_set_error
+                            ("Unrecognized consolidation function %s",
+                             rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam);
+                    default:
+                        break;
+                    }
+                    /* default: 1 pdp per cdp */
+                    rrd.rra_def[rrd.stat_head->rra_cnt].pdp_cnt = 1;
+                    break;
+                case 1:
+                    switch (cf_conv
+                            (rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam)) {
+                    case CF_HWPREDICT:
+                    case CF_MHWPREDICT:
+                    case CF_DEVSEASONAL:
+                    case CF_SEASONAL:
+                    case CF_DEVPREDICT:
+                    case CF_FAILURES:
+                        row_cnt = atoi(token);
+                        if (row_cnt <= 0)
+                            rrd_set_error("Invalid row count: %i", row_cnt);
+                        rrd.rra_def[rrd.stat_head->rra_cnt].row_cnt = row_cnt;
+                        break;
+                    default:
+                        rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_cdp_xff_val].u_val = atof(token);
+                        if (rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_cdp_xff_val].u_val < 0.0
+                            || rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_cdp_xff_val].u_val >= 1.0)
+                            rrd_set_error
+                                ("Invalid xff: must be between 0 and 1");
+                        break;
+                    }
+                    break;
+                case 2:
+                    switch (cf_conv
+                            (rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam)) {
+                    case CF_HWPREDICT:
+                    case CF_MHWPREDICT:
+                        rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_hw_alpha].
+                            u_val = atof(token);
+                        if (atof(token) <= 0.0 || atof(token) >= 1.0)
+                            rrd_set_error
+                                ("Invalid alpha: must be between 0 and 1");
+                        break;
+                    case CF_DEVSEASONAL:
+                    case CF_SEASONAL:
+                        rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_seasonal_gamma].u_val = atof(token);
+                        if (atof(token) <= 0.0 || atof(token) >= 1.0)
+                            rrd_set_error
+                                ("Invalid gamma: must be between 0 and 1");
+                        rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_seasonal_smooth_idx].u_cnt =
+                            hashed_name %
+                            rrd.rra_def[rrd.stat_head->rra_cnt].row_cnt;
+                        break;
+                    case CF_FAILURES:
+                        /* specifies the # of violations that constitutes the failure threshold */
+                        rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_failure_threshold].u_cnt = atoi(token);
+                        if (atoi(token) < 1
+                            || atoi(token) > MAX_FAILURES_WINDOW_LEN)
+                            rrd_set_error
+                                ("Failure threshold is out of range %d, %d",
+                                 1, MAX_FAILURES_WINDOW_LEN);
+                        break;
+                    case CF_DEVPREDICT:
+                        /* specifies the index (1-based) of CF_DEVSEASONAL array
+                         * associated with this CF_DEVPREDICT array. */
+                        rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_dependent_rra_idx].u_cnt =
+                            atoi(token) - 1;
+                        break;
+                    default:
+                        rrd.rra_def[rrd.stat_head->rra_cnt].pdp_cnt =
+                            atoi(token);
+                        if (atoi(token) < 1)
+                            rrd_set_error("Invalid step: must be >= 1");
+                        break;
+                    }
+                    break;
+                case 3:
+                    switch (cf_conv
+                            (rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam)) {
+                    case CF_HWPREDICT:
+                    case CF_MHWPREDICT:
+                        rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_hw_beta].
+                            u_val = atof(token);
+                        if (atof(token) < 0.0 || atof(token) > 1.0)
+                            rrd_set_error
+                                ("Invalid beta: must be between 0 and 1");
+                        break;
+                    case CF_DEVSEASONAL:
+                    case CF_SEASONAL:
+                        /* specifies the index (1-based) of CF_HWPREDICT array
+                         * associated with this CF_DEVSEASONAL or CF_SEASONAL array. 
+                         * */
+                        rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_dependent_rra_idx].u_cnt =
+                            atoi(token) - 1;
+                        break;
+                    case CF_FAILURES:
+                        /* specifies the window length */
+                        rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_window_len].u_cnt = atoi(token);
+                        if (atoi(token) < 1
+                            || atoi(token) > MAX_FAILURES_WINDOW_LEN)
+                            rrd_set_error
+                                ("Window length is out of range %d, %d", 1,
+                                 MAX_FAILURES_WINDOW_LEN);
+                        /* verify that window length exceeds the failure threshold */
+                        if (rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_window_len].u_cnt <
+                            rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_failure_threshold].u_cnt)
+                            rrd_set_error
+                                ("Window length is shorter than the failure threshold");
+                        break;
+                    case CF_DEVPREDICT:
+                        /* shouldn't be any more arguments */
+                        rrd_set_error
+                            ("Unexpected extra argument for consolidation function DEVPREDICT");
+                        break;
+                    default:
+                        row_cnt = atoi(token);
+                        if (row_cnt <= 0)
+                            rrd_set_error("Invalid row count: %i", row_cnt);
+                        rrd.rra_def[rrd.stat_head->rra_cnt].row_cnt = row_cnt;
+                        break;
+                    }
+                    break;
+                case 4:
+                    switch (cf_conv
+                            (rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam)) {
+                    case CF_FAILURES:
+                        /* specifies the index (1-based) of CF_DEVSEASONAL array
+                         * associated with this CF_DEVFAILURES array. */
+                        rrd.rra_def[rrd.stat_head->rra_cnt].
+                            par[RRA_dependent_rra_idx].u_cnt =
+                            atoi(token) - 1;
+                        break;
+                    case CF_DEVSEASONAL:
+                    case CF_SEASONAL:
+                        /* optional smoothing window */
+                        if (sscanf(token, "smoothing-window=%lf",
+                                   &(rrd.rra_def[rrd.stat_head->rra_cnt].
+                                     par[RRA_seasonal_smoothing_window].
+                                     u_val))) {
+                            strcpy(rrd.stat_head->version, RRD_VERSION);    /* smoothing-window causes Version 4 */
+                            if (rrd.rra_def[rrd.stat_head->rra_cnt].
+                                par[RRA_seasonal_smoothing_window].u_val < 0.0
+                                || rrd.rra_def[rrd.stat_head->rra_cnt].
+                                par[RRA_seasonal_smoothing_window].u_val >
+                                1.0) {
+                                rrd_set_error
+                                    ("Invalid smoothing-window %f: must be between 0 and 1",
+                                     rrd.rra_def[rrd.stat_head->rra_cnt].
+                                     par[RRA_seasonal_smoothing_window].
+                                     u_val);
+                            }
+                        } else {
+                            rrd_set_error("Invalid option %s", token);
+                        }
+                        break;
+                    case CF_HWPREDICT:
+                    case CF_MHWPREDICT:
+                        /* length of the associated CF_SEASONAL and CF_DEVSEASONAL arrays. */
+                        period = atoi(token);
+                        if (period >
+                            rrd.rra_def[rrd.stat_head->rra_cnt].row_cnt)
+                            rrd_set_error
+                                ("Length of seasonal cycle exceeds length of HW prediction array");
+                        break;
+                    default:
+                        /* shouldn't be any more arguments */
+                        rrd_set_error
+                            ("Unexpected extra argument for consolidation function %s",
+                             rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam);
+                        break;
+                    }
+                    break;
+                case 5:
+                    /* If we are here, this must be a CF_HWPREDICT RRA.
+                     * Specifies the index (1-based) of CF_SEASONAL array
+                     * associated with this CF_HWPREDICT array. If this argument 
+                     * is missing, then the CF_SEASONAL, CF_DEVSEASONAL, CF_DEVPREDICT,
+                     * CF_FAILURES.
+                     * arrays are created automatically. */
+                    rrd.rra_def[rrd.stat_head->rra_cnt].
+                        par[RRA_dependent_rra_idx].u_cnt = atoi(token) - 1;
+                    break;
+                default:
+                    /* should never get here */
+                    rrd_set_error("Unknown error");
+                    break;
+                }       /* end switch */
+                if (rrd_test_error()) {
+                    /* all errors are unrecoverable */
+                    free(argvcopy);
+                    rrd_free2(&rrd);
+                    return (-1);
+                }
+                token = strtok_r(NULL, ":", &tokptr);
+                token_idx++;
+            }           /* end while */
+            free(argvcopy);
+#ifdef DEBUG
+            fprintf(stderr,
+                    "Creating RRA CF: %s, dep idx %lu, current idx %lu\n",
+                    rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam,
+                    rrd.rra_def[rrd.stat_head->rra_cnt].
+                    par[RRA_dependent_rra_idx].u_cnt, rrd.stat_head->rra_cnt);
+#endif
+            /* should we create CF_SEASONAL, CF_DEVSEASONAL, and CF_DEVPREDICT? */
+            if ((cf_conv(rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam) ==
+                 CF_HWPREDICT
+                 || cf_conv(rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam) ==
+                 CF_MHWPREDICT)
+                && rrd.rra_def[rrd.stat_head->rra_cnt].
+                par[RRA_dependent_rra_idx].u_cnt == rrd.stat_head->rra_cnt) {
+#ifdef DEBUG
+                fprintf(stderr, "Creating HW contingent RRAs\n");
+#endif
+                if (create_hw_contingent_rras(&rrd, period, hashed_name) ==
+                    -1) {
+                    rrd_set_error("creating contingent RRA");
+                    rrd_free2(&rrd);
+                    return -1;
+                }
+            }
+            rrd.stat_head->rra_cnt++;
+        } else {
+            rrd_set_error("can't parse argument '%s'", argv[i]);
+            rrd_free2(&rrd);
+            return -1;
+        }
+    }
+
+
+    if (rrd.stat_head->rra_cnt < 1) {
+        rrd_set_error("you must define at least one Round Robin Archive");
+        rrd_free2(&rrd);
+        return (-1);
+    }
+
+    if (rrd.stat_head->ds_cnt < 1) {
+        rrd_set_error("you must define at least one Data Source");
+        rrd_free2(&rrd);
+        return (-1);
+    }
+    return rrd_create_fn(filename, &rrd);
+}
+
+void parseGENERIC_DS(
+    const char *def,
+    rrd_t *rrd,
+    int ds_idx)
+{
+    char      minstr[DS_NAM_SIZE], maxstr[DS_NAM_SIZE];
+    char     *old_locale;
+
+    /*
+       int temp;
+
+       temp = sscanf(def,"%lu:%18[^:]:%18[^:]", 
+       &(rrd -> ds_def[ds_idx].par[DS_mrhb_cnt].u_cnt),
+       minstr,maxstr);
+     */
+    old_locale = setlocale(LC_NUMERIC, "C");
+    if (sscanf(def, "%lu:%18[^:]:%18[^:]",
+               &(rrd->ds_def[ds_idx].par[DS_mrhb_cnt].u_cnt),
+               minstr, maxstr) == 3) {
+        if (minstr[0] == 'U' && minstr[1] == 0)
+            rrd->ds_def[ds_idx].par[DS_min_val].u_val = DNAN;
+        else
+            rrd->ds_def[ds_idx].par[DS_min_val].u_val = atof(minstr);
+
+        if (maxstr[0] == 'U' && maxstr[1] == 0)
+            rrd->ds_def[ds_idx].par[DS_max_val].u_val = DNAN;
+        else
+            rrd->ds_def[ds_idx].par[DS_max_val].u_val = atof(maxstr);
+
+        if (!isnan(rrd->ds_def[ds_idx].par[DS_min_val].u_val) &&
+            !isnan(rrd->ds_def[ds_idx].par[DS_max_val].u_val) &&
+            rrd->ds_def[ds_idx].par[DS_min_val].u_val
+            >= rrd->ds_def[ds_idx].par[DS_max_val].u_val) {
+            rrd_set_error("min must be less than max in DS definition");
+            setlocale(LC_NUMERIC, old_locale);
+            return;
+        }
+    } else {
+        rrd_set_error("failed to parse data source %s", def);
+    }
+    setlocale(LC_NUMERIC, old_locale);
+}
+
+/* Create the CF_DEVPREDICT, CF_DEVSEASONAL, CF_SEASONAL, and CF_FAILURES RRAs
+ * associated with a CF_HWPREDICT RRA. */
+int create_hw_contingent_rras(
+    rrd_t *rrd,
+    unsigned short period,
+    unsigned long hashed_name)
+{
+    size_t    old_size;
+    rra_def_t *current_rra;
+
+    /* save index to CF_HWPREDICT */
+    unsigned long hw_index = rrd->stat_head->rra_cnt;
+
+    /* advance the pointer */
+    (rrd->stat_head->rra_cnt)++;
+    /* allocate the memory for the 4 contingent RRAs */
+    old_size = sizeof(rra_def_t) * (rrd->stat_head->rra_cnt);
+    if ((rrd->rra_def = (rra_def_t*)rrd_realloc(rrd->rra_def,
+                                    old_size + 4 * sizeof(rra_def_t))) ==
+        NULL) {
+        rrd_free2(rrd);
+        rrd_set_error("allocating rrd.rra_def");
+        return (-1);
+    }
+    /* clear memory */
+    memset(&(rrd->rra_def[rrd->stat_head->rra_cnt]), 0,
+           4 * sizeof(rra_def_t));
+
+    /* create the CF_SEASONAL RRA */
+    current_rra = &(rrd->rra_def[rrd->stat_head->rra_cnt]);
+    strcpy(current_rra->cf_nam, "SEASONAL");
+    current_rra->row_cnt = period;
+    current_rra->par[RRA_seasonal_smooth_idx].u_cnt = hashed_name % period;
+    current_rra->pdp_cnt = 1;
+    current_rra->par[RRA_seasonal_gamma].u_val =
+        rrd->rra_def[hw_index].par[RRA_hw_alpha].u_val;
+    current_rra->par[RRA_dependent_rra_idx].u_cnt = hw_index;
+    rrd->rra_def[hw_index].par[RRA_dependent_rra_idx].u_cnt =
+        rrd->stat_head->rra_cnt;
+
+    /* create the CF_DEVSEASONAL RRA */
+    (rrd->stat_head->rra_cnt)++;
+    current_rra = &(rrd->rra_def[rrd->stat_head->rra_cnt]);
+    strcpy(current_rra->cf_nam, "DEVSEASONAL");
+    current_rra->row_cnt = period;
+    current_rra->par[RRA_seasonal_smooth_idx].u_cnt = hashed_name % period;
+    current_rra->pdp_cnt = 1;
+    current_rra->par[RRA_seasonal_gamma].u_val =
+        rrd->rra_def[hw_index].par[RRA_hw_alpha].u_val;
+    current_rra->par[RRA_dependent_rra_idx].u_cnt = hw_index;
+
+    /* create the CF_DEVPREDICT RRA */
+    (rrd->stat_head->rra_cnt)++;
+    current_rra = &(rrd->rra_def[rrd->stat_head->rra_cnt]);
+    strcpy(current_rra->cf_nam, "DEVPREDICT");
+    current_rra->row_cnt = (rrd->rra_def[hw_index]).row_cnt;
+    current_rra->pdp_cnt = 1;
+    current_rra->par[RRA_dependent_rra_idx].u_cnt = hw_index + 2;   /* DEVSEASONAL */
+
+    /* create the CF_FAILURES RRA */
+    (rrd->stat_head->rra_cnt)++;
+    current_rra = &(rrd->rra_def[rrd->stat_head->rra_cnt]);
+    strcpy(current_rra->cf_nam, "FAILURES");
+    current_rra->row_cnt = period;
+    current_rra->pdp_cnt = 1;
+    current_rra->par[RRA_delta_pos].u_val = 2.0;
+    current_rra->par[RRA_delta_neg].u_val = 2.0;
+    current_rra->par[RRA_failure_threshold].u_cnt = 7;
+    current_rra->par[RRA_window_len].u_cnt = 9;
+    current_rra->par[RRA_dependent_rra_idx].u_cnt = hw_index + 2;   /* DEVSEASONAL */
+    return 0;
+}
+
+/* create and empty rrd file according to the specs given */
+
+int rrd_create_fn(
+    const char *file_name,
+    rrd_t *rrd)
+{
+    unsigned long i, ii;
+    rrd_value_t *unknown;
+    int       unkn_cnt;
+    rrd_file_t *rrd_file_dn;
+    rrd_t     rrd_dn;
+    unsigned  rrd_flags = RRD_READWRITE | RRD_CREAT;
+
+    if (opt_no_overwrite) {
+      rrd_flags |= RRD_EXCL ;
+    }
+
+    unkn_cnt = 0;
+    for (i = 0; i < rrd->stat_head->rra_cnt; i++)
+        unkn_cnt += rrd->stat_head->ds_cnt * rrd->rra_def[i].row_cnt;
+
+    if ((rrd_file_dn = rrd_open(file_name, rrd, rrd_flags)) == NULL) {
+        rrd_set_error("creating '%s': %s", file_name, rrd_strerror(errno));
+        rrd_free2(rrd);
+        return (-1);
+    }
+
+    rrd_write(rrd_file_dn, rrd->stat_head, sizeof(stat_head_t));
+
+    rrd_write(rrd_file_dn, rrd->ds_def, sizeof(ds_def_t) * rrd->stat_head->ds_cnt);
+
+    rrd_write(rrd_file_dn, rrd->rra_def,
+          sizeof(rra_def_t) * rrd->stat_head->rra_cnt);
+
+    rrd_write(rrd_file_dn, rrd->live_head, sizeof(live_head_t));
+
+    if ((rrd->pdp_prep = (pdp_prep_t*)calloc(1, sizeof(pdp_prep_t))) == NULL) {
+        rrd_set_error("allocating pdp_prep");
+        rrd_free2(rrd);
+        rrd_close(rrd_file_dn);
+        return (-1);
+    }
+
+    strcpy(rrd->pdp_prep->last_ds, "U");
+
+    rrd->pdp_prep->scratch[PDP_val].u_val = 0.0;
+    rrd->pdp_prep->scratch[PDP_unkn_sec_cnt].u_cnt =
+        rrd->live_head->last_up % rrd->stat_head->pdp_step;
+
+    for (i = 0; i < rrd->stat_head->ds_cnt; i++)
+        rrd_write(rrd_file_dn, rrd->pdp_prep, sizeof(pdp_prep_t));
+
+    if ((rrd->cdp_prep = (cdp_prep_t*)calloc(1, sizeof(cdp_prep_t))) == NULL) {
+        rrd_set_error("allocating cdp_prep");
+        rrd_free2(rrd);
+        rrd_close(rrd_file_dn);
+        return (-1);
+    }
+
+
+    for (i = 0; i < rrd->stat_head->rra_cnt; i++) {
+        switch (cf_conv(rrd->rra_def[i].cf_nam)) {
+        case CF_HWPREDICT:
+        case CF_MHWPREDICT:
+            init_hwpredict_cdp(rrd->cdp_prep);
+            break;
+        case CF_SEASONAL:
+        case CF_DEVSEASONAL:
+            init_seasonal_cdp(rrd->cdp_prep);
+            break;
+        case CF_FAILURES:
+            /* initialize violation history to 0 */
+            for (ii = 0; ii < MAX_CDP_PAR_EN; ii++) {
+                /* We can zero everything out, by setting u_val to the
+                 * NULL address. Each array entry in scratch is 8 bytes
+                 * (a double), but u_cnt only accessed 4 bytes (long) */
+                rrd->cdp_prep->scratch[ii].u_val = 0.0;
+            }
+            break;
+        default:
+            /* can not be zero because we don't know anything ... */
+            rrd->cdp_prep->scratch[CDP_val].u_val = DNAN;
+            /* startup missing pdp count */
+            rrd->cdp_prep->scratch[CDP_unkn_pdp_cnt].u_cnt =
+                ((rrd->live_head->last_up -
+                  rrd->pdp_prep->scratch[PDP_unkn_sec_cnt].u_cnt)
+                 % (rrd->stat_head->pdp_step
+                    * rrd->rra_def[i].pdp_cnt)) / rrd->stat_head->pdp_step;
+            break;
+        }
+
+        for (ii = 0; ii < rrd->stat_head->ds_cnt; ii++) {
+            rrd_write(rrd_file_dn, rrd->cdp_prep, sizeof(cdp_prep_t));
+        }
+    }
+
+    /* now, we must make sure that the rest of the rrd
+       struct is properly initialized */
+
+    if ((rrd->rra_ptr = (rra_ptr_t*)calloc(1, sizeof(rra_ptr_t))) == NULL) {
+        rrd_set_error("allocating rra_ptr");
+        rrd_free2(rrd);
+        rrd_close(rrd_file_dn);
+        return (-1);
+    }
+
+    /* changed this initialization to be consistent with
+     * rrd_restore. With the old value (0), the first update
+     * would occur for cur_row = 1 because rrd_update increments
+     * the pointer a priori. */
+    for (i = 0; i < rrd->stat_head->rra_cnt; i++) {
+        rrd->rra_ptr->cur_row = rrd_select_initial_row(rrd_file_dn, i, &rrd->rra_def[i]);
+        rrd_write(rrd_file_dn, rrd->rra_ptr, sizeof(rra_ptr_t));
+    }
+
+    /* write the empty data area */
+    if ((unknown = (rrd_value_t *) malloc(512 * sizeof(rrd_value_t))) == NULL) {
+        rrd_set_error("allocating unknown");
+        rrd_free2(rrd);
+        rrd_close(rrd_file_dn);
+        return (-1);
+    }
+    for (i = 0; i < 512; ++i)
+        unknown[i] = DNAN;
+
+    while (unkn_cnt > 0) {
+        if(rrd_write(rrd_file_dn, unknown, sizeof(rrd_value_t) * min(unkn_cnt, 512)) < 0)
+        {
+            rrd_set_error("creating rrd: %s", rrd_strerror(errno));
+            return -1;
+        }
+
+        unkn_cnt -= 512;
+    }
+    free(unknown);
+    rrd_free2(rrd);
+    if (rrd_close(rrd_file_dn) == -1) {
+        rrd_set_error("creating rrd: %s", rrd_strerror(errno));
+        return -1;
+    }
+    /* flush all we don't need out of the cache */
+    rrd_init(&rrd_dn);
+    if((rrd_file_dn = rrd_open(file_name, &rrd_dn, RRD_READONLY)) != NULL)
+    {
+        rrd_dontneed(rrd_file_dn, &rrd_dn);
+        /* rrd_free(&rrd_dn); */
+        rrd_close(rrd_file_dn);
+    }
+    return (0);
+}
+
+
+static void rrd_free2(
+    rrd_t *rrd)
+{
+    free(rrd->live_head);
+    free(rrd->stat_head);
+    free(rrd->ds_def);
+    free(rrd->rra_def);
+    free(rrd->rra_ptr);
+    free(rrd->pdp_prep);
+    free(rrd->cdp_prep);
+    free(rrd->rrd_value);
+}
+
diff --git a/program/src/rrd_daemon.c b/program/src/rrd_daemon.c
new file mode 100644 (file)
index 0000000..154f0de
--- /dev/null
@@ -0,0 +1,3198 @@
+/**
+ * RRDTool - src/rrd_daemon.c
+ * Copyright (C) 2008,2009 Florian octo Forster
+ * Copyright (C) 2008,2009 Kevin Brintnall
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ *   kevin brintnall <kbrint@rufus.net>
+ **/
+
+#if 0
+/*
+ * First tell the compiler to stick to the C99 and POSIX standards as close as
+ * possible.
+ */
+#ifndef __STRICT_ANSI__ /* {{{ */
+# define __STRICT_ANSI__
+#endif
+
+#ifndef _ISOC99_SOURCE
+# define _ISOC99_SOURCE
+#endif
+
+#ifdef _POSIX_C_SOURCE
+# undef _POSIX_C_SOURCE
+#endif
+#define _POSIX_C_SOURCE 200112L
+
+/* Single UNIX needed for strdup. */
+#ifdef _XOPEN_SOURCE
+# undef _XOPEN_SOURCE
+#endif
+#define _XOPEN_SOURCE 500
+
+#ifndef _REENTRANT
+# define _REENTRANT
+#endif
+
+#ifndef _THREAD_SAFE
+# define _THREAD_SAFE
+#endif
+
+#ifdef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* }}} */
+#endif /* 0 */
+
+/*
+ * Now for some includes..
+ */
+/* {{{ */
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__) && !defined(HAVE_CONFIG_H)
+#include "../win32/config.h"
+#else
+#ifdef HAVE_CONFIG_H
+#include "../rrd_config.h"
+#endif
+#endif
+
+#include "rrd.h"
+#include "rrd_client.h"
+
+#include <stdlib.h>
+
+#ifndef WIN32
+#ifdef HAVE_STDINT_H
+#  include <stdint.h>
+#endif
+#include <unistd.h>
+#include <strings.h>
+#include <inttypes.h>
+#include <sys/socket.h>
+
+#else
+
+#endif
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/un.h>
+#include <netdb.h>
+#include <poll.h>
+#include <syslog.h>
+#include <pthread.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/time.h>
+#include <time.h>
+#include <libgen.h>
+#include <grp.h>
+
+#include <glib-2.0/glib.h>
+/* }}} */
+
+#define RRDD_LOG(severity, ...) \
+  do { \
+    if (stay_foreground) \
+      fprintf(stderr, __VA_ARGS__); \
+    syslog ((severity), __VA_ARGS__); \
+  } while (0)
+
+#ifndef __GNUC__
+# define __attribute__(x) /**/
+#endif
+
+/*
+ * Types
+ */
+typedef enum { RESP_ERR = -1, RESP_OK = 0 } response_code;
+
+struct listen_socket_s
+{
+  int fd;
+  char addr[PATH_MAX + 1];
+  int family;
+
+  /* state for BATCH processing */
+  time_t batch_start;
+  int batch_cmd;
+
+  /* buffered IO */
+  char *rbuf;
+  off_t next_cmd;
+  off_t next_read;
+
+  char *wbuf;
+  ssize_t wbuf_len;
+
+  uint32_t permissions;
+
+  gid_t  socket_group;
+  mode_t socket_permissions;
+};
+typedef struct listen_socket_s listen_socket_t;
+
+struct command_s;
+typedef struct command_s command_t;
+/* note: guard against "unused" warnings in the handlers */
+#define DISPATCH_PROTO listen_socket_t *sock   __attribute__((unused)),\
+                       time_t now              __attribute__((unused)),\
+                       char  *buffer           __attribute__((unused)),\
+                       size_t buffer_size      __attribute__((unused))
+
+#define HANDLER_PROTO  command_t *cmd          __attribute__((unused)),\
+                       DISPATCH_PROTO
+
+struct command_s {
+  char   *cmd;
+  int (*handler)(HANDLER_PROTO);
+
+  char  context;               /* where we expect to see it */
+#define CMD_CONTEXT_CLIENT     (1<<0)
+#define CMD_CONTEXT_BATCH      (1<<1)
+#define CMD_CONTEXT_JOURNAL    (1<<2)
+#define CMD_CONTEXT_ANY                (0x7f)
+
+  char *syntax;
+  char *help;
+};
+
+struct cache_item_s;
+typedef struct cache_item_s cache_item_t;
+struct cache_item_s
+{
+  char *file;
+  char **values;
+  size_t values_num;
+  time_t last_flush_time;
+  time_t last_update_stamp;
+#define CI_FLAGS_IN_TREE  (1<<0)
+#define CI_FLAGS_IN_QUEUE (1<<1)
+  int flags;
+  pthread_cond_t  flushed;
+  cache_item_t *prev;
+  cache_item_t *next;
+};
+
+struct callback_flush_data_s
+{
+  time_t now;
+  time_t abs_timeout;
+  char **keys;
+  size_t keys_num;
+};
+typedef struct callback_flush_data_s callback_flush_data_t;
+
+enum queue_side_e
+{
+  HEAD,
+  TAIL
+};
+typedef enum queue_side_e queue_side_t;
+
+/* describe a set of journal files */
+typedef struct {
+  char **files;
+  size_t files_num;
+} journal_set;
+
+/* max length of socket command or response */
+#define CMD_MAX 4096
+#define RBUF_SIZE (CMD_MAX*2)
+
+/*
+ * Variables
+ */
+static int stay_foreground = 0;
+static uid_t daemon_uid;
+
+static listen_socket_t *listen_fds = NULL;
+static size_t listen_fds_num = 0;
+
+enum {
+  RUNNING,             /* normal operation */
+  FLUSHING,            /* flushing remaining values */
+  SHUTDOWN             /* shutting down */
+} state = RUNNING;
+
+static pthread_t *queue_threads;
+static pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
+static int config_queue_threads = 4;
+
+static pthread_t flush_thread;
+static pthread_cond_t flush_cond = PTHREAD_COND_INITIALIZER;
+
+static pthread_mutex_t connection_threads_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t  connection_threads_done = PTHREAD_COND_INITIALIZER;
+static int connection_threads_num = 0;
+
+/* Cache stuff */
+static GTree          *cache_tree = NULL;
+static cache_item_t   *cache_queue_head = NULL;
+static cache_item_t   *cache_queue_tail = NULL;
+static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static int config_write_interval = 300;
+static int config_write_jitter   = 0;
+static int config_flush_interval = 3600;
+static int config_flush_at_shutdown = 0;
+static char *config_pid_file = NULL;
+static char *config_base_dir = NULL;
+static size_t _config_base_dir_len = 0;
+static int config_write_base_only = 0;
+
+static listen_socket_t **config_listen_address_list = NULL;
+static size_t config_listen_address_list_len = 0;
+
+static uint64_t stats_queue_length = 0;
+static uint64_t stats_updates_received = 0;
+static uint64_t stats_flush_received = 0;
+static uint64_t stats_updates_written = 0;
+static uint64_t stats_data_sets_written = 0;
+static uint64_t stats_journal_bytes = 0;
+static uint64_t stats_journal_rotate = 0;
+static pthread_mutex_t stats_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Journaled updates */
+#define JOURNAL_REPLAY(s) ((s) == NULL)
+#define JOURNAL_BASE "rrd.journal"
+static journal_set *journal_cur = NULL;
+static journal_set *journal_old = NULL;
+static char *journal_dir = NULL;
+static FILE *journal_fh = NULL;                /* current journal file handle */
+static long  journal_size = 0;         /* current journal size */
+#define JOURNAL_MAX (1 * 1024 * 1024 * 1024)
+static pthread_mutex_t journal_lock = PTHREAD_MUTEX_INITIALIZER;
+static int journal_write(char *cmd, char *args);
+static void journal_done(void);
+static void journal_rotate(void);
+
+/* prototypes for forward refernces */
+static int handle_request_help (HANDLER_PROTO);
+
+/* 
+ * Functions
+ */
+static void sig_common (const char *sig) /* {{{ */
+{
+  RRDD_LOG(LOG_NOTICE, "caught SIG%s", sig);
+  state = FLUSHING;
+  pthread_cond_broadcast(&flush_cond);
+  pthread_cond_broadcast(&queue_cond);
+} /* }}} void sig_common */
+
+static void sig_int_handler (int s __attribute__((unused))) /* {{{ */
+{
+  sig_common("INT");
+} /* }}} void sig_int_handler */
+
+static void sig_term_handler (int s __attribute__((unused))) /* {{{ */
+{
+  sig_common("TERM");
+} /* }}} void sig_term_handler */
+
+static void sig_usr1_handler (int s __attribute__((unused))) /* {{{ */
+{
+  config_flush_at_shutdown = 1;
+  sig_common("USR1");
+} /* }}} void sig_usr1_handler */
+
+static void sig_usr2_handler (int s __attribute__((unused))) /* {{{ */
+{
+  config_flush_at_shutdown = 0;
+  sig_common("USR2");
+} /* }}} void sig_usr2_handler */
+
+static void install_signal_handlers(void) /* {{{ */
+{
+  /* These structures are static, because `sigaction' behaves weird if the are
+   * overwritten.. */
+  static struct sigaction sa_int;
+  static struct sigaction sa_term;
+  static struct sigaction sa_pipe;
+  static struct sigaction sa_usr1;
+  static struct sigaction sa_usr2;
+
+  /* Install signal handlers */
+  memset (&sa_int, 0, sizeof (sa_int));
+  sa_int.sa_handler = sig_int_handler;
+  sigaction (SIGINT, &sa_int, NULL);
+
+  memset (&sa_term, 0, sizeof (sa_term));
+  sa_term.sa_handler = sig_term_handler;
+  sigaction (SIGTERM, &sa_term, NULL);
+
+  memset (&sa_pipe, 0, sizeof (sa_pipe));
+  sa_pipe.sa_handler = SIG_IGN;
+  sigaction (SIGPIPE, &sa_pipe, NULL);
+
+  memset (&sa_pipe, 0, sizeof (sa_usr1));
+  sa_usr1.sa_handler = sig_usr1_handler;
+  sigaction (SIGUSR1, &sa_usr1, NULL);
+
+  memset (&sa_usr2, 0, sizeof (sa_usr2));
+  sa_usr2.sa_handler = sig_usr2_handler;
+  sigaction (SIGUSR2, &sa_usr2, NULL);
+
+} /* }}} void install_signal_handlers */
+
+static int open_pidfile(char *action, int oflag) /* {{{ */
+{
+  int fd;
+  const char *file;
+  char *file_copy, *dir;
+
+  file = (config_pid_file != NULL)
+    ? config_pid_file
+    : LOCALSTATEDIR "/run/rrdcached.pid";
+
+  /* dirname may modify its argument */
+  file_copy = strdup(file);
+  if (file_copy == NULL)
+  {
+    fprintf(stderr, "rrdcached: strdup(): %s\n",
+        rrd_strerror(errno));
+    return -1;
+  }
+
+  dir = dirname(file_copy);
+  if (rrd_mkdir_p(dir, 0777) != 0)
+  {
+    fprintf(stderr, "Failed to create pidfile directory '%s': %s\n",
+        dir, rrd_strerror(errno));
+    return -1;
+  }
+
+  free(file_copy);
+
+  fd = open(file, oflag, S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
+  if (fd < 0)
+    fprintf(stderr, "rrdcached: can't %s pid file '%s' (%s)\n",
+            action, file, rrd_strerror(errno));
+
+  return(fd);
+} /* }}} static int open_pidfile */
+
+/* check existing pid file to see whether a daemon is running */
+static int check_pidfile(void)
+{
+  int pid_fd;
+  pid_t pid;
+  char pid_str[16];
+
+  pid_fd = open_pidfile("open", O_RDWR);
+  if (pid_fd < 0)
+    return pid_fd;
+
+  if (read(pid_fd, pid_str, sizeof(pid_str)) <= 0)
+    return -1;
+
+  pid = atoi(pid_str);
+  if (pid <= 0)
+    return -1;
+
+  /* another running process that we can signal COULD be
+   * a competing rrdcached */
+  if (pid != getpid() && kill(pid, 0) == 0)
+  {
+    fprintf(stderr,
+            "FATAL: Another rrdcached daemon is running?? (pid %d)\n", pid);
+    close(pid_fd);
+    return -1;
+  }
+
+  lseek(pid_fd, 0, SEEK_SET);
+  if (ftruncate(pid_fd, 0) == -1)
+  {
+    fprintf(stderr,
+            "FATAL: Faild to truncate stale PID file. (pid %d)\n", pid);
+    close(pid_fd);
+    return -1;
+  }
+
+  fprintf(stderr,
+          "rrdcached: removed stale PID file (no rrdcached on pid %d)\n"
+          "rrdcached: starting normally.\n", pid);
+
+  return pid_fd;
+} /* }}} static int check_pidfile */
+
+static int write_pidfile (int fd) /* {{{ */
+{
+  pid_t pid;
+  FILE *fh;
+
+  pid = getpid ();
+
+  fh = fdopen (fd, "w");
+  if (fh == NULL)
+  {
+    RRDD_LOG (LOG_ERR, "write_pidfile: fdopen() failed.");
+    close(fd);
+    return (-1);
+  }
+
+  fprintf (fh, "%i\n", (int) pid);
+  fclose (fh);
+
+  return (0);
+} /* }}} int write_pidfile */
+
+static int remove_pidfile (void) /* {{{ */
+{
+  char *file;
+  int status;
+
+  file = (config_pid_file != NULL)
+    ? config_pid_file
+    : LOCALSTATEDIR "/run/rrdcached.pid";
+
+  status = unlink (file);
+  if (status == 0)
+    return (0);
+  return (errno);
+} /* }}} int remove_pidfile */
+
+static char *next_cmd (listen_socket_t *sock, ssize_t *len) /* {{{ */
+{
+  char *eol;
+
+  eol = memchr(sock->rbuf + sock->next_cmd, '\n',
+               sock->next_read - sock->next_cmd);
+
+  if (eol == NULL)
+  {
+    /* no commands left, move remainder back to front of rbuf */
+    memmove(sock->rbuf, sock->rbuf + sock->next_cmd,
+            sock->next_read - sock->next_cmd);
+    sock->next_read -= sock->next_cmd;
+    sock->next_cmd = 0;
+    *len = 0;
+    return NULL;
+  }
+  else
+  {
+    char *cmd = sock->rbuf + sock->next_cmd;
+    *eol = '\0';
+
+    sock->next_cmd = eol - sock->rbuf + 1;
+
+    if (eol > sock->rbuf && *(eol-1) == '\r')
+      *(--eol) = '\0'; /* handle "\r\n" EOL */
+
+    *len = eol - cmd;
+
+    return cmd;
+  }
+
+  /* NOTREACHED */
+  assert(1==0);
+} /* }}} char *next_cmd */
+
+/* add the characters directly to the write buffer */
+static int add_to_wbuf(listen_socket_t *sock, char *str, size_t len) /* {{{ */
+{
+  char *new_buf;
+
+  assert(sock != NULL);
+
+  new_buf = rrd_realloc(sock->wbuf, sock->wbuf_len + len + 1);
+  if (new_buf == NULL)
+  {
+    RRDD_LOG(LOG_ERR, "add_to_wbuf: realloc failed");
+    return -1;
+  }
+
+  strncpy(new_buf + sock->wbuf_len, str, len + 1);
+
+  sock->wbuf = new_buf;
+  sock->wbuf_len += len;
+
+  return 0;
+} /* }}} static int add_to_wbuf */
+
+/* add the text to the "extra" info that's sent after the status line */
+static int add_response_info(listen_socket_t *sock, char *fmt, ...) /* {{{ */
+{
+  va_list argp;
+  char buffer[CMD_MAX];
+  int len;
+
+  if (JOURNAL_REPLAY(sock)) return 0;
+  if (sock->batch_start) return 0; /* no extra info returned when in BATCH */
+
+  va_start(argp, fmt);
+#ifdef HAVE_VSNPRINTF
+  len = vsnprintf(buffer, sizeof(buffer), fmt, argp);
+#else
+  len = vsprintf(buffer, fmt, argp);
+#endif
+  va_end(argp);
+  if (len < 0)
+  {
+    RRDD_LOG(LOG_ERR, "add_response_info: vnsprintf failed");
+    return -1;
+  }
+
+  return add_to_wbuf(sock, buffer, len);
+} /* }}} static int add_response_info */
+
+static int count_lines(char *str) /* {{{ */
+{
+  int lines = 0;
+
+  if (str != NULL)
+  {
+    while ((str = strchr(str, '\n')) != NULL)
+    {
+      ++lines;
+      ++str;
+    }
+  }
+
+  return lines;
+} /* }}} static int count_lines */
+
+/* send the response back to the user.
+ * returns 0 on success, -1 on error
+ * write buffer is always zeroed after this call */
+static int send_response (listen_socket_t *sock, response_code rc,
+                          char *fmt, ...) /* {{{ */
+{
+  va_list argp;
+  char buffer[CMD_MAX];
+  int lines;
+  ssize_t wrote;
+  int rclen, len;
+
+  if (JOURNAL_REPLAY(sock)) return rc;
+
+  if (sock->batch_start)
+  {
+    if (rc == RESP_OK)
+      return rc; /* no response on success during BATCH */
+    lines = sock->batch_cmd;
+  }
+  else if (rc == RESP_OK)
+    lines = count_lines(sock->wbuf);
+  else
+    lines = -1;
+
+  rclen = sprintf(buffer, "%d ", lines);
+  va_start(argp, fmt);
+#ifdef HAVE_VSNPRINTF
+  len = vsnprintf(buffer+rclen, sizeof(buffer)-rclen, fmt, argp);
+#else
+  len = vsprintf(buffer+rclen, fmt, argp);
+#endif
+  va_end(argp);
+  if (len < 0)
+    return -1;
+
+  len += rclen;
+
+  /* append the result to the wbuf, don't write to the user */
+  if (sock->batch_start)
+    return add_to_wbuf(sock, buffer, len);
+
+  /* first write must be complete */
+  if (len != write(sock->fd, buffer, len))
+  {
+    RRDD_LOG(LOG_INFO, "send_response: could not write status message");
+    return -1;
+  }
+
+  if (sock->wbuf != NULL && rc == RESP_OK)
+  {
+    wrote = 0;
+    while (wrote < sock->wbuf_len)
+    {
+      ssize_t wb = write(sock->fd, sock->wbuf + wrote, sock->wbuf_len - wrote);
+      if (wb <= 0)
+      {
+        RRDD_LOG(LOG_INFO, "send_response: could not write results");
+        return -1;
+      }
+      wrote += wb;
+    }
+  }
+
+  free(sock->wbuf); sock->wbuf = NULL;
+  sock->wbuf_len = 0;
+
+  return 0;
+} /* }}} */
+
+static void wipe_ci_values(cache_item_t *ci, time_t when)
+{
+  ci->values = NULL;
+  ci->values_num = 0;
+
+  ci->last_flush_time = when;
+  if (config_write_jitter > 0)
+    ci->last_flush_time += (rrd_random() % config_write_jitter);
+}
+
+/* remove_from_queue
+ * remove a "cache_item_t" item from the queue.
+ * must hold 'cache_lock' when calling this
+ */
+static void remove_from_queue(cache_item_t *ci) /* {{{ */
+{
+  if (ci == NULL) return;
+  if ((ci->flags & CI_FLAGS_IN_QUEUE) == 0) return; /* not queued */
+
+  if (ci->prev == NULL)
+    cache_queue_head = ci->next; /* reset head */
+  else
+    ci->prev->next = ci->next;
+
+  if (ci->next == NULL)
+    cache_queue_tail = ci->prev; /* reset the tail */
+  else
+    ci->next->prev = ci->prev;
+
+  ci->next = ci->prev = NULL;
+  ci->flags &= ~CI_FLAGS_IN_QUEUE;
+
+  pthread_mutex_lock (&stats_lock);
+  assert (stats_queue_length > 0);
+  stats_queue_length--;
+  pthread_mutex_unlock (&stats_lock);
+
+} /* }}} static void remove_from_queue */
+
+/* free the resources associated with the cache_item_t
+ * must hold cache_lock when calling this function
+ */
+static void *free_cache_item(cache_item_t *ci) /* {{{ */
+{
+  if (ci == NULL) return NULL;
+
+  remove_from_queue(ci);
+
+  for (size_t i=0; i < ci->values_num; i++)
+    free(ci->values[i]);
+
+  free (ci->values);
+  free (ci->file);
+
+  /* in case anyone is waiting */
+  pthread_cond_broadcast(&ci->flushed);
+  pthread_cond_destroy(&ci->flushed);
+
+  free (ci);
+
+  return NULL;
+} /* }}} static void *free_cache_item */
+
+/*
+ * enqueue_cache_item:
+ * `cache_lock' must be acquired before calling this function!
+ */
+static int enqueue_cache_item (cache_item_t *ci, /* {{{ */
+    queue_side_t side)
+{
+  if (ci == NULL)
+    return (-1);
+
+  if (ci->values_num == 0)
+    return (0);
+
+  if (side == HEAD)
+  {
+    if (cache_queue_head == ci)
+      return 0;
+
+    /* remove if further down in queue */
+    remove_from_queue(ci);
+
+    ci->prev = NULL;
+    ci->next = cache_queue_head;
+    if (ci->next != NULL)
+      ci->next->prev = ci;
+    cache_queue_head = ci;
+
+    if (cache_queue_tail == NULL)
+      cache_queue_tail = cache_queue_head;
+  }
+  else /* (side == TAIL) */
+  {
+    /* We don't move values back in the list.. */
+    if (ci->flags & CI_FLAGS_IN_QUEUE)
+      return (0);
+
+    assert (ci->next == NULL);
+    assert (ci->prev == NULL);
+
+    ci->prev = cache_queue_tail;
+
+    if (cache_queue_tail == NULL)
+      cache_queue_head = ci;
+    else
+      cache_queue_tail->next = ci;
+
+    cache_queue_tail = ci;
+  }
+
+  ci->flags |= CI_FLAGS_IN_QUEUE;
+
+  pthread_cond_signal(&queue_cond);
+  pthread_mutex_lock (&stats_lock);
+  stats_queue_length++;
+  pthread_mutex_unlock (&stats_lock);
+
+  return (0);
+} /* }}} int enqueue_cache_item */
+
+/*
+ * tree_callback_flush:
+ * Called via `g_tree_foreach' in `flush_thread_main'. `cache_lock' is held
+ * while this is in progress.
+ */
+static gboolean tree_callback_flush (gpointer key, gpointer value, /* {{{ */
+    gpointer data)
+{
+  cache_item_t *ci;
+  callback_flush_data_t *cfd;
+
+  ci = (cache_item_t *) value;
+  cfd = (callback_flush_data_t *) data;
+
+  if (ci->flags & CI_FLAGS_IN_QUEUE)
+    return FALSE;
+
+  if (ci->values_num > 0
+      && (ci->last_flush_time <= cfd->abs_timeout || state != RUNNING))
+  {
+    enqueue_cache_item (ci, TAIL);
+  }
+  else if (((cfd->now - ci->last_flush_time) >= config_flush_interval)
+      && (ci->values_num <= 0))
+  {
+    assert ((char *) key == ci->file);
+    if (!rrd_add_ptr((void ***)&cfd->keys, &cfd->keys_num, (void *)key))
+    {
+      RRDD_LOG (LOG_ERR, "tree_callback_flush: rrd_add_ptrs failed.");
+      return (FALSE);
+    }
+  }
+
+  return (FALSE);
+} /* }}} gboolean tree_callback_flush */
+
+static int flush_old_values (int max_age)
+{
+  callback_flush_data_t cfd;
+  size_t k;
+
+  memset (&cfd, 0, sizeof (cfd));
+  /* Pass the current time as user data so that we don't need to call
+   * `time' for each node. */
+  cfd.now = time (NULL);
+  cfd.keys = NULL;
+  cfd.keys_num = 0;
+
+  if (max_age > 0)
+    cfd.abs_timeout = cfd.now - max_age;
+  else
+    cfd.abs_timeout = cfd.now + 2*config_write_jitter + 1;
+
+  /* `tree_callback_flush' will return the keys of all values that haven't
+   * been touched in the last `config_flush_interval' seconds in `cfd'.
+   * The char*'s in this array point to the same memory as ci->file, so we
+   * don't need to free them separately. */
+  g_tree_foreach (cache_tree, tree_callback_flush, (gpointer) &cfd);
+
+  for (k = 0; k < cfd.keys_num; k++)
+  {
+    gboolean status = g_tree_remove(cache_tree, cfd.keys[k]);
+    /* should never fail, since we have held the cache_lock
+     * the entire time */
+    assert(status == TRUE);
+  }
+
+  if (cfd.keys != NULL)
+  {
+    free (cfd.keys);
+    cfd.keys = NULL;
+  }
+
+  return (0);
+} /* int flush_old_values */
+
+static void *flush_thread_main (void *args __attribute__((unused))) /* {{{ */
+{
+  struct timeval now;
+  struct timespec next_flush;
+  int status;
+
+  gettimeofday (&now, NULL);
+  next_flush.tv_sec = now.tv_sec + config_flush_interval;
+  next_flush.tv_nsec = 1000 * now.tv_usec;
+
+  pthread_mutex_lock(&cache_lock);
+
+  while (state == RUNNING)
+  {
+    gettimeofday (&now, NULL);
+    if ((now.tv_sec > next_flush.tv_sec)
+        || ((now.tv_sec == next_flush.tv_sec)
+          && ((1000 * now.tv_usec) > next_flush.tv_nsec)))
+    {
+      RRDD_LOG(LOG_DEBUG, "flushing old values");
+
+      /* Determine the time of the next cache flush. */
+      next_flush.tv_sec = now.tv_sec + config_flush_interval;
+
+      /* Flush all values that haven't been written in the last
+       * `config_write_interval' seconds. */
+      flush_old_values (config_write_interval);
+
+      /* unlock the cache while we rotate so we don't block incoming
+       * updates if the fsync() blocks on disk I/O */
+      pthread_mutex_unlock(&cache_lock);
+      journal_rotate();
+      pthread_mutex_lock(&cache_lock);
+    }
+
+    status = pthread_cond_timedwait(&flush_cond, &cache_lock, &next_flush);
+    if (status != 0 && status != ETIMEDOUT)
+    {
+      RRDD_LOG (LOG_ERR, "flush_thread_main: "
+                "pthread_cond_timedwait returned %i.", status);
+    }
+  }
+
+  if (config_flush_at_shutdown)
+    flush_old_values (-1); /* flush everything */
+
+  state = SHUTDOWN;
+
+  pthread_mutex_unlock(&cache_lock);
+
+  return NULL;
+} /* void *flush_thread_main */
+
+static void *queue_thread_main (void *args __attribute__((unused))) /* {{{ */
+{
+  pthread_mutex_lock (&cache_lock);
+
+  while (state != SHUTDOWN
+         || (cache_queue_head != NULL && config_flush_at_shutdown))
+  {
+    cache_item_t *ci;
+    char *file;
+    char **values;
+    size_t values_num;
+    int status;
+
+    /* Now, check if there's something to store away. If not, wait until
+     * something comes in. */
+    if (cache_queue_head == NULL)
+    {
+      status = pthread_cond_wait (&queue_cond, &cache_lock);
+      if ((status != 0) && (status != ETIMEDOUT))
+      {
+        RRDD_LOG (LOG_ERR, "queue_thread_main: "
+            "pthread_cond_wait returned %i.", status);
+      }
+    }
+
+    /* Check if a value has arrived. This may be NULL if we timed out or there
+     * was an interrupt such as a signal. */
+    if (cache_queue_head == NULL)
+      continue;
+
+    ci = cache_queue_head;
+
+    /* copy the relevant parts */
+    file = strdup (ci->file);
+    if (file == NULL)
+    {
+      RRDD_LOG (LOG_ERR, "queue_thread_main: strdup failed.");
+      continue;
+    }
+
+    assert(ci->values != NULL);
+    assert(ci->values_num > 0);
+
+    values = ci->values;
+    values_num = ci->values_num;
+
+    wipe_ci_values(ci, time(NULL));
+    remove_from_queue(ci);
+
+    pthread_mutex_unlock (&cache_lock);
+
+    rrd_clear_error ();
+    status = rrd_update_r (file, NULL, (int) values_num, (void *) values);
+    if (status != 0)
+    {
+      RRDD_LOG (LOG_NOTICE, "queue_thread_main: "
+          "rrd_update_r (%s) failed with status %i. (%s)",
+          file, status, rrd_get_error());
+    }
+
+    journal_write("wrote", file);
+
+    /* Search again in the tree.  It's possible someone issued a "FORGET"
+     * while we were writing the update values. */
+    pthread_mutex_lock(&cache_lock);
+    ci = (cache_item_t *) g_tree_lookup(cache_tree, file);
+    if (ci)
+      pthread_cond_broadcast(&ci->flushed);
+    pthread_mutex_unlock(&cache_lock);
+
+    if (status == 0)
+    {
+      pthread_mutex_lock (&stats_lock);
+      stats_updates_written++;
+      stats_data_sets_written += values_num;
+      pthread_mutex_unlock (&stats_lock);
+    }
+
+    rrd_free_ptrs((void ***) &values, &values_num);
+    free(file);
+
+    pthread_mutex_lock (&cache_lock);
+  }
+  pthread_mutex_unlock (&cache_lock);
+
+  return (NULL);
+} /* }}} void *queue_thread_main */
+
+static int buffer_get_field (char **buffer_ret, /* {{{ */
+    size_t *buffer_size_ret, char **field_ret)
+{
+  char *buffer;
+  size_t buffer_pos;
+  size_t buffer_size;
+  char *field;
+  size_t field_size;
+  int status;
+
+  buffer = *buffer_ret;
+  buffer_pos = 0;
+  buffer_size = *buffer_size_ret;
+  field = *buffer_ret;
+  field_size = 0;
+
+  if (buffer_size <= 0)
+    return (-1);
+
+  /* This is ensured by `handle_request'. */
+  assert (buffer[buffer_size - 1] == '\0');
+
+  status = -1;
+  while (buffer_pos < buffer_size)
+  {
+    /* Check for end-of-field or end-of-buffer */
+    if (buffer[buffer_pos] == ' ' || buffer[buffer_pos] == '\0')
+    {
+      field[field_size] = 0;
+      field_size++;
+      buffer_pos++;
+      status = 0;
+      break;
+    }
+    /* Handle escaped characters. */
+    else if (buffer[buffer_pos] == '\\')
+    {
+      if (buffer_pos >= (buffer_size - 1))
+        break;
+      buffer_pos++;
+      field[field_size] = buffer[buffer_pos];
+      field_size++;
+      buffer_pos++;
+    }
+    /* Normal operation */ 
+    else
+    {
+      field[field_size] = buffer[buffer_pos];
+      field_size++;
+      buffer_pos++;
+    }
+  } /* while (buffer_pos < buffer_size) */
+
+  if (status != 0)
+    return (status);
+
+  *buffer_ret = buffer + buffer_pos;
+  *buffer_size_ret = buffer_size - buffer_pos;
+  *field_ret = field;
+
+  return (0);
+} /* }}} int buffer_get_field */
+
+/* if we're restricting writes to the base directory,
+ * check whether the file falls within the dir
+ * returns 1 if OK, otherwise 0
+ */
+static int check_file_access (const char *file, listen_socket_t *sock) /* {{{ */
+{
+  assert(file != NULL);
+
+  if (!config_write_base_only
+      || JOURNAL_REPLAY(sock)
+      || config_base_dir == NULL)
+    return 1;
+
+  if (strstr(file, "../") != NULL) goto err;
+
+  /* relative paths without "../" are ok */
+  if (*file != '/') return 1;
+
+  /* file must be of the format base + "/" + <1+ char filename> */
+  if (strlen(file) < _config_base_dir_len + 2) goto err;
+  if (strncmp(file, config_base_dir, _config_base_dir_len) != 0) goto err;
+  if (*(file + _config_base_dir_len) != '/') goto err;
+
+  return 1;
+
+err:
+  if (sock != NULL && sock->fd >= 0)
+    send_response(sock, RESP_ERR, "%s\n", rrd_strerror(EACCES));
+
+  return 0;
+} /* }}} static int check_file_access */
+
+/* when using a base dir, convert relative paths to absolute paths.
+ * if necessary, modifies the "filename" pointer to point
+ * to the new path created in "tmp".  "tmp" is provided
+ * by the caller and sizeof(tmp) must be >= PATH_MAX.
+ *
+ * this allows us to optimize for the expected case (absolute path)
+ * with a no-op.
+ */
+static void get_abs_path(char **filename, char *tmp)
+{
+  assert(tmp != NULL);
+  assert(filename != NULL && *filename != NULL);
+
+  if (config_base_dir == NULL || **filename == '/')
+    return;
+
+  snprintf(tmp, PATH_MAX, "%s/%s", config_base_dir, *filename);
+  *filename = tmp;
+} /* }}} static int get_abs_path */
+
+static int flush_file (const char *filename) /* {{{ */
+{
+  cache_item_t *ci;
+
+  pthread_mutex_lock (&cache_lock);
+
+  ci = (cache_item_t *) g_tree_lookup (cache_tree, filename);
+  if (ci == NULL)
+  {
+    pthread_mutex_unlock (&cache_lock);
+    return (ENOENT);
+  }
+
+  if (ci->values_num > 0)
+  {
+    /* Enqueue at head */
+    enqueue_cache_item (ci, HEAD);
+    pthread_cond_wait(&ci->flushed, &cache_lock);
+  }
+
+  /* DO NOT DO ANYTHING WITH ci HERE!!  The entry
+   * may have been purged during our cond_wait() */
+
+  pthread_mutex_unlock(&cache_lock);
+
+  return (0);
+} /* }}} int flush_file */
+
+static int syntax_error(listen_socket_t *sock, command_t *cmd) /* {{{ */
+{
+  char *err = "Syntax error.\n";
+
+  if (cmd && cmd->syntax)
+    err = cmd->syntax;
+
+  return send_response(sock, RESP_ERR, "Usage: %s", err);
+} /* }}} static int syntax_error() */
+
+static int handle_request_stats (HANDLER_PROTO) /* {{{ */
+{
+  uint64_t copy_queue_length;
+  uint64_t copy_updates_received;
+  uint64_t copy_flush_received;
+  uint64_t copy_updates_written;
+  uint64_t copy_data_sets_written;
+  uint64_t copy_journal_bytes;
+  uint64_t copy_journal_rotate;
+
+  uint64_t tree_nodes_number;
+  uint64_t tree_depth;
+
+  pthread_mutex_lock (&stats_lock);
+  copy_queue_length       = stats_queue_length;
+  copy_updates_received   = stats_updates_received;
+  copy_flush_received     = stats_flush_received;
+  copy_updates_written    = stats_updates_written;
+  copy_data_sets_written  = stats_data_sets_written;
+  copy_journal_bytes      = stats_journal_bytes;
+  copy_journal_rotate     = stats_journal_rotate;
+  pthread_mutex_unlock (&stats_lock);
+
+  pthread_mutex_lock (&cache_lock);
+  tree_nodes_number = (uint64_t) g_tree_nnodes (cache_tree);
+  tree_depth        = (uint64_t) g_tree_height (cache_tree);
+  pthread_mutex_unlock (&cache_lock);
+
+  add_response_info(sock,
+                    "QueueLength: %"PRIu64"\n", copy_queue_length);
+  add_response_info(sock,
+                    "UpdatesReceived: %"PRIu64"\n", copy_updates_received);
+  add_response_info(sock,
+                    "FlushesReceived: %"PRIu64"\n", copy_flush_received);
+  add_response_info(sock,
+                    "UpdatesWritten: %"PRIu64"\n", copy_updates_written);
+  add_response_info(sock,
+                    "DataSetsWritten: %"PRIu64"\n", copy_data_sets_written);
+  add_response_info(sock, "TreeNodesNumber: %"PRIu64"\n", tree_nodes_number);
+  add_response_info(sock, "TreeDepth: %"PRIu64"\n", tree_depth);
+  add_response_info(sock, "JournalBytes: %"PRIu64"\n", copy_journal_bytes);
+  add_response_info(sock, "JournalRotate: %"PRIu64"\n", copy_journal_rotate);
+
+  send_response(sock, RESP_OK, "Statistics follow\n");
+
+  return (0);
+} /* }}} int handle_request_stats */
+
+static int handle_request_flush (HANDLER_PROTO) /* {{{ */
+{
+  char *file, file_tmp[PATH_MAX];
+  int status;
+
+  status = buffer_get_field (&buffer, &buffer_size, &file);
+  if (status != 0)
+  {
+    return syntax_error(sock,cmd);
+  }
+  else
+  {
+    pthread_mutex_lock(&stats_lock);
+    stats_flush_received++;
+    pthread_mutex_unlock(&stats_lock);
+
+    get_abs_path(&file, file_tmp);
+    if (!check_file_access(file, sock)) return 0;
+
+    status = flush_file (file);
+    if (status == 0)
+      return send_response(sock, RESP_OK, "Successfully flushed %s.\n", file);
+    else if (status == ENOENT)
+    {
+      /* no file in our tree; see whether it exists at all */
+      struct stat statbuf;
+
+      memset(&statbuf, 0, sizeof(statbuf));
+      if (stat(file, &statbuf) == 0 && S_ISREG(statbuf.st_mode))
+        return send_response(sock, RESP_OK, "Nothing to flush: %s.\n", file);
+      else
+        return send_response(sock, RESP_ERR, "No such file: %s.\n", file);
+    }
+    else if (status < 0)
+      return send_response(sock, RESP_ERR, "Internal error.\n");
+    else
+      return send_response(sock, RESP_ERR, "Failed with status %i.\n", status);
+  }
+
+  /* NOTREACHED */
+  assert(1==0);
+} /* }}} int handle_request_flush */
+
+static int handle_request_flushall(HANDLER_PROTO) /* {{{ */
+{
+  RRDD_LOG(LOG_DEBUG, "Received FLUSHALL");
+
+  pthread_mutex_lock(&cache_lock);
+  flush_old_values(-1);
+  pthread_mutex_unlock(&cache_lock);
+
+  return send_response(sock, RESP_OK, "Started flush.\n");
+} /* }}} static int handle_request_flushall */
+
+static int handle_request_pending(HANDLER_PROTO) /* {{{ */
+{
+  int status;
+  char *file, file_tmp[PATH_MAX];
+  cache_item_t *ci;
+
+  status = buffer_get_field(&buffer, &buffer_size, &file);
+  if (status != 0)
+    return syntax_error(sock,cmd);
+
+  get_abs_path(&file, file_tmp);
+
+  pthread_mutex_lock(&cache_lock);
+  ci = g_tree_lookup(cache_tree, file);
+  if (ci == NULL)
+  {
+    pthread_mutex_unlock(&cache_lock);
+    return send_response(sock, RESP_ERR, "%s\n", rrd_strerror(ENOENT));
+  }
+
+  for (size_t i=0; i < ci->values_num; i++)
+    add_response_info(sock, "%s\n", ci->values[i]);
+
+  pthread_mutex_unlock(&cache_lock);
+  return send_response(sock, RESP_OK, "updates pending\n");
+} /* }}} static int handle_request_pending */
+
+static int handle_request_forget(HANDLER_PROTO) /* {{{ */
+{
+  int status;
+  gboolean found;
+  char *file, file_tmp[PATH_MAX];
+
+  status = buffer_get_field(&buffer, &buffer_size, &file);
+  if (status != 0)
+    return syntax_error(sock,cmd);
+
+  get_abs_path(&file, file_tmp);
+  if (!check_file_access(file, sock)) return 0;
+
+  pthread_mutex_lock(&cache_lock);
+  found = g_tree_remove(cache_tree, file);
+  pthread_mutex_unlock(&cache_lock);
+
+  if (found == TRUE)
+  {
+    if (!JOURNAL_REPLAY(sock))
+      journal_write("forget", file);
+
+    return send_response(sock, RESP_OK, "Gone!\n");
+  }
+  else
+    return send_response(sock, RESP_ERR, "%s\n", rrd_strerror(ENOENT));
+
+  /* NOTREACHED */
+  assert(1==0);
+} /* }}} static int handle_request_forget */
+
+static int handle_request_queue (HANDLER_PROTO) /* {{{ */
+{
+  cache_item_t *ci;
+
+  pthread_mutex_lock(&cache_lock);
+
+  ci = cache_queue_head;
+  while (ci != NULL)
+  {
+    add_response_info(sock, "%d %s\n", ci->values_num, ci->file);
+    ci = ci->next;
+  }
+
+  pthread_mutex_unlock(&cache_lock);
+
+  return send_response(sock, RESP_OK, "in queue.\n");
+} /* }}} int handle_request_queue */
+
+static int handle_request_update (HANDLER_PROTO) /* {{{ */
+{
+  char *file, file_tmp[PATH_MAX];
+  int values_num = 0;
+  int status;
+  char orig_buf[CMD_MAX];
+
+  cache_item_t *ci;
+
+  /* save it for the journal later */
+  if (!JOURNAL_REPLAY(sock))
+    strncpy(orig_buf, buffer, buffer_size);
+
+  status = buffer_get_field (&buffer, &buffer_size, &file);
+  if (status != 0)
+    return syntax_error(sock,cmd);
+
+  pthread_mutex_lock(&stats_lock);
+  stats_updates_received++;
+  pthread_mutex_unlock(&stats_lock);
+
+  get_abs_path(&file, file_tmp);
+  if (!check_file_access(file, sock)) return 0;
+
+  pthread_mutex_lock (&cache_lock);
+  ci = g_tree_lookup (cache_tree, file);
+
+  if (ci == NULL) /* {{{ */
+  {
+    struct stat statbuf;
+    cache_item_t *tmp;
+
+    /* don't hold the lock while we setup; stat(2) might block */
+    pthread_mutex_unlock(&cache_lock);
+
+    memset (&statbuf, 0, sizeof (statbuf));
+    status = stat (file, &statbuf);
+    if (status != 0)
+    {
+      RRDD_LOG (LOG_NOTICE, "handle_request_update: stat (%s) failed.", file);
+
+      status = errno;
+      if (status == ENOENT)
+        return send_response(sock, RESP_ERR, "No such file: %s\n", file);
+      else
+        return send_response(sock, RESP_ERR,
+                             "stat failed with error %i.\n", status);
+    }
+    if (!S_ISREG (statbuf.st_mode))
+      return send_response(sock, RESP_ERR, "Not a regular file: %s\n", file);
+
+    if (access(file, R_OK|W_OK) != 0)
+      return send_response(sock, RESP_ERR, "Cannot read/write %s: %s\n",
+                           file, rrd_strerror(errno));
+
+    ci = (cache_item_t *) malloc (sizeof (cache_item_t));
+    if (ci == NULL)
+    {
+      RRDD_LOG (LOG_ERR, "handle_request_update: malloc failed.");
+
+      return send_response(sock, RESP_ERR, "malloc failed.\n");
+    }
+    memset (ci, 0, sizeof (cache_item_t));
+
+    ci->file = strdup (file);
+    if (ci->file == NULL)
+    {
+      free (ci);
+      RRDD_LOG (LOG_ERR, "handle_request_update: strdup failed.");
+
+      return send_response(sock, RESP_ERR, "strdup failed.\n");
+    }
+
+    wipe_ci_values(ci, now);
+    ci->flags = CI_FLAGS_IN_TREE;
+    pthread_cond_init(&ci->flushed, NULL);
+
+    pthread_mutex_lock(&cache_lock);
+
+    /* another UPDATE might have added this entry in the meantime */
+    tmp = g_tree_lookup (cache_tree, file);
+    if (tmp == NULL)
+      g_tree_replace (cache_tree, (void *) ci->file, (void *) ci);
+    else
+    {
+      free_cache_item (ci);
+      ci = tmp;
+    }
+
+    /* state may have changed while we were unlocked */
+    if (state == SHUTDOWN)
+      return -1;
+  } /* }}} */
+  assert (ci != NULL);
+
+  /* don't re-write updates in replay mode */
+  if (!JOURNAL_REPLAY(sock))
+    journal_write("update", orig_buf);
+
+  while (buffer_size > 0)
+  {
+    char *value;
+    time_t stamp;
+    char *eostamp;
+
+    status = buffer_get_field (&buffer, &buffer_size, &value);
+    if (status != 0)
+    {
+      RRDD_LOG (LOG_INFO, "handle_request_update: Error reading field.");
+      break;
+    }
+
+    /* make sure update time is always moving forward */
+    stamp = strtol(value, &eostamp, 10);
+    if (eostamp == value || eostamp == NULL || *eostamp != ':')
+    {
+      pthread_mutex_unlock(&cache_lock);
+      return send_response(sock, RESP_ERR,
+                           "Cannot find timestamp in '%s'!\n", value);
+    }
+    else if (stamp <= ci->last_update_stamp)
+    {
+      pthread_mutex_unlock(&cache_lock);
+      return send_response(sock, RESP_ERR,
+                           "illegal attempt to update using time %ld when last"
+                           " update time is %ld (minimum one second step)\n",
+                           stamp, ci->last_update_stamp);
+    }
+    else
+      ci->last_update_stamp = stamp;
+
+    if (!rrd_add_strdup(&ci->values, &ci->values_num, value))
+    {
+      RRDD_LOG (LOG_ERR, "handle_request_update: rrd_add_strdup failed.");
+      continue;
+    }
+
+    values_num++;
+  }
+
+  if (((now - ci->last_flush_time) >= config_write_interval)
+      && ((ci->flags & CI_FLAGS_IN_QUEUE) == 0)
+      && (ci->values_num > 0))
+  {
+    enqueue_cache_item (ci, TAIL);
+  }
+
+  pthread_mutex_unlock (&cache_lock);
+
+  if (values_num < 1)
+    return send_response(sock, RESP_ERR, "No values updated.\n");
+  else
+    return send_response(sock, RESP_OK,
+                         "errors, enqueued %i value(s).\n", values_num);
+
+  /* NOTREACHED */
+  assert(1==0);
+
+} /* }}} int handle_request_update */
+
+/* we came across a "WROTE" entry during journal replay.
+ * throw away any values that we have accumulated for this file
+ */
+static int handle_request_wrote (HANDLER_PROTO) /* {{{ */
+{
+  cache_item_t *ci;
+  const char *file = buffer;
+
+  pthread_mutex_lock(&cache_lock);
+
+  ci = g_tree_lookup(cache_tree, file);
+  if (ci == NULL)
+  {
+    pthread_mutex_unlock(&cache_lock);
+    return (0);
+  }
+
+  if (ci->values)
+    rrd_free_ptrs((void ***) &ci->values, &ci->values_num);
+
+  wipe_ci_values(ci, now);
+  remove_from_queue(ci);
+
+  pthread_mutex_unlock(&cache_lock);
+  return (0);
+} /* }}} int handle_request_wrote */
+
+/* start "BATCH" processing */
+static int batch_start (HANDLER_PROTO) /* {{{ */
+{
+  int status;
+  if (sock->batch_start)
+    return send_response(sock, RESP_ERR, "Already in BATCH\n");
+
+  status = send_response(sock, RESP_OK,
+                         "Go ahead.  End with dot '.' on its own line.\n");
+  sock->batch_start = time(NULL);
+  sock->batch_cmd = 0;
+
+  return status;
+} /* }}} static int batch_start */
+
+/* finish "BATCH" processing and return results to the client */
+static int batch_done (HANDLER_PROTO) /* {{{ */
+{
+  assert(sock->batch_start);
+  sock->batch_start = 0;
+  sock->batch_cmd  = 0;
+  return send_response(sock, RESP_OK, "errors\n");
+} /* }}} static int batch_done */
+
+static int handle_request_quit (HANDLER_PROTO) /* {{{ */
+{
+  return -1;
+} /* }}} static int handle_request_quit */
+
+static command_t list_of_commands[] = { /* {{{ */
+  {
+    "UPDATE",
+    handle_request_update,
+    CMD_CONTEXT_ANY,
+    "UPDATE <filename> <values> [<values> ...]\n"
+    ,
+    "Adds the given file to the internal cache if it is not yet known and\n"
+    "appends the given value(s) to the entry. See the rrdcached(1) manpage\n"
+    "for details.\n"
+    "\n"
+    "Each <values> has the following form:\n"
+    "  <values> = <time>:<value>[:<value>[...]]\n"
+    "See the rrdupdate(1) manpage for details.\n"
+  },
+  {
+    "WROTE",
+    handle_request_wrote,
+    CMD_CONTEXT_JOURNAL,
+    NULL,
+    NULL
+  },
+  {
+    "FLUSH",
+    handle_request_flush,
+    CMD_CONTEXT_CLIENT | CMD_CONTEXT_BATCH,
+    "FLUSH <filename>\n"
+    ,
+    "Adds the given filename to the head of the update queue and returns\n"
+    "after it has been dequeued.\n"
+  },
+  {
+    "FLUSHALL",
+    handle_request_flushall,
+    CMD_CONTEXT_CLIENT,
+    "FLUSHALL\n"
+    ,
+    "Triggers writing of all pending updates.  Returns immediately.\n"
+  },
+  {
+    "PENDING",
+    handle_request_pending,
+    CMD_CONTEXT_CLIENT,
+    "PENDING <filename>\n"
+    ,
+    "Shows any 'pending' updates for a file, in order.\n"
+    "The updates shown have not yet been written to the underlying RRD file.\n"
+  },
+  {
+    "FORGET",
+    handle_request_forget,
+    CMD_CONTEXT_ANY,
+    "FORGET <filename>\n"
+    ,
+    "Removes the file completely from the cache.\n"
+    "Any pending updates for the file will be lost.\n"
+  },
+  {
+    "QUEUE",
+    handle_request_queue,
+    CMD_CONTEXT_CLIENT,
+    "QUEUE\n"
+    ,
+        "Shows all files in the output queue.\n"
+    "The output is zero or more lines in the following format:\n"
+    "(where <num_vals> is the number of values to be written)\n"
+    "\n"
+    "<num_vals> <filename>\n"
+  },
+  {
+    "STATS",
+    handle_request_stats,
+    CMD_CONTEXT_CLIENT,
+    "STATS\n"
+    ,
+    "Returns some performance counters, see the rrdcached(1) manpage for\n"
+    "a description of the values.\n"
+  },
+  {
+    "HELP",
+    handle_request_help,
+    CMD_CONTEXT_CLIENT,
+    "HELP [<command>]\n",
+    NULL, /* special! */
+  },
+  {
+    "BATCH",
+    batch_start,
+    CMD_CONTEXT_CLIENT,
+    "BATCH\n"
+    ,
+    "The 'BATCH' command permits the client to initiate a bulk load\n"
+    "   of commands to rrdcached.\n"
+    "\n"
+    "Usage:\n"
+    "\n"
+    "    client: BATCH\n"
+    "    server: 0 Go ahead.  End with dot '.' on its own line.\n"
+    "    client: command #1\n"
+    "    client: command #2\n"
+    "    client: ... and so on\n"
+    "    client: .\n"
+    "    server: 2 errors\n"
+    "    server: 7 message for command #7\n"
+    "    server: 9 message for command #9\n"
+    "\n"
+    "For more information, consult the rrdcached(1) documentation.\n"
+  },
+  {
+    ".",   /* BATCH terminator */
+    batch_done,
+    CMD_CONTEXT_BATCH,
+    NULL,
+    NULL
+  },
+  {
+    "QUIT",
+    handle_request_quit,
+    CMD_CONTEXT_CLIENT | CMD_CONTEXT_BATCH,
+    "QUIT\n"
+    ,
+    "Disconnect from rrdcached.\n"
+  }
+}; /* }}} command_t list_of_commands[] */
+static size_t list_of_commands_len = sizeof (list_of_commands)
+  / sizeof (list_of_commands[0]);
+
+static command_t *find_command(char *cmd)
+{
+  size_t i;
+
+  for (i = 0; i < list_of_commands_len; i++)
+    if (strcasecmp(cmd, list_of_commands[i].cmd) == 0)
+      return (&list_of_commands[i]);
+  return NULL;
+}
+
+/* We currently use the index in the `list_of_commands' array as a bit position
+ * in `listen_socket_t.permissions'. This member schould NEVER be accessed from
+ * outside these functions so that switching to a more elegant storage method
+ * is easily possible. */
+static ssize_t find_command_index (const char *cmd) /* {{{ */
+{
+  size_t i;
+
+  for (i = 0; i < list_of_commands_len; i++)
+    if (strcasecmp(cmd, list_of_commands[i].cmd) == 0)
+      return ((ssize_t) i);
+  return (-1);
+} /* }}} ssize_t find_command_index */
+
+static int socket_permission_check (listen_socket_t *sock, /* {{{ */
+    const char *cmd)
+{
+  ssize_t i;
+
+  if (JOURNAL_REPLAY(sock))
+    return (1);
+
+  if (cmd == NULL)
+    return (-1);
+
+  if ((strcasecmp ("QUIT", cmd) == 0)
+      || (strcasecmp ("HELP", cmd) == 0))
+    return (1);
+  else if (strcmp (".", cmd) == 0)
+    cmd = "BATCH";
+
+  i = find_command_index (cmd);
+  if (i < 0)
+    return (-1);
+  assert (i < 32);
+
+  if ((sock->permissions & (1 << i)) != 0)
+    return (1);
+  return (0);
+} /* }}} int socket_permission_check */
+
+static int socket_permission_add (listen_socket_t *sock, /* {{{ */
+    const char *cmd)
+{
+  ssize_t i;
+
+  i = find_command_index (cmd);
+  if (i < 0)
+    return (-1);
+  assert (i < 32);
+
+  sock->permissions |= (1 << i);
+  return (0);
+} /* }}} int socket_permission_add */
+
+/* check whether commands are received in the expected context */
+static int command_check_context(listen_socket_t *sock, command_t *cmd)
+{
+  if (JOURNAL_REPLAY(sock))
+    return (cmd->context & CMD_CONTEXT_JOURNAL);
+  else if (sock->batch_start)
+    return (cmd->context & CMD_CONTEXT_BATCH);
+  else
+    return (cmd->context & CMD_CONTEXT_CLIENT);
+
+  /* NOTREACHED */
+  assert(1==0);
+}
+
+static int handle_request_help (HANDLER_PROTO) /* {{{ */
+{
+  int status;
+  char *cmd_str;
+  char *resp_txt;
+  command_t *help = NULL;
+
+  status = buffer_get_field (&buffer, &buffer_size, &cmd_str);
+  if (status == 0)
+    help = find_command(cmd_str);
+
+  if (help && (help->syntax || help->help))
+  {
+    char tmp[CMD_MAX];
+
+    snprintf(tmp, sizeof(tmp)-1, "Help for %s\n", help->cmd);
+    resp_txt = tmp;
+
+    if (help->syntax)
+      add_response_info(sock, "Usage: %s\n", help->syntax);
+
+    if (help->help)
+      add_response_info(sock, "%s\n", help->help);
+  }
+  else
+  {
+    size_t i;
+
+    resp_txt = "Command overview\n";
+
+    for (i = 0; i < list_of_commands_len; i++)
+    {
+      if (list_of_commands[i].syntax == NULL)
+        continue;
+      add_response_info (sock, "%s", list_of_commands[i].syntax);
+    }
+  }
+
+  return send_response(sock, RESP_OK, resp_txt);
+} /* }}} int handle_request_help */
+
+static int handle_request (DISPATCH_PROTO) /* {{{ */
+{
+  char *buffer_ptr = buffer;
+  char *cmd_str = NULL;
+  command_t *cmd = NULL;
+  int status;
+
+  assert (buffer[buffer_size - 1] == '\0');
+
+  status = buffer_get_field (&buffer_ptr, &buffer_size, &cmd_str);
+  if (status != 0)
+  {
+    RRDD_LOG (LOG_INFO, "handle_request: Unable parse command.");
+    return (-1);
+  }
+
+  if (sock != NULL && sock->batch_start)
+    sock->batch_cmd++;
+
+  cmd = find_command(cmd_str);
+  if (!cmd)
+    return send_response(sock, RESP_ERR, "Unknown command: %s\n", cmd_str);
+
+  if (!socket_permission_check (sock, cmd->cmd))
+    return send_response(sock, RESP_ERR, "Permission denied.\n");
+
+  if (!command_check_context(sock, cmd))
+    return send_response(sock, RESP_ERR, "Can't use '%s' here.\n", cmd_str);
+
+  return cmd->handler(cmd, sock, now, buffer_ptr, buffer_size);
+} /* }}} int handle_request */
+
+static void journal_set_free (journal_set *js) /* {{{ */
+{
+  if (js == NULL)
+    return;
+
+  rrd_free_ptrs((void ***) &js->files, &js->files_num);
+
+  free(js);
+} /* }}} journal_set_free */
+
+static void journal_set_remove (journal_set *js) /* {{{ */
+{
+  if (js == NULL)
+    return;
+
+  for (uint i=0; i < js->files_num; i++)
+  {
+    RRDD_LOG(LOG_DEBUG, "removing old journal %s", js->files[i]);
+    unlink(js->files[i]);
+  }
+} /* }}} journal_set_remove */
+
+/* close current journal file handle.
+ * MUST hold journal_lock before calling */
+static void journal_close(void) /* {{{ */
+{
+  if (journal_fh != NULL)
+  {
+    if (fclose(journal_fh) != 0)
+      RRDD_LOG(LOG_ERR, "cannot close journal: %s", rrd_strerror(errno));
+  }
+
+  journal_fh = NULL;
+  journal_size = 0;
+} /* }}} journal_close */
+
+/* MUST hold journal_lock before calling */
+static void journal_new_file(void) /* {{{ */
+{
+  struct timeval now;
+  int  new_fd;
+  char new_file[PATH_MAX + 1];
+
+  assert(journal_dir != NULL);
+  assert(journal_cur != NULL);
+
+  journal_close();
+
+  gettimeofday(&now, NULL);
+  /* this format assures that the files sort in strcmp() order */
+  snprintf(new_file, PATH_MAX, "%s/%s.%010d.%06d",
+           journal_dir, JOURNAL_BASE, (int)now.tv_sec, (int)now.tv_usec);
+
+  new_fd = open(new_file, O_WRONLY|O_CREAT|O_APPEND,
+                S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+  if (new_fd < 0)
+    goto error;
+
+  journal_fh = fdopen(new_fd, "a");
+  if (journal_fh == NULL)
+    goto error;
+
+  journal_size = ftell(journal_fh);
+  RRDD_LOG(LOG_DEBUG, "started new journal %s", new_file);
+
+  /* record the file in the journal set */
+  rrd_add_strdup(&journal_cur->files, &journal_cur->files_num, new_file);
+
+  return;
+
+error:
+  RRDD_LOG(LOG_CRIT,
+           "JOURNALING DISABLED: Error while trying to create %s : %s",
+           new_file, rrd_strerror(errno));
+  RRDD_LOG(LOG_CRIT,
+           "JOURNALING DISABLED: All values will be flushed at shutdown");
+
+  close(new_fd);
+  config_flush_at_shutdown = 1;
+
+} /* }}} journal_new_file */
+
+/* MUST NOT hold journal_lock before calling this */
+static void journal_rotate(void) /* {{{ */
+{
+  journal_set *old_js = NULL;
+
+  if (journal_dir == NULL)
+    return;
+
+  RRDD_LOG(LOG_DEBUG, "rotating journals");
+
+  pthread_mutex_lock(&stats_lock);
+  ++stats_journal_rotate;
+  pthread_mutex_unlock(&stats_lock);
+
+  pthread_mutex_lock(&journal_lock);
+
+  journal_close();
+
+  /* rotate the journal sets */
+  old_js = journal_old;
+  journal_old = journal_cur;
+  journal_cur = calloc(1, sizeof(journal_set));
+
+  if (journal_cur != NULL)
+    journal_new_file();
+  else
+    RRDD_LOG(LOG_CRIT, "journal_rotate: malloc(journal_set) failed\n");
+
+  pthread_mutex_unlock(&journal_lock);
+
+  journal_set_remove(old_js);
+  journal_set_free  (old_js);
+
+} /* }}} static void journal_rotate */
+
+/* MUST hold journal_lock when calling */
+static void journal_done(void) /* {{{ */
+{
+  if (journal_cur == NULL)
+    return;
+
+  journal_close();
+
+  if (config_flush_at_shutdown)
+  {
+    RRDD_LOG(LOG_INFO, "removing journals");
+    journal_set_remove(journal_old);
+    journal_set_remove(journal_cur);
+  }
+  else
+  {
+    RRDD_LOG(LOG_INFO, "expedited shutdown; "
+             "journals will be used at next startup");
+  }
+
+  journal_set_free(journal_cur);
+  journal_set_free(journal_old);
+  free(journal_dir);
+
+} /* }}} static void journal_done */
+
+static int journal_write(char *cmd, char *args) /* {{{ */
+{
+  int chars;
+
+  if (journal_fh == NULL)
+    return 0;
+
+  pthread_mutex_lock(&journal_lock);
+  chars = fprintf(journal_fh, "%s %s\n", cmd, args);
+  journal_size += chars;
+
+  if (journal_size > JOURNAL_MAX)
+    journal_new_file();
+
+  pthread_mutex_unlock(&journal_lock);
+
+  if (chars > 0)
+  {
+    pthread_mutex_lock(&stats_lock);
+    stats_journal_bytes += chars;
+    pthread_mutex_unlock(&stats_lock);
+  }
+
+  return chars;
+} /* }}} static int journal_write */
+
+static int journal_replay (const char *file) /* {{{ */
+{
+  FILE *fh;
+  int entry_cnt = 0;
+  int fail_cnt = 0;
+  uint64_t line = 0;
+  char entry[CMD_MAX];
+  time_t now;
+
+  if (file == NULL) return 0;
+
+  {
+    char *reason = "unknown error";
+    int status = 0;
+    struct stat statbuf;
+
+    memset(&statbuf, 0, sizeof(statbuf));
+    if (stat(file, &statbuf) != 0)
+    {
+      reason = "stat error";
+      status = errno;
+    }
+    else if (!S_ISREG(statbuf.st_mode))
+    {
+      reason = "not a regular file";
+      status = EPERM;
+    }
+    if (statbuf.st_uid != daemon_uid)
+    {
+      reason = "not owned by daemon user";
+      status = EACCES;
+    }
+    if (statbuf.st_mode & (S_IWGRP|S_IWOTH))
+    {
+      reason = "must not be user/group writable";
+      status = EACCES;
+    }
+
+    if (status != 0)
+    {
+      RRDD_LOG(LOG_ERR, "journal_replay: %s : %s (%s)",
+               file, rrd_strerror(status), reason);
+      return 0;
+    }
+  }
+
+  fh = fopen(file, "r");
+  if (fh == NULL)
+  {
+    if (errno != ENOENT)
+      RRDD_LOG(LOG_ERR, "journal_replay: cannot open journal file: '%s' (%s)",
+               file, rrd_strerror(errno));
+    return 0;
+  }
+  else
+    RRDD_LOG(LOG_NOTICE, "replaying from journal: %s", file);
+
+  now = time(NULL);
+
+  while(!feof(fh))
+  {
+    size_t entry_len;
+
+    ++line;
+    if (fgets(entry, sizeof(entry), fh) == NULL)
+      break;
+    entry_len = strlen(entry);
+
+    /* check \n termination in case journal writing crashed mid-line */
+    if (entry_len == 0)
+      continue;
+    else if (entry[entry_len - 1] != '\n')
+    {
+      RRDD_LOG(LOG_NOTICE, "Malformed journal entry at line %"PRIu64, line);
+      ++fail_cnt;
+      continue;
+    }
+
+    entry[entry_len - 1] = '\0';
+
+    if (handle_request(NULL, now, entry, entry_len) == 0)
+      ++entry_cnt;
+    else
+      ++fail_cnt;
+  }
+
+  fclose(fh);
+
+  RRDD_LOG(LOG_INFO, "Replayed %d entries (%d failures)",
+           entry_cnt, fail_cnt);
+
+  return entry_cnt > 0 ? 1 : 0;
+} /* }}} static int journal_replay */
+
+static int journal_sort(const void *v1, const void *v2)
+{
+  char **jn1 = (char **) v1;
+  char **jn2 = (char **) v2;
+
+  return strcmp(*jn1,*jn2);
+}
+
+static void journal_init(void) /* {{{ */
+{
+  int had_journal = 0;
+  DIR *dir;
+  struct dirent *dent;
+  char path[PATH_MAX+1];
+
+  if (journal_dir == NULL) return;
+
+  pthread_mutex_lock(&journal_lock);
+
+  journal_cur = calloc(1, sizeof(journal_set));
+  if (journal_cur == NULL)
+  {
+    RRDD_LOG(LOG_CRIT, "journal_rotate: malloc(journal_set) failed\n");
+    return;
+  }
+
+  RRDD_LOG(LOG_INFO, "checking for journal files");
+
+  /* Handle old journal files during transition.  This gives them the
+   * correct sort order.  TODO: remove after first release
+   */
+  {
+    char old_path[PATH_MAX+1];
+    snprintf(old_path, PATH_MAX, "%s/%s", journal_dir, JOURNAL_BASE ".old" );
+    snprintf(path,     PATH_MAX, "%s/%s", journal_dir, JOURNAL_BASE ".0000");
+    rename(old_path, path);
+
+    snprintf(old_path, PATH_MAX, "%s/%s", journal_dir, JOURNAL_BASE        );
+    snprintf(path,     PATH_MAX, "%s/%s", journal_dir, JOURNAL_BASE ".0001");
+    rename(old_path, path);
+  }
+
+  dir = opendir(journal_dir);
+  while ((dent = readdir(dir)) != NULL)
+  {
+    /* looks like a journal file? */
+    if (strncmp(dent->d_name, JOURNAL_BASE, strlen(JOURNAL_BASE)))
+      continue;
+
+    snprintf(path, PATH_MAX, "%s/%s", journal_dir, dent->d_name);
+
+    if (!rrd_add_strdup(&journal_cur->files, &journal_cur->files_num, path))
+    {
+      RRDD_LOG(LOG_CRIT, "journal_init: cannot add journal file %s!",
+               dent->d_name);
+      break;
+    }
+  }
+  closedir(dir);
+
+  qsort(journal_cur->files, journal_cur->files_num,
+        sizeof(journal_cur->files[0]), journal_sort);
+
+  for (uint i=0; i < journal_cur->files_num; i++)
+    had_journal += journal_replay(journal_cur->files[i]);
+
+  journal_new_file();
+
+  /* it must have been a crash.  start a flush */
+  if (had_journal && config_flush_at_shutdown)
+    flush_old_values(-1);
+
+  pthread_mutex_unlock(&journal_lock);
+
+  RRDD_LOG(LOG_INFO, "journal processing complete");
+
+} /* }}} static void journal_init */
+
+static void free_listen_socket(listen_socket_t *sock) /* {{{ */
+{
+  assert(sock != NULL);
+
+  free(sock->rbuf);  sock->rbuf = NULL;
+  free(sock->wbuf);  sock->wbuf = NULL;
+  free(sock);
+} /* }}} void free_listen_socket */
+
+static void close_connection(listen_socket_t *sock) /* {{{ */
+{
+  if (sock->fd >= 0)
+  {
+    close(sock->fd);
+    sock->fd = -1;
+  }
+
+  free_listen_socket(sock);
+
+} /* }}} void close_connection */
+
+static void *connection_thread_main (void *args) /* {{{ */
+{
+  listen_socket_t *sock;
+  int fd;
+
+  sock = (listen_socket_t *) args;
+  fd = sock->fd;
+
+  /* init read buffers */
+  sock->next_read = sock->next_cmd = 0;
+  sock->rbuf = malloc(RBUF_SIZE);
+  if (sock->rbuf == NULL)
+  {
+    RRDD_LOG(LOG_ERR, "connection_thread_main: cannot malloc read buffer");
+    close_connection(sock);
+    return NULL;
+  }
+
+  pthread_mutex_lock (&connection_threads_lock);
+  connection_threads_num++;
+  pthread_mutex_unlock (&connection_threads_lock);
+
+  while (state == RUNNING)
+  {
+    char *cmd;
+    ssize_t cmd_len;
+    ssize_t rbytes;
+    time_t now;
+
+    struct pollfd pollfd;
+    int status;
+
+    pollfd.fd = fd;
+    pollfd.events = POLLIN | POLLPRI;
+    pollfd.revents = 0;
+
+    status = poll (&pollfd, 1, /* timeout = */ 500);
+    if (state != RUNNING)
+      break;
+    else if (status == 0) /* timeout */
+      continue;
+    else if (status < 0) /* error */
+    {
+      status = errno;
+      if (status != EINTR)
+        RRDD_LOG (LOG_ERR, "connection_thread_main: poll(2) failed.");
+      continue;
+    }
+
+    if ((pollfd.revents & POLLHUP) != 0) /* normal shutdown */
+      break;
+    else if ((pollfd.revents & (POLLIN | POLLPRI)) == 0)
+    {
+      RRDD_LOG (LOG_WARNING, "connection_thread_main: "
+          "poll(2) returned something unexpected: %#04hx",
+          pollfd.revents);
+      break;
+    }
+
+    rbytes = read(fd, sock->rbuf + sock->next_read,
+                  RBUF_SIZE - sock->next_read);
+    if (rbytes < 0)
+    {
+      RRDD_LOG(LOG_ERR, "connection_thread_main: read() failed.");
+      break;
+    }
+    else if (rbytes == 0)
+      break; /* eof */
+
+    sock->next_read += rbytes;
+
+    if (sock->batch_start)
+      now = sock->batch_start;
+    else
+      now = time(NULL);
+
+    while ((cmd = next_cmd(sock, &cmd_len)) != NULL)
+    {
+      status = handle_request (sock, now, cmd, cmd_len+1);
+      if (status != 0)
+        goto out_close;
+    }
+  }
+
+out_close:
+  close_connection(sock);
+
+  /* Remove this thread from the connection threads list */
+  pthread_mutex_lock (&connection_threads_lock);
+  connection_threads_num--;
+  if (connection_threads_num <= 0)
+    pthread_cond_broadcast(&connection_threads_done);
+  pthread_mutex_unlock (&connection_threads_lock);
+
+  return (NULL);
+} /* }}} void *connection_thread_main */
+
+static int open_listen_socket_unix (const listen_socket_t *sock) /* {{{ */
+{
+  int fd;
+  struct sockaddr_un sa;
+  listen_socket_t *temp;
+  int status;
+  const char *path;
+  char *path_copy, *dir;
+
+  path = sock->addr;
+  if (strncmp(path, "unix:", strlen("unix:")) == 0)
+    path += strlen("unix:");
+
+  /* dirname may modify its argument */
+  path_copy = strdup(path);
+  if (path_copy == NULL)
+  {
+    fprintf(stderr, "rrdcached: strdup(): %s\n",
+        rrd_strerror(errno));
+    return (-1);
+  }
+
+  dir = dirname(path_copy);
+  if (rrd_mkdir_p(dir, 0777) != 0)
+  {
+    fprintf(stderr, "Failed to create socket directory '%s': %s\n",
+        dir, rrd_strerror(errno));
+    return (-1);
+  }
+
+  free(path_copy);
+
+  temp = (listen_socket_t *) rrd_realloc (listen_fds,
+      sizeof (listen_fds[0]) * (listen_fds_num + 1));
+  if (temp == NULL)
+  {
+    fprintf (stderr, "rrdcached: open_listen_socket_unix: realloc failed.\n");
+    return (-1);
+  }
+  listen_fds = temp;
+  memcpy (listen_fds + listen_fds_num, sock, sizeof (listen_fds[0]));
+
+  fd = socket (PF_UNIX, SOCK_STREAM, /* protocol = */ 0);
+  if (fd < 0)
+  {
+    fprintf (stderr, "rrdcached: unix socket(2) failed: %s\n",
+             rrd_strerror(errno));
+    return (-1);
+  }
+
+  memset (&sa, 0, sizeof (sa));
+  sa.sun_family = AF_UNIX;
+  strncpy (sa.sun_path, path, sizeof (sa.sun_path) - 1);
+
+  /* if we've gotten this far, we own the pid file.  any daemon started
+   * with the same args must not be alive.  therefore, ensure that we can
+   * create the socket...
+   */
+  unlink(path);
+
+  status = bind (fd, (struct sockaddr *) &sa, sizeof (sa));
+  if (status != 0)
+  {
+    fprintf (stderr, "rrdcached: bind(%s) failed: %s.\n",
+             path, rrd_strerror(errno));
+    close (fd);
+    return (-1);
+  }
+
+  /* tweak the sockets group ownership */
+  if (sock->socket_group != (gid_t)-1)
+  {
+    if ( (chown(path, getuid(), sock->socket_group) != 0) ||
+        (chmod(path, (S_IRUSR|S_IWUSR|S_IXUSR | S_IRGRP|S_IWGRP)) != 0) )
+    {
+      fprintf(stderr, "rrdcached: failed to set socket group permissions (%s)\n", strerror(errno));
+    }
+  }
+
+  if (sock->socket_permissions != (mode_t)-1)
+  {
+    if (chmod(path, sock->socket_permissions) != 0)
+      fprintf(stderr, "rrdcached: failed to set socket file permissions (%o): %s\n",
+          (unsigned int)sock->socket_permissions, strerror(errno));
+  }
+
+  status = listen (fd, /* backlog = */ 10);
+  if (status != 0)
+  {
+    fprintf (stderr, "rrdcached: listen(%s) failed: %s.\n",
+             path, rrd_strerror(errno));
+    close (fd);
+    unlink (path);
+    return (-1);
+  }
+
+  listen_fds[listen_fds_num].fd = fd;
+  listen_fds[listen_fds_num].family = PF_UNIX;
+  strncpy(listen_fds[listen_fds_num].addr, path,
+          sizeof (listen_fds[listen_fds_num].addr) - 1);
+  listen_fds_num++;
+
+  return (0);
+} /* }}} int open_listen_socket_unix */
+
+static int open_listen_socket_network(const listen_socket_t *sock) /* {{{ */
+{
+  struct addrinfo ai_hints;
+  struct addrinfo *ai_res;
+  struct addrinfo *ai_ptr;
+  char addr_copy[NI_MAXHOST];
+  char *addr;
+  char *port;
+  int status;
+
+  strncpy (addr_copy, sock->addr, sizeof(addr_copy)-1);
+  addr_copy[sizeof (addr_copy) - 1] = 0;
+  addr = addr_copy;
+
+  memset (&ai_hints, 0, sizeof (ai_hints));
+  ai_hints.ai_flags = 0;
+#ifdef AI_ADDRCONFIG
+  ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+  ai_hints.ai_family = AF_UNSPEC;
+  ai_hints.ai_socktype = SOCK_STREAM;
+
+  port = NULL;
+  if (*addr == '[') /* IPv6+port format */
+  {
+    /* `addr' is something like "[2001:780:104:2:211:24ff:feab:26f8]:12345" */
+    addr++;
+
+    port = strchr (addr, ']');
+    if (port == NULL)
+    {
+      fprintf (stderr, "rrdcached: Malformed address: %s\n", sock->addr);
+      return (-1);
+    }
+    *port = 0;
+    port++;
+
+    if (*port == ':')
+      port++;
+    else if (*port == 0)
+      port = NULL;
+    else
+    {
+      fprintf (stderr, "rrdcached: Garbage after address: %s\n", port);
+      return (-1);
+    }
+  } /* if (*addr == '[') */
+  else
+  {
+    port = rindex(addr, ':');
+    if (port != NULL)
+    {
+      *port = 0;
+      port++;
+    }
+  }
+  ai_res = NULL;
+  status = getaddrinfo (addr,
+                        port == NULL ? RRDCACHED_DEFAULT_PORT : port,
+                        &ai_hints, &ai_res);
+  if (status != 0)
+  {
+    fprintf (stderr, "rrdcached: getaddrinfo(%s) failed: %s\n",
+             addr, gai_strerror (status));
+    return (-1);
+  }
+
+  for (ai_ptr = ai_res; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+  {
+    int fd;
+    listen_socket_t *temp;
+    int one = 1;
+
+    temp = (listen_socket_t *) rrd_realloc (listen_fds,
+        sizeof (listen_fds[0]) * (listen_fds_num + 1));
+    if (temp == NULL)
+    {
+      fprintf (stderr,
+               "rrdcached: open_listen_socket_network: realloc failed.\n");
+      continue;
+    }
+    listen_fds = temp;
+    memcpy (listen_fds + listen_fds_num, sock, sizeof (listen_fds[0]));
+
+    fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
+    if (fd < 0)
+    {
+      fprintf (stderr, "rrdcached: network socket(2) failed: %s.\n",
+               rrd_strerror(errno));
+      continue;
+    }
+
+    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+
+    status = bind (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+    if (status != 0)
+    {
+      fprintf (stderr, "rrdcached: bind(%s) failed: %s.\n",
+               sock->addr, rrd_strerror(errno));
+      close (fd);
+      continue;
+    }
+
+    status = listen (fd, /* backlog = */ 10);
+    if (status != 0)
+    {
+      fprintf (stderr, "rrdcached: listen(%s) failed: %s\n.",
+               sock->addr, rrd_strerror(errno));
+      close (fd);
+      freeaddrinfo(ai_res);
+      return (-1);
+    }
+
+    listen_fds[listen_fds_num].fd = fd;
+    listen_fds[listen_fds_num].family = ai_ptr->ai_family;
+    listen_fds_num++;
+  } /* for (ai_ptr) */
+
+  freeaddrinfo(ai_res);
+  return (0);
+} /* }}} static int open_listen_socket_network */
+
+static int open_listen_socket (const listen_socket_t *sock) /* {{{ */
+{
+  assert(sock != NULL);
+  assert(sock->addr != NULL);
+
+  if (strncmp ("unix:", sock->addr, strlen ("unix:")) == 0
+      || sock->addr[0] == '/')
+    return (open_listen_socket_unix(sock));
+  else
+    return (open_listen_socket_network(sock));
+} /* }}} int open_listen_socket */
+
+static int close_listen_sockets (void) /* {{{ */
+{
+  size_t i;
+
+  for (i = 0; i < listen_fds_num; i++)
+  {
+    close (listen_fds[i].fd);
+
+    if (listen_fds[i].family == PF_UNIX)
+      unlink(listen_fds[i].addr);
+  }
+
+  free (listen_fds);
+  listen_fds = NULL;
+  listen_fds_num = 0;
+
+  return (0);
+} /* }}} int close_listen_sockets */
+
+static void *listen_thread_main (void *args __attribute__((unused))) /* {{{ */
+{
+  struct pollfd *pollfds;
+  int pollfds_num;
+  int status;
+  int i;
+
+  if (listen_fds_num < 1)
+  {
+    RRDD_LOG(LOG_ERR, "listen_thread_main: no listen_fds !");
+    return (NULL);
+  }
+
+  pollfds_num = listen_fds_num;
+  pollfds = (struct pollfd *) malloc (sizeof (*pollfds) * pollfds_num);
+  if (pollfds == NULL)
+  {
+    RRDD_LOG (LOG_ERR, "listen_thread_main: malloc failed.");
+    return (NULL);
+  }
+  memset (pollfds, 0, sizeof (*pollfds) * pollfds_num);
+
+  RRDD_LOG(LOG_INFO, "listening for connections");
+
+  while (state == RUNNING)
+  {
+    for (i = 0; i < pollfds_num; i++)
+    {
+      pollfds[i].fd = listen_fds[i].fd;
+      pollfds[i].events = POLLIN | POLLPRI;
+      pollfds[i].revents = 0;
+    }
+
+    status = poll (pollfds, pollfds_num, /* timeout = */ 1000);
+    if (state != RUNNING)
+      break;
+    else if (status == 0) /* timeout */
+      continue;
+    else if (status < 0) /* error */
+    {
+      status = errno;
+      if (status != EINTR)
+      {
+        RRDD_LOG (LOG_ERR, "listen_thread_main: poll(2) failed.");
+      }
+      continue;
+    }
+
+    for (i = 0; i < pollfds_num; i++)
+    {
+      listen_socket_t *client_sock;
+      struct sockaddr_storage client_sa;
+      socklen_t client_sa_size;
+      pthread_t tid;
+      pthread_attr_t attr;
+
+      if (pollfds[i].revents == 0)
+        continue;
+
+      if ((pollfds[i].revents & (POLLIN | POLLPRI)) == 0)
+      {
+        RRDD_LOG (LOG_ERR, "listen_thread_main: "
+            "poll(2) returned something unexpected for listen FD #%i.",
+            pollfds[i].fd);
+        continue;
+      }
+
+      client_sock = (listen_socket_t *) malloc (sizeof (listen_socket_t));
+      if (client_sock == NULL)
+      {
+        RRDD_LOG (LOG_ERR, "listen_thread_main: malloc failed.");
+        continue;
+      }
+      memcpy(client_sock, &listen_fds[i], sizeof(listen_fds[0]));
+
+      client_sa_size = sizeof (client_sa);
+      client_sock->fd = accept (pollfds[i].fd,
+          (struct sockaddr *) &client_sa, &client_sa_size);
+      if (client_sock->fd < 0)
+      {
+        RRDD_LOG (LOG_ERR, "listen_thread_main: accept(2) failed.");
+        free(client_sock);
+        continue;
+      }
+
+      pthread_attr_init (&attr);
+      pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+
+      status = pthread_create (&tid, &attr, connection_thread_main,
+                               client_sock);
+      if (status != 0)
+      {
+        RRDD_LOG (LOG_ERR, "listen_thread_main: pthread_create failed.");
+        close_connection(client_sock);
+        continue;
+      }
+    } /* for (pollfds_num) */
+  } /* while (state == RUNNING) */
+
+  RRDD_LOG(LOG_INFO, "starting shutdown");
+
+  close_listen_sockets ();
+
+  pthread_mutex_lock (&connection_threads_lock);
+  while (connection_threads_num > 0)
+    pthread_cond_wait(&connection_threads_done, &connection_threads_lock);
+  pthread_mutex_unlock (&connection_threads_lock);
+
+  free(pollfds);
+
+  return (NULL);
+} /* }}} void *listen_thread_main */
+
+static int daemonize (void) /* {{{ */
+{
+  int pid_fd;
+  char *base_dir;
+
+  daemon_uid = geteuid();
+
+  pid_fd = open_pidfile("create", O_CREAT|O_EXCL|O_WRONLY);
+  if (pid_fd < 0)
+    pid_fd = check_pidfile();
+  if (pid_fd < 0)
+    return pid_fd;
+
+  /* open all the listen sockets */
+  if (config_listen_address_list_len > 0)
+  {
+    for (size_t i = 0; i < config_listen_address_list_len; i++)
+      open_listen_socket (config_listen_address_list[i]);
+
+    rrd_free_ptrs((void ***) &config_listen_address_list,
+                  &config_listen_address_list_len);
+  }
+  else
+  {
+    listen_socket_t sock;
+    memset(&sock, 0, sizeof(sock));
+    strncpy(sock.addr, RRDCACHED_DEFAULT_ADDRESS, sizeof(sock.addr)-1);
+    open_listen_socket (&sock);
+  }
+
+  if (listen_fds_num < 1)
+  {
+    fprintf (stderr, "rrdcached: FATAL: cannot open any listen sockets\n");
+    goto error;
+  }
+
+  if (!stay_foreground)
+  {
+    pid_t child;
+
+    child = fork ();
+    if (child < 0)
+    {
+      fprintf (stderr, "daemonize: fork(2) failed.\n");
+      goto error;
+    }
+    else if (child > 0)
+      exit(0);
+
+    /* Become session leader */
+    setsid ();
+
+    /* Open the first three file descriptors to /dev/null */
+    close (2);
+    close (1);
+    close (0);
+
+    open ("/dev/null", O_RDWR);
+    if (dup(0) == -1 || dup(0) == -1){
+        RRDD_LOG (LOG_ERR, "faild to run dup.\n");
+    }
+  } /* if (!stay_foreground) */
+
+  /* Change into the /tmp directory. */
+  base_dir = (config_base_dir != NULL)
+    ? config_base_dir
+    : "/tmp";
+
+  if (chdir (base_dir) != 0)
+  {
+    fprintf (stderr, "daemonize: chdir (%s) failed.\n", base_dir);
+    goto error;
+  }
+
+  install_signal_handlers();
+
+  openlog ("rrdcached", LOG_PID, LOG_DAEMON);
+  RRDD_LOG(LOG_INFO, "starting up");
+
+  cache_tree = g_tree_new_full ((GCompareDataFunc) strcmp, NULL, NULL,
+                                (GDestroyNotify) free_cache_item);
+  if (cache_tree == NULL)
+  {
+    RRDD_LOG (LOG_ERR, "daemonize: g_tree_new failed.");
+    goto error;
+  }
+
+  return write_pidfile (pid_fd);
+
+error:
+  remove_pidfile();
+  return -1;
+} /* }}} int daemonize */
+
+static int cleanup (void) /* {{{ */
+{
+  pthread_cond_broadcast (&flush_cond);
+  pthread_join (flush_thread, NULL);
+
+  pthread_cond_broadcast (&queue_cond);
+  for (int i = 0; i < config_queue_threads; i++)
+    pthread_join (queue_threads[i], NULL);
+
+  if (config_flush_at_shutdown)
+  {
+    assert(cache_queue_head == NULL);
+    RRDD_LOG(LOG_INFO, "clean shutdown; all RRDs flushed");
+  }
+
+  free(queue_threads);
+  free(config_base_dir);
+
+  pthread_mutex_lock(&cache_lock);
+  g_tree_destroy(cache_tree);
+
+  pthread_mutex_lock(&journal_lock);
+  journal_done();
+
+  RRDD_LOG(LOG_INFO, "goodbye");
+  closelog ();
+
+  remove_pidfile ();
+  free(config_pid_file);
+
+  return (0);
+} /* }}} int cleanup */
+
+static int read_options (int argc, char **argv) /* {{{ */
+{
+  int option;
+  int status = 0;
+
+  char **permissions = NULL;
+  size_t permissions_len = 0;
+
+  gid_t  socket_group = (gid_t)-1;
+  mode_t socket_permissions = (mode_t)-1;
+
+  while ((option = getopt(argc, argv, "gl:s:m:P:f:w:z:t:Bb:p:Fj:h?")) != -1)
+  {
+    switch (option)
+    {
+      case 'g':
+        stay_foreground=1;
+        break;
+
+      case 'l':
+      {
+        listen_socket_t *new;
+
+        new = malloc(sizeof(listen_socket_t));
+        if (new == NULL)
+        {
+          fprintf(stderr, "read_options: malloc failed.\n");
+          return(2);
+        }
+        memset(new, 0, sizeof(listen_socket_t));
+
+        strncpy(new->addr, optarg, sizeof(new->addr)-1);
+
+        /* Add permissions to the socket {{{ */
+        if (permissions_len != 0)
+        {
+          size_t i;
+          for (i = 0; i < permissions_len; i++)
+          {
+            status = socket_permission_add (new, permissions[i]);
+            if (status != 0)
+            {
+              fprintf (stderr, "read_options: Adding permission \"%s\" to "
+                  "socket failed. Most likely, this permission doesn't "
+                  "exist. Check your command line.\n", permissions[i]);
+              status = 4;
+            }
+          }
+        }
+        else /* if (permissions_len == 0) */
+        {
+          /* Add permission for ALL commands to the socket. */
+          size_t i;
+          for (i = 0; i < list_of_commands_len; i++)
+          {
+            status = socket_permission_add (new, list_of_commands[i].cmd);
+            if (status != 0)
+            {
+              fprintf (stderr, "read_options: Adding permission \"%s\" to "
+                  "socket failed. This should never happen, ever! Sorry.\n",
+                  permissions[i]);
+              status = 4;
+            }
+          }
+        }
+        /* }}} Done adding permissions. */
+
+        new->socket_group = socket_group;
+        new->socket_permissions = socket_permissions;
+
+        if (!rrd_add_ptr((void ***)&config_listen_address_list,
+                         &config_listen_address_list_len, new))
+        {
+          fprintf(stderr, "read_options: rrd_add_ptr failed.\n");
+          return (2);
+        }
+      }
+      break;
+
+      /* set socket group permissions */
+      case 's':
+      {
+       gid_t group_gid;
+       struct group *grp;
+
+       group_gid = strtoul(optarg, NULL, 10);
+       if (errno != EINVAL && group_gid>0)
+       {
+         /* we were passed a number */
+         grp = getgrgid(group_gid);
+       }
+       else
+       {
+         grp = getgrnam(optarg);
+       }
+
+       if (grp)
+       {
+         socket_group = grp->gr_gid;
+       }
+       else
+       {
+         /* no idea what the user wanted... */
+         fprintf (stderr, "read_options: couldn't map \"%s\" to a group, Sorry\n", optarg);
+         return (5);
+       }
+      }
+      break;
+
+      /* set socket file permissions */
+      case 'm':
+      {
+        long  tmp;
+        char *endptr = NULL;
+
+        tmp = strtol (optarg, &endptr, 8);
+        if ((endptr == optarg) || (! endptr) || (*endptr != '\0')
+            || (tmp > 07777) || (tmp < 0)) {
+          fprintf (stderr, "read_options: Invalid file mode \"%s\".\n",
+              optarg);
+          return (5);
+        }
+
+        socket_permissions = (mode_t)tmp;
+      }
+      break;
+
+      case 'P':
+      {
+        char *optcopy;
+        char *saveptr;
+        char *dummy;
+        char *ptr;
+
+        rrd_free_ptrs ((void *) &permissions, &permissions_len);
+
+        optcopy = strdup (optarg);
+        dummy = optcopy;
+        saveptr = NULL;
+        while ((ptr = strtok_r (dummy, ", ", &saveptr)) != NULL)
+        {
+          dummy = NULL;
+          rrd_add_strdup ((void *) &permissions, &permissions_len, ptr);
+        }
+
+        free (optcopy);
+      }
+      break;
+
+      case 'f':
+      {
+        int temp;
+
+        temp = atoi (optarg);
+        if (temp > 0)
+          config_flush_interval = temp;
+        else
+        {
+          fprintf (stderr, "Invalid flush interval: %s\n", optarg);
+          status = 3;
+        }
+      }
+      break;
+
+      case 'w':
+      {
+        int temp;
+
+        temp = atoi (optarg);
+        if (temp > 0)
+          config_write_interval = temp;
+        else
+        {
+          fprintf (stderr, "Invalid write interval: %s\n", optarg);
+          status = 2;
+        }
+      }
+      break;
+
+      case 'z':
+      {
+        int temp;
+
+        temp = atoi(optarg);
+        if (temp > 0)
+          config_write_jitter = temp;
+        else
+        {
+          fprintf (stderr, "Invalid write jitter: -z %s\n", optarg);
+          status = 2;
+        }
+
+        break;
+      }
+
+      case 't':
+      {
+        int threads;
+        threads = atoi(optarg);
+        if (threads >= 1)
+          config_queue_threads = threads;
+        else
+        {
+          fprintf (stderr, "Invalid thread count: -t %s\n", optarg);
+          return 1;
+        }
+      }
+      break;
+
+      case 'B':
+        config_write_base_only = 1;
+        break;
+
+      case 'b':
+      {
+        size_t len;
+        char base_realpath[PATH_MAX];
+
+        if (config_base_dir != NULL)
+          free (config_base_dir);
+        config_base_dir = strdup (optarg);
+        if (config_base_dir == NULL)
+        {
+          fprintf (stderr, "read_options: strdup failed.\n");
+          return (3);
+        }
+
+        if (rrd_mkdir_p (config_base_dir, 0777) != 0)
+        {
+          fprintf (stderr, "Failed to create base directory '%s': %s\n",
+              config_base_dir, rrd_strerror (errno));
+          return (3);
+        }
+
+        /* make sure that the base directory is not resolved via
+         * symbolic links.  this makes some performance-enhancing
+         * assumptions possible (we don't have to resolve paths
+         * that start with a "/")
+         */
+        if (realpath(config_base_dir, base_realpath) == NULL)
+        {
+          fprintf (stderr, "Failed to canonicalize the base directory '%s': "
+              "%s\n", config_base_dir, rrd_strerror(errno));
+          return 5;
+        }
+
+        len = strlen (config_base_dir);
+        while ((len > 0) && (config_base_dir[len - 1] == '/'))
+        {
+          config_base_dir[len - 1] = 0;
+          len--;
+        }
+
+        if (len < 1)
+        {
+          fprintf (stderr, "Invalid base directory: %s\n", optarg);
+          return (4);
+        }
+
+        _config_base_dir_len = len;
+
+        len = strlen (base_realpath);
+        while ((len > 0) && (base_realpath[len - 1] == '/'))
+        {
+          base_realpath[len - 1] = '\0';
+          len--;
+        }
+
+        if (strncmp(config_base_dir,
+                         base_realpath, sizeof(base_realpath)) != 0)
+        {
+          fprintf(stderr,
+                  "Base directory (-b) resolved via file system links!\n"
+                  "Please consult rrdcached '-b' documentation!\n"
+                  "Consider specifying the real directory (%s)\n",
+                  base_realpath);
+          return 5;
+        }
+      }
+      break;
+
+      case 'p':
+      {
+        if (config_pid_file != NULL)
+          free (config_pid_file);
+        config_pid_file = strdup (optarg);
+        if (config_pid_file == NULL)
+        {
+          fprintf (stderr, "read_options: strdup failed.\n");
+          return (3);
+        }
+      }
+      break;
+
+      case 'F':
+        config_flush_at_shutdown = 1;
+        break;
+
+      case 'j':
+      {
+        const char *dir = journal_dir = strdup(optarg);
+
+        status = rrd_mkdir_p(dir, 0777);
+        if (status != 0)
+        {
+          fprintf(stderr, "Failed to create journal directory '%s': %s\n",
+              dir, rrd_strerror(errno));
+          return 6;
+        }
+
+        if (access(dir, R_OK|W_OK|X_OK) != 0)
+        {
+          fprintf(stderr, "Must specify a writable directory with -j! (%s)\n",
+                  errno ? rrd_strerror(errno) : "");
+          return 6;
+        }
+      }
+      break;
+
+      case 'h':
+      case '?':
+        printf ("RRDCacheD %s\n"
+            "Copyright (C) 2008,2009 Florian octo Forster and Kevin Brintnall\n"
+            "\n"
+            "Usage: rrdcached [options]\n"
+            "\n"
+            "Valid options are:\n"
+            "  -l <address>  Socket address to listen to.\n"
+            "  -P <perms>    Sets the permissions to assign to all following "
+                            "sockets\n"
+            "  -w <seconds>  Interval in which to write data.\n"
+            "  -z <delay>    Delay writes up to <delay> seconds to spread load\n"
+            "  -t <threads>  Number of write threads.\n"
+            "  -f <seconds>  Interval in which to flush dead data.\n"
+            "  -p <file>     Location of the PID-file.\n"
+            "  -b <dir>      Base directory to change to.\n"
+            "  -B            Restrict file access to paths within -b <dir>\n"
+            "  -g            Do not fork and run in the foreground.\n"
+            "  -j <dir>      Directory in which to create the journal files.\n"
+            "  -F            Always flush all updates at shutdown\n"
+            "  -s <id|name>  Make socket g+rw to named group\n"
+            "\n"
+            "For more information and a detailed description of all options "
+            "please refer\n"
+            "to the rrdcached(1) manual page.\n",
+            VERSION);
+        status = -1;
+        break;
+    } /* switch (option) */
+  } /* while (getopt) */
+
+  /* advise the user when values are not sane */
+  if (config_flush_interval < 2 * config_write_interval)
+    fprintf(stderr, "WARNING: flush interval (-f) should be at least"
+            " 2x write interval (-w) !\n");
+  if (config_write_jitter > config_write_interval)
+    fprintf(stderr, "WARNING: write delay (-z) should NOT be larger than"
+            " write interval (-w) !\n");
+
+  if (config_write_base_only && config_base_dir == NULL)
+    fprintf(stderr, "WARNING: -B does not make sense without -b!\n"
+            "  Consult the rrdcached documentation\n");
+
+  if (journal_dir == NULL)
+    config_flush_at_shutdown = 1;
+
+  rrd_free_ptrs ((void *) &permissions, &permissions_len);
+
+  return (status);
+} /* }}} int read_options */
+
+int main (int argc, char **argv)
+{
+  int status;
+
+  status = read_options (argc, argv);
+  if (status != 0)
+  {
+    if (status < 0)
+      status = 0;
+    return (status);
+  }
+
+  status = daemonize ();
+  if (status != 0)
+  {
+    fprintf (stderr, "rrdcached: daemonize failed, exiting.\n");
+    return (1);
+  }
+
+  journal_init();
+
+  /* start the queue threads */
+  queue_threads = calloc(config_queue_threads, sizeof(*queue_threads));
+  if (queue_threads == NULL)
+  {
+    RRDD_LOG (LOG_ERR, "FATAL: cannot calloc queue threads");
+    cleanup();
+    return (1);
+  }
+  for (int i = 0; i < config_queue_threads; i++)
+  {
+    memset (&queue_threads[i], 0, sizeof (*queue_threads));
+    status = pthread_create (&queue_threads[i], NULL, queue_thread_main, NULL);
+    if (status != 0)
+    {
+      RRDD_LOG (LOG_ERR, "FATAL: cannot create queue thread");
+      cleanup();
+      return (1);
+    }
+  }
+
+  /* start the flush thread */
+  memset(&flush_thread, 0, sizeof(flush_thread));
+  status = pthread_create (&flush_thread, NULL, flush_thread_main, NULL);
+  if (status != 0)
+  {
+    RRDD_LOG (LOG_ERR, "FATAL: cannot create flush thread");
+    cleanup();
+    return (1);
+  }
+
+  listen_thread_main (NULL);
+  cleanup ();
+
+  return (0);
+} /* int main */
+
+/*
+ * vim: set sw=2 sts=2 ts=8 et fdm=marker :
+ */
diff --git a/program/src/rrd_datalang.c b/program/src/rrd_datalang.c
new file mode 100644 (file)
index 0000000..463f22c
--- /dev/null
@@ -0,0 +1,34 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_datalang  A system for passing named and typed parameters between
+ *               the different parts of rrdtool
+ *
+ * In rrdtool thre are a number of places where large and complex
+ * data structures have to be passed from one function to another
+ * eg when rrd_info returns its findings, but also when a function like
+ * rrd_graph get called.
+ *
+ * At the moment function calling is done with an argc/argv type interface
+ * it's special property is that all data is passed as strings, which can lead
+ * to unnecessary conversions being performed when rrdtool functions are called
+ * from a typed language
+ *
+ * Data returns from functions is not standardized at all, which is
+ * efficient in the sense that the data return interface can be tailord to
+ * the specific needs of the function at hand, but it also leads to
+ * increassed probability for implementation errors as things have to be
+ * reinvented for each function. Also adding new functions into all the
+ * language bindings is quite cumbersom.
+ *
+ * Therefore I want to develop a standardized interface for passing named
+ * and typed data into functions and for returning data from functions to
+ * their callers.  I am thinking about working of the code in rrd_info.c ...
+ *
+ * Does anyone have experiance in this field or any pointers to read up on
+ * related work ? Or maybe even an existing library for this ?
+ *
+ * Cheers
+ * tobi / 2001-03-10
+ * 
+ *****************************************************************************/
diff --git a/program/src/rrd_diff.c b/program/src/rrd_diff.c
new file mode 100644 (file)
index 0000000..07f8fb3
--- /dev/null
@@ -0,0 +1,123 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ * This code is stolen from rateup (mrtg-2.x) by Dave Rand
+ *****************************************************************************
+ * diff calculate the difference between two very long integers available as
+ *      strings
+ *****************************************************************************
+ * $Id$
+ * $Log$
+ * Revision 1.4  2003/03/10 00:30:34  oetiker
+ * handle cases with two negative numbers
+ * --  Sasha Mikheev <sasha@avalon-net.co.il>
+ *
+ * Revision 1.3  2002/04/01 18:31:22  oetiker
+ * "!" takes a higher preference than "||" this means rrd_update N:: would
+ * segfault -- Oliver Cook <ollie@uk.clara.net>
+ *
+ * Revision 1.2  2002/02/01 20:34:49  oetiker
+ * fixed version number and date/time
+ *
+ * Revision 1.1.1.1  2001/02/25 22:25:05  oetiker
+ * checkin
+ *
+ * Revision 1.1  1998/10/08 18:21:45  oetiker
+ * Initial revision
+ *
+ * Revision 1.3  1998/02/06 21:10:52  oetiker
+ * removed max define .. it is now in rrd_tool.h
+ *
+ * Revision 1.2  1997/12/07 20:38:03  oetiker
+ * ansified
+ *
+ * Revision 1.1  1997/11/28 23:31:59  oetiker
+ * Initial revision
+ *
+ *****************************************************************************/
+
+#include <ctype.h>
+#include "rrd_tool.h"
+
+double rrd_diff(
+    char *a,
+    char *b)
+{
+    char      res[LAST_DS_LEN + 1], *a1, *b1, *r1, *fix;
+    int       c, x, m;
+    char      a_neg = 0, b_neg = 0;
+    double    result;
+
+    while (!(isdigit((int) *a) || *a == 0)) {
+        if (*a == '-')
+            a_neg = 1;
+        a++;
+    }
+    fix = a;
+    while (isdigit((int) *fix))
+        fix++;
+    *fix = 0;           /* maybe there is some non digit data in the string */
+    while (!(isdigit((int) *b) || *b == 0)) {
+        if (*b == '-')
+            b_neg = 1;
+        b++;
+    }
+    fix = b;
+    while (isdigit((int) *fix))
+        fix++;
+    *fix = 0;           /* maybe there is some non digit data in the string */
+    if (!isdigit((int) *a) || !isdigit((int) *b))
+        return DNAN;
+    if (a_neg + b_neg == 1) /* can not handle numbers with different signs yet */
+        return DNAN;
+    a1 = &a[strlen(a) - 1];
+    m = max(strlen(a), strlen(b));
+    if (m > LAST_DS_LEN)
+        return DNAN;    /* result string too short */
+
+    r1 = &res[m + 1];
+    for (b1 = res; b1 <= r1; b1++)
+        *b1 = ' ';
+    b1 = &b[strlen(b) - 1];
+    r1[1] = 0;          /* Null terminate result */
+    c = 0;
+    for (x = 0; x < m; x++) {
+        if (a1 >= a && b1 >= b) {
+            *r1 = ((*a1 - c) - *b1) + '0';
+        } else if (a1 >= a) {
+            *r1 = (*a1 - c);
+        } else {
+            *r1 = ('0' - *b1 - c) + '0';
+        }
+        if (*r1 < '0') {
+            *r1 += 10;
+            c = 1;
+        } else if (*r1 > '9') { /* 0 - 10 */
+            *r1 -= 10;
+            c = 1;
+        } else {
+            c = 0;
+        }
+        a1--;
+        b1--;
+        r1--;
+    }
+    if (c) {
+        r1 = &res[m + 1];
+        for (x = 0; isdigit((int) *r1) && x < m; x++, r1--) {
+            *r1 = ('9' - *r1 + c) + '0';
+            if (*r1 > '9') {
+                *r1 -= 10;
+                c = 1;
+            } else {
+                c = 0;
+            }
+        }
+        result = -atof(res);
+    } else
+        result = atof(res);
+
+    if (a_neg + b_neg == 2) /* both are negatives, reverse sign */
+        result = -result;
+
+    return result;
+}
diff --git a/program/src/rrd_dump.c b/program/src/rrd_dump.c
new file mode 100644 (file)
index 0000000..07919b7
--- /dev/null
@@ -0,0 +1,589 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_dump  Display a RRD
+ *****************************************************************************
+ * $Id$
+ * $Log$
+ * Revision 1.7  2004/05/25 20:53:21  oetiker
+ * prevent small leak when resources are exhausted -- Mike Slifcak
+ *
+ * Revision 1.6  2004/05/25 20:51:49  oetiker
+ * Update displayed copyright messages to be consistent. -- Mike Slifcak
+ *
+ * Revision 1.5  2003/02/13 07:05:27  oetiker
+ * Find attached the patch I promised to send to you. Please note that there
+ * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c
+ * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This
+ * library is identical to librrd, but it contains support code for per-thread
+ * global variables currently used for error information only. This is similar
+ * to how errno per-thread variables are implemented.  librrd_th must be linked
+ * alongside of libpthred
+ *
+ * There is also a new file "THREADS", holding some documentation.
+ *
+ * -- Peter Stamfest <peter@stamfest.at>
+ *
+ * Revision 1.4  2002/02/01 20:34:49  oetiker
+ * fixed version number and date/time
+ *
+ * Revision 1.3  2001/03/10 23:54:39  oetiker
+ * Support for COMPUTE data sources (CDEF data sources). Removes the RPN
+ * parser and calculator from rrd_graph and puts then in a new file,
+ * rrd_rpncalc.c. Changes to core files rrd_create and rrd_update. Some
+ * clean-up of aberrant behavior stuff, including a bug fix.
+ * Documentation update (rrdcreate.pod, rrdupdate.pod). Change xml format.
+ * -- Jake Brutlag <jakeb@corp.webtv.net>
+ *
+ * Revision 1.2  2001/03/04 13:01:55  oetiker
+ *
+ * Revision 1.1.1.1  2001/02/25 22:25:05  oetiker
+ * checkin
+ *
+ *****************************************************************************/
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include "rrd_client.h"
+
+#include <locale.h>
+
+#if !(defined(NETWARE) || defined(WIN32))
+extern char *tzname[2];
+#endif
+
+//Local prototypes
+size_t rrd_dump_opt_cb_fileout(
+    const void *data,
+    size_t len,
+    void *user);
+
+int rrd_dump_opt_r(
+    const char *filename,
+    char *outname,
+    int opt_noheader);
+
+int rrd_dump_cb_r(
+    const char *filename,
+    int opt_header,
+    rrd_output_callback_t cb,
+    void *user)
+{
+    unsigned int i, ii, ix, iii = 0;
+    time_t    now;
+    char      somestring[255];
+    rrd_value_t my_cdp;
+    off_t     rra_base, rra_start, rra_next;
+    rrd_file_t *rrd_file;
+    rrd_t     rrd;
+    rrd_value_t value;
+    struct tm tm;
+    char *old_locale = "";
+
+//These two macros are local defines to clean up visible code from its redndancy
+//and make it easier to read.
+#define CB_PUTS(str)                                            \
+    cb((str), strlen((str)), user)
+#define CB_FMTS(...) do {                                       \
+    char buffer[256];                                           \
+    snprintf (buffer, sizeof(buffer), __VA_ARGS__);             \
+    CB_PUTS (buffer);                                           \
+    } while (0)
+//These macros are to be undefined at the end of this function
+
+    //Check if we got a (valid) callback method
+    if (!cb) {
+        return (-1);
+    }
+
+    rrd_init(&rrd);
+
+    rrd_file = rrd_open(filename, &rrd, RRD_READONLY | RRD_READAHEAD);
+    if (rrd_file == NULL) {
+        rrd_free(&rrd);
+        return (-1);
+    }
+
+    old_locale = setlocale(LC_NUMERIC, "C");
+
+
+    if (opt_header == 1) {
+        CB_PUTS("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+        CB_PUTS("<!DOCTYPE rrd SYSTEM \"http://oss.oetiker.ch/rrdtool/rrdtool.dtd\">\n");
+        CB_PUTS("<!-- Round Robin Database Dump -->\n");
+        CB_PUTS("<rrd>\n");
+    } else if (opt_header == 2) {
+        CB_PUTS("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+        CB_PUTS("<!-- Round Robin Database Dump -->\n");
+        CB_PUTS("<rrd xmlns=\"http://oss.oetiker.ch/rrdtool/rrdtool-dump.xml\" "
+                "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
+        CB_PUTS("\txsi:schemaLocation=\"http://oss.oetiker.ch/rrdtool/rrdtool-dump.xml "
+                "http://oss.oetiker.ch/rrdtool/rrdtool-dump.xsd\">\n");
+    } else {
+        CB_PUTS("<!-- Round Robin Database Dump -->\n");
+        CB_PUTS("<rrd>\n");
+    }
+
+    if (atoi(rrd.stat_head->version) <= 3) {
+        CB_FMTS("\t<version>%s</version>\n", RRD_VERSION3);
+    } else {
+        CB_FMTS("\t<version>%s</version>\n", RRD_VERSION);
+    }
+    
+    CB_FMTS("\t<step>%lu</step> <!-- Seconds -->\n",
+        rrd.stat_head->pdp_step);
+
+#ifdef HAVE_STRFTIME
+    localtime_r(&rrd.live_head->last_up, &tm);
+    strftime(somestring, 255, "%Y-%m-%d %H:%M:%S %Z", &tm);
+#else
+# error "Need strftime"
+#endif
+    CB_FMTS("\t<lastupdate>%lld</lastupdate> <!-- %s -->\n\n",
+        (long long) rrd.live_head->last_up, somestring);
+    for (i = 0; i < rrd.stat_head->ds_cnt; i++) {
+        CB_PUTS("\t<ds>\n");
+
+        CB_FMTS("\t\t<name> %s </name>\n", rrd.ds_def[i].ds_nam);
+
+        CB_FMTS("\t\t<type> %s </type>\n", rrd.ds_def[i].dst);
+
+        if (dst_conv(rrd.ds_def[i].dst) != DST_CDEF) {
+            CB_FMTS("\t\t<minimal_heartbeat>%lu</minimal_heartbeat>\n",
+                    rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt);
+
+            if (isnan(rrd.ds_def[i].par[DS_min_val].u_val)) {
+                CB_PUTS("\t\t<min>NaN</min>\n");
+            } else {
+                CB_FMTS("\t\t<min>%0.10e</min>\n",
+                    rrd.ds_def[i].par[DS_min_val].u_val);
+            }
+            
+            if (isnan(rrd.ds_def[i].par[DS_max_val].u_val)) {
+                CB_PUTS("\t\t<max>NaN</max>\n");
+            } else {
+                CB_FMTS("\t\t<max>%0.10e</max>\n",
+                    rrd.ds_def[i].par[DS_max_val].u_val);
+            }
+        } else {        /* DST_CDEF */
+            char     *str = NULL;
+
+            rpn_compact2str((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]),
+                rrd.ds_def, &str);
+
+            //Splitting into 3 writes to avoid allocating memory
+            //This is better compared to snprintf as str may be of arbitrary size
+            CB_PUTS("\t\t<cdef> ");
+            CB_PUTS(str);
+            CB_PUTS(" </cdef>\n");
+
+            free(str);
+        }
+
+        CB_PUTS("\n\t\t<!-- PDP Status -->\n");
+        CB_FMTS("\t\t<last_ds>%s</last_ds>\n",
+            rrd.pdp_prep[i].last_ds);
+
+        if (isnan(rrd.pdp_prep[i].scratch[PDP_val].u_val)) {
+            CB_PUTS("\t\t<value>NaN</value>\n");
+        } else {
+            CB_FMTS("\t\t<value>%0.10e</value>\n",
+                rrd.pdp_prep[i].scratch[PDP_val].u_val);
+        }
+
+        CB_FMTS("\t\t<unknown_sec> %lu </unknown_sec>\n",
+            rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
+
+        CB_PUTS("\t</ds>\n\n");
+    }
+
+    CB_PUTS("\t<!-- Round Robin Archives -->\n");
+
+    rra_base = rrd_file->header_len;
+    rra_next = rra_base;
+
+    for (i = 0; i < rrd.stat_head->rra_cnt; i++) {
+
+        long      timer = 0;
+
+        rra_start = rra_next;
+        rra_next += (rrd.stat_head->ds_cnt
+                     * rrd.rra_def[i].row_cnt * sizeof(rrd_value_t));
+
+        CB_PUTS("\t<rra>\n");
+
+        CB_FMTS("\t\t<cf>%s</cf>\n", rrd.rra_def[i].cf_nam);
+
+        CB_FMTS("\t\t<pdp_per_row>%lu</pdp_per_row> <!-- %lu seconds -->\n\n",
+            rrd.rra_def[i].pdp_cnt, 
+            rrd.rra_def[i].pdp_cnt * rrd.stat_head->pdp_step);
+
+        /* support for RRA parameters */
+        CB_PUTS("\t\t<params>\n");
+
+        switch (cf_conv(rrd.rra_def[i].cf_nam)) {
+        case CF_HWPREDICT:
+        case CF_MHWPREDICT:
+            CB_FMTS("\t\t<hw_alpha>%0.10e</hw_alpha>\n",
+                rrd.rra_def[i].par[RRA_hw_alpha].u_val);
+
+            CB_FMTS("\t\t<hw_beta>%0.10e</hw_beta>\n",
+                rrd.rra_def[i].par[RRA_hw_beta].u_val);
+
+            CB_FMTS("\t\t<dependent_rra_idx>%lu</dependent_rra_idx>\n",
+                rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt);
+            break;
+        case CF_SEASONAL:
+        case CF_DEVSEASONAL:
+            CB_FMTS("\t\t<seasonal_gamma>%0.10e</seasonal_gamma>\n",
+                rrd.rra_def[i].par[RRA_seasonal_gamma].u_val);
+
+            CB_FMTS("\t\t<seasonal_smooth_idx>%lu</seasonal_smooth_idx>\n",
+                rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt);
+
+            if (atoi(rrd.stat_head->version) >= 4) {
+                CB_FMTS("\t\t<smoothing_window>%0.10e</smoothing_window>\n",
+                    rrd.rra_def[i].par[RRA_seasonal_smoothing_window].u_val);
+            }
+
+            CB_FMTS("\t\t<dependent_rra_idx>%lu</dependent_rra_idx>\n",
+                rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt);
+            break;
+        case CF_FAILURES:
+            CB_FMTS("\t\t<delta_pos>%0.10e</delta_pos>\n",
+                rrd.rra_def[i].par[RRA_delta_pos].u_val);
+
+            CB_FMTS("\t\t<delta_neg>%0.10e</delta_neg>\n",
+                rrd.rra_def[i].par[RRA_delta_neg].u_val);
+
+            CB_FMTS("\t\t<window_len>%lu</window_len>\n",
+                rrd.rra_def[i].par[RRA_window_len].u_cnt);
+
+            CB_FMTS("\t\t<failure_threshold>%lu</failure_threshold>\n",
+                rrd.rra_def[i].par[RRA_failure_threshold].u_cnt);
+
+            /* fall thru */
+        case CF_DEVPREDICT:
+            CB_FMTS("\t\t<dependent_rra_idx>%lu</dependent_rra_idx>\n",
+                rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt);
+            break;
+        case CF_AVERAGE:
+        case CF_MAXIMUM:
+        case CF_MINIMUM:
+        case CF_LAST:
+        default:
+            CB_FMTS("\t\t<xff>%0.10e</xff>\n",
+                rrd.rra_def[i].par[RRA_cdp_xff_val].u_val);
+            break;
+        }
+
+        CB_PUTS("\t\t</params>\n");
+        CB_PUTS("\t\t<cdp_prep>\n");
+
+        for (ii = 0; ii < rrd.stat_head->ds_cnt; ii++) {
+            unsigned long ivalue;
+
+            CB_PUTS("\t\t\t<ds>\n");
+            /* support for exporting all CDP parameters */
+            /* parameters common to all CFs */
+            /* primary_val and secondary_val do not need to be saved between updates
+             * so strictly speaking they could be omitted.
+             * However, they can be useful for diagnostic purposes, so are included here. */
+            value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].
+                scratch[CDP_primary_val].u_val;
+            if (isnan(value)) {
+                CB_PUTS("\t\t\t<primary_value>NaN</primary_value>\n");
+            } else {
+                CB_FMTS("\t\t\t<primary_value>%0.10e</primary_value>\n", value);
+            }
+
+            value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].
+                scratch[CDP_secondary_val].u_val;
+            if (isnan(value)) {
+                CB_PUTS("\t\t\t<secondary_value>NaN</secondary_value>\n");
+            } else {
+                CB_FMTS("\t\t\t<secondary_value>%0.10e</secondary_value>\n", value);
+            }
+
+            switch (cf_conv(rrd.rra_def[i].cf_nam)) {
+            case CF_HWPREDICT:
+            case CF_MHWPREDICT:
+                value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].
+                    scratch[CDP_hw_intercept].u_val;
+                if (isnan(value)) {
+                    CB_PUTS("\t\t\t<intercept>NaN</intercept>\n");
+                } else {
+                    CB_FMTS("\t\t\t<intercept>%0.10e</intercept>\n", value);
+                }
+
+                value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].
+                    scratch[CDP_hw_last_intercept].u_val;
+                if (isnan(value)) {
+                    CB_PUTS("\t\t\t<last_intercept>NaN</last_intercept>\n");
+                } else {
+                    CB_FMTS("\t\t\t<last_intercept>%0.10e</last_intercept>\n", value);
+                }
+
+                value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].
+                    scratch[CDP_hw_slope].u_val;
+                if (isnan(value)) {
+                    CB_PUTS("\t\t\t<slope>NaN</slope>\n");
+                } else {
+                    CB_FMTS("\t\t\t<slope>%0.10e</slope>\n", value);
+                }
+
+                value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].
+                    scratch[CDP_hw_last_slope].u_val;
+                if (isnan(value)) {
+                    CB_PUTS("\t\t\t<last_slope>NaN</last_slope>\n");
+                } else {
+                    CB_FMTS("\t\t\t<last_slope>%0.10e</last_slope>\n", value);
+                }
+
+                ivalue = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].
+                    scratch[CDP_null_count].u_cnt;
+                CB_FMTS("\t\t\t<nan_count>%lu</nan_count>\n", ivalue);
+
+                ivalue = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].
+                    scratch[CDP_last_null_count].u_cnt;
+                CB_FMTS("\t\t\t<last_nan_count>%lu</last_nan_count>\n", ivalue);
+                break;
+            case CF_SEASONAL:
+            case CF_DEVSEASONAL:
+                value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].
+                    scratch[CDP_hw_seasonal].u_val;
+                if (isnan(value)) {
+                    CB_PUTS("\t\t\t<seasonal>NaN</seasonal>\n");
+                } else {
+                    CB_FMTS("\t\t\t<seasonal>%0.10e</seasonal>\n", value);
+                }
+
+                value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].
+                    scratch[CDP_hw_last_seasonal].u_val;
+                if (isnan(value)) {
+                    CB_PUTS("\t\t\t<last_seasonal>NaN</last_seasonal>\n");
+                } else {
+                    CB_FMTS("\t\t\t<last_seasonal>%0.10e</last_seasonal>\n", value);
+                }
+
+                ivalue = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].
+                    scratch[CDP_init_seasonal].u_cnt;
+                CB_FMTS("\t\t\t<init_flag>%lu</init_flag>\n", ivalue);
+                break;
+            case CF_DEVPREDICT:
+                break;
+            case CF_FAILURES:
+            {
+                unsigned short vidx;
+                char *violations_array = (char *) ((void *)
+                    rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].scratch);
+                CB_PUTS("\t\t\t<history>");
+                for (vidx = 0;
+                    vidx < rrd.rra_def[i].par[RRA_window_len].u_cnt;
+                    ++vidx) {
+                    CB_FMTS("%d", violations_array[vidx]);
+                }
+                CB_PUTS("</history>\n");
+            }
+                break;
+            case CF_AVERAGE:
+            case CF_MAXIMUM:
+            case CF_MINIMUM:
+            case CF_LAST:
+            default:
+                value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].scratch[CDP_val].u_val;
+                if (isnan(value)) {
+                    CB_PUTS("\t\t\t<value>NaN</value>\n");
+                } else {
+                    CB_FMTS("\t\t\t<value>%0.10e</value>\n", value);
+                }
+
+                CB_FMTS("\t\t\t<unknown_datapoints>%lu</unknown_datapoints>\n",
+                    rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].
+                        scratch[CDP_unkn_pdp_cnt].u_cnt);
+                break;
+            }
+            CB_PUTS("\t\t\t</ds>\n");
+        }
+        CB_PUTS("\t\t</cdp_prep>\n");
+
+        CB_PUTS("\t\t<database>\n");
+        rrd_seek(rrd_file, (rra_start + (rrd.rra_ptr[i].cur_row + 1)
+                            * rrd.stat_head->ds_cnt
+                            * sizeof(rrd_value_t)), SEEK_SET);
+        timer = -(long)(rrd.rra_def[i].row_cnt - 1);
+        ii = rrd.rra_ptr[i].cur_row;
+        for (ix = 0; ix < rrd.rra_def[i].row_cnt; ix++) {
+            ii++;
+            if (ii >= rrd.rra_def[i].row_cnt) {
+                rrd_seek(rrd_file, rra_start, SEEK_SET);
+                ii = 0; /* wrap if max row cnt is reached */
+            }
+            now = (rrd.live_head->last_up
+                   - rrd.live_head->last_up
+                   % (rrd.rra_def[i].pdp_cnt * rrd.stat_head->pdp_step))
+                + (timer * rrd.rra_def[i].pdp_cnt * rrd.stat_head->pdp_step);
+
+            timer++;
+#if HAVE_STRFTIME
+            localtime_r(&now, &tm);
+            strftime(somestring, 255, "%Y-%m-%d %H:%M:%S %Z", &tm);
+#else
+# error "Need strftime"
+#endif
+            CB_FMTS("\t\t\t<!-- %s / %lld --> <row>",  somestring, (long long) now);
+            for (iii = 0; iii < rrd.stat_head->ds_cnt; iii++) {
+                rrd_read(rrd_file, &my_cdp, sizeof(rrd_value_t) * 1);
+                if (isnan(my_cdp)) {
+                    CB_PUTS("<v>NaN</v>");
+                } else {
+                    CB_FMTS("<v>%0.10e</v>", my_cdp);
+                }
+            }
+            CB_PUTS("</row>\n");
+        }
+        CB_PUTS("\t\t</database>\n\t</rra>\n");
+    }
+
+    CB_PUTS("</rrd>\n");
+
+    rrd_free(&rrd);
+
+    setlocale(LC_NUMERIC, old_locale);
+
+    return rrd_close(rrd_file);
+
+//Undefining the previously defined shortcuts
+//See start of this function
+#undef CB_PUTS
+#undef CB_FMTS
+//End of macro undefining
+
+}
+
+size_t rrd_dump_opt_cb_fileout(
+    const void *data,
+    size_t len,
+    void *user)
+{
+    return fwrite(data, 1, len, (FILE *)user);
+}
+
+int rrd_dump_opt_r(
+    const char *filename,
+    char *outname,
+    int opt_noheader)
+{
+    FILE     *out_file;
+    int       res;
+
+    out_file = NULL;
+    if (outname) {
+        if (!(out_file = fopen(outname, "w"))) {
+            return (-1);
+        }
+    } else {
+        out_file = stdout;
+    }
+
+    res = rrd_dump_cb_r(filename, opt_noheader, rrd_dump_opt_cb_fileout, (void *)out_file);
+
+    if (out_file != stdout) {
+        fclose(out_file);
+    }
+
+    return res;
+}
+
+/* backward compatibility with 1.2.x */
+int rrd_dump_r(
+    const char *filename,
+    char *outname)
+{
+    return rrd_dump_opt_r(filename, outname, 0);
+}
+
+int rrd_dump(
+    int argc,
+    char **argv)
+{
+    int       rc;
+    /** 
+     * 0 = no header
+     * 1 = dtd header
+     * 2 = xsd header
+     */
+    int       opt_header = 1;
+    char     *opt_daemon = NULL;
+
+    /* init rrd clean */
+
+    optind = 0;
+    opterr = 0;         /* initialize getopt */
+
+    while (42) {/* ha ha */
+        int       opt;
+        int       option_index = 0;
+        static struct option long_options[] = {
+            {"daemon", required_argument, 0, 'd'},
+            {"header", required_argument, 0, 'h'},
+            {"no-header", no_argument, 0, 'n'},
+            {0, 0, 0, 0}
+        };
+
+        opt = getopt_long(argc, argv, "d:h:n", long_options, &option_index);
+
+        if (opt == EOF)
+            break;
+
+        switch (opt) {
+        case 'd':
+            if (opt_daemon != NULL)
+                    free (opt_daemon);
+            opt_daemon = strdup (optarg);
+            if (opt_daemon == NULL)
+            {
+                rrd_set_error ("strdup failed.");
+                return (-1);
+            }
+            break;
+
+        case 'n':
+           opt_header = 0;
+           break;
+
+        case 'h':
+          if (strcmp(optarg, "dtd") == 0) {
+               opt_header = 1;
+          } else if (strcmp(optarg, "xsd") == 0) {
+               opt_header = 2;
+          } else if (strcmp(optarg, "none") == 0) {
+               opt_header = 0;
+          }
+          break;
+
+        default:
+            rrd_set_error("usage rrdtool %s [--header|-h {none,xsd,dtd}] [--no-header]"
+                          "file.rrd [file.xml]", argv[0]);
+            return (-1);
+            break;
+        }
+    }                   /* while (42) */
+
+    if ((argc - optind) < 1 || (argc - optind) > 2) {
+        rrd_set_error("usage rrdtool %s [--header|-h {none,xsd,dtd}] [--no-header]"
+                      "file.rrd [file.xml]", argv[0]);
+        return (-1);
+    }
+
+    rc = rrdc_flush_if_daemon(opt_daemon, argv[optind]);
+    if (opt_daemon) free(opt_daemon);
+    if (rc) return (rc);
+
+    if ((argc - optind) == 2) {
+        rc = rrd_dump_opt_r(argv[optind], argv[optind + 1], opt_header);
+    } else {
+        rc = rrd_dump_opt_r(argv[optind], NULL, opt_header);
+    }
+
+    return rc;
+}
diff --git a/program/src/rrd_error.c b/program/src/rrd_error.c
new file mode 100644 (file)
index 0000000..db666ca
--- /dev/null
@@ -0,0 +1,151 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_error.c   Common Header File
+ *****************************************************************************
+ * $Id$
+ * $Log$
+ * Revision 1.4  2003/02/22 21:57:03  oetiker
+ * a patch to avoid a memory leak and a Makefile.am patch to
+ * distribute all required source files -- Peter Stamfest <peter@stamfest.at>
+ *
+ * Revision 1.3  2003/02/13 07:05:27  oetiker
+ * Find attached the patch I promised to send to you. Please note that there
+ * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c
+ * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This
+ * library is identical to librrd, but it contains support code for per-thread
+ * global variables currently used for error information only. This is similar
+ * to how errno per-thread variables are implemented.  librrd_th must be linked
+ * alongside of libpthred
+ *
+ * There is also a new file "THREADS", holding some documentation.
+ *
+ * -- Peter Stamfest <peter@stamfest.at>
+ *
+ * Revision 1.2  2002/02/01 20:34:49  oetiker
+ * fixed version number and date/time
+ *
+ * Revision 1.1.1.1  2001/02/25 22:25:05  oetiker
+ * checkin
+ *
+ *************************************************************************** */
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "rrd_tool.h"
+
+#define MAXLEN 4096
+#define ERRBUFLEN 256
+#define CTX (rrd_get_context())
+
+void rrd_set_error(
+    char *fmt,
+    ...)
+{
+    va_list   argp;
+
+    rrd_clear_error();
+    va_start(argp, fmt);
+#ifdef HAVE_VSNPRINTF
+    vsnprintf(CTX->rrd_error, sizeof(CTX->rrd_error), fmt, argp);
+#else
+    vsprintf(CTX->rrd_error, fmt, argp);
+#endif
+    va_end(argp);
+}
+
+int rrd_test_error(
+    void)
+{
+    return CTX->rrd_error[0] != '\0';
+}
+
+void rrd_clear_error(
+    void)
+{
+    CTX->rrd_error[0] = '\0';
+}
+
+char     *rrd_get_error(
+    void)
+{
+    return CTX->rrd_error;
+}
+
+#if 0
+/* PS: Keep this stuff around, maybe we want it again if we use
+   rrd_contexts to really associate them with single RRD files and
+   operations on them... Then a single thread may use more than one
+   context. Using these functions would require to change each and
+   every function containing any of the non _r versions... */
+void rrd_set_error_r(
+    rrd_context_t * rrd_ctx,
+    char *fmt,
+    ...)
+{
+    va_list   argp;
+
+    rrd_clear_error_r(rrd_ctx);
+    va_start(argp, fmt);
+#ifdef HAVE_VSNPRINTF
+    vsnprintf(rrd_ctx->rrd_error, sizeof(rrd_ctx->rrd_error), fmt, argp);
+    rrd_ctx->rrd_error[sizeof(rrd_ctx->rrd_error) - 1] = '\0';
+#else
+    vsprintf(rrd_ctx->rrd_error, fmt, argp);
+#endif
+    va_end(argp);
+}
+
+int rrd_test_error_r(
+    rrd_context_t * rrd_ctx)
+{
+    return rrd_ctx->rrd_error[0] != '\0';
+}
+
+void rrd_clear_error_r(
+    rrd_context_t * rrd_ctx)
+{
+    rrd_ctx->rrd_error[0] = '\0';
+}
+
+char     *rrd_get_error_r(
+    rrd_context_t * rrd_ctx)
+{
+    return rrd_ctx->rrd_error;
+}
+#endif
+
+/* PS: Should we move this to some other file? It is not really error
+   related. */
+rrd_context_t *rrd_new_context(
+    void)
+{
+    rrd_context_t *rrd_ctx = (rrd_context_t *) malloc(sizeof(rrd_context_t));
+
+    if (!rrd_ctx) {
+        return NULL;
+    }
+
+    rrd_ctx->rrd_error[0] = '\0';
+    rrd_ctx->lib_errstr[0] = '\0';
+    return rrd_ctx;
+}
+
+void rrd_free_context(
+    rrd_context_t * rrd_ctx)
+{
+    if (rrd_ctx) {
+        free(rrd_ctx);
+    }
+}
+
+#if 0
+void rrd_globalize_error(
+    rrd_context_t * rrd_ctx)
+{
+    if (rrd_ctx) {
+        rrd_set_error(rrd_ctx->rrd_error);
+    }
+}
+#endif
diff --git a/program/src/rrd_fetch.c b/program/src/rrd_fetch.c
new file mode 100644 (file)
index 0000000..b46ba7a
--- /dev/null
@@ -0,0 +1,485 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_fetch.c  read date from an rrd to use for further processing
+ *****************************************************************************
+ * $Id$
+ * $Log$
+ * Revision 1.8  2004/05/18 18:53:03  oetiker
+ * big spell checking patch -- slif@bellsouth.net
+ *
+ * Revision 1.7  2003/11/11 19:46:21  oetiker
+ * replaced time_value with rrd_time_value as MacOS X introduced a struct of that name in their standard headers
+ *
+ * Revision 1.6  2003/01/16 23:27:54  oetiker
+ * fix border condition in rra selection of rrd_fetch
+ * -- Stanislav Sinyagin <ssinyagin@yahoo.com>
+ *
+ * Revision 1.5  2002/06/23 22:29:40  alex
+ * Added "step=1800" and such to "DEF"
+ * Cleaned some of the signed vs. unsigned problems
+ *
+ * Revision 1.4  2002/02/01 20:34:49  oetiker
+ * fixed version number and date/time
+ *
+ * Revision 1.3  2001/12/24 06:51:49  alex
+ * A patch of size 44Kbytes... in short:
+ *
+ * Found and repaired the off-by-one error in rrd_fetch_fn().
+ * As a result I had to remove the hacks in rrd_fetch_fn(),
+ * rrd_tool.c, vdef_calc(), data_calc(), data_proc() and
+ * reduce_data().  There may be other places which I didn't
+ * find so be careful.
+ *
+ * Enhanced debugging in rrd_fetch_fn(), it shows the RRA selection
+ * process.
+ *
+ * Added the ability to print VDEF timestamps.  At the moment it
+ * is a hack, I needed it now to fix the off-by-one error.
+ * If the format string is "%c" (and nothing else!), the time
+ * will be printed by both ctime() and as a long int.
+ *
+ * Moved some code around (slightly altering it) from rrd_graph()
+ *   initializing     now in rrd_graph_init()
+ *   options parsing  now in rrd_graph_options()
+ *   script parsing   now in rrd_graph_script()
+ *
+ * Revision 1.2  2001/12/17 12:48:43  oetiker
+ * fix overflow error ...
+ *
+ * Revision 1.1.1.1  2001/02/25 22:25:05  oetiker
+ * checkin
+ *
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_client.h"
+
+#include "rrd_is_thread_safe.h"
+/* #define DEBUG */
+
+int rrd_fetch(
+    int argc,
+    char **argv,
+    time_t *start,
+    time_t *end,        /* which time frame do you want ?
+                         * will be changed to represent reality */
+    unsigned long *step,    /* which stepsize do you want? 
+                             * will be changed to represent reality */
+    unsigned long *ds_cnt,  /* number of data sources in file */
+    char ***ds_namv,    /* names of data sources */
+    rrd_value_t **data)
+{                       /* two dimensional array containing the data */
+    long      step_tmp = 1;
+    time_t    start_tmp = 0, end_tmp = 0;
+    const char *cf;
+    char *opt_daemon = NULL;
+    int status;
+
+    rrd_time_value_t start_tv, end_tv;
+    char     *parsetime_error = NULL;
+    struct option long_options[] = {
+        {"resolution", required_argument, 0, 'r'},
+        {"start", required_argument, 0, 's'},
+        {"end", required_argument, 0, 'e'},
+        {"daemon", required_argument, 0, 'd'},
+        {0, 0, 0, 0}
+    };
+
+    optind = 0;
+    opterr = 0;         /* initialize getopt */
+
+    /* init start and end time */
+    rrd_parsetime("end-24h", &start_tv);
+    rrd_parsetime("now", &end_tv);
+
+    while (1) {
+        int       option_index = 0;
+        int       opt;
+
+        opt = getopt_long(argc, argv, "r:s:e:d:", long_options, &option_index);
+
+        if (opt == EOF)
+            break;
+
+        switch (opt) {
+        case 's':
+            if ((parsetime_error = rrd_parsetime(optarg, &start_tv))) {
+                rrd_set_error("start time: %s", parsetime_error);
+                return -1;
+            }
+            break;
+        case 'e':
+            if ((parsetime_error = rrd_parsetime(optarg, &end_tv))) {
+                rrd_set_error("end time: %s", parsetime_error);
+                return -1;
+            }
+            break;
+        case 'r':
+            step_tmp = atol(optarg);
+            break;
+
+        case 'd':
+            if (opt_daemon != NULL)
+                    free (opt_daemon);
+            opt_daemon = strdup (optarg);
+            if (opt_daemon == NULL)
+            {
+                rrd_set_error ("strdup failed.");
+                return (-1);
+            }
+            break;
+
+        case '?':
+            rrd_set_error("unknown option '-%c'", optopt);
+            return (-1);
+        }
+    }
+
+
+    if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+        return -1;
+    }
+
+
+    if (start_tmp < 3600 * 24 * 365 * 10) {
+        rrd_set_error("the first entry to fetch should be after 1980");
+        return (-1);
+    }
+
+    if (end_tmp < start_tmp) {
+        rrd_set_error("start (%ld) should be less than end (%ld)", start_tmp,
+                      end_tmp);
+        return (-1);
+    }
+
+    *start = start_tmp;
+    *end = end_tmp;
+
+    if (step_tmp < 1) {
+        rrd_set_error("step must be >= 1 second");
+        return -1;
+    }
+    *step = step_tmp;
+
+    if (optind + 1 >= argc) {
+        rrd_set_error("Usage: rrdtool %s <file> <CF> [options]", argv[0]);
+        return -1;
+    }
+
+    status = rrdc_flush_if_daemon(opt_daemon, argv[optind]);
+    if (opt_daemon) free (opt_daemon);
+    if (status) return (-1);
+
+    cf = argv[optind + 1];
+
+    status = rrd_fetch_r(argv[optind], cf, start, end, step,
+            ds_cnt, ds_namv, data);
+    if (status != 0)
+        return (-1);
+    return (0);
+}
+
+int rrd_fetch_r(
+    const char *filename,   /* name of the rrd */
+    const char *cf,     /* which consolidation function ? */
+    time_t *start,
+    time_t *end,        /* which time frame do you want ?
+                         * will be changed to represent reality */
+    unsigned long *step,    /* which stepsize do you want? 
+                             * will be changed to represent reality */
+    unsigned long *ds_cnt,  /* number of data sources in file */
+    char ***ds_namv,    /* names of data_sources */
+    rrd_value_t **data)
+{                       /* two dimensional array containing the data */
+    enum cf_en cf_idx;
+
+    if ((int) (cf_idx = cf_conv(cf)) == -1) {
+        return -1;
+    }
+
+    return (rrd_fetch_fn
+            (filename, cf_idx, start, end, step, ds_cnt, ds_namv, data));
+} /* int rrd_fetch_r */
+
+int rrd_fetch_fn(
+    const char *filename,   /* name of the rrd */
+    enum cf_en cf_idx,  /* which consolidation function ? */
+    time_t *start,
+    time_t *end,        /* which time frame do you want ?
+                         * will be changed to represent reality */
+    unsigned long *step,    /* which stepsize do you want? 
+                             * will be changed to represent reality */
+    unsigned long *ds_cnt,  /* number of data sources in file */
+    char ***ds_namv,    /* names of data_sources */
+    rrd_value_t **data)
+{                       /* two dimensional array containing the data */
+    long      i, ii;
+    time_t    cal_start, cal_end, rra_start_time, rra_end_time;
+    long      best_full_rra = 0, best_part_rra = 0, chosen_rra =
+        0, rra_pointer = 0;
+    long      best_full_step_diff = 0, best_part_step_diff =
+        0, tmp_step_diff = 0, tmp_match = 0, best_match = 0;
+    long      full_match, rra_base;
+    off_t     start_offset, end_offset;
+    int       first_full = 1;
+    int       first_part = 1;
+    rrd_t     rrd;
+    rrd_file_t *rrd_file;
+    rrd_value_t *data_ptr;
+    unsigned long rows;
+
+#ifdef DEBUG
+    fprintf(stderr, "Entered rrd_fetch_fn() searching for the best match\n");
+    fprintf(stderr, "Looking for: start %10lu end %10lu step %5lu\n",
+            *start, *end, *step);
+#endif
+
+#ifdef HAVE_LIBDBI
+    /* handle libdbi datasources */
+    if (strncmp("sql",filename,3)==0) {
+      if (filename[3]==filename[4]) {
+       return rrd_fetch_fn_libdbi(filename,cf_idx,start,end,step,ds_cnt,ds_namv,data);
+      }
+    }
+#endif
+
+    rrd_init(&rrd);
+    rrd_file = rrd_open(filename, &rrd, RRD_READONLY);
+    if (rrd_file == NULL)
+        goto err_free;
+
+    /* when was the really last update of this file ? */
+
+    if (((*ds_namv) =
+         (char **) malloc(rrd.stat_head->ds_cnt * sizeof(char *))) == NULL) {
+        rrd_set_error("malloc fetch ds_namv array");
+        goto err_close;
+    }
+
+    for (i = 0; (unsigned long) i < rrd.stat_head->ds_cnt; i++) {
+        if ((((*ds_namv)[i]) = (char*)malloc(sizeof(char) * DS_NAM_SIZE)) == NULL) {
+            rrd_set_error("malloc fetch ds_namv entry");
+            goto err_free_ds_namv;
+        }
+        strncpy((*ds_namv)[i], rrd.ds_def[i].ds_nam, DS_NAM_SIZE - 1);
+        (*ds_namv)[i][DS_NAM_SIZE - 1] = '\0';
+
+    }
+
+    /* find the rra which best matches the requirements */
+    for (i = 0; (unsigned) i < rrd.stat_head->rra_cnt; i++) {
+        if (cf_conv(rrd.rra_def[i].cf_nam) == cf_idx) {
+
+            cal_end = (rrd.live_head->last_up - (rrd.live_head->last_up
+                                                 % (rrd.rra_def[i].pdp_cnt
+                                                    *
+                                                    rrd.stat_head->
+                                                    pdp_step)));
+            cal_start =
+                (cal_end -
+                 (rrd.rra_def[i].pdp_cnt * rrd.rra_def[i].row_cnt *
+                  rrd.stat_head->pdp_step));
+
+            full_match = *end - *start;
+#ifdef DEBUG
+            fprintf(stderr, "Considering: start %10lu end %10lu step %5lu ",
+                    cal_start, cal_end,
+                    rrd.stat_head->pdp_step * rrd.rra_def[i].pdp_cnt);
+#endif
+            /* we need step difference in either full or partial case */
+            tmp_step_diff = labs(*step - (rrd.stat_head->pdp_step
+                                          * rrd.rra_def[i].pdp_cnt));
+            /* best full match */
+            if (cal_start <= *start) {
+                if (first_full || (tmp_step_diff < best_full_step_diff)) {
+                    first_full = 0;
+                    best_full_step_diff = tmp_step_diff;
+                    best_full_rra = i;
+#ifdef DEBUG
+                    fprintf(stderr, "best full match so far\n");
+                } else {
+                    fprintf(stderr, "full match, not best\n");
+#endif
+                }
+
+            } else {
+                /* best partial match */
+                tmp_match = full_match;
+                if (cal_start > *start)
+                    tmp_match -= (cal_start - *start);
+                if (first_part ||
+                    (best_match < tmp_match) ||
+                    (best_match == tmp_match &&
+                     tmp_step_diff < best_part_step_diff)) {
+#ifdef DEBUG
+                    fprintf(stderr, "best partial so far\n");
+#endif
+                    first_part = 0;
+                    best_match = tmp_match;
+                    best_part_step_diff = tmp_step_diff;
+                    best_part_rra = i;
+                } else {
+#ifdef DEBUG
+                    fprintf(stderr, "partial match, not best\n");
+#endif
+                }
+            }
+        }
+    }
+
+    /* lets see how the matching went. */
+    if (first_full == 0)
+        chosen_rra = best_full_rra;
+    else if (first_part == 0)
+        chosen_rra = best_part_rra;
+    else {
+        rrd_set_error
+            ("the RRD does not contain an RRA matching the chosen CF");
+        goto err_free_all_ds_namv;
+    }
+
+    /* set the wish parameters to their real values */
+    *step = rrd.stat_head->pdp_step * rrd.rra_def[chosen_rra].pdp_cnt;
+    *start -= (*start % *step);
+    *end += (*step - *end % *step);
+    rows = (*end - *start) / *step + 1;
+
+#ifdef DEBUG
+    fprintf(stderr,
+            "We found:    start %10lu end %10lu step %5lu rows  %lu\n",
+            *start, *end, *step, rows);
+#endif
+
+/* Start and end are now multiples of the step size.  The amount of
+** steps we want is (end-start)/step and *not* an extra one.
+** Reasoning:  if step is s and we want to graph from t to t+s,
+** we need exactly ((t+s)-t)/s rows.  The row to collect from the
+** database is the one with time stamp (t+s) which means t to t+s.
+*/
+    *ds_cnt = rrd.stat_head->ds_cnt;
+    if (((*data) = (rrd_value_t*)malloc(*ds_cnt * rows * sizeof(rrd_value_t))) == NULL) {
+        rrd_set_error("malloc fetch data area");
+        goto err_free_all_ds_namv;
+    }
+
+    data_ptr = (*data);
+
+    /* find base address of rra */
+    rra_base = rrd_file->header_len;
+    for (i = 0; i < chosen_rra; i++)
+        rra_base += (*ds_cnt * rrd.rra_def[i].row_cnt * sizeof(rrd_value_t));
+
+    /* find start and end offset */
+    rra_end_time = (rrd.live_head->last_up
+                    - (rrd.live_head->last_up % *step));
+    rra_start_time = (rra_end_time
+                      - (*step * (rrd.rra_def[chosen_rra].row_cnt - 1)));
+    /* here's an error by one if we don't be careful */
+    start_offset = (long) (*start + *step - rra_start_time) / (long) *step;
+    end_offset = (long) (rra_end_time - *end) / (long) *step;
+#ifdef DEBUG
+    fprintf(stderr,
+            "rra_start %lu, rra_end %lu, start_off %li, end_off %li\n",
+            rra_start_time, rra_end_time, start_offset, end_offset);
+#endif
+    /* only seek if the start time is before the end time */
+    if (*start <= rra_end_time && *end >= rra_start_time - (off_t)*step ){
+        if (start_offset <= 0)
+            rra_pointer = rrd.rra_ptr[chosen_rra].cur_row + 1;
+        else
+            rra_pointer = rrd.rra_ptr[chosen_rra].cur_row + 1 + start_offset;
+
+        rra_pointer = rra_pointer % (signed) rrd.rra_def[chosen_rra].row_cnt;
+         
+        if (rrd_seek(rrd_file, (rra_base + (rra_pointer * (*ds_cnt)
+                                        * sizeof(rrd_value_t))),
+                 SEEK_SET) != 0) {
+            rrd_set_error("seek error in RRA");
+            goto err_free_data;
+        }
+#ifdef DEBUG
+        fprintf(stderr, "First Seek: rra_base %lu rra_pointer %lu\n",
+                rra_base, rra_pointer);
+#endif
+    }
+    
+    /* step trough the array */
+
+    for (i = start_offset;
+         i < (signed) rrd.rra_def[chosen_rra].row_cnt - end_offset; i++) {
+        /* no valid data yet */
+        if (i < 0) {
+#ifdef DEBUG
+            fprintf(stderr, "pre fetch %li -- ", i);
+#endif
+            for (ii = 0; (unsigned) ii < *ds_cnt; ii++) {
+                *(data_ptr++) = DNAN;
+#ifdef DEBUG
+                fprintf(stderr, "%10.2f ", *(data_ptr - 1));
+#endif
+            }
+        }
+        /* past the valid data area */
+        else if (i >= (signed) rrd.rra_def[chosen_rra].row_cnt) {
+#ifdef DEBUG
+            fprintf(stderr, "past fetch %li -- ", i);
+#endif
+            for (ii = 0; (unsigned) ii < *ds_cnt; ii++) {
+                *(data_ptr++) = DNAN;
+#ifdef DEBUG
+                fprintf(stderr, "%10.2f ", *(data_ptr - 1));
+#endif
+            }
+        } else {
+            /* OK we are inside the valid area but the pointer has to 
+             * be wrapped*/
+            if (rra_pointer >= (signed) rrd.rra_def[chosen_rra].row_cnt) {
+                rra_pointer -= rrd.rra_def[chosen_rra].row_cnt;
+                if (rrd_seek(rrd_file, (rra_base + rra_pointer * (*ds_cnt)
+                                        * sizeof(rrd_value_t)),
+                             SEEK_SET) != 0) {
+                    rrd_set_error("wrap seek in RRA did fail");
+                    goto err_free_data;
+                }
+#ifdef DEBUG
+                fprintf(stderr, "wrap seek ...\n");
+#endif
+            }
+
+            if (rrd_read(rrd_file, data_ptr, sizeof(rrd_value_t) * (*ds_cnt))
+                != (ssize_t) (sizeof(rrd_value_t) * (*ds_cnt))) {
+                rrd_set_error("fetching cdp from rra");
+                goto err_free_data;
+            }
+#ifdef DEBUG
+            fprintf(stderr, "post fetch %li -- ", i);
+            for (ii = 0; ii < *ds_cnt; ii++)
+                fprintf(stderr, "%10.2f ", *(data_ptr + ii));
+#endif
+            data_ptr += *ds_cnt;
+            rra_pointer++;
+        }
+#ifdef DEBUG
+        fprintf(stderr, "\n");
+#endif
+
+    }
+
+    rrd_close(rrd_file);
+    rrd_free(&rrd);
+    return (0);
+  err_free_data:
+    free(*data);
+    *data = NULL;
+  err_free_all_ds_namv:
+    for (i = 0; (unsigned long) i < rrd.stat_head->ds_cnt; ++i)
+        free((*ds_namv)[i]);
+  err_free_ds_namv:
+    free(*ds_namv);
+  err_close:
+    rrd_close(rrd_file);
+  err_free:
+    rrd_free(&rrd);
+    return (-1);
+}
diff --git a/program/src/rrd_fetch_libdbi.c b/program/src/rrd_fetch_libdbi.c
new file mode 100644 (file)
index 0000000..0e8bc93
--- /dev/null
@@ -0,0 +1,655 @@
+#include "rrd_tool.h"
+#include <dbi/dbi.h>
+#include <time.h>
+
+/* the structures */
+struct sql_table_helper {
+  dbi_conn conn;
+  int connected;
+  dbi_result result;
+  char const* filename;
+  char const* dbdriver;
+  char* table_start;
+  char* table_next;
+  char const* where;
+  char * timestamp;
+  char * value;
+};
+
+/* the prototypes */
+static void _sql_close(struct sql_table_helper* th);
+static int _sql_setparam(struct sql_table_helper* th,char* key, char* value);
+static int _sql_fetchrow(struct sql_table_helper* th,time_t *timestamp, rrd_value_t *value,int ordered);
+static char* _find_next_separator(char* start,char separator);
+static char* _find_next_separator_twice(char*start,char separator);
+static char _hexcharhelper(char c);
+static int _inline_unescape (char* string);
+static double rrd_fetch_dbi_double(dbi_result *result,int idx);
+static long rrd_fetch_dbi_long(dbi_result *result,int idx);
+
+/* the real code */
+
+/* helpers to get correctly converted values from DB*/
+static long rrd_fetch_dbi_long(dbi_result *result,int idx) {
+  char *ptmp="";
+  long value=DNAN;
+  /* get the attributes for this filed */
+  unsigned int attr=dbi_result_get_field_attribs_idx(result,idx);
+  unsigned int type=dbi_result_get_field_type_idx(result,idx);
+  /* return NAN if NULL */
+  if(dbi_result_field_is_null_idx(result,idx)) { return DNAN; }
+  /* do some conversions */
+  switch (type) {
+    case DBI_TYPE_STRING:
+      ptmp=(char*)dbi_result_get_string_idx(result,idx);
+      value=atoi(ptmp);
+      break;
+    case DBI_TYPE_INTEGER:
+      if        (attr & DBI_INTEGER_SIZE1) { value=dbi_result_get_char_idx(result,idx);
+      } else if (attr & DBI_INTEGER_SIZE2) { value=dbi_result_get_short_idx(result,idx);
+      } else if (attr & DBI_INTEGER_SIZE3) { value=dbi_result_get_int_idx(result,idx);
+      } else if (attr & DBI_INTEGER_SIZE4) { value=dbi_result_get_int_idx(result,idx);
+      } else if (attr & DBI_INTEGER_SIZE8) { value=dbi_result_get_longlong_idx(result,idx);
+      } else {                               value=DNAN;
+        if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported attribute flags %i for type INTEGER\n",time(NULL),idx,attr ); }
+      }
+      break;
+    case DBI_TYPE_DECIMAL:
+      if        (attr & DBI_DECIMAL_SIZE4) { value=floor(dbi_result_get_float_idx(result,idx));
+      } else if (attr & DBI_DECIMAL_SIZE8) { value=floor(dbi_result_get_double_idx(result,idx));
+      } else {                               value=DNAN;
+        if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported attribute flags %i for type DECIMAL\n",time(NULL),idx,attr ); }
+      }
+      break;
+    case DBI_TYPE_BINARY:
+      attr=dbi_result_get_field_length_idx(result,idx);
+      ptmp=(char*)dbi_result_get_binary_copy_idx(result,idx);
+      ptmp[attr-1]=0;
+      /* check for "known" libdbi error */
+      if (strncmp("ERROR",ptmp,5)==0) {
+       if (!getenv("RRD_NO_LIBDBI_BUG_WARNING")) {
+         fprintf(stderr,"rrdtool_fetch_libDBI: you have possibly triggered a bug in libDBI by using a (TINY,MEDIUM,LONG) TEXT field with mysql\n  this may trigger a core dump in at least one version of libdbi\n  if you are not touched by this bug and you find this message annoying\n  please set the environment-variable RRD_NO_LIBDBI_BUG_WARNING to ignore this message\n");
+       }
+      }
+      /* convert to number */
+      value=atoi(ptmp);
+      /* free pointer */
+      free(ptmp);
+      break;
+    case DBI_TYPE_DATETIME:
+       value=dbi_result_get_datetime_idx(result,idx);
+       break;
+    default:
+      if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported type: %i with attribute %i\n",time(NULL),idx,type,attr ); }
+      value=DNAN;
+      break;
+  }
+  return value;
+}
+
+static double rrd_fetch_dbi_double(dbi_result *result,int idx) {
+  char *ptmp="";
+  double value=DNAN;
+  /* get the attributes for this filed */
+  unsigned int attr=dbi_result_get_field_attribs_idx(result,idx);
+  unsigned int type=dbi_result_get_field_type_idx(result,idx);
+  /* return NAN if NULL */
+  if(dbi_result_field_is_null_idx(result,idx)) { return DNAN; }
+  /* do some conversions */
+  switch (type) {
+    case DBI_TYPE_STRING:
+      ptmp=(char*)dbi_result_get_string_idx(result,idx);
+      value=strtod(ptmp,NULL);
+      break;
+    case DBI_TYPE_INTEGER:
+      if        (attr & DBI_INTEGER_SIZE1) { value=dbi_result_get_char_idx(result,idx);
+      } else if (attr & DBI_INTEGER_SIZE2) { value=dbi_result_get_short_idx(result,idx);
+      } else if (attr & DBI_INTEGER_SIZE3) { value=dbi_result_get_int_idx(result,idx);
+      } else if (attr & DBI_INTEGER_SIZE4) { value=dbi_result_get_int_idx(result,idx);
+      } else if (attr & DBI_INTEGER_SIZE8) { value=dbi_result_get_longlong_idx(result,idx);
+      } else {                               value=DNAN;
+        if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported attribute flags %i for type INTEGER\n",time(NULL),idx,attr ); }
+      }
+      break;
+    case DBI_TYPE_DECIMAL:
+      if        (attr & DBI_DECIMAL_SIZE4) { value=dbi_result_get_float_idx(result,idx);
+      } else if (attr & DBI_DECIMAL_SIZE8) { value=dbi_result_get_double_idx(result,idx);
+      } else {                               value=DNAN;
+        if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported attribute flags %i for type DECIMAL\n",time(NULL),idx,attr ); }
+      }
+      break;
+    case DBI_TYPE_BINARY:
+      attr=dbi_result_get_field_length_idx(result,idx);
+      ptmp=(char*)dbi_result_get_binary_copy_idx(result,idx);
+      ptmp[attr-1]=0;
+      /* check for "known" libdbi error */
+      if (strncmp("ERROR",ptmp,5)==0) {
+       if (!getenv("RRD_NO_LIBDBI_BUG_WARNING")) {
+         fprintf(stderr,"rrdtool_fetch_libDBI: you have possibly triggered a bug in libDBI by using a (TINY,MEDIUM,LONG) TEXT field with mysql\n  this may trigger a core dump in at least one version of libdbi\n  if you are not touched by this bug and you find this message annoying\n  please set the environment-variable RRD_NO_LIBDBI_BUG_WARNING to ignore this message\n");
+       }
+      }
+      /* convert to number */
+      value=strtod(ptmp,NULL);
+      /* free pointer */
+      free(ptmp);
+      break;
+    case DBI_TYPE_DATETIME:
+       value=dbi_result_get_datetime_idx(result,idx);
+       break;
+    default:
+      if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported type: %i with attribute %i\n",time(NULL),idx,type,attr ); }
+      value=DNAN;
+      break;
+  }
+  return value;
+}
+
+static void _sql_close(struct sql_table_helper* th) {
+  /* close only if connected */
+  if (th->conn) {
+    if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: close connection\n",time(NULL) ); }
+    /* shutdown dbi */
+    dbi_conn_close(th->conn);
+    if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: shutting down libdbi\n",time(NULL) ); }
+    dbi_shutdown();
+    /* and assign empty */
+    th->conn=NULL;
+    th->connected=0;
+  }
+}
+
+static int _sql_setparam(struct sql_table_helper* th,char* key, char* value) {
+  char* dbi_errstr=NULL;
+  dbi_driver driver;
+  /* if not connected */
+  if (! th->conn) {
+    /* initialize some stuff */
+    th->table_next=th->table_start;
+    th->result=NULL;
+    th->connected=0;
+    /* initialize db */
+    if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: initialize libDBI\n",time(NULL) ); }
+    dbi_initialize(NULL);
+    /* load the driver */
+    driver=dbi_driver_open(th->dbdriver);
+    if (! driver) {
+      rrd_set_error( "libdbi - no such driver: %s (possibly a dynamic link problem of the driver being linked without -ldbi)",th->dbdriver); 
+      return -1; 
+    }
+    /* and connect to driver */
+    th->conn=dbi_conn_open(driver);
+    /* and handle errors */
+    if (! th->conn) { 
+      rrd_set_error( "libdbi - could not open connection to driver %s",th->dbdriver); 
+      dbi_shutdown();
+      return -1;
+    }
+  }
+  if (th->connected) {
+    rrd_set_error( "we are already connected - can not set parameter %s=%s",key,value);
+    _sql_close(th);
+    return -1; 
+  }
+  if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: setting option %s to %s\n",time(NULL),key,value ); }
+  if (dbi_conn_set_option(th->conn,key,value)) {
+    dbi_conn_error(th->conn,(const char**)&dbi_errstr);
+    rrd_set_error( "libdbi: problems setting %s to %s - %s",key,value,dbi_errstr);
+    _sql_close(th);
+    return -1;
+  }
+  return 0;
+}
+
+static int _sql_fetchrow(struct sql_table_helper* th,time_t *timestamp, rrd_value_t *value,int ordered) {
+  char* dbi_errstr=NULL;
+  char sql[10240];
+  time_t startt=0,endt=0;
+  /*connect to the database if needed */
+  if (! th->conn) {
+      rrd_set_error( "libdbi no parameters set for libdbi",th->filename,dbi_errstr);
+      return -1;
+  }
+  if (! th->connected) {
+    /* and now connect */
+    if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: connect to DB\n",time(NULL) ); }
+    if (dbi_conn_connect(th->conn) <0) {
+      dbi_conn_error(th->conn,(const char**)&dbi_errstr);
+      rrd_set_error( "libdbi: problems connecting to db with connect string %s - error: %s",th->filename,dbi_errstr);
+      _sql_close(th);
+      return -1;
+    }
+    th->connected=1;
+  }
+  /* now find out regarding an existing result-set */
+  if (! th->result) {
+    /* return if table_next is NULL */
+    if (th->table_next==NULL) { 
+    if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: reached last table to connect to\n",time(NULL) ); }
+      /* but first close connection */
+      _sql_close(th);
+      /* and return with end of data */
+      return 0;
+    }
+    /* calculate the table to use next */
+    th->table_start=th->table_next;
+    th->table_next=_find_next_separator(th->table_start,'+');
+    _inline_unescape(th->table_start);
+    /* and prepare FULL SQL Statement */
+    if (ordered) {
+      snprintf(sql,sizeof(sql)-1,"SELECT %s as rrd_time, %s as rrd_value FROM %s WHERE %s ORDER BY %s",
+              th->timestamp,th->value,th->table_start,th->where,th->timestamp);
+    } else {
+      snprintf(sql,sizeof(sql)-1,"SELECT %s as rrd_time, %s as rrd_value FROM %s WHERE %s",
+              th->timestamp,th->value,th->table_start,th->where);
+    }
+    /* and execute sql */
+    if (getenv("RRDDEBUGSQL")) { startt=time(NULL); fprintf(stderr,"RRDDEBUGSQL: %li: executing %s\n",startt,sql); }
+    th->result=dbi_conn_query(th->conn,sql);
+    if (startt) { endt=time(NULL);fprintf(stderr,"RRDDEBUGSQL: %li: timing %li\n",endt,endt-startt); }
+    /* handle error case */
+    if (! th->result) {
+      dbi_conn_error(th->conn,(const char**)&dbi_errstr);      
+      if (startt) { fprintf(stderr,"RRDDEBUGSQL: %li: error %s\n",endt,dbi_errstr); }
+      rrd_set_error("libdbi: problems with query: %s - errormessage: %s",sql,dbi_errstr);
+      _sql_close(th);
+      return -1;
+    }
+  }
+  /* and now fetch key and value */
+  if (! dbi_result_next_row(th->result)) {
+    /* free result */
+    dbi_result_free(th->result);
+    th->result=NULL;
+    /* and call recursively - this will open the next table or close connection as a whole*/
+    return _sql_fetchrow(th,timestamp,value,ordered);
+  } 
+  /* and return with flag for one value */
+  *timestamp=rrd_fetch_dbi_long(th->result,1);
+  *value=rrd_fetch_dbi_double(th->result,2);
+  return 1;
+}
+
+static char* _find_next_separator(char* start,char separator) {
+  char* found=strchr(start,separator);
+  /* have we found it */
+  if (found) {
+    /* then 0 terminate current string */
+    *found=0; 
+    /* and return the pointer past the separator */
+    return (found+1);
+  }
+  /* not found, so return NULL */
+  return NULL;
+}
+
+static char* _find_next_separator_twice(char*start,char separator) {
+  char *found=start;
+  /* find next separator in string*/
+  while (found) {
+    /* if found and the next one is also a separator */
+    if (found[1] == separator) {
+      /* then 0 terminate current string */
+      *found=0;
+      /* and return the pointer past the current one*/
+      return (found+2);
+    }
+    /* find next occurance */
+    found=strchr(found+1,separator);
+  }
+  /* not found, so return NULL */
+  return NULL;
+}
+
+static char _hexcharhelper(char c) {
+  switch (c) {
+  case '0': return 0 ; break;
+  case '1': return 1 ; break;
+  case '2': return 2 ; break;
+  case '3': return 3 ; break;
+  case '4': return 4 ; break;
+  case '5': return 5 ; break;
+  case '6': return 6 ; break;
+  case '7': return 7 ; break;
+  case '8': return 8 ; break;
+  case '9': return 9 ; break;
+  case 'a': return 10 ; break;
+  case 'b': return 11 ; break;
+  case 'c': return 12 ; break;
+  case 'd': return 13 ; break;
+  case 'e': return 14 ; break;
+  case 'f': return 15 ; break;
+  case 'A': return 10 ; break;
+  case 'B': return 11 ; break;
+  case 'C': return 12 ; break;
+  case 'D': return 13 ; break;
+  case 'E': return 14 ; break;
+  case 'F': return 15 ; break;
+  }
+  return -1;
+}
+
+static int _inline_unescape (char* string) {
+  char *src=string;
+  char *dst=string;
+  char c,h1,h2;
+  while((c= *src)) {
+    src++;
+    if (c == '%') {
+      if (*src == '%') { 
+       /* increase src pointer by 1 skiping second % */
+       src+=1;
+      } else {
+       /* try to calculate hex value from the next 2 values*/
+       h1=_hexcharhelper(*src);
+       if (h1 == (char)-1) {
+         rrd_set_error("string escape error at: %s\n",string);
+         return(1);
+       }
+       h2=_hexcharhelper(*(src+1));
+       if (h1 == (char)-1) {
+         rrd_set_error("string escape error at: %s\n",string);
+         return(1);
+       }
+       c=h2+(h1<<4);
+       /* increase src pointer by 2 skiping 2 chars */
+       src+=2;
+      } 
+    }
+    *dst=c;
+    dst++;
+  }
+  *dst=0;
+  return 0;
+}
+
+int
+rrd_fetch_fn_libdbi(
+    const char     *filename,  /* name of the rrd */
+    enum cf_en     cf_idx __attribute__((unused)), /* consolidation function */
+    time_t         *start,
+    time_t         *end,       /* which time frame do you want ?
+                               * will be changed to represent reality */
+    unsigned long  *step,      /* which stepsize do you want? 
+                               * will be changed to represent reality */
+    unsigned long  *ds_cnt,    /* number of data sources in file */
+    char           ***ds_namv, /* names of data_sources */
+    rrd_value_t    **data)     /* two dimensional array containing the data */
+{
+  /* the separator used */
+  char separator='/';
+  /* a local copy of the filename - used for copying plus some pointer variables */
+  char filenameworkcopy[10240];
+  char *tmpptr=filenameworkcopy;
+  char *nextptr=NULL;
+  char *libdbiargs=NULL;
+  char *sqlargs=NULL;
+  /* the settings for the "works" of rrd */
+  int fillmissing=0;
+  unsigned long minstepsize=300;
+  /* by default assume unixtimestamp */
+  int isunixtime=1;
+  /* the result-set */
+  long r_timestamp,l_timestamp,d_timestamp;
+  double r_value,l_value,d_value;
+  int r_status;
+  int rows;
+  long idx;
+  int derive=0;
+  /* the libdbi connection data and the table_help structure */
+  struct sql_table_helper table_help;
+  char where[10240];
+  table_help.conn=NULL;
+  table_help.where=where;
+
+  /* some loop variables */
+  int i=0;
+
+  /* check header */
+  if (strncmp("sql",filename,3)!=0) { 
+    rrd_set_error( "formatstring wrong - %s",filename );return -1; 
+  }
+  if (filename[3]!=filename[4]) { 
+    rrd_set_error( "formatstring wrong - %s",filename );return -1; 
+  }
+
+  /* now make this the separator */
+  separator=filename[3];
+
+  /* copy filename for local modifications during parsing */
+  strncpy(filenameworkcopy,filename+5,sizeof(filenameworkcopy));
+
+  /* get the driver */
+  table_help.dbdriver=tmpptr;
+  libdbiargs=_find_next_separator(tmpptr,separator);
+  if (! libdbiargs) { 
+    /* error in argument */
+    rrd_set_error( "formatstring wrong as we did not find \"%c\"- %s",separator,table_help.dbdriver);
+    return -1; 
+  }
+
+  /* now find the next double separator - this defines the args to the database */
+  sqlargs=_find_next_separator_twice(libdbiargs,separator);
+  if (!sqlargs) {
+    rrd_set_error( "formatstring wrong for db arguments as we did not find \"%c%c\" in \"%s\"",separator,separator,libdbiargs);
+    return 1;
+  }
+
+  /* now we can start with the SQL Statement - best to start with this first, 
+     as then the error-handling is easier, as we do not have to handle libdbi shutdown as well */
+
+  /* parse the table(s) */
+  table_help.table_start=sqlargs;
+  nextptr=_find_next_separator(table_help.table_start,separator);
+  if (! nextptr) { 
+    /* error in argument */
+    rrd_set_error( "formatstring wrong - %s",tmpptr);
+    return -1; 
+  }
+  /* hex-unescape the value */
+  if(_inline_unescape(table_help.table_start)) { return -1; }
+
+  /* parse the unix timestamp column */
+  table_help.timestamp=nextptr;
+  nextptr=_find_next_separator(nextptr,separator);
+  if (! nextptr) { 
+    /* error in argument */
+    rrd_set_error( "formatstring wrong - %s",tmpptr);
+    return -1; 
+  }
+  /* if we have leading '*', then we have a TIMEDATE Field*/
+  if (table_help.timestamp[0]=='*') { isunixtime=0; table_help.timestamp++; }
+  /* hex-unescape the value */
+  if(_inline_unescape(table_help.timestamp)) { return -1; }
+
+  /* parse the value column */
+  table_help.value=nextptr;
+  nextptr=_find_next_separator(nextptr,separator);
+  if (! nextptr) { 
+    /* error in argument */
+    rrd_set_error( "formatstring wrong - %s",tmpptr);
+    return -1; 
+  }
+  /* hex-unescape the value */
+  if(_inline_unescape(table_help.value)) { return -1; }
+  
+  /* now prepare WHERE clause as empty string*/
+  where[0]=0;
+
+  /* and the where clause */
+  sqlargs=nextptr;
+  while(sqlargs) {
+    /* find next separator */
+    nextptr=_find_next_separator(sqlargs,separator);
+    /* now handle fields */
+    if (strcmp(sqlargs,"derive")==0) { /* the derive option with the default allowed max delta */
+      derive=600;
+    } else if (strcmp(sqlargs,"prediction")==0) {
+      rrd_set_error("argument prediction is no longer supported in a DEF - use new generic CDEF-functions instead");
+      return -1;
+    } else if (strcmp(sqlargs,"sigma")==0) {
+      rrd_set_error("argument sigma is no longer supported in a DEF - use new generic CDEF-functions instead");
+      return -1;
+    } else if (*sqlargs==0) { /* ignore empty */
+    } else { /* else add to where string */
+      if (where[0]) {strcat(where," AND ");}
+      strcat(where,sqlargs);
+    }
+    /* and continue loop with next pointer */
+    sqlargs=nextptr;
+  }
+  /* and unescape */
+  if(_inline_unescape(where)) { return -1; }
+
+  /* now parse LIBDBI options - this start initializing libdbi and beyond this point we need to reset the db as well in case of errors*/
+  while (libdbiargs) {
+    /* find separator */
+    nextptr=_find_next_separator(libdbiargs,separator);
+    /* now find =, separating key from value*/
+    tmpptr=_find_next_separator(libdbiargs,'=');
+    if (! tmpptr) { 
+      rrd_set_error( "formatstring wrong for db arguments as we did not find \"=\" in \"%s\"",libdbiargs);
+      _sql_close(&table_help);
+      return 1;
+    }
+    /* hex-unescape the value */
+    if(_inline_unescape(tmpptr)) { return -1; }
+    /* now handle the key/value pair */
+    if (strcmp(libdbiargs,"rrdminstepsize")==0) { /* allow override for minstepsize */
+      i=atoi(tmpptr);if (i>0) { minstepsize=i; }
+    } else if (strcmp(libdbiargs,"rrdfillmissing")==0) { /* allow override for minstepsize */
+      i=atoi(tmpptr);if (i>0) { fillmissing=i; }
+    } else if (strcmp(libdbiargs,"rrdderivemaxstep")==0) { /* allow override for derived max delta */
+      i=atoi(tmpptr);if (i>0) { if (derive) { derive=i; }}
+    } else { /* store in libdbi, as these are parameters */
+      if (_sql_setparam(&table_help,libdbiargs,tmpptr)) { return -1; }
+    }
+    /* and continue loop with next pointer */
+    libdbiargs=nextptr;
+  }
+  
+  /* and modify step if given */
+  if (*step<minstepsize) {*step=minstepsize;}
+  *start-=(*start)%(*step);
+  *end-=(*end)%(*step);
+
+  /* and append the SQL WHERE Clause for the timeframe calculated above (adding AND if required) */
+  if (where[0]) {strcat(where," AND ");}
+  i=strlen(where);
+  if (isunixtime) {
+    snprintf(where+i,sizeof(where)-1-i,"%li < %s AND %s < %li",*start,table_help.timestamp,table_help.timestamp,*end);
+  } else {
+    char tsstart[64];strftime(tsstart,sizeof(tsstart),"%Y-%m-%d %H:%M:%S",localtime(start));
+    char tsend[64];strftime(tsend,sizeof(tsend),"%Y-%m-%d %H:%M:%S",localtime(end));
+    snprintf(where+i,sizeof(where)-1-i,"'%s' < %s AND %s < '%s'",tsstart,table_help.timestamp,table_help.timestamp,tsend);
+  }
+
+  /* and now calculate the number of rows in the resultset... */
+  rows=((*end)-(*start))/(*step)+2;
+  
+  /* define the result set variables/columns returned */
+  *ds_cnt=5;
+  *ds_namv=(char**)malloc((*ds_cnt)*sizeof(char*));
+  for (i=0;i<(int)(*ds_cnt);i++) {
+    tmpptr=(char*)malloc(sizeof(char) * DS_NAM_SIZE);
+    (*ds_namv)[i]=tmpptr;
+    /* now copy what is required */
+    switch (i) {
+    case 0: strncpy(tmpptr,"min",DS_NAM_SIZE-1); break;
+    case 1: strncpy(tmpptr,"avg",DS_NAM_SIZE-1); break;
+    case 2: strncpy(tmpptr,"max",DS_NAM_SIZE-1); break;
+    case 3: strncpy(tmpptr,"count",DS_NAM_SIZE-1); break;
+    case 4: strncpy(tmpptr,"sigma",DS_NAM_SIZE-1); break;
+    }
+  }
+
+  /* allocate memory for resultset (with the following columns: min,avg,max,count,sigma) */
+  i=rows * sizeof(rrd_value_t)*(*ds_cnt);
+  if (((*data) = malloc(i))==NULL){
+    /* and return error */
+    rrd_set_error("malloc failed for %i bytes",i);
+    return(-1);
+  }
+  /* and fill with NAN */
+  for(i=0;i<rows;i++) {
+    (*data)[i*(*ds_cnt)+0]=DNAN; /* MIN */
+    (*data)[i*(*ds_cnt)+1]=DNAN; /* AVG */
+    (*data)[i*(*ds_cnt)+2]=DNAN; /* MAX */
+    (*data)[i*(*ds_cnt)+3]=0;    /* COUNT */
+    (*data)[i*(*ds_cnt)+4]=DNAN; /* SIGMA */
+  }
+  /* and assign undefined values for last - in case of derived calculation */
+  l_value=DNAN;l_timestamp=0;
+  /* here goes the real work processing all data */
+  while((r_status=_sql_fetchrow(&table_help,&r_timestamp,&r_value,derive))>0) {
+    /* processing of value */
+    /* calculate index for the timestamp */
+    idx=(r_timestamp-(*start))/(*step);
+    /* some out of bounds checks on idx */
+    if (idx<0) { idx=0;}
+    if (idx>rows) { idx=rows;}
+    /* and calculate derivative if necessary */
+    if (derive) {
+      /* calc deltas */
+      d_timestamp=r_timestamp-l_timestamp;
+      d_value=r_value-l_value;
+      /* assign current as last values */
+      l_timestamp=r_timestamp;
+      l_value=r_value;
+      /* assign DNAN by default for value */
+      r_value=DNAN;
+      /* check for timestamp delta to be within an acceptable range */
+      if ((d_timestamp>0)&&(d_timestamp<2*derive)) {
+       /* only handle positive delta - avoid wrap-arrounds/counter resets showing up as spikes */
+       if (d_value>0) {
+         /* and normalize to per second */
+         r_value=d_value/d_timestamp;
+       }
+      }
+    }
+    /* only add value if we have a value that is not NAN */
+    if (! isnan(r_value)) {
+      if ((*data)[idx*(*ds_cnt)+3]==0) { /* count is 0 so assign to overwrite DNAN */
+       (*data)[idx*(*ds_cnt)+0]=r_value; /* MIN */
+       (*data)[idx*(*ds_cnt)+1]=r_value; /* AVG */
+       (*data)[idx*(*ds_cnt)+2]=r_value; /* MAX */
+       (*data)[idx*(*ds_cnt)+3]=1;       /* COUNT */
+       (*data)[idx*(*ds_cnt)+4]=r_value; /* SIGMA */
+      } else {
+       /* MIN */
+       if ((*data)[idx*(*ds_cnt)+0]>r_value) { (*data)[idx*(*ds_cnt)+0]=r_value; }
+        /* AVG - at this moment still sum - corrected in post processing */
+       (*data)[idx*(*ds_cnt)+1]+=r_value;
+        /* MAX */
+       if ((*data)[idx*(*ds_cnt)+2]<r_value) { (*data)[idx*(*ds_cnt)+2]=r_value; }
+        /* COUNT */
+       (*data)[idx*(*ds_cnt)+3]++;
+        /* SIGMA - at this moment still sum of squares - corrected in post processing */
+       (*data)[idx*(*ds_cnt)+4]+=r_value*r_value;
+      }
+    }
+  }
+  /* and check for negativ status, pass back immediately */
+  if (r_status==-1) { return -1; }
+
+  /* post processing */
+  for(idx=0;idx<rows;idx++) {
+    long count=(*data)[idx*(*ds_cnt)+3];
+    if (count>0) {
+      /* calc deviation first */
+      if (count>2) {
+       r_value=count*(*data)[idx*(*ds_cnt)+4]-(*data)[idx*(*ds_cnt)+1]*(*data)[idx*(*ds_cnt)+1];
+       if (r_value<0) { 
+         r_value=DNAN; 
+       } else {
+         r_value=sqrt(r_value/(count*(count-1)));
+       }
+      }
+      (*data)[idx*(*ds_cnt)+4]=r_value;
+      /* now the average */
+      (*data)[idx*(*ds_cnt)+1]/=count;
+    }
+  }
+
+  /* and return OK */
+  return 0;
+}
diff --git a/program/src/rrd_first.c b/program/src/rrd_first.c
new file mode 100644 (file)
index 0000000..faae577
--- /dev/null
@@ -0,0 +1,98 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_first Return
+ *****************************************************************************
+ * Initial version by Burton Strauss, ntopSupport.com - 3/2005
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include "rrd_tool.h"
+
+
+time_t rrd_first(
+    int argc,
+    char **argv)
+{
+    int       target_rraindex = 0;
+    char     *endptr;
+    struct option long_options[] = {
+        {"rraindex", required_argument, 0, 129},
+        {0, 0, 0, 0}
+    };
+
+    optind = 0;
+    opterr = 0;         /* initialize getopt */
+
+    while (1) {
+        int       option_index = 0;
+        int       opt;
+
+        opt = getopt_long(argc, argv, "", long_options, &option_index);
+
+        if (opt == EOF)
+            break;
+
+        switch (opt) {
+        case 129:
+            target_rraindex = strtol(optarg, &endptr, 0);
+            if (target_rraindex < 0) {
+                rrd_set_error("invalid rraindex number");
+                return (-1);
+            }
+            break;
+        default:
+            rrd_set_error("usage rrdtool %s [--rraindex number] file.rrd",
+                          argv[0]);
+            return (-1);
+        }
+    }
+
+    if (optind >= argc) {
+        rrd_set_error("not enough arguments");
+        return -1;
+    }
+
+    return (rrd_first_r(argv[optind], target_rraindex));
+}
+
+
+time_t rrd_first_r(
+    const char *filename,
+    const int rraindex)
+{
+    off_t     rra_start, timer;
+    time_t    then = -1;
+    rrd_t     rrd;
+    rrd_file_t *rrd_file;
+
+    rrd_init(&rrd);
+    rrd_file = rrd_open(filename, &rrd, RRD_READONLY);
+    if (rrd_file == NULL) {
+        goto err_free;
+    }
+
+    if ((rraindex < 0) || (rraindex >= (int) rrd.stat_head->rra_cnt)) {
+        rrd_set_error("invalid rraindex number");
+        goto err_close;
+    }
+
+    rra_start = rrd_file->header_len;
+    rrd_seek(rrd_file,
+             (rra_start +
+              (rrd.rra_ptr[rraindex].cur_row + 1) *
+              rrd.stat_head->ds_cnt * sizeof(rrd_value_t)), SEEK_SET);
+    timer = -(long)(rrd.rra_def[rraindex].row_cnt - 1);
+    if (rrd.rra_ptr[rraindex].cur_row + 1 > rrd.rra_def[rraindex].row_cnt) {
+        rrd_seek(rrd_file, rra_start, SEEK_SET);
+    }
+    then = (rrd.live_head->last_up -
+            rrd.live_head->last_up %
+            (rrd.rra_def[rraindex].pdp_cnt * rrd.stat_head->pdp_step)) +
+        (timer * rrd.rra_def[rraindex].pdp_cnt * rrd.stat_head->pdp_step);
+  err_close:
+    rrd_close(rrd_file);
+  err_free:
+    rrd_free(&rrd);
+    return (then);
+}
diff --git a/program/src/rrd_flushcached.c b/program/src/rrd_flushcached.c
new file mode 100644 (file)
index 0000000..93b0fb5
--- /dev/null
@@ -0,0 +1,118 @@
+/**
+ * RRDTool - src/rrd_flushcached.c
+ * Copyright (C) 2008 Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "rrd_tool.h"
+#include "rrd_client.h"
+
+int rrd_flushcached (int argc, char **argv)
+{
+    char *opt_daemon = NULL;
+    int status;
+    int i;
+
+    /* initialize getopt */
+    optind = 0;
+    opterr = 0;
+
+    while (42)
+    {
+        int opt;
+        static struct option long_options[] =
+        {
+            {"daemon", required_argument, 0, 'd'},
+            {0, 0, 0, 0}
+        };
+
+        opt = getopt_long(argc, argv, "d:", long_options, NULL);
+
+        if (opt == -1)
+            break;
+
+        switch (opt)
+        {
+            case 'd':
+                if (opt_daemon != NULL)
+                    free (opt_daemon);
+                opt_daemon = strdup (optarg);
+                if (opt_daemon == NULL)
+                {
+                    rrd_set_error ("strdup failed.");
+                    return (-1);
+                }
+                break;
+
+            default:
+                rrd_set_error ("Usage: rrdtool %s [--daemon <addr>] <file>",
+                        argv[0]);
+                return (-1);
+        }
+    } /* while (42) */
+
+    if ((argc - optind) < 1)
+    {
+        rrd_set_error ("Usage: rrdtool %s [--daemon <addr>] <file> [<file> ...]", argv[0]);
+        return (-1);
+    }
+
+    /* try to connect to rrdcached */
+    status = rrdc_connect(opt_daemon);
+    if (status != 0) goto out;
+
+    if (! rrdc_is_connected(opt_daemon))
+    {
+        rrd_set_error ("Daemon address unknown. Please use the \"--daemon\" "
+                "option to set an address on the command line or set the "
+                "\"%s\" environment variable.",
+                ENV_RRDCACHED_ADDRESS);
+        status = -1;
+        goto out;
+    }
+
+    status = 0;
+    for (i = optind; i < argc; i++)
+    {
+        status = rrdc_flush(argv[i]);
+        if (status)
+        {
+            char *error;
+            int   remaining;
+
+            error     = strdup(rrd_get_error());
+            remaining = argc - optind - 1;
+
+            rrd_set_error("Flushing of file \"%s\" failed: %s. Skipping "
+                    "remaining %i file%s.", argv[i],
+                    ((! error) || (*error == '\0')) ? "unknown error" : error,
+                    remaining, (remaining == 1) ? "" : "s");
+            free(error);
+            break;
+        }
+    }
+
+out:
+    if (opt_daemon) free(opt_daemon);
+
+    return status;
+} /* int rrd_flush */
+
+/*
+ * vim: set sw=4 sts=4 et fdm=marker :
+ */
diff --git a/program/src/rrd_format.c b/program/src/rrd_format.c
new file mode 100644 (file)
index 0000000..18e1ecd
--- /dev/null
@@ -0,0 +1,113 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_format.c  RRD Database Format helper functions
+ *****************************************************************************
+ * $Id$
+ * $Log$
+ * Revision 1.5  2004/05/18 18:53:03  oetiker
+ * big spell checking patch -- slif@bellsouth.net
+ *
+ * Revision 1.4  2003/02/13 07:05:27  oetiker
+ * Find attached the patch I promised to send to you. Please note that there
+ * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c
+ * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This
+ * library is identical to librrd, but it contains support code for per-thread
+ * global variables currently used for error information only. This is similar
+ * to how errno per-thread variables are implemented.  librrd_th must be linked
+ * alongside of libpthred
+ *
+ * There is also a new file "THREADS", holding some documentation.
+ *
+ * -- Peter Stamfest <peter@stamfest.at>
+ *
+ * Revision 1.3  2002/02/01 20:34:49  oetiker
+ * fixed version number and date/time
+ *
+ * Revision 1.2  2001/03/10 23:54:39  oetiker
+ * Support for COMPUTE data sources (CDEF data sources). Removes the RPN
+ * parser and calculator from rrd_graph and puts then in a new file,
+ * rrd_rpncalc.c. Changes to core files rrd_create and rrd_update. Some
+ * clean-up of aberrant behavior stuff, including a bug fix.
+ * Documentation update (rrdcreate.pod, rrdupdate.pod). Change xml format.
+ * -- Jake Brutlag <jakeb@corp.webtv.net>
+ *
+ * Revision 1.1.1.1  2001/02/25 22:25:05  oetiker
+ * checkin
+ *
+ * Revision 1.3  1998/03/08 12:35:11  oetiker
+ * checkpointing things because the current setup seems to work
+ * according to the things said in the manpages
+ *
+ * Revision 1.2  1998/02/26 22:58:22  oetiker
+ * fixed define
+ *
+ * Revision 1.1  1998/02/21 16:14:41  oetiker
+ * Initial revision
+ *
+ *
+ *****************************************************************************/
+#include "rrd_tool.h"
+
+#define converter(VV,VVV) \
+   if (strcmp(#VV, string) == 0) return VVV;
+
+/* conversion functions to allow symbolic entry of enumerations */
+enum dst_en dst_conv(
+    char *string)
+{
+    converter(COUNTER, DST_COUNTER)
+        converter(ABSOLUTE, DST_ABSOLUTE)
+        converter(GAUGE, DST_GAUGE)
+        converter(DERIVE, DST_DERIVE)
+        converter(COMPUTE, DST_CDEF)
+        rrd_set_error("unknown data acquisition function '%s'", string);
+    return (enum dst_en)(-1);
+}
+
+
+enum cf_en cf_conv(
+    const char *string)
+{
+
+    converter(AVERAGE, CF_AVERAGE)
+        converter(MIN, CF_MINIMUM)
+        converter(MAX, CF_MAXIMUM)
+        converter(LAST, CF_LAST)
+        converter(HWPREDICT, CF_HWPREDICT)
+        converter(MHWPREDICT, CF_MHWPREDICT)
+        converter(DEVPREDICT, CF_DEVPREDICT)
+        converter(SEASONAL, CF_SEASONAL)
+        converter(DEVSEASONAL, CF_DEVSEASONAL)
+        converter(FAILURES, CF_FAILURES)
+        rrd_set_error("unknown consolidation function '%s'", string);
+    return (enum cf_en)(-1);
+}
+
+#undef converter
+
+long ds_match(
+    rrd_t *rrd,
+    char *ds_nam)
+{
+    unsigned long i;
+
+    for (i = 0; i < rrd->stat_head->ds_cnt; i++)
+        if ((strcmp(ds_nam, rrd->ds_def[i].ds_nam)) == 0)
+            return i;
+    rrd_set_error("unknown data source name '%s'", ds_nam);
+    return -1;
+}
+
+off_t rrd_get_header_size(
+    rrd_t *rrd)
+{
+    return sizeof(stat_head_t) + \
+        sizeof(ds_def_t) * rrd->stat_head->ds_cnt + \
+        sizeof(rra_def_t) * rrd->stat_head->rra_cnt + \
+        sizeof(time_t) + \
+        sizeof(live_head_t) + \
+        sizeof(pdp_prep_t) * rrd->stat_head->ds_cnt + \
+        sizeof(cdp_prep_t) * rrd->stat_head->ds_cnt * rrd->stat_head->rra_cnt + \
+        sizeof(rra_ptr_t) * rrd->stat_head->rra_cnt;
+}
diff --git a/program/src/rrd_format.h b/program/src/rrd_format.h
new file mode 100644 (file)
index 0000000..75a3c52
--- /dev/null
@@ -0,0 +1,428 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_format.h  RRD Database Format header
+ *****************************************************************************/
+
+#ifndef _RRD_FORMAT_H
+#define _RRD_FORMAT_H
+
+/* 
+ * _RRD_TOOL_H
+ *   We're building RRDTool itself.
+ *
+ * RRD_EXPORT_DEPRECATED
+ *   User is requesting internal function which need this struct. They have
+ *   been told that this will change and have agreed to adapt their programs.
+ */
+#if !defined(_RRD_TOOL_H) && !defined(RRD_EXPORT_DEPRECATED)
+# error "Do not include rrd_format.h directly. Include rrd.h instead!"
+#endif
+
+#include "rrd.h"
+
+/*****************************************************************************
+ * put this in your /usr/lib/magic file (/etc/magic on HPUX)
+ *
+ *  # rrd database format
+ *  0       string          RRD\0           rrd file
+ *  >5      string          >\0             version '%s'
+ *
+ *****************************************************************************/
+
+#define RRD_COOKIE    "RRD"
+/* #define RRD_VERSION   "0002" */
+/* changed because microsecond precision requires another field */
+#define RRD_VERSION   "0004"
+#define RRD_VERSION3  "0003"
+#define FLOAT_COOKIE  8.642135E130
+
+typedef union unival {
+    unsigned long u_cnt;
+    rrd_value_t u_val;
+} unival;
+
+
+/****************************************************************************
+ * The RRD Database Structure
+ * ---------------------------
+ * 
+ * In oder to properly describe the database structure lets define a few
+ * new words:
+ *
+ * ds - Data Source (ds) providing input to the database. A Data Source (ds)
+ *       can be a traffic counter, a temperature, the number of users logged
+ *       into a system. The rrd database format can handle the input of
+ *       several Data Sources (ds) in a singe database.
+ *  
+ * dst - Data Source Type (dst). The Data Source Type (dst) defines the rules
+ *       applied to Build Primary Data Points from the input provided by the
+ *       data sources (ds).
+ *
+ * pdp - Primary Data Point (pdp). After the database has accepted the
+ *       input from the data sources (ds). It starts building Primary
+ *       Data Points (pdp) from the data. Primary Data Points (pdp)
+ *       are evenly spaced along the time axis (pdp_step). The values
+ *       of the Primary Data Points are calculated from the values of
+ *       the data source (ds) and the exact time these values were
+ *       provided by the data source (ds).
+ *
+ * pdp_st - PDP Start (pdp_st). The moments (pdp_st) in time where
+ *       these steps occur are defined by the moments where the
+ *       number of seconds since 1970-jan-1 modulo pdp_step equals
+ *       zero (pdp_st). 
+ *
+ * cf -  Consolidation Function (cf). An arbitrary Consolidation Function (cf)
+ *       (averaging, min, max) is applied to the primary data points (pdp) to
+ *       calculate the consolidated data point.
+ *
+ * cdp - Consolidated Data Point (cdp) is the long term storage format for data
+ *       in the rrd database. Consolidated Data Points represent one or
+ *       several primary data points collected along the time axis. The
+ *       Consolidated Data Points (cdp) are stored in Round Robin Archives
+ *       (rra).
+ *
+ * rra - Round Robin Archive (rra). This is the place where the
+ *       consolidated data points (cdp) get stored. The data is
+ *       organized in rows (row) and columns (col). The Round Robin
+ *       Archive got its name from the method data is stored in
+ *       there. An RRD database can contain several Round Robin
+ *       Archives. Each Round Robin Archive can have a different row
+ *       spacing along the time axis (pdp_cnt) and a different
+ *       consolidation function (cf) used to build its consolidated
+ *       data points (cdp).  
+ * 
+ * rra_st - RRA Start (rra_st). The moments (rra_st) in time where
+ *       Consolidated Data Points (cdp) are added to an rra are
+ *       defined by the moments where the number of seconds since
+ *       1970-jan-1 modulo pdp_cnt*pdp_step equals zero (rra_st).
+ *
+ * row - Row (row). A row represent all consolidated data points (cdp)
+ *       in a round robin archive who are of the same age.
+ *       
+ * col - Column (col). A column (col) represent all consolidated
+ *       data points (cdp) in a round robin archive (rra) who
+ *       originated from the same data source (ds).
+ *
+ */
+
+/****************************************************************************
+ * POS 1: stat_head_t                           static header of the database
+ ****************************************************************************/
+
+typedef struct stat_head_t {
+
+    /* Data Base Identification Section ** */
+    char      cookie[4];    /* RRD */
+    char      version[5];   /* version of the format */
+    double    float_cookie; /* is it the correct double
+                             * representation ?  */
+
+    /* Data Base Structure Definition **** */
+    unsigned long ds_cnt;   /* how many different ds provide
+                             * input to the rrd */
+    unsigned long rra_cnt;  /* how many rras will be maintained
+                             * in the rrd */
+    unsigned long pdp_step; /* pdp interval in seconds */
+
+    unival    par[10];  /* global parameters ... unused
+                           at the moment */
+} stat_head_t;
+
+
+/****************************************************************************
+ * POS 2: ds_def_t  (* ds_cnt)                        Data Source definitions
+ ****************************************************************************/
+
+enum dst_en { DST_COUNTER = 0,  /* data source types available */
+    DST_ABSOLUTE,
+    DST_GAUGE,
+    DST_DERIVE,
+    DST_CDEF
+};
+
+enum ds_param_en { DS_mrhb_cnt = 0, /* minimum required heartbeat. A
+                                     * data source must provide input at
+                                     * least every ds_mrhb seconds,
+                                     * otherwise it is regarded dead and
+                                     * will be set to UNKNOWN */
+    DS_min_val,         /* the processed input of a ds must */
+    DS_max_val,         /* be between max_val and min_val
+                         * both can be set to UNKNOWN if you
+                         * do not care. Data outside the limits
+                         * set to UNKNOWN */
+    DS_cdef = DS_mrhb_cnt
+};                      /* pointer to encoded rpn
+                         * expression only applies to DST_CDEF */
+
+/* The magic number here is one less than DS_NAM_SIZE */
+#define DS_NAM_FMT    "%19[a-zA-Z0-9_-]"
+#define DS_NAM_SIZE   20
+
+#define DST_FMT    "%19[A-Z]"
+#define DST_SIZE   20
+
+typedef struct ds_def_t {
+    char      ds_nam[DS_NAM_SIZE];  /* Name of the data source (null terminated) */
+    char      dst[DST_SIZE];    /* Type of data source (null terminated) */
+    unival    par[10];  /* index of this array see ds_param_en */
+} ds_def_t;
+
+/****************************************************************************
+ * POS 3: rra_def_t ( *  rra_cnt)         one for each store to be maintained
+ ****************************************************************************/
+enum cf_en { CF_AVERAGE = 0,    /* data consolidation functions */
+    CF_MINIMUM,
+    CF_MAXIMUM,
+    CF_LAST,
+    CF_HWPREDICT,
+    /* An array of predictions using the seasonal 
+     * Holt-Winters algorithm. Requires an RRA of type
+     * CF_SEASONAL for this data source. */
+    CF_SEASONAL,
+    /* An array of seasonal effects. Requires an RRA of
+     * type CF_HWPREDICT for this data source. */
+    CF_DEVPREDICT,
+    /* An array of deviation predictions based upon
+     * smoothed seasonal deviations. Requires an RRA of
+     * type CF_DEVSEASONAL for this data source. */
+    CF_DEVSEASONAL,
+    /* An array of smoothed seasonal deviations. Requires
+     * an RRA of type CF_HWPREDICT for this data source.
+     * */
+    CF_FAILURES,
+    /* HWPREDICT that follows a moving baseline */
+    CF_MHWPREDICT
+        /* new entries must come last !!! */
+};
+
+                       /* A binary array of failure indicators: 1 indicates
+                        * that the number of violations in the prescribed
+                        * window exceeded the prescribed threshold. */
+
+#define MAX_RRA_PAR_EN 10
+enum rra_par_en { RRA_cdp_xff_val = 0,  /* what part of the consolidated
+                                         * datapoint must be known, to produce a
+                                         * valid entry in the rra */
+    /* CF_HWPREDICT: */
+    RRA_hw_alpha = 1,
+    /* exponential smoothing parameter for the intercept in
+     * the Holt-Winters prediction algorithm. */
+    RRA_hw_beta = 2,
+    /* exponential smoothing parameter for the slope in
+     * the Holt-Winters prediction algorithm. */
+
+    RRA_dependent_rra_idx = 3,
+    /* For CF_HWPREDICT: index of the RRA with the seasonal 
+     * effects of the Holt-Winters algorithm (of type
+     * CF_SEASONAL).
+     * For CF_DEVPREDICT: index of the RRA with the seasonal
+     * deviation predictions (of type CF_DEVSEASONAL).
+     * For CF_SEASONAL: index of the RRA with the Holt-Winters
+     * intercept and slope coefficient (of type CF_HWPREDICT).
+     * For CF_DEVSEASONAL: index of the RRA with the 
+     * Holt-Winters prediction (of type CF_HWPREDICT).
+     * For CF_FAILURES: index of the CF_DEVSEASONAL array.
+     * */
+
+    /* CF_SEASONAL and CF_DEVSEASONAL: */
+    RRA_seasonal_gamma = 1,
+    /* exponential smoothing parameter for seasonal effects. */
+
+    RRA_seasonal_smoothing_window = 2,
+    /* fraction of the season to include in the running average
+     * smoother */
+
+    /* RRA_dependent_rra_idx = 3, */
+
+    RRA_seasonal_smooth_idx = 4,
+    /* an integer between 0 and row_count - 1 which
+     * is index in the seasonal cycle for applying
+     * the period smoother. */
+
+    /* CF_FAILURES: */
+    RRA_delta_pos = 1,  /* confidence bound scaling parameters */
+    RRA_delta_neg = 2,
+    /* RRA_dependent_rra_idx = 3, */
+    RRA_window_len = 4,
+    RRA_failure_threshold = 5
+    /* For CF_FAILURES, number of violations within the last
+     * window required to mark a failure. */
+};
+
+                    /* For CF_FAILURES, the length of the window for measuring
+                     * failures. */
+
+#define CF_NAM_FMT    "%19[A-Z]"
+#define CF_NAM_SIZE   20
+
+typedef struct rra_def_t {
+    char      cf_nam[CF_NAM_SIZE];  /* consolidation function (null term) */
+    unsigned long row_cnt;  /* number of entries in the store */
+    unsigned long pdp_cnt;  /* how many primary data points are
+                             * required for a consolidated data
+                             * point?*/
+    unival    par[MAX_RRA_PAR_EN];  /* index see rra_param_en */
+
+} rra_def_t;
+
+
+/****************************************************************************
+ ****************************************************************************
+ ****************************************************************************
+ * LIVE PART OF THE HEADER. THIS WILL BE WRITTEN ON EVERY UPDATE         *
+ ****************************************************************************
+ ****************************************************************************
+ ****************************************************************************/
+/****************************************************************************
+ * POS 4: live_head_t                    
+ ****************************************************************************/
+
+typedef struct live_head_t {
+    time_t    last_up;  /* when was rrd last updated */
+    long      last_up_usec; /* micro seconds part of the
+                               update timestamp. Always >= 0 */
+} live_head_t;
+
+
+/****************************************************************************
+ * POS 5: pdp_prep_t  (* ds_cnt)                     here we prepare the pdps 
+ ****************************************************************************/
+#define LAST_DS_LEN 30  /* DO NOT CHANGE THIS ... */
+
+enum pdp_par_en { PDP_unkn_sec_cnt = 0, /* how many seconds of the current
+                                         * pdp value is unknown data? */
+
+    PDP_val
+};                      /* current value of the pdp.
+                           this depends on dst */
+
+typedef struct pdp_prep_t {
+    char      last_ds[LAST_DS_LEN]; /* the last reading from the data
+                                     * source.  this is stored in ASCII
+                                     * to cater for very large counters
+                                     * we might encounter in connection
+                                     * with SNMP. */
+    unival    scratch[10];  /* contents according to pdp_par_en */
+} pdp_prep_t;
+
+/* data is passed from pdp to cdp when seconds since epoch modulo pdp_step == 0
+   obviously the updates do not occur at these times only. Especially does the
+   format allow for updates to occur at different times for each data source.
+   The rules which makes this work is as follows:
+
+   * DS updates may only occur at ever increasing points in time
+   * When any DS update arrives after a cdp update time, the *previous*
+     update cycle gets executed. All pdps are transfered to cdps and the
+     cdps feed the rras where necessary. Only then the new DS value
+     is loaded into the PDP.                                                   */
+
+
+/****************************************************************************
+ * POS 6: cdp_prep_t (* rra_cnt * ds_cnt )      data prep area for cdp values
+ ****************************************************************************/
+#define MAX_CDP_PAR_EN 10
+#define MAX_CDP_FAILURES_IDX 8
+/* max CDP scratch entries avail to record violations for a FAILURES RRA */
+#define MAX_FAILURES_WINDOW_LEN 28
+enum cdp_par_en { CDP_val = 0,
+    /* the base_interval is always an
+     * average */
+    CDP_unkn_pdp_cnt,
+    /* how many unknown pdp were
+     * integrated. This and the cdp_xff
+     * will decide if this is going to
+     * be a UNKNOWN or a valid value */
+    CDP_hw_intercept,
+    /* Current intercept coefficient for the Holt-Winters
+     * prediction algorithm. */
+    CDP_hw_last_intercept,
+    /* Last iteration intercept coefficient for the Holt-Winters
+     * prediction algorihtm. */
+    CDP_hw_slope,
+    /* Current slope coefficient for the Holt-Winters
+     * prediction algorithm. */
+    CDP_hw_last_slope,
+    /* Last iteration slope coeffient. */
+    CDP_null_count,
+    /* Number of sequential Unknown (DNAN) values + 1 preceding
+     * the current prediction.
+     * */
+    CDP_last_null_count,
+    /* Last iteration count of Unknown (DNAN) values. */
+    CDP_primary_val = 8,
+    /* optimization for bulk updates: the value of the first CDP
+     * value to be written in the bulk update. */
+    CDP_secondary_val = 9,
+    /* optimization for bulk updates: the value of subsequent
+     * CDP values to be written in the bulk update. */
+    CDP_hw_seasonal = CDP_hw_intercept,
+    /* Current seasonal coefficient for the Holt-Winters
+     * prediction algorithm. This is stored in CDP prep to avoid
+     * redundant seek operations. */
+    CDP_hw_last_seasonal = CDP_hw_last_intercept,
+    /* Last iteration seasonal coeffient. */
+    CDP_seasonal_deviation = CDP_hw_intercept,
+    CDP_last_seasonal_deviation = CDP_hw_last_intercept,
+    CDP_init_seasonal = CDP_null_count
+};
+
+                   /* init_seasonal is a flag which when > 0, forces smoothing updates
+                    * to occur when rra_ptr.cur_row == 0 */
+
+typedef struct cdp_prep_t {
+    unival    scratch[MAX_CDP_PAR_EN];
+    /* contents according to cdp_par_en *
+     * init state should be NAN */
+
+} cdp_prep_t;
+
+/****************************************************************************
+ * POS 7: rra_ptr_t (* rra_cnt)       pointers to the current row in each rra
+ ****************************************************************************/
+
+typedef struct rra_ptr_t {
+    unsigned long cur_row;  /* current row in the rra */
+} rra_ptr_t;
+
+
+/****************************************************************************
+ ****************************************************************************
+ * One single struct to hold all the others. For convenience.
+ ****************************************************************************
+ ****************************************************************************/
+typedef struct rrd_t {
+    stat_head_t *stat_head; /* the static header */
+    ds_def_t *ds_def;   /* list of data source definitions */
+    rra_def_t *rra_def; /* list of round robin archive def */
+    live_head_t *live_head; /* rrd v >= 3 last_up with us */
+    time_t   *legacy_last_up;   /* rrd v < 3 last_up time */
+    pdp_prep_t *pdp_prep;   /* pdp data prep area */
+    cdp_prep_t *cdp_prep;   /* cdp prep area */
+    rra_ptr_t *rra_ptr; /* list of rra pointers */
+    rrd_value_t *rrd_value; /* list of rrd values */
+} rrd_t;
+
+/****************************************************************************
+ ****************************************************************************
+ * AFTER the header section we have the DATA STORAGE AREA it is made up from
+ * Consolidated Data Points organized in Round Robin Archives.
+ ****************************************************************************
+ ****************************************************************************
+
+ *RRA 0
+ (0,0) .................... ( ds_cnt -1 , 0)
+ .
+ . 
+ .
+ (0, row_cnt -1) ... (ds_cnt -1, row_cnt -1)
+
+ *RRA 1
+ *RRA 2
+
+ *RRA rra_cnt -1
+ ****************************************************************************/
+
+
+#endif
diff --git a/program/src/rrd_getopt.c b/program/src/rrd_getopt.c
new file mode 100644 (file)
index 0000000..f973471
--- /dev/null
@@ -0,0 +1,950 @@
+/* Getopt for GNU.
+   NOTE: getopt is now part of the C library, so if you don't know what
+   "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+   before changing it!
+
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97
+   Free Software Foundation, Inc.
+
+   This file is part of the GNU C Library.  Its master source is NOT part of
+   the C library, however.  The master source lives in /gd/gnu/lib.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+\f
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+   Ditto for AIX 3.2 and <stdlib.h>.  */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+
+#if !defined WIN32 && (!defined (__STDC__) || !__STDC__)
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+
+#ifdef HAVE_CONFIG_H
+#include "../rrd_config.h"
+#endif
+
+#include "rrd_i18n.h"
+
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+   contain conflicting prototypes for getopt.  */
+#include <stdlib.h>
+#include <unistd.h>
+#endif                          /* GNU C library.  */
+
+#ifdef VMS
+#include <unixlib.h>
+#if HAVE_STRING_H - 0
+#include <string.h>
+#endif
+#endif
+
+#if defined (_WIN32) && !defined (__CYGWIN32__)
+/* It's not Unix, really.  See?  Capital letters.  */
+#include <windows.h>
+#define getpid() GetCurrentProcessId()
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+   but it behaves differently for the user, since it allows the user
+   to intersperse the options with the other arguments.
+
+   As `getopt' works, it permutes the elements of ARGV so that,
+   when it is done, all the options precede everything else.  Thus
+   all application programs are extended to handle flexible argument order.
+
+   Setting the environment variable POSIXLY_CORRECT disables permutation.
+   Then the behavior is completely standard.
+
+   GNU application programs can use a third alternative mode in which
+   they can distinguish the relative order of options and other arguments.  */
+
+#include "rrd_getopt.h"
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+/*
+ * On some versions of Solaris, opterr and friends are defined in core libc
+ * rather than in a separate getopt module.  Define these variables only
+ * if configure found they aren't there by default.  (We assume that testing
+ * opterr is sufficient for all of these except optreset.)
+ */
+#ifndef HAVE_INT_OPTERR
+
+char     *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+/* Callers store zero here to inhibit the error message
+   for unrecognized options.  */
+
+int       opterr = 1;
+
+/* Set to an option character which was unrecognized.
+   This must be initialized on some systems to avoid linking in the
+   system's own getopt implementation.  */
+
+int       optopt = '?';
+
+/* 1003.2 says this must be 1 before any call.  */
+int       optind = 1;
+
+#else
+ extern int      opterr;
+ extern int      optind;
+ extern int      optopt;
+ extern char     *optarg;
+#endif
+
+
+/* Formerly, initialization of getopt depended on optind==0, which
+   causes problems with re-calling getopt as programs generally don't
+   know that. */
+
+int       __getopt_initialized = 0;
+
+/* The next char to be scanned in the option-element
+   in which the last option character we returned was found.
+   This allows us to pick up the scan where we left off.
+
+   If this is zero, or a null string, it means resume the scan
+   by advancing to the next ARGV-element.  */
+
+static char *nextchar;
+
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+   If the caller did not specify anything,
+   the default is REQUIRE_ORDER if the environment variable
+   POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+   REQUIRE_ORDER means don't recognize them as options;
+   stop option processing when the first non-option is seen.
+   This is what Unix does.
+   This mode of operation is selected by either setting the environment
+   variable POSIXLY_CORRECT, or using `+' as the first character
+   of the list of option characters.
+
+   PERMUTE is the default.  We permute the contents of ARGV as we scan,
+   so that eventually all the non-options are at the end.  This allows options
+   to be given in any order, even with programs that were not written to
+   expect this.
+
+   RETURN_IN_ORDER is an option available to programs that were written
+   to expect options and other ARGV-elements in any order and that care about
+   the ordering of the two.  We describe each non-option ARGV-element
+   as if it were the argument of an option with character code 1.
+   Using `-' as the first character of the list of option characters
+   selects this mode of operation.
+
+   The special argument `--' forces an end of option-scanning regardless
+   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */
+
+static enum {
+    REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable.  */
+static char *posixly_correct;
+\f
+/* we must include string as there are warnings without it ... */
+#include <string.h>
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+   because there are many ways it can cause trouble.
+   On some systems, it contains special magic macros that don't work
+   in GCC.  */
+#define        my_index        strchr
+#else
+
+/* Avoid depending on library functions or files
+   whose names are inconsistent.  */
+
+char     *getenv(
+    );
+
+static char* my_index(const char* str, int chr)
+{
+    while (*str) {
+        if (*str == chr)
+            return (char *) str;
+        str++;
+    }
+    return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+   If not using GCC, it is ok not to declare it.  */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+   That was relevant to code that was here before.  */
+#if !defined (__STDC__) || !__STDC__
+/* gcc with -traditional declares the built-in strlen to return int,
+   and has done so at least since version 2.4.5. -- rms.  */
+extern int strlen(
+    const char *);
+#endif                          /* not __STDC__ */
+#endif                          /* __GNUC__ */
+
+#endif                          /* not __GNU_LIBRARY__ */
+\f
+/* Handle permutation of arguments.  */
+
+/* Describe the part of ARGV that contains non-options that have
+   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
+   `last_nonopt' is the index after the last of them.  */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Bash 2.0 gives us an environment variable containing flags
+   indicating ARGV elements that should not be considered arguments.  */
+
+static const char *nonoption_flags;
+static int nonoption_flags_len;
+
+static int original_argc;
+static char *const *original_argv;
+
+/* Make sure the environment variable bash 2.0 puts in the environment
+   is valid for the getopt call we must make sure that the ARGV passed
+   to getopt is that one passed to the process.  */
+static void store_args(
+    int argc,
+    char *const *argv) __attribute__ ((unused));
+static void store_args(
+    int argc,
+    char *const *argv)
+{
+    /* XXX This is no good solution.  We should rather copy the args so
+       that we can compare them later.  But we must not use malloc(3).  */
+    original_argc = argc;
+    original_argv = argv;
+}
+
+text_set_element(__libc_subinit, store_args);
+#endif
+
+/* Exchange two adjacent subsequences of ARGV.
+   One subsequence is elements [first_nonopt,last_nonopt)
+   which contains all the non-options that have been skipped so far.
+   The other is elements [last_nonopt,optind), which contains all
+   the options processed since those non-options were skipped.
+
+   `first_nonopt' and `last_nonopt' are relocated so that they describe
+   the new indices of the non-options in ARGV after they are moved.  */
+
+#if defined (__STDC__) && __STDC__
+static void exchange(
+    char **);
+#endif
+
+static void exchange(char** argv)
+{
+    int       bottom = first_nonopt;
+    int       middle = last_nonopt;
+    int       top = optind;
+    char     *tem;
+
+    /* Exchange the shorter segment with the far end of the longer segment.
+       That puts the shorter segment into the right place.
+       It leaves the longer segment in the right place overall,
+       but it consists of two parts that need to be swapped next.  */
+
+    while (top > middle && middle > bottom) {
+        if (top - middle > middle - bottom) {
+            /* Bottom segment is the short one.  */
+            int       len = middle - bottom;
+            register int i;
+
+            /* Swap it with the top part of the top segment.  */
+            for (i = 0; i < len; i++) {
+                tem = argv[bottom + i];
+                argv[bottom + i] = argv[top - (middle - bottom) + i];
+                argv[top - (middle - bottom) + i] = tem;
+            }
+            /* Exclude the moved bottom segment from further swapping.  */
+            top -= len;
+        } else {
+            /* Top segment is the short one.  */
+            int       len = top - middle;
+            register int i;
+
+            /* Swap it with the bottom part of the bottom segment.  */
+            for (i = 0; i < len; i++) {
+                tem = argv[bottom + i];
+                argv[bottom + i] = argv[middle + i];
+                argv[middle + i] = tem;
+            }
+            /* Exclude the moved top segment from further swapping.  */
+            bottom += len;
+        }
+    }
+
+    /* Update records for the slots the non-options now occupy.  */
+
+    first_nonopt += (optind - last_nonopt);
+    last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made.  */
+
+#if defined (__STDC__) && __STDC__
+static const char *_getopt_initialize(
+    int,
+    char *const *,
+    const char *);
+#endif
+static const char* _getopt_initialize(int argc,
+                                      char** argv,
+                                      const char* optstring)
+{
+    /* Start processing options with ARGV-element 1 (since ARGV-element 0
+       is the program name); the sequence of previously skipped
+       non-option ARGV-elements is empty.  */
+
+    first_nonopt = last_nonopt = optind = 1;
+
+    nextchar = NULL;
+
+    posixly_correct = getenv("POSIXLY_CORRECT");
+
+    /* Determine how to handle the ordering of options and nonoptions.  */
+
+    if (optstring[0] == '-') {
+        ordering = RETURN_IN_ORDER;
+        ++optstring;
+    } else if (optstring[0] == '+') {
+        ordering = REQUIRE_ORDER;
+        ++optstring;
+    } else if (posixly_correct != NULL)
+        ordering = REQUIRE_ORDER;
+    else
+        ordering = PERMUTE;
+
+#ifdef _LIBC
+    if (posixly_correct == NULL
+        && argc == original_argc && argv == original_argv) {
+        /* Bash 2.0 puts a special variable in the environment for each
+           command it runs, specifying which ARGV elements are the results of
+           file name wildcard expansion and therefore should not be
+           considered as options.  */
+        char      var[100];
+
+        sprintf(var, "_%d_GNU_nonoption_argv_flags_", getpid());
+        nonoption_flags = getenv(var);
+        if (nonoption_flags == NULL)
+            nonoption_flags_len = 0;
+        else
+            nonoption_flags_len = strlen(nonoption_flags);
+    } else
+        nonoption_flags_len = 0;
+#endif
+
+    return optstring;
+}
+\f
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+   given in OPTSTRING.
+
+   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+   then it is an option element.  The characters of this element
+   (aside from the initial '-') are option characters.  If `getopt'
+   is called repeatedly, it returns successively each of the option characters
+   from each of the option elements.
+
+   If `getopt' finds another option character, it returns that character,
+   updating `optind' and `nextchar' so that the next call to `getopt' can
+   resume the scan with the following option character or ARGV-element.
+
+   If there are no more option characters, `getopt' returns -1.
+   Then `optind' is the index in ARGV of the first ARGV-element
+   that is not an option.  (The ARGV-elements have been permuted
+   so that those that are not options now come last.)
+
+   OPTSTRING is a string containing the legitimate option characters.
+   If an option character is seen that is not listed in OPTSTRING,
+   return '?' after printing an error message.  If you set `opterr' to
+   zero, the error message is suppressed but we still return '?'.
+
+   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+   so the following text in the same ARGV-element, or the text of the following
+   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   wants an optional arg; if there is text in the current ARGV-element,
+   it is returned in `optarg', otherwise `optarg' is set to zero.
+
+   If OPTSTRING starts with `-' or `+', it requests different methods of
+   handling the non-option ARGV-elements.
+   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+   Long-named options begin with `--' instead of `-'.
+   Their names may be abbreviated as long as the abbreviation is unique
+   or is an exact match for some defined option.  If they have an
+   argument, it follows the option name in the same ARGV-element, separated
+   from the option name by a `=', or else the in next ARGV-element.
+   When `getopt' finds a long-named option, it returns 0 if that option's
+   `flag' field is nonzero, the value of the option's `val' field
+   if the `flag' field is zero.
+
+   The elements of ARGV aren't really const, because we permute them.
+   But we pretend they're const in the prototype to be compatible
+   with other systems.
+
+   LONGOPTS is a vector of `struct option' terminated by an
+   element containing a name which is zero.
+
+   LONGIND returns the index in LONGOPT of the long-named option found.
+   It is only valid when a long-named option has been found by the most
+   recent call.
+
+   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+   long-named options.  */
+
+int _getopt_internal(int argc,
+                     char** argv,
+                     const char *optstring,
+                     const struct option *longopts,
+                     int* longind,
+                     int long_only)
+{
+    optarg = NULL;
+
+    if (!__getopt_initialized || optind == 0) {
+        optstring = _getopt_initialize(argc, argv, optstring);
+        optind = 1;     /* Don't scan ARGV[0], the program name.  */
+        __getopt_initialized = 1;
+    }
+
+    /* Test whether ARGV[optind] points to a non-option argument.
+       Either it does not have option syntax, or there is an environment flag
+       from the shell indicating it is not an option.  The later information
+       is only used when the used in the GNU libc.  */
+#ifdef _LIBC
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0'       \
+                    || (optind < nonoption_flags_len                         \
+                        && nonoption_flags[optind] == '1'))
+#else
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+    if (nextchar == NULL || *nextchar == '\0') {
+        /* Advance to the next ARGV-element.  */
+
+        /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+           moved back by the user (who may also have changed the arguments).  */
+        if (last_nonopt > optind)
+            last_nonopt = optind;
+        if (first_nonopt > optind)
+            first_nonopt = optind;
+
+        if (ordering == PERMUTE) {
+            /* If we have just processed some options following some non-options,
+               exchange them so that the options come first.  */
+
+            if (first_nonopt != last_nonopt && last_nonopt != optind)
+                exchange((char **) argv);
+            else if (last_nonopt != optind)
+                first_nonopt = optind;
+
+            /* Skip any additional non-options
+               and extend the range of non-options previously skipped.  */
+
+            while (optind < argc && NONOPTION_P)
+                optind++;
+            last_nonopt = optind;
+        }
+
+        /* The special ARGV-element `--' means premature end of options.
+           Skip it like a null option,
+           then exchange with previous non-options as if it were an option,
+           then skip everything else like a non-option.  */
+
+        if (optind != argc && !strcmp(argv[optind], "--")) {
+            optind++;
+
+            if (first_nonopt != last_nonopt && last_nonopt != optind)
+                exchange((char **) argv);
+            else if (first_nonopt == last_nonopt)
+                first_nonopt = optind;
+            last_nonopt = argc;
+
+            optind = argc;
+        }
+
+        /* If we have done all the ARGV-elements, stop the scan
+           and back over any non-options that we skipped and permuted.  */
+
+        if (optind == argc) {
+            /* Set the next-arg-index to point at the non-options
+               that we previously skipped, so the caller will digest them.  */
+            if (first_nonopt != last_nonopt)
+                optind = first_nonopt;
+            return -1;
+        }
+
+        /* If we have come to a non-option and did not permute it,
+           either stop the scan or describe it to the caller and pass it by.  */
+
+        if (NONOPTION_P) {
+            if (ordering == REQUIRE_ORDER)
+                return -1;
+            optarg = argv[optind++];
+            return 1;
+        }
+
+        /* We have found another option-ARGV-element.
+           Skip the initial punctuation.  */
+
+        nextchar = (argv[optind] + 1
+                    + (longopts != NULL && argv[optind][1] == '-'));
+    }
+
+    /* Decode the current option-ARGV-element.  */
+
+    /* Check whether the ARGV-element is a long option.
+
+       If long_only and the ARGV-element has the form "-f", where f is
+       a valid short option, don't consider it an abbreviated form of
+       a long option that starts with f.  Otherwise there would be no
+       way to give the -f short option.
+
+       On the other hand, if there's a long option "fubar" and
+       the ARGV-element is "-fu", do consider that an abbreviation of
+       the long option, just like "--fu", and not "-f" with arg "u".
+
+       This distinction seems to be the most useful approach.  */
+
+    if (longopts != NULL
+        && (argv[optind][1] == '-' || (long_only && (argv[optind][2]
+                                                     || !my_index(optstring,
+                                                                  argv[optind]
+                                                                  [1]))))) {
+        char     *nameend;
+        const struct option *p;
+        const struct option *pfound = NULL;
+        int       exact = 0;
+        int       ambig = 0;
+        int       indfound = -1;
+        int       option_index;
+
+        for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+            /* Do nothing.  */ ;
+
+        /* Test all long options for either exact match
+           or abbreviated matches.  */
+        for (p = longopts, option_index = 0; p->name; p++, option_index++)
+            if (!strncmp(p->name, nextchar, nameend - nextchar)) {
+                if ((unsigned int) (nameend - nextchar)
+                    == (unsigned int) strlen(p->name)) {
+                    /* Exact match found.  */
+                    pfound = p;
+                    indfound = option_index;
+                    exact = 1;
+                    break;
+                } else if (pfound == NULL) {
+                    /* First nonexact match found.  */
+                    pfound = p;
+                    indfound = option_index;
+                } else
+                    /* Second or later nonexact match found.  */
+                    ambig = 1;
+            }
+
+        if (ambig && !exact) {
+            if (opterr)
+                fprintf(stderr, _("%s: option `%s' is ambiguous\n"),
+                        argv[0], argv[optind]);
+            nextchar += strlen(nextchar);
+            optind++;
+            optopt = 0;
+            return '?';
+        }
+
+        if (pfound != NULL) {
+            option_index = indfound;
+            optind++;
+            if (*nameend) {
+                /* Don't test has_arg with >, because some C compilers don't
+                   allow it to be used on enums.  */
+                if (pfound->has_arg)
+                    optarg = nameend + 1;
+                else {
+                    if (opterr) {
+                        if (argv[optind - 1][1] == '-')
+                            /* --option */
+                            fprintf(stderr,
+                                    _
+                                    ("%s: option `--%s' doesn't allow an argument\n"),
+                                    argv[0], pfound->name);
+                        else
+                            /* +option or -option */
+                            fprintf(stderr,
+                                    _
+                                    ("%s: option `%c%s' doesn't allow an argument\n"),
+                                    argv[0], argv[optind - 1][0],
+                                    pfound->name);
+                    }
+                    nextchar += strlen(nextchar);
+
+                    optopt = pfound->val;
+                    return '?';
+                }
+            } else if (pfound->has_arg == 1) {
+                if (optind < argc)
+                    optarg = argv[optind++];
+                else {
+                    if (opterr)
+                        fprintf(stderr,
+                                _("%s: option `%s' requires an argument\n"),
+                                argv[0], argv[optind - 1]);
+                    nextchar += strlen(nextchar);
+                    optopt = pfound->val;
+                    return optstring[0] == ':' ? ':' : '?';
+                }
+            }
+            nextchar += strlen(nextchar);
+            if (longind != NULL)
+                *longind = option_index;
+            if (pfound->flag) {
+                *(pfound->flag) = pfound->val;
+                return 0;
+            }
+            return pfound->val;
+        }
+
+        /* Can't find it as a long option.  If this is not getopt_long_only,
+           or the option starts with '--' or is not a valid short
+           option, then it's an error.
+           Otherwise interpret it as a short option.  */
+        if (!long_only || argv[optind][1] == '-'
+            || my_index(optstring, *nextchar) == NULL) {
+            if (opterr) {
+                if (argv[optind][1] == '-')
+                    /* --option */
+                    fprintf(stderr, _("%s: unrecognized option `--%s'\n"),
+                            argv[0], nextchar);
+                else
+                    /* +option or -option */
+                    fprintf(stderr, _("%s: unrecognized option `%c%s'\n"),
+                            argv[0], argv[optind][0], nextchar);
+            }
+            nextchar = (char *) "";
+            optind++;
+            optopt = 0;
+            return '?';
+        }
+    }
+
+    /* Look at and handle the next short option-character.  */
+
+    {
+        char      c = *nextchar++;
+        char     *temp = my_index(optstring, c);
+
+        /* Increment `optind' when we start to process its last character.  */
+        if (*nextchar == '\0')
+            ++optind;
+
+        if (temp == NULL || c == ':') {
+            if (opterr) {
+                if (posixly_correct)
+                    /* 1003.2 specifies the format of this message.  */
+                    fprintf(stderr, _("%s: illegal option -- %c\n"),
+                            argv[0], c);
+                else
+                    fprintf(stderr, _("%s: invalid option -- %c\n"),
+                            argv[0], c);
+            }
+            optopt = c;
+            return '?';
+        }
+        /* Convenience. Treat POSIX -W foo same as long option --foo */
+        if (temp[0] == 'W' && temp[1] == ';') {
+            char     *nameend;
+            const struct option *p;
+            const struct option *pfound = NULL;
+            int       exact = 0;
+            int       ambig = 0;
+            int       indfound = 0;
+            int       option_index;
+
+            /* This is an option that requires an argument.  */
+            if (*nextchar != '\0') {
+                optarg = nextchar;
+                /* If we end this ARGV-element by taking the rest as an arg,
+                   we must advance to the next element now.  */
+                optind++;
+            } else if (optind == argc) {
+                if (opterr) {
+                    /* 1003.2 specifies the format of this message.  */
+                    fprintf(stderr,
+                            _("%s: option requires an argument -- %c\n"),
+                            argv[0], c);
+                }
+                optopt = c;
+                if (optstring[0] == ':')
+                    c = ':';
+                else
+                    c = '?';
+                return c;
+            } else
+                /* We already incremented `optind' once;
+                   increment it again when taking next ARGV-elt as argument.  */
+                optarg = argv[optind++];
+
+            /* optarg is now the argument, see if it's in the
+               table of longopts.  */
+
+            for (nextchar = nameend = optarg; *nameend && *nameend != '=';
+                 nameend++)
+                /* Do nothing.  */ ;
+
+            /* Test all long options for either exact match
+               or abbreviated matches.  */
+            for (p = longopts, option_index = 0; p->name; p++, option_index++)
+                if (!strncmp(p->name, nextchar, nameend - nextchar)) {
+                    if ((unsigned int) (nameend - nextchar) ==
+                        strlen(p->name)) {
+                        /* Exact match found.  */
+                        pfound = p;
+                        indfound = option_index;
+                        exact = 1;
+                        break;
+                    } else if (pfound == NULL) {
+                        /* First nonexact match found.  */
+                        pfound = p;
+                        indfound = option_index;
+                    } else
+                        /* Second or later nonexact match found.  */
+                        ambig = 1;
+                }
+            if (ambig && !exact) {
+                if (opterr)
+                    fprintf(stderr, _("%s: option `-W %s' is ambiguous\n"),
+                            argv[0], argv[optind]);
+                nextchar += strlen(nextchar);
+                optind++;
+                return '?';
+            }
+            if (pfound != NULL) {
+                option_index = indfound;
+                if (*nameend) {
+                    /* Don't test has_arg with >, because some C compilers don't
+                       allow it to be used on enums.  */
+                    if (pfound->has_arg)
+                        optarg = nameend + 1;
+                    else {
+                        if (opterr)
+                            fprintf(stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name);
+
+                        nextchar += strlen(nextchar);
+                        return '?';
+                    }
+                } else if (pfound->has_arg == 1) {
+                    if (optind < argc)
+                        optarg = argv[optind++];
+                    else {
+                        if (opterr)
+                            fprintf(stderr,
+                                    _
+                                    ("%s: option `%s' requires an argument\n"),
+                                    argv[0], argv[optind - 1]);
+                        nextchar += strlen(nextchar);
+                        return optstring[0] == ':' ? ':' : '?';
+                    }
+                }
+                nextchar += strlen(nextchar);
+                if (longind != NULL)
+                    *longind = option_index;
+                if (pfound->flag) {
+                    *(pfound->flag) = pfound->val;
+                    return 0;
+                }
+                return pfound->val;
+            }
+            nextchar = NULL;
+            return 'W'; /* Let the application handle it.   */
+        }
+        if (temp[1] == ':') {
+            if (temp[2] == ':') {
+                /* This is an option that accepts an argument optionally.  */
+                if (*nextchar != '\0') {
+                    optarg = nextchar;
+                    optind++;
+                } else
+                    optarg = NULL;
+                nextchar = NULL;
+            } else {
+                /* This is an option that requires an argument.  */
+                if (*nextchar != '\0') {
+                    optarg = nextchar;
+                    /* If we end this ARGV-element by taking the rest as an arg,
+                       we must advance to the next element now.  */
+                    optind++;
+                } else if (optind == argc) {
+                    if (opterr) {
+                        /* 1003.2 specifies the format of this message.  */
+                        fprintf(stderr,
+                                _("%s: option requires an argument -- %c\n"),
+                                argv[0], c);
+                    }
+                    optopt = c;
+                    if (optstring[0] == ':')
+                        c = ':';
+                    else
+                        c = '?';
+                } else
+                    /* We already incremented `optind' once;
+                       increment it again when taking next ARGV-elt as argument.  */
+                    optarg = argv[optind++];
+                nextchar = NULL;
+            }
+        }
+        return c;
+    }
+}
+
+int getopt(
+    int argc,
+    char** argv,
+    const char* optstring)
+{
+    return _getopt_internal(argc, argv, optstring,
+                            (const struct option *) 0, (int *) 0, 0);
+}
+
+#endif                          /* Not ELIDE_CODE.  */
+\f
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+   the above definition of `getopt'.  */
+
+int main(
+    argc,
+    argv)
+    int argc;
+    char    **argv;
+{
+    int       c;
+    int       digit_optind = 0;
+
+    while (1) {
+        int       this_option_optind = optind ? optind : 1;
+
+        c = getopt(argc, argv, "abc:d:0123456789");
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            if (digit_optind != 0 && digit_optind != this_option_optind)
+                printf("digits occur in two different argv-elements.\n");
+            digit_optind = this_option_optind;
+            printf("option %c\n", c);
+            break;
+
+        case 'a':
+            printf("option a\n");
+            break;
+
+        case 'b':
+            printf("option b\n");
+            break;
+
+        case 'c':
+            printf("option c with value `%s'\n", optarg);
+            break;
+
+        case '?':
+            break;
+
+        default:
+            printf("?? getopt returned character code 0%o ??\n", c);
+        }
+    }
+
+    if (optind < argc) {
+        printf("non-option ARGV-elements: ");
+        while (optind < argc)
+            printf("%s ", argv[optind++]);
+        printf("\n");
+    }
+
+    exit(0);
+}
+
+#endif                          /* TEST */
diff --git a/program/src/rrd_getopt.h b/program/src/rrd_getopt.h
new file mode 100644 (file)
index 0000000..1d443f1
--- /dev/null
@@ -0,0 +1,151 @@
+/* Declarations for getopt.
+   Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+
+   This file is part of the GNU C Library.  Its master source is NOT part of
+   the C library, however.  The master source lives in /gd/gnu/lib.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern    "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+    extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+    extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+   for unrecognized options.  */
+
+    extern int opterr;
+
+/* Set to an option character which was unrecognized.  */
+
+    extern int optopt;
+
+/* Describe the long-named options requested by the application.
+   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+   of `struct option' terminated by an element containing a name which is
+   zero.
+
+   The field `has_arg' is:
+   no_argument         (or 0) if the option does not take an argument,
+   required_argument   (or 1) if the option requires an argument,
+   optional_argument   (or 2) if the option takes an optional argument.
+
+   If the field `flag' is not NULL, it points to a variable that is set
+   to the value given in the field `val' when the option is found, but
+   left unchanged if the option is not found.
+
+   To have a long-named option do something other than set an `int' to
+   a compiled-in constant, such as set a value from `optarg', set the
+   option's `flag' field to zero and its `val' field to a nonzero
+   value (the equivalent single-letter option character, if there is
+   one).  For long options that have a zero `flag' field, `getopt'
+   returns the contents of the `val' field.  */
+
+    struct option {
+#if defined (__STDC__) && __STDC__
+        const char *name;
+#else
+        char     *name;
+#endif
+        /* has_arg can't be an enum because some compilers complain about
+           type mismatches in all the code that assumes it is an int.  */
+        int       has_arg;
+        int      *flag;
+        int       val;
+    };
+
+/* Names for the values of the `has_arg' field of `struct option'.  */
+
+#define        no_argument             0
+#define required_argument      1
+#define optional_argument      2
+
+#if defined (__STDC__) && __STDC__
+#ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+   differences in the consts, in stdlib.h.  To avoid compilation
+   errors, only prototype getopt for the GNU C library.  */
+    extern int getopt(
+    int argc,
+    char *const *argv,
+    const char *shortopts);
+#else                   /* not __GNU_LIBRARY__ */
+    extern int getopt(
+        );
+#endif                  /* __GNU_LIBRARY__ */
+    extern int getopt_long(
+    int argc,
+    char *const *argv,
+    const char *shortopts,
+    const struct option *longopts,
+    int *longind);
+    extern int getopt_long_only(
+    int argc,
+    char *const *argv,
+    const char *shortopts,
+    const struct option *longopts,
+    int *longind);
+
+/* Internal only.  Users should not call this directly.  */
+    extern int _getopt_internal(
+    int argc,
+    char *const *argv,
+    const char *shortopts,
+    const struct option *longopts,
+    int *longind,
+    int long_only);
+#else                   /* not __STDC__ */
+    int getopt_long(int argc,
+                    char **argv,
+                    const char *options,
+                    const struct option *long_options,
+                    int *opt_index);
+    int _getopt_internal(int argc,
+                         char **argv,
+                         const char *shortopts,
+                         const struct option *longopts,
+                         int *longind,
+                         int long_only);
+#endif                  /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+#endif                  /* _GETOPT_H */
diff --git a/program/src/rrd_getopt1.c b/program/src/rrd_getopt1.c
new file mode 100644 (file)
index 0000000..1ace77e
--- /dev/null
@@ -0,0 +1,183 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+   Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+
+   This file is part of the GNU C Library.  Its master source is NOT part of
+   the C library, however.  The master source lives in /gd/gnu/lib.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+\f
+
+#if !defined WIN32 && (!defined (__STDC__) || !__STDC__)
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "../rrd_config.h"
+#endif
+
+#include "rrd_getopt.h"
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef        NULL
+#define NULL 0
+#endif
+
+int getopt_long(int argc,
+                char** argv,
+                const char* options,
+                const struct option* long_options,
+                int* opt_index)
+{
+    return _getopt_internal(argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+   If an option that starts with '-' (not '--') doesn't match a long option,
+   but does match a short option, it is parsed as a short option
+   instead.  */
+
+int getopt_long_only(int argc,
+                     char** argv,
+                     const char* options,
+                     const struct option* long_options,
+                     int* opt_index)
+{
+    return _getopt_internal(argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif                          /* Not ELIDE_CODE.  */
+\f
+#ifdef TEST
+
+#include <stdio.h>
+
+int main(
+    argc,
+    argv)
+    int argc;
+    char    **argv;
+{
+    int       c;
+    int       digit_optind = 0;
+    struct option long_options[] = {
+        {"add", 1, 0, 0},
+        {"append", 0, 0, 0},
+        {"delete", 1, 0, 0},
+        {"verbose", 0, 0, 0},
+        {"create", 0, 0, 0},
+        {"file", 1, 0, 0},
+        {0, 0, 0, 0}
+    };
+
+    while (1) {
+        int       this_option_optind = optind ? optind : 1;
+        int       option_index = 0;
+
+        c = getopt_long(argc, argv, "abc:d:0123456789",
+                        long_options, &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 0:
+            printf("option %s", long_options[option_index].name);
+            if (optarg)
+                printf(" with arg %s", optarg);
+            printf("\n");
+            break;
+
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            if (digit_optind != 0 && digit_optind != this_option_optind)
+                printf("digits occur in two different argv-elements.\n");
+            digit_optind = this_option_optind;
+            printf("option %c\n", c);
+            break;
+
+        case 'a':
+            printf("option a\n");
+            break;
+
+        case 'b':
+            printf("option b\n");
+            break;
+
+        case 'c':
+            printf("option c with value `%s'\n", optarg);
+            break;
+
+        case 'd':
+            printf("option d with value `%s'\n", optarg);
+            break;
+
+        case '?':
+            break;
+
+        default:
+            printf("?? getopt returned character code 0%o ??\n", c);
+        }
+    }
+
+    if (optind < argc) {
+        printf("non-option ARGV-elements: ");
+        while (optind < argc)
+            printf("%s ", argv[optind++]);
+        printf("\n");
+    }
+
+    exit(0);
+}
+
+#endif                          /* TEST */
diff --git a/program/src/rrd_gfx.c b/program/src/rrd_gfx.c
new file mode 100644 (file)
index 0000000..aff036c
--- /dev/null
@@ -0,0 +1,318 @@
+/****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ ****************************************************************************
+ * rrd_gfx.c  graphics wrapper for rrdtool
+  **************************************************************************/
+
+/* #define DEBUG */
+
+/* stupid MSVC doesnt support variadic macros = no debug for now! */
+#ifdef _MSC_VER
+# define RRDPRINTF()
+#else
+# ifdef DEBUG
+#  define RRDPRINTF(...)  fprintf(stderr, __VA_ARGS__);
+# else
+#  define RRDPRINTF(...)
+# endif                         /* DEBUG */
+#endif                          /* _MSC_VER */
+
+#include "rrd_tool.h"
+#include "rrd_graph.h"
+
+
+/* create a new line */
+void gfx_line(
+    image_desc_t *im,
+    double X0,
+    double Y0,
+    double X1,
+    double Y1,
+    double width,
+    gfx_color_t color)
+{
+    gfx_dashed_line(im, X0, Y0, X1, Y1, width, color, 0, 0);
+}
+
+void gfx_dashed_line(
+    image_desc_t *im,
+    double X0,
+    double Y0,
+    double X1,
+    double Y1,
+    double width,
+    gfx_color_t color,
+    double dash_on,
+    double dash_off)
+{
+    cairo_t  *cr = im->cr;
+    double    dashes[2];
+    double    x = 0;
+    double    y = 0;
+
+    dashes[0] = dash_on;
+    dashes[1] = dash_off;
+
+    cairo_save(cr);
+    cairo_new_path(cr);
+    cairo_set_line_width(cr, width);
+    gfx_line_fit(im, &x, &y);
+    gfx_line_fit(im, &X0, &Y0);
+    cairo_move_to(cr, X0, Y0);
+    gfx_line_fit(im, &X1, &Y1);
+    cairo_line_to(cr, X1, Y1);
+    if (dash_on > 0 || dash_off > 0)
+        cairo_set_dash(cr, dashes, 2, x);
+    cairo_set_source_rgba(cr, color.red, color.green, color.blue,
+                          color.alpha);
+    cairo_stroke(cr);
+    cairo_restore(cr);
+}
+
+/* create a new area */
+void gfx_new_area(
+    image_desc_t *im,
+    double X0,
+    double Y0,
+    double X1,
+    double Y1,
+    double X2,
+    double Y2,
+    gfx_color_t color)
+{
+    cairo_t  *cr = im->cr;
+
+    cairo_new_path(cr);
+    gfx_area_fit(im, &X0, &Y0);
+    cairo_move_to(cr, X0, Y0);
+    gfx_area_fit(im, &X1, &Y1);
+    cairo_line_to(cr, X1, Y1);
+    gfx_area_fit(im, &X2, &Y2);
+    cairo_line_to(cr, X2, Y2);
+    cairo_set_source_rgba(cr, color.red, color.green, color.blue,
+                          color.alpha);
+}
+
+/* add a point to a line or to an area */
+void gfx_add_point(
+    image_desc_t *im,
+    double x,
+    double y)
+{
+    cairo_t  *cr = im->cr;
+
+    gfx_area_fit(im, &x, &y);
+    cairo_line_to(cr, x, y);
+}
+
+void gfx_close_path(
+    image_desc_t *im)
+{
+    cairo_t  *cr = im->cr;
+
+    cairo_close_path(cr);
+    cairo_fill(cr);
+}
+
+/* create a text node */
+static PangoLayout *gfx_prep_text(
+    image_desc_t *im,
+    double x,
+    gfx_color_t color,
+    PangoFontDescription *font_desc,
+    double tabwidth,
+    const char *text)
+{
+    PangoLayout  *layout = im->layout;
+    const PangoFontDescription *pfd;
+    cairo_t  *cr = im->cr;
+
+    static double last_tabwidth = -1;
+
+
+
+    /* for performance reasons we might
+       want todo that only once ... tabs will always
+       be the same */
+    long      i;
+    long      tab_count = strlen(text);
+    long      tab_shift = fmod(x, tabwidth);
+    int       border = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
+    
+    gchar    *utf8_text;
+
+    if (last_tabwidth < 0 || last_tabwidth != tabwidth){
+        PangoTabArray *tab_array;
+        // fprintf(stderr,"t");
+        last_tabwidth = tabwidth;
+        tab_array = pango_tab_array_new(tab_count, (gboolean) (1));
+        for (i = 1; i <= tab_count; i++) {
+             pango_tab_array_set_tab(tab_array,
+                                     i, PANGO_TAB_LEFT,
+                                     tabwidth * i - tab_shift + border);
+        }
+        pango_layout_set_tabs(layout, tab_array);
+        pango_tab_array_free(tab_array);
+    }
+   pfd = pango_layout_get_font_description(layout);
+
+   if (!pfd || !pango_font_description_equal (pfd,font_desc)){
+        pango_layout_set_font_description(layout, font_desc);
+  }
+//   fprintf(stderr,"%s\n",pango_font_description_to_string(pango_layout_get_font_description(layout))); 
+
+   cairo_new_path(cr);
+   cairo_set_source_rgba(cr, color.red, color.green, color.blue,
+                          color.alpha);
+/*     layout = pango_cairo_create_layout(cr); */
+
+//    pango_cairo_context_set_font_options(pango_context, im->font_options);
+//    pango_cairo_context_set_resolution(pango_context, 100);
+
+/*     pango_cairo_update_context(cr, pango_context); */
+
+
+    /* pango expects the string to be utf-8 encoded */
+    utf8_text = g_locale_to_utf8((const gchar *) text, -1, NULL, NULL, NULL);
+
+    /* In case of an error, i.e. utf8_text == NULL (locale settings messed
+     * up?), we fall back to a possible "invalid UTF-8 string" warning instead
+     * of provoking a failed assertion in libpango. */
+    if (im->with_markup)
+        pango_layout_set_markup(layout, utf8_text ? utf8_text : text, -1);
+    else
+        pango_layout_set_text(layout, utf8_text ? utf8_text : text, -1);
+
+    g_free(utf8_text);
+    return layout;
+}
+
+/* Size Text Node */
+double gfx_get_text_width(
+    image_desc_t *im,
+    double start,
+    PangoFontDescription *font_desc,
+    double tabwidth,
+    char *text)
+{
+    PangoLayout *layout;
+    PangoRectangle log_rect;
+    gfx_color_t color = { 0, 0, 0, 0 };
+    layout = gfx_prep_text(im, start, color, font_desc, tabwidth, text);
+    pango_layout_get_pixel_extents(layout, NULL, &log_rect);
+/*    g_object_unref(layout); */
+    return log_rect.width;
+}
+
+void gfx_text(
+    image_desc_t *im,
+    double x,
+    double y,
+    gfx_color_t color,
+    PangoFontDescription *font_desc,
+    double tabwidth,
+    double angle,
+    enum gfx_h_align_en h_align,
+    enum gfx_v_align_en v_align,
+    const char *text)
+{
+    PangoLayout *layout;
+    PangoRectangle log_rect;
+    cairo_t  *cr = im->cr;
+    double    sx = 0;
+    double    sy = 0;
+
+    cairo_save(cr);
+    cairo_translate(cr, x, y);
+/*    gfx_line(cr,-2,0,2,0,1,color);
+    gfx_line(cr,0,-2,0,2,1,color); */
+    layout = gfx_prep_text(im, x, color, font_desc, tabwidth, text);
+    pango_layout_get_pixel_extents(layout, NULL, &log_rect);
+    cairo_rotate(cr, -angle * G_PI / 180.0);
+    sx = log_rect.x;
+    switch (h_align) {
+    case GFX_H_RIGHT:
+        sx -= log_rect.width;
+        break;
+    case GFX_H_CENTER:
+        sx -= log_rect.width / 2;
+        break;
+    case GFX_H_LEFT:
+        break;
+    case GFX_H_NULL:
+        break;
+    }
+    sy = log_rect.y;
+    switch (v_align) {
+    case GFX_V_TOP:
+        break;
+    case GFX_V_CENTER:
+        sy -= log_rect.height / 2;
+        break;
+    case GFX_V_BOTTOM:
+        sy -= log_rect.height;
+        break;
+    case GFX_V_NULL:
+        break;
+    }
+    pango_cairo_update_layout(cr, layout);
+    cairo_move_to(cr, sx, sy);
+    pango_cairo_show_layout(cr, layout);
+/*    g_object_unref(layout); */
+    cairo_restore(cr);
+
+}
+
+/* convert color */
+struct gfx_color_t gfx_hex_to_col(
+    long unsigned int color)
+{
+    struct gfx_color_t gfx_color;
+
+    gfx_color.red = 1.0 / 255.0 * ((color & 0xff000000) >> (3 * 8));
+    gfx_color.green = 1.0 / 255.0 * ((color & 0x00ff0000) >> (2 * 8));
+    gfx_color.blue = 1.0 / 255.0 * ((color & 0x0000ff00) >> (1 * 8));
+    gfx_color.alpha = 1.0 / 255.0 * (color & 0x000000ff);
+    return gfx_color;
+}
+
+/* gridfit_lines */
+
+void gfx_line_fit(
+    image_desc_t *im,
+    double *x,
+    double *y)
+{
+    cairo_t  *cr = im->cr;
+    double    line_width;
+    double    line_height;
+
+    if (!im->gridfit)
+        return;
+    cairo_user_to_device(cr, x, y);
+    line_width = cairo_get_line_width(cr);
+    line_height = line_width;
+    cairo_user_to_device_distance(cr, &line_width, &line_height);
+    line_width = line_width / 2.0 - (long) (line_width / 2.0);
+    line_height = line_height / 2.0 - (long) (line_height / 2.0);
+    *x = (double) ((long) (*x + 0.5)) - line_width;
+    *y = (double) ((long) (*y + 0.5)) + line_height;
+    cairo_device_to_user(cr, x, y);
+}
+
+/* gridfit_areas */
+
+void gfx_area_fit(
+    image_desc_t *im,
+    double *x,
+    double *y)
+{
+    cairo_t  *cr = im->cr;
+
+    if (!im->gridfit)
+        return;
+    cairo_user_to_device(cr, x, y);
+    *x = (double) ((long) (*x + 0.5));
+    *y = (double) ((long) (*y + 0.5));
+    cairo_device_to_user(cr, x, y);
+}
diff --git a/program/src/rrd_graph.c b/program/src/rrd_graph.c
new file mode 100644 (file)
index 0000000..eeff31c
--- /dev/null
@@ -0,0 +1,5151 @@
+/****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ ****************************************************************************
+ * rrd__graph.c  produce graphs from data in rrdfiles
+ ****************************************************************************/
+
+
+#include <sys/stat.h>
+
+#ifdef WIN32
+#include "strftime.h"
+#endif
+
+#include "rrd_tool.h"
+
+/* for basename */
+#ifdef HAVE_LIBGEN_H
+#  include <libgen.h>
+#else
+#include "plbasename.h"
+#endif
+
+#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#include <io.h>
+#include <fcntl.h>
+#endif
+
+#include <time.h>
+
+#include <locale.h>
+
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+
+#include "rrd_graph.h"
+#include "rrd_client.h"
+
+/* some constant definitions */
+
+
+
+#ifndef RRD_DEFAULT_FONT
+/* there is special code later to pick Cour.ttf when running on windows */
+#define RRD_DEFAULT_FONT "DejaVu Sans Mono,Bitstream Vera Sans Mono,monospace,Courier"
+#endif
+
+text_prop_t text_prop[] = {
+    {8.0, RRD_DEFAULT_FONT,NULL}
+    ,                   /* default */
+    {9.0, RRD_DEFAULT_FONT,NULL}
+    ,                   /* title */
+    {7.0, RRD_DEFAULT_FONT,NULL}
+    ,                   /* axis */
+    {8.0, RRD_DEFAULT_FONT,NULL}
+    ,                   /* unit */
+    {8.0, RRD_DEFAULT_FONT,NULL} /* legend */
+    ,
+    {5.5, RRD_DEFAULT_FONT,NULL} /* watermark */
+};
+
+xlab_t    xlab[] = {
+    {0, 0, TMT_SECOND, 30, TMT_MINUTE, 5, TMT_MINUTE, 5, 0, "%H:%M"}
+    ,
+    {2, 0, TMT_MINUTE, 1, TMT_MINUTE, 5, TMT_MINUTE, 5, 0, "%H:%M"}
+    ,
+    {5, 0, TMT_MINUTE, 2, TMT_MINUTE, 10, TMT_MINUTE, 10, 0, "%H:%M"}
+    ,
+    {10, 0, TMT_MINUTE, 5, TMT_MINUTE, 20, TMT_MINUTE, 20, 0, "%H:%M"}
+    ,
+    {30, 0, TMT_MINUTE, 10, TMT_HOUR, 1, TMT_HOUR, 1, 0, "%H:%M"}
+    ,
+    {60, 0, TMT_MINUTE, 30, TMT_HOUR, 2, TMT_HOUR, 2, 0, "%H:%M"}
+    ,
+    {60, 24 * 3600, TMT_MINUTE, 30, TMT_HOUR, 2, TMT_HOUR, 6, 0, "%a %H:%M"}
+    ,
+    {180, 0, TMT_HOUR, 1, TMT_HOUR, 6, TMT_HOUR, 6, 0, "%H:%M"}
+    ,
+    {180, 24 * 3600, TMT_HOUR, 1, TMT_HOUR, 6, TMT_HOUR, 12, 0, "%a %H:%M"}
+    ,
+    /*{300,             0,   TMT_HOUR,3,    TMT_HOUR,12,   TMT_HOUR,12,    12*3600,"%a %p"},  this looks silly */
+    {600, 0, TMT_HOUR, 6, TMT_DAY, 1, TMT_DAY, 1, 24 * 3600, "%a"}
+    ,
+    {1200, 0, TMT_HOUR, 6, TMT_DAY, 1, TMT_DAY, 1, 24 * 3600, "%d"}
+    ,
+    {1800, 0, TMT_HOUR, 12, TMT_DAY, 1, TMT_DAY, 2, 24 * 3600, "%a %d"}
+    ,
+    {2400, 0, TMT_HOUR, 12, TMT_DAY, 1, TMT_DAY, 2, 24 * 3600, "%a"}
+    ,
+    {3600, 0, TMT_DAY, 1, TMT_WEEK, 1, TMT_WEEK, 1, 7 * 24 * 3600, "Week %V"}
+    ,
+    {3 * 3600, 0, TMT_WEEK, 1, TMT_MONTH, 1, TMT_WEEK, 2, 7 * 24 * 3600,
+     "Week %V"}
+    ,
+    {6 * 3600, 0, TMT_MONTH, 1, TMT_MONTH, 1, TMT_MONTH, 1, 30 * 24 * 3600,
+     "%b"}
+    ,
+    {48 * 3600, 0, TMT_MONTH, 1, TMT_MONTH, 3, TMT_MONTH, 3, 30 * 24 * 3600,
+     "%b"}
+    ,
+    {315360, 0, TMT_MONTH, 3, TMT_YEAR, 1, TMT_YEAR, 1, 365 * 24 * 3600, "%Y"}
+    ,
+    {10 * 24 * 3600, 0, TMT_YEAR, 1, TMT_YEAR, 1, TMT_YEAR, 1,
+     365 * 24 * 3600, "%y"}
+    ,
+    {-1, 0, TMT_MONTH, 0, TMT_MONTH, 0, TMT_MONTH, 0, 0, ""}
+};
+
+/* sensible y label intervals ...*/
+
+ylab_t    ylab[] = {
+    {0.1, {1, 2, 5, 10}
+     }
+    ,
+    {0.2, {1, 5, 10, 20}
+     }
+    ,
+    {0.5, {1, 2, 4, 10}
+     }
+    ,
+    {1.0, {1, 2, 5, 10}
+     }
+    ,
+    {2.0, {1, 5, 10, 20}
+     }
+    ,
+    {5.0, {1, 2, 4, 10}
+     }
+    ,
+    {10.0, {1, 2, 5, 10}
+     }
+    ,
+    {20.0, {1, 5, 10, 20}
+     }
+    ,
+    {50.0, {1, 2, 4, 10}
+     }
+    ,
+    {100.0, {1, 2, 5, 10}
+     }
+    ,
+    {200.0, {1, 5, 10, 20}
+     }
+    ,
+    {500.0, {1, 2, 4, 10}
+     }
+    ,
+    {0.0, {0, 0, 0, 0}
+     }
+};
+
+
+gfx_color_t graph_col[] =   /* default colors */
+{
+    {1.00, 1.00, 1.00, 1.00},   /* canvas     */
+    {0.95, 0.95, 0.95, 1.00},   /* background */
+    {0.81, 0.81, 0.81, 1.00},   /* shade A    */
+    {0.62, 0.62, 0.62, 1.00},   /* shade B    */
+    {0.56, 0.56, 0.56, 0.75},   /* grid       */
+    {0.87, 0.31, 0.31, 0.60},   /* major grid */
+    {0.00, 0.00, 0.00, 1.00},   /* font       */
+    {0.50, 0.12, 0.12, 1.00},   /* arrow      */
+    {0.12, 0.12, 0.12, 1.00},   /* axis       */
+    {0.00, 0.00, 0.00, 1.00}    /* frame      */
+};
+
+
+/* #define DEBUG */
+
+#ifdef DEBUG
+# define DPRINT(x)    (void)(printf x, printf("\n"))
+#else
+# define DPRINT(x)
+#endif
+
+
+/* initialize with xtr(im,0); */
+int xtr(
+    image_desc_t *im,
+    time_t mytime)
+{
+    static double pixie;
+
+    if (mytime == 0) {
+        pixie = (double) im->xsize / (double) (im->end - im->start);
+        return im->xorigin;
+    }
+    return (int) ((double) im->xorigin + pixie * (mytime - im->start));
+}
+
+/* translate data values into y coordinates */
+double ytr(
+    image_desc_t *im,
+    double value)
+{
+    static double pixie;
+    double    yval;
+
+    if (isnan(value)) {
+        if (!im->logarithmic)
+            pixie = (double) im->ysize / (im->maxval - im->minval);
+        else
+            pixie =
+                (double) im->ysize / (log10(im->maxval) - log10(im->minval));
+        yval = im->yorigin;
+    } else if (!im->logarithmic) {
+        yval = im->yorigin - pixie * (value - im->minval);
+    } else {
+        if (value < im->minval) {
+            yval = im->yorigin;
+        } else {
+            yval = im->yorigin - pixie * (log10(value) - log10(im->minval));
+        }
+    }
+    return yval;
+}
+
+
+
+/* conversion function for symbolic entry names */
+
+
+#define conv_if(VV,VVV) \
+   if (strcmp(#VV, string) == 0) return VVV ;
+
+enum gf_en gf_conv(
+    char *string)
+{
+
+    conv_if(PRINT, GF_PRINT);
+    conv_if(GPRINT, GF_GPRINT);
+    conv_if(COMMENT, GF_COMMENT);
+    conv_if(HRULE, GF_HRULE);
+    conv_if(VRULE, GF_VRULE);
+    conv_if(LINE, GF_LINE);
+    conv_if(AREA, GF_AREA);
+    conv_if(STACK, GF_STACK);
+    conv_if(TICK, GF_TICK);
+    conv_if(TEXTALIGN, GF_TEXTALIGN);
+    conv_if(DEF, GF_DEF);
+    conv_if(CDEF, GF_CDEF);
+    conv_if(VDEF, GF_VDEF);
+    conv_if(XPORT, GF_XPORT);
+    conv_if(SHIFT, GF_SHIFT);
+
+    return (enum gf_en)(-1);
+}
+
+enum gfx_if_en if_conv(
+    char *string)
+{
+
+    conv_if(PNG, IF_PNG);
+    conv_if(SVG, IF_SVG);
+    conv_if(EPS, IF_EPS);
+    conv_if(PDF, IF_PDF);
+
+    return (enum gfx_if_en)(-1);
+}
+
+enum tmt_en tmt_conv(
+    char *string)
+{
+
+    conv_if(SECOND, TMT_SECOND);
+    conv_if(MINUTE, TMT_MINUTE);
+    conv_if(HOUR, TMT_HOUR);
+    conv_if(DAY, TMT_DAY);
+    conv_if(WEEK, TMT_WEEK);
+    conv_if(MONTH, TMT_MONTH);
+    conv_if(YEAR, TMT_YEAR);
+    return (enum tmt_en)(-1);
+}
+
+enum grc_en grc_conv(
+    char *string)
+{
+
+    conv_if(BACK, GRC_BACK);
+    conv_if(CANVAS, GRC_CANVAS);
+    conv_if(SHADEA, GRC_SHADEA);
+    conv_if(SHADEB, GRC_SHADEB);
+    conv_if(GRID, GRC_GRID);
+    conv_if(MGRID, GRC_MGRID);
+    conv_if(FONT, GRC_FONT);
+    conv_if(ARROW, GRC_ARROW);
+    conv_if(AXIS, GRC_AXIS);
+    conv_if(FRAME, GRC_FRAME);
+
+    return (enum grc_en)(-1);
+}
+
+enum text_prop_en text_prop_conv(
+    char *string)
+{
+
+    conv_if(DEFAULT, TEXT_PROP_DEFAULT);
+    conv_if(TITLE, TEXT_PROP_TITLE);
+    conv_if(AXIS, TEXT_PROP_AXIS);
+    conv_if(UNIT, TEXT_PROP_UNIT);
+    conv_if(LEGEND, TEXT_PROP_LEGEND);
+    conv_if(WATERMARK, TEXT_PROP_WATERMARK);
+    return (enum text_prop_en)(-1);
+}
+
+
+#undef conv_if
+
+int im_free(
+    image_desc_t *im)
+{
+    unsigned long i, ii;
+    cairo_status_t status = (cairo_status_t) 0;
+
+    if (im == NULL)
+        return 0;
+
+    if (im->daemon_addr != NULL)
+      free(im->daemon_addr);
+
+    for (i = 0; i < (unsigned) im->gdes_c; i++) {
+        if (im->gdes[i].data_first) {
+            /* careful here, because a single pointer can occur several times */
+            free(im->gdes[i].data);
+            if (im->gdes[i].ds_namv) {
+                for (ii = 0; ii < im->gdes[i].ds_cnt; ii++)
+                    free(im->gdes[i].ds_namv[ii]);
+                free(im->gdes[i].ds_namv);
+            }
+        }
+        /* free allocated memory used for dashed lines */
+        if (im->gdes[i].p_dashes != NULL)
+            free(im->gdes[i].p_dashes);
+
+        free(im->gdes[i].p_data);
+        free(im->gdes[i].rpnp);
+    }
+    free(im->gdes);
+    if (im->font_options)
+        cairo_font_options_destroy(im->font_options);
+
+    if (im->cr) {
+        status = cairo_status(im->cr);
+        cairo_destroy(im->cr);
+    }
+    if (im->rendered_image) {
+        free(im->rendered_image);
+    }
+
+    if (im->layout) {
+        g_object_unref (im->layout);
+    }
+
+    if (im->surface)
+        cairo_surface_destroy(im->surface);
+
+    if (status)
+        fprintf(stderr, "OOPS: Cairo has issues it can't even die: %s\n",
+                cairo_status_to_string(status));
+
+    return 0;
+}
+
+/* find SI magnitude symbol for the given number*/
+void auto_scale(
+    image_desc_t *im,   /* image description */
+    double *value,
+    char **symb_ptr,
+    double *magfact)
+{
+
+    char     *symbol[] = { "a", /* 10e-18 Atto */
+        "f",            /* 10e-15 Femto */
+        "p",            /* 10e-12 Pico */
+        "n",            /* 10e-9  Nano */
+        "u",            /* 10e-6  Micro */
+        "m",            /* 10e-3  Milli */
+        " ",            /* Base */
+        "k",            /* 10e3   Kilo */
+        "M",            /* 10e6   Mega */
+        "G",            /* 10e9   Giga */
+        "T",            /* 10e12  Tera */
+        "P",            /* 10e15  Peta */
+        "E"
+    };                  /* 10e18  Exa */
+
+    int       symbcenter = 6;
+    int       sindex;
+
+    if (*value == 0.0 || isnan(*value)) {
+        sindex = 0;
+        *magfact = 1.0;
+    } else {
+        sindex = floor(log(fabs(*value)) / log((double) im->base));
+        *magfact = pow((double) im->base, (double) sindex);
+        (*value) /= (*magfact);
+    }
+    if (sindex <= symbcenter && sindex >= -symbcenter) {
+        (*symb_ptr) = symbol[sindex + symbcenter];
+    } else {
+        (*symb_ptr) = "?";
+    }
+}
+
+
+static char si_symbol[] = {
+    'a',                /* 10e-18 Atto */
+    'f',                /* 10e-15 Femto */
+    'p',                /* 10e-12 Pico */
+    'n',                /* 10e-9  Nano */
+    'u',                /* 10e-6  Micro */
+    'm',                /* 10e-3  Milli */
+    ' ',                /* Base */
+    'k',                /* 10e3   Kilo */
+    'M',                /* 10e6   Mega */
+    'G',                /* 10e9   Giga */
+    'T',                /* 10e12  Tera */
+    'P',                /* 10e15  Peta */
+    'E',                /* 10e18  Exa */
+};
+static const int si_symbcenter = 6;
+
+/* find SI magnitude symbol for the numbers on the y-axis*/
+void si_unit(
+    image_desc_t *im    /* image description */
+    )
+{
+
+    double    digits, viewdigits = 0;
+
+    digits =
+        floor(log(max(fabs(im->minval), fabs(im->maxval))) /
+              log((double) im->base));
+
+    if (im->unitsexponent != 9999) {
+        /* unitsexponent = 9, 6, 3, 0, -3, -6, -9, etc */
+        viewdigits = floor((double)(im->unitsexponent / 3));
+    } else {
+        viewdigits = digits;
+    }
+
+    im->magfact = pow((double) im->base, digits);
+
+#ifdef DEBUG
+    printf("digits %6.3f  im->magfact %6.3f\n", digits, im->magfact);
+#endif
+
+    im->viewfactor = im->magfact / pow((double) im->base, viewdigits);
+
+    if (((viewdigits + si_symbcenter) < sizeof(si_symbol)) &&
+        ((viewdigits + si_symbcenter) >= 0))
+        im->symbol = si_symbol[(int) viewdigits + si_symbcenter];
+    else
+        im->symbol = '?';
+}
+
+/*  move min and max values around to become sensible */
+
+void expand_range(
+    image_desc_t *im)
+{
+    double    sensiblevalues[] = { 1000.0, 900.0, 800.0, 750.0, 700.0,
+        600.0, 500.0, 400.0, 300.0, 250.0,
+        200.0, 125.0, 100.0, 90.0, 80.0,
+        75.0, 70.0, 60.0, 50.0, 40.0, 30.0,
+        25.0, 20.0, 10.0, 9.0, 8.0,
+        7.0, 6.0, 5.0, 4.0, 3.5, 3.0,
+        2.5, 2.0, 1.8, 1.5, 1.2, 1.0,
+        0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0, -1
+    };
+
+    double    scaled_min, scaled_max;
+    double    adj;
+    int       i;
+
+
+
+#ifdef DEBUG
+    printf("Min: %6.2f Max: %6.2f MagFactor: %6.2f\n",
+           im->minval, im->maxval, im->magfact);
+#endif
+
+    if (isnan(im->ygridstep)) {
+        if (im->extra_flags & ALTAUTOSCALE) {
+            /* measure the amplitude of the function. Make sure that
+               graph boundaries are slightly higher then max/min vals
+               so we can see amplitude on the graph */
+            double    delt, fact;
+
+            delt = im->maxval - im->minval;
+            adj = delt * 0.1;
+            fact = 2.0 * pow(10.0,
+                             floor(log10
+                                   (max(fabs(im->minval), fabs(im->maxval)) /
+                                    im->magfact)) - 2);
+            if (delt < fact) {
+                adj = (fact - delt) * 0.55;
+#ifdef DEBUG
+                printf
+                    ("Min: %6.2f Max: %6.2f delt: %6.2f fact: %6.2f adj: %6.2f\n",
+                     im->minval, im->maxval, delt, fact, adj);
+#endif
+            }
+            im->minval -= adj;
+            im->maxval += adj;
+        } else if (im->extra_flags & ALTAUTOSCALE_MIN) {
+            /* measure the amplitude of the function. Make sure that
+               graph boundaries are slightly lower than min vals
+               so we can see amplitude on the graph */
+            adj = (im->maxval - im->minval) * 0.1;
+            im->minval -= adj;
+        } else if (im->extra_flags & ALTAUTOSCALE_MAX) {
+            /* measure the amplitude of the function. Make sure that
+               graph boundaries are slightly higher than max vals
+               so we can see amplitude on the graph */
+            adj = (im->maxval - im->minval) * 0.1;
+            im->maxval += adj;
+        } else {
+            scaled_min = im->minval / im->magfact;
+            scaled_max = im->maxval / im->magfact;
+
+            for (i = 1; sensiblevalues[i] > 0; i++) {
+                if (sensiblevalues[i - 1] >= scaled_min &&
+                    sensiblevalues[i] <= scaled_min)
+                    im->minval = sensiblevalues[i] * (im->magfact);
+
+                if (-sensiblevalues[i - 1] <= scaled_min &&
+                    -sensiblevalues[i] >= scaled_min)
+                    im->minval = -sensiblevalues[i - 1] * (im->magfact);
+
+                if (sensiblevalues[i - 1] >= scaled_max &&
+                    sensiblevalues[i] <= scaled_max)
+                    im->maxval = sensiblevalues[i - 1] * (im->magfact);
+
+                if (-sensiblevalues[i - 1] <= scaled_max &&
+                    -sensiblevalues[i] >= scaled_max)
+                    im->maxval = -sensiblevalues[i] * (im->magfact);
+            }
+        }
+    } else {
+        /* adjust min and max to the grid definition if there is one */
+        im->minval = (double) im->ylabfact * im->ygridstep *
+            floor(im->minval / ((double) im->ylabfact * im->ygridstep));
+        im->maxval = (double) im->ylabfact * im->ygridstep *
+            ceil(im->maxval / ((double) im->ylabfact * im->ygridstep));
+    }
+
+#ifdef DEBUG
+    fprintf(stderr, "SCALED Min: %6.2f Max: %6.2f Factor: %6.2f\n",
+            im->minval, im->maxval, im->magfact);
+#endif
+}
+
+
+void apply_gridfit(
+    image_desc_t *im)
+{
+    if (isnan(im->minval) || isnan(im->maxval))
+        return;
+    ytr(im, DNAN);
+    if (im->logarithmic) {
+        double    ya, yb, ypix, ypixfrac;
+        double    log10_range = log10(im->maxval) - log10(im->minval);
+
+        ya = pow((double) 10, floor(log10(im->minval)));
+        while (ya < im->minval)
+            ya *= 10;
+        if (ya > im->maxval)
+            return;     /* don't have y=10^x gridline */
+        yb = ya * 10;
+        if (yb <= im->maxval) {
+            /* we have at least 2 y=10^x gridlines.
+               Make sure distance between them in pixels
+               are an integer by expanding im->maxval */
+            double    y_pixel_delta = ytr(im, ya) - ytr(im, yb);
+            double    factor = y_pixel_delta / floor(y_pixel_delta);
+            double    new_log10_range = factor * log10_range;
+            double    new_ymax_log10 = log10(im->minval) + new_log10_range;
+
+            im->maxval = pow(10, new_ymax_log10);
+            ytr(im, DNAN);  /* reset precalc */
+            log10_range = log10(im->maxval) - log10(im->minval);
+        }
+        /* make sure first y=10^x gridline is located on
+           integer pixel position by moving scale slightly
+           downwards (sub-pixel movement) */
+        ypix = ytr(im, ya) + im->ysize; /* add im->ysize so it always is positive */
+        ypixfrac = ypix - floor(ypix);
+        if (ypixfrac > 0 && ypixfrac < 1) {
+            double    yfrac = ypixfrac / im->ysize;
+
+            im->minval = pow(10, log10(im->minval) - yfrac * log10_range);
+            im->maxval = pow(10, log10(im->maxval) - yfrac * log10_range);
+            ytr(im, DNAN);  /* reset precalc */
+        }
+    } else {
+        /* Make sure we have an integer pixel distance between
+           each minor gridline */
+        double    ypos1 = ytr(im, im->minval);
+        double    ypos2 = ytr(im, im->minval + im->ygrid_scale.gridstep);
+        double    y_pixel_delta = ypos1 - ypos2;
+        double    factor = y_pixel_delta / floor(y_pixel_delta);
+        double    new_range = factor * (im->maxval - im->minval);
+        double    gridstep = im->ygrid_scale.gridstep;
+        double    minor_y, minor_y_px, minor_y_px_frac;
+
+        if (im->maxval > 0.0)
+            im->maxval = im->minval + new_range;
+        else
+            im->minval = im->maxval - new_range;
+        ytr(im, DNAN);  /* reset precalc */
+        /* make sure first minor gridline is on integer pixel y coord */
+        minor_y = gridstep * floor(im->minval / gridstep);
+        while (minor_y < im->minval)
+            minor_y += gridstep;
+        minor_y_px = ytr(im, minor_y) + im->ysize;  /* ensure > 0 by adding ysize */
+        minor_y_px_frac = minor_y_px - floor(minor_y_px);
+        if (minor_y_px_frac > 0 && minor_y_px_frac < 1) {
+            double    yfrac = minor_y_px_frac / im->ysize;
+            double    range = im->maxval - im->minval;
+
+            im->minval = im->minval - yfrac * range;
+            im->maxval = im->maxval - yfrac * range;
+            ytr(im, DNAN);  /* reset precalc */
+        }
+        calc_horizontal_grid(im);   /* recalc with changed im->maxval */
+    }
+}
+
+/* reduce data reimplementation by Alex */
+
+void reduce_data(
+    enum cf_en cf,      /* which consolidation function ? */
+    unsigned long cur_step, /* step the data currently is in */
+    time_t *start,      /* start, end and step as requested ... */
+    time_t *end,        /* ... by the application will be   ... */
+    unsigned long *step,    /* ... adjusted to represent reality    */
+    unsigned long *ds_cnt,  /* number of data sources in file */
+    rrd_value_t **data)
+{                       /* two dimensional array containing the data */
+    int       i, reduce_factor = ceil((double) (*step) / (double) cur_step);
+    unsigned long col, dst_row, row_cnt, start_offset, end_offset, skiprows =
+        0;
+    rrd_value_t *srcptr, *dstptr;
+
+    (*step) = cur_step * reduce_factor; /* set new step size for reduced data */
+    dstptr = *data;
+    srcptr = *data;
+    row_cnt = ((*end) - (*start)) / cur_step;
+
+#ifdef DEBUG
+#define DEBUG_REDUCE
+#endif
+#ifdef DEBUG_REDUCE
+    printf("Reducing %lu rows with factor %i time %lu to %lu, step %lu\n",
+           row_cnt, reduce_factor, *start, *end, cur_step);
+    for (col = 0; col < row_cnt; col++) {
+        printf("time %10lu: ", *start + (col + 1) * cur_step);
+        for (i = 0; i < *ds_cnt; i++)
+            printf(" %8.2e", srcptr[*ds_cnt * col + i]);
+        printf("\n");
+    }
+#endif
+
+    /* We have to combine [reduce_factor] rows of the source
+     ** into one row for the destination.  Doing this we also
+     ** need to take care to combine the correct rows.  First
+     ** alter the start and end time so that they are multiples
+     ** of the new step time.  We cannot reduce the amount of
+     ** time so we have to move the end towards the future and
+     ** the start towards the past.
+     */
+    end_offset = (*end) % (*step);
+    start_offset = (*start) % (*step);
+
+    /* If there is a start offset (which cannot be more than
+     ** one destination row), skip the appropriate number of
+     ** source rows and one destination row.  The appropriate
+     ** number is what we do know (start_offset/cur_step) of
+     ** the new interval (*step/cur_step aka reduce_factor).
+     */
+#ifdef DEBUG_REDUCE
+    printf("start_offset: %lu  end_offset: %lu\n", start_offset, end_offset);
+    printf("row_cnt before:  %lu\n", row_cnt);
+#endif
+    if (start_offset) {
+        (*start) = (*start) - start_offset;
+        skiprows = reduce_factor - start_offset / cur_step;
+        srcptr += skiprows * *ds_cnt;
+        for (col = 0; col < (*ds_cnt); col++)
+            *dstptr++ = DNAN;
+        row_cnt -= skiprows;
+    }
+#ifdef DEBUG_REDUCE
+    printf("row_cnt between: %lu\n", row_cnt);
+#endif
+
+    /* At the end we have some rows that are not going to be
+     ** used, the amount is end_offset/cur_step
+     */
+    if (end_offset) {
+        (*end) = (*end) - end_offset + (*step);
+        skiprows = end_offset / cur_step;
+        row_cnt -= skiprows;
+    }
+#ifdef DEBUG_REDUCE
+    printf("row_cnt after:   %lu\n", row_cnt);
+#endif
+
+/* Sanity check: row_cnt should be multiple of reduce_factor */
+/* if this gets triggered, something is REALLY WRONG ... we die immediately */
+
+    if (row_cnt % reduce_factor) {
+        printf("SANITY CHECK: %lu rows cannot be reduced by %i \n",
+               row_cnt, reduce_factor);
+        printf("BUG in reduce_data()\n");
+        exit(1);
+    }
+
+    /* Now combine reduce_factor intervals at a time
+     ** into one interval for the destination.
+     */
+
+    for (dst_row = 0; (long int) row_cnt >= reduce_factor; dst_row++) {
+        for (col = 0; col < (*ds_cnt); col++) {
+            rrd_value_t newval = DNAN;
+            unsigned long validval = 0;
+
+            for (i = 0; i < reduce_factor; i++) {
+                if (isnan(srcptr[i * (*ds_cnt) + col])) {
+                    continue;
+                }
+                validval++;
+                if (isnan(newval))
+                    newval = srcptr[i * (*ds_cnt) + col];
+                else {
+                    switch (cf) {
+                    case CF_HWPREDICT:
+                    case CF_MHWPREDICT:
+                    case CF_DEVSEASONAL:
+                    case CF_DEVPREDICT:
+                    case CF_SEASONAL:
+                    case CF_AVERAGE:
+                        newval += srcptr[i * (*ds_cnt) + col];
+                        break;
+                    case CF_MINIMUM:
+                        newval = min(newval, srcptr[i * (*ds_cnt) + col]);
+                        break;
+                    case CF_FAILURES:
+                        /* an interval contains a failure if any subintervals contained a failure */
+                    case CF_MAXIMUM:
+                        newval = max(newval, srcptr[i * (*ds_cnt) + col]);
+                        break;
+                    case CF_LAST:
+                        newval = srcptr[i * (*ds_cnt) + col];
+                        break;
+                    }
+                }
+            }
+            if (validval == 0) {
+                newval = DNAN;
+            } else {
+                switch (cf) {
+                case CF_HWPREDICT:
+                case CF_MHWPREDICT:
+                case CF_DEVSEASONAL:
+                case CF_DEVPREDICT:
+                case CF_SEASONAL:
+                case CF_AVERAGE:
+                    newval /= validval;
+                    break;
+                case CF_MINIMUM:
+                case CF_FAILURES:
+                case CF_MAXIMUM:
+                case CF_LAST:
+                    break;
+                }
+            }
+            *dstptr++ = newval;
+        }
+        srcptr += (*ds_cnt) * reduce_factor;
+        row_cnt -= reduce_factor;
+    }
+    /* If we had to alter the endtime, we didn't have enough
+     ** source rows to fill the last row. Fill it with NaN.
+     */
+    if (end_offset)
+        for (col = 0; col < (*ds_cnt); col++)
+            *dstptr++ = DNAN;
+#ifdef DEBUG_REDUCE
+    row_cnt = ((*end) - (*start)) / *step;
+    srcptr = *data;
+    printf("Done reducing. Currently %lu rows, time %lu to %lu, step %lu\n",
+           row_cnt, *start, *end, *step);
+    for (col = 0; col < row_cnt; col++) {
+        printf("time %10lu: ", *start + (col + 1) * (*step));
+        for (i = 0; i < *ds_cnt; i++)
+            printf(" %8.2e", srcptr[*ds_cnt * col + i]);
+        printf("\n");
+    }
+#endif
+}
+
+
+/* get the data required for the graphs from the
+   relevant rrds ... */
+
+int data_fetch(
+    image_desc_t *im)
+{
+    int       i, ii;
+    int       skip;
+
+    /* pull the data from the rrd files ... */
+    for (i = 0; i < (int) im->gdes_c; i++) {
+        /* only GF_DEF elements fetch data */
+        if (im->gdes[i].gf != GF_DEF)
+            continue;
+
+        skip = 0;
+        /* do we have it already ? */
+        for (ii = 0; ii < i; ii++) {
+            if (im->gdes[ii].gf != GF_DEF)
+                continue;
+            if ((strcmp(im->gdes[i].rrd, im->gdes[ii].rrd) == 0)
+                && (im->gdes[i].cf == im->gdes[ii].cf)
+                && (im->gdes[i].cf_reduce == im->gdes[ii].cf_reduce)
+                && (im->gdes[i].start_orig == im->gdes[ii].start_orig)
+                && (im->gdes[i].end_orig == im->gdes[ii].end_orig)
+                && (im->gdes[i].step_orig == im->gdes[ii].step_orig)) {
+                /* OK, the data is already there.
+                 ** Just copy the header portion
+                 */
+                im->gdes[i].start = im->gdes[ii].start;
+                im->gdes[i].end = im->gdes[ii].end;
+                im->gdes[i].step = im->gdes[ii].step;
+                im->gdes[i].ds_cnt = im->gdes[ii].ds_cnt;
+                im->gdes[i].ds_namv = im->gdes[ii].ds_namv;
+                im->gdes[i].data = im->gdes[ii].data;
+                im->gdes[i].data_first = 0;
+                skip = 1;
+            }
+            if (skip)
+                break;
+        }
+        if (!skip) {
+            unsigned long ft_step = im->gdes[i].step;   /* ft_step will record what we got from fetch */
+
+            /* Flush the file if
+             * - a connection to the daemon has been established
+             * - this is the first occurrence of that RRD file
+             */
+            if (rrdc_is_connected(im->daemon_addr))
+            {
+                int status;
+
+                status = 0;
+                for (ii = 0; ii < i; ii++)
+                {
+                    if (strcmp (im->gdes[i].rrd, im->gdes[ii].rrd) == 0)
+                    {
+                        status = 1;
+                        break;
+                    }
+                }
+
+                if (status == 0)
+                {
+                    status = rrdc_flush (im->gdes[i].rrd);
+                    if (status != 0)
+                    {
+                        rrd_set_error ("rrdc_flush (%s) failed with status %i.",
+                                im->gdes[i].rrd, status);
+                        return (-1);
+                    }
+                }
+            } /* if (rrdc_is_connected()) */
+
+            if ((rrd_fetch_fn(im->gdes[i].rrd,
+                              im->gdes[i].cf,
+                              &im->gdes[i].start,
+                              &im->gdes[i].end,
+                              &ft_step,
+                              &im->gdes[i].ds_cnt,
+                              &im->gdes[i].ds_namv,
+                              &im->gdes[i].data)) == -1) {
+                return -1;
+            }
+            im->gdes[i].data_first = 1;
+
+            if (ft_step < im->gdes[i].step) {
+                reduce_data(im->gdes[i].cf_reduce,
+                            ft_step,
+                            &im->gdes[i].start,
+                            &im->gdes[i].end,
+                            &im->gdes[i].step,
+                            &im->gdes[i].ds_cnt, &im->gdes[i].data);
+            } else {
+                im->gdes[i].step = ft_step;
+            }
+        }
+
+        /* lets see if the required data source is really there */
+        for (ii = 0; ii < (int) im->gdes[i].ds_cnt; ii++) {
+            if (strcmp(im->gdes[i].ds_namv[ii], im->gdes[i].ds_nam) == 0) {
+                im->gdes[i].ds = ii;
+            }
+        }
+        if (im->gdes[i].ds == -1) {
+            rrd_set_error("No DS called '%s' in '%s'",
+                          im->gdes[i].ds_nam, im->gdes[i].rrd);
+            return -1;
+        }
+
+    }
+    return 0;
+}
+
+/* evaluate the expressions in the CDEF functions */
+
+/*************************************************************
+ * CDEF stuff
+ *************************************************************/
+
+long find_var_wrapper(
+    void *arg1,
+    char *key)
+{
+    return find_var((image_desc_t *) arg1, key);
+}
+
+/* find gdes containing var*/
+long find_var(
+    image_desc_t *im,
+    char *key)
+{
+    long      ii;
+
+    for (ii = 0; ii < im->gdes_c - 1; ii++) {
+        if ((im->gdes[ii].gf == GF_DEF
+             || im->gdes[ii].gf == GF_VDEF || im->gdes[ii].gf == GF_CDEF)
+            && (strcmp(im->gdes[ii].vname, key) == 0)) {
+            return ii;
+        }
+    }
+    return -1;
+}
+
+/* find the greatest common divisor for all the numbers
+   in the 0 terminated num array */
+long lcd(
+    long *num)
+{
+    long      rest;
+    int       i;
+
+    for (i = 0; num[i + 1] != 0; i++) {
+        do {
+            rest = num[i] % num[i + 1];
+            num[i] = num[i + 1];
+            num[i + 1] = rest;
+        } while (rest != 0);
+        num[i + 1] = num[i];
+    }
+/*    return i==0?num[i]:num[i-1]; */
+    return num[i];
+}
+
+/* run the rpn calculator on all the VDEF and CDEF arguments */
+int data_calc(
+    image_desc_t *im)
+{
+
+    int       gdi;
+    int       dataidx;
+    long     *steparray, rpi;
+    int       stepcnt;
+    time_t    now;
+    rpnstack_t rpnstack;
+
+    rpnstack_init(&rpnstack);
+
+    for (gdi = 0; gdi < im->gdes_c; gdi++) {
+        /* Look for GF_VDEF and GF_CDEF in the same loop,
+         * so CDEFs can use VDEFs and vice versa
+         */
+        switch (im->gdes[gdi].gf) {
+        case GF_XPORT:
+            break;
+        case GF_SHIFT:{
+            graph_desc_t *vdp = &im->gdes[im->gdes[gdi].vidx];
+
+            /* remove current shift */
+            vdp->start -= vdp->shift;
+            vdp->end -= vdp->shift;
+
+            /* vdef */
+            if (im->gdes[gdi].shidx >= 0)
+                vdp->shift = im->gdes[im->gdes[gdi].shidx].vf.val;
+            /* constant */
+            else
+                vdp->shift = im->gdes[gdi].shval;
+
+            /* normalize shift to multiple of consolidated step */
+            vdp->shift = (vdp->shift / (long) vdp->step) * (long) vdp->step;
+
+            /* apply shift */
+            vdp->start += vdp->shift;
+            vdp->end += vdp->shift;
+            break;
+        }
+        case GF_VDEF:
+            /* A VDEF has no DS.  This also signals other parts
+             * of rrdtool that this is a VDEF value, not a CDEF.
+             */
+            im->gdes[gdi].ds_cnt = 0;
+            if (vdef_calc(im, gdi)) {
+                rrd_set_error("Error processing VDEF '%s'",
+                              im->gdes[gdi].vname);
+                rpnstack_free(&rpnstack);
+                return -1;
+            }
+            break;
+        case GF_CDEF:
+            im->gdes[gdi].ds_cnt = 1;
+            im->gdes[gdi].ds = 0;
+            im->gdes[gdi].data_first = 1;
+            im->gdes[gdi].start = 0;
+            im->gdes[gdi].end = 0;
+            steparray = NULL;
+            stepcnt = 0;
+            dataidx = -1;
+
+            /* Find the variables in the expression.
+             * - VDEF variables are substituted by their values
+             *   and the opcode is changed into OP_NUMBER.
+             * - CDEF variables are analized for their step size,
+             *   the lowest common denominator of all the step
+             *   sizes of the data sources involved is calculated
+             *   and the resulting number is the step size for the
+             *   resulting data source.
+             */
+            for (rpi = 0; im->gdes[gdi].rpnp[rpi].op != OP_END; rpi++) {
+                if (im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE ||
+                    im->gdes[gdi].rpnp[rpi].op == OP_PREV_OTHER) {
+                    long      ptr = im->gdes[gdi].rpnp[rpi].ptr;
+
+                    if (im->gdes[ptr].ds_cnt == 0) {    /* this is a VDEF data source */
+#if 0
+                        printf
+                            ("DEBUG: inside CDEF '%s' processing VDEF '%s'\n",
+                             im->gdes[gdi].vname, im->gdes[ptr].vname);
+                        printf("DEBUG: value from vdef is %f\n",
+                               im->gdes[ptr].vf.val);
+#endif
+                        im->gdes[gdi].rpnp[rpi].val = im->gdes[ptr].vf.val;
+                        im->gdes[gdi].rpnp[rpi].op = OP_NUMBER;
+                    } else {    /* normal variables and PREF(variables) */
+
+                        /* add one entry to the array that keeps track of the step sizes of the
+                         * data sources going into the CDEF. */
+                        if ((steparray =
+                             (long*)rrd_realloc(steparray,
+                                         (++stepcnt +
+                                          1) * sizeof(*steparray))) == NULL) {
+                            rrd_set_error("realloc steparray");
+                            rpnstack_free(&rpnstack);
+                            return -1;
+                        };
+
+                        steparray[stepcnt - 1] = im->gdes[ptr].step;
+
+                        /* adjust start and end of cdef (gdi) so
+                         * that it runs from the latest start point
+                         * to the earliest endpoint of any of the
+                         * rras involved (ptr)
+                         */
+
+                        if (im->gdes[gdi].start < im->gdes[ptr].start)
+                            im->gdes[gdi].start = im->gdes[ptr].start;
+
+                        if (im->gdes[gdi].end == 0 ||
+                            im->gdes[gdi].end > im->gdes[ptr].end)
+                            im->gdes[gdi].end = im->gdes[ptr].end;
+
+                        /* store pointer to the first element of
+                         * the rra providing data for variable,
+                         * further save step size and data source
+                         * count of this rra
+                         */
+                        im->gdes[gdi].rpnp[rpi].data =
+                            im->gdes[ptr].data + im->gdes[ptr].ds;
+                        im->gdes[gdi].rpnp[rpi].step = im->gdes[ptr].step;
+                        im->gdes[gdi].rpnp[rpi].ds_cnt = im->gdes[ptr].ds_cnt;
+
+                        /* backoff the *.data ptr; this is done so
+                         * rpncalc() function doesn't have to treat
+                         * the first case differently
+                         */
+                    }   /* if ds_cnt != 0 */
+                }       /* if OP_VARIABLE */
+            }           /* loop through all rpi */
+
+            /* move the data pointers to the correct period */
+            for (rpi = 0; im->gdes[gdi].rpnp[rpi].op != OP_END; rpi++) {
+                if (im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE ||
+                    im->gdes[gdi].rpnp[rpi].op == OP_PREV_OTHER) {
+                    long      ptr = im->gdes[gdi].rpnp[rpi].ptr;
+                    long      diff =
+                        im->gdes[gdi].start - im->gdes[ptr].start;
+
+                    if (diff > 0)
+                        im->gdes[gdi].rpnp[rpi].data +=
+                            (diff / im->gdes[ptr].step) *
+                            im->gdes[ptr].ds_cnt;
+                }
+            }
+
+            if (steparray == NULL) {
+                rrd_set_error("rpn expressions without DEF"
+                              " or CDEF variables are not supported");
+                rpnstack_free(&rpnstack);
+                return -1;
+            }
+            steparray[stepcnt] = 0;
+            /* Now find the resulting step.  All steps in all
+             * used RRAs have to be visited
+             */
+            im->gdes[gdi].step = lcd(steparray);
+            free(steparray);
+            if ((im->gdes[gdi].data = (rrd_value_t*)malloc(((im->gdes[gdi].end -
+                                               im->gdes[gdi].start)
+                                              / im->gdes[gdi].step)
+                                             * sizeof(double))) == NULL) {
+                rrd_set_error("malloc im->gdes[gdi].data");
+                rpnstack_free(&rpnstack);
+                return -1;
+            }
+
+            /* Step through the new cdef results array and
+             * calculate the values
+             */
+            for (now = im->gdes[gdi].start + im->gdes[gdi].step;
+                 now <= im->gdes[gdi].end; now += im->gdes[gdi].step) {
+                rpnp_t   *rpnp = im->gdes[gdi].rpnp;
+
+                /* 3rd arg of rpn_calc is for OP_VARIABLE lookups;
+                 * in this case we are advancing by timesteps;
+                 * we use the fact that time_t is a synonym for long
+                 */
+                if (rpn_calc(rpnp, &rpnstack, (long) now,
+                             im->gdes[gdi].data, ++dataidx) == -1) {
+                    /* rpn_calc sets the error string */
+                    rpnstack_free(&rpnstack);
+                    return -1;
+                }
+            }           /* enumerate over time steps within a CDEF */
+            break;
+        default:
+            continue;
+        }
+    }                   /* enumerate over CDEFs */
+    rpnstack_free(&rpnstack);
+    return 0;
+}
+
+/* from http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm */
+/* yes we are loosing precision by doing tos with floats instead of doubles
+   but it seems more stable this way. */
+
+static int AlmostEqual2sComplement(
+    float A,
+    float B,
+    int maxUlps)
+{
+
+    int       aInt = *(int *) &A;
+    int       bInt = *(int *) &B;
+    int       intDiff;
+
+    /* Make sure maxUlps is non-negative and small enough that the
+       default NAN won't compare as equal to anything.  */
+
+    /* assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); */
+
+    /* Make aInt lexicographically ordered as a twos-complement int */
+
+    if (aInt < 0)
+        aInt = 0x80000000l - aInt;
+
+    /* Make bInt lexicographically ordered as a twos-complement int */
+
+    if (bInt < 0)
+        bInt = 0x80000000l - bInt;
+
+    intDiff = abs(aInt - bInt);
+
+    if (intDiff <= maxUlps)
+        return 1;
+
+    return 0;
+}
+
+/* massage data so, that we get one value for each x coordinate in the graph */
+int data_proc(
+    image_desc_t *im)
+{
+    long      i, ii;
+    double    pixstep = (double) (im->end - im->start)
+        / (double) im->xsize;   /* how much time
+                                   passes in one pixel */
+    double    paintval;
+    double    minval = DNAN, maxval = DNAN;
+
+    unsigned long gr_time;
+
+    /* memory for the processed data */
+    for (i = 0; i < im->gdes_c; i++) {
+        if ((im->gdes[i].gf == GF_LINE) ||
+            (im->gdes[i].gf == GF_AREA) || (im->gdes[i].gf == GF_TICK)) {
+            if ((im->gdes[i].p_data = (rrd_value_t*)malloc((im->xsize + 1)
+                                             * sizeof(rrd_value_t))) == NULL) {
+                rrd_set_error("malloc data_proc");
+                return -1;
+            }
+        }
+    }
+
+    for (i = 0; i < im->xsize; i++) {   /* for each pixel */
+        long      vidx;
+
+        gr_time = im->start + pixstep * i;  /* time of the current step */
+        paintval = 0.0;
+
+        for (ii = 0; ii < im->gdes_c; ii++) {
+            double    value;
+
+            switch (im->gdes[ii].gf) {
+            case GF_LINE:
+            case GF_AREA:
+            case GF_TICK:
+                if (!im->gdes[ii].stack)
+                    paintval = 0.0;
+                value = im->gdes[ii].yrule;
+                if (isnan(value) || (im->gdes[ii].gf == GF_TICK)) {
+                    /* The time of the data doesn't necessarily match
+                     ** the time of the graph. Beware.
+                     */
+                    vidx = im->gdes[ii].vidx;
+                    if (im->gdes[vidx].gf == GF_VDEF) {
+                        value = im->gdes[vidx].vf.val;
+                    } else
+                        if (((long int) gr_time >=
+                             (long int) im->gdes[vidx].start)
+                            && ((long int) gr_time <
+                                (long int) im->gdes[vidx].end)) {
+                        value = im->gdes[vidx].data[(unsigned long)
+                                                    floor((double)
+                                                          (gr_time -
+                                                           im->gdes[vidx].
+                                                           start)
+                                                          /
+                                                          im->gdes[vidx].step)
+                                                    * im->gdes[vidx].ds_cnt +
+                                                    im->gdes[vidx].ds];
+                    } else {
+                        value = DNAN;
+                    }
+                };
+
+                if (!isnan(value)) {
+                    paintval += value;
+                    im->gdes[ii].p_data[i] = paintval;
+                    /* GF_TICK: the data values are not
+                     ** relevant for min and max
+                     */
+                    if (finite(paintval) && im->gdes[ii].gf != GF_TICK) {
+                        if ((isnan(minval) || paintval < minval) &&
+                            !(im->logarithmic && paintval <= 0.0))
+                            minval = paintval;
+                        if (isnan(maxval) || paintval > maxval)
+                            maxval = paintval;
+                    }
+                } else {
+                    im->gdes[ii].p_data[i] = DNAN;
+                }
+                break;
+            case GF_STACK:
+                rrd_set_error
+                    ("STACK should already be turned into LINE or AREA here");
+                return -1;
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
+    /* if min or max have not been asigned a value this is because
+       there was no data in the graph ... this is not good ...
+       lets set these to dummy values then ... */
+
+    if (im->logarithmic) {
+        if (isnan(minval) || isnan(maxval) || maxval <= 0) {
+            minval = 0.0;   /* catching this right away below */
+            maxval = 5.1;
+        }
+        /* in logarithm mode, where minval is smaller or equal
+           to 0 make the beast just way smaller than maxval */
+        if (minval <= 0) {
+            minval = maxval / 10e8;
+        }
+    } else {
+        if (isnan(minval) || isnan(maxval)) {
+            minval = 0.0;
+            maxval = 1.0;
+        }
+    }
+
+    /* adjust min and max values given by the user */
+    /* for logscale we add something on top */
+    if (isnan(im->minval)
+        || ((!im->rigid) && im->minval > minval)
+        ) {
+        if (im->logarithmic)
+            im->minval = minval / 2.0;
+        else
+            im->minval = minval;
+    }
+    if (isnan(im->maxval)
+        || (!im->rigid && im->maxval < maxval)
+        ) {
+        if (im->logarithmic)
+            im->maxval = maxval * 2.0;
+        else
+            im->maxval = maxval;
+    }
+
+    /* make sure min is smaller than max */
+    if (im->minval > im->maxval) {
+        if (im->minval > 0)
+            im->minval = 0.99 * im->maxval;
+        else
+            im->minval = 1.01 * im->maxval;
+    }
+
+    /* make sure min and max are not equal */
+    if (AlmostEqual2sComplement(im->minval, im->maxval, 4)) {
+        if (im->maxval > 0)
+            im->maxval *= 1.01;
+        else
+            im->maxval *= 0.99;
+
+        /* make sure min and max are not both zero */
+        if (AlmostEqual2sComplement(im->maxval, 0, 4)) {
+            im->maxval = 1.0;
+        }
+    }
+    return 0;
+}
+
+static int find_first_weekday(void){
+    static int first_weekday = -1;
+    if (first_weekday == -1){
+#ifdef HAVE__NL_TIME_WEEK_1STDAY
+        /* according to http://sourceware.org/ml/libc-locales/2009-q1/msg00011.html */
+        long week_1stday_l = (long) nl_langinfo (_NL_TIME_WEEK_1STDAY);
+        if (week_1stday_l == 19971130) first_weekday = 0; /* Sun */
+        else if (week_1stday_l == 19971201) first_weekday = 1; /* Mon */
+        else first_weekday = 1; /* we go for a monday default */
+#else
+        first_weekday = 1;
+#endif
+    }
+    return first_weekday;
+}
+
+/* identify the point where the first gridline, label ... gets placed */
+
+time_t find_first_time(
+    time_t start,       /* what is the initial time */
+    enum tmt_en baseint,    /* what is the basic interval */
+    long basestep       /* how many if these do we jump a time */
+    )
+{
+    struct tm tm;
+
+    localtime_r(&start, &tm);
+
+    switch (baseint) {
+    case TMT_SECOND:
+        tm.       tm_sec -= tm.tm_sec % basestep;
+
+        break;
+    case TMT_MINUTE:
+        tm.       tm_sec = 0;
+        tm.       tm_min -= tm.tm_min % basestep;
+
+        break;
+    case TMT_HOUR:
+        tm.       tm_sec = 0;
+        tm.       tm_min = 0;
+        tm.       tm_hour -= tm.tm_hour % basestep;
+
+        break;
+    case TMT_DAY:
+        /* we do NOT look at the basestep for this ... */
+        tm.       tm_sec = 0;
+        tm.       tm_min = 0;
+        tm.       tm_hour = 0;
+
+        break;
+    case TMT_WEEK:
+        /* we do NOT look at the basestep for this ... */
+        tm.       tm_sec = 0;
+        tm.       tm_min = 0;
+        tm.       tm_hour = 0;
+        tm.       tm_mday -= tm.tm_wday - find_first_weekday();
+
+        if (tm.tm_wday == 0 && find_first_weekday() > 0)
+            tm.       tm_mday -= 7; /* we want the *previous* week */
+
+        break;
+    case TMT_MONTH:
+        tm.       tm_sec = 0;
+        tm.       tm_min = 0;
+        tm.       tm_hour = 0;
+        tm.       tm_mday = 1;
+        tm.       tm_mon -= tm.tm_mon % basestep;
+
+        break;
+
+    case TMT_YEAR:
+        tm.       tm_sec = 0;
+        tm.       tm_min = 0;
+        tm.       tm_hour = 0;
+        tm.       tm_mday = 1;
+        tm.       tm_mon = 0;
+        tm.       tm_year -= (
+    tm.tm_year + 1900) %basestep;
+
+    }
+    return mktime(&tm);
+}
+
+/* identify the point where the next gridline, label ... gets placed */
+time_t find_next_time(
+    time_t current,     /* what is the initial time */
+    enum tmt_en baseint,    /* what is the basic interval */
+    long basestep       /* how many if these do we jump a time */
+    )
+{
+    struct tm tm;
+    time_t    madetime;
+
+    localtime_r(&current, &tm);
+
+    do {
+        switch (baseint) {
+        case TMT_SECOND:
+            tm.       tm_sec += basestep;
+
+            break;
+        case TMT_MINUTE:
+            tm.       tm_min += basestep;
+
+            break;
+        case TMT_HOUR:
+            tm.       tm_hour += basestep;
+
+            break;
+        case TMT_DAY:
+            tm.       tm_mday += basestep;
+
+            break;
+        case TMT_WEEK:
+            tm.       tm_mday += 7 * basestep;
+
+            break;
+        case TMT_MONTH:
+            tm.       tm_mon += basestep;
+
+            break;
+        case TMT_YEAR:
+            tm.       tm_year += basestep;
+        }
+        madetime = mktime(&tm);
+    } while (madetime == -1);   /* this is necessary to skip impssible times
+                                   like the daylight saving time skips */
+    return madetime;
+
+}
+
+
+/* calculate values required for PRINT and GPRINT functions */
+
+int print_calc(
+    image_desc_t *im)
+{
+    long      i, ii, validsteps;
+    double    printval;
+    struct tm tmvdef;
+    int       graphelement = 0;
+    long      vidx;
+    int       max_ii;
+    double    magfact = -1;
+    char     *si_symb = "";
+    char     *percent_s;
+    int       prline_cnt = 0;
+
+    /* wow initializing tmvdef is quite a task :-) */
+    time_t    now = time(NULL);
+
+    localtime_r(&now, &tmvdef);
+    for (i = 0; i < im->gdes_c; i++) {
+        vidx = im->gdes[i].vidx;
+        switch (im->gdes[i].gf) {
+        case GF_PRINT:
+        case GF_GPRINT:
+            /* PRINT and GPRINT can now print VDEF generated values.
+             * There's no need to do any calculations on them as these
+             * calculations were already made.
+             */
+            if (im->gdes[vidx].gf == GF_VDEF) { /* simply use vals */
+                printval = im->gdes[vidx].vf.val;
+                localtime_r(&im->gdes[vidx].vf.when, &tmvdef);
+            } else {    /* need to calculate max,min,avg etcetera */
+                max_ii = ((im->gdes[vidx].end - im->gdes[vidx].start)
+                          / im->gdes[vidx].step * im->gdes[vidx].ds_cnt);
+                printval = DNAN;
+                validsteps = 0;
+                for (ii = im->gdes[vidx].ds;
+                     ii < max_ii; ii += im->gdes[vidx].ds_cnt) {
+                    if (!finite(im->gdes[vidx].data[ii]))
+                        continue;
+                    if (isnan(printval)) {
+                        printval = im->gdes[vidx].data[ii];
+                        validsteps++;
+                        continue;
+                    }
+
+                    switch (im->gdes[i].cf) {
+                    case CF_HWPREDICT:
+                    case CF_MHWPREDICT:
+                    case CF_DEVPREDICT:
+                    case CF_DEVSEASONAL:
+                    case CF_SEASONAL:
+                    case CF_AVERAGE:
+                        validsteps++;
+                        printval += im->gdes[vidx].data[ii];
+                        break;
+                    case CF_MINIMUM:
+                        printval = min(printval, im->gdes[vidx].data[ii]);
+                        break;
+                    case CF_FAILURES:
+                    case CF_MAXIMUM:
+                        printval = max(printval, im->gdes[vidx].data[ii]);
+                        break;
+                    case CF_LAST:
+                        printval = im->gdes[vidx].data[ii];
+                    }
+                }
+                if (im->gdes[i].cf == CF_AVERAGE || im->gdes[i].cf > CF_LAST) {
+                    if (validsteps > 1) {
+                        printval = (printval / validsteps);
+                    }
+                }
+            }           /* prepare printval */
+
+            if ((percent_s = strstr(im->gdes[i].format, "%S")) != NULL) {
+                /* Magfact is set to -1 upon entry to print_calc.  If it
+                 * is still less than 0, then we need to run auto_scale.
+                 * Otherwise, put the value into the correct units.  If
+                 * the value is 0, then do not set the symbol or magnification
+                 * so next the calculation will be performed again. */
+                if (magfact < 0.0) {
+                    auto_scale(im, &printval, &si_symb, &magfact);
+                    if (printval == 0.0)
+                        magfact = -1.0;
+                } else {
+                    printval /= magfact;
+                }
+                *(++percent_s) = 's';
+            } else if (strstr(im->gdes[i].format, "%s") != NULL) {
+                auto_scale(im, &printval, &si_symb, &magfact);
+            }
+
+            if (im->gdes[i].gf == GF_PRINT) {
+                rrd_infoval_t prline;
+
+                if (im->gdes[i].strftm) {
+                    prline.u_str = (char*)malloc((FMT_LEG_LEN + 2) * sizeof(char));
+                    strftime(prline.u_str,
+                             FMT_LEG_LEN, im->gdes[i].format, &tmvdef);
+                } else if (bad_format(im->gdes[i].format)) {
+                    rrd_set_error
+                        ("bad format for PRINT in '%s'", im->gdes[i].format);
+                    return -1;
+                } else {
+                    prline.u_str =
+                        sprintf_alloc(im->gdes[i].format, printval, si_symb);
+                }
+                grinfo_push(im,
+                            sprintf_alloc
+                            ("print[%ld]", prline_cnt++), RD_I_STR, prline);
+                free(prline.u_str);
+            } else {
+                /* GF_GPRINT */
+
+                if (im->gdes[i].strftm) {
+                    strftime(im->gdes[i].legend,
+                             FMT_LEG_LEN, im->gdes[i].format, &tmvdef);
+                } else {
+                    if (bad_format(im->gdes[i].format)) {
+                        rrd_set_error
+                            ("bad format for GPRINT in '%s'",
+                             im->gdes[i].format);
+                        return -1;
+                    }
+#ifdef HAVE_SNPRINTF
+                    snprintf(im->gdes[i].legend,
+                             FMT_LEG_LEN - 2,
+                             im->gdes[i].format, printval, si_symb);
+#else
+                    sprintf(im->gdes[i].legend,
+                            im->gdes[i].format, printval, si_symb);
+#endif
+                }
+                graphelement = 1;
+            }
+            break;
+        case GF_LINE:
+        case GF_AREA:
+        case GF_TICK:
+            graphelement = 1;
+            break;
+        case GF_HRULE:
+            if (isnan(im->gdes[i].yrule)) { /* we must set this here or the legend printer can not decide to print the legend */
+                im->gdes[i].yrule = im->gdes[vidx].vf.val;
+            };
+            graphelement = 1;
+            break;
+        case GF_VRULE:
+            if (im->gdes[i].xrule == 0) {   /* again ... the legend printer needs it */
+                im->gdes[i].xrule = im->gdes[vidx].vf.when;
+            };
+            graphelement = 1;
+            break;
+        case GF_COMMENT:
+        case GF_TEXTALIGN:
+        case GF_DEF:
+        case GF_CDEF:
+        case GF_VDEF:
+#ifdef WITH_PIECHART
+        case GF_PART:
+#endif
+        case GF_SHIFT:
+        case GF_XPORT:
+            break;
+        case GF_STACK:
+            rrd_set_error
+                ("STACK should already be turned into LINE or AREA here");
+            return -1;
+            break;
+        }
+    }
+    return graphelement;
+}
+
+
+
+/* place legends with color spots */
+int leg_place(
+    image_desc_t *im,
+    int calc_width)
+{
+    /* graph labels */
+    int       interleg = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
+    int       border = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
+    int       fill = 0, fill_last;
+    double    legendwidth; // = im->ximg - 2 * border;
+    int       leg_c = 0;
+    double    leg_x = border;
+    int       leg_y = 0; //im->yimg;
+    int       leg_y_prev = 0; // im->yimg;
+    int       leg_cc;
+    double    glue = 0;
+    int       i, ii, mark = 0;
+    char      default_txtalign = TXA_JUSTIFIED; /*default line orientation */
+    int      *legspace;
+    char     *tab;
+    char      saved_legend[FMT_LEG_LEN + 5];
+
+    if(calc_width){
+        legendwidth = 0;
+    }
+    else{
+        legendwidth = im->legendwidth - 2 * border;
+    }
+
+
+    if (!(im->extra_flags & NOLEGEND) && !(im->extra_flags & ONLY_GRAPH)) {
+        if ((legspace = (int*)malloc(im->gdes_c * sizeof(int))) == NULL) {
+            rrd_set_error("malloc for legspace");
+            return -1;
+        }
+
+        for (i = 0; i < im->gdes_c; i++) {
+            char      prt_fctn; /*special printfunctions */
+            if(calc_width){
+                strcpy(saved_legend, im->gdes[i].legend);
+            }
+
+            fill_last = fill;
+            /* hide legends for rules which are not displayed */
+            if (im->gdes[i].gf == GF_TEXTALIGN) {
+                default_txtalign = im->gdes[i].txtalign;
+            }
+
+            if (!(im->extra_flags & FORCE_RULES_LEGEND)) {
+                if (im->gdes[i].gf == GF_HRULE
+                    && (im->gdes[i].yrule <
+                        im->minval || im->gdes[i].yrule > im->maxval))
+                    im->gdes[i].legend[0] = '\0';
+                if (im->gdes[i].gf == GF_VRULE
+                    && (im->gdes[i].xrule <
+                        im->start || im->gdes[i].xrule > im->end))
+                    im->gdes[i].legend[0] = '\0';
+            }
+
+            /* turn \\t into tab */
+            while ((tab = strstr(im->gdes[i].legend, "\\t"))) {
+                memmove(tab, tab + 1, strlen(tab));
+                tab[0] = (char) 9;
+            }
+
+            leg_cc = strlen(im->gdes[i].legend);
+            /* is there a controle code at the end of the legend string ? */
+            if (leg_cc >= 2 && im->gdes[i].legend[leg_cc - 2] == '\\') {
+                prt_fctn = im->gdes[i].legend[leg_cc - 1];
+                leg_cc -= 2;
+                im->gdes[i].legend[leg_cc] = '\0';
+            } else {
+                prt_fctn = '\0';
+            }
+            /* only valid control codes */
+            if (prt_fctn != 'l' && prt_fctn != 'n' &&   /* a synonym for l */
+                prt_fctn != 'r' &&
+                prt_fctn != 'j' &&
+                prt_fctn != 'c' &&
+                prt_fctn != 'u' &&
+                prt_fctn != 's' && prt_fctn != '\0' && prt_fctn != 'g') {
+                free(legspace);
+                rrd_set_error
+                    ("Unknown control code at the end of '%s\\%c'",
+                     im->gdes[i].legend, prt_fctn);
+                return -1;
+            }
+            /* \n -> \l */
+            if (prt_fctn == 'n') {
+                prt_fctn = 'l';
+            }
+
+            /* remove exess space from the end of the legend for \g */
+            while (prt_fctn == 'g' &&
+                   leg_cc > 0 && im->gdes[i].legend[leg_cc - 1] == ' ') {
+                leg_cc--;
+                im->gdes[i].legend[leg_cc] = '\0';
+            }
+
+            if (leg_cc != 0) {
+
+                /* no interleg space if string ends in \g */
+                legspace[i] = (prt_fctn == 'g' ? 0 : interleg);
+                if (fill > 0) {
+                    fill += legspace[i];
+                }
+                fill +=
+                    gfx_get_text_width(im,
+                                       fill + border,
+                                       im->
+                                       text_prop
+                                       [TEXT_PROP_LEGEND].
+                                       font_desc,
+                                       im->tabwidth, im->gdes[i].legend);
+                leg_c++;
+            } else {
+                legspace[i] = 0;
+            }
+            /* who said there was a special tag ... ? */
+            if (prt_fctn == 'g') {
+                prt_fctn = '\0';
+            }
+
+            if (prt_fctn == '\0') {
+                if(calc_width && (fill > legendwidth)){
+                    legendwidth = fill;
+                }
+                if (i == im->gdes_c - 1 || fill > legendwidth) {
+                    /* just one legend item is left right or center */
+                    switch (default_txtalign) {
+                    case TXA_RIGHT:
+                        prt_fctn = 'r';
+                        break;
+                    case TXA_CENTER:
+                        prt_fctn = 'c';
+                        break;
+                    case TXA_JUSTIFIED:
+                        prt_fctn = 'j';
+                        break;
+                    default:
+                        prt_fctn = 'l';
+                        break;
+                    }
+                }
+                /* is it time to place the legends ? */
+                if (fill > legendwidth) {
+                    if (leg_c > 1) {
+                        /* go back one */
+                        i--;
+                        fill = fill_last;
+                        leg_c--;
+                    }
+                }
+                if (leg_c == 1 && prt_fctn == 'j') {
+                    prt_fctn = 'l';
+                }
+            }
+
+            if (prt_fctn != '\0') {
+                leg_x = border;
+                if (leg_c >= 2 && prt_fctn == 'j') {
+                    glue = (double)(legendwidth - fill) / (double)(leg_c - 1);
+                } else {
+                    glue = 0;
+                }
+                if (prt_fctn == 'c')
+                    leg_x = (double)(legendwidth - fill) / 2.0;
+                if (prt_fctn == 'r')
+                    leg_x = legendwidth - fill + border;
+                for (ii = mark; ii <= i; ii++) {
+                    if (im->gdes[ii].legend[0] == '\0')
+                        continue;   /* skip empty legends */
+                    im->gdes[ii].leg_x = leg_x;
+                    im->gdes[ii].leg_y = leg_y + border;
+                    leg_x +=
+                        (double)gfx_get_text_width(im, leg_x,
+                                           im->
+                                           text_prop
+                                           [TEXT_PROP_LEGEND].
+                                           font_desc,
+                                           im->tabwidth, im->gdes[ii].legend)
+                        +(double)legspace[ii]
+                        + glue;
+                }
+                leg_y_prev = leg_y;
+                if (leg_x > border || prt_fctn == 's')
+                    leg_y += im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
+                if (prt_fctn == 's')
+                    leg_y -= im->text_prop[TEXT_PROP_LEGEND].size;
+                if (prt_fctn == 'u')
+                    leg_y -= im->text_prop[TEXT_PROP_LEGEND].size *1.8;
+
+                if(calc_width && (fill > legendwidth)){
+                    legendwidth = fill;
+                }
+                fill = 0;
+                leg_c = 0;
+                mark = ii;
+            }
+
+            if(calc_width){
+                strcpy(im->gdes[i].legend, saved_legend);
+            }
+        }
+
+        if(calc_width){
+            im->legendwidth = legendwidth + 2 * border;
+        }
+        else{
+            im->legendheight = leg_y + border * 0.6;
+        }
+        free(legspace);
+    }
+    return 0;
+}
+
+/* create a grid on the graph. it determines what to do
+   from the values of xsize, start and end */
+
+/* the xaxis labels are determined from the number of seconds per pixel
+   in the requested graph */
+
+int calc_horizontal_grid(
+    image_desc_t
+    *im)
+{
+    double    range;
+    double    scaledrange;
+    int       pixel, i;
+    int       gridind = 0;
+    int       decimals, fractionals;
+
+    im->ygrid_scale.labfact = 2;
+    range = im->maxval - im->minval;
+    scaledrange = range / im->magfact;
+    /* does the scale of this graph make it impossible to put lines
+       on it? If so, give up. */
+    if (isnan(scaledrange)) {
+        return 0;
+    }
+
+    /* find grid spaceing */
+    pixel = 1;
+    if (isnan(im->ygridstep)) {
+        if (im->extra_flags & ALTYGRID) {
+            /* find the value with max number of digits. Get number of digits */
+            decimals =
+                ceil(log10
+                     (max(fabs(im->maxval), fabs(im->minval)) *
+                      im->viewfactor / im->magfact));
+            if (decimals <= 0)  /* everything is small. make place for zero */
+                decimals = 1;
+            im->ygrid_scale.gridstep =
+                pow((double) 10,
+                    floor(log10(range * im->viewfactor / im->magfact))) /
+                im->viewfactor * im->magfact;
+            if (im->ygrid_scale.gridstep == 0)  /* range is one -> 0.1 is reasonable scale */
+                im->ygrid_scale.gridstep = 0.1;
+            /* should have at least 5 lines but no more then 15 */
+            if (range / im->ygrid_scale.gridstep < 5
+                && im->ygrid_scale.gridstep >= 30)
+                im->ygrid_scale.gridstep /= 10;
+            if (range / im->ygrid_scale.gridstep > 15)
+                im->ygrid_scale.gridstep *= 10;
+            if (range / im->ygrid_scale.gridstep > 5) {
+                im->ygrid_scale.labfact = 1;
+                if (range / im->ygrid_scale.gridstep > 8
+                    || im->ygrid_scale.gridstep <
+                    1.8 * im->text_prop[TEXT_PROP_AXIS].size)
+                    im->ygrid_scale.labfact = 2;
+            } else {
+                im->ygrid_scale.gridstep /= 5;
+                im->ygrid_scale.labfact = 5;
+            }
+            fractionals =
+                floor(log10
+                      (im->ygrid_scale.gridstep *
+                       (double) im->ygrid_scale.labfact * im->viewfactor /
+                       im->magfact));
+            if (fractionals < 0) {  /* small amplitude. */
+                int       len = decimals - fractionals + 1;
+
+                if (im->unitslength < len + 2)
+                    im->unitslength = len + 2;
+                sprintf(im->ygrid_scale.labfmt,
+                        "%%%d.%df%s", len,
+                        -fractionals, (im->symbol != ' ' ? " %c" : ""));
+            } else {
+                int       len = decimals + 1;
+
+                if (im->unitslength < len + 2)
+                    im->unitslength = len + 2;
+                sprintf(im->ygrid_scale.labfmt,
+                        "%%%d.0f%s", len, (im->symbol != ' ' ? " %c" : ""));
+            }
+        } else {        /* classic rrd grid */
+            for (i = 0; ylab[i].grid > 0; i++) {
+                pixel = im->ysize / (scaledrange / ylab[i].grid);
+                gridind = i;
+                if (pixel >= 5)
+                    break;
+            }
+
+            for (i = 0; i < 4; i++) {
+                if (pixel * ylab[gridind].lfac[i] >=
+                    1.8 * im->text_prop[TEXT_PROP_AXIS].size) {
+                    im->ygrid_scale.labfact = ylab[gridind].lfac[i];
+                    break;
+                }
+            }
+
+            im->ygrid_scale.gridstep = ylab[gridind].grid * im->magfact;
+        }
+    } else {
+        im->ygrid_scale.gridstep = im->ygridstep;
+        im->ygrid_scale.labfact = im->ylabfact;
+    }
+    return 1;
+}
+
+int draw_horizontal_grid(
+    image_desc_t
+    *im)
+{
+    int       i;
+    double    scaledstep;
+    char      graph_label[100];
+    int       nlabels = 0;
+    double    X0 = im->xorigin;
+    double    X1 = im->xorigin + im->xsize;
+    int       sgrid = (int) (im->minval / im->ygrid_scale.gridstep - 1);
+    int       egrid = (int) (im->maxval / im->ygrid_scale.gridstep + 1);
+    double    MaxY;
+    double second_axis_magfact = 0;
+    char *second_axis_symb = "";
+
+    scaledstep =
+        im->ygrid_scale.gridstep /
+        (double) im->magfact * (double) im->viewfactor;
+    MaxY = scaledstep * (double) egrid;
+    for (i = sgrid; i <= egrid; i++) {
+        double    Y0 = ytr(im,
+                           im->ygrid_scale.gridstep * i);
+        double    YN = ytr(im,
+                           im->ygrid_scale.gridstep * (i + 1));
+
+        if (floor(Y0 + 0.5) >=
+            im->yorigin - im->ysize && floor(Y0 + 0.5) <= im->yorigin) {
+            /* Make sure at least 2 grid labels are shown, even if it doesn't agree
+               with the chosen settings. Add a label if required by settings, or if
+               there is only one label so far and the next grid line is out of bounds. */
+            if (i % im->ygrid_scale.labfact == 0
+                || (nlabels == 1
+                    && (YN < im->yorigin - im->ysize || YN > im->yorigin))) {
+                if (im->symbol == ' ') {
+                    if (im->extra_flags & ALTYGRID) {
+                        sprintf(graph_label,
+                                im->ygrid_scale.labfmt,
+                                scaledstep * (double) i);
+                    } else {
+                        if (MaxY < 10) {
+                            sprintf(graph_label, "%4.1f",
+                                    scaledstep * (double) i);
+                        } else {
+                            sprintf(graph_label, "%4.0f",
+                                    scaledstep * (double) i);
+                        }
+                    }
+                } else {
+                    char      sisym = (i == 0 ? ' ' : im->symbol);
+
+                    if (im->extra_flags & ALTYGRID) {
+                        sprintf(graph_label,
+                                im->ygrid_scale.labfmt,
+                                scaledstep * (double) i, sisym);
+                    } else {
+                        if (MaxY < 10) {
+                            sprintf(graph_label, "%4.1f %c",
+                                    scaledstep * (double) i, sisym);
+                        } else {
+                            sprintf(graph_label, "%4.0f %c",
+                                    scaledstep * (double) i, sisym);
+                        }
+                    }
+                }
+                nlabels++;
+                if (im->second_axis_scale != 0){
+                        char graph_label_right[100];
+                        double sval = im->ygrid_scale.gridstep*(double)i*im->second_axis_scale+im->second_axis_shift;
+                        if (im->second_axis_format[0] == '\0'){
+                            if (!second_axis_magfact){
+                                double dummy = im->ygrid_scale.gridstep*(double)(sgrid+egrid)/2.0*im->second_axis_scale+im->second_axis_shift;
+                                auto_scale(im,&dummy,&second_axis_symb,&second_axis_magfact);
+                            }
+                            sval /= second_axis_magfact;
+
+                            if(MaxY < 10) {
+                                sprintf(graph_label_right,"%5.1f %s",sval,second_axis_symb);
+                            } else {
+                                sprintf(graph_label_right,"%5.0f %s",sval,second_axis_symb);
+                            }
+                        }
+                        else {
+                           sprintf(graph_label_right,im->second_axis_format,sval);
+                        }
+                        gfx_text ( im,
+                               X1+7, Y0,
+                               im->graph_col[GRC_FONT],
+                               im->text_prop[TEXT_PROP_AXIS].font_desc,
+                               im->tabwidth,0.0, GFX_H_LEFT, GFX_V_CENTER,
+                               graph_label_right );
+                }
+
+                gfx_text(im,
+                         X0 -
+                         im->
+                         text_prop[TEXT_PROP_AXIS].
+                         size, Y0,
+                         im->graph_col[GRC_FONT],
+                         im->
+                         text_prop[TEXT_PROP_AXIS].
+                         font_desc,
+                         im->tabwidth, 0.0,
+                         GFX_H_RIGHT, GFX_V_CENTER, graph_label);
+                gfx_line(im, X0 - 2, Y0, X0, Y0,
+                         MGRIDWIDTH, im->graph_col[GRC_MGRID]);
+                gfx_line(im, X1, Y0, X1 + 2, Y0,
+                         MGRIDWIDTH, im->graph_col[GRC_MGRID]);
+                gfx_dashed_line(im, X0 - 2, Y0,
+                                X1 + 2, Y0,
+                                MGRIDWIDTH,
+                                im->
+                                graph_col
+                                [GRC_MGRID],
+                                im->grid_dash_on, im->grid_dash_off);
+            } else if (!(im->extra_flags & NOMINOR)) {
+                gfx_line(im,
+                         X0 - 2, Y0,
+                         X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]);
+                gfx_line(im, X1, Y0, X1 + 2, Y0,
+                         GRIDWIDTH, im->graph_col[GRC_GRID]);
+                gfx_dashed_line(im, X0 - 1, Y0,
+                                X1 + 1, Y0,
+                                GRIDWIDTH,
+                                im->
+                                graph_col[GRC_GRID],
+                                im->grid_dash_on, im->grid_dash_off);
+            }
+        }
+    }
+    return 1;
+}
+
+/* this is frexp for base 10 */
+double    frexp10(
+    double,
+    double *);
+double frexp10(
+    double x,
+    double *e)
+{
+    double    mnt;
+    int       iexp;
+
+    iexp = floor(log((double)fabs(x)) / log((double)10));
+    mnt = x / pow(10.0, iexp);
+    if (mnt >= 10.0) {
+        iexp++;
+        mnt = x / pow(10.0, iexp);
+    }
+    *e = iexp;
+    return mnt;
+}
+
+
+/* logaritmic horizontal grid */
+int horizontal_log_grid(
+    image_desc_t
+    *im)
+{
+    double    yloglab[][10] = {
+        {
+         1.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0,
+         0.0, 0.0, 0.0}, {
+                          1.0, 5.0, 10., 0.0, 0.0, 0.0, 0.0,
+                          0.0, 0.0, 0.0}, {
+                                           1.0, 2.0, 5.0, 7.0, 10., 0.0, 0.0,
+                                           0.0, 0.0, 0.0}, {
+                                                            1.0, 2.0, 4.0,
+                                                            6.0, 8.0, 10.,
+                                                            0.0,
+                                                            0.0, 0.0, 0.0}, {
+                                                                             1.0,
+                                                                             2.0,
+                                                                             3.0,
+                                                                             4.0,
+                                                                             5.0,
+                                                                             6.0,
+                                                                             7.0,
+                                                                             8.0,
+                                                                             9.0,
+                                                                             10.},
+        {
+         0, 0, 0, 0, 0, 0, 0, 0, 0, 0}  /* last line */
+    };
+    int       i, j, val_exp, min_exp;
+    double    nex;      /* number of decades in data */
+    double    logscale; /* scale in logarithmic space */
+    int       exfrac = 1;   /* decade spacing */
+    int       mid = -1; /* row in yloglab for major grid */
+    double    mspac;    /* smallest major grid spacing (pixels) */
+    int       flab;     /* first value in yloglab to use */
+    double    value, tmp, pre_value;
+    double    X0, X1, Y0;
+    char      graph_label[100];
+
+    nex = log10(im->maxval / im->minval);
+    logscale = im->ysize / nex;
+    /* major spacing for data with high dynamic range */
+    while (logscale * exfrac < 3 * im->text_prop[TEXT_PROP_LEGEND].size) {
+        if (exfrac == 1)
+            exfrac = 3;
+        else
+            exfrac += 3;
+    }
+
+    /* major spacing for less dynamic data */
+    do {
+        /* search best row in yloglab */
+        mid++;
+        for (i = 0; yloglab[mid][i + 1] < 10.0; i++);
+        mspac = logscale * log10(10.0 / yloglab[mid][i]);
+    }
+    while (mspac >
+           2 * im->text_prop[TEXT_PROP_LEGEND].size && yloglab[mid][0] > 0);
+    if (mid)
+        mid--;
+    /* find first value in yloglab */
+    for (flab = 0;
+         yloglab[mid][flab] < 10
+         && frexp10(im->minval, &tmp) > yloglab[mid][flab]; flab++);
+    if (yloglab[mid][flab] == 10.0) {
+        tmp += 1.0;
+        flab = 0;
+    }
+    val_exp = tmp;
+    if (val_exp % exfrac)
+        val_exp += abs(-val_exp % exfrac);
+    X0 = im->xorigin;
+    X1 = im->xorigin + im->xsize;
+    /* draw grid */
+    pre_value = DNAN;
+    while (1) {
+
+        value = yloglab[mid][flab] * pow(10.0, val_exp);
+        if (AlmostEqual2sComplement(value, pre_value, 4))
+            break;      /* it seems we are not converging */
+        pre_value = value;
+        Y0 = ytr(im, value);
+        if (floor(Y0 + 0.5) <= im->yorigin - im->ysize)
+            break;
+        /* major grid line */
+        gfx_line(im,
+                 X0 - 2, Y0, X0, Y0, MGRIDWIDTH, im->graph_col[GRC_MGRID]);
+        gfx_line(im, X1, Y0, X1 + 2, Y0,
+                 MGRIDWIDTH, im->graph_col[GRC_MGRID]);
+        gfx_dashed_line(im, X0 - 2, Y0,
+                        X1 + 2, Y0,
+                        MGRIDWIDTH,
+                        im->
+                        graph_col
+                        [GRC_MGRID], im->grid_dash_on, im->grid_dash_off);
+        /* label */
+        if (im->extra_flags & FORCE_UNITS_SI) {
+            int       scale;
+            double    pvalue;
+            char      symbol;
+
+            scale = floor(val_exp / 3.0);
+            if (value >= 1.0)
+                pvalue = pow(10.0, val_exp % 3);
+            else
+                pvalue = pow(10.0, ((val_exp + 1) % 3) + 2);
+            pvalue *= yloglab[mid][flab];
+            if (((scale + si_symbcenter) < (int) sizeof(si_symbol))
+                && ((scale + si_symbcenter) >= 0))
+                symbol = si_symbol[scale + si_symbcenter];
+            else
+                symbol = '?';
+            sprintf(graph_label, "%3.0f %c", pvalue, symbol);
+        } else {
+            sprintf(graph_label, "%3.0e", value);
+        }
+        if (im->second_axis_scale != 0){
+                char graph_label_right[100];
+                double sval = value*im->second_axis_scale+im->second_axis_shift;
+                if (im->second_axis_format[0] == '\0'){
+                        if (im->extra_flags & FORCE_UNITS_SI) {
+                                double mfac = 1;
+                                char   *symb = "";
+                                auto_scale(im,&sval,&symb,&mfac);
+                                sprintf(graph_label_right,"%4.0f %s", sval,symb);
+                        }
+                        else {
+                                sprintf(graph_label_right,"%3.0e", sval);
+                        }
+                }
+                else {
+                      sprintf(graph_label_right,im->second_axis_format,sval);
+                }
+
+                gfx_text ( im,
+                               X1+7, Y0,
+                               im->graph_col[GRC_FONT],
+                               im->text_prop[TEXT_PROP_AXIS].font_desc,
+                               im->tabwidth,0.0, GFX_H_LEFT, GFX_V_CENTER,
+                               graph_label_right );
+        }
+
+        gfx_text(im,
+                 X0 -
+                 im->
+                 text_prop[TEXT_PROP_AXIS].
+                 size, Y0,
+                 im->graph_col[GRC_FONT],
+                 im->
+                 text_prop[TEXT_PROP_AXIS].
+                 font_desc,
+                 im->tabwidth, 0.0,
+                 GFX_H_RIGHT, GFX_V_CENTER, graph_label);
+        /* minor grid */
+        if (mid < 4 && exfrac == 1) {
+            /* find first and last minor line behind current major line
+             * i is the first line and j tha last */
+            if (flab == 0) {
+                min_exp = val_exp - 1;
+                for (i = 1; yloglab[mid][i] < 10.0; i++);
+                i = yloglab[mid][i - 1] + 1;
+                j = 10;
+            } else {
+                min_exp = val_exp;
+                i = yloglab[mid][flab - 1] + 1;
+                j = yloglab[mid][flab];
+            }
+
+            /* draw minor lines below current major line */
+            for (; i < j; i++) {
+
+                value = i * pow(10.0, min_exp);
+                if (value < im->minval)
+                    continue;
+                Y0 = ytr(im, value);
+                if (floor(Y0 + 0.5) <= im->yorigin - im->ysize)
+                    break;
+                /* draw lines */
+                gfx_line(im,
+                         X0 - 2, Y0,
+                         X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]);
+                gfx_line(im, X1, Y0, X1 + 2, Y0,
+                         GRIDWIDTH, im->graph_col[GRC_GRID]);
+                gfx_dashed_line(im, X0 - 1, Y0,
+                                X1 + 1, Y0,
+                                GRIDWIDTH,
+                                im->
+                                graph_col[GRC_GRID],
+                                im->grid_dash_on, im->grid_dash_off);
+            }
+        } else if (exfrac > 1) {
+            for (i = val_exp - exfrac / 3 * 2; i < val_exp; i += exfrac / 3) {
+                value = pow(10.0, i);
+                if (value < im->minval)
+                    continue;
+                Y0 = ytr(im, value);
+                if (floor(Y0 + 0.5) <= im->yorigin - im->ysize)
+                    break;
+                /* draw lines */
+                gfx_line(im,
+                         X0 - 2, Y0,
+                         X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]);
+                gfx_line(im, X1, Y0, X1 + 2, Y0,
+                         GRIDWIDTH, im->graph_col[GRC_GRID]);
+                gfx_dashed_line(im, X0 - 1, Y0,
+                                X1 + 1, Y0,
+                                GRIDWIDTH,
+                                im->
+                                graph_col[GRC_GRID],
+                                im->grid_dash_on, im->grid_dash_off);
+            }
+        }
+
+        /* next decade */
+        if (yloglab[mid][++flab] == 10.0) {
+            flab = 0;
+            val_exp += exfrac;
+        }
+    }
+
+    /* draw minor lines after highest major line */
+    if (mid < 4 && exfrac == 1) {
+        /* find first and last minor line below current major line
+         * i is the first line and j tha last */
+        if (flab == 0) {
+            min_exp = val_exp - 1;
+            for (i = 1; yloglab[mid][i] < 10.0; i++);
+            i = yloglab[mid][i - 1] + 1;
+            j = 10;
+        } else {
+            min_exp = val_exp;
+            i = yloglab[mid][flab - 1] + 1;
+            j = yloglab[mid][flab];
+        }
+
+        /* draw minor lines below current major line */
+        for (; i < j; i++) {
+
+            value = i * pow(10.0, min_exp);
+            if (value < im->minval)
+                continue;
+            Y0 = ytr(im, value);
+            if (floor(Y0 + 0.5) <= im->yorigin - im->ysize)
+                break;
+            /* draw lines */
+            gfx_line(im,
+                     X0 - 2, Y0, X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]);
+            gfx_line(im, X1, Y0, X1 + 2, Y0,
+                     GRIDWIDTH, im->graph_col[GRC_GRID]);
+            gfx_dashed_line(im, X0 - 1, Y0,
+                            X1 + 1, Y0,
+                            GRIDWIDTH,
+                            im->
+                            graph_col[GRC_GRID],
+                            im->grid_dash_on, im->grid_dash_off);
+        }
+    }
+    /* fancy minor gridlines */
+    else if (exfrac > 1) {
+        for (i = val_exp - exfrac / 3 * 2; i < val_exp; i += exfrac / 3) {
+            value = pow(10.0, i);
+            if (value < im->minval)
+                continue;
+            Y0 = ytr(im, value);
+            if (floor(Y0 + 0.5) <= im->yorigin - im->ysize)
+                break;
+            /* draw lines */
+            gfx_line(im,
+                     X0 - 2, Y0, X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]);
+            gfx_line(im, X1, Y0, X1 + 2, Y0,
+                     GRIDWIDTH, im->graph_col[GRC_GRID]);
+            gfx_dashed_line(im, X0 - 1, Y0,
+                            X1 + 1, Y0,
+                            GRIDWIDTH,
+                            im->
+                            graph_col[GRC_GRID],
+                            im->grid_dash_on, im->grid_dash_off);
+        }
+    }
+
+    return 1;
+}
+
+
+void vertical_grid(
+    image_desc_t *im)
+{
+    int       xlab_sel; /* which sort of label and grid ? */
+    time_t    ti, tilab, timajor;
+    long      factor;
+    char      graph_label[100];
+    double    X0, Y0, Y1;   /* points for filled graph and more */
+    struct tm tm;
+
+    /* the type of time grid is determined by finding
+       the number of seconds per pixel in the graph */
+    if (im->xlab_user.minsec == -1) {
+        factor = (im->end - im->start) / im->xsize;
+        xlab_sel = 0;
+        while (xlab[xlab_sel + 1].minsec !=
+               -1 && xlab[xlab_sel + 1].minsec <= factor) {
+            xlab_sel++;
+        }               /* pick the last one */
+        while (xlab[xlab_sel - 1].minsec ==
+               xlab[xlab_sel].minsec
+               && xlab[xlab_sel].length > (im->end - im->start)) {
+            xlab_sel--;
+        }               /* go back to the smallest size */
+        im->xlab_user.gridtm = xlab[xlab_sel].gridtm;
+        im->xlab_user.gridst = xlab[xlab_sel].gridst;
+        im->xlab_user.mgridtm = xlab[xlab_sel].mgridtm;
+        im->xlab_user.mgridst = xlab[xlab_sel].mgridst;
+        im->xlab_user.labtm = xlab[xlab_sel].labtm;
+        im->xlab_user.labst = xlab[xlab_sel].labst;
+        im->xlab_user.precis = xlab[xlab_sel].precis;
+        im->xlab_user.stst = xlab[xlab_sel].stst;
+    }
+
+    /* y coords are the same for every line ... */
+    Y0 = im->yorigin;
+    Y1 = im->yorigin - im->ysize;
+    /* paint the minor grid */
+    if (!(im->extra_flags & NOMINOR)) {
+        for (ti = find_first_time(im->start,
+                                  im->
+                                  xlab_user.
+                                  gridtm,
+                                  im->
+                                  xlab_user.
+                                  gridst),
+             timajor =
+             find_first_time(im->start,
+                             im->xlab_user.
+                             mgridtm,
+                             im->xlab_user.
+                             mgridst);
+             ti < im->end;
+             ti =
+             find_next_time(ti, im->xlab_user.gridtm, im->xlab_user.gridst)
+            ) {
+            /* are we inside the graph ? */
+            if (ti < im->start || ti > im->end)
+                continue;
+            while (timajor < ti) {
+                timajor = find_next_time(timajor,
+                                         im->
+                                         xlab_user.
+                                         mgridtm, im->xlab_user.mgridst);
+            }
+            if (ti == timajor)
+                continue;   /* skip as falls on major grid line */
+            X0 = xtr(im, ti);
+            gfx_line(im, X0, Y1 - 2, X0, Y1,
+                     GRIDWIDTH, im->graph_col[GRC_GRID]);
+            gfx_line(im, X0, Y0, X0, Y0 + 2,
+                     GRIDWIDTH, im->graph_col[GRC_GRID]);
+            gfx_dashed_line(im, X0, Y0 + 1, X0,
+                            Y1 - 1, GRIDWIDTH,
+                            im->
+                            graph_col[GRC_GRID],
+                            im->grid_dash_on, im->grid_dash_off);
+        }
+    }
+
+    /* paint the major grid */
+    for (ti = find_first_time(im->start,
+                              im->
+                              xlab_user.
+                              mgridtm,
+                              im->
+                              xlab_user.
+                              mgridst);
+         ti < im->end;
+         ti = find_next_time(ti, im->xlab_user.mgridtm, im->xlab_user.mgridst)
+        ) {
+        /* are we inside the graph ? */
+        if (ti < im->start || ti > im->end)
+            continue;
+        X0 = xtr(im, ti);
+        gfx_line(im, X0, Y1 - 2, X0, Y1,
+                 MGRIDWIDTH, im->graph_col[GRC_MGRID]);
+        gfx_line(im, X0, Y0, X0, Y0 + 3,
+                 MGRIDWIDTH, im->graph_col[GRC_MGRID]);
+        gfx_dashed_line(im, X0, Y0 + 3, X0,
+                        Y1 - 2, MGRIDWIDTH,
+                        im->
+                        graph_col
+                        [GRC_MGRID], im->grid_dash_on, im->grid_dash_off);
+    }
+    /* paint the labels below the graph */
+    for (ti =
+         find_first_time(im->start -
+                         im->xlab_user.
+                         precis / 2,
+                         im->xlab_user.
+                         labtm,
+                         im->xlab_user.
+                         labst);
+         ti <=
+         im->end -
+         im->xlab_user.precis / 2;
+         ti = find_next_time(ti, im->xlab_user.labtm, im->xlab_user.labst)
+        ) {
+        tilab = ti + im->xlab_user.precis / 2;  /* correct time for the label */
+        /* are we inside the graph ? */
+        if (tilab < im->start || tilab > im->end)
+            continue;
+#if HAVE_STRFTIME
+        localtime_r(&tilab, &tm);
+        strftime(graph_label, 99, im->xlab_user.stst, &tm);
+#else
+# error "your libc has no strftime I guess we'll abort the exercise here."
+#endif
+        gfx_text(im,
+                 xtr(im, tilab),
+                 Y0 + 3,
+                 im->graph_col[GRC_FONT],
+                 im->
+                 text_prop[TEXT_PROP_AXIS].
+                 font_desc,
+                 im->tabwidth, 0.0,
+                 GFX_H_CENTER, GFX_V_TOP, graph_label);
+    }
+
+}
+
+
+void axis_paint(
+    image_desc_t *im)
+{
+    /* draw x and y axis */
+    /* gfx_line ( im->canvas, im->xorigin+im->xsize,im->yorigin,
+       im->xorigin+im->xsize,im->yorigin-im->ysize,
+       GRIDWIDTH, im->graph_col[GRC_AXIS]);
+
+       gfx_line ( im->canvas, im->xorigin,im->yorigin-im->ysize,
+       im->xorigin+im->xsize,im->yorigin-im->ysize,
+       GRIDWIDTH, im->graph_col[GRC_AXIS]); */
+
+    gfx_line(im, im->xorigin - 4,
+             im->yorigin,
+             im->xorigin + im->xsize +
+             4, im->yorigin, MGRIDWIDTH, im->graph_col[GRC_AXIS]);
+    gfx_line(im, im->xorigin,
+             im->yorigin + 4,
+             im->xorigin,
+             im->yorigin - im->ysize -
+             4, MGRIDWIDTH, im->graph_col[GRC_AXIS]);
+    /* arrow for X and Y axis direction */
+    gfx_new_area(im, im->xorigin + im->xsize + 2, im->yorigin - 3, im->xorigin + im->xsize + 2, im->yorigin + 3, im->xorigin + im->xsize + 7, im->yorigin,  /* horyzontal */
+                 im->graph_col[GRC_ARROW]);
+    gfx_close_path(im);
+    gfx_new_area(im, im->xorigin - 3, im->yorigin - im->ysize - 2, im->xorigin + 3, im->yorigin - im->ysize - 2, im->xorigin, im->yorigin - im->ysize - 7,  /* vertical */
+                 im->graph_col[GRC_ARROW]);
+    gfx_close_path(im);
+    if (im->second_axis_scale != 0){
+       gfx_line ( im, im->xorigin+im->xsize,im->yorigin+4,
+                         im->xorigin+im->xsize,im->yorigin-im->ysize-4,
+                         MGRIDWIDTH, im->graph_col[GRC_AXIS]);
+       gfx_new_area ( im,
+                   im->xorigin+im->xsize-2,  im->yorigin-im->ysize-2,
+                   im->xorigin+im->xsize+3,  im->yorigin-im->ysize-2,
+                   im->xorigin+im->xsize,    im->yorigin-im->ysize-7, /* LINEOFFSET */
+                   im->graph_col[GRC_ARROW]);
+       gfx_close_path(im);
+    }
+
+}
+
+void grid_paint(
+    image_desc_t *im)
+{
+    long      i;
+    int       res = 0;
+    double    X0, Y0;   /* points for filled graph and more */
+    struct gfx_color_t water_color;
+
+    if (im->draw_3d_border > 0) {
+           /* draw 3d border */
+           i = im->draw_3d_border;
+           gfx_new_area(im, 0, im->yimg,
+                        i, im->yimg - i, i, i, im->graph_col[GRC_SHADEA]);
+           gfx_add_point(im, im->ximg - i, i);
+           gfx_add_point(im, im->ximg, 0);
+           gfx_add_point(im, 0, 0);
+           gfx_close_path(im);
+           gfx_new_area(im, i, im->yimg - i,
+                        im->ximg - i,
+                        im->yimg - i, im->ximg - i, i, im->graph_col[GRC_SHADEB]);
+           gfx_add_point(im, im->ximg, 0);
+           gfx_add_point(im, im->ximg, im->yimg);
+           gfx_add_point(im, 0, im->yimg);
+           gfx_close_path(im);
+    }
+    if (im->draw_x_grid == 1)
+        vertical_grid(im);
+    if (im->draw_y_grid == 1) {
+        if (im->logarithmic) {
+            res = horizontal_log_grid(im);
+        } else {
+            res = draw_horizontal_grid(im);
+        }
+
+        /* dont draw horizontal grid if there is no min and max val */
+        if (!res) {
+            char     *nodata = "No Data found";
+
+            gfx_text(im, im->ximg / 2,
+                     (2 * im->yorigin -
+                      im->ysize) / 2,
+                     im->graph_col[GRC_FONT],
+                     im->
+                     text_prop[TEXT_PROP_AXIS].
+                     font_desc,
+                     im->tabwidth, 0.0,
+                     GFX_H_CENTER, GFX_V_CENTER, nodata);
+        }
+    }
+
+    /* yaxis unit description */
+    if (im->ylegend[0] != '\0'){
+        gfx_text(im,
+                 im->xOriginLegendY+10,
+                 im->yOriginLegendY,
+                 im->graph_col[GRC_FONT],
+                 im->
+                 text_prop[TEXT_PROP_UNIT].
+                 font_desc,
+                 im->tabwidth,
+                 RRDGRAPH_YLEGEND_ANGLE, GFX_H_CENTER, GFX_V_CENTER, im->ylegend);
+
+    }
+    if (im->second_axis_legend[0] != '\0'){
+            gfx_text( im,
+                  im->xOriginLegendY2+10,
+                  im->yOriginLegendY2,
+                  im->graph_col[GRC_FONT],
+                  im->text_prop[TEXT_PROP_UNIT].font_desc,
+                  im->tabwidth,
+                  RRDGRAPH_YLEGEND_ANGLE,
+                  GFX_H_CENTER, GFX_V_CENTER,
+                  im->second_axis_legend);
+    }
+
+    /* graph title */
+    gfx_text(im,
+             im->xOriginTitle, im->yOriginTitle+6,
+             im->graph_col[GRC_FONT],
+             im->
+             text_prop[TEXT_PROP_TITLE].
+             font_desc,
+             im->tabwidth, 0.0, GFX_H_CENTER, GFX_V_TOP, im->title);
+    /* rrdtool 'logo' */
+    if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+        water_color = im->graph_col[GRC_FONT];
+        water_color.alpha = 0.3;
+        double xpos = im->legendposition == EAST ? im->xOriginLegendY : im->ximg - 4;
+        gfx_text(im, xpos, 5,
+                 water_color,
+                 im->
+                 text_prop[TEXT_PROP_WATERMARK].
+                 font_desc, im->tabwidth,
+                 -90, GFX_H_LEFT, GFX_V_TOP, "RRDTOOL / TOBI OETIKER");
+    }
+    /* graph watermark */
+    if (im->watermark[0] != '\0') {
+        water_color = im->graph_col[GRC_FONT];
+        water_color.alpha = 0.3;
+        gfx_text(im,
+                 im->ximg / 2, im->yimg - 6,
+                 water_color,
+                 im->
+                 text_prop[TEXT_PROP_WATERMARK].
+                 font_desc, im->tabwidth, 0,
+                 GFX_H_CENTER, GFX_V_BOTTOM, im->watermark);
+    }
+
+    /* graph labels */
+    if (!(im->extra_flags & NOLEGEND) && !(im->extra_flags & ONLY_GRAPH)) {
+        for (i = 0; i < im->gdes_c; i++) {
+            if (im->gdes[i].legend[0] == '\0')
+                continue;
+            /* im->gdes[i].leg_y is the bottom of the legend */
+            X0 = im->xOriginLegend + im->gdes[i].leg_x;
+            Y0 = im->legenddirection == TOP_DOWN ? im->yOriginLegend + im->gdes[i].leg_y : im->yOriginLegend + im->legendheight - im->gdes[i].leg_y;
+            gfx_text(im, X0, Y0,
+                     im->graph_col[GRC_FONT],
+                     im->
+                     text_prop
+                     [TEXT_PROP_LEGEND].font_desc,
+                     im->tabwidth, 0.0,
+                     GFX_H_LEFT, GFX_V_BOTTOM, im->gdes[i].legend);
+            /* The legend for GRAPH items starts with "M " to have
+               enough space for the box */
+            if (im->gdes[i].gf != GF_PRINT &&
+                im->gdes[i].gf != GF_GPRINT && im->gdes[i].gf != GF_COMMENT) {
+                double    boxH, boxV;
+                double    X1, Y1;
+
+                boxH = gfx_get_text_width(im, 0,
+                                          im->
+                                          text_prop
+                                          [TEXT_PROP_LEGEND].
+                                          font_desc,
+                                          im->tabwidth, "o") * 1.2;
+                boxV = boxH;
+                /* shift the box up a bit */
+                Y0 -= boxV * 0.4;
+
+        if (im->dynamic_labels && im->gdes[i].gf == GF_HRULE) { /* [-] */ 
+                       cairo_save(im->cr);
+                       cairo_new_path(im->cr);
+                       cairo_set_line_width(im->cr, 1.0);
+                       gfx_line(im,
+                               X0, Y0 - boxV / 2,
+                               X0 + boxH, Y0 - boxV / 2,
+                               1.0, im->gdes[i].col);
+                       gfx_close_path(im);
+               } else if (im->dynamic_labels && im->gdes[i].gf == GF_VRULE) { /* [|] */
+                       cairo_save(im->cr);
+                       cairo_new_path(im->cr);
+                       cairo_set_line_width(im->cr, 1.0);
+                       gfx_line(im,
+                               X0 + boxH / 2, Y0,
+                               X0 + boxH / 2, Y0 - boxV,
+                               1.0, im->gdes[i].col);
+                       gfx_close_path(im);
+               } else if (im->dynamic_labels && im->gdes[i].gf == GF_LINE) { /* [/] */
+                       cairo_save(im->cr);
+                       cairo_new_path(im->cr);
+                       cairo_set_line_width(im->cr, im->gdes[i].linewidth);
+                       gfx_line(im,
+                               X0, Y0,
+                               X0 + boxH, Y0 - boxV,
+                               im->gdes[i].linewidth, im->gdes[i].col);
+                       gfx_close_path(im);
+               } else {
+               /* make sure transparent colors show up the same way as in the graph */
+                       gfx_new_area(im,
+                                    X0, Y0 - boxV,
+                                    X0, Y0, X0 + boxH, Y0, im->graph_col[GRC_BACK]);
+                       gfx_add_point(im, X0 + boxH, Y0 - boxV);
+                       gfx_close_path(im);
+                       gfx_new_area(im, X0, Y0 - boxV, X0,
+                                    Y0, X0 + boxH, Y0, im->gdes[i].col);
+                       gfx_add_point(im, X0 + boxH, Y0 - boxV);
+                       gfx_close_path(im);
+                       cairo_save(im->cr);
+                       cairo_new_path(im->cr);
+                       cairo_set_line_width(im->cr, 1.0);
+                       X1 = X0 + boxH;
+                       Y1 = Y0 - boxV;
+                       gfx_line_fit(im, &X0, &Y0);
+                       gfx_line_fit(im, &X1, &Y1);
+                       cairo_move_to(im->cr, X0, Y0);
+                       cairo_line_to(im->cr, X1, Y0);
+                       cairo_line_to(im->cr, X1, Y1);
+                       cairo_line_to(im->cr, X0, Y1);
+                       cairo_close_path(im->cr);
+                       cairo_set_source_rgba(im->cr,
+                                             im->graph_col[GRC_FRAME].red,
+                                             im->graph_col[GRC_FRAME].green,
+                                             im->graph_col[GRC_FRAME].blue,
+                                             im->graph_col[GRC_FRAME].alpha);
+               }
+                if (im->gdes[i].dash) {
+                    /* make box borders in legend dashed if the graph is dashed */
+                    double    dashes[] = {
+                        3.0
+                    };
+                    cairo_set_dash(im->cr, dashes, 1, 0.0);
+                }
+                cairo_stroke(im->cr);
+                cairo_restore(im->cr);
+            }
+        }
+    }
+}
+
+
+/*****************************************************
+ * lazy check make sure we rely need to create this graph
+ *****************************************************/
+
+int lazy_check(
+    image_desc_t *im)
+{
+    FILE     *fd = NULL;
+    int       size = 1;
+    struct stat imgstat;
+
+    if (im->lazy == 0)
+        return 0;       /* no lazy option */
+    if (strlen(im->graphfile) == 0)
+        return 0;       /* inmemory option */
+    if (stat(im->graphfile, &imgstat) != 0)
+        return 0;       /* can't stat */
+    /* one pixel in the existing graph is more then what we would
+       change here ... */
+    if (time(NULL) - imgstat.st_mtime > (im->end - im->start) / im->xsize)
+        return 0;
+    if ((fd = fopen(im->graphfile, "rb")) == NULL)
+        return 0;       /* the file does not exist */
+    switch (im->imgformat) {
+    case IF_PNG:
+        size = PngSize(fd, &(im->ximg), &(im->yimg));
+        break;
+    default:
+        size = 1;
+    }
+    fclose(fd);
+    return size;
+}
+
+
+int graph_size_location(
+    image_desc_t
+    *im,
+    int elements)
+{
+    /* The actual size of the image to draw is determined from
+     ** several sources.  The size given on the command line is
+     ** the graph area but we need more as we have to draw labels
+     ** and other things outside the graph area. If the option
+     ** --full-size-mode is selected the size defines the total
+     ** image size and the size available for the graph is
+     ** calculated.
+     */
+
+    /** +---+-----------------------------------+
+     ** | y |...............graph title.........|
+     ** |   +---+-------------------------------+
+     ** | a | y |                               |
+     ** | x |   |                               |
+     ** | i | a |                               |
+     ** | s | x |       main graph area         |
+     ** |   | i |                               |
+     ** | t | s |                               |
+     ** | i |   |                               |
+     ** | t | l |                               |
+     ** | l | b +-------------------------------+
+     ** | e | l |       x axis labels           |
+     ** +---+---+-------------------------------+
+     ** |....................legends............|
+     ** +---------------------------------------+
+     ** |                   watermark           |
+     ** +---------------------------------------+
+     */
+
+    int       Xvertical = 0, Xvertical2 = 0, Ytitle =
+        0, Xylabel = 0, Xmain = 0, Ymain =
+        0, Yxlabel = 0, Xspacing = 15, Yspacing = 15, Ywatermark = 4;
+
+    // no legends and no the shall be plotted it's easy
+    if (im->extra_flags & ONLY_GRAPH) {
+        im->xorigin = 0;
+        im->ximg = im->xsize;
+        im->yimg = im->ysize;
+        im->yorigin = im->ysize;
+        ytr(im, DNAN);
+        return 0;
+    }
+
+    if(im->watermark[0] != '\0') {
+        Ywatermark = im->text_prop[TEXT_PROP_WATERMARK].size * 2;
+    }
+
+    // calculate the width of the left vertical legend
+    if (im->ylegend[0] != '\0') {
+        Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2;
+    }
+
+    // calculate the width of the right vertical legend
+    if (im->second_axis_legend[0] != '\0') {
+        Xvertical2 = im->text_prop[TEXT_PROP_UNIT].size * 2;
+    }
+    else{
+        Xvertical2 = Xspacing;
+    }
+
+    if (im->title[0] != '\0') {
+        /* The title is placed "inbetween" two text lines so it
+         ** automatically has some vertical spacing.  The horizontal
+         ** spacing is added here, on each side.
+         */
+        /* if necessary, reduce the font size of the title until it fits the image width */
+        Ytitle = im->text_prop[TEXT_PROP_TITLE].size * 2.6 + 10;
+    }
+    else{
+        // we have no title; get a little clearing from the top
+        Ytitle = 1.5 * Yspacing;
+    }
+
+    if (elements) {
+        if (im->draw_x_grid) {
+            // calculate the height of the horizontal labelling
+            Yxlabel = im->text_prop[TEXT_PROP_AXIS].size * 2.5;
+        }
+        if (im->draw_y_grid || im->forceleftspace) {
+            // calculate the width of the vertical labelling
+            Xylabel =
+                gfx_get_text_width(im, 0,
+                                   im->text_prop[TEXT_PROP_AXIS].font_desc,
+                                   im->tabwidth, "0") * im->unitslength;
+        }
+    }
+
+    // add some space to the labelling
+    Xylabel += Xspacing;
+
+    /* If the legend is printed besides the graph the width has to be
+     ** calculated first. Placing the legend north or south of the
+     ** graph requires the width calculation first, so the legend is
+     ** skipped for the moment.
+     */
+    im->legendheight = 0;
+    im->legendwidth = 0;
+    if (!(im->extra_flags & NOLEGEND)) {
+        if(im->legendposition == WEST || im->legendposition == EAST){
+            if (leg_place(im, 1) == -1){
+                return -1;
+            }
+        }
+    }
+
+    if (im->extra_flags & FULL_SIZE_MODE) {
+
+        /* The actual size of the image to draw has been determined by the user.
+         ** The graph area is the space remaining after accounting for the legend,
+         ** the watermark, the axis labels, and the title.
+         */
+        im->ximg = im->xsize;
+        im->yimg = im->ysize;
+        Xmain = im->ximg;
+        Ymain = im->yimg;
+
+        /* Now calculate the total size.  Insert some spacing where
+           desired.  im->xorigin and im->yorigin need to correspond
+           with the lower left corner of the main graph area or, if
+           this one is not set, the imaginary box surrounding the
+           pie chart area. */
+        /* Initial size calculation for the main graph area */
+
+        Xmain -= Xylabel;// + Xspacing;
+        if((im->legendposition == WEST || im->legendposition == EAST) && !(im->extra_flags & NOLEGEND) ){
+            Xmain -= im->legendwidth;// + Xspacing;
+        }
+        if (im->second_axis_scale != 0){
+            Xmain -= Xylabel;
+        }
+        if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+            Xmain -= Xspacing;
+        }
+
+        Xmain -= Xvertical + Xvertical2;
+
+        /* limit the remaining space to 0 */
+        if(Xmain < 1){
+            Xmain = 1;
+        }
+        im->xsize = Xmain;
+
+        /* Putting the legend north or south, the height can now be calculated */
+        if (!(im->extra_flags & NOLEGEND)) {
+            if(im->legendposition == NORTH || im->legendposition == SOUTH){
+                im->legendwidth = im->ximg;
+                if (leg_place(im, 0) == -1){
+                    return -1;
+                }
+            }
+        }
+
+        if( (im->legendposition == NORTH || im->legendposition == SOUTH)  && !(im->extra_flags & NOLEGEND) ){
+            Ymain -=  Yxlabel + im->legendheight;
+        }
+        else{
+            Ymain -= Yxlabel;
+        }
+
+        /* reserve space for the title *or* some padding above the graph */
+        Ymain -= Ytitle;
+
+            /* reserve space for padding below the graph */
+        if (im->extra_flags & NOLEGEND) {
+            Ymain -= Yspacing;
+        }
+
+        if (im->watermark[0] != '\0') {
+            Ymain -= Ywatermark;
+        }
+        /* limit the remaining height to 0 */
+        if(Ymain < 1){
+            Ymain = 1;
+        }
+        im->ysize = Ymain;
+    } else {            /* dimension options -width and -height refer to the dimensions of the main graph area */
+
+        /* The actual size of the image to draw is determined from
+         ** several sources.  The size given on the command line is
+         ** the graph area but we need more as we have to draw labels
+         ** and other things outside the graph area.
+         */
+
+        if (elements) {
+            Xmain = im->xsize; // + Xspacing;
+            Ymain = im->ysize;
+        }
+
+        im->ximg = Xmain + Xylabel;
+        if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+            im->ximg += Xspacing;
+        }
+
+        if( (im->legendposition == WEST || im->legendposition == EAST) && !(im->extra_flags & NOLEGEND) ){
+            im->ximg += im->legendwidth;// + Xspacing;
+        }
+        if (im->second_axis_scale != 0){
+            im->ximg += Xylabel;
+        }
+
+        im->ximg += Xvertical + Xvertical2;
+
+        if (!(im->extra_flags & NOLEGEND)) {
+            if(im->legendposition == NORTH || im->legendposition == SOUTH){
+                im->legendwidth = im->ximg;
+                if (leg_place(im, 0) == -1){
+                    return -1;
+                }
+            }
+        }
+
+        im->yimg = Ymain + Yxlabel;
+        if( (im->legendposition == NORTH || im->legendposition == SOUTH)  && !(im->extra_flags & NOLEGEND) ){
+             im->yimg += im->legendheight;
+        }
+
+        /* reserve space for the title *or* some padding above the graph */
+        if (Ytitle) {
+            im->yimg += Ytitle;
+        } else {
+            im->yimg += 1.5 * Yspacing;
+        }
+        /* reserve space for padding below the graph */
+        if (im->extra_flags & NOLEGEND) {
+            im->yimg += Yspacing;
+        }
+
+        if (im->watermark[0] != '\0') {
+            im->yimg += Ywatermark;
+        }
+    }
+
+
+    /* In case of putting the legend in west or east position the first
+     ** legend calculation might lead to wrong positions if some items
+     ** are not aligned on the left hand side (e.g. centered) as the
+     ** legendwidth wight have been increased after the item was placed.
+     ** In this case the positions have to be recalculated.
+     */
+    if (!(im->extra_flags & NOLEGEND)) {
+        if(im->legendposition == WEST || im->legendposition == EAST){
+            if (leg_place(im, 0) == -1){
+                return -1;
+            }
+        }
+    }
+
+    /* After calculating all dimensions
+     ** it is now possible to calculate
+     ** all offsets.
+     */
+    switch(im->legendposition){
+        case NORTH:
+            im->xOriginTitle   = Xvertical + Xylabel + (im->xsize / 2);
+            im->yOriginTitle   = 0;
+
+            im->xOriginLegend  = 0;
+            im->yOriginLegend  = Ytitle;
+
+            im->xOriginLegendY = 0;
+            im->yOriginLegendY = Ytitle + im->legendheight + (Ymain / 2) + Yxlabel;
+
+            im->xorigin        = Xvertical + Xylabel;
+            im->yorigin        = Ytitle + im->legendheight + Ymain;
+
+            im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegendY2 += Xylabel;
+            }
+            im->yOriginLegendY2 = Ytitle + im->legendheight + (Ymain / 2) + Yxlabel;
+
+            break;
+
+        case WEST:
+            im->xOriginTitle   = im->legendwidth + Xvertical + Xylabel + im->xsize / 2;
+            im->yOriginTitle   = 0;
+
+            im->xOriginLegend  = 0;
+            im->yOriginLegend  = Ytitle;
+
+            im->xOriginLegendY = im->legendwidth;
+            im->yOriginLegendY = Ytitle + (Ymain / 2);
+
+            im->xorigin        = im->legendwidth + Xvertical + Xylabel;
+            im->yorigin        = Ytitle + Ymain;
+
+            im->xOriginLegendY2 = im->legendwidth + Xvertical + Xylabel + Xmain;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegendY2 += Xylabel;
+            }
+            im->yOriginLegendY2 = Ytitle + (Ymain / 2);
+
+            break;
+
+        case SOUTH:
+            im->xOriginTitle   = Xvertical + Xylabel + im->xsize / 2;
+            im->yOriginTitle   = 0;
+
+            im->xOriginLegend  = 0;
+            im->yOriginLegend  = Ytitle + Ymain + Yxlabel;
+
+            im->xOriginLegendY = 0;
+            im->yOriginLegendY = Ytitle + (Ymain / 2);
+
+            im->xorigin        = Xvertical + Xylabel;
+            im->yorigin        = Ytitle + Ymain;
+
+            im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegendY2 += Xylabel;
+            }
+            im->yOriginLegendY2 = Ytitle + (Ymain / 2);
+
+            break;
+
+        case EAST:
+            im->xOriginTitle   = Xvertical + Xylabel + im->xsize / 2;
+            im->yOriginTitle   = 0;
+
+            im->xOriginLegend  = Xvertical + Xylabel + Xmain + Xvertical2;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegend += Xylabel;
+            }
+            im->yOriginLegend  = Ytitle;
+
+            im->xOriginLegendY = 0;
+            im->yOriginLegendY = Ytitle + (Ymain / 2);
+
+            im->xorigin        = Xvertical + Xylabel;
+            im->yorigin        = Ytitle + Ymain;
+
+            im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegendY2 += Xylabel;
+            }
+            im->yOriginLegendY2 = Ytitle + (Ymain / 2);
+
+            if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+                im->xOriginTitle    += Xspacing;
+                im->xOriginLegend   += Xspacing;
+                im->xOriginLegendY  += Xspacing;
+                im->xorigin         += Xspacing;
+                im->xOriginLegendY2 += Xspacing;
+            }
+            break;
+    }
+
+    xtr(im, 0);
+    ytr(im, DNAN);
+    return 0;
+}
+
+static cairo_status_t cairo_output(
+    void *closure,
+    const unsigned char
+    *data,
+    unsigned int length)
+{
+    image_desc_t *im = (image_desc_t*)closure;
+
+    im->rendered_image =
+        (unsigned char*)realloc(im->rendered_image, im->rendered_image_size + length);
+    if (im->rendered_image == NULL)
+        return CAIRO_STATUS_WRITE_ERROR;
+    memcpy(im->rendered_image + im->rendered_image_size, data, length);
+    im->rendered_image_size += length;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* draw that picture thing ... */
+int graph_paint(
+    image_desc_t *im)
+{
+    int       i, ii;
+    int       lazy = lazy_check(im);
+    double    areazero = 0.0;
+    graph_desc_t *lastgdes = NULL;
+    rrd_infoval_t info;
+
+//    PangoFontMap *font_map = pango_cairo_font_map_get_default();
+
+    /* pull the data from the rrd files ... */
+    if (data_fetch(im) == -1)
+        return -1;
+    /* evaluate VDEF and CDEF operations ... */
+    if (data_calc(im) == -1)
+        return -1;
+    /* calculate and PRINT and GPRINT definitions. We have to do it at
+     * this point because it will affect the length of the legends
+     * if there are no graph elements (i==0) we stop here ...
+     * if we are lazy, try to quit ...
+     */
+    i = print_calc(im);
+    if (i < 0)
+        return -1;
+
+    /* if we want and can be lazy ... quit now */
+    if (i == 0)
+        return 0;
+
+/**************************************************************
+ *** Calculating sizes and locations became a bit confusing ***
+ *** so I moved this into a separate function.              ***
+ **************************************************************/
+    if (graph_size_location(im, i) == -1)
+        return -1;
+
+    info.u_cnt = im->xorigin;
+    grinfo_push(im, sprintf_alloc("graph_left"), RD_I_CNT, info);
+    info.u_cnt = im->yorigin - im->ysize;
+    grinfo_push(im, sprintf_alloc("graph_top"), RD_I_CNT, info);
+    info.u_cnt = im->xsize;
+    grinfo_push(im, sprintf_alloc("graph_width"), RD_I_CNT, info);
+    info.u_cnt = im->ysize;
+    grinfo_push(im, sprintf_alloc("graph_height"), RD_I_CNT, info);
+    info.u_cnt = im->ximg;
+    grinfo_push(im, sprintf_alloc("image_width"), RD_I_CNT, info);
+    info.u_cnt = im->yimg;
+    grinfo_push(im, sprintf_alloc("image_height"), RD_I_CNT, info);
+    info.u_cnt = im->start;
+    grinfo_push(im, sprintf_alloc("graph_start"), RD_I_CNT, info);
+    info.u_cnt = im->end;
+    grinfo_push(im, sprintf_alloc("graph_end"), RD_I_CNT, info);
+
+    /* if we want and can be lazy ... quit now */
+    if (lazy)
+        return 0;
+
+    /* get actual drawing data and find min and max values */
+    if (data_proc(im) == -1)
+        return -1;
+    if (!im->logarithmic) {
+        si_unit(im);
+    }
+
+    /* identify si magnitude Kilo, Mega Giga ? */
+    if (!im->rigid && !im->logarithmic)
+        expand_range(im);   /* make sure the upper and lower limit are
+                               sensible values */
+
+    info.u_val = im->minval;
+    grinfo_push(im, sprintf_alloc("value_min"), RD_I_VAL, info);
+    info.u_val = im->maxval;
+    grinfo_push(im, sprintf_alloc("value_max"), RD_I_VAL, info);
+
+
+    if (!calc_horizontal_grid(im))
+        return -1;
+    /* reset precalc */
+    ytr(im, DNAN);
+/*   if (im->gridfit)
+     apply_gridfit(im); */
+    /* the actual graph is created by going through the individual
+       graph elements and then drawing them */
+    cairo_surface_destroy(im->surface);
+    switch (im->imgformat) {
+    case IF_PNG:
+        im->surface =
+            cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+                                       im->ximg * im->zoom,
+                                       im->yimg * im->zoom);
+        break;
+    case IF_PDF:
+        im->gridfit = 0;
+        im->surface = strlen(im->graphfile)
+            ? cairo_pdf_surface_create(im->graphfile, im->ximg * im->zoom,
+                                       im->yimg * im->zoom)
+            : cairo_pdf_surface_create_for_stream
+            (&cairo_output, im, im->ximg * im->zoom, im->yimg * im->zoom);
+        break;
+    case IF_EPS:
+        im->gridfit = 0;
+        im->surface = strlen(im->graphfile)
+            ?
+            cairo_ps_surface_create(im->graphfile, im->ximg * im->zoom,
+                                    im->yimg * im->zoom)
+            : cairo_ps_surface_create_for_stream
+            (&cairo_output, im, im->ximg * im->zoom, im->yimg * im->zoom);
+        break;
+    case IF_SVG:
+        im->gridfit = 0;
+        im->surface = strlen(im->graphfile)
+            ?
+            cairo_svg_surface_create(im->
+                                     graphfile,
+                                     im->ximg * im->zoom, im->yimg * im->zoom)
+            : cairo_svg_surface_create_for_stream
+            (&cairo_output, im, im->ximg * im->zoom, im->yimg * im->zoom);
+        cairo_svg_surface_restrict_to_version
+            (im->surface, CAIRO_SVG_VERSION_1_1);
+        break;
+    };
+    cairo_destroy(im->cr);
+    im->cr = cairo_create(im->surface);
+    cairo_set_antialias(im->cr, im->graph_antialias);
+    cairo_scale(im->cr, im->zoom, im->zoom);
+//    pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(font_map), 100);
+    gfx_new_area(im, 0, 0, 0, im->yimg,
+                 im->ximg, im->yimg, im->graph_col[GRC_BACK]);
+    gfx_add_point(im, im->ximg, 0);
+    gfx_close_path(im);
+    gfx_new_area(im, im->xorigin,
+                 im->yorigin,
+                 im->xorigin +
+                 im->xsize, im->yorigin,
+                 im->xorigin +
+                 im->xsize,
+                 im->yorigin - im->ysize, im->graph_col[GRC_CANVAS]);
+    gfx_add_point(im, im->xorigin, im->yorigin - im->ysize);
+    gfx_close_path(im);
+    cairo_rectangle(im->cr, im->xorigin, im->yorigin - im->ysize - 1.0,
+                    im->xsize, im->ysize + 2.0);
+    cairo_clip(im->cr);
+    if (im->minval > 0.0)
+        areazero = im->minval;
+    if (im->maxval < 0.0)
+        areazero = im->maxval;
+    for (i = 0; i < im->gdes_c; i++) {
+        switch (im->gdes[i].gf) {
+        case GF_CDEF:
+        case GF_VDEF:
+        case GF_DEF:
+        case GF_PRINT:
+        case GF_GPRINT:
+        case GF_COMMENT:
+        case GF_TEXTALIGN:
+        case GF_HRULE:
+        case GF_VRULE:
+        case GF_XPORT:
+        case GF_SHIFT:
+            break;
+        case GF_TICK:
+            for (ii = 0; ii < im->xsize; ii++) {
+                if (!isnan(im->gdes[i].p_data[ii])
+                    && im->gdes[i].p_data[ii] != 0.0) {
+                    if (im->gdes[i].yrule > 0) {
+                        gfx_line(im,
+                                 im->xorigin + ii,
+                                 im->yorigin + 1.0,
+                                 im->xorigin + ii,
+                                 im->yorigin -
+                                 im->gdes[i].yrule *
+                                 im->ysize, 1.0, im->gdes[i].col);
+                    } else if (im->gdes[i].yrule < 0) {
+                        gfx_line(im,
+                                 im->xorigin + ii,
+                                 im->yorigin - im->ysize - 1.0,
+                                 im->xorigin + ii,
+                                 im->yorigin - im->ysize -
+                                                im->gdes[i].
+                                                yrule *
+                                 im->ysize, 1.0, im->gdes[i].col);
+                    }
+                }
+            }
+            break;
+        case GF_LINE:
+        case GF_AREA:
+            /* fix data points at oo and -oo */
+            for (ii = 0; ii < im->xsize; ii++) {
+                if (isinf(im->gdes[i].p_data[ii])) {
+                    if (im->gdes[i].p_data[ii] > 0) {
+                        im->gdes[i].p_data[ii] = im->maxval;
+                    } else {
+                        im->gdes[i].p_data[ii] = im->minval;
+                    }
+
+                }
+            }           /* for */
+
+            /* *******************************************************
+               a           ___. (a,t)
+               |   |    ___
+               ____|   |   |   |
+               |       |___|
+               -------|--t-1--t--------------------------------
+
+               if we know the value at time t was a then
+               we draw a square from t-1 to t with the value a.
+
+               ********************************************************* */
+            if (im->gdes[i].col.alpha != 0.0) {
+                /* GF_LINE and friend */
+                if (im->gdes[i].gf == GF_LINE) {
+                    double    last_y = 0.0;
+                    int       draw_on = 0;
+
+                    cairo_save(im->cr);
+                    cairo_new_path(im->cr);
+                    cairo_set_line_width(im->cr, im->gdes[i].linewidth);
+                    if (im->gdes[i].dash) {
+                        cairo_set_dash(im->cr,
+                                       im->gdes[i].p_dashes,
+                                       im->gdes[i].ndash, im->gdes[i].offset);
+                    }
+
+                    for (ii = 1; ii < im->xsize; ii++) {
+                        if (isnan(im->gdes[i].p_data[ii])
+                            || (im->slopemode == 1
+                                && isnan(im->gdes[i].p_data[ii - 1]))) {
+                            draw_on = 0;
+                            continue;
+                        }
+                        if (draw_on == 0) {
+                            last_y = ytr(im, im->gdes[i].p_data[ii]);
+                            if (im->slopemode == 0) {
+                                double    x = ii - 1 + im->xorigin;
+                                double    y = last_y;
+
+                                gfx_line_fit(im, &x, &y);
+                                cairo_move_to(im->cr, x, y);
+                                x = ii + im->xorigin;
+                                y = last_y;
+                                gfx_line_fit(im, &x, &y);
+                                cairo_line_to(im->cr, x, y);
+                            } else {
+                                double    x = ii - 1 + im->xorigin;
+                                double    y =
+                                    ytr(im, im->gdes[i].p_data[ii - 1]);
+                                gfx_line_fit(im, &x, &y);
+                                cairo_move_to(im->cr, x, y);
+                                x = ii + im->xorigin;
+                                y = last_y;
+                                gfx_line_fit(im, &x, &y);
+                                cairo_line_to(im->cr, x, y);
+                            }
+                            draw_on = 1;
+                        } else {
+                            double    x1 = ii + im->xorigin;
+                            double    y1 = ytr(im, im->gdes[i].p_data[ii]);
+
+                            if (im->slopemode == 0
+                                && !AlmostEqual2sComplement(y1, last_y, 4)) {
+                                double    x = ii - 1 + im->xorigin;
+                                double    y = y1;
+
+                                gfx_line_fit(im, &x, &y);
+                                cairo_line_to(im->cr, x, y);
+                            };
+                            last_y = y1;
+                            gfx_line_fit(im, &x1, &y1);
+                            cairo_line_to(im->cr, x1, y1);
+                        };
+                    }
+                    cairo_set_source_rgba(im->cr,
+                                          im->gdes[i].
+                                          col.red,
+                                          im->gdes[i].
+                                          col.green,
+                                          im->gdes[i].
+                                          col.blue, im->gdes[i].col.alpha);
+                    cairo_set_line_cap(im->cr, CAIRO_LINE_CAP_ROUND);
+                    cairo_set_line_join(im->cr, CAIRO_LINE_JOIN_ROUND);
+                    cairo_stroke(im->cr);
+                    cairo_restore(im->cr);
+                } else {
+                    int       idxI = -1;
+                    double   *foreY =
+                        (double *) malloc(sizeof(double) * im->xsize * 2);
+                    double   *foreX =
+                        (double *) malloc(sizeof(double) * im->xsize * 2);
+                    double   *backY =
+                        (double *) malloc(sizeof(double) * im->xsize * 2);
+                    double   *backX =
+                        (double *) malloc(sizeof(double) * im->xsize * 2);
+                    int       drawem = 0;
+
+                    for (ii = 0; ii <= im->xsize; ii++) {
+                        double    ybase, ytop;
+
+                        if (idxI > 0 && (drawem != 0 || ii == im->xsize)) {
+                            int       cntI = 1;
+                            int       lastI = 0;
+
+                            while (cntI < idxI
+                                   &&
+                                   AlmostEqual2sComplement(foreY
+                                                           [lastI],
+                                                           foreY[cntI], 4)
+                                   &&
+                                   AlmostEqual2sComplement(foreY
+                                                           [lastI],
+                                                           foreY
+                                                           [cntI + 1], 4)) {
+                                cntI++;
+                            }
+                            gfx_new_area(im,
+                                         backX[0], backY[0],
+                                         foreX[0], foreY[0],
+                                         foreX[cntI],
+                                         foreY[cntI], im->gdes[i].col);
+                            while (cntI < idxI) {
+                                lastI = cntI;
+                                cntI++;
+                                while (cntI < idxI
+                                       &&
+                                       AlmostEqual2sComplement(foreY
+                                                               [lastI],
+                                                               foreY[cntI], 4)
+                                       &&
+                                       AlmostEqual2sComplement(foreY
+                                                               [lastI],
+                                                               foreY
+                                                               [cntI
+                                                                + 1], 4)) {
+                                    cntI++;
+                                }
+                                gfx_add_point(im, foreX[cntI], foreY[cntI]);
+                            }
+                            gfx_add_point(im, backX[idxI], backY[idxI]);
+                            while (idxI > 1) {
+                                lastI = idxI;
+                                idxI--;
+                                while (idxI > 1
+                                       &&
+                                       AlmostEqual2sComplement(backY
+                                                               [lastI],
+                                                               backY[idxI], 4)
+                                       &&
+                                       AlmostEqual2sComplement(backY
+                                                               [lastI],
+                                                               backY
+                                                               [idxI
+                                                                - 1], 4)) {
+                                    idxI--;
+                                }
+                                gfx_add_point(im, backX[idxI], backY[idxI]);
+                            }
+                            idxI = -1;
+                            drawem = 0;
+                            gfx_close_path(im);
+                        }
+                        if (drawem != 0) {
+                            drawem = 0;
+                            idxI = -1;
+                        }
+                        if (ii == im->xsize)
+                            break;
+                        if (im->slopemode == 0 && ii == 0) {
+                            continue;
+                        }
+                        if (isnan(im->gdes[i].p_data[ii])) {
+                            drawem = 1;
+                            continue;
+                        }
+                        ytop = ytr(im, im->gdes[i].p_data[ii]);
+                        if (lastgdes && im->gdes[i].stack) {
+                            ybase = ytr(im, lastgdes->p_data[ii]);
+                        } else {
+                            ybase = ytr(im, areazero);
+                        }
+                        if (ybase == ytop) {
+                            drawem = 1;
+                            continue;
+                        }
+
+                        if (ybase > ytop) {
+                            double    extra = ytop;
+
+                            ytop = ybase;
+                            ybase = extra;
+                        }
+                        if (im->slopemode == 0) {
+                            backY[++idxI] = ybase - 0.2;
+                            backX[idxI] = ii + im->xorigin - 1;
+                            foreY[idxI] = ytop + 0.2;
+                            foreX[idxI] = ii + im->xorigin - 1;
+                        }
+                        backY[++idxI] = ybase - 0.2;
+                        backX[idxI] = ii + im->xorigin;
+                        foreY[idxI] = ytop + 0.2;
+                        foreX[idxI] = ii + im->xorigin;
+                    }
+                    /* close up any remaining area */
+                    free(foreY);
+                    free(foreX);
+                    free(backY);
+                    free(backX);
+                }       /* else GF_LINE */
+            }
+            /* if color != 0x0 */
+            /* make sure we do not run into trouble when stacking on NaN */
+            for (ii = 0; ii < im->xsize; ii++) {
+                if (isnan(im->gdes[i].p_data[ii])) {
+                    if (lastgdes && (im->gdes[i].stack)) {
+                        im->gdes[i].p_data[ii] = lastgdes->p_data[ii];
+                    } else {
+                        im->gdes[i].p_data[ii] = areazero;
+                    }
+                }
+            }
+            lastgdes = &(im->gdes[i]);
+            break;
+        case GF_STACK:
+            rrd_set_error
+                ("STACK should already be turned into LINE or AREA here");
+            return -1;
+            break;
+        }               /* switch */
+    }
+    cairo_reset_clip(im->cr);
+
+    /* grid_paint also does the text */
+    if (!(im->extra_flags & ONLY_GRAPH))
+        grid_paint(im);
+    if (!(im->extra_flags & ONLY_GRAPH))
+        axis_paint(im);
+    /* the RULES are the last thing to paint ... */
+    for (i = 0; i < im->gdes_c; i++) {
+
+        switch (im->gdes[i].gf) {
+        case GF_HRULE:
+            if (im->gdes[i].yrule >= im->minval
+                && im->gdes[i].yrule <= im->maxval) {
+                cairo_save(im->cr);
+                if (im->gdes[i].dash) {
+                    cairo_set_dash(im->cr,
+                                   im->gdes[i].p_dashes,
+                                   im->gdes[i].ndash, im->gdes[i].offset);
+                }
+                gfx_line(im, im->xorigin,
+                         ytr(im, im->gdes[i].yrule),
+                         im->xorigin + im->xsize,
+                         ytr(im, im->gdes[i].yrule), 1.0, im->gdes[i].col);
+                cairo_stroke(im->cr);
+                cairo_restore(im->cr);
+            }
+            break;
+        case GF_VRULE:
+            if (im->gdes[i].xrule >= im->start
+                && im->gdes[i].xrule <= im->end) {
+                cairo_save(im->cr);
+                if (im->gdes[i].dash) {
+                    cairo_set_dash(im->cr,
+                                   im->gdes[i].p_dashes,
+                                   im->gdes[i].ndash, im->gdes[i].offset);
+                }
+                gfx_line(im,
+                         xtr(im, im->gdes[i].xrule),
+                         im->yorigin, xtr(im,
+                                          im->
+                                          gdes[i].
+                                          xrule),
+                         im->yorigin - im->ysize, 1.0, im->gdes[i].col);
+                cairo_stroke(im->cr);
+                cairo_restore(im->cr);
+            }
+            break;
+        default:
+            break;
+        }
+    }
+
+
+    switch (im->imgformat) {
+    case IF_PNG:
+    {
+        cairo_status_t status;
+
+        status = strlen(im->graphfile) ?
+            cairo_surface_write_to_png(im->surface, im->graphfile)
+            : cairo_surface_write_to_png_stream(im->surface, &cairo_output,
+                                                im);
+
+        if (status != CAIRO_STATUS_SUCCESS) {
+            rrd_set_error("Could not save png to '%s'", im->graphfile);
+            return 1;
+        }
+        break;
+    }
+    default:
+        if (strlen(im->graphfile)) {
+            cairo_show_page(im->cr);
+        } else {
+            cairo_surface_finish(im->surface);
+        }
+        break;
+    }
+
+    return 0;
+}
+
+
+/*****************************************************
+ * graph stuff
+ *****************************************************/
+
+int gdes_alloc(
+    image_desc_t *im)
+{
+
+    im->gdes_c++;
+    if ((im->gdes = (graph_desc_t *)
+         rrd_realloc(im->gdes, (im->gdes_c)
+                     * sizeof(graph_desc_t))) == NULL) {
+        rrd_set_error("realloc graph_descs");
+        return -1;
+    }
+
+
+    im->gdes[im->gdes_c - 1].step = im->step;
+    im->gdes[im->gdes_c - 1].step_orig = im->step;
+    im->gdes[im->gdes_c - 1].stack = 0;
+    im->gdes[im->gdes_c - 1].linewidth = 0;
+    im->gdes[im->gdes_c - 1].debug = 0;
+    im->gdes[im->gdes_c - 1].start = im->start;
+    im->gdes[im->gdes_c - 1].start_orig = im->start;
+    im->gdes[im->gdes_c - 1].end = im->end;
+    im->gdes[im->gdes_c - 1].end_orig = im->end;
+    im->gdes[im->gdes_c - 1].vname[0] = '\0';
+    im->gdes[im->gdes_c - 1].data = NULL;
+    im->gdes[im->gdes_c - 1].ds_namv = NULL;
+    im->gdes[im->gdes_c - 1].data_first = 0;
+    im->gdes[im->gdes_c - 1].p_data = NULL;
+    im->gdes[im->gdes_c - 1].rpnp = NULL;
+    im->gdes[im->gdes_c - 1].p_dashes = NULL;
+    im->gdes[im->gdes_c - 1].shift = 0.0;
+    im->gdes[im->gdes_c - 1].dash = 0;
+    im->gdes[im->gdes_c - 1].ndash = 0;
+    im->gdes[im->gdes_c - 1].offset = 0;
+    im->gdes[im->gdes_c - 1].col.red = 0.0;
+    im->gdes[im->gdes_c - 1].col.green = 0.0;
+    im->gdes[im->gdes_c - 1].col.blue = 0.0;
+    im->gdes[im->gdes_c - 1].col.alpha = 0.0;
+    im->gdes[im->gdes_c - 1].legend[0] = '\0';
+    im->gdes[im->gdes_c - 1].format[0] = '\0';
+    im->gdes[im->gdes_c - 1].strftm = 0;
+    im->gdes[im->gdes_c - 1].rrd[0] = '\0';
+    im->gdes[im->gdes_c - 1].ds = -1;
+    im->gdes[im->gdes_c - 1].cf_reduce = CF_AVERAGE;
+    im->gdes[im->gdes_c - 1].cf = CF_AVERAGE;
+    im->gdes[im->gdes_c - 1].yrule = DNAN;
+    im->gdes[im->gdes_c - 1].xrule = 0;
+    return 0;
+}
+
+/* copies input untill the first unescaped colon is found
+   or until input ends. backslashes have to be escaped as well */
+int scan_for_col(
+    const char *const input,
+    int len,
+    char *const output)
+{
+    int       inp, outp = 0;
+
+    for (inp = 0; inp < len && input[inp] != ':' && input[inp] != '\0'; inp++) {
+        if (input[inp] == '\\'
+            && input[inp + 1] != '\0'
+            && (input[inp + 1] == '\\' || input[inp + 1] == ':')) {
+            output[outp++] = input[++inp];
+        } else {
+            output[outp++] = input[inp];
+        }
+    }
+    output[outp] = '\0';
+    return inp;
+}
+
+/* Now just a wrapper around rrd_graph_v */
+int rrd_graph(
+    int argc,
+    char **argv,
+    char ***prdata,
+    int *xsize,
+    int *ysize,
+    FILE * stream,
+    double *ymin,
+    double *ymax)
+{
+    int       prlines = 0;
+    rrd_info_t *grinfo = NULL;
+    rrd_info_t *walker;
+
+    grinfo = rrd_graph_v(argc, argv);
+    if (grinfo == NULL)
+        return -1;
+    walker = grinfo;
+    (*prdata) = NULL;
+    while (walker) {
+        if (strcmp(walker->key, "image_info") == 0) {
+            prlines++;
+            if (((*prdata) =
+                 (char**)rrd_realloc((*prdata),
+                             (prlines + 1) * sizeof(char *))) == NULL) {
+                rrd_set_error("realloc prdata");
+                return 0;
+            }
+            /* imginfo goes to position 0 in the prdata array */
+            (*prdata)[prlines - 1] = (char*)malloc((strlen(walker->value.u_str)
+                                             + 2) * sizeof(char));
+            strcpy((*prdata)[prlines - 1], walker->value.u_str);
+            (*prdata)[prlines] = NULL;
+        }
+        /* skip anything else */
+        walker = walker->next;
+    }
+    walker = grinfo;
+    *xsize = 0;
+    *ysize = 0;
+    *ymin = 0;
+    *ymax = 0;
+    while (walker) {
+        if (strcmp(walker->key, "image_width") == 0) {
+            *xsize = walker->value.u_cnt;
+        } else if (strcmp(walker->key, "image_height") == 0) {
+            *ysize = walker->value.u_cnt;
+        } else if (strcmp(walker->key, "value_min") == 0) {
+            *ymin = walker->value.u_val;
+        } else if (strcmp(walker->key, "value_max") == 0) {
+            *ymax = walker->value.u_val;
+        } else if (strncmp(walker->key, "print", 5) == 0) { /* keys are prdate[0..] */
+            prlines++;
+            if (((*prdata) =
+                 (char**)rrd_realloc((*prdata),
+                             (prlines + 1) * sizeof(char *))) == NULL) {
+                rrd_set_error("realloc prdata");
+                return 0;
+            }
+            (*prdata)[prlines - 1] = (char*)malloc((strlen(walker->value.u_str)
+                                             + 2) * sizeof(char));
+            (*prdata)[prlines] = NULL;
+            strcpy((*prdata)[prlines - 1], walker->value.u_str);
+        } else if (strcmp(walker->key, "image") == 0) {
+            if ( fwrite(walker->value.u_blo.ptr, walker->value.u_blo.size, 1,
+                   (stream ? stream : stdout)) == 0 && ferror(stream ? stream : stdout)){
+                rrd_set_error("writing image");
+                return 0;
+            }
+        }
+        /* skip anything else */
+        walker = walker->next;
+    }
+    rrd_info_free(grinfo);
+    return 0;
+}
+
+
+/* Some surgery done on this function, it became ridiculously big.
+** Things moved:
+** - initializing     now in rrd_graph_init()
+** - options parsing  now in rrd_graph_options()
+** - script parsing   now in rrd_graph_script()
+*/
+rrd_info_t *rrd_graph_v(
+    int argc,
+    char **argv)
+{
+    image_desc_t im;
+    rrd_info_t *grinfo;
+    char *old_locale;
+    rrd_graph_init(&im);
+    /* a dummy surface so that we can measure text sizes for placements */
+    old_locale = setlocale(LC_NUMERIC, "C");
+    rrd_graph_options(argc, argv, &im);
+    if (rrd_test_error()) {
+        rrd_info_free(im.grinfo);
+        im_free(&im);
+        return NULL;
+    }
+
+    if (optind >= argc) {
+        rrd_info_free(im.grinfo);
+        im_free(&im);
+        rrd_set_error("missing filename");
+        return NULL;
+    }
+
+    if (strlen(argv[optind]) >= MAXPATH) {
+        rrd_set_error("filename (including path) too long");
+        rrd_info_free(im.grinfo);
+        im_free(&im);
+        return NULL;
+    }
+
+    strncpy(im.graphfile, argv[optind], MAXPATH - 1);
+    im.graphfile[MAXPATH - 1] = '\0';
+
+    if (strcmp(im.graphfile, "-") == 0) {
+        im.graphfile[0] = '\0';
+    }
+
+    rrd_graph_script(argc, argv, &im, 1);
+    setlocale(LC_NUMERIC, old_locale); /* reenable locale for rendering the graph */
+
+    if (rrd_test_error()) {
+        rrd_info_free(im.grinfo);
+        im_free(&im);
+        return NULL;
+    }
+
+    /* Everything is now read and the actual work can start */
+
+    if (graph_paint(&im) == -1) {
+        rrd_info_free(im.grinfo);
+        im_free(&im);
+        return NULL;
+    }
+
+
+    /* The image is generated and needs to be output.
+     ** Also, if needed, print a line with information about the image.
+     */
+
+    if (im.imginfo) {
+        rrd_infoval_t info;
+        char     *path;
+        char     *filename;
+
+        path = strdup(im.graphfile);
+        filename = basename(path);
+        info.u_str =
+            sprintf_alloc(im.imginfo,
+                          filename,
+                          (long) (im.zoom *
+                                  im.ximg), (long) (im.zoom * im.yimg));
+        grinfo_push(&im, sprintf_alloc("image_info"), RD_I_STR, info);
+        free(info.u_str);
+        free(path);
+    }
+    if (im.rendered_image) {
+        rrd_infoval_t img;
+
+        img.u_blo.size = im.rendered_image_size;
+        img.u_blo.ptr = im.rendered_image;
+        grinfo_push(&im, sprintf_alloc("image"), RD_I_BLO, img);
+    }
+    grinfo = im.grinfo;
+    im_free(&im);
+    return grinfo;
+}
+
+static void
+rrd_set_font_desc (
+    image_desc_t *im,int prop,char *font, double size ){
+    if (font){
+        strncpy(im->text_prop[prop].font, font, sizeof(text_prop[prop].font) - 1);
+        im->text_prop[prop].font[sizeof(text_prop[prop].font) - 1] = '\0';
+        im->text_prop[prop].font_desc = pango_font_description_from_string( font );
+    };
+    if (size > 0){
+        im->text_prop[prop].size = size;
+    };
+    if (im->text_prop[prop].font_desc && im->text_prop[prop].size ){
+        pango_font_description_set_size(im->text_prop[prop].font_desc, im->text_prop[prop].size * PANGO_SCALE);
+    };
+}
+
+void rrd_graph_init(
+    image_desc_t
+    *im)
+{
+    unsigned int i;
+    char     *deffont = getenv("RRD_DEFAULT_FONT");
+    static PangoFontMap *fontmap = NULL;
+    PangoContext *context;
+
+#ifdef HAVE_TZSET
+    tzset();
+#endif
+
+    im->base = 1000;
+    im->daemon_addr = NULL;
+    im->draw_x_grid = 1;
+    im->draw_y_grid = 1;
+    im->draw_3d_border = 2;
+    im->dynamic_labels = 0;
+    im->extra_flags = 0;
+    im->font_options = cairo_font_options_create();
+    im->forceleftspace = 0;
+    im->gdes_c = 0;
+    im->gdes = NULL;
+    im->graph_antialias = CAIRO_ANTIALIAS_GRAY;
+    im->grid_dash_off = 1;
+    im->grid_dash_on = 1;
+    im->gridfit = 1;
+    im->grinfo = (rrd_info_t *) NULL;
+    im->grinfo_current = (rrd_info_t *) NULL;
+    im->imgformat = IF_PNG;
+    im->imginfo = NULL;
+    im->lazy = 0;
+    im->legenddirection = TOP_DOWN;
+    im->legendheight = 0;
+    im->legendposition = SOUTH;
+    im->legendwidth = 0;
+    im->logarithmic = 0;
+    im->maxval = DNAN;
+    im->minval = 0;
+    im->minval = DNAN;
+    im->prt_c = 0;
+    im->rigid = 0;
+    im->rendered_image_size = 0;
+    im->rendered_image = NULL;
+    im->slopemode = 0;
+    im->step = 0;
+    im->symbol = ' ';
+    im->tabwidth = 40.0;
+    im->title[0] = '\0';
+    im->unitsexponent = 9999;
+    im->unitslength = 6;
+    im->viewfactor = 1.0;
+    im->watermark[0] = '\0';
+    im->with_markup = 0;
+    im->ximg = 0;
+    im->xlab_user.minsec = -1;
+    im->xorigin = 0;
+    im->xOriginLegend = 0;
+    im->xOriginLegendY = 0;
+    im->xOriginLegendY2 = 0;
+    im->xOriginTitle = 0;
+    im->xsize = 400;
+    im->ygridstep = DNAN;
+    im->yimg = 0;
+    im->ylegend[0] = '\0';
+    im->second_axis_scale = 0; /* 0 disables it */
+    im->second_axis_shift = 0; /* no shift by default */
+    im->second_axis_legend[0] = '\0';
+    im->second_axis_format[0] = '\0';
+    im->yorigin = 0;
+    im->yOriginLegend = 0;
+    im->yOriginLegendY = 0;
+    im->yOriginLegendY2 = 0;
+    im->yOriginTitle = 0;
+    im->ysize = 100;
+    im->zoom = 1;
+
+    im->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10);
+    im->cr = cairo_create(im->surface);
+
+    for (i = 0; i < DIM(text_prop); i++) {
+        im->text_prop[i].size = -1;
+        rrd_set_font_desc(im,i, deffont ? deffont : text_prop[i].font,text_prop[i].size);
+    }
+
+    if (fontmap == NULL){
+        fontmap = pango_cairo_font_map_get_default();
+    }
+
+    context =  pango_cairo_font_map_create_context((PangoCairoFontMap*)fontmap);
+
+    pango_cairo_context_set_resolution(context, 100);
+
+    pango_cairo_update_context(im->cr,context);
+
+    im->layout = pango_layout_new(context);
+
+//  im->layout = pango_cairo_create_layout(im->cr);
+
+
+    cairo_font_options_set_hint_style
+        (im->font_options, CAIRO_HINT_STYLE_FULL);
+    cairo_font_options_set_hint_metrics
+        (im->font_options, CAIRO_HINT_METRICS_ON);
+    cairo_font_options_set_antialias(im->font_options, CAIRO_ANTIALIAS_GRAY);
+
+
+
+    for (i = 0; i < DIM(graph_col); i++)
+        im->graph_col[i] = graph_col[i];
+
+
+}
+
+
+void rrd_graph_options(
+    int argc,
+    char *argv[],
+    image_desc_t
+    *im)
+{
+    int       stroff;
+    char     *parsetime_error = NULL;
+    char      scan_gtm[12], scan_mtm[12], scan_ltm[12], col_nam[12];
+    time_t    start_tmp = 0, end_tmp = 0;
+    long      long_tmp;
+    rrd_time_value_t start_tv, end_tv;
+    long unsigned int color;
+
+    /* defines for long options without a short equivalent. should be bytes,
+       and may not collide with (the ASCII value of) short options */
+#define LONGOPT_UNITS_SI 255
+
+/* *INDENT-OFF* */
+    struct option long_options[] = {
+        { "alt-autoscale",      no_argument,       0, 'A'},
+        { "imgformat",          required_argument, 0, 'a'},
+        { "font-smoothing-threshold", required_argument, 0, 'B'},
+        { "base",               required_argument, 0, 'b'},
+        { "color",              required_argument, 0, 'c'},
+        { "full-size-mode",     no_argument,       0, 'D'},
+        { "daemon",             required_argument, 0, 'd'},
+        { "slope-mode",         no_argument,       0, 'E'},
+        { "end",                required_argument, 0, 'e'},
+        { "force-rules-legend", no_argument,       0, 'F'},
+        { "imginfo",            required_argument, 0, 'f'},
+        { "graph-render-mode",  required_argument, 0, 'G'},
+        { "no-legend",          no_argument,       0, 'g'},
+        { "height",             required_argument, 0, 'h'},
+        { "no-minor",           no_argument,       0, 'I'},
+        { "interlaced",         no_argument,       0, 'i'},
+        { "alt-autoscale-min",  no_argument,       0, 'J'},
+        { "only-graph",         no_argument,       0, 'j'},
+        { "units-length",       required_argument, 0, 'L'},
+        { "lower-limit",        required_argument, 0, 'l'},
+        { "alt-autoscale-max",  no_argument,       0, 'M'},
+        { "zoom",               required_argument, 0, 'm'},
+        { "no-gridfit",         no_argument,       0, 'N'},
+        { "font",               required_argument, 0, 'n'},
+        { "logarithmic",        no_argument,       0, 'o'},
+        { "pango-markup",       no_argument,       0, 'P'},
+        { "font-render-mode",   required_argument, 0, 'R'},
+        { "rigid",              no_argument,       0, 'r'},
+        { "step",               required_argument, 0, 'S'},
+        { "start",              required_argument, 0, 's'},
+        { "tabwidth",           required_argument, 0, 'T'},
+        { "title",              required_argument, 0, 't'},
+        { "upper-limit",        required_argument, 0, 'u'},
+        { "vertical-label",     required_argument, 0, 'v'},
+        { "watermark",          required_argument, 0, 'W'},
+        { "width",              required_argument, 0, 'w'},
+        { "units-exponent",     required_argument, 0, 'X'},
+        { "x-grid",             required_argument, 0, 'x'},
+        { "alt-y-grid",         no_argument,       0, 'Y'},
+        { "y-grid",             required_argument, 0, 'y'},
+        { "lazy",               no_argument,       0, 'z'},
+        { "units",              required_argument, 0, LONGOPT_UNITS_SI},
+        { "alt-y-mrtg",         no_argument,       0, 1000},    /* this has no effect it is just here to save old apps from crashing when they use it */
+        { "disable-rrdtool-tag",no_argument,       0, 1001},
+        { "right-axis",         required_argument, 0, 1002},
+        { "right-axis-label",   required_argument, 0, 1003},
+        { "right-axis-format",  required_argument, 0, 1004},
+        { "legend-position",    required_argument, 0, 1005},
+        { "legend-direction",   required_argument, 0, 1006},
+        { "border",             required_argument, 0, 1007},
+        { "grid-dash",          required_argument, 0, 1008},
+        { "dynamic-labels",     no_argument,       0, 1009},
+        {  0, 0, 0, 0}
+};
+/* *INDENT-ON* */
+
+    optind = 0;
+    opterr = 0;         /* initialize getopt */
+    rrd_parsetime("end-24h", &start_tv);
+    rrd_parsetime("now", &end_tv);
+    while (1) {
+        int       option_index = 0;
+        int       opt;
+        int       col_start, col_end;
+
+        opt = getopt_long(argc, argv,
+                          "Aa:B:b:c:Dd:Ee:Ff:G:gh:IiJjL:l:Mm:Nn:oPR:rS:s:T:t:u:v:W:w:X:x:Yy:z",
+                          long_options, &option_index);
+        if (opt == EOF)
+            break;
+        switch (opt) {
+        case 'I':
+            im->extra_flags |= NOMINOR;
+            break;
+        case 'Y':
+            im->extra_flags |= ALTYGRID;
+            break;
+        case 'A':
+            im->extra_flags |= ALTAUTOSCALE;
+            break;
+        case 'J':
+            im->extra_flags |= ALTAUTOSCALE_MIN;
+            break;
+        case 'M':
+            im->extra_flags |= ALTAUTOSCALE_MAX;
+            break;
+        case 'j':
+            im->extra_flags |= ONLY_GRAPH;
+            break;
+        case 'g':
+            im->extra_flags |= NOLEGEND;
+            break;
+        case 1005:
+            if (strcmp(optarg, "north") == 0) {
+                im->legendposition = NORTH;
+            } else if (strcmp(optarg, "west") == 0) {
+                im->legendposition = WEST;
+            } else if (strcmp(optarg, "south") == 0) {
+                im->legendposition = SOUTH;
+            } else if (strcmp(optarg, "east") == 0) {
+                im->legendposition = EAST;
+            } else {
+                rrd_set_error("unknown legend-position '%s'", optarg);
+                return;
+            }
+            break;
+        case 1006:
+            if (strcmp(optarg, "topdown") == 0) {
+                im->legenddirection = TOP_DOWN;
+            } else if (strcmp(optarg, "bottomup") == 0) {
+                im->legenddirection = BOTTOM_UP;
+            } else {
+                rrd_set_error("unknown legend-position '%s'", optarg);
+                return;
+            }
+            break;
+        case 'F':
+            im->extra_flags |= FORCE_RULES_LEGEND;
+            break;
+        case 1001:
+            im->extra_flags |= NO_RRDTOOL_TAG;
+            break;
+        case LONGOPT_UNITS_SI:
+            if (im->extra_flags & FORCE_UNITS) {
+                rrd_set_error("--units can only be used once!");
+                return;
+            }
+            if (strcmp(optarg, "si") == 0)
+                im->extra_flags |= FORCE_UNITS_SI;
+            else {
+                rrd_set_error("invalid argument for --units: %s", optarg);
+                return;
+            }
+            break;
+        case 'X':
+            im->unitsexponent = atoi(optarg);
+            break;
+        case 'L':
+            im->unitslength = atoi(optarg);
+            im->forceleftspace = 1;
+            break;
+        case 'T':
+            im->tabwidth = atof(optarg);
+            break;
+        case 'S':
+            im->step = atoi(optarg);
+            break;
+        case 'N':
+            im->gridfit = 0;
+            break;
+        case 'P':
+            im->with_markup = 1;
+            break;
+        case 's':
+            if ((parsetime_error = rrd_parsetime(optarg, &start_tv))) {
+                rrd_set_error("start time: %s", parsetime_error);
+                return;
+            }
+            break;
+        case 'e':
+            if ((parsetime_error = rrd_parsetime(optarg, &end_tv))) {
+                rrd_set_error("end time: %s", parsetime_error);
+                return;
+            }
+            break;
+        case 'x':
+            if (strcmp(optarg, "none") == 0) {
+                im->draw_x_grid = 0;
+                break;
+            };
+            if (sscanf(optarg,
+                       "%10[A-Z]:%ld:%10[A-Z]:%ld:%10[A-Z]:%ld:%ld:%n",
+                       scan_gtm,
+                       &im->xlab_user.gridst,
+                       scan_mtm,
+                       &im->xlab_user.mgridst,
+                       scan_ltm,
+                       &im->xlab_user.labst,
+                       &im->xlab_user.precis, &stroff) == 7 && stroff != 0) {
+                strncpy(im->xlab_form, optarg + stroff,
+                        sizeof(im->xlab_form) - 1);
+                im->xlab_form[sizeof(im->xlab_form) - 1] = '\0';
+                if ((int)
+                    (im->xlab_user.gridtm = tmt_conv(scan_gtm)) == -1) {
+                    rrd_set_error("unknown keyword %s", scan_gtm);
+                    return;
+                } else if ((int)
+                           (im->xlab_user.mgridtm = tmt_conv(scan_mtm))
+                           == -1) {
+                    rrd_set_error("unknown keyword %s", scan_mtm);
+                    return;
+                } else if ((int)
+                           (im->xlab_user.labtm = tmt_conv(scan_ltm)) == -1) {
+                    rrd_set_error("unknown keyword %s", scan_ltm);
+                    return;
+                }
+                im->xlab_user.minsec = 1;
+                im->xlab_user.stst = im->xlab_form;
+            } else {
+                rrd_set_error("invalid x-grid format");
+                return;
+            }
+            break;
+        case 'y':
+
+            if (strcmp(optarg, "none") == 0) {
+                im->draw_y_grid = 0;
+                break;
+            };
+            if (sscanf(optarg, "%lf:%d", &im->ygridstep, &im->ylabfact) == 2) {
+                if (im->ygridstep <= 0) {
+                    rrd_set_error("grid step must be > 0");
+                    return;
+                } else if (im->ylabfact < 1) {
+                    rrd_set_error("label factor must be > 0");
+                    return;
+                }
+            } else {
+                rrd_set_error("invalid y-grid format");
+                return;
+            }
+            break;
+        case 1007:
+            im->draw_3d_border = atoi(optarg);
+            break;
+        case 1008: /* grid-dash */
+            if(sscanf(optarg,
+                      "%lf:%lf",
+                      &im->grid_dash_on,
+                      &im->grid_dash_off) != 2) {
+                rrd_set_error("expected grid-dash format float:float");
+                return;
+            }
+            break;   
+        case 1009: /* enable dynamic labels */
+            im->dynamic_labels = 1;
+            break;         
+        case 1002: /* right y axis */
+
+            if(sscanf(optarg,
+                      "%lf:%lf",
+                      &im->second_axis_scale,
+                      &im->second_axis_shift) == 2) {
+                if(im->second_axis_scale==0){
+                    rrd_set_error("the second_axis_scale  must not be 0");
+                    return;
+                }
+            } else {
+                rrd_set_error("invalid right-axis format expected scale:shift");
+                return;
+            }
+            break;
+        case 1003:
+            strncpy(im->second_axis_legend,optarg,150);
+            im->second_axis_legend[150]='\0';
+            break;
+        case 1004:
+            if (bad_format(optarg)){
+                rrd_set_error("use either %le or %lf formats");
+                return;
+            }
+            strncpy(im->second_axis_format,optarg,150);
+            im->second_axis_format[150]='\0';
+            break;
+        case 'v':
+            strncpy(im->ylegend, optarg, 150);
+            im->ylegend[150] = '\0';
+            break;
+        case 'u':
+            im->maxval = atof(optarg);
+            break;
+        case 'l':
+            im->minval = atof(optarg);
+            break;
+        case 'b':
+            im->base = atol(optarg);
+            if (im->base != 1024 && im->base != 1000) {
+                rrd_set_error
+                    ("the only sensible value for base apart from 1000 is 1024");
+                return;
+            }
+            break;
+        case 'w':
+            long_tmp = atol(optarg);
+            if (long_tmp < 10) {
+                rrd_set_error("width below 10 pixels");
+                return;
+            }
+            im->xsize = long_tmp;
+            break;
+        case 'h':
+            long_tmp = atol(optarg);
+            if (long_tmp < 10) {
+                rrd_set_error("height below 10 pixels");
+                return;
+            }
+            im->ysize = long_tmp;
+            break;
+        case 'D':
+            im->extra_flags |= FULL_SIZE_MODE;
+            break;
+        case 'i':
+            /* interlaced png not supported at the moment */
+            break;
+        case 'r':
+            im->rigid = 1;
+            break;
+        case 'f':
+            im->imginfo = optarg;
+            break;
+        case 'a':
+            if ((int)
+                (im->imgformat = if_conv(optarg)) == -1) {
+                rrd_set_error("unsupported graphics format '%s'", optarg);
+                return;
+            }
+            break;
+        case 'z':
+            im->lazy = 1;
+            break;
+        case 'E':
+            im->slopemode = 1;
+            break;
+        case 'o':
+            im->logarithmic = 1;
+            break;
+        case 'c':
+            if (sscanf(optarg,
+                       "%10[A-Z]#%n%8lx%n",
+                       col_nam, &col_start, &color, &col_end) == 2) {
+                int       ci;
+                int       col_len = col_end - col_start;
+
+                switch (col_len) {
+                case 3:
+                    color =
+                        (((color & 0xF00) * 0x110000) | ((color & 0x0F0) *
+                                                         0x011000) |
+                         ((color & 0x00F)
+                          * 0x001100)
+                         | 0x000000FF);
+                    break;
+                case 4:
+                    color =
+                        (((color & 0xF000) *
+                          0x11000) | ((color & 0x0F00) *
+                                      0x01100) | ((color &
+                                                   0x00F0) *
+                                                  0x00110) |
+                         ((color & 0x000F) * 0x00011)
+                        );
+                    break;
+                case 6:
+                    color = (color << 8) + 0xff /* shift left by 8 */ ;
+                    break;
+                case 8:
+                    break;
+                default:
+                    rrd_set_error("the color format is #RRGGBB[AA]");
+                    return;
+                }
+                if ((ci = grc_conv(col_nam)) != -1) {
+                    im->graph_col[ci] = gfx_hex_to_col(color);
+                } else {
+                    rrd_set_error("invalid color name '%s'", col_nam);
+                    return;
+                }
+            } else {
+                rrd_set_error("invalid color def format");
+                return;
+            }
+            break;
+        case 'n':{
+            char      prop[15];
+            double    size = 1;
+            int       end;
+
+            if (sscanf(optarg, "%10[A-Z]:%lf%n", prop, &size, &end) >= 2) {
+                int       sindex, propidx;
+
+                if ((sindex = text_prop_conv(prop)) != -1) {
+                    for (propidx = sindex;
+                         propidx < TEXT_PROP_LAST; propidx++) {
+                        if (size > 0) {
+                            rrd_set_font_desc(im,propidx,NULL,size);
+                        }
+                        if ((int) strlen(optarg) > end+2) {
+                            if (optarg[end] == ':') {
+                                rrd_set_font_desc(im,propidx,optarg + end + 1,0);
+                            } else {
+                                rrd_set_error
+                                    ("expected : after font size in '%s'",
+                                     optarg);
+                                return;
+                            }
+                        }
+                        /* only run the for loop for DEFAULT (0) for
+                           all others, we break here. woodo programming */
+                        if (propidx == sindex && sindex != 0)
+                            break;
+                    }
+                } else {
+                    rrd_set_error("invalid fonttag '%s'", prop);
+                    return;
+                }
+            } else {
+                rrd_set_error("invalid text property format");
+                return;
+            }
+            break;
+        }
+        case 'm':
+            im->zoom = atof(optarg);
+            if (im->zoom <= 0.0) {
+                rrd_set_error("zoom factor must be > 0");
+                return;
+            }
+            break;
+        case 't':
+            strncpy(im->title, optarg, 150);
+            im->title[150] = '\0';
+            break;
+        case 'R':
+            if (strcmp(optarg, "normal") == 0) {
+                cairo_font_options_set_antialias
+                    (im->font_options, CAIRO_ANTIALIAS_GRAY);
+                cairo_font_options_set_hint_style
+                    (im->font_options, CAIRO_HINT_STYLE_FULL);
+            } else if (strcmp(optarg, "light") == 0) {
+                cairo_font_options_set_antialias
+                    (im->font_options, CAIRO_ANTIALIAS_GRAY);
+                cairo_font_options_set_hint_style
+                    (im->font_options, CAIRO_HINT_STYLE_SLIGHT);
+            } else if (strcmp(optarg, "mono") == 0) {
+                cairo_font_options_set_antialias
+                    (im->font_options, CAIRO_ANTIALIAS_NONE);
+                cairo_font_options_set_hint_style
+                    (im->font_options, CAIRO_HINT_STYLE_FULL);
+            } else {
+                rrd_set_error("unknown font-render-mode '%s'", optarg);
+                return;
+            }
+            break;
+        case 'G':
+            if (strcmp(optarg, "normal") == 0)
+                im->graph_antialias = CAIRO_ANTIALIAS_GRAY;
+            else if (strcmp(optarg, "mono") == 0)
+                im->graph_antialias = CAIRO_ANTIALIAS_NONE;
+            else {
+                rrd_set_error("unknown graph-render-mode '%s'", optarg);
+                return;
+            }
+            break;
+        case 'B':
+            /* not supported curently */
+            break;
+        case 'W':
+            strncpy(im->watermark, optarg, 100);
+            im->watermark[99] = '\0';
+            break;
+        case 'd':
+        {
+            if (im->daemon_addr != NULL)
+            {
+                rrd_set_error ("You cannot specify --daemon "
+                        "more than once.");
+                return;
+            }
+
+            im->daemon_addr = strdup(optarg);
+            if (im->daemon_addr == NULL)
+            {
+              rrd_set_error("strdup failed");
+              return;
+            }
+
+            break;
+        }
+        case '?':
+            if (optopt != 0)
+                rrd_set_error("unknown option '%c'", optopt);
+            else
+                rrd_set_error("unknown option '%s'", argv[optind - 1]);
+            return;
+        }
+    } /* while (1) */
+
+    {   /* try to connect to rrdcached */
+        int status = rrdc_connect(im->daemon_addr);
+        if (status != 0) return;
+    }
+
+    pango_cairo_context_set_font_options(pango_layout_get_context(im->layout), im->font_options);
+    pango_layout_context_changed(im->layout);
+
+
+
+    if (im->logarithmic && im->minval <= 0) {
+        rrd_set_error
+            ("for a logarithmic yaxis you must specify a lower-limit > 0");
+        return;
+    }
+
+    if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+        /* error string is set in rrd_parsetime.c */
+        return;
+    }
+
+    if (start_tmp < 3600 * 24 * 365 * 10) {
+        rrd_set_error
+            ("the first entry to fetch should be after 1980 (%ld)",
+             start_tmp);
+        return;
+    }
+
+    if (end_tmp < start_tmp) {
+        rrd_set_error
+            ("start (%ld) should be less than end (%ld)", start_tmp, end_tmp);
+        return;
+    }
+
+    im->start = start_tmp;
+    im->end = end_tmp;
+    im->step = max((long) im->step, (im->end - im->start) / im->xsize);
+}
+
+int rrd_graph_color(
+    image_desc_t
+    *im,
+    char *var,
+    char *err,
+    int optional)
+{
+    char     *color;
+    graph_desc_t *gdp = &im->gdes[im->gdes_c - 1];
+
+    color = strstr(var, "#");
+    if (color == NULL) {
+        if (optional == 0) {
+            rrd_set_error("Found no color in %s", err);
+            return 0;
+        }
+        return 0;
+    } else {
+        int       n = 0;
+        char     *rest;
+        long unsigned int col;
+
+        rest = strstr(color, ":");
+        if (rest != NULL)
+            n = rest - color;
+        else
+            n = strlen(color);
+        switch (n) {
+        case 7:
+            sscanf(color, "#%6lx%n", &col, &n);
+            col = (col << 8) + 0xff /* shift left by 8 */ ;
+            if (n != 7)
+                rrd_set_error("Color problem in %s", err);
+            break;
+        case 9:
+            sscanf(color, "#%8lx%n", &col, &n);
+            if (n == 9)
+                break;
+        default:
+            rrd_set_error("Color problem in %s", err);
+        }
+        if (rrd_test_error())
+            return 0;
+        gdp->col = gfx_hex_to_col(col);
+        return n;
+    }
+}
+
+
+int bad_format(
+    char *fmt)
+{
+    char     *ptr;
+    int       n = 0;
+
+    ptr = fmt;
+    while (*ptr != '\0')
+        if (*ptr++ == '%') {
+
+            /* line cannot end with percent char */
+            if (*ptr == '\0')
+                return 1;
+            /* '%s', '%S' and '%%' are allowed */
+            if (*ptr == 's' || *ptr == 'S' || *ptr == '%')
+                ptr++;
+            /* %c is allowed (but use only with vdef!) */
+            else if (*ptr == 'c') {
+                ptr++;
+                n = 1;
+            }
+
+            /* or else '% 6.2lf' and such are allowed */
+            else {
+                /* optional padding character */
+                if (*ptr == ' ' || *ptr == '+' || *ptr == '-')
+                    ptr++;
+                /* This should take care of 'm.n' with all three optional */
+                while (*ptr >= '0' && *ptr <= '9')
+                    ptr++;
+                if (*ptr == '.')
+                    ptr++;
+                while (*ptr >= '0' && *ptr <= '9')
+                    ptr++;
+                /* Either 'le', 'lf' or 'lg' must follow here */
+                if (*ptr++ != 'l')
+                    return 1;
+                if (*ptr == 'e' || *ptr == 'f' || *ptr == 'g')
+                    ptr++;
+                else
+                    return 1;
+                n++;
+            }
+        }
+
+    return (n != 1);
+}
+
+
+int vdef_parse(
+    struct graph_desc_t
+    *gdes,
+    const char *const str)
+{
+    /* A VDEF currently is either "func" or "param,func"
+     * so the parsing is rather simple.  Change if needed.
+     */
+    double    param;
+    char      func[30];
+    int       n;
+
+    n = 0;
+    sscanf(str, "%le,%29[A-Z]%n", &param, func, &n);
+    if (n == (int) strlen(str)) {   /* matched */
+        ;
+    } else {
+        n = 0;
+        sscanf(str, "%29[A-Z]%n", func, &n);
+        if (n == (int) strlen(str)) {   /* matched */
+            param = DNAN;
+        } else {
+            rrd_set_error
+                ("Unknown function string '%s' in VDEF '%s'",
+                 str, gdes->vname);
+            return -1;
+        }
+    }
+    if (!strcmp("PERCENT", func))
+        gdes->vf.op = VDEF_PERCENT;
+    else if (!strcmp("PERCENTNAN", func))
+        gdes->vf.op = VDEF_PERCENTNAN;
+    else if (!strcmp("MAXIMUM", func))
+        gdes->vf.op = VDEF_MAXIMUM;
+    else if (!strcmp("AVERAGE", func))
+        gdes->vf.op = VDEF_AVERAGE;
+    else if (!strcmp("STDEV", func))
+        gdes->vf.op = VDEF_STDEV;
+    else if (!strcmp("MINIMUM", func))
+        gdes->vf.op = VDEF_MINIMUM;
+    else if (!strcmp("TOTAL", func))
+        gdes->vf.op = VDEF_TOTAL;
+    else if (!strcmp("FIRST", func))
+        gdes->vf.op = VDEF_FIRST;
+    else if (!strcmp("LAST", func))
+        gdes->vf.op = VDEF_LAST;
+    else if (!strcmp("LSLSLOPE", func))
+        gdes->vf.op = VDEF_LSLSLOPE;
+    else if (!strcmp("LSLINT", func))
+        gdes->vf.op = VDEF_LSLINT;
+    else if (!strcmp("LSLCORREL", func))
+        gdes->vf.op = VDEF_LSLCORREL;
+    else {
+        rrd_set_error
+            ("Unknown function '%s' in VDEF '%s'\n", func, gdes->vname);
+        return -1;
+    };
+    switch (gdes->vf.op) {
+    case VDEF_PERCENT:
+    case VDEF_PERCENTNAN:
+        if (isnan(param)) { /* no parameter given */
+            rrd_set_error
+                ("Function '%s' needs parameter in VDEF '%s'\n",
+                 func, gdes->vname);
+            return -1;
+        };
+        if (param >= 0.0 && param <= 100.0) {
+            gdes->vf.param = param;
+            gdes->vf.val = DNAN;    /* undefined */
+            gdes->vf.when = 0;  /* undefined */
+        } else {
+            rrd_set_error
+                ("Parameter '%f' out of range in VDEF '%s'\n",
+                 param, gdes->vname);
+            return -1;
+        };
+        break;
+    case VDEF_MAXIMUM:
+    case VDEF_AVERAGE:
+    case VDEF_STDEV:
+    case VDEF_MINIMUM:
+    case VDEF_TOTAL:
+    case VDEF_FIRST:
+    case VDEF_LAST:
+    case VDEF_LSLSLOPE:
+    case VDEF_LSLINT:
+    case VDEF_LSLCORREL:
+        if (isnan(param)) {
+            gdes->vf.param = DNAN;
+            gdes->vf.val = DNAN;
+            gdes->vf.when = 0;
+        } else {
+            rrd_set_error
+                ("Function '%s' needs no parameter in VDEF '%s'\n",
+                 func, gdes->vname);
+            return -1;
+        };
+        break;
+    };
+    return 0;
+}
+
+
+int vdef_calc(
+    image_desc_t *im,
+    int gdi)
+{
+    graph_desc_t *src, *dst;
+    rrd_value_t *data;
+    long      step, steps;
+
+    dst = &im->gdes[gdi];
+    src = &im->gdes[dst->vidx];
+    data = src->data + src->ds;
+
+    steps = (src->end - src->start) / src->step;
+#if 0
+    printf
+        ("DEBUG: start == %lu, end == %lu, %lu steps\n",
+         src->start, src->end, steps);
+#endif
+    switch (dst->vf.op) {
+    case VDEF_PERCENT:{
+        rrd_value_t *array;
+        int       field;
+        if ((array = (rrd_value_t*)malloc(steps * sizeof(double))) == NULL) {
+            rrd_set_error("malloc VDEV_PERCENT");
+            return -1;
+        }
+        for (step = 0; step < steps; step++) {
+            array[step] = data[step * src->ds_cnt];
+        }
+        qsort(array, step, sizeof(double), vdef_percent_compar);
+        field = round((dst->vf.param * (double)(steps - 1)) / 100.0);
+        dst->vf.val = array[field];
+        dst->vf.when = 0;   /* no time component */
+        free(array);
+#if 0
+        for (step = 0; step < steps; step++)
+            printf("DEBUG: %3li:%10.2f %c\n",
+                   step, array[step], step == field ? '*' : ' ');
+#endif
+    }
+        break;
+    case VDEF_PERCENTNAN:{
+        rrd_value_t *array;
+        int       field;
+       /* count number of "valid" values */
+       int nancount=0;
+       for (step = 0; step < steps; step++) {
+         if (!isnan(data[step * src->ds_cnt])) { nancount++; }
+       }
+       /* and allocate it */
+        if ((array = (rrd_value_t*)malloc(nancount * sizeof(double))) == NULL) {
+            rrd_set_error("malloc VDEV_PERCENT");
+            return -1;
+        }
+       /* and fill it in */
+       field=0;
+        for (step = 0; step < steps; step++) {
+           if (!isnan(data[step * src->ds_cnt])) {
+                array[field] = data[step * src->ds_cnt];
+               field++;
+            }
+        }
+        qsort(array, nancount, sizeof(double), vdef_percent_compar);
+        field = round( dst->vf.param * (double)(nancount - 1) / 100.0);
+        dst->vf.val = array[field];
+        dst->vf.when = 0;   /* no time component */
+        free(array);
+    }
+        break;
+    case VDEF_MAXIMUM:
+        step = 0;
+        while (step != steps && isnan(data[step * src->ds_cnt]))
+            step++;
+        if (step == steps) {
+            dst->vf.val = DNAN;
+            dst->vf.when = 0;
+        } else {
+            dst->vf.val = data[step * src->ds_cnt];
+            dst->vf.when = src->start + (step + 1) * src->step;
+        }
+        while (step != steps) {
+            if (finite(data[step * src->ds_cnt])) {
+                if (data[step * src->ds_cnt] > dst->vf.val) {
+                    dst->vf.val = data[step * src->ds_cnt];
+                    dst->vf.when = src->start + (step + 1) * src->step;
+                }
+            }
+            step++;
+        }
+        break;
+    case VDEF_TOTAL:
+    case VDEF_STDEV:
+    case VDEF_AVERAGE:{
+        int       cnt = 0;
+        double    sum = 0.0;
+        double    average = 0.0;
+
+        for (step = 0; step < steps; step++) {
+            if (finite(data[step * src->ds_cnt])) {
+                sum += data[step * src->ds_cnt];
+                cnt++;
+            };
+        }
+        if (cnt) {
+            if (dst->vf.op == VDEF_TOTAL) {
+                dst->vf.val = sum * src->step;
+                dst->vf.when = 0;   /* no time component */
+            } else if (dst->vf.op == VDEF_AVERAGE) {
+                dst->vf.val = sum / cnt;
+                dst->vf.when = 0;   /* no time component */
+            } else {
+                average = sum / cnt;
+                sum = 0.0;
+                for (step = 0; step < steps; step++) {
+                    if (finite(data[step * src->ds_cnt])) {
+                        sum += pow((data[step * src->ds_cnt] - average), 2.0);
+                    };
+                }
+                dst->vf.val = pow(sum / cnt, 0.5);
+                dst->vf.when = 0;   /* no time component */
+            };
+        } else {
+            dst->vf.val = DNAN;
+            dst->vf.when = 0;
+        }
+    }
+        break;
+    case VDEF_MINIMUM:
+        step = 0;
+        while (step != steps && isnan(data[step * src->ds_cnt]))
+            step++;
+        if (step == steps) {
+            dst->vf.val = DNAN;
+            dst->vf.when = 0;
+        } else {
+            dst->vf.val = data[step * src->ds_cnt];
+            dst->vf.when = src->start + (step + 1) * src->step;
+        }
+        while (step != steps) {
+            if (finite(data[step * src->ds_cnt])) {
+                if (data[step * src->ds_cnt] < dst->vf.val) {
+                    dst->vf.val = data[step * src->ds_cnt];
+                    dst->vf.when = src->start + (step + 1) * src->step;
+                }
+            }
+            step++;
+        }
+        break;
+    case VDEF_FIRST:
+        /* The time value returned here is one step before the
+         * actual time value.  This is the start of the first
+         * non-NaN interval.
+         */
+        step = 0;
+        while (step != steps && isnan(data[step * src->ds_cnt]))
+            step++;
+        if (step == steps) {    /* all entries were NaN */
+            dst->vf.val = DNAN;
+            dst->vf.when = 0;
+        } else {
+            dst->vf.val = data[step * src->ds_cnt];
+            dst->vf.when = src->start + step * src->step;
+        }
+        break;
+    case VDEF_LAST:
+        /* The time value returned here is the
+         * actual time value.  This is the end of the last
+         * non-NaN interval.
+         */
+        step = steps - 1;
+        while (step >= 0 && isnan(data[step * src->ds_cnt]))
+            step--;
+        if (step < 0) { /* all entries were NaN */
+            dst->vf.val = DNAN;
+            dst->vf.when = 0;
+        } else {
+            dst->vf.val = data[step * src->ds_cnt];
+            dst->vf.when = src->start + (step + 1) * src->step;
+        }
+        break;
+    case VDEF_LSLSLOPE:
+    case VDEF_LSLINT:
+    case VDEF_LSLCORREL:{
+        /* Bestfit line by linear least squares method */
+
+        int       cnt = 0;
+        double    SUMx, SUMy, SUMxy, SUMxx, SUMyy, slope, y_intercept, correl;
+
+        SUMx = 0;
+        SUMy = 0;
+        SUMxy = 0;
+        SUMxx = 0;
+        SUMyy = 0;
+        for (step = 0; step < steps; step++) {
+            if (finite(data[step * src->ds_cnt])) {
+                cnt++;
+                SUMx += step;
+                SUMxx += step * step;
+                SUMxy += step * data[step * src->ds_cnt];
+                SUMy += data[step * src->ds_cnt];
+                SUMyy += data[step * src->ds_cnt] * data[step * src->ds_cnt];
+            };
+        }
+
+        slope = (SUMx * SUMy - cnt * SUMxy) / (SUMx * SUMx - cnt * SUMxx);
+        y_intercept = (SUMy - slope * SUMx) / cnt;
+        correl =
+            (SUMxy -
+             (SUMx * SUMy) / cnt) /
+            sqrt((SUMxx -
+                  (SUMx * SUMx) / cnt) * (SUMyy - (SUMy * SUMy) / cnt));
+        if (cnt) {
+            if (dst->vf.op == VDEF_LSLSLOPE) {
+                dst->vf.val = slope;
+                dst->vf.when = 0;
+            } else if (dst->vf.op == VDEF_LSLINT) {
+                dst->vf.val = y_intercept;
+                dst->vf.when = 0;
+            } else if (dst->vf.op == VDEF_LSLCORREL) {
+                dst->vf.val = correl;
+                dst->vf.when = 0;
+            };
+        } else {
+            dst->vf.val = DNAN;
+            dst->vf.when = 0;
+        }
+    }
+        break;
+    }
+    return 0;
+}
+
+/* NaN < -INF < finite_values < INF */
+int vdef_percent_compar(
+    const void
+    *a,
+    const void
+    *b)
+{
+    /* Equality is not returned; this doesn't hurt except
+     * (maybe) for a little performance.
+     */
+
+    /* First catch NaN values. They are smallest */
+    if (isnan(*(double *) a))
+        return -1;
+    if (isnan(*(double *) b))
+        return 1;
+    /* NaN doesn't reach this part so INF and -INF are extremes.
+     * The sign from isinf() is compatible with the sign we return
+     */
+    if (isinf(*(double *) a))
+        return isinf(*(double *) a);
+    if (isinf(*(double *) b))
+        return isinf(*(double *) b);
+    /* If we reach this, both values must be finite */
+    if (*(double *) a < *(double *) b)
+        return -1;
+    else
+        return 1;
+}
+
+void grinfo_push(
+    image_desc_t *im,
+    char *key,
+    rrd_info_type_t type,
+    rrd_infoval_t 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
new file mode 100644 (file)
index 0000000..8e28f63
--- /dev/null
@@ -0,0 +1,487 @@
+#ifndef _RRD_GRAPH_H
+#define _RRD_GRAPH_H
+
+#define y0 cairo_y0
+#define y1 cairo_y1
+#define index cairo_index
+
+/* this may configure __EXTENSIONS__ without which pango will fail to compile
+   so load this early */
+#include "../rrd_config.h"
+
+#include <cairo.h>
+#include <cairo-pdf.h>
+#include <cairo-svg.h>
+#include <cairo-ps.h>
+
+#include <pango/pangocairo.h>
+
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+
+#ifdef WIN32
+#  include <windows.h>
+#  define MAXPATH MAX_PATH
+#endif
+
+#define ALTYGRID        0x01   /* use alternative y grid algorithm */
+#define ALTAUTOSCALE    0x02   /* use alternative algorithm to find lower and upper bounds */
+#define ALTAUTOSCALE_MIN 0x04   /* use alternative algorithm to find lower bounds */
+#define ALTAUTOSCALE_MAX 0x08   /* use alternative algorithm to find upper bounds */
+#define NOLEGEND        0x10   /* use no legend */
+#define NOMINOR          0x20   /* Turn off minor gridlines */
+#define ONLY_GRAPH       0x40   /* use only graph */
+#define FORCE_RULES_LEGEND 0x80 /* force printing of HRULE and VRULE legend */
+
+#define FORCE_UNITS 0x100   /* mask for all FORCE_UNITS_* flags */
+#define FORCE_UNITS_SI 0x100    /* force use of SI units in Y axis (no effect in linear graph, SI instead of E in log graph) */
+
+#define FULL_SIZE_MODE     0x200    /* -width and -height indicate the total size of the image */
+#define NO_RRDTOOL_TAG 0x400  /* disable the rrdtool tag */
+
+enum tmt_en { TMT_SECOND = 0, TMT_MINUTE, TMT_HOUR, TMT_DAY,
+    TMT_WEEK, TMT_MONTH, TMT_YEAR
+};
+
+enum grc_en { GRC_CANVAS = 0, GRC_BACK, GRC_SHADEA, GRC_SHADEB,
+    GRC_GRID, GRC_MGRID, GRC_FONT, GRC_ARROW, GRC_AXIS, GRC_FRAME, __GRC_END__
+};
+
+#define MGRIDWIDTH 0.6
+#define GRIDWIDTH  0.4
+
+enum gf_en { GF_PRINT = 0, GF_GPRINT, GF_COMMENT, GF_HRULE, GF_VRULE, GF_LINE,
+    GF_AREA, GF_STACK, GF_TICK, GF_TEXTALIGN,
+    GF_DEF, GF_CDEF, GF_VDEF, GF_SHIFT,
+    GF_XPORT
+};
+
+enum txa_en { TXA_LEFT = 0, TXA_RIGHT, TXA_CENTER, TXA_JUSTIFIED };
+
+enum vdef_op_en {
+    VDEF_MAXIMUM = 0    /* like the MAX in (G)PRINT */
+        , VDEF_MINIMUM  /* like the MIN in (G)PRINT */
+        , VDEF_AVERAGE  /* like the AVERAGE in (G)PRINT */
+        , VDEF_STDEV    /* the standard deviation */
+        , VDEF_PERCENT  /* Nth percentile */
+        , VDEF_TOTAL    /* average multiplied by time */
+        , VDEF_FIRST    /* first non-unknown value and time */
+        , VDEF_LAST     /* last  non-unknown value and time */
+        , VDEF_LSLSLOPE /* least squares line slope */
+        , VDEF_LSLINT   /* least squares line y_intercept */
+        , VDEF_LSLCORREL    /* least squares line correlation coefficient */
+        , VDEF_PERCENTNAN  /* Nth percentile ignoring NAN*/
+};
+enum text_prop_en { 
+    TEXT_PROP_DEFAULT = 0,  /* default settings */
+    TEXT_PROP_TITLE,    /* properties for the title */
+    TEXT_PROP_AXIS,     /* for the numbers next to the axis */
+    TEXT_PROP_UNIT,     /* for the vertical unit description */
+    TEXT_PROP_LEGEND,   /* for the legend below the graph */
+    TEXT_PROP_WATERMARK, /* for the little text to the side of the graph */
+    TEXT_PROP_LAST
+};
+
+enum legend_pos{ NORTH = 0, WEST, SOUTH, EAST };
+enum legend_direction { TOP_DOWN = 0, BOTTOM_UP };
+
+enum gfx_if_en { IF_PNG = 0, IF_SVG, IF_EPS, IF_PDF };
+enum gfx_en { GFX_LINE = 0, GFX_AREA, GFX_TEXT };
+enum gfx_h_align_en { GFX_H_NULL = 0, GFX_H_LEFT, GFX_H_RIGHT, GFX_H_CENTER };
+enum gfx_v_align_en { GFX_V_NULL = 0, GFX_V_TOP, GFX_V_BOTTOM, GFX_V_CENTER };
+
+/* cairo color components */
+typedef struct gfx_color_t {
+    double    red;
+    double    green;
+    double    blue;
+    double    alpha;
+} gfx_color_t;
+
+
+typedef struct text_prop_t {
+    double    size;
+    char      font[1024];
+    PangoFontDescription *font_desc;
+} text_prop_t;
+
+
+typedef struct vdef_t {
+    enum vdef_op_en op;
+    double    param;    /* parameter for function, if applicable */
+    double    val;      /* resulting value */
+    time_t    when;     /* timestamp, if applicable */
+} vdef_t;
+
+typedef struct xlab_t {
+    long      minsec;   /* minimum sec per pix */
+    long      length;   /* number of secs on the image */
+    enum tmt_en gridtm; /* grid interval in what ? */
+    long      gridst;   /* how many whats per grid */
+    enum tmt_en mgridtm;    /* label interval in what ? */
+    long      mgridst;  /* how many whats per label */
+    enum tmt_en labtm;  /* label interval in what ? */
+    long      labst;    /* how many whats per label */
+    long      precis;   /* label precision -> label placement */
+    char     *stst;     /* strftime string */
+} xlab_t;
+
+typedef struct ygrid_scale_t {  /* y axis grid scaling info */
+    double    gridstep;
+    int       labfact;
+    char      labfmt[64];
+} ygrid_scale_t;
+
+/* sensible y label intervals ...*/
+
+typedef struct ylab_t {
+    double    grid;     /* grid spacing */
+    int       lfac[4];  /* associated label spacing */
+} ylab_t;
+
+/* this structure describes the elements which can make up a graph.
+   because they are quite diverse, not all elements will use all the
+   possible parts of the structure. */
+#ifdef HAVE_SNPRINTF
+#define FMT_LEG_LEN 200
+#else
+#define FMT_LEG_LEN 2000
+#endif
+
+typedef struct graph_desc_t {
+    enum gf_en gf;      /* graphing function */
+    int       stack;    /* boolean */
+    int       debug;    /* boolean */
+    char      vname[MAX_VNAME_LEN + 1]; /* name of the variable */
+    long      vidx;     /* gdes reference */
+    char      rrd[1024];    /* name of the rrd_file containing data */
+    char      ds_nam[DS_NAM_SIZE];  /* data source name */
+    long      ds;       /* data source number */
+    enum cf_en cf;      /* consolidation function */
+    enum cf_en cf_reduce;   /* consolidation function for reduce_data() */
+    struct gfx_color_t col; /* graph color */
+    char      format[FMT_LEG_LEN + 5];  /* format for PRINT AND GPRINT */
+    char      legend[FMT_LEG_LEN + 5];  /* legend */
+    int       strftm;   /* should the VDEF legend be formated with strftime */
+    double    leg_x, leg_y; /* location of legend */
+    double    yrule;    /* value for y rule line and for VDEF */
+    time_t    xrule;    /* time for x rule line and for VDEF */
+    vdef_t    vf;       /* instruction for VDEF function */
+    rpnp_t   *rpnp;     /* instructions for CDEF function */
+
+    /* SHIFT implementation */
+    int       shidx;    /* gdes reference for offset (-1 --> constant) */
+    time_t    shval;    /* offset if shidx is -1 */
+    time_t    shift;    /* current shift applied */
+
+    /* description of data fetched for the graph element */
+    time_t    start, end;   /* timestaps for first and last data element */
+    time_t    start_orig, end_orig; /* timestaps for first and last data element */
+    unsigned long step; /* time between samples */
+    unsigned long step_orig;    /* time between samples */
+    unsigned long ds_cnt;   /* how many data sources are there in the fetch */
+    long      data_first;   /* first pointer to this data */
+    char    **ds_namv;  /* name of datasources  in the fetch. */
+    rrd_value_t *data;  /* the raw data drawn from the rrd */
+    rrd_value_t *p_data;    /* processed data, xsize elments */
+    double    linewidth;    /* linewideth */
+
+    /* dashed line stuff */
+    int       dash;     /* boolean, draw dashed line? */
+    double   *p_dashes; /* pointer do dash array which keeps the lengths of dashes */
+    int       ndash;    /* number of dash segments */
+    double    offset;   /* dash offset along the line */
+
+    enum txa_en txtalign;   /* change default alignment strategy for text */
+} graph_desc_t;
+
+typedef struct image_desc_t {
+
+    /* configuration of graph */
+
+    char      graphfile[MAXPATH];   /* filename for graphic */
+    long      xsize, ysize; /* graph area size in pixels */
+    struct gfx_color_t graph_col[__GRC_END__];  /* real colors for the graph */
+    text_prop_t text_prop[TEXT_PROP_LAST];  /* text properties */
+    char      ylegend[210]; /* legend along the yaxis */
+    char      title[210];   /* title for graph */
+    char      watermark[110];   /* watermark for graph */
+    int       draw_x_grid;  /* no x-grid at all */
+    int       draw_y_grid;  /* no y-grid at all */
+    unsigned int draw_3d_border; /* size of border in pixels, 0 for off */
+    unsigned int dynamic_labels; /* pick the label shape according to the line drawn */
+    double    grid_dash_on, grid_dash_off;
+    xlab_t    xlab_user;    /* user defined labeling for xaxis */
+    char      xlab_form[210];   /* format for the label on the xaxis */
+    double    second_axis_scale; /* relative to the first axis (0 to disable) */
+    double    second_axis_shift; /* how much is it shifted vs the first axis */
+    char      second_axis_legend[210]; /* label to put on the seond axis */
+    char      second_axis_format[210]; /* format for the numbers on the scond axis */    
+
+    double    ygridstep;    /* user defined step for y grid */
+    int       ylabfact; /* every how many y grid shall a label be written ? */
+    double    tabwidth; /* tabwdith */
+    time_t    start, end;   /* what time does the graph cover */
+    unsigned long step; /* any preference for the default step ? */
+    rrd_value_t minval, maxval; /* extreme values in the data */
+    int       rigid;    /* do not expand range even with 
+                           values outside */
+    ygrid_scale_t ygrid_scale;  /* calculated y axis grid info */
+    int       gridfit;  /* adjust y-axis range etc so all
+                           grindlines falls in integer pixel values */
+    char     *imginfo;  /* construct an <IMG ... tag and return 
+                           as first retval */
+    enum gfx_if_en imgformat;   /* image format */
+    char     *daemon_addr;  /* rrdcached connection string */
+    int       lazy;     /* only update the image if there is
+                           reasonable probablility that the
+                           existing one is out of date */
+    int       slopemode;    /* connect the dots of the curve directly, not using a stair */
+    enum legend_pos legendposition; /* the position of the legend: north, west, south or east */
+    enum legend_direction legenddirection; /* The direction of the legend topdown or bottomup */
+    int       logarithmic;  /* scale the yaxis logarithmic */
+    double    force_scale_min;  /* Force a scale--min */
+    double    force_scale_max;  /* Force a scale--max */
+
+    /* status information */
+    int       with_markup;
+    long      xorigin, yorigin; /* where is (0,0) of the graph */
+    long      xOriginTitle, yOriginTitle; /* where is the origin of the title */
+    long      xOriginLegendY, yOriginLegendY; /* where is the origin of the y legend */
+    long      xOriginLegendY2, yOriginLegendY2; /* where is the origin of the second y legend */
+    long      xOriginLegend, yOriginLegend; /* where is the origin of the legend */
+    long      ximg, yimg;   /* total size of the image */
+    long      legendwidth, legendheight; /* the calculated height and width of the legend */
+    size_t    rendered_image_size;
+    double    zoom;
+    double    magfact;  /* numerical magnitude */
+    long      base;     /* 1000 or 1024 depending on what we graph */
+    char      symbol;   /* magnitude symbol for y-axis */
+    float     viewfactor;   /* how should the numbers on the y-axis be scaled for viewing ? */
+    int       unitsexponent;    /* 10*exponent for units on y-asis */
+    int       unitslength;  /* width of the yaxis labels */
+    int       forceleftspace;   /* do not kill the space to the left of the y-axis if there is no grid */
+
+    int       extra_flags;  /* flags for boolean options */
+    /* data elements */
+
+    unsigned char *rendered_image;
+    long      prt_c;    /* number of print elements */
+    long      gdes_c;   /* number of graphics elements */
+    graph_desc_t *gdes; /* points to an array of graph elements */
+    cairo_surface_t *surface;   /* graphics library */
+    cairo_t  *cr;       /* drawin context */
+    cairo_font_options_t *font_options; /* cairo font options */
+    cairo_antialias_t graph_antialias;  /* antialiasing for the graph */
+    PangoLayout *layout; /* the pango layout we use for writing fonts */
+    rrd_info_t *grinfo; /* root pointer to extra graph info */
+    rrd_info_t *grinfo_current; /* pointing to current entry */
+} image_desc_t;
+
+/* Prototypes */
+int       xtr(
+    image_desc_t *,
+    time_t);
+double    ytr(
+    image_desc_t *,
+    double);
+enum gf_en gf_conv(
+    char *);
+enum gfx_if_en if_conv(
+    char *);
+enum tmt_en tmt_conv(
+    char *);
+enum grc_en grc_conv(
+    char *);
+enum text_prop_en text_prop_conv(
+    char *);
+int       im_free(
+    image_desc_t *);
+void      auto_scale(
+    image_desc_t *,
+    double *,
+    char **,
+    double *);
+void      si_unit(
+    image_desc_t *);
+void      expand_range(
+    image_desc_t *);
+void      apply_gridfit(
+    image_desc_t *);
+void      reduce_data(
+    enum cf_en,
+    unsigned long,
+    time_t *,
+    time_t *,
+    unsigned long *,
+    unsigned long *,
+    rrd_value_t **);
+int       data_fetch(
+    image_desc_t *);
+long      find_var(
+    image_desc_t *,
+    char *);
+long      find_var_wrapper(
+    void *arg1,
+    char *key);
+long      lcd(
+    long *);
+int       data_calc(
+    image_desc_t *);
+int       data_proc(
+    image_desc_t *);
+time_t    find_first_time(
+    time_t,
+    enum tmt_en,
+    long);
+time_t    find_next_time(
+    time_t,
+    enum tmt_en,
+    long);
+int       print_calc(
+    image_desc_t *);
+int       leg_place(
+    image_desc_t *,
+    int);
+int       calc_horizontal_grid(
+    image_desc_t *);
+int       draw_horizontal_grid(
+    image_desc_t *);
+int       horizontal_log_grid(
+    image_desc_t *);
+void      vertical_grid(
+    image_desc_t *);
+void      axis_paint(
+    image_desc_t *);
+void      grid_paint(
+    image_desc_t *);
+int       lazy_check(
+    image_desc_t *);
+int       graph_paint(
+    image_desc_t *);
+
+int       gdes_alloc(
+    image_desc_t *);
+int       scan_for_col(
+    const char *const,
+    int,
+    char *const);
+void      rrd_graph_init(
+    image_desc_t *);
+
+void      rrd_graph_options(
+    int,
+    char **,
+    image_desc_t *);
+void      rrd_graph_script(
+    int,
+    char **,
+    image_desc_t *,
+    int);
+int       rrd_graph_color(
+    image_desc_t *,
+    char *,
+    char *,
+    int);
+int       bad_format(
+    char *);
+int       vdef_parse(
+    struct graph_desc_t *,
+    const char *const);
+int       vdef_calc(
+    image_desc_t *,
+    int);
+int       vdef_percent_compar(
+    const void *,
+    const void *);
+int       graph_size_location(
+    image_desc_t *,
+    int);
+
+
+/* create a new line */
+void      gfx_line(
+    image_desc_t *im,
+    double X0,
+    double Y0,
+    double X1,
+    double Y1,
+    double width,
+    gfx_color_t color);
+
+void      gfx_dashed_line(
+    image_desc_t *im,
+    double X0,
+    double Y0,
+    double X1,
+    double Y1,
+    double width,
+    gfx_color_t color,
+    double dash_on,
+    double dash_off);
+
+/* create a new area */
+void      gfx_new_area(
+    image_desc_t *im,
+    double X0,
+    double Y0,
+    double X1,
+    double Y1,
+    double X2,
+    double Y2,
+    gfx_color_t color);
+
+/* add a point to a line or to an area */
+void      gfx_add_point(
+    image_desc_t *im,
+    double x,
+    double y);
+
+/* close current path so it ends at the same point as it started */
+void      gfx_close_path(
+    image_desc_t *im);
+
+
+/* create a text node */
+void      gfx_text(
+    image_desc_t *im,
+    double x,
+    double y,
+    gfx_color_t color,
+    PangoFontDescription *font_desc,
+    double tabwidth,
+    double angle,
+    enum gfx_h_align_en h_align,
+    enum gfx_v_align_en v_align,
+    const char *text);
+
+/* measure width of a text string */
+double    gfx_get_text_width(
+    image_desc_t *im,
+    double start,
+    PangoFontDescription *font_desc,
+    double tabwidth,
+    char *text);
+
+
+/* convert color */
+gfx_color_t gfx_hex_to_col(
+    long unsigned int);
+
+void      gfx_line_fit(
+    image_desc_t *im,
+    double *x,
+    double *y);
+
+void      gfx_area_fit(
+    image_desc_t *im,
+    double *x,
+    double *y);
+
+#endif
+
+void      grinfo_push(
+    image_desc_t *im,
+    char *key,
+    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
new file mode 100644 (file)
index 0000000..e2dd06c
--- /dev/null
@@ -0,0 +1,1161 @@
+/****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ ****************************************************************************
+ * rrd_graph_helper.c  commandline parser functions 
+ *                     this code initially written by Alex van den Bogaerdt
+ ****************************************************************************/
+
+#include "rrd_graph.h"
+
+#define dprintf if (gdp->debug) printf
+
+/* NOTE ON PARSING:
+ *
+ * we use the following:
+ * 
+ * i=0;  sscanf(&line[*eaten], "what to find%n", variables, &i)
+ *
+ * Usually you want to find a separator as well.  Example:
+ * i=0; sscanf(&line[*eaten], "%li:%n", &someint, &i)
+ *
+ * When the separator is not found, i is not set and thus remains zero.
+ * Another way would be to compare strlen() to i
+ *
+ * Why is this important?  Because 12345abc should not be matched as
+ * integer 12345 ...
+ */
+
+/* NOTE ON VNAMES:
+ *
+ * "if ((gdp->vidx=find_var(im, l))!=-1)" is not good enough, at least
+ * not by itself.
+ *
+ * A vname as a result of a VDEF is quite different from a vname
+ * resulting of a DEF or CDEF.
+ */
+
+/* NOTE ON VNAMES:
+ *
+ * A vname called "123" is not to be parsed as the number 123
+ */
+
+
+/* Define prototypes for the parsing methods.
+  Inputs:
+   const char *const line    - a fixed pointer to a fixed string
+   unsigned int *const eaten - a fixed pointer to a changing index in that line
+   graph_desc_t *const gdp   - a fixed pointer to a changing graph description
+   image_desc_t *const im    - a fixed pointer to a changing image description
+*/
+
+int       rrd_parse_find_gf(
+    const char *const,
+    unsigned int *const,
+    graph_desc_t *const);
+
+int       rrd_parse_legend(
+    const char *const,
+    unsigned int *const,
+    graph_desc_t *const);
+
+int       rrd_parse_color(
+    const char *const,
+    graph_desc_t *const);
+
+int       rrd_parse_textalign(
+    const char *const,
+    unsigned int *const,
+    graph_desc_t *const);
+
+
+int       rrd_parse_CF(
+    const char *const,
+    unsigned int *const,
+    graph_desc_t *const,
+    enum cf_en *const);
+
+int       rrd_parse_print(
+    const char *const,
+    unsigned int *const,
+    graph_desc_t *const,
+    image_desc_t *const);
+
+int       rrd_parse_shift(
+    const char *const,
+    unsigned int *const,
+    graph_desc_t *const,
+    image_desc_t *const);
+
+int       rrd_parse_xport(
+    const char *const,
+    unsigned int *const,
+    graph_desc_t *const,
+    image_desc_t *const);
+
+int       rrd_parse_PVHLAST(
+    const char *const,
+    unsigned int *const,
+    graph_desc_t *const,
+    image_desc_t *const);
+
+int       rrd_parse_make_vname(
+    const char *const,
+    unsigned int *const,
+    graph_desc_t *const,
+    image_desc_t *const);
+
+int       rrd_parse_find_vname(
+    const char *const,
+    unsigned int *const,
+    graph_desc_t *const,
+    image_desc_t *const);
+
+int       rrd_parse_def(
+    const char *const,
+    unsigned int *const,
+    graph_desc_t *const,
+    image_desc_t *const);
+
+int       rrd_parse_vdef(
+    const char *const,
+    unsigned int *const,
+    graph_desc_t *const,
+    image_desc_t *const);
+
+int       rrd_parse_cdef(
+    const char *const,
+    unsigned int *const,
+    graph_desc_t *const,
+    image_desc_t *const);
+
+int rrd_parse_find_gf(
+    const char *const line,
+    unsigned int *const eaten,
+    graph_desc_t *const gdp)
+{
+    char      funcname[11], c1 = 0;
+    int       i = 0;
+
+    /* start an argument with DEBUG to be able to see how it is parsed */
+    sscanf(&line[*eaten], "DEBUG%n", &i);
+    if (i) {
+        gdp->debug = 1;
+        (*eaten) += i;
+        i = 0;
+        dprintf("Scanning line '%s'\n", &line[*eaten]);
+    }
+    i = 0;
+    c1 = '\0';
+    sscanf(&line[*eaten], "%10[A-Z]%n%c", funcname, &i, &c1);
+    if (!i) {
+        rrd_set_error("Could not make sense out of '%s'", line);
+        return 1;
+    }
+    (*eaten) += i;
+    if ((int) (gdp->gf = gf_conv(funcname)) == -1) {
+        rrd_set_error("'%s' is not a valid function name", funcname);
+        return 1;
+    } else {
+        dprintf("- found function name '%s'\n", funcname);
+    }
+
+    if (c1 == '\0') {
+        rrd_set_error("Function %s needs parameters.  Line: %s\n", funcname,
+                      line);
+        return 1;
+    }
+    if (c1 == ':')
+        (*eaten)++;
+
+    /* Some commands have a parameter before the colon
+     * (currently only LINE)
+     */
+    switch (gdp->gf) {
+    case GF_LINE:
+        if (c1 == ':') {
+            gdp->linewidth = 1;
+            dprintf("- using default width of 1\n");
+        } else {
+            i = 0;
+            sscanf(&line[*eaten], "%lf:%n", &gdp->linewidth, &i);
+            if (!i) {
+                rrd_set_error("Cannot parse line width '%s' in line '%s'\n",
+                              &line[*eaten], line);
+                return 1;
+            } else {
+                dprintf("- scanned width %f\n", gdp->linewidth);
+                if (isnan(gdp->linewidth)) {
+                    rrd_set_error
+                        ("LINE width '%s' is not a number in line '%s'\n",
+                         &line[*eaten], line);
+                    return 1;
+                }
+                if (isinf(gdp->linewidth)) {
+                    rrd_set_error
+                        ("LINE width '%s' is out of range in line '%s'\n",
+                         &line[*eaten], line);
+                    return 1;
+                }
+                if (gdp->linewidth < 0) {
+                    rrd_set_error
+                        ("LINE width '%s' is less than 0 in line '%s'\n",
+                         &line[*eaten], line);
+                    return 1;
+                }
+            }
+            (*eaten) += i;
+        }
+        break;
+    default:
+        if (c1 == ':')
+            break;
+        rrd_set_error("Malformed '%s' command in line '%s'\n", &line[*eaten],
+                      line);
+        return 1;
+    }
+    if (line[*eaten] == '\0') {
+        rrd_set_error("Expected some arguments after '%s'\n", line);
+        return 1;
+    }
+    return 0;
+}
+
+int rrd_parse_legend(
+    const char *const line,
+    unsigned int *const eaten,
+    graph_desc_t *const gdp)
+{
+    int       i;
+
+    if (line[*eaten] == '\0' || line[*eaten] == ':') {
+        dprintf("- no (or: empty) legend found\n");
+        return 0;
+    }
+
+    i = scan_for_col(&line[*eaten], FMT_LEG_LEN, gdp->legend);
+
+    (*eaten) += i;
+
+    if (line[*eaten] != '\0' && line[*eaten] != ':') {
+        rrd_set_error("Legend too long");
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+int rrd_parse_color(
+    const char *const string,
+    graph_desc_t *const gdp)
+{
+    unsigned int r = 0, g = 0, b = 0, a = 0, i;
+
+    /* matches the following formats:
+     ** RGB
+     ** RGBA
+     ** RRGGBB
+     ** RRGGBBAA
+     */
+
+    i = 0;
+    while (string[i] && isxdigit((unsigned int) string[i]))
+        i++;
+    if (string[i] != '\0')
+        return 1;       /* garbage follows hexdigits */
+    switch (i) {
+    case 3:
+    case 4:
+        sscanf(string, "%1x%1x%1x%1x", &r, &g, &b, &a);
+        r *= 0x11;
+        g *= 0x11;
+        b *= 0x11;
+        a *= 0x11;
+        if (i == 3)
+            a = 0xFF;
+        break;
+    case 6:
+    case 8:
+        sscanf(string, "%02x%02x%02x%02x", &r, &g, &b, &a);
+        if (i == 6)
+            a = 0xFF;
+        break;
+    default:
+        return 1;       /* wrong number of digits */
+    }
+    gdp->col = gfx_hex_to_col(r << 24 | g << 16 | b << 8 | a);
+    return 0;
+}
+
+int rrd_parse_CF(
+    const char *const line,
+    unsigned int *const eaten,
+    graph_desc_t *const gdp,
+    enum cf_en *cf)
+{
+    char      symname[CF_NAM_SIZE];
+    int       i = 0;
+
+    sscanf(&line[*eaten], CF_NAM_FMT "%n", symname, &i);
+    if ((!i) || ((line[(*eaten) + i] != '\0') && (line[(*eaten) + i] != ':'))) {
+        rrd_set_error("Cannot parse CF in '%s'", line);
+        return 1;
+    }
+    (*eaten) += i;
+    dprintf("- using CF '%s'\n", symname);
+
+    if ((int) (*cf = cf_conv(symname)) == -1) {
+        rrd_set_error("Unknown CF '%s' in '%s'", symname, line);
+        return 1;
+    }
+
+    if (line[*eaten] != '\0')
+        (*eaten)++;
+    return 0;
+}
+
+/* Try to match next token as a vname.
+ *
+ * Returns:
+ * -1     an error occured and the error string is set
+ * other  the vname index number
+ *
+ * *eaten is incremented only when a vname is found.
+ */
+int rrd_parse_find_vname(
+    const char *const line,
+    unsigned int *const eaten,
+    graph_desc_t *const gdp,
+    image_desc_t *const im)
+{
+    char      tmpstr[MAX_VNAME_LEN + 1];
+    int       i;
+    long      vidx;
+
+    i = 0;
+    sscanf(&line[*eaten], DEF_NAM_FMT "%n", tmpstr, &i);
+    if (!i) {
+        rrd_set_error("Could not parse line '%s'", line);
+        return -1;
+    }
+    if (line[*eaten + i] != ':' && line[*eaten + i] != '\0') {
+        rrd_set_error("Could not parse line '%s'", line);
+        return -1;
+    }
+    dprintf("- Considering '%s'\n", tmpstr);
+
+    if ((vidx = find_var(im, tmpstr)) < 0) {
+        dprintf("- Not a vname\n");
+        rrd_set_error("Not a valid vname: %s in line %s", tmpstr, line);
+        return -1;
+    }
+    dprintf("- Found vname '%s' vidx '%li'\n", tmpstr, gdp->vidx);
+    if (line[*eaten + i] == ':')
+        i++;
+    (*eaten) += i;
+    return vidx;
+}
+
+/* Parsing old-style xPRINT and new-style xPRINT */
+int rrd_parse_print(
+    const char *const line,
+    unsigned int *const eaten,
+    graph_desc_t *const gdp,
+    image_desc_t *const im)
+{
+    /* vname:CF:format in case of DEF-based vname
+     ** vname:CF:format in case of CDEF-based vname
+     ** vname:format[:strftime] in case of VDEF-based vname
+     */
+    if ((gdp->vidx = rrd_parse_find_vname(line, eaten, gdp, im)) < 0)
+        return 1;
+
+    switch (im->gdes[gdp->vidx].gf) {
+    case GF_DEF:
+    case GF_CDEF:
+        dprintf("- vname is of type DEF or CDEF, looking for CF\n");
+        if (rrd_parse_CF(line, eaten, gdp, &gdp->cf))
+            return 1;
+        break;
+    case GF_VDEF:
+        dprintf("- vname is of type VDEF\n");
+        break;
+    default:
+        rrd_set_error("Encountered unknown type variable '%s'",
+                      im->gdes[gdp->vidx].vname);
+        return 1;
+    }
+
+    if (rrd_parse_legend(line, eaten, gdp))
+        return 1;
+    /* for *PRINT the legend itself gets rendered later. We only
+       get the format at this juncture */
+    strcpy(gdp->format, gdp->legend);
+    gdp->legend[0] = '\0';
+    /* this is a very crud test, parsing :style flags should be in a function */
+    if (im->gdes[gdp->vidx].gf == GF_VDEF
+        && strcmp(line + (*eaten), ":strftime") == 0) {
+        gdp->strftm = 1;
+        (*eaten) += strlen(":strftime");
+    }
+    return 0;
+}
+
+/* SHIFT:_def_or_cdef:_vdef_or_number_
+ */
+int rrd_parse_shift(
+    const char *const line,
+    unsigned int *const eaten,
+    graph_desc_t *const gdp,
+    image_desc_t *const im)
+{
+    int       i;
+
+    if ((gdp->vidx = rrd_parse_find_vname(line, eaten, gdp, im)) < 0)
+        return 1;
+
+    switch (im->gdes[gdp->vidx].gf) {
+    case GF_DEF:
+    case GF_CDEF:
+        dprintf("- vname is of type DEF or CDEF, OK\n");
+        break;
+    case GF_VDEF:
+        rrd_set_error("Cannot shift a VDEF: '%s' in line '%s'\n",
+                      im->gdes[gdp->vidx].vname, line);
+        return 1;
+    default:
+        rrd_set_error("Encountered unknown type variable '%s' in line '%s'",
+                      im->gdes[gdp->vidx].vname, line);
+        return 1;
+    }
+
+    if ((gdp->shidx = rrd_parse_find_vname(line, eaten, gdp, im)) >= 0) {
+        switch (im->gdes[gdp->shidx].gf) {
+        case GF_DEF:
+        case GF_CDEF:
+            rrd_set_error("Offset cannot be a (C)DEF: '%s' in line '%s'\n",
+                          im->gdes[gdp->shidx].vname, line);
+            return 1;
+        case GF_VDEF:
+            dprintf("- vname is of type VDEF, OK\n");
+            break;
+        default:
+            rrd_set_error
+                ("Encountered unknown type variable '%s' in line '%s'",
+                 im->gdes[gdp->vidx].vname, line);
+            return 1;
+        }
+    } else {
+        long      time_tmp = 0;
+
+        rrd_clear_error();
+        i = 0;
+        sscanf(&line[*eaten], "%li%n", &time_tmp, &i);
+        gdp->shval = time_tmp;
+        if (i != (int) strlen(&line[*eaten])) {
+            rrd_set_error("Not a valid offset: %s in line %s", &line[*eaten],
+                          line);
+            return 1;
+        }
+        (*eaten) += i;
+        dprintf("- offset is number %li\n", gdp->shval);
+        gdp->shidx = -1;
+    }
+    return 0;
+}
+
+/* XPORT:_def_or_cdef[:legend]
+ */
+int rrd_parse_xport(
+    const char *const line,
+    unsigned int *const eaten,
+    graph_desc_t *const gdp,
+    image_desc_t *const im)
+{
+    if ((gdp->vidx = rrd_parse_find_vname(line, eaten, gdp, im)) < 0)
+        return 1;
+
+    switch (im->gdes[gdp->vidx].gf) {
+    case GF_DEF:
+    case GF_CDEF:
+        dprintf("- vname is of type DEF or CDEF, OK\n");
+        break;
+    case GF_VDEF:
+        rrd_set_error("Cannot xport a VDEF: '%s' in line '%s'\n",
+                      im->gdes[gdp->vidx].vname, line);
+        return 1;
+    default:
+        rrd_set_error("Encountered unknown type variable '%s' in line '%s'",
+                      im->gdes[gdp->vidx].vname, line);
+        return 1;
+    }
+    dprintf("- looking for legend in '%s'\n", &line[*eaten]);
+    if (rrd_parse_legend(line, eaten, gdp))
+        return 1;
+    return 0;
+}
+
+int rrd_parse_textalign(
+    const char *const line,
+    unsigned int *const eaten,
+    graph_desc_t *const gdp)
+{
+    if (strcmp(&line[*eaten], "left") == 0) {
+        gdp->txtalign = TXA_LEFT;
+    } else if (strcmp(&line[*eaten], "right") == 0) {
+        gdp->txtalign = TXA_RIGHT;
+    } else if (strcmp(&line[*eaten], "justified") == 0) {
+        gdp->txtalign = TXA_JUSTIFIED;
+    } else if (strcmp(&line[*eaten], "center") == 0) {
+        gdp->txtalign = TXA_CENTER;
+    } else {
+        rrd_set_error("Unknown alignement type '%s'", &line[*eaten]);
+        return 1;
+    }
+    *eaten += strlen(&line[*eaten]);
+    return 0;
+}
+
+
+/* Parsing of VRULE, HRULE, LINE, AREA, STACK and TICK
+** is done in one function.
+**
+** Stacking VRULE, HRULE or TICK is not allowed.
+**
+** If a number (which is valid to enter) is more than a
+** certain amount of characters, it is caught as an error.
+** While this is arguable, so is entering fixed numbers
+** with more than MAX_VNAME_LEN significant digits.
+*/
+int rrd_parse_PVHLAST(
+    const char *const line,
+    unsigned int *const eaten,
+    graph_desc_t *const gdp,
+    image_desc_t *const im)
+{
+    int       i, j, k;
+    int       colorfound = 0;
+    char      tmpstr[MAX_VNAME_LEN + 10];   /* vname#RRGGBBAA\0 */
+    static int spacecnt = 0;
+
+    if (spacecnt == 0) {
+        float     one_space = gfx_get_text_width(im, 0,
+                                                 im->
+                                                 text_prop[TEXT_PROP_LEGEND].
+                                                 font_desc,
+                                                 im->tabwidth, "    ") / 4.0;
+        float     target_space = gfx_get_text_width(im, 0,
+                                                    im->
+                                                    text_prop
+                                                    [TEXT_PROP_LEGEND].font_desc,
+                                                    im->tabwidth, "oo");
+
+        spacecnt = target_space / one_space;
+        dprintf("- spacecnt: %i onespace: %f targspace: %f\n", spacecnt,
+                one_space, target_space);
+    }
+
+
+    dprintf("- parsing '%s'\n", &line[*eaten]);
+
+    /* have simpler code in the drawing section */
+    if (gdp->gf == GF_STACK) {
+        gdp->stack = 1;
+    }
+
+    i = scan_for_col(&line[*eaten], MAX_VNAME_LEN + 9, tmpstr);
+    if (line[*eaten + i] != '\0' && line[*eaten + i] != ':') {
+        rrd_set_error("Cannot parse line '%s'", line);
+        return 1;
+    }
+
+    j = i;
+    while (j > 0 && tmpstr[j] != '#')
+        j--;
+
+    if (j) {
+        tmpstr[j] = '\0';
+    }
+    /* We now have:
+     * tmpstr[0]    containing vname
+     * tmpstr[j]    if j!=0 then containing color
+     * i            size of vname + color
+     * j            if j!=0 then size of vname
+     */
+
+    /* Number or vname ?
+     * If it is an existing vname, that's OK, provided that it is a
+     * valid type (need time for VRULE, not a float)
+     * Else see if it parses as a number.
+     */
+    dprintf("- examining string '%s'\n", tmpstr);
+    if ((gdp->vidx = find_var(im, tmpstr)) >= 0) {
+        dprintf("- found vname: '%s' vidx %li\n", tmpstr, gdp->vidx);
+        switch (gdp->gf) {
+        case GF_VRULE:
+        case GF_HRULE:
+            if (im->gdes[gdp->vidx].gf != GF_VDEF) {
+                rrd_set_error("Using vname %s of wrong type in line %s\n",
+                              im->gdes[gdp->gf].vname, line);
+                return 1;
+            }
+            break;
+        default:;
+        }
+    } else {
+        long      time_tmp = 0;
+
+        dprintf("- it is not an existing vname\n");
+        switch (gdp->gf) {
+        case GF_VRULE:
+            k = 0;
+            sscanf(tmpstr, "%li%n", &time_tmp, &k);
+            gdp->xrule = time_tmp;
+            if (((j != 0) && (k == j)) || ((j == 0) && (k == i))) {
+                dprintf("- found time: %li\n", gdp->xrule);
+            } else {
+                dprintf("- is is not a valid number: %li\n", gdp->xrule);
+                rrd_set_error
+                    ("parameter '%s' does not represent time in line %s\n",
+                     tmpstr, line);
+                return 1;
+            }
+        default:
+            k = 0;
+            sscanf(tmpstr, "%lf%n", &gdp->yrule, &k);
+            if (((j != 0) && (k == j)) || ((j == 0) && (k == i))) {
+                dprintf("- found number: %lf\n", gdp->yrule);
+            } else {
+                dprintf("- is is not a valid number: %lf\n", gdp->yrule);
+                rrd_set_error
+                    ("parameter '%s' does not represent a number in line %s\n",
+                     tmpstr, line);
+                return 1;
+            }
+        }
+    }
+
+    if (j) {
+        j++;
+        dprintf("- examining color '%s'\n", &tmpstr[j]);
+        if (rrd_parse_color(&tmpstr[j], gdp)) {
+            rrd_set_error("Could not parse color in '%s'", &tmpstr[j]);
+            return 1;
+        }
+        dprintf("- parsed color %0.0f,%0.0f,%0.0f,%0.0f\n", gdp->col.red,
+                gdp->col.green, gdp->col.blue, gdp->col.alpha);
+        colorfound = 1;
+    } else {
+        dprintf("- no color present in '%s'\n", tmpstr);
+    }
+
+    (*eaten) += i;      /* after vname#color */
+    if (line[*eaten] != '\0') {
+        (*eaten)++;     /* after colon */
+    }
+
+    if (gdp->gf == GF_TICK) {
+        dprintf("- parsing '%s'\n", &line[*eaten]);
+        dprintf("- looking for optional TICK number\n");
+        j = 0;
+        sscanf(&line[*eaten], "%lf%n", &gdp->yrule, &j);
+        if (j) {
+            if (line[*eaten + j] != '\0' && line[*eaten + j] != ':') {
+                rrd_set_error("Cannot parse TICK fraction '%s'", line);
+                return 1;
+            }
+            dprintf("- found number %f\n", gdp->yrule);
+            if (gdp->yrule > 1.0 || gdp->yrule < -1.0) {
+                rrd_set_error("Tick factor should be <= 1.0");
+                return 1;
+            }
+            (*eaten) += j;
+        } else {
+            dprintf("- not found, defaulting to 0.1\n");
+            gdp->yrule = 0.1;
+        }
+        if (line[*eaten] == '\0') {
+            dprintf("- done parsing line\n");
+            return 0;
+        } else {
+            if (line[*eaten] == ':') {
+                (*eaten)++;
+            } else {
+                rrd_set_error("Can't make sense of that TICK line");
+                return 1;
+            }
+        }
+    }
+
+    dprintf("- parsing '%s'\n", &line[*eaten]);
+
+    /* Legend is next.  A legend without a color is an error.
+     ** Stacking an item without having a legend is OK however
+     ** then an empty legend should be specified.
+     **   LINE:val#color:STACK  means legend is string "STACK"
+     **   LINE:val#color::STACK means no legend, and do STACK
+     **   LINE:val:STACK        is an error (legend but no color)
+     **   LINE:val::STACK   means no legend, and do STACK
+     */
+    if (colorfound) {
+        int       err = 0;
+        char     *linecp = strdup(line);
+
+        dprintf("- looking for optional legend\n");
+
+        dprintf("- examining '%s'\n", &line[*eaten]);
+        if (linecp[*eaten] != '\0' && linecp[*eaten] != ':') {
+            int       spi;
+
+            /* If the legend is not empty, it has to be prefixed with spacecnt ' ' characters. This then gets
+             * replaced by the color box later on. */
+            for (spi = 0; spi < spacecnt && (*eaten) > 1; spi++) {
+                linecp[--(*eaten)] = ' ';
+            }
+        }
+
+        if (rrd_parse_legend(linecp, eaten, gdp))
+            err = 1;
+        free(linecp);
+        if (err)
+            return 1;
+
+        dprintf("- found legend '%s'\n", &gdp->legend[2]);
+    } else {
+        dprintf("- skipping empty legend\n");
+        if (line[*eaten] != '\0' && line[*eaten] != ':') {
+            rrd_set_error("Legend set but no color: %s", &line[*eaten]);
+            return 1;
+        }
+    }
+    if (line[*eaten] == '\0') {
+        dprintf("- done parsing line\n");
+        return 0;
+    }
+    (*eaten)++;         /* after colon */
+
+    /* HRULE, VRULE and TICK cannot be stacked. */
+    if ((gdp->gf != GF_HRULE)
+        && (gdp->gf != GF_VRULE)
+        && (gdp->gf != GF_TICK)) {
+
+        dprintf("- parsing '%s', looking for STACK\n", &line[*eaten]);
+        j = scan_for_col(&line[*eaten], 5, tmpstr);
+        if (!strcmp("STACK", tmpstr)) {
+            dprintf("- found STACK\n");
+            gdp->stack = 1;
+            (*eaten) += j;
+            if (line[*eaten] == ':') {
+                (*eaten) += 1;
+            } else if (line[*eaten] == '\0') {
+                dprintf("- done parsing line\n");
+                return 0;
+            } else {
+                dprintf("- found %s instead of just STACK\n", &line[*eaten]);
+                rrd_set_error("STACK expected but %s found", &line[*eaten]);
+                return 1;
+            }
+        } else
+            dprintf("- not STACKing\n");
+    }
+
+    dprintf("- still more, should be dashes[=...]\n");
+    dprintf("- parsing '%s'\n", &line[*eaten]);
+    if (line[*eaten] != '\0') {
+        /* parse dash arguments here. Possible options:
+           - dashes
+           - dashes=n_on[,n_off[,n_on,n_off]]
+           - dashes[=n_on[,n_off[,n_on,n_off]]]:dash-offset=offset
+           allowing 64 characters for definition of dash style */
+        j = scan_for_col(&line[*eaten], 64, tmpstr);
+        /* start with dashes */
+        if (strcmp(tmpstr, "dashes") == 0) {
+            /* if line was "dashes" or "dashes:dash-offset=xdashes="
+               tmpstr will be "dashes" */
+            dprintf("- found %s\n", tmpstr);
+            /* initialise all required variables we need for dashed lines
+               using default dash length of 5 pixels */
+            gdp->dash = 1;
+            gdp->p_dashes = (double *) malloc(sizeof(double));
+            gdp->p_dashes[0] = 5;
+            gdp->ndash = 1;
+            gdp->offset = 0;
+            (*eaten) += j;
+        } else if (sscanf(tmpstr, "dashes=%s", tmpstr)) {
+            /* dashes=n_on[,n_off[,n_on,n_off]] */
+            char      csv[64];
+            char     *pch;
+            float     dsh;
+            int       count = 0;
+            char     *saveptr;
+
+            strcpy(csv, tmpstr);
+
+            pch = strtok_r(tmpstr, ",", &saveptr);
+            while (pch != NULL) {
+                pch = strtok_r(NULL, ",", &saveptr);
+                count++;
+            }
+            dprintf("- %d dash value(s) found: ", count);
+            if (count > 0) {
+                gdp->dash = 1;
+                gdp->ndash = count;
+                gdp->p_dashes = (double *) malloc(sizeof(double) * count);
+                pch = strtok_r(csv, ",", &saveptr);
+                count = 0;
+                while (pch != NULL) {
+                    if (sscanf(pch, "%f", &dsh)) {
+                        gdp->p_dashes[count] = (double) dsh;
+                        dprintf("%.1f ", gdp->p_dashes[count]);
+                        count++;
+                    }
+                    pch = strtok_r(NULL, ",", &saveptr);
+                }
+                dprintf("\n");
+            } else
+                dprintf("- syntax error. No dash lengths found!\n");
+            (*eaten) += j;
+        } else
+            dprintf("- error: expected dashes[=...], found %s\n", tmpstr);
+        if (line[*eaten] == ':') {
+            (*eaten) += 1;
+        } else if (line[*eaten] == '\0') {
+            dprintf("- done parsing line\n");
+            return 0;
+        }
+        /* dashes[=n_on[,n_off[,n_on,n_off]]]:dash-offset=offset
+           allowing 16 characters for dash-offset=....
+           => 4 characters for the offset value */
+        j = scan_for_col(&line[*eaten], 16, tmpstr);
+        if (sscanf(tmpstr, "dash-offset=%lf", &gdp->offset)) {
+            dprintf("- found dash-offset=%.1f\n", gdp->offset);
+            gdp->dash = 1;
+            (*eaten) += j;
+            if (line[*eaten] == ':')
+                (*eaten) += 1;
+        }
+        if (line[*eaten] == '\0') {
+            dprintf("- done parsing line\n");
+            return 0;
+        }
+    }
+    if (line[*eaten] == '\0') {
+        dprintf("- done parsing line\n");
+        return 0;
+    }
+    (*eaten)++;
+    dprintf("- parsing '%s'\n", &line[*eaten]);
+
+    return 0;
+}
+
+int rrd_parse_make_vname(
+    const char *const line,
+    unsigned int *const eaten,
+    graph_desc_t *const gdp,
+    image_desc_t *const im)
+{
+    char      tmpstr[MAX_VNAME_LEN + 10];
+    int       i = 0;
+
+    sscanf(&line[*eaten], DEF_NAM_FMT "=%n", tmpstr, &i);
+    if (!i) {
+        rrd_set_error("Cannot parse vname from '%s'", line);
+        return 1;
+    }
+    if (line[*eaten+i] == '\0') {
+        rrd_set_error("String ends after the = sign on '%s'", line);
+        return 1;
+    }
+    dprintf("- found candidate '%s'\n", tmpstr);
+
+    if ((gdp->vidx = find_var(im, tmpstr)) >= 0) {
+        rrd_set_error("Attempting to reuse '%s'", im->gdes[gdp->vidx].vname);
+        return 1;
+    }
+    strcpy(gdp->vname, tmpstr);
+    dprintf("- created vname '%s' vidx %lu\n", gdp->vname, im->gdes_c - 1);
+    (*eaten) += i;
+    return 0;
+}
+
+int rrd_parse_def(
+    const char *const line,
+    unsigned int *const eaten,
+    graph_desc_t *const gdp,
+    image_desc_t *const im)
+{
+    int       i = 0;
+    char      command[7];   /* step, start, end, reduce */
+    char      tmpstr[256];
+    rrd_time_value_t start_tv, end_tv;
+    time_t    start_tmp = 0, end_tmp = 0;
+    char     *parsetime_error = NULL;
+
+    start_tv.type = end_tv.type = ABSOLUTE_TIME;
+    start_tv.offset = end_tv.offset = 0;
+    localtime_r(&gdp->start, &start_tv.tm);
+    localtime_r(&gdp->end, &end_tv.tm);
+
+    dprintf("- parsing '%s'\n", &line[*eaten]);
+    dprintf("- from line '%s'\n", line);
+
+    if (rrd_parse_make_vname(line, eaten, gdp, im))
+        return 1;
+    i = scan_for_col(&line[*eaten], sizeof(gdp->rrd) - 1, gdp->rrd);
+    if (line[*eaten + i] != ':') {
+        rrd_set_error("Problems reading database name");
+        return 1;
+    }
+    (*eaten) += ++i;
+    dprintf("- using file '%s'\n", gdp->rrd);
+
+    i = 0;
+    sscanf(&line[*eaten], DS_NAM_FMT ":%n", gdp->ds_nam, &i);
+    if (!i) {
+        rrd_set_error("Cannot parse DS in '%s'", line);
+        return 1;
+    }
+    (*eaten) += i;
+    dprintf("- using DS '%s'\n", gdp->ds_nam);
+
+    if (rrd_parse_CF(line, eaten, gdp, &gdp->cf))
+        return 1;
+    gdp->cf_reduce = gdp->cf;
+
+    if (line[*eaten] == '\0')
+        return 0;
+
+    while (1) {
+        dprintf("- optional parameter follows: %s\n", &line[*eaten]);
+        i = 0;
+        sscanf(&line[*eaten], "%6[a-z]=%n", command, &i);
+        if (!i) {
+            rrd_set_error("Parse error in '%s'", line);
+            return 1;
+        }
+        (*eaten) += i;
+        dprintf("- processing '%s'\n", command);
+        if (!strcmp("reduce", command)) {
+            if (rrd_parse_CF(line, eaten, gdp, &gdp->cf_reduce))
+                return 1;
+            if (line[*eaten] != '\0')
+                (*eaten)--;
+        } else if (!strcmp("step", command)) {
+            i = 0;
+            sscanf(&line[*eaten], "%lu%n", &gdp->step, &i);
+            gdp->step_orig = gdp->step;
+            (*eaten) += i;
+            dprintf("- using step %lu\n", gdp->step);
+        } else if (!strcmp("start", command)) {
+            i = scan_for_col(&line[*eaten], 255, tmpstr);
+            (*eaten) += i;
+            if ((parsetime_error = rrd_parsetime(tmpstr, &start_tv))) {
+                rrd_set_error("start time: %s", parsetime_error);
+                return 1;
+            }
+            dprintf("- done parsing:  '%s'\n", &line[*eaten]);
+        } else if (!strcmp("end", command)) {
+            i = scan_for_col(&line[*eaten], 255, tmpstr);
+            (*eaten) += i;
+            if ((parsetime_error = rrd_parsetime(tmpstr, &end_tv))) {
+                rrd_set_error("end time: %s", parsetime_error);
+                return 1;
+            }
+            dprintf("- done parsing:  '%s'\n", &line[*eaten]);
+        } else {
+            rrd_set_error("Parse error in '%s'", line);
+            return 1;
+        }
+        if (line[*eaten] == '\0')
+            break;
+        if (line[*eaten] != ':') {
+            dprintf("- Expected to see end of string but got '%s'\n",
+                    &line[*eaten]);
+            rrd_set_error("Parse error in '%s'", line);
+            return 1;
+        }
+        (*eaten)++;
+    }
+    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) {
+        rrd_set_error("the first entry to fetch should be "
+                      "after 1980 (%ld)", start_tmp);
+        return 1;
+    }
+
+    if (end_tmp < start_tmp) {
+        rrd_set_error("start (%ld) should be less than end (%ld)",
+                      start_tmp, end_tmp);
+        return 1;
+    }
+
+    gdp->start = start_tmp;
+    gdp->end = end_tmp;
+    gdp->start_orig = start_tmp;
+    gdp->end_orig = end_tmp;
+
+    dprintf("- start time %lu\n", gdp->start);
+    dprintf("- end   time %lu\n", gdp->end);
+
+    return 0;
+}
+
+int rrd_parse_vdef(
+    const char *const line,
+    unsigned int *const eaten,
+    graph_desc_t *const gdp,
+    image_desc_t *const im)
+{
+    char      tmpstr[MAX_VNAME_LEN + 1];    /* vname\0 */
+    int       i = 0;
+
+    dprintf("- parsing '%s'\n", &line[*eaten]);
+    if (rrd_parse_make_vname(line, eaten, gdp, im))
+        return 1;
+
+    sscanf(&line[*eaten], DEF_NAM_FMT ",%n", tmpstr, &i);
+    if (!i) {
+        rrd_set_error("Cannot parse line '%s'", line);
+        return 1;
+    }
+    if ((gdp->vidx = find_var(im, tmpstr)) < 0) {
+        rrd_set_error("Not a valid vname: %s in line %s", tmpstr, line);
+        return 1;
+    }
+    if (im->gdes[gdp->vidx].gf != GF_DEF && im->gdes[gdp->vidx].gf != GF_CDEF) {
+        rrd_set_error("variable '%s' not DEF nor "
+                      "CDEF in VDEF '%s'", tmpstr, gdp->vname);
+        return 1;
+    }
+    dprintf("- found vname: '%s' vidx %li\n", tmpstr, gdp->vidx);
+    (*eaten) += i;
+
+    dprintf("- calling vdef_parse with param '%s'\n", &line[*eaten]);
+    vdef_parse(gdp, &line[*eaten]);
+    while (line[*eaten] != '\0' && line[*eaten] != ':')
+        (*eaten)++;
+
+    return 0;
+}
+
+int rrd_parse_cdef(
+    const char *const line,
+    unsigned int *const eaten,
+    graph_desc_t *const gdp,
+    image_desc_t *const im)
+{
+    dprintf("- parsing '%s'\n", &line[*eaten]);
+    if (rrd_parse_make_vname(line, eaten, gdp, im))
+        return 1;
+    if ((gdp->rpnp = rpn_parse((void *) im, &line[*eaten], &find_var_wrapper)
+        ) == NULL) {
+        rrd_set_error("invalid rpn expression in: %s", &line[*eaten]);
+        return 1;
+    };
+    while (line[*eaten] != '\0' && line[*eaten] != ':')
+        (*eaten)++;
+    return 0;
+}
+
+void rrd_graph_script(
+    int argc,
+    char *argv[],
+    image_desc_t *const im,
+    int optno)
+{
+    int       i;
+
+    /* save state for STACK backward compat function */
+    enum gf_en last_gf = GF_PRINT;
+    float     last_linewidth = 0.0;
+
+    for (i = optind + optno; i < argc; i++) {
+        graph_desc_t *gdp;
+        unsigned int eaten = 0;
+
+        if (gdes_alloc(im))
+            return;     /* the error string is already set */
+        gdp = &im->gdes[im->gdes_c - 1];
+#ifdef DEBUG
+        gdp->debug = 1;
+#endif
+
+        if (rrd_parse_find_gf(argv[i], &eaten, gdp))
+            return;
+
+        switch (gdp->gf) {
+        case GF_SHIFT: /* vname:value */
+            if (rrd_parse_shift(argv[i], &eaten, gdp, im))
+                return;
+            break;
+        case GF_TEXTALIGN: /* left|right|center|justified */
+            if (rrd_parse_textalign(argv[i], &eaten, gdp))
+                return;
+            break;
+        case GF_XPORT:
+            if (rrd_parse_xport(argv[i], &eaten, gdp, im))
+                return;
+            break;
+        case GF_PRINT: /* vname:CF:format -or- vname:format */
+            im->prt_c++;
+        case GF_GPRINT:    /* vname:CF:format -or- vname:format */
+            if (rrd_parse_print(argv[i], &eaten, gdp, im))
+                return;
+            break;
+        case GF_COMMENT:   /* text */
+            if (rrd_parse_legend(argv[i], &eaten, gdp))
+                return;
+            break;
+        case GF_VRULE: /* value#color[:legend] */
+        case GF_HRULE: /* value#color[:legend] */
+        case GF_LINE:  /* vname-or-value[#color[:legend]][:STACK] */
+        case GF_AREA:  /* vname-or-value[#color[:legend]][:STACK] */
+        case GF_TICK:  /* vname#color[:num[:legend]] */
+            if (rrd_parse_PVHLAST(argv[i], &eaten, gdp, im))
+                return;
+            last_gf = gdp->gf;
+            last_linewidth = gdp->linewidth;
+            break;
+        case GF_STACK: /* vname-or-value[#color[:legend]] */
+            if (rrd_parse_PVHLAST(argv[i], &eaten, gdp, im))
+                return;
+            if (last_gf == GF_LINE || last_gf == GF_AREA) {
+                gdp->gf = last_gf;
+                gdp->linewidth = last_linewidth;
+            } else {
+                rrd_set_error("STACK must follow LINE or AREA! command:\n%s",
+                              &argv[i][eaten], argv[i]);
+                return;
+            }
+            break;
+            /* data acquisition */
+        case GF_DEF:   /* vname=x:DS:CF:[:step=#][:start=#][:end=#] */
+            if (rrd_parse_def(argv[i], &eaten, gdp, im))
+                return;
+            break;
+        case GF_CDEF:  /* vname=rpn-expression */
+            if (rrd_parse_cdef(argv[i], &eaten, gdp, im))
+                return;
+            break;
+        case GF_VDEF:  /* vname=rpn-expression */
+            if (rrd_parse_vdef(argv[i], &eaten, gdp, im))
+                return;
+            break;
+        }
+        if (gdp->debug) {
+            dprintf("used %i out of %zi chars\n", eaten, strlen(argv[i]));
+            dprintf("parsed line: '%s'\n", argv[i]);
+            dprintf("remaining: '%s'\n", &argv[i][eaten]);
+            if (eaten >= strlen(argv[i]))
+                dprintf("Command finished successfully\n");
+        }
+        if (eaten < strlen(argv[i])) {
+            rrd_set_error("I don't understand '%s' in command: '%s'.",
+                          &argv[i][eaten], argv[i]);
+            return;
+        }
+    }
+}
diff --git a/program/src/rrd_hw.c b/program/src/rrd_hw.c
new file mode 100644 (file)
index 0000000..1eba57e
--- /dev/null
@@ -0,0 +1,560 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_hw.c : Support for Holt-Winters Smoothing/ Aberrant Behavior Detection
+ *****************************************************************************
+ * Initial version by Jake Brutlag, WebTV Networks, 5/1/00
+ *****************************************************************************/
+
+#include <stdlib.h>
+
+#include "rrd_tool.h"
+#include "rrd_hw.h"
+#include "rrd_hw_math.h"
+#include "rrd_hw_update.h"
+
+#define hw_dep_idx(rrd, rra_idx) rrd->rra_def[rra_idx].par[RRA_dependent_rra_idx].u_cnt
+
+/* #define DEBUG */
+
+/* private functions */
+static unsigned long MyMod(
+    signed long val,
+    unsigned long mod);
+
+int lookup_seasonal(
+    rrd_t *rrd,
+    unsigned long rra_idx,
+    unsigned long rra_start,
+    rrd_file_t *rrd_file,
+    unsigned long offset,
+    rrd_value_t **seasonal_coef)
+{
+    unsigned long pos_tmp;
+
+    /* rra_ptr[].cur_row points to the rra row to be written; this function
+     * reads cur_row + offset */
+    unsigned long row_idx = rrd->rra_ptr[rra_idx].cur_row + offset;
+
+    /* handle wrap around */
+    if (row_idx >= rrd->rra_def[rra_idx].row_cnt)
+        row_idx = row_idx % (rrd->rra_def[rra_idx].row_cnt);
+
+    /* rra_start points to the appropriate rra block in the file */
+    /* compute the pointer to the appropriate location in the file */
+    pos_tmp =
+        rra_start +
+        (row_idx) * (rrd->stat_head->ds_cnt) * sizeof(rrd_value_t);
+
+    /* allocate memory if need be */
+    if (*seasonal_coef == NULL)
+        *seasonal_coef =
+            (rrd_value_t *) malloc((rrd->stat_head->ds_cnt) *
+                                   sizeof(rrd_value_t));
+    if (*seasonal_coef == NULL) {
+        rrd_set_error("memory allocation failure: seasonal coef");
+        return -1;
+    }
+
+    if (!rrd_seek(rrd_file, pos_tmp, SEEK_SET)) {
+        if (rrd_read
+            (rrd_file, *seasonal_coef,
+             sizeof(rrd_value_t) * rrd->stat_head->ds_cnt)
+            == (ssize_t) (sizeof(rrd_value_t) * rrd->stat_head->ds_cnt)) {
+            /* success! */
+            /* we can safely ignore the rule requiring a seek operation between read
+             * and write, because this read moves the file pointer to somewhere
+             * in the file other than the next write location.
+             * */
+            return 0;
+        } else {
+            rrd_set_error("read operation failed in lookup_seasonal(): %lu\n",
+                          pos_tmp);
+        }
+    } else {
+        rrd_set_error("seek operation failed in lookup_seasonal(): %lu\n",
+                      pos_tmp);
+    }
+
+    return -1;
+}
+
+/* For the specified CDP prep area and the FAILURES RRA,
+ * erase all history of past violations.
+ */
+void erase_violations(
+    rrd_t *rrd,
+    unsigned long cdp_idx,
+    unsigned long rra_idx)
+{
+    unsigned short i;
+    char     *violations_array;
+
+    /* check that rra_idx is a CF_FAILURES array */
+    if (cf_conv(rrd->rra_def[rra_idx].cf_nam) != CF_FAILURES) {
+#ifdef DEBUG
+        fprintf(stderr, "erase_violations called for non-FAILURES RRA: %s\n",
+                rrd->rra_def[rra_idx].cf_nam);
+#endif
+        return;
+    }
+#ifdef DEBUG
+    fprintf(stderr, "scratch buffer before erase:\n");
+    for (i = 0; i < MAX_CDP_PAR_EN; i++) {
+        fprintf(stderr, "%lu ", rrd->cdp_prep[cdp_idx].scratch[i].u_cnt);
+    }
+    fprintf(stderr, "\n");
+#endif
+
+    /* WARNING: an array of longs on disk is treated as an array of chars
+     * in memory. */
+    violations_array = (char *) ((void *) rrd->cdp_prep[cdp_idx].scratch);
+    /* erase everything in the part of the CDP scratch array that will be
+     * used to store violations for the current window */
+    for (i = rrd->rra_def[rra_idx].par[RRA_window_len].u_cnt; i > 0; i--) {
+        violations_array[i - 1] = 0;
+    }
+#ifdef DEBUG
+    fprintf(stderr, "scratch buffer after erase:\n");
+    for (i = 0; i < MAX_CDP_PAR_EN; i++) {
+        fprintf(stderr, "%lu ", rrd->cdp_prep[cdp_idx].scratch[i].u_cnt);
+    }
+    fprintf(stderr, "\n");
+#endif
+}
+
+/* Smooth a periodic array with a moving average: equal weights and
+ * length = 5% of the period. */
+int apply_smoother(
+    rrd_t *rrd,
+    unsigned long rra_idx,
+    unsigned long rra_start,
+    rrd_file_t *rrd_file)
+{
+    unsigned long i, j, k;
+    unsigned long totalbytes;
+    rrd_value_t *rrd_values;
+    unsigned long row_length = rrd->stat_head->ds_cnt;
+    unsigned long row_count = rrd->rra_def[rra_idx].row_cnt;
+    unsigned long offset;
+    FIFOqueue **buffers;
+    rrd_value_t *working_average;
+    rrd_value_t *baseline;
+
+    if (atoi(rrd->stat_head->version) >= 4) {
+        offset = floor(rrd->rra_def[rra_idx].
+                       par[RRA_seasonal_smoothing_window].
+                       u_val / 2 * row_count);
+    } else {
+        offset = floor(0.05 / 2 * row_count);
+    }
+
+    if (offset == 0)
+        return 0;       /* no smoothing */
+
+    /* allocate memory */
+    totalbytes = sizeof(rrd_value_t) * row_length * row_count;
+    rrd_values = (rrd_value_t *) malloc(totalbytes);
+    if (rrd_values == NULL) {
+        rrd_set_error("apply smoother: memory allocation failure");
+        return -1;
+    }
+
+    /* rra_start is at the beginning of this rra */
+    if (rrd_seek(rrd_file, rra_start, SEEK_SET)) {
+        rrd_set_error("seek to rra %d failed", rra_start);
+        free(rrd_values);
+        return -1;
+    }
+
+    /* could read all data in a single block, but we need to
+     * check for NA values */
+    for (i = 0; i < row_count; ++i) {
+        for (j = 0; j < row_length; ++j) {
+            if (rrd_read
+                (rrd_file, &(rrd_values[i * row_length + j]),
+                 sizeof(rrd_value_t) * 1)
+                != (ssize_t) (sizeof(rrd_value_t) * 1)) {
+                rrd_set_error("reading value failed: %s",
+                              rrd_strerror(errno));
+            }
+            if (isnan(rrd_values[i * row_length + j])) {
+                /* can't apply smoothing, still uninitialized values */
+#ifdef DEBUG
+                fprintf(stderr,
+                        "apply_smoother: NA detected in seasonal array: %ld %ld\n",
+                        i, j);
+#endif
+                free(rrd_values);
+                return 0;
+            }
+        }
+    }
+
+    /* allocate queues, one for each data source */
+    buffers = (FIFOqueue **) malloc(sizeof(FIFOqueue *) * row_length);
+    for (i = 0; i < row_length; ++i) {
+        queue_alloc(&(buffers[i]), 2 * offset + 1);
+    }
+    /* need working average initialized to 0 */
+    working_average = (rrd_value_t *) calloc(row_length, sizeof(rrd_value_t));
+    baseline = (rrd_value_t *) calloc(row_length, sizeof(rrd_value_t));
+
+    /* compute sums of the first 2*offset terms */
+    for (i = 0; i < 2 * offset; ++i) {
+        k = MyMod(i - offset, row_count);
+        for (j = 0; j < row_length; ++j) {
+            queue_push(buffers[j], rrd_values[k * row_length + j]);
+            working_average[j] += rrd_values[k * row_length + j];
+        }
+    }
+
+    /* compute moving averages */
+    for (i = offset; i < row_count + offset; ++i) {
+        for (j = 0; j < row_length; ++j) {
+            k = MyMod(i, row_count);
+            /* add a term to the sum */
+            working_average[j] += rrd_values[k * row_length + j];
+            queue_push(buffers[j], rrd_values[k * row_length + j]);
+
+            /* reset k to be the center of the window */
+            k = MyMod(i - offset, row_count);
+            /* overwrite rdd_values entry, the old value is already
+             * saved in buffers */
+            rrd_values[k * row_length + j] =
+                working_average[j] / (2 * offset + 1);
+            baseline[j] += rrd_values[k * row_length + j];
+
+            /* remove a term from the sum */
+            working_average[j] -= queue_pop(buffers[j]);
+        }
+    }
+
+    for (i = 0; i < row_length; ++i) {
+        queue_dealloc(buffers[i]);
+        baseline[i] /= row_count;
+    }
+    free(buffers);
+    free(working_average);
+
+    if (cf_conv(rrd->rra_def[rra_idx].cf_nam) == CF_SEASONAL) {
+        rrd_value_t (
+    *init_seasonality) (
+    rrd_value_t seasonal_coef,
+    rrd_value_t intercept);
+
+        switch (cf_conv(rrd->rra_def[hw_dep_idx(rrd, rra_idx)].cf_nam)) {
+        case CF_HWPREDICT:
+            init_seasonality = hw_additive_init_seasonality;
+            break;
+        case CF_MHWPREDICT:
+            init_seasonality = hw_multiplicative_init_seasonality;
+            break;
+        default:
+            rrd_set_error("apply smoother: SEASONAL rra doesn't have "
+                          "valid dependency: %s",
+                          rrd->rra_def[hw_dep_idx(rrd, rra_idx)].cf_nam);
+            return -1;
+        }
+
+        for (j = 0; j < row_length; ++j) {
+            for (i = 0; i < row_count; ++i) {
+                rrd_values[i * row_length + j] =
+                    init_seasonality(rrd_values[i * row_length + j],
+                                     baseline[j]);
+            }
+            /* update the baseline coefficient,
+             * first, compute the cdp_index. */
+            offset = hw_dep_idx(rrd, rra_idx) * row_length + j;
+            (rrd->cdp_prep[offset]).scratch[CDP_hw_intercept].u_val +=
+                baseline[j];
+        }
+        /* flush cdp to disk */
+        if (rrd_seek(rrd_file, sizeof(stat_head_t) +
+                     rrd->stat_head->ds_cnt * sizeof(ds_def_t) +
+                     rrd->stat_head->rra_cnt * sizeof(rra_def_t) +
+                     sizeof(live_head_t) +
+                     rrd->stat_head->ds_cnt * sizeof(pdp_prep_t), SEEK_SET)) {
+            rrd_set_error("apply_smoother: seek to cdp_prep failed");
+            free(rrd_values);
+            return -1;
+        }
+        if (rrd_write(rrd_file, rrd->cdp_prep,
+                      sizeof(cdp_prep_t) *
+                      (rrd->stat_head->rra_cnt) * rrd->stat_head->ds_cnt)
+            != (ssize_t) (sizeof(cdp_prep_t) * (rrd->stat_head->rra_cnt) *
+                          (rrd->stat_head->ds_cnt))) {
+            rrd_set_error("apply_smoother: cdp_prep write failed");
+            free(rrd_values);
+            return -1;
+        }
+    }
+
+    /* endif CF_SEASONAL */
+    /* flush updated values to disk */
+    if (rrd_seek(rrd_file, rra_start, SEEK_SET)) {
+        rrd_set_error("apply_smoother: seek to pos %d failed", rra_start);
+        free(rrd_values);
+        return -1;
+    }
+    /* write as a single block */
+    if (rrd_write
+        (rrd_file, rrd_values, sizeof(rrd_value_t) * row_length * row_count)
+        != (ssize_t) (sizeof(rrd_value_t) * row_length * row_count)) {
+        rrd_set_error("apply_smoother: write failed to %lu", rra_start);
+        free(rrd_values);
+        return -1;
+    }
+
+    free(rrd_values);
+    free(baseline);
+    return 0;
+}
+
+/* Reset aberrant behavior model coefficients, including intercept, slope,
+ * seasonal, and seasonal deviation for the specified data source. */
+void reset_aberrant_coefficients(
+    rrd_t *rrd,
+    rrd_file_t *rrd_file,
+    unsigned long ds_idx)
+{
+    unsigned long cdp_idx, rra_idx, i;
+    unsigned long cdp_start, rra_start;
+    rrd_value_t nan_buffer = DNAN;
+
+    /* compute the offset for the cdp area */
+    cdp_start = sizeof(stat_head_t) +
+        rrd->stat_head->ds_cnt * sizeof(ds_def_t) +
+        rrd->stat_head->rra_cnt * sizeof(rra_def_t) +
+        sizeof(live_head_t) + rrd->stat_head->ds_cnt * sizeof(pdp_prep_t);
+    /* compute the offset for the first rra */
+    rra_start = cdp_start +
+        (rrd->stat_head->ds_cnt) * (rrd->stat_head->rra_cnt) *
+        sizeof(cdp_prep_t) + rrd->stat_head->rra_cnt * sizeof(rra_ptr_t);
+
+    /* loop over the RRAs */
+    for (rra_idx = 0; rra_idx < rrd->stat_head->rra_cnt; rra_idx++) {
+        cdp_idx = rra_idx * (rrd->stat_head->ds_cnt) + ds_idx;
+        switch (cf_conv(rrd->rra_def[rra_idx].cf_nam)) {
+        case CF_HWPREDICT:
+        case CF_MHWPREDICT:
+            init_hwpredict_cdp(&(rrd->cdp_prep[cdp_idx]));
+            break;
+        case CF_SEASONAL:
+        case CF_DEVSEASONAL:
+            /* don't use init_seasonal because it will reset burn-in, which
+             * means different data sources will be calling for the smoother
+             * at different times. */
+            rrd->cdp_prep[cdp_idx].scratch[CDP_hw_seasonal].u_val = DNAN;
+            rrd->cdp_prep[cdp_idx].scratch[CDP_hw_last_seasonal].u_val = DNAN;
+            /* move to first entry of data source for this rra */
+            rrd_seek(rrd_file, rra_start + ds_idx * sizeof(rrd_value_t),
+                     SEEK_SET);
+            /* entries for the same data source are not contiguous, 
+             * temporal entries are contiguous */
+            for (i = 0; i < rrd->rra_def[rra_idx].row_cnt; ++i) {
+                if (rrd_write(rrd_file, &nan_buffer, sizeof(rrd_value_t) * 1)
+                    != sizeof(rrd_value_t) * 1) {
+                    rrd_set_error
+                        ("reset_aberrant_coefficients: write failed data source %lu rra %s",
+                         ds_idx, rrd->rra_def[rra_idx].cf_nam);
+                    return;
+                }
+                rrd_seek(rrd_file, (rrd->stat_head->ds_cnt - 1) *
+                         sizeof(rrd_value_t), SEEK_CUR);
+            }
+            break;
+        case CF_FAILURES:
+            erase_violations(rrd, cdp_idx, rra_idx);
+            break;
+        default:
+            break;
+        }
+        /* move offset to the next rra */
+        rra_start += rrd->rra_def[rra_idx].row_cnt * rrd->stat_head->ds_cnt *
+            sizeof(rrd_value_t);
+    }
+    rrd_seek(rrd_file, cdp_start, SEEK_SET);
+    if (rrd_write(rrd_file, rrd->cdp_prep,
+                  sizeof(cdp_prep_t) *
+                  (rrd->stat_head->rra_cnt) * rrd->stat_head->ds_cnt)
+        != (ssize_t) (sizeof(cdp_prep_t) * (rrd->stat_head->rra_cnt) *
+                      (rrd->stat_head->ds_cnt))) {
+        rrd_set_error("reset_aberrant_coefficients: cdp_prep write failed");
+    }
+}
+
+void init_hwpredict_cdp(
+    cdp_prep_t *cdp)
+{
+    cdp->scratch[CDP_hw_intercept].u_val = DNAN;
+    cdp->scratch[CDP_hw_last_intercept].u_val = DNAN;
+    cdp->scratch[CDP_hw_slope].u_val = DNAN;
+    cdp->scratch[CDP_hw_last_slope].u_val = DNAN;
+    cdp->scratch[CDP_null_count].u_cnt = 1;
+    cdp->scratch[CDP_last_null_count].u_cnt = 1;
+}
+
+void init_seasonal_cdp(
+    cdp_prep_t *cdp)
+{
+    cdp->scratch[CDP_hw_seasonal].u_val = DNAN;
+    cdp->scratch[CDP_hw_last_seasonal].u_val = DNAN;
+    cdp->scratch[CDP_init_seasonal].u_cnt = 1;
+}
+
+int update_aberrant_CF(
+    rrd_t *rrd,
+    rrd_value_t pdp_val,
+    enum cf_en current_cf,
+    unsigned long cdp_idx,
+    unsigned long rra_idx,
+    unsigned long ds_idx,
+    unsigned short CDP_scratch_idx,
+    rrd_value_t *seasonal_coef)
+{
+    static hw_functions_t hw_multiplicative_functions = {
+        hw_multiplicative_calculate_prediction,
+        hw_multiplicative_calculate_intercept,
+        hw_calculate_slope,
+        hw_multiplicative_calculate_seasonality,
+        hw_multiplicative_init_seasonality,
+        hw_calculate_seasonal_deviation,
+        hw_init_seasonal_deviation,
+        1.0             /* identity value */
+    };
+
+    static hw_functions_t hw_additive_functions = {
+        hw_additive_calculate_prediction,
+        hw_additive_calculate_intercept,
+        hw_calculate_slope,
+        hw_additive_calculate_seasonality,
+        hw_additive_init_seasonality,
+        hw_calculate_seasonal_deviation,
+        hw_init_seasonal_deviation,
+        0.0             /* identity value  */
+    };
+
+    rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = pdp_val;
+    switch (current_cf) {
+    case CF_HWPREDICT:
+        return update_hwpredict(rrd, cdp_idx, rra_idx, ds_idx,
+                                CDP_scratch_idx, &hw_additive_functions);
+    case CF_MHWPREDICT:
+        return update_hwpredict(rrd, cdp_idx, rra_idx, ds_idx,
+                                CDP_scratch_idx,
+                                &hw_multiplicative_functions);
+    case CF_DEVPREDICT:
+        return update_devpredict(rrd, cdp_idx, rra_idx, ds_idx,
+                                 CDP_scratch_idx);
+    case CF_SEASONAL:
+        switch (cf_conv(rrd->rra_def[hw_dep_idx(rrd, rra_idx)].cf_nam)) {
+        case CF_HWPREDICT:
+            return update_seasonal(rrd, cdp_idx, rra_idx, ds_idx,
+                                   CDP_scratch_idx, seasonal_coef,
+                                   &hw_additive_functions);
+        case CF_MHWPREDICT:
+            return update_seasonal(rrd, cdp_idx, rra_idx, ds_idx,
+                                   CDP_scratch_idx, seasonal_coef,
+                                   &hw_multiplicative_functions);
+        default:
+            return -1;
+        }
+    case CF_DEVSEASONAL:
+        switch (cf_conv(rrd->rra_def[hw_dep_idx(rrd, rra_idx)].cf_nam)) {
+        case CF_HWPREDICT:
+            return update_devseasonal(rrd, cdp_idx, rra_idx, ds_idx,
+                                      CDP_scratch_idx, seasonal_coef,
+                                      &hw_additive_functions);
+        case CF_MHWPREDICT:
+            return update_devseasonal(rrd, cdp_idx, rra_idx, ds_idx,
+                                      CDP_scratch_idx, seasonal_coef,
+                                      &hw_multiplicative_functions);
+        default:
+            return -1;
+        }
+    case CF_FAILURES:
+        switch (cf_conv
+                (rrd->rra_def[hw_dep_idx(rrd, hw_dep_idx(rrd, rra_idx))].
+                 cf_nam)) {
+        case CF_HWPREDICT:
+            return update_failures(rrd, cdp_idx, rra_idx, ds_idx,
+                                   CDP_scratch_idx, &hw_additive_functions);
+        case CF_MHWPREDICT:
+            return update_failures(rrd, cdp_idx, rra_idx, ds_idx,
+                                   CDP_scratch_idx,
+                                   &hw_multiplicative_functions);
+        default:
+            return -1;
+        }
+    case CF_AVERAGE:
+    default:
+        return 0;
+    }
+    return -1;
+}
+
+static unsigned long MyMod(
+    signed long val,
+    unsigned long mod)
+{
+    unsigned long new_val;
+
+    if (val < 0)
+        new_val = ((unsigned long) abs(val)) % mod;
+    else
+        new_val = (val % mod);
+
+    if (val < 0)
+        return (mod - new_val);
+    else
+        return (new_val);
+}
+
+/* a standard fixed-capacity FIF0 queue implementation
+ * No overflow checking is performed. */
+int queue_alloc(
+    FIFOqueue **q,
+    int capacity)
+{
+    *q = (FIFOqueue *) malloc(sizeof(FIFOqueue));
+    if (*q == NULL)
+        return -1;
+    (*q)->queue = (rrd_value_t *) malloc(sizeof(rrd_value_t) * capacity);
+    if ((*q)->queue == NULL) {
+        free(*q);
+        return -1;
+    }
+    (*q)->capacity = capacity;
+    (*q)->head = capacity;
+    (*q)->tail = 0;
+    return 0;
+}
+
+int queue_isempty(
+    FIFOqueue *q)
+{
+    return (q->head % q->capacity == q->tail);
+}
+
+void queue_push(
+    FIFOqueue *q,
+    rrd_value_t value)
+{
+    q->queue[(q->tail)++] = value;
+    q->tail = q->tail % q->capacity;
+}
+
+rrd_value_t queue_pop(
+    FIFOqueue *q)
+{
+    q->head = q->head % q->capacity;
+    return q->queue[(q->head)++];
+}
+
+void queue_dealloc(
+    FIFOqueue *q)
+{
+    free(q->queue);
+    free(q);
+}
diff --git a/program/src/rrd_hw.h b/program/src/rrd_hw.h
new file mode 100644 (file)
index 0000000..a24834d
--- /dev/null
@@ -0,0 +1,65 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_hw.h : Support for Holt-Winters Smoothing/ Aberrant Behavior Detection
+ *****************************************************************************/
+
+/* functions implemented in rrd_hw.c */
+int       update_aberrant_CF(
+    rrd_t *rrd,
+    rrd_value_t pdp_val,
+    enum cf_en current_cf,
+    unsigned long cdp_idx,
+    unsigned long rra_idx,
+    unsigned long ds_idx,
+    unsigned short CDP_scratch_idx,
+    rrd_value_t *seasonal_coef);
+int       create_hw_contingent_rras(
+    rrd_t *rrd,
+    unsigned short period,
+    unsigned long hashed_name);
+int       lookup_seasonal(
+    rrd_t *rrd,
+    unsigned long rra_idx,
+    unsigned long rra_start,
+    rrd_file_t *rrd_file,
+    unsigned long offset,
+    rrd_value_t **seasonal_coef);
+void      erase_violations(
+    rrd_t *rrd,
+    unsigned long cdp_idx,
+    unsigned long rra_idx);
+int       apply_smoother(
+    rrd_t *rrd,
+    unsigned long rra_idx,
+    unsigned long rra_start,
+    rrd_file_t *rrd_file);
+void      reset_aberrant_coefficients(
+    rrd_t *rrd,
+    rrd_file_t *rrd_file,
+    unsigned long ds_idx);
+void      init_hwpredict_cdp(
+    cdp_prep_t *);
+void      init_seasonal_cdp(
+    cdp_prep_t *);
+
+#define BURNIN_CYCLES 3
+
+/* a standard fixed-capacity FIFO queue implementation */
+typedef struct FIFOqueue {
+    rrd_value_t *queue;
+    int       capacity, head, tail;
+} FIFOqueue;
+
+int       queue_alloc(
+    FIFOqueue **q,
+    int capacity);
+void      queue_dealloc(
+    FIFOqueue *q);
+void      queue_push(
+    FIFOqueue *q,
+    rrd_value_t value);
+int       queue_isempty(
+    FIFOqueue *q);
+rrd_value_t queue_pop(
+    FIFOqueue *q);
diff --git a/program/src/rrd_hw_math.c b/program/src/rrd_hw_math.c
new file mode 100644 (file)
index 0000000..05fbf02
--- /dev/null
@@ -0,0 +1,143 @@
+/*****************************************************************************
+ * rrd_hw_math.c  Math functions for Holt-Winters computations
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_hw_math.h"
+
+/*****************************************************************************
+ * RRDtool supports both the additive and multiplicative Holt-Winters methods. 
+ * The additive method makes predictions by adding seasonality to the baseline, 
+ * whereas the multiplicative method multiplies the seasonality coefficient by 
+ * the baseline to make a prediction. This file contains all the differences
+ * between the additive and multiplicative methods, as well as a few math 
+ * functions common to them both.
+ ****************************************************************************/
+
+/*****************************************************************************
+ * Functions for additive Holt-Winters
+ *****************************************************************************/
+
+rrd_value_t hw_additive_calculate_prediction(
+    rrd_value_t intercept,
+    rrd_value_t slope,
+    int null_count,
+    rrd_value_t seasonal_coef)
+{
+    return intercept + slope * null_count + seasonal_coef;
+}
+
+rrd_value_t hw_additive_calculate_intercept(
+    rrd_value_t hw_alpha,
+    rrd_value_t observed,
+    rrd_value_t seasonal_coef,
+    unival *coefs)
+{
+    return hw_alpha * (observed - seasonal_coef)
+        + (1 - hw_alpha) * (coefs[CDP_hw_intercept].u_val
+                            +
+                            (coefs[CDP_hw_slope].u_val) *
+                            (coefs[CDP_null_count].u_cnt));
+}
+
+rrd_value_t hw_additive_calculate_seasonality(
+    rrd_value_t hw_gamma,
+    rrd_value_t observed,
+    rrd_value_t intercept,
+    rrd_value_t seasonal_coef)
+{
+    return hw_gamma * (observed - intercept)
+        + (1 - hw_gamma) * seasonal_coef;
+}
+
+rrd_value_t hw_additive_init_seasonality(
+    rrd_value_t seasonal_coef,
+    rrd_value_t intercept)
+{
+    return seasonal_coef - intercept;
+}
+
+/*****************************************************************************
+ * Functions for multiplicative Holt-Winters
+ *****************************************************************************/
+
+rrd_value_t hw_multiplicative_calculate_prediction(
+    rrd_value_t intercept,
+    rrd_value_t slope,
+    int null_count,
+    rrd_value_t seasonal_coef)
+{
+    return (intercept + slope * null_count) * seasonal_coef;
+}
+
+rrd_value_t hw_multiplicative_calculate_intercept(
+    rrd_value_t hw_alpha,
+    rrd_value_t observed,
+    rrd_value_t seasonal_coef,
+    unival *coefs)
+{
+    if (seasonal_coef <= 0) {
+        return DNAN;
+    }
+
+    return hw_alpha * (observed / seasonal_coef)
+        + (1 - hw_alpha) * (coefs[CDP_hw_intercept].u_val
+                            +
+                            (coefs[CDP_hw_slope].u_val) *
+                            (coefs[CDP_null_count].u_cnt));
+}
+
+rrd_value_t hw_multiplicative_calculate_seasonality(
+    rrd_value_t hw_gamma,
+    rrd_value_t observed,
+    rrd_value_t intercept,
+    rrd_value_t seasonal_coef)
+{
+    if (intercept <= 0) {
+        return DNAN;
+    }
+
+    return hw_gamma * (observed / intercept)
+        + (1 - hw_gamma) * seasonal_coef;
+}
+
+rrd_value_t hw_multiplicative_init_seasonality(
+    rrd_value_t seasonal_coef,
+    rrd_value_t intercept)
+{
+    if (intercept <= 0) {
+        return DNAN;
+    }
+
+    return seasonal_coef / intercept;
+}
+
+/*****************************************************************************
+ * Math functions common to additive and multiplicative Holt-Winters
+ *****************************************************************************/
+
+rrd_value_t hw_calculate_slope(
+    rrd_value_t hw_beta,
+    unival *coefs)
+{
+    return hw_beta * (coefs[CDP_hw_intercept].u_val -
+                      coefs[CDP_hw_last_intercept].u_val)
+        + (1 - hw_beta) * coefs[CDP_hw_slope].u_val;
+}
+
+rrd_value_t hw_calculate_seasonal_deviation(
+    rrd_value_t hw_gamma,
+    rrd_value_t prediction,
+    rrd_value_t observed,
+    rrd_value_t last)
+{
+    return hw_gamma * fabs(prediction - observed)
+        + (1 - hw_gamma) * last;
+}
+
+rrd_value_t hw_init_seasonal_deviation(
+    rrd_value_t prediction,
+    rrd_value_t observed)
+{
+    return fabs(prediction - observed);
+}
diff --git a/program/src/rrd_hw_math.h b/program/src/rrd_hw_math.h
new file mode 100644 (file)
index 0000000..3677b31
--- /dev/null
@@ -0,0 +1,132 @@
+/*****************************************************************************
+ * rrd_hw_math.h  Math functions for Holt-Winters computations
+ *****************************************************************************/
+
+#include "rrd.h"
+#include "rrd_format.h"
+
+/* since /usr/include/bits/mathcalls.h:265 defines gamma already */
+#define gamma hw_gamma
+
+/*****************************************************************************
+ * Functions for additive Holt-Winters
+ *****************************************************************************/
+
+rrd_value_t hw_additive_calculate_prediction(
+    rrd_value_t intercept,
+    rrd_value_t slope,
+    int null_count,
+    rrd_value_t seasonal_coef);
+
+rrd_value_t hw_additive_calculate_intercept(
+    rrd_value_t alpha,
+    rrd_value_t scratch,
+    rrd_value_t seasonal_coef,
+    unival *coefs);
+
+rrd_value_t hw_additive_calculate_seasonality(
+    rrd_value_t gamma,
+    rrd_value_t scratch,
+    rrd_value_t intercept,
+    rrd_value_t seasonal_coef);
+
+rrd_value_t hw_additive_init_seasonality(
+    rrd_value_t seasonal_coef,
+    rrd_value_t intercept);
+
+/*****************************************************************************
+ * Functions for multiplicative Holt-Winters
+ *****************************************************************************/
+
+rrd_value_t hw_multiplicative_calculate_prediction(
+    rrd_value_t intercept,
+    rrd_value_t slope,
+    int null_count,
+    rrd_value_t seasonal_coef);
+
+rrd_value_t hw_multiplicative_calculate_intercept(
+    rrd_value_t alpha,
+    rrd_value_t scratch,
+    rrd_value_t seasonal_coef,
+    unival *coefs);
+
+rrd_value_t hw_multiplicative_calculate_seasonality(
+    rrd_value_t gamma,
+    rrd_value_t scratch,
+    rrd_value_t intercept,
+    rrd_value_t seasonal_coef);
+
+rrd_value_t hw_multiplicative_init_seasonality(
+    rrd_value_t seasonal_coef,
+    rrd_value_t intercept);
+
+/*****************************************************************************
+ * Math functions common to additive and multiplicative Holt-Winters
+ *****************************************************************************/
+
+rrd_value_t hw_calculate_slope(
+    rrd_value_t beta,
+    unival *coefs);
+
+rrd_value_t hw_calculate_seasonal_deviation(
+    rrd_value_t gamma,
+    rrd_value_t prediction,
+    rrd_value_t observed,
+    rrd_value_t last);
+
+rrd_value_t hw_init_seasonal_deviation(
+    rrd_value_t prediction,
+    rrd_value_t observed);
+
+
+/* Function container */
+
+typedef struct hw_functions_t {
+    rrd_value_t (
+    *predict) (
+    rrd_value_t intercept,
+    rrd_value_t slope,
+    int null_count,
+    rrd_value_t seasonal_coef);
+
+    rrd_value_t (
+    *intercept) (
+    rrd_value_t alpha,
+    rrd_value_t observed,
+    rrd_value_t seasonal_coef,
+    unival *coefs);
+
+    rrd_value_t (
+    *slope)   (
+    rrd_value_t beta,
+    unival *coefs);
+
+    rrd_value_t (
+    *seasonality) (
+    rrd_value_t gamma,
+    rrd_value_t observed,
+    rrd_value_t intercept,
+    rrd_value_t seasonal_coef);
+
+    rrd_value_t (
+    *init_seasonality) (
+    rrd_value_t seasonal_coef,
+    rrd_value_t intercept);
+
+    rrd_value_t (
+    *seasonal_deviation) (
+    rrd_value_t gamma,
+    rrd_value_t prediction,
+    rrd_value_t observed,
+    rrd_value_t last);
+
+    rrd_value_t (
+    *init_seasonal_deviation) (
+    rrd_value_t prediction,
+    rrd_value_t observed);
+
+    rrd_value_t identity;
+} hw_functions_t;
+
+
+#undef gamma
diff --git a/program/src/rrd_hw_update.c b/program/src/rrd_hw_update.c
new file mode 100644 (file)
index 0000000..90a8a52
--- /dev/null
@@ -0,0 +1,475 @@
+/*****************************************************************************
+ * rrd_hw_update.c  Functions for updating a Holt-Winters RRA
+ ****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_format.h"
+#include "rrd_hw_math.h"
+#include "rrd_hw_update.h"
+
+static void init_slope_intercept(
+    unival *coefs,
+    unsigned short CDP_scratch_idx)
+{
+#ifdef DEBUG
+    fprintf(stderr, "Initialization of slope/intercept\n");
+#endif
+    coefs[CDP_hw_intercept].u_val = coefs[CDP_scratch_idx].u_val;
+    coefs[CDP_hw_last_intercept].u_val = coefs[CDP_scratch_idx].u_val;
+    /* initialize the slope to 0 */
+    coefs[CDP_hw_slope].u_val = 0.0;
+    coefs[CDP_hw_last_slope].u_val = 0.0;
+    /* initialize null count to 1 */
+    coefs[CDP_null_count].u_cnt = 1;
+    coefs[CDP_last_null_count].u_cnt = 1;
+}
+
+static int hw_is_violation(
+    rrd_value_t observed,
+    rrd_value_t prediction,
+    rrd_value_t deviation,
+    rrd_value_t delta_pos,
+    rrd_value_t delta_neg)
+{
+    return (observed > prediction + delta_pos * deviation
+            || observed < prediction - delta_neg * deviation);
+}
+
+int update_hwpredict(
+    rrd_t *rrd,
+    unsigned long cdp_idx,
+    unsigned long rra_idx,
+    unsigned long ds_idx,
+    unsigned short CDP_scratch_idx,
+    hw_functions_t * functions)
+{
+    rrd_value_t prediction;
+    unsigned long dependent_rra_idx, seasonal_cdp_idx;
+    unival   *coefs = rrd->cdp_prep[cdp_idx].scratch;
+    rra_def_t *current_rra = &(rrd->rra_def[rra_idx]);
+    rrd_value_t seasonal_coef;
+
+    /* save coefficients from current prediction */
+    coefs[CDP_hw_last_intercept].u_val = coefs[CDP_hw_intercept].u_val;
+    coefs[CDP_hw_last_slope].u_val = coefs[CDP_hw_slope].u_val;
+    coefs[CDP_last_null_count].u_cnt = coefs[CDP_null_count].u_cnt;
+
+    /* retrieve the current seasonal coef */
+    dependent_rra_idx = current_rra->par[RRA_dependent_rra_idx].u_cnt;
+    seasonal_cdp_idx = dependent_rra_idx * (rrd->stat_head->ds_cnt) + ds_idx;
+
+    seasonal_coef = (dependent_rra_idx < rra_idx)
+        ? rrd->cdp_prep[seasonal_cdp_idx].scratch[CDP_hw_last_seasonal].u_val
+        : rrd->cdp_prep[seasonal_cdp_idx].scratch[CDP_hw_seasonal].u_val;
+
+    /* compute the prediction */
+    if (isnan(coefs[CDP_hw_intercept].u_val)
+        || isnan(coefs[CDP_hw_slope].u_val)
+        || isnan(seasonal_coef)) {
+        prediction = DNAN;
+
+        /* bootstrap initialization of slope and intercept */
+        if (isnan(coefs[CDP_hw_intercept].u_val) &&
+            !isnan(coefs[CDP_scratch_idx].u_val)) {
+            init_slope_intercept(coefs, CDP_scratch_idx);
+        }
+        /* if seasonal coefficient is NA, then don't update intercept, slope */
+    } else {
+        prediction = functions->predict(coefs[CDP_hw_intercept].u_val,
+                                        coefs[CDP_hw_slope].u_val,
+                                        coefs[CDP_null_count].u_cnt,
+                                        seasonal_coef);
+#ifdef DEBUG
+        fprintf(stderr,
+                "computed prediction: %f (intercept %f, slope %f, season %f)\n",
+                prediction, coefs[CDP_hw_intercept].u_val,
+                coefs[CDP_hw_slope].u_val, seasonal_coef);
+#endif
+        if (isnan(coefs[CDP_scratch_idx].u_val)) {
+            /* NA value, no updates of intercept, slope;
+             * increment the null count */
+            (coefs[CDP_null_count].u_cnt)++;
+        } else {
+            /* update the intercept */
+            coefs[CDP_hw_intercept].u_val =
+                functions->intercept(current_rra->par[RRA_hw_alpha].u_val,
+                                     coefs[CDP_scratch_idx].u_val,
+                                     seasonal_coef, coefs);
+
+            /* update the slope */
+            coefs[CDP_hw_slope].u_val =
+                functions->slope(current_rra->par[RRA_hw_beta].u_val, coefs);
+
+            /* reset the null count */
+            coefs[CDP_null_count].u_cnt = 1;
+#ifdef DEBUG
+            fprintf(stderr, "Updating intercept = %f, slope = %f\n",
+                    coefs[CDP_hw_intercept].u_val, coefs[CDP_hw_slope].u_val);
+#endif
+        }
+    }
+
+    /* store the prediction for writing */
+    coefs[CDP_scratch_idx].u_val = prediction;
+    return 0;
+}
+
+int update_seasonal(
+    rrd_t *rrd,
+    unsigned long cdp_idx,
+    unsigned long rra_idx,
+    unsigned long ds_idx,
+    unsigned short CDP_scratch_idx,
+    rrd_value_t *seasonal_coef,
+    hw_functions_t * functions)
+{
+/* TODO: extract common if subblocks in the wake of I/O optimization */
+    rrd_value_t intercept, seasonal;
+    rra_def_t *current_rra = &(rrd->rra_def[rra_idx]);
+    rra_def_t *hw_rra =
+        &(rrd->rra_def[current_rra->par[RRA_dependent_rra_idx].u_cnt]);
+
+    /* obtain cdp_prep index for HWPREDICT */
+    unsigned long hw_cdp_idx = (current_rra->par[RRA_dependent_rra_idx].u_cnt)
+        * (rrd->stat_head->ds_cnt) + ds_idx;
+    unival   *coefs = rrd->cdp_prep[hw_cdp_idx].scratch;
+
+    /* update seasonal coefficient in cdp prep areas */
+    seasonal = rrd->cdp_prep[cdp_idx].scratch[CDP_hw_seasonal].u_val;
+    rrd->cdp_prep[cdp_idx].scratch[CDP_hw_last_seasonal].u_val = seasonal;
+    rrd->cdp_prep[cdp_idx].scratch[CDP_hw_seasonal].u_val =
+        seasonal_coef[ds_idx];
+
+    if (isnan(rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val)) {
+        /* no update, store the old value unchanged,
+         * doesn't matter if it is NA */
+        rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = seasonal;
+        return 0;
+    }
+
+    /* update seasonal value for disk */
+    if (current_rra->par[RRA_dependent_rra_idx].u_cnt < rra_idx) {
+        /* associated HWPREDICT has already been updated */
+        /* check for possible NA values */
+        if (isnan(coefs[CDP_hw_last_intercept].u_val)
+            || isnan(coefs[CDP_hw_last_slope].u_val)) {
+            /* this should never happen, as HWPREDICT was already updated */
+            rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = DNAN;
+        } else if (isnan(seasonal)) {
+            /* initialization: intercept is not currently being updated */
+#ifdef DEBUG
+            fprintf(stderr, "Initialization of seasonal coef %lu\n",
+                    rrd->rra_ptr[rra_idx].cur_row);
+#endif
+            rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+                functions->init_seasonality(rrd->cdp_prep[cdp_idx].
+                                            scratch[CDP_scratch_idx].u_val,
+                                            coefs[CDP_hw_last_intercept].
+                                            u_val);
+        } else {
+            intercept = coefs[CDP_hw_intercept].u_val;
+
+            rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+                functions->seasonality(current_rra->par[RRA_seasonal_gamma].
+                                       u_val,
+                                       rrd->cdp_prep[cdp_idx].
+                                       scratch[CDP_scratch_idx].u_val,
+                                       intercept, seasonal);
+#ifdef DEBUG
+            fprintf(stderr,
+                    "Updating seasonal = %f (params: gamma %f, new intercept %f, old seasonal %f)\n",
+                    rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val,
+                    current_rra->par[RRA_seasonal_gamma].u_val,
+                    intercept, seasonal);
+#endif
+        }
+    } else {
+        /* SEASONAL array is updated first, which means the new intercept
+         * hasn't be computed; so we compute it here. */
+
+        /* check for possible NA values */
+        if (isnan(coefs[CDP_hw_intercept].u_val)
+            || isnan(coefs[CDP_hw_slope].u_val)) {
+            /* Initialization of slope and intercept will occur.
+             * force seasonal coefficient to 0 or 1. */
+            rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+                functions->identity;
+        } else if (isnan(seasonal)) {
+            /* initialization: intercept will not be updated
+             * CDP_hw_intercept = CDP_hw_last_intercept; just need to 
+             * subtract/divide by this baseline value. */
+#ifdef DEBUG
+            fprintf(stderr, "Initialization of seasonal coef %lu\n",
+                    rrd->rra_ptr[rra_idx].cur_row);
+#endif
+            rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+                functions->init_seasonality(rrd->cdp_prep[cdp_idx].
+                                            scratch[CDP_scratch_idx].u_val,
+                                            coefs[CDP_hw_intercept].u_val);
+        } else {
+            /* Note that we must get CDP_scratch_idx from SEASONAL array, as CDP_scratch_idx
+             * for HWPREDICT array will be DNAN. */
+            intercept = functions->intercept(hw_rra->par[RRA_hw_alpha].u_val,
+                                             rrd->cdp_prep[cdp_idx].
+                                             scratch[CDP_scratch_idx].u_val,
+                                             seasonal, coefs);
+
+            rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+                functions->seasonality(current_rra->par[RRA_seasonal_gamma].
+                                       u_val,
+                                       rrd->cdp_prep[cdp_idx].
+                                       scratch[CDP_scratch_idx].u_val,
+                                       intercept, seasonal);
+        }
+    }
+#ifdef DEBUG
+    fprintf(stderr, "seasonal coefficient set= %f\n",
+            rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val);
+#endif
+    return 0;
+}
+
+int update_devpredict(
+    rrd_t *rrd,
+    unsigned long cdp_idx,
+    unsigned long rra_idx,
+    unsigned long ds_idx,
+    unsigned short CDP_scratch_idx)
+{
+    /* there really isn't any "update" here; the only reason this information
+     * is stored separately from DEVSEASONAL is to preserve deviation predictions
+     * for a longer duration than one seasonal cycle. */
+    unsigned long seasonal_cdp_idx =
+        (rrd->rra_def[rra_idx].par[RRA_dependent_rra_idx].u_cnt)
+        * (rrd->stat_head->ds_cnt) + ds_idx;
+
+    if (rrd->rra_def[rra_idx].par[RRA_dependent_rra_idx].u_cnt < rra_idx) {
+        /* associated DEVSEASONAL array already updated */
+        rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val
+            =
+            rrd->cdp_prep[seasonal_cdp_idx].
+            scratch[CDP_last_seasonal_deviation].u_val;
+    } else {
+        /* associated DEVSEASONAL not yet updated */
+        rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val
+            =
+            rrd->cdp_prep[seasonal_cdp_idx].scratch[CDP_seasonal_deviation].
+            u_val;
+    }
+    return 0;
+}
+
+int update_devseasonal(
+    rrd_t *rrd,
+    unsigned long cdp_idx,
+    unsigned long rra_idx,
+    unsigned long ds_idx,
+    unsigned short CDP_scratch_idx,
+    rrd_value_t *seasonal_dev,
+    hw_functions_t * functions)
+{
+    rrd_value_t prediction = 0, seasonal_coef = DNAN;
+    rra_def_t *current_rra = &(rrd->rra_def[rra_idx]);
+
+    /* obtain cdp_prep index for HWPREDICT */
+    unsigned long hw_rra_idx = current_rra->par[RRA_dependent_rra_idx].u_cnt;
+    unsigned long hw_cdp_idx = hw_rra_idx * (rrd->stat_head->ds_cnt) + ds_idx;
+    unsigned long seasonal_cdp_idx;
+    unival   *coefs = rrd->cdp_prep[hw_cdp_idx].scratch;
+
+    rrd->cdp_prep[cdp_idx].scratch[CDP_last_seasonal_deviation].u_val =
+        rrd->cdp_prep[cdp_idx].scratch[CDP_seasonal_deviation].u_val;
+    /* retrieve the next seasonal deviation value, could be NA */
+    rrd->cdp_prep[cdp_idx].scratch[CDP_seasonal_deviation].u_val =
+        seasonal_dev[ds_idx];
+
+    /* retrieve the current seasonal_coef (not to be confused with the
+     * current seasonal deviation). Could make this more readable by introducing
+     * some wrapper functions. */
+    seasonal_cdp_idx =
+        (rrd->rra_def[hw_rra_idx].par[RRA_dependent_rra_idx].u_cnt)
+        * (rrd->stat_head->ds_cnt) + ds_idx;
+    if (rrd->rra_def[hw_rra_idx].par[RRA_dependent_rra_idx].u_cnt < rra_idx)
+        /* SEASONAL array already updated */
+        seasonal_coef =
+            rrd->cdp_prep[seasonal_cdp_idx].scratch[CDP_hw_last_seasonal].
+            u_val;
+    else
+        /* SEASONAL array not yet updated */
+        seasonal_coef =
+            rrd->cdp_prep[seasonal_cdp_idx].scratch[CDP_hw_seasonal].u_val;
+
+    /* compute the abs value of the difference between the prediction and
+     * observed value */
+    if (hw_rra_idx < rra_idx) {
+        /* associated HWPREDICT has already been updated */
+        if (isnan(coefs[CDP_hw_last_intercept].u_val) ||
+            isnan(coefs[CDP_hw_last_slope].u_val) || isnan(seasonal_coef)) {
+            /* one of the prediction values is uinitialized */
+            rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = DNAN;
+            return 0;
+        } else {
+            prediction =
+                functions->predict(coefs[CDP_hw_last_intercept].u_val,
+                                   coefs[CDP_hw_last_slope].u_val,
+                                   coefs[CDP_last_null_count].u_cnt,
+                                   seasonal_coef);
+        }
+    } else {
+        /* associated HWPREDICT has NOT been updated */
+        if (isnan(coefs[CDP_hw_intercept].u_val) ||
+            isnan(coefs[CDP_hw_slope].u_val) || isnan(seasonal_coef)) {
+            /* one of the prediction values is uinitialized */
+            rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = DNAN;
+            return 0;
+        } else {
+            prediction = functions->predict(coefs[CDP_hw_intercept].u_val,
+                                            coefs[CDP_hw_slope].u_val,
+                                            coefs[CDP_null_count].u_cnt,
+                                            seasonal_coef);
+        }
+    }
+
+    if (isnan(rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val)) {
+        /* no update, store existing value unchanged, doesn't
+         * matter if it is NA */
+        rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+            rrd->cdp_prep[cdp_idx].scratch[CDP_last_seasonal_deviation].u_val;
+    } else
+        if (isnan
+            (rrd->cdp_prep[cdp_idx].scratch[CDP_last_seasonal_deviation].
+             u_val)) {
+        /* initialization */
+#ifdef DEBUG
+        fprintf(stderr, "Initialization of seasonal deviation\n");
+#endif
+        rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+            functions->init_seasonal_deviation(prediction,
+                                               rrd->cdp_prep[cdp_idx].
+                                               scratch[CDP_scratch_idx].
+                                               u_val);
+    } else {
+        /* exponential smoothing update */
+        rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+            functions->seasonal_deviation(rrd->rra_def[rra_idx].
+                                          par[RRA_seasonal_gamma].u_val,
+                                          prediction,
+                                          rrd->cdp_prep[cdp_idx].
+                                          scratch[CDP_scratch_idx].u_val,
+                                          rrd->cdp_prep[cdp_idx].
+                                          scratch
+                                          [CDP_last_seasonal_deviation].
+                                          u_val);
+    }
+    return 0;
+}
+
+/* Check for a failure based on a threshold # of violations within the specified
+ * window. */
+int update_failures(
+    rrd_t *rrd,
+    unsigned long cdp_idx,
+    unsigned long rra_idx,
+    unsigned long ds_idx,
+    unsigned short CDP_scratch_idx,
+    hw_functions_t * functions)
+{
+    /* detection of a violation depends on 3 RRAs:
+     * HWPREDICT, SEASONAL, and DEVSEASONAL */
+    rra_def_t *current_rra = &(rrd->rra_def[rra_idx]);
+    unsigned long dev_rra_idx = current_rra->par[RRA_dependent_rra_idx].u_cnt;
+    rra_def_t *dev_rra = &(rrd->rra_def[dev_rra_idx]);
+    unsigned long hw_rra_idx = dev_rra->par[RRA_dependent_rra_idx].u_cnt;
+    rra_def_t *hw_rra = &(rrd->rra_def[hw_rra_idx]);
+    unsigned long seasonal_rra_idx = hw_rra->par[RRA_dependent_rra_idx].u_cnt;
+    unsigned long temp_cdp_idx;
+    rrd_value_t deviation = DNAN;
+    rrd_value_t seasonal_coef = DNAN;
+    rrd_value_t prediction = DNAN;
+    char      violation = 0;
+    unsigned short violation_cnt = 0, i;
+    char     *violations_array;
+
+    /* usual checks to determine the order of the RRAs */
+    temp_cdp_idx = dev_rra_idx * (rrd->stat_head->ds_cnt) + ds_idx;
+    if (rra_idx < seasonal_rra_idx) {
+        /* DEVSEASONAL not yet updated */
+        deviation =
+            rrd->cdp_prep[temp_cdp_idx].scratch[CDP_seasonal_deviation].u_val;
+    } else {
+        /* DEVSEASONAL already updated */
+        deviation =
+            rrd->cdp_prep[temp_cdp_idx].scratch[CDP_last_seasonal_deviation].
+            u_val;
+    }
+    if (!isnan(deviation)) {
+
+        temp_cdp_idx = seasonal_rra_idx * (rrd->stat_head->ds_cnt) + ds_idx;
+        if (rra_idx < seasonal_rra_idx) {
+            /* SEASONAL not yet updated */
+            seasonal_coef =
+                rrd->cdp_prep[temp_cdp_idx].scratch[CDP_hw_seasonal].u_val;
+        } else {
+            /* SEASONAL already updated */
+            seasonal_coef =
+                rrd->cdp_prep[temp_cdp_idx].scratch[CDP_hw_last_seasonal].
+                u_val;
+        }
+        /* in this code block, we know seasonal coef is not DNAN, because deviation is not
+         * null */
+
+        temp_cdp_idx = hw_rra_idx * (rrd->stat_head->ds_cnt) + ds_idx;
+        if (rra_idx < hw_rra_idx) {
+            /* HWPREDICT not yet updated */
+            prediction =
+                functions->predict(rrd->cdp_prep[temp_cdp_idx].
+                                   scratch[CDP_hw_intercept].u_val,
+                                   rrd->cdp_prep[temp_cdp_idx].
+                                   scratch[CDP_hw_slope].u_val,
+                                   rrd->cdp_prep[temp_cdp_idx].
+                                   scratch[CDP_null_count].u_cnt,
+                                   seasonal_coef);
+        } else {
+            /* HWPREDICT already updated */
+            prediction =
+                functions->predict(rrd->cdp_prep[temp_cdp_idx].
+                                   scratch[CDP_hw_last_intercept].u_val,
+                                   rrd->cdp_prep[temp_cdp_idx].
+                                   scratch[CDP_hw_last_slope].u_val,
+                                   rrd->cdp_prep[temp_cdp_idx].
+                                   scratch[CDP_last_null_count].u_cnt,
+                                   seasonal_coef);
+        }
+
+        /* determine if the observed value is a violation */
+        if (!isnan(rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val)) {
+            if (hw_is_violation
+                (rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val,
+                 prediction, deviation, current_rra->par[RRA_delta_pos].u_val,
+                 current_rra->par[RRA_delta_neg].u_val)) {
+                violation = 1;
+            }
+        } else {
+            violation = 1;  /* count DNAN values as violations */
+        }
+
+    }
+
+    /* determine if a failure has occurred and update the failure array */
+    violation_cnt = violation;
+    violations_array = (char *) ((void *) rrd->cdp_prep[cdp_idx].scratch);
+    for (i = current_rra->par[RRA_window_len].u_cnt; i > 1; i--) {
+        /* shift */
+        violations_array[i - 1] = violations_array[i - 2];
+        violation_cnt += violations_array[i - 1];
+    }
+    violations_array[0] = violation;
+
+    if (violation_cnt < current_rra->par[RRA_failure_threshold].u_cnt)
+        /* not a failure */
+        rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = 0.0;
+    else
+        rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = 1.0;
+
+    return (rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val);
+}
diff --git a/program/src/rrd_hw_update.h b/program/src/rrd_hw_update.h
new file mode 100644 (file)
index 0000000..e59e2db
--- /dev/null
@@ -0,0 +1,44 @@
+/*****************************************************************************
+ * rrd_hw_update.h  Functions for updating a Holt-Winters RRA
+ ****************************************************************************/
+
+int       update_hwpredict(
+    rrd_t *rrd,
+    unsigned long cdp_idx,
+    unsigned long rra_idx,
+    unsigned long ds_idx,
+    unsigned short CDP_scratch_idx,
+    hw_functions_t * functions);
+
+int       update_seasonal(
+    rrd_t *rrd,
+    unsigned long cdp_idx,
+    unsigned long rra_idx,
+    unsigned long ds_idx,
+    unsigned short CDP_scratch_idx,
+    rrd_value_t *seasonal_coef,
+    hw_functions_t * functions);
+
+int       update_devpredict(
+    rrd_t *rrd,
+    unsigned long cdp_idx,
+    unsigned long rra_idx,
+    unsigned long ds_idx,
+    unsigned short CDP_scratch_idx);
+
+int       update_devseasonal(
+    rrd_t *rrd,
+    unsigned long cdp_idx,
+    unsigned long rra_idx,
+    unsigned long ds_idx,
+    unsigned short CDP_scratch_idx,
+    rrd_value_t *seasonal_dev,
+    hw_functions_t * functions);
+
+int       update_failures(
+    rrd_t *rrd,
+    unsigned long cdp_idx,
+    unsigned long rra_idx,
+    unsigned long ds_idx,
+    unsigned short CDP_scratch_idx,
+    hw_functions_t * functions);
diff --git a/program/src/rrd_i18n.h b/program/src/rrd_i18n.h
new file mode 100644 (file)
index 0000000..763c67a
--- /dev/null
@@ -0,0 +1,31 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Takao Fujiwara, 2008
+ *****************************************************************************
+ * rrd_i18n.h   Common Header File
+ *****************************************************************************/
+#ifdef  __cplusplus
+extern    "C" {
+#endif
+
+
+#ifndef _RRD_I18N_H
+#define _RRD_I18N_H
+
+#ifdef ENABLE_NLS
+#  ifdef _LIBC
+#    include <libintl.h>
+#  else
+#    include "gettext.h"
+#    define _(String) gettext (String)
+#  endif
+#else
+#  define _(String) (String)
+#endif
+
+#define N_(String) String
+
+#endif
+
+#ifdef  __cplusplus
+}
+#endif
diff --git a/program/src/rrd_info.c b/program/src/rrd_info.c
new file mode 100644 (file)
index 0000000..4f8b5dd
--- /dev/null
@@ -0,0 +1,452 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_info  Get Information about the configuration of an RRD
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include "rrd_client.h"
+#include <stdarg.h>
+
+/* proto */
+rrd_info_t *rrd_info(
+    int,
+    char **);
+rrd_info_t *rrd_info_r(
+    char *filename);
+
+/* allocate memory for string */
+char     *sprintf_alloc(
+    char *fmt,
+    ...)
+{
+    char     *str = NULL;
+    va_list   argp;
+#ifdef HAVE_VASPRINTF
+    va_start( argp, fmt );
+    if (vasprintf( &str, fmt, argp ) == -1){
+        va_end(argp);
+        rrd_set_error ("vasprintf failed.");
+        return(NULL);
+    }
+#else
+    int       maxlen = 1024 + strlen(fmt);
+    str = (char*)malloc(sizeof(char) * (maxlen + 1));
+    if (str != NULL) {
+        va_start(argp, fmt);
+#ifdef HAVE_VSNPRINTF
+        vsnprintf(str, maxlen, fmt, argp);
+#else
+        vsprintf(str, fmt, argp);
+#endif
+    }
+#endif /* HAVE_VASPRINTF */
+    va_end(argp);
+    return str;
+}
+
+/* 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, rrd_info_type_t type, rrd_infoval_t value)
+{
+    rrd_info_t *next;
+
+    next = (rrd_info_t*)malloc(sizeof(*next));
+    next->next = (rrd_info_t *) 0;
+    if (info)
+        info->next = next;
+    next->type = type;
+    next->key = key;
+    switch (type) {
+    case RD_I_VAL:
+        next->value.u_val = value.u_val;
+        break;
+    case RD_I_CNT:
+        next->value.u_cnt = value.u_cnt;
+        break;
+    case RD_I_INT:
+        next->value.u_int = value.u_int;
+        break;
+    case RD_I_STR:
+        next->value.u_str = (char*)malloc(sizeof(char) * (strlen(value.u_str) + 1));
+        strcpy(next->value.u_str, value.u_str);
+        break;
+    case RD_I_BLO:
+        next->value.u_blo.size = value.u_blo.size;
+        next->value.u_blo.ptr =
+            (unsigned char *)malloc(sizeof(unsigned char) * value.u_blo.size);
+        memcpy(next->value.u_blo.ptr, value.u_blo.ptr, value.u_blo.size);
+        break;
+    }
+    return (next);
+}
+
+
+rrd_info_t *rrd_info(
+    int argc,
+    char **argv)
+{
+    rrd_info_t *info;
+    char *opt_daemon = NULL;
+    int status;
+
+    optind = 0;
+    opterr = 0;         /* initialize getopt */
+
+    while (42) {
+        int       opt;
+        int       option_index = 0;
+        static struct option long_options[] = {
+            {"daemon", required_argument, 0, 'd'},
+            {0, 0, 0, 0}
+        };
+
+        opt = getopt_long(argc, argv, "d:", long_options, &option_index);
+
+        if (opt == EOF)
+            break;
+
+        switch (opt) {
+        case 'd':
+            if (opt_daemon != NULL)
+                    free (opt_daemon);
+            opt_daemon = strdup (optarg);
+            if (opt_daemon == NULL)
+            {
+                rrd_set_error ("strdup failed.");
+                return (NULL);
+            }
+            break;
+
+        default:
+            rrd_set_error ("Usage: rrdtool %s [--daemon <addr>] <file>",
+                    argv[0]);
+            return (NULL);
+            break;
+        }
+    }                   /* while (42) */
+
+    if ((argc - optind) != 1) {
+        rrd_set_error ("Usage: rrdtool %s [--daemon <addr>] <file>",
+                argv[0]);
+        return (NULL);
+    }
+
+    status = rrdc_flush_if_daemon(opt_daemon, argv[optind]);
+    if (opt_daemon) free (opt_daemon);
+    if (status) return (NULL);
+
+    info = rrd_info_r(argv[optind]);
+
+    return (info);
+} /* rrd_info_t *rrd_info */
+
+rrd_info_t *rrd_info_r(
+    char *filename)
+{
+    unsigned int i, ii = 0;
+    rrd_t     rrd;
+    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;
+
+    rrd_init(&rrd);
+    rrd_file = rrd_open(filename, &rrd, RRD_READONLY);
+    if (rrd_file == NULL)
+        goto err_free;
+
+    info.u_str = filename;
+    cd = rrd_info_push(NULL, sprintf_alloc("filename"), RD_I_STR, info);
+    data = cd;
+
+    info.u_str = rrd.stat_head->version;
+    cd = rrd_info_push(cd, sprintf_alloc("rrd_version"), RD_I_STR, info);
+
+    info.u_cnt = rrd.stat_head->pdp_step;
+    cd = rrd_info_push(cd, sprintf_alloc("step"), RD_I_CNT, info);
+
+    info.u_cnt = rrd.live_head->last_up;
+    cd = rrd_info_push(cd, sprintf_alloc("last_update"), RD_I_CNT, info);
+
+    info.u_cnt = rrd_get_header_size(&rrd);
+    cd = rrd_info_push(cd, sprintf_alloc("header_size"), RD_I_CNT, info);
+
+    for (i = 0; i < rrd.stat_head->ds_cnt; i++) {
+
+        info.u_cnt=i;
+        cd= rrd_info_push(cd,sprintf_alloc("ds[%s].index",
+                                     rrd.ds_def[i].ds_nam),
+                     RD_I_CNT, info);
+    
+        info.u_str = rrd.ds_def[i].dst;
+        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);
+        switch (current_ds) {
+        case DST_CDEF:
+        {
+            char     *buffer = NULL;
+
+            rpn_compact2str((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]),
+                            rrd.ds_def, &buffer);
+            info.u_str = buffer;
+            cd = rrd_info_push(cd,
+                               sprintf_alloc("ds[%s].cdef",
+                                             rrd.ds_def[i].ds_nam), RD_I_STR,
+                               info);
+            free(buffer);
+        }
+            break;
+        default:
+            info.u_cnt = rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt;
+            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 = 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 = 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 = 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 = 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 = 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 = 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 = rrd_info_push(cd, sprintf_alloc("rra[%d].rows", i), RD_I_CNT,
+                           info);
+
+        info.u_cnt = rrd.rra_ptr[i].cur_row;
+        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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = rrd_info_push(cd, sprintf_alloc("rra[%d].xff", i), RD_I_VAL,
+                               info);
+            break;
+        }
+
+        for (ii = 0; ii < rrd.stat_head->ds_cnt; ii++) {
+            switch (current_cf) {
+            case CF_HWPREDICT:
+            case CF_MHWPREDICT:
+                info.u_val =
+                    rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
+                                 ii].scratch[CDP_hw_intercept].u_val;
+                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 = 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 = rrd_info_push(cd,
+                                   sprintf_alloc
+                                   ("rra[%d].cdp_prep[%d].NaN_count", i, ii),
+                                   RD_I_CNT, info);
+                break;
+            case CF_SEASONAL:
+                info.u_val =
+                    rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
+                                 ii].scratch[CDP_hw_seasonal].u_val;
+                cd = rrd_info_push(cd,
+                                   sprintf_alloc
+                                   ("rra[%d].cdp_prep[%d].seasonal", i, ii),
+                                   RD_I_VAL, info);
+                break;
+            case CF_DEVSEASONAL:
+                info.u_val =
+                    rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
+                                 ii].scratch[CDP_seasonal_deviation].u_val;
+                cd = rrd_info_push(cd,
+                                   sprintf_alloc
+                                   ("rra[%d].cdp_prep[%d].deviation", i, ii),
+                                   RD_I_VAL, info);
+                break;
+            case CF_DEVPREDICT:
+                break;
+            case CF_FAILURES:
+            {
+                unsigned short j;
+                char     *violations_array;
+                char      history[MAX_FAILURES_WINDOW_LEN + 1];
+
+                violations_array =
+                    (char *) rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
+                                          ii].scratch;
+                for (j = 0; j < rrd.rra_def[i].par[RRA_window_len].u_cnt; ++j)
+                    history[j] = (violations_array[j] == 1) ? '1' : '0';
+                history[j] = '\0';
+                info.u_str = history;
+                cd = rrd_info_push(cd,
+                                   sprintf_alloc
+                                   ("rra[%d].cdp_prep[%d].history", i, ii),
+                                   RD_I_STR, info);
+            }
+                break;
+            default:
+                info.u_val =
+                    rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
+                                 ii].scratch[CDP_val].u_val;
+                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 = rrd_info_push(cd,
+                                   sprintf_alloc
+                                   ("rra[%d].cdp_prep[%d].unknown_datapoints",
+                                    i, ii), RD_I_CNT, info);
+                break;
+            }
+        }
+    }
+
+    rrd_close(rrd_file);
+  err_free:
+    rrd_free(&rrd);
+    return (data);
+}
+
+
+void rrd_info_print(
+    rrd_info_t * data)
+{
+    while (data) {
+        printf("%s = ", data->key);
+
+        switch (data->type) {
+        case RD_I_VAL:
+            if (isnan(data->value.u_val))
+                printf("NaN\n");
+            else
+                printf("%0.10e\n", data->value.u_val);
+            break;
+        case RD_I_CNT:
+            printf("%lu\n", data->value.u_cnt);
+            break;
+        case RD_I_INT:
+            printf("%d\n", data->value.u_int);
+            break;
+        case RD_I_STR:
+            printf("\"%s\"\n", data->value.u_str);
+            break;
+        case RD_I_BLO:
+            printf("BLOB_SIZE:%lu\n", data->value.u_blo.size);
+            fwrite(data->value.u_blo.ptr, data->value.u_blo.size, 1, stdout);
+            break;
+        }
+        data = data->next;
+    }
+}
+
+void rrd_info_free(
+    rrd_info_t * data)
+{
+    rrd_info_t *save;
+
+    while (data) {
+        save = data;
+        if (data->key) {
+            if (data->type == RD_I_STR) {
+                free(data->value.u_str);
+            }
+            if (data->type == RD_I_BLO) {
+                free(data->value.u_blo.ptr);
+            }
+            free(data->key);
+        }
+        data = data->next;
+        free(save);
+    }
+}
diff --git a/program/src/rrd_is_thread_safe.h b/program/src/rrd_is_thread_safe.h
new file mode 100644 (file)
index 0000000..3990f8d
--- /dev/null
@@ -0,0 +1,28 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ * This file:     Copyright 2003 Peter Stamfest <peter@stamfest.at> 
+ *                             & Tobias Oetiker
+ * Distributed under the GPL
+ *****************************************************************************
+ * rrd_is_thread_safe.c   Poisons some nasty function calls using GNU cpp
+ *****************************************************************************
+ * $Id$
+ *************************************************************************** */
+
+#ifndef _RRD_IS_THREAD_SAFE_H
+#define _RRD_IS_THREAD_SAFE_H
+
+#ifdef  __cplusplus
+extern    "C" {
+#endif
+
+#undef strerror
+
+#if( 2 < __GNUC__ )
+#pragma GCC poison strtok asctime ctime gmtime localtime tmpnam strerror
+#endif
+
+#ifdef  __cplusplus
+}
+#endif
+#endif /*_RRD_IS_THREAD_SAFE_H */
diff --git a/program/src/rrd_last.c b/program/src/rrd_last.c
new file mode 100644 (file)
index 0000000..8555fbc
--- /dev/null
@@ -0,0 +1,84 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_last.c
+ *****************************************************************************
+ * Initial version by Russ Wright, @Home Network, 9/28/98
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_client.h"
+
+time_t rrd_last(
+    int argc,
+    char **argv)
+{
+    char *opt_daemon = NULL;
+    int status;
+
+    optind = 0;
+    opterr = 0;         /* initialize getopt */
+
+    while (42) {
+        int       opt;
+        int       option_index = 0;
+        static struct option long_options[] = {
+            {"daemon", required_argument, 0, 'd'},
+            {0, 0, 0, 0}
+        };
+
+        opt = getopt_long(argc, argv, "d:", long_options, &option_index);
+
+        if (opt == EOF)
+            break;
+
+        switch (opt) {
+        case 'd':
+            if (opt_daemon != NULL)
+                    free (opt_daemon);
+            opt_daemon = strdup (optarg);
+            if (opt_daemon == NULL)
+            {
+                rrd_set_error ("strdup failed.");
+                return (-1);
+            }
+            break;
+
+        default:
+            rrd_set_error ("Usage: rrdtool %s [--daemon <addr>] <file>",
+                    argv[0]);
+            return (-1);
+            break;
+        }
+    }                   /* while (42) */
+
+    if ((argc - optind) != 1) {
+        rrd_set_error ("Usage: rrdtool %s [--daemon <addr>] <file>",
+                argv[0]);
+        return (-1);
+    }
+
+    status = rrdc_flush_if_daemon(opt_daemon, argv[optind]);
+    if (opt_daemon) free(opt_daemon);
+    if (status) return (-1);
+
+    return (rrd_last_r (argv[optind]));
+}
+
+time_t rrd_last_r(
+    const char *filename)
+{
+    time_t    lastup = -1;
+    rrd_file_t *rrd_file;
+
+    rrd_t     rrd;
+
+    rrd_init(&rrd);
+    rrd_file = rrd_open(filename, &rrd, RRD_READONLY);
+    if (rrd_file != NULL) {
+        lastup = rrd.live_head->last_up;
+        rrd_close(rrd_file);
+    }
+    rrd_free(&rrd);
+    return (lastup);
+}
diff --git a/program/src/rrd_lastupdate.c b/program/src/rrd_lastupdate.c
new file mode 100644 (file)
index 0000000..42d9c95
--- /dev/null
@@ -0,0 +1,167 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *                Copyright by Florian Forster, 2008
+ *****************************************************************************
+ * rrd_lastupdate  Get the last datum entered for each DS
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include "rrd_client.h"
+#include <stdarg.h>
+
+int rrd_lastupdate (int argc, char **argv)
+{
+    time_t    last_update;
+    char    **ds_names;
+    char    **last_ds;
+    unsigned long ds_count, i;
+    int status;
+
+    char *opt_daemon = NULL;
+
+    optind = 0;
+    opterr = 0;         /* initialize getopt */
+
+    while (42) {
+        int       opt;
+        int       option_index = 0;
+        static struct option long_options[] = {
+            {"daemon", required_argument, 0, 'd'},
+            {0, 0, 0, 0}
+        };
+
+        opt = getopt_long (argc, argv, "d:", long_options, &option_index);
+
+        if (opt == EOF)
+            break;
+
+        switch (opt) {
+        case 'd':
+            if (opt_daemon != NULL)
+                    free (opt_daemon);
+            opt_daemon = strdup (optarg);
+            if (opt_daemon == NULL)
+            {
+                rrd_set_error ("strdup failed.");
+                return (-1);
+            }
+            break;
+
+        default:
+            rrd_set_error ("Usage: rrdtool %s [--daemon <addr>] <file>",
+                    argv[0]);
+            return (-1);
+            break;
+        }
+    }                   /* while (42) */
+
+    if ((argc - optind) != 1) {
+        rrd_set_error ("Usage: rrdtool %s [--daemon <addr>] <file>",
+                argv[0]);
+        return (-1);
+    }
+
+    status = rrdc_flush_if_daemon(opt_daemon, argv[optind]);
+    if (opt_daemon) free (opt_daemon);
+    if (status) return (-1);
+
+    status = rrd_lastupdate_r (argv[optind],
+            &last_update, &ds_count, &ds_names, &last_ds);
+    if (status != 0)
+        return (status);
+
+    for (i = 0; i < ds_count; i++)
+        printf(" %s", ds_names[i]);
+    printf ("\n\n");
+
+    printf ("%10lu:", last_update);
+    for (i = 0; i < ds_count; i++) {
+        printf(" %s", last_ds[i]);
+        free(last_ds[i]);
+        free(ds_names[i]);
+    }
+    printf("\n");
+
+    free(last_ds);
+    free(ds_names);
+
+    return (0);
+} /* int rrd_lastupdate */
+
+int rrd_lastupdate_r(const char *filename,
+        time_t *ret_last_update,
+        unsigned long *ret_ds_count,
+        char ***ret_ds_names,
+        char ***ret_last_ds)
+{
+    unsigned long i = 0;
+    rrd_t     rrd;
+    rrd_file_t *rrd_file;
+
+    rrd_init(&rrd);
+    rrd_file = rrd_open(filename, &rrd, RRD_READONLY);
+    if (rrd_file == NULL) {
+        rrd_free(&rrd);
+        return (-1);
+    }
+
+    *ret_last_update = rrd.live_head->last_up;
+    *ret_ds_count = rrd.stat_head->ds_cnt;
+    *ret_ds_names = (char **) malloc (rrd.stat_head->ds_cnt * sizeof(char *));
+    if (*ret_ds_names == NULL) {
+        rrd_set_error ("malloc fetch ret_ds_names array");
+        rrd_close (rrd_file);
+        rrd_free (&rrd);
+        return (-1);
+    }
+    memset (*ret_ds_names, 0, rrd.stat_head->ds_cnt * sizeof(char *));
+
+    *ret_last_ds = (char **) malloc (rrd.stat_head->ds_cnt * sizeof(char *));
+    if (*ret_last_ds == NULL) {
+        rrd_set_error ("malloc fetch ret_last_ds array");
+        free (*ret_ds_names);
+        *ret_ds_names = NULL;
+        rrd_close (rrd_file);
+        rrd_free (&rrd);
+        return (-1);
+    }
+    memset (*ret_last_ds, 0, rrd.stat_head->ds_cnt * sizeof(char *));
+
+    for (i = 0; i < rrd.stat_head->ds_cnt; i++) {
+        (*ret_ds_names)[i] = sprintf_alloc("%s", rrd.ds_def[i].ds_nam);
+        (*ret_last_ds)[i] = sprintf_alloc("%s", rrd.pdp_prep[i].last_ds);
+
+        if (((*ret_ds_names)[i] == NULL) || ((*ret_last_ds)[i] == NULL))
+            break;
+    }
+
+    /* Check if all names and values could be copied and free everything if
+     * not. */
+    if (i < rrd.stat_head->ds_cnt) {
+        rrd_set_error ("sprintf_alloc failed");
+        for (i = 0; i < rrd.stat_head->ds_cnt; i++) {
+            if ((*ret_ds_names)[i] != NULL)
+            {
+                free ((*ret_ds_names)[i]);
+                (*ret_ds_names)[i] = NULL;
+            }
+            if ((*ret_last_ds)[i] != NULL)
+            {
+                free ((*ret_last_ds)[i]);
+                (*ret_last_ds)[i] = NULL;
+            }
+        }
+        free (*ret_ds_names);
+        *ret_ds_names = NULL;
+        free (*ret_last_ds);
+        *ret_last_ds = NULL;
+        rrd_close (rrd_file);
+        rrd_free (&rrd);
+        return (-1);
+    }
+
+    rrd_free(&rrd);
+    rrd_close(rrd_file);
+    return (0);
+} /* int rrd_lastupdate_r */
diff --git a/program/src/rrd_nan_inf.c b/program/src/rrd_nan_inf.c
new file mode 100644 (file)
index 0000000..2914d1a
--- /dev/null
@@ -0,0 +1,40 @@
+int       done_nan = 0;
+int       done_inf = 0;
+
+double    dnan;
+double    dinf;
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#include <math.h>
+#include "rrd.h"
+
+#define NAN_FUNC (double)fmod(0.0,0.0)
+#define INF_FUNC (double)fabs((double)log(0.0))
+
+#else
+#include "rrd.h"
+
+#define NAN_FUNC (double)(0.0/0.0)
+#define INF_FUNC (double)(1.0/0.0)
+
+#endif
+
+double rrd_set_to_DNAN(
+    void)
+{
+    if (!done_nan) {
+        dnan = NAN_FUNC;
+        done_nan = 1;
+    }
+    return dnan;
+}
+
+double rrd_set_to_DINF(
+    void)
+{
+    if (!done_inf) {
+        dinf = INF_FUNC;
+        done_inf = 1;
+    }
+    return dinf;
+}
diff --git a/program/src/rrd_not_thread_safe.c b/program/src/rrd_not_thread_safe.c
new file mode 100644 (file)
index 0000000..0d3ddbe
--- /dev/null
@@ -0,0 +1,42 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ * This file:     Copyright 2003 Peter Stamfest <peter@stamfest.at> 
+ *                             & Tobias Oetiker
+ * Distributed under the GPL
+ *****************************************************************************
+ * rrd_not_thread_safe.c   Contains routines used when thread safety is not
+ *                         an issue
+ *****************************************************************************
+ * $Id$
+ *************************************************************************** */
+#include "rrd.h"
+#include "rrd_tool.h"
+#define MAXLEN 4096
+#define ERRBUFLEN 256
+
+/* 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 rrd_context_t global_ctx = {
+    "",
+    ""
+};
+
+/* #include <stdarg.h> */
+
+rrd_context_t *rrd_get_context(
+    void)
+{
+    return &global_ctx;
+}
+
+/* how ugly that is!!! - make sure strerror is what it should be. It
+   might be redefined to help in keeping other modules thread safe by
+   silently turning misplaced strerror into rrd_strerror, but here
+   this turns recursive! */
+#undef strerror
+const char *rrd_strerror(
+    int err)
+{
+    return strerror(err);
+}
diff --git a/program/src/rrd_open.c b/program/src/rrd_open.c
new file mode 100644 (file)
index 0000000..820b547
--- /dev/null
@@ -0,0 +1,772 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_open.c  Open an RRD File
+ *****************************************************************************
+ * $Id$
+ *****************************************************************************/
+
+#ifdef WIN32
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_BROKEN_MS_ASYNC
+#include <sys/types.h>
+#include <utime.h>
+#endif
+
+#include "rrd_tool.h"
+#include "unused.h"
+#define MEMBLK 8192
+
+#ifdef WIN32
+#define        _LK_UNLCK       0       /* Unlock */
+#define        _LK_LOCK        1       /* Lock */
+#define        _LK_NBLCK       2       /* Non-blocking lock */
+#define        _LK_RLCK        3       /* Lock for read only */
+#define        _LK_NBRLCK      4       /* Non-blocking lock for read only */
+
+
+#define        LK_UNLCK        _LK_UNLCK
+#define        LK_LOCK         _LK_LOCK
+#define        LK_NBLCK        _LK_NBLCK
+#define        LK_RLCK         _LK_RLCK
+#define        LK_NBRLCK       _LK_NBRLCK
+#endif
+
+/* DEBUG 2 prints information obtained via mincore(2) */
+#define DEBUG 1
+/* do not calculate exact madvise hints but assume 1 page for headers and
+ * set DONTNEED for the rest, which is assumed to be data */
+/* Avoid calling madvise on areas that were already hinted. May be benefical if
+ * your syscalls are very slow */
+
+#ifdef HAVE_MMAP
+/* the cast to void* is there to avoid this warning seen on ia64 with certain
+   versions of gcc: 'cast increases required alignment of target type'
+*/
+#define __rrd_read(dst, dst_t, cnt) { \
+       size_t wanted = sizeof(dst_t)*(cnt); \
+       if (offset + wanted > rrd_file->file_len) { \
+               rrd_set_error("reached EOF while loading header " #dst); \
+               goto out_nullify_head; \
+       } \
+       (dst) = (dst_t*)(void*) (data + offset); \
+       offset += wanted; \
+    }
+#else
+#define __rrd_read(dst, dst_t, cnt) { \
+       size_t wanted = sizeof(dst_t)*(cnt); \
+        size_t got; \
+       if ((dst = (dst_t*)malloc(wanted)) == NULL) { \
+               rrd_set_error(#dst " malloc"); \
+               goto out_nullify_head; \
+       } \
+        got = read (rrd_simple_file->fd, dst, wanted); \
+       if (got != wanted) { \
+               rrd_set_error("short read while reading header " #dst); \
+                goto out_nullify_head; \
+       } \
+       offset += got; \
+    }
+#endif
+
+/* get the address of the start of this page */
+#if defined USE_MADVISE || defined HAVE_POSIX_FADVISE
+#ifndef PAGE_START
+#define PAGE_START(addr) ((addr)&(~(_page_size-1)))
+#endif
+#endif
+
+/* Open a database file, return its header and an open filehandle,
+ * positioned to the first cdp in the first rra.
+ * In the error path of rrd_open, only rrd_free(&rrd) has to be called
+ * before returning an error. Do not call rrd_close upon failure of rrd_open.
+ * If creating a new file, the parameter rrd must be initialised with
+ * details of the file content.
+ * If opening an existing file, then use rrd must be initialised by
+ * rrd_init(rrd) prior to invoking rrd_open
+ */
+
+rrd_file_t *rrd_open(
+    const char *const file_name,
+    rrd_t *rrd,
+    unsigned rdwr)
+{
+    unsigned long ui;
+    int       flags = 0;
+    int       version;
+
+#ifdef HAVE_MMAP
+    ssize_t   _page_size = sysconf(_SC_PAGESIZE);
+    char     *data = MAP_FAILED;
+#endif
+    off_t     offset = 0;
+    struct stat statb;
+    rrd_file_t *rrd_file = NULL;
+    rrd_simple_file_t *rrd_simple_file = NULL;
+    size_t     newfile_size = 0;
+    size_t header_len, value_cnt, data_len;
+
+    /* Are we creating a new file? */
+    if((rdwr & RRD_CREAT) && (rrd->stat_head != NULL))
+    {
+        header_len = rrd_get_header_size(rrd);
+
+        value_cnt = 0;
+        for (ui = 0; ui < rrd->stat_head->rra_cnt; ui++)
+            value_cnt += rrd->stat_head->ds_cnt * rrd->rra_def[ui].row_cnt;
+
+        data_len = sizeof(rrd_value_t) * value_cnt;
+
+        newfile_size = header_len + data_len;
+    }
+    
+    rrd_file = (rrd_file_t*)malloc(sizeof(rrd_file_t));
+    if (rrd_file == NULL) {
+        rrd_set_error("allocating rrd_file descriptor for '%s'", file_name);
+        return NULL;
+    }
+    memset(rrd_file, 0, sizeof(rrd_file_t));
+
+    rrd_file->pvt = malloc(sizeof(rrd_simple_file_t));
+    if(rrd_file->pvt == NULL) {
+        rrd_set_error("allocating rrd_simple_file for '%s'", file_name);
+        return NULL;
+    }
+    memset(rrd_file->pvt, 0, sizeof(rrd_simple_file_t));
+    rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
+
+#ifdef DEBUG
+    if ((rdwr & (RRD_READONLY | RRD_READWRITE)) ==
+        (RRD_READONLY | RRD_READWRITE)) {
+        /* Both READONLY and READWRITE were given, which is invalid.  */
+        rrd_set_error("in read/write request mask");
+        exit(-1);
+    }
+#endif
+
+#ifdef HAVE_MMAP
+    rrd_simple_file->mm_prot = PROT_READ;
+    rrd_simple_file->mm_flags = 0;
+#endif
+
+    if (rdwr & RRD_READONLY) {
+        flags |= O_RDONLY;
+#ifdef HAVE_MMAP
+        rrd_simple_file->mm_flags = MAP_PRIVATE;
+# ifdef MAP_NORESERVE
+        rrd_simple_file->mm_flags |= MAP_NORESERVE;  /* readonly, so no swap backing needed */
+# endif
+#endif
+    } else {
+        if (rdwr & RRD_READWRITE) {
+            flags |= O_RDWR;
+#ifdef HAVE_MMAP 
+            rrd_simple_file->mm_flags = MAP_SHARED; 
+            rrd_simple_file->mm_prot |= PROT_WRITE; 
+#endif 
+        }
+        if (rdwr & RRD_CREAT) {
+            flags |= (O_CREAT | O_TRUNC);
+        }
+        if (rdwr & RRD_EXCL) {
+            flags |= O_EXCL;
+        }
+    }
+    if (rdwr & RRD_READAHEAD) {
+#ifdef MAP_POPULATE
+        rrd_simple_file->mm_flags |= MAP_POPULATE;   /* populate ptes and data */
+#endif
+#if defined MAP_NONBLOCK
+        rrd_simple_file->mm_flags |= MAP_NONBLOCK;   /* just populate ptes */
+#endif
+    }
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+    flags |= O_BINARY;
+#endif
+
+    if ((rrd_simple_file->fd = open(file_name, flags, 0666)) < 0) {
+        rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
+        goto out_free;
+    }
+
+#ifdef HAVE_MMAP
+#ifdef HAVE_BROKEN_MS_ASYNC
+    if (rdwr & RRD_READWRITE) {    
+        /* some unices, the files mtime does not get update    
+           on msync MS_ASYNC, in order to help them,     
+           we update the the timestamp at this point.      
+           The thing happens pretty 'close' to the open    
+           call so the chances of a race should be minimal.    
+                
+           Maybe ask your vendor to fix your OS ... */    
+           utime(file_name,NULL);  
+    }
+#endif    
+#endif
+
+    /* Better try to avoid seeks as much as possible. stat may be heavy but
+     * many concurrent seeks are even worse.  */
+    if (newfile_size == 0 && ((fstat(rrd_simple_file->fd, &statb)) < 0)) {
+        rrd_set_error("fstat '%s': %s", file_name, rrd_strerror(errno));
+        goto out_close;
+    }
+    if (newfile_size == 0) {
+        rrd_file->file_len = statb.st_size;
+    } else {
+        rrd_file->file_len = newfile_size;
+        lseek(rrd_simple_file->fd, newfile_size - 1, SEEK_SET);
+        if ( write(rrd_simple_file->fd, "\0", 1) == -1){    /* poke */
+            rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno));
+            goto out_close;
+        }
+        lseek(rrd_simple_file->fd, 0, SEEK_SET);
+    }
+#ifdef HAVE_POSIX_FADVISE
+    /* In general we need no read-ahead when dealing with rrd_files.
+       When we stop reading, it is highly unlikely that we start up again.
+       In this manner we actually save time and diskaccess (and buffer cache).
+       Thanks to Dave Plonka for the Idea of using POSIX_FADV_RANDOM here. */
+    posix_fadvise(rrd_simple_file->fd, 0, 0, POSIX_FADV_RANDOM);
+#endif
+
+/*
+        if (rdwr & RRD_READWRITE)
+        {
+           if (setvbuf((rrd_simple_file->fd),NULL,_IONBF,2)) {
+                  rrd_set_error("failed to disable the stream buffer\n");
+                  return (-1);
+           }
+        }
+*/
+
+#ifdef HAVE_MMAP
+    data = mmap(0, rrd_file->file_len, 
+        rrd_simple_file->mm_prot, rrd_simple_file->mm_flags,
+        rrd_simple_file->fd, offset);
+
+    /* lets see if the first read worked */
+    if (data == MAP_FAILED) {
+        rrd_set_error("mmaping file '%s': %s", file_name,
+                      rrd_strerror(errno));
+        goto out_close;
+    }
+    rrd_simple_file->file_start = data;
+    if (rdwr & RRD_CREAT) {
+        memset(data, DNAN, newfile_size - 1);
+        goto out_done;
+    }
+#endif
+    if (rdwr & RRD_CREAT)
+        goto out_done;
+#ifdef USE_MADVISE
+    if (rdwr & RRD_COPY) {
+        /* We will read everything in a moment (copying) */
+        madvise(data, rrd_file->file_len, MADV_WILLNEED );
+        madvise(data, rrd_file->file_len, MADV_SEQUENTIAL );
+    } else {
+        /* We do not need to read anything in for the moment */
+        madvise(data, rrd_file->file_len, MADV_RANDOM);
+        /* the stat_head will be needed soonish, so hint accordingly */
+        madvise(data, sizeof(stat_head_t), MADV_WILLNEED);
+        madvise(data, sizeof(stat_head_t), MADV_RANDOM);
+    }
+#endif
+
+    __rrd_read(rrd->stat_head, stat_head_t,
+               1);
+
+    /* lets do some test if we are on track ... */
+    if (memcmp(rrd->stat_head->cookie, RRD_COOKIE, sizeof(RRD_COOKIE)) != 0) {
+        rrd_set_error("'%s' is not an RRD file", file_name);
+        goto out_nullify_head;
+    }
+
+    if (rrd->stat_head->float_cookie != FLOAT_COOKIE) {
+        rrd_set_error("This RRD was created on another architecture");
+        goto out_nullify_head;
+    }
+
+    version = atoi(rrd->stat_head->version);
+
+    if (version > atoi(RRD_VERSION)) {
+        rrd_set_error("can't handle RRD file version %s",
+                      rrd->stat_head->version);
+        goto out_nullify_head;
+    }
+#if defined USE_MADVISE
+    /* the ds_def will be needed soonish, so hint accordingly */
+    madvise(data + PAGE_START(offset),
+            sizeof(ds_def_t) * rrd->stat_head->ds_cnt, MADV_WILLNEED);
+#endif
+    __rrd_read(rrd->ds_def, ds_def_t,
+               rrd->stat_head->ds_cnt);
+
+#if defined USE_MADVISE
+    /* the rra_def will be needed soonish, so hint accordingly */
+    madvise(data + PAGE_START(offset),
+            sizeof(rra_def_t) * rrd->stat_head->rra_cnt, MADV_WILLNEED);
+#endif
+    __rrd_read(rrd->rra_def, rra_def_t,
+               rrd->stat_head->rra_cnt);
+
+    /* handle different format for the live_head */
+    if (version < 3) {
+        rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
+        if (rrd->live_head == NULL) {
+            rrd_set_error("live_head_t malloc");
+            goto out_close;
+        }
+#if defined USE_MADVISE
+        /* the live_head will be needed soonish, so hint accordingly */
+        madvise(data + PAGE_START(offset), sizeof(time_t), MADV_WILLNEED);
+#endif
+        __rrd_read(rrd->legacy_last_up, time_t,
+                   1);
+
+        rrd->live_head->last_up = *rrd->legacy_last_up;
+        rrd->live_head->last_up_usec = 0;
+    } else {
+#if defined USE_MADVISE
+        /* the live_head will be needed soonish, so hint accordingly */
+        madvise(data + PAGE_START(offset),
+                sizeof(live_head_t), MADV_WILLNEED);
+#endif
+        __rrd_read(rrd->live_head, live_head_t,
+                   1);
+    }
+    __rrd_read(rrd->pdp_prep, pdp_prep_t,
+               rrd->stat_head->ds_cnt);
+    __rrd_read(rrd->cdp_prep, cdp_prep_t,
+               rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt);
+    __rrd_read(rrd->rra_ptr, rra_ptr_t,
+               rrd->stat_head->rra_cnt);
+
+    rrd_file->header_len = offset;
+    rrd_file->pos = offset;
+
+    {
+      unsigned long row_cnt = 0;
+
+      for (ui=0; ui<rrd->stat_head->rra_cnt; ui++)
+        row_cnt += rrd->rra_def[ui].row_cnt;
+
+      size_t  correct_len = rrd_file->header_len +
+        sizeof(rrd_value_t) * row_cnt * rrd->stat_head->ds_cnt;
+
+      if (correct_len > rrd_file->file_len)
+      {
+        rrd_set_error("'%s' is too small (should be %ld bytes)",
+                      file_name, (long long) correct_len);
+        goto out_nullify_head;
+      }
+    }
+
+  out_done:
+    return (rrd_file);
+  out_nullify_head:
+    rrd->stat_head = NULL;
+  out_close:
+#ifdef HAVE_MMAP
+    if (data != MAP_FAILED)
+      munmap(data, rrd_file->file_len);
+#endif
+
+    close(rrd_simple_file->fd);
+  out_free:
+    free(rrd_file->pvt);
+    free(rrd_file);
+    return NULL;
+}
+
+
+#if defined DEBUG && DEBUG > 1
+/* Print list of in-core pages of a the current rrd_file.  */
+static
+void mincore_print(
+    rrd_file_t *rrd_file,
+    char *mark)
+{
+    rrd_simple_file_t *rrd_simple_file;
+    rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
+#ifdef HAVE_MMAP
+    /* pretty print blocks in core */
+    size_t     off;
+    unsigned char *vec;
+    ssize_t   _page_size = sysconf(_SC_PAGESIZE);
+
+    off = rrd_file->file_len +
+        ((rrd_file->file_len + _page_size - 1) / _page_size);
+    vec = malloc(off);
+    if (vec != NULL) {
+        memset(vec, 0, off);
+        if (mincore(rrd_simple_file->file_start, rrd_file->file_len, vec) == 0) {
+            int       prev;
+            unsigned  is_in = 0, was_in = 0;
+
+            for (off = 0, prev = 0; off < rrd_file->file_len; ++off) {
+                is_in = vec[off] & 1;   /* if lsb set then is core resident */
+                if (off == 0)
+                    was_in = is_in;
+                if (was_in != is_in) {
+                    fprintf(stderr, "%s: %sin core: %p len %ld\n", mark,
+                            was_in ? "" : "not ", vec + prev, off - prev);
+                    was_in = is_in;
+                    prev = off;
+                }
+            }
+            fprintf(stderr,
+                    "%s: %sin core: %p len %ld\n", mark,
+                    was_in ? "" : "not ", vec + prev, off - prev);
+        } else
+            fprintf(stderr, "mincore: %s", rrd_strerror(errno));
+    }
+#else
+    fprintf(stderr, "sorry mincore only works with mmap");
+#endif
+}
+#endif                          /* defined DEBUG && DEBUG > 1 */
+
+/*
+ * get exclusive lock to whole file.
+ * lock gets removed when we close the file
+ *
+ * returns 0 on success
+ */
+int rrd_lock(
+    rrd_file_t *rrd_file)
+{
+    int       rcstat;
+    rrd_simple_file_t *rrd_simple_file;
+    rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
+
+    {
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+        struct _stat st;
+
+        if (_fstat(rrd_simple_file->fd, &st) == 0) {
+            rcstat = _locking(rrd_simple_file->fd, _LK_NBLCK, st.st_size);
+        } else {
+            rcstat = -1;
+        }
+#else
+        struct flock lock;
+
+        lock.l_type = F_WRLCK;  /* exclusive write lock */
+        lock.l_len = 0; /* whole file */
+        lock.l_start = 0;   /* start of file */
+        lock.l_whence = SEEK_SET;   /* end of file */
+
+        rcstat = fcntl(rrd_simple_file->fd, F_SETLK, &lock);
+#endif
+    }
+
+    return (rcstat);
+}
+
+
+/* drop cache except for the header and the active pages */
+void rrd_dontneed(
+    rrd_file_t *rrd_file,
+    rrd_t *rrd)
+{
+    rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
+#if defined USE_MADVISE || defined HAVE_POSIX_FADVISE
+    size_t dontneed_start;
+    size_t rra_start;
+    size_t active_block;
+    size_t i;
+    ssize_t   _page_size = sysconf(_SC_PAGESIZE);
+
+    if (rrd_file == NULL) {
+#if defined DEBUG && DEBUG
+           fprintf (stderr, "rrd_dontneed: Argument 'rrd_file' is NULL.\n");
+#endif
+           return;
+    }
+
+#if defined DEBUG && DEBUG > 1
+    mincore_print(rrd_file, "before");
+#endif
+
+    /* ignoring errors from RRDs that are smaller then the file_len+rounding */
+    rra_start = rrd_file->header_len;
+    dontneed_start = PAGE_START(rra_start) + _page_size;
+    for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
+        active_block =
+            PAGE_START(rra_start
+                       + rrd->rra_ptr[i].cur_row
+                       * rrd->stat_head->ds_cnt * sizeof(rrd_value_t));
+        if (active_block > dontneed_start) {
+#ifdef USE_MADVISE
+            madvise(rrd_simple_file->file_start + dontneed_start,
+                    active_block - dontneed_start - 1, MADV_DONTNEED);
+#endif
+/* in linux at least only fadvise DONTNEED seems to purge pages from cache */
+#ifdef HAVE_POSIX_FADVISE
+            posix_fadvise(rrd_simple_file->fd, dontneed_start,
+                          active_block - dontneed_start - 1,
+                          POSIX_FADV_DONTNEED);
+#endif
+        }
+        dontneed_start = active_block;
+        /* do not release 'hot' block if update for this RAA will occur
+         * within 10 minutes */
+        if (rrd->stat_head->pdp_step * rrd->rra_def[i].pdp_cnt -
+            rrd->live_head->last_up % (rrd->stat_head->pdp_step *
+                                       rrd->rra_def[i].pdp_cnt) < 10 * 60) {
+            dontneed_start += _page_size;
+        }
+        rra_start +=
+            rrd->rra_def[i].row_cnt * rrd->stat_head->ds_cnt *
+            sizeof(rrd_value_t);
+    }
+
+    if (dontneed_start < rrd_file->file_len) {
+#ifdef USE_MADVISE
+           madvise(rrd_simple_file->file_start + dontneed_start,
+                   rrd_file->file_len - dontneed_start, MADV_DONTNEED);
+#endif
+#ifdef HAVE_POSIX_FADVISE
+           posix_fadvise(rrd_simple_file->fd, dontneed_start,
+                         rrd_file->file_len - dontneed_start,
+                         POSIX_FADV_DONTNEED);
+#endif
+    }
+
+#if defined DEBUG && DEBUG > 1
+    mincore_print(rrd_file, "after");
+#endif
+#endif                          /* without madvise and posix_fadvise it does not make much sense todo anything */
+}
+
+
+
+
+
+int rrd_close(
+    rrd_file_t *rrd_file)
+{
+    rrd_simple_file_t *rrd_simple_file;
+    rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
+    int       ret;
+
+#ifdef HAVE_MMAP
+    ret = msync(rrd_simple_file->file_start, rrd_file->file_len, MS_ASYNC);
+    if (ret != 0)
+        rrd_set_error("msync rrd_file: %s", rrd_strerror(errno));
+    ret = munmap(rrd_simple_file->file_start, rrd_file->file_len);
+    if (ret != 0)
+        rrd_set_error("munmap rrd_file: %s", rrd_strerror(errno));
+#endif
+    ret = close(rrd_simple_file->fd);
+    if (ret != 0)
+        rrd_set_error("closing file: %s", rrd_strerror(errno));
+    free(rrd_file->pvt);
+    free(rrd_file);
+    rrd_file = NULL;
+    return ret;
+}
+
+
+/* Set position of rrd_file.  */
+
+off_t rrd_seek(
+    rrd_file_t *rrd_file,
+    off_t off,
+    int whence)
+{
+    off_t     ret = 0;
+    rrd_simple_file_t *rrd_simple_file;
+    rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
+
+#ifdef HAVE_MMAP
+    if (whence == SEEK_SET)
+        rrd_file->pos = off;
+    else if (whence == SEEK_CUR)
+        rrd_file->pos += off;
+    else if (whence == SEEK_END)
+        rrd_file->pos = rrd_file->file_len + off;
+#else
+    ret = lseek(rrd_simple_file->fd, off, whence);
+    if (ret < 0)
+        rrd_set_error("lseek: %s", rrd_strerror(errno));
+    rrd_file->pos = ret;
+#endif
+    /* mimic fseek, which returns 0 upon success */
+    return ret < 0;     /*XXX: or just ret to mimic lseek */
+}
+
+
+/* Get current position in rrd_file.  */
+
+off_t rrd_tell(
+    rrd_file_t *rrd_file)
+{
+    return rrd_file->pos;
+}
+
+
+/* Read count bytes into buffer buf, starting at rrd_file->pos.
+ * Returns the number of bytes read or <0 on error.  */
+
+ssize_t rrd_read(
+    rrd_file_t *rrd_file,
+    void *buf,
+    size_t count)
+{
+    rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
+#ifdef HAVE_MMAP
+    size_t    _cnt = count;
+    ssize_t   _surplus;
+
+    if (rrd_file->pos > rrd_file->file_len || _cnt == 0)    /* EOF */
+        return 0;
+    if (buf == NULL)
+        return -1;      /* EINVAL */
+    _surplus = rrd_file->pos + _cnt - rrd_file->file_len;
+    if (_surplus > 0) { /* short read */
+        _cnt -= _surplus;
+    }
+    if (_cnt == 0)
+        return 0;       /* EOF */
+    buf = memcpy(buf, rrd_simple_file->file_start + rrd_file->pos, _cnt);
+
+    rrd_file->pos += _cnt;  /* mimmic read() semantics */
+    return _cnt;
+#else
+    ssize_t   ret;
+
+    ret = read(rrd_simple_file->fd, buf, count);
+    if (ret > 0)
+        rrd_file->pos += ret;   /* mimmic read() semantics */
+    return ret;
+#endif
+}
+
+
+/* Write count bytes from buffer buf to the current position
+ * rrd_file->pos of rrd_simple_file->fd.
+ * Returns the number of bytes written or <0 on error.  */
+
+ssize_t rrd_write(
+    rrd_file_t *rrd_file,
+    const void *buf,
+    size_t count)
+{
+    rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
+#ifdef HAVE_MMAP
+    size_t old_size = rrd_file->file_len;
+    if (count == 0)
+        return 0;
+    if (buf == NULL)
+        return -1;      /* EINVAL */
+    
+    if((rrd_file->pos + count) > old_size)
+    {
+        rrd_set_error("attempting to write beyond end of file");
+        return -1;
+    }
+    memcpy(rrd_simple_file->file_start + rrd_file->pos, buf, count);
+    rrd_file->pos += count;
+    return count;       /* mimmic write() semantics */
+#else
+    ssize_t   _sz = write(rrd_simple_file->fd, buf, count);
+
+    if (_sz > 0)
+        rrd_file->pos += _sz;
+    return _sz;
+#endif
+}
+
+
+/* this is a leftover from the old days, it serves no purpose
+   and is therefore turned into a no-op */
+void rrd_flush(
+    rrd_file_t *rrd_file  __attribute__((unused)))
+{
+}
+
+/* Initialize RRD header.  */
+
+void rrd_init(
+    rrd_t *rrd)
+{
+    rrd->stat_head = NULL;
+    rrd->ds_def = NULL;
+    rrd->rra_def = NULL;
+    rrd->live_head = NULL;
+    rrd->legacy_last_up = NULL;
+    rrd->rra_ptr = NULL;
+    rrd->pdp_prep = NULL;
+    rrd->cdp_prep = NULL;
+    rrd->rrd_value = NULL;
+}
+
+
+/* free RRD header data.  */
+
+#ifdef HAVE_MMAP
+void rrd_free(
+    rrd_t *rrd)
+{
+    if (rrd->legacy_last_up) {  /* this gets set for version < 3 only */
+        free(rrd->live_head);
+    }
+}
+#else
+void rrd_free(
+    rrd_t *rrd)
+{
+    free(rrd->live_head);
+    free(rrd->stat_head);
+    free(rrd->ds_def);
+    free(rrd->rra_def);
+    free(rrd->rra_ptr);
+    free(rrd->pdp_prep);
+    free(rrd->cdp_prep);
+    free(rrd->rrd_value);
+}
+#endif
+
+
+/* routine used by external libraries to free memory allocated by
+ * rrd library */
+
+void rrd_freemem(
+    void *mem)
+{
+    free(mem);
+}
+
+/*
+ * rra_update informs us about the RRAs being updated
+ * The low level storage API may use this information for
+ * aligning RRAs within stripes, or other performance enhancements
+ */
+void rrd_notify_row(
+    rrd_file_t *rrd_file  __attribute__((unused)),
+    int rra_idx  __attribute__((unused)),
+    unsigned long rra_row  __attribute__((unused)),
+    time_t rra_time  __attribute__((unused)))
+{
+}
+
+/*
+ * This function is called when creating a new RRD
+ * The storage implementation can use this opportunity to select
+ * a sensible starting row within the file.
+ * The default implementation is random, to ensure that all RRAs
+ * don't change to a new disk block at the same time
+ */
+unsigned long rrd_select_initial_row(
+    rrd_file_t *rrd_file  __attribute__((unused)),
+    int rra_idx  __attribute__((unused)),
+    rra_def_t *rra
+    )
+{
+    return rrd_random() % rra->row_cnt;
+}
diff --git a/program/src/rrd_parsetime.c b/program/src/rrd_parsetime.c
new file mode 100644 (file)
index 0000000..f3a8e2e
--- /dev/null
@@ -0,0 +1,1046 @@
+/*  
+ *  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 <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "rrd_tool.h"
+
+/* Structures and unions */
+
+enum {                  /* symbols */
+    MIDNIGHT, NOON, TEATIME,
+    PM, AM, YESTERDAY, TODAY, TOMORROW, NOW, START, END,
+    SECONDS, MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS,
+    MONTHS_MINUTES,
+    NUMBER, PLUS, MINUS, DOT, COLON, SLASH, ID, JUNK,
+    JAN, FEB, MAR, APR, MAY, JUN,
+    JUL, AUG, SEP, OCT, NOV, DEC,
+    SUN, MON, TUE, WED, THU, FRI, SAT
+};
+
+/* the below is for plus_minus() */
+#define PREVIOUS_OP    (-1)
+
+/* parse translation table - table driven parsers can be your FRIEND!
+ */
+struct SpecialToken {
+    char     *name;     /* token name */
+    int       value;    /* token id */
+};
+static const struct SpecialToken VariousWords[] = {
+    {"midnight", MIDNIGHT}, /* 00:00:00 of today or tomorrow */
+    {"noon", NOON},     /* 12:00:00 of today or tomorrow */
+    {"teatime", TEATIME},   /* 16:00:00 of today or tomorrow */
+    {"am", AM},         /* morning times for 0-12 clock */
+    {"pm", PM},         /* evening times for 0-12 clock */
+    {"tomorrow", TOMORROW},
+    {"yesterday", YESTERDAY},
+    {"today", TODAY},
+    {"now", NOW},
+    {"n", NOW},
+    {"start", START},
+    {"s", START},
+    {"end", END},
+    {"e", END},
+
+    {"jan", JAN},
+    {"feb", FEB},
+    {"mar", MAR},
+    {"apr", APR},
+    {"may", MAY},
+    {"jun", JUN},
+    {"jul", JUL},
+    {"aug", AUG},
+    {"sep", SEP},
+    {"oct", OCT},
+    {"nov", NOV},
+    {"dec", DEC},
+    {"january", JAN},
+    {"february", FEB},
+    {"march", MAR},
+    {"april", APR},
+    {"may", MAY},
+    {"june", JUN},
+    {"july", JUL},
+    {"august", AUG},
+    {"september", SEP},
+    {"october", OCT},
+    {"november", NOV},
+    {"december", DEC},
+    {"sunday", SUN},
+    {"sun", SUN},
+    {"monday", MON},
+    {"mon", MON},
+    {"tuesday", TUE},
+    {"tue", TUE},
+    {"wednesday", WED},
+    {"wed", WED},
+    {"thursday", THU},
+    {"thu", THU},
+    {"friday", FRI},
+    {"fri", FRI},
+    {"saturday", SAT},
+    {"sat", SAT},
+    {NULL, 0}           /*** SENTINEL ***/
+};
+
+static const struct SpecialToken TimeMultipliers[] = {
+    {"second", SECONDS},    /* seconds multiplier */
+    {"seconds", SECONDS},   /* (pluralized) */
+    {"sec", SECONDS},   /* (generic) */
+    {"s", SECONDS},     /* (short generic) */
+    {"minute", MINUTES},    /* minutes multiplier */
+    {"minutes", MINUTES},   /* (pluralized) */
+    {"min", MINUTES},   /* (generic) */
+    {"m", MONTHS_MINUTES},  /* (short generic) */
+    {"hour", HOURS},    /* hours ... */
+    {"hours", HOURS},   /* (pluralized) */
+    {"hr", HOURS},      /* (generic) */
+    {"h", HOURS},       /* (short generic) */
+    {"day", DAYS},      /* days ... */
+    {"days", DAYS},     /* (pluralized) */
+    {"d", DAYS},        /* (short generic) */
+    {"week", WEEKS},    /* week ... */
+    {"weeks", WEEKS},   /* (pluralized) */
+    {"wk", WEEKS},      /* (generic) */
+    {"w", WEEKS},       /* (short generic) */
+    {"month", MONTHS},  /* week ... */
+    {"months", MONTHS}, /* (pluralized) */
+    {"mon", MONTHS},    /* (generic) */
+    {"year", YEARS},    /* year ... */
+    {"years", YEARS},   /* (pluralized) */
+    {"yr", YEARS},      /* (generic) */
+    {"y", YEARS},       /* (short generic) */
+    {NULL, 0}           /*** SENTINEL ***/
+};
+
+/* File scope variables */
+
+/* context dependent list of specials for parser to recognize,
+ * required for us to be able distinguish between 'mon' as 'month'
+ * and 'mon' as 'monday'
+ */
+static const struct SpecialToken *Specials;
+
+static const char **scp;    /* scanner - pointer at arglist */
+static char scc;        /* scanner - count of remaining arguments */
+static const char *sct; /* scanner - next char pointer in current argument */
+static int need;        /* scanner - need to advance to next argument */
+
+static char *sc_token = NULL;   /* scanner - token buffer */
+static size_t sc_len;   /* scanner - length of token buffer */
+static int sc_tokid;    /* scanner - token id */
+
+/* Local functions */
+static void EnsureMemFree(
+    void);
+
+static void EnsureMemFree(
+    void)
+{
+    if (sc_token) {
+        free(sc_token);
+        sc_token = NULL;
+    }
+}
+
+/*
+ * A hack to compensate for the lack of the C++ exceptions
+ *
+ * Every function func that might generate parsing "exception"
+ * should return TIME_OK (aka NULL) or pointer to the error message,
+ * and should be called like this: try(func(args));
+ *
+ * if the try is not successful it will reset the token pointer ...
+ *
+ * [NOTE: when try(...) is used as the only statement in the "if-true"
+ *  part of the if statement that also has an "else" part it should be
+ *  either enclosed in the curly braces (despite the fact that it looks
+ *  like a single statement) or NOT followed by the ";"]
+ */
+#define try(b)         { \
+                       char *_e; \
+                       if((_e=(b))) \
+                         { \
+                         EnsureMemFree(); \
+                         return _e; \
+                         } \
+                       }
+
+/*
+ * The panic() function was used in the original code to die, we redefine
+ * it as macro to start the chain of ascending returns that in conjunction
+ * with the try(b) above will simulate a sort of "exception handling"
+ */
+
+#define panic(e)       { \
+                       return (e); \
+                       }
+
+/*
+ * ve() and e() are used to set the return error,
+ * the most appropriate use for these is inside panic(...) 
+ */
+#define MAX_ERR_MSG_LEN        1024
+static char errmsg[MAX_ERR_MSG_LEN];
+
+static char *ve(
+    char *fmt,
+    va_list ap)
+{
+#ifdef HAVE_VSNPRINTF
+    vsnprintf(errmsg, MAX_ERR_MSG_LEN, fmt, ap);
+#else
+    vsprintf(errmsg, fmt, ap);
+#endif
+    EnsureMemFree();
+    return (errmsg);
+}
+
+static char *e(
+    char *fmt,
+    ...)
+{
+    char     *err;
+    va_list   ap;
+
+    va_start(ap, fmt);
+    err = ve(fmt, ap);
+    va_end(ap);
+    return (err);
+}
+
+/* Compare S1 and S2, ignoring case, returning less than, equal to or
+   greater than zero if S1 is lexicographically less than,
+   equal to or greater than S2.  -- copied from GNU libc*/
+static int mystrcasecmp(
+    const char *s1,
+    const char *s2)
+{
+    const unsigned char *p1 = (const unsigned char *) s1;
+    const unsigned char *p2 = (const unsigned char *) s2;
+    unsigned char c1, c2;
+
+    if (p1 == p2)
+        return 0;
+
+    do {
+        c1 = tolower(*p1++);
+        c2 = tolower(*p2++);
+        if (c1 == '\0')
+            break;
+    }
+    while (c1 == c2);
+
+    return c1 - c2;
+}
+
+/*
+ * parse a token, checking if it's something special to us
+ */
+static int parse_token(
+    char *arg)
+{
+    int       i;
+
+    for (i = 0; Specials[i].name != NULL; i++)
+        if (mystrcasecmp(Specials[i].name, arg) == 0)
+            return sc_tokid = Specials[i].value;
+
+    /* not special - must be some random id */
+    return sc_tokid = ID;
+}                       /* parse_token */
+
+
+
+/*
+ * init_scanner() sets up the scanner to eat arguments
+ */
+static char *init_scanner(
+    int argc,
+    const char **argv)
+{
+    scp = argv;
+    scc = argc;
+    need = 1;
+    sc_len = 1;
+    while (argc-- > 0)
+        sc_len += strlen(*argv++);
+
+    sc_token = (char *) malloc(sc_len * sizeof(char));
+    if (sc_token == NULL)
+        return "Failed to allocate memory";
+    return TIME_OK;
+}                       /* init_scanner */
+
+/*
+ * token() fetches a token from the input stream
+ */
+static int token(
+    void)
+{
+    int       idx;
+
+    while (1) {
+        memset(sc_token, '\0', sc_len);
+        sc_tokid = EOF;
+        idx = 0;
+
+        /* if we need to read another argument, walk along the argument list;
+         * when we fall off the arglist, we'll just return EOF forever
+         */
+        if (need) {
+            if (scc < 1)
+                return sc_tokid;
+            sct = *scp;
+            scp++;
+            scc--;
+            need = 0;
+        }
+        /* eat whitespace now - if we walk off the end of the argument,
+         * we'll continue, which puts us up at the top of the while loop
+         * to fetch the next argument in
+         */
+        while (isspace((unsigned char) *sct) || *sct == '_' || *sct == ',')
+            ++sct;
+        if (!*sct) {
+            need = 1;
+            continue;
+        }
+
+        /* preserve the first character of the new token
+         */
+        sc_token[0] = *sct++;
+
+        /* then see what it is
+         */
+        if (isdigit((unsigned char) (sc_token[0]))) {
+            while (isdigit((unsigned char) (*sct)))
+                sc_token[++idx] = *sct++;
+            sc_token[++idx] = '\0';
+            return sc_tokid = NUMBER;
+        } else if (isalpha((unsigned char) (sc_token[0]))) {
+            while (isalpha((unsigned char) (*sct)))
+                sc_token[++idx] = *sct++;
+            sc_token[++idx] = '\0';
+            return parse_token(sc_token);
+        } else
+            switch (sc_token[0]) {
+            case ':':
+                return sc_tokid = COLON;
+            case '.':
+                return sc_tokid = DOT;
+            case '+':
+                return sc_tokid = PLUS;
+            case '-':
+                return sc_tokid = MINUS;
+            case '/':
+                return sc_tokid = SLASH;
+            default:
+                /*OK, we did not make it ... */
+                sct--;
+                return sc_tokid = EOF;
+            }
+    }                   /* while (1) */
+}                       /* token */
+
+
+/* 
+ * expect2() gets a token and complains if it's not the token we want
+ */
+static char *expect2(
+    int desired,
+    char *complain_fmt,
+    ...)
+{
+    va_list   ap;
+
+    va_start(ap, complain_fmt);
+    if (token() != desired) {
+        panic(ve(complain_fmt, ap));
+    }
+    va_end(ap);
+    return TIME_OK;
+
+}                       /* expect2 */
+
+
+/*
+ * plus_minus() is used to parse a single NUMBER TIME-UNIT pair
+ *              for the OFFSET-SPEC.
+ *              It also applies those m-guessing heuristics.
+ */
+static char *plus_minus(
+    rrd_time_value_t * ptv,
+    int doop)
+{
+    static int op = PLUS;
+    static int prev_multiplier = -1;
+    int       delta;
+
+    if (doop >= 0) {
+        op = doop;
+        try(expect2
+            (NUMBER, "There should be number after '%c'",
+             op == PLUS ? '+' : '-'));
+        prev_multiplier = -1;   /* reset months-minutes guessing mechanics */
+    }
+    /* if doop is < 0 then we repeat the previous op
+     * with the prefetched number */
+
+    delta = atoi(sc_token);
+
+    if (token() == MONTHS_MINUTES) {
+        /* hard job to guess what does that -5m means: -5mon or -5min? */
+        switch (prev_multiplier) {
+        case DAYS:
+        case WEEKS:
+        case MONTHS:
+        case YEARS:
+            sc_tokid = MONTHS;
+            break;
+
+        case SECONDS:
+        case MINUTES:
+        case HOURS:
+            sc_tokid = MINUTES;
+            break;
+
+        default:
+            if (delta < 6)  /* it may be some other value but in the context
+                             * of RRD who needs less than 6 min deltas? */
+                sc_tokid = MONTHS;
+            else
+                sc_tokid = MINUTES;
+        }
+    }
+    prev_multiplier = sc_tokid;
+    switch (sc_tokid) {
+    case YEARS:
+        ptv->tm.  tm_year += (
+    op == PLUS) ? delta : -delta;
+
+        return TIME_OK;
+    case MONTHS:
+        ptv->tm.  tm_mon += (
+    op == PLUS) ? delta : -delta;
+
+        return TIME_OK;
+    case WEEKS:
+        delta *= 7;
+        /* FALLTHRU */
+    case DAYS:
+        ptv->tm.  tm_mday += (
+    op == PLUS) ? delta : -delta;
+
+        return TIME_OK;
+    case HOURS:
+        ptv->offset += (op == PLUS) ? delta * 60 * 60 : -delta * 60 * 60;
+        return TIME_OK;
+    case MINUTES:
+        ptv->offset += (op == PLUS) ? delta * 60 : -delta * 60;
+        return TIME_OK;
+    case SECONDS:
+        ptv->offset += (op == PLUS) ? delta : -delta;
+        return TIME_OK;
+    default:           /*default unit is seconds */
+        ptv->offset += (op == PLUS) ? delta : -delta;
+        return TIME_OK;
+    }
+    panic(e("well-known time unit expected after %d", delta));
+    /* NORETURN */
+    return TIME_OK;     /* to make compiler happy :) */
+}                       /* plus_minus */
+
+
+/*
+ * tod() computes the time of day (TIME-OF-DAY-SPEC)
+ */
+static char *tod(
+    rrd_time_value_t * ptv)
+{
+    int       hour, minute = 0;
+    int       tlen;
+
+    /* save token status in  case we must abort */
+    int       scc_sv = scc;
+    const char *sct_sv = sct;
+    int       sc_tokid_sv = sc_tokid;
+
+    tlen = strlen(sc_token);
+
+    /* first pick out the time of day - we assume a HH (COLON|DOT) MM time
+     */
+    if (tlen > 2) {
+        return TIME_OK;
+    }
+
+    hour = atoi(sc_token);
+
+    token();
+    if (sc_tokid == SLASH || sc_tokid == DOT) {
+        /* guess we are looking at a date */
+        scc = scc_sv;
+        sct = sct_sv;
+        sc_tokid = sc_tokid_sv;
+        sprintf(sc_token, "%d", hour);
+        return TIME_OK;
+    }
+    if (sc_tokid == COLON) {
+        try(expect2(NUMBER,
+                    "Parsing HH:MM syntax, expecting MM as number, got none"));
+        minute = atoi(sc_token);
+        if (minute > 59) {
+            panic(e("parsing HH:MM syntax, got MM = %d (>59!)", minute));
+        }
+        token();
+    }
+
+    /* check if an AM or PM specifier was given
+     */
+    if (sc_tokid == AM || sc_tokid == PM) {
+        if (hour > 12) {
+            panic(e("there cannot be more than 12 AM or PM hours"));
+        }
+        if (sc_tokid == PM) {
+            if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */
+                hour += 12;
+        } else {
+            if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */
+                hour = 0;
+        }
+        token();
+    } else if (hour > 23) {
+        /* guess it was not a time then ... */
+        scc = scc_sv;
+        sct = sct_sv;
+        sc_tokid = sc_tokid_sv;
+        sprintf(sc_token, "%d", hour);
+        return TIME_OK;
+    }
+    ptv->tm.  tm_hour = hour;
+    ptv->tm.  tm_min = minute;
+    ptv->tm.  tm_sec = 0;
+
+    if (ptv->tm.tm_hour == 24) {
+        ptv->tm.  tm_hour = 0;
+        ptv->tm.  tm_mday++;
+    }
+    return TIME_OK;
+}                       /* tod */
+
+
+/*
+ * assign_date() assigns a date, adjusting year as appropriate
+ */
+static char *assign_date(
+    rrd_time_value_t * ptv,
+    long mday,
+    long mon,
+    long year)
+{
+    if (year > 138) {
+        if (year > 1970)
+            year -= 1900;
+        else {
+            panic(e("invalid year %d (should be either 00-99 or >1900)",
+                    year));
+        }
+    } else if (year >= 0 && year < 38) {
+        year += 100;    /* Allow year 2000-2037 to be specified as   */
+    }
+    /* 00-37 until the problem of 2038 year will */
+    /* arise for unices with 32-bit time_t :)    */
+    if (year < 70) {
+        panic(e("won't handle dates before epoch (01/01/1970), sorry"));
+    }
+
+    ptv->tm.  tm_mday = mday;
+    ptv->tm.  tm_mon = mon;
+    ptv->tm.  tm_year = year;
+
+    return TIME_OK;
+}                       /* assign_date */
+
+
+/* 
+ * day() picks apart DAY-SPEC-[12]
+ */
+static char *day(
+    rrd_time_value_t * ptv)
+{
+    /* using time_t seems to help portability with 64bit oses */
+    time_t    mday = 0, wday, mon, year = ptv->tm.tm_year;
+    int       tlen;
+
+    switch (sc_tokid) {
+    case YESTERDAY:
+        ptv->tm.  tm_mday--;
+
+        /* FALLTRHU */
+    case TODAY:        /* force ourselves to stay in today - no further processing */
+        token();
+        break;
+    case TOMORROW:
+        ptv->tm.  tm_mday++;
+
+        token();
+        break;
+
+    case JAN:
+    case FEB:
+    case MAR:
+    case APR:
+    case MAY:
+    case JUN:
+    case JUL:
+    case AUG:
+    case SEP:
+    case OCT:
+    case NOV:
+    case DEC:
+        /* do month mday [year]
+         */
+        mon = (sc_tokid - JAN);
+        try(expect2(NUMBER, "the day of the month should follow month name"));
+        mday = atol(sc_token);
+        if (token() == NUMBER) {
+            year = atol(sc_token);
+            token();
+        } else
+            year = ptv->tm.tm_year;
+
+        try(assign_date(ptv, mday, mon, year));
+        break;
+
+    case SUN:
+    case MON:
+    case TUE:
+    case WED:
+    case THU:
+    case FRI:
+    case SAT:
+        /* do a particular day of the week
+         */
+        wday = (sc_tokid - SUN);
+        ptv->tm.  tm_mday += (
+    wday - ptv->tm.tm_wday);
+
+        token();
+        break;
+        /*
+           mday = ptv->tm.tm_mday;
+           mday += (wday - ptv->tm.tm_wday);
+           ptv->tm.tm_wday = wday;
+
+           try(assign_date(ptv, mday, ptv->tm.tm_mon, ptv->tm.tm_year));
+           break;
+         */
+
+    case NUMBER:
+        /* get numeric <sec since 1970>, MM/DD/[YY]YY, or DD.MM.[YY]YY
+         */
+        tlen = strlen(sc_token);
+        mon = atol(sc_token);
+        if (mon > 10 * 365 * 24 * 60 * 60) {
+            ptv->tm = *localtime(&mon);
+
+            token();
+            break;
+        }
+
+        if (mon > 19700101 && mon < 24000101) { /*works between 1900 and 2400 */
+            char      cmon[3], cmday[3], cyear[5];
+
+            strncpy(cyear, sc_token, 4);
+            cyear[4] = '\0';
+            year = atol(cyear);
+            strncpy(cmon, &(sc_token[4]), 2);
+            cmon[2] = '\0';
+            mon = atol(cmon);
+            strncpy(cmday, &(sc_token[6]), 2);
+            cmday[2] = '\0';
+            mday = atol(cmday);
+            token();
+        } else {
+            token();
+
+            if (mon <= 31 && (sc_tokid == SLASH || sc_tokid == DOT)) {
+                int       sep;
+
+                sep = sc_tokid;
+                try(expect2(NUMBER, "there should be %s number after '%c'",
+                            sep == DOT ? "month" : "day",
+                            sep == DOT ? '.' : '/'));
+                mday = atol(sc_token);
+                if (token() == sep) {
+                    try(expect2
+                        (NUMBER, "there should be year number after '%c'",
+                         sep == DOT ? '.' : '/'));
+                    year = atol(sc_token);
+                    token();
+                }
+
+                /* flip months and days for European timing
+                 */
+                if (sep == DOT) {
+                    long      x = mday;
+
+                    mday = mon;
+                    mon = x;
+                }
+            }
+        }
+
+        mon--;
+        if (mon < 0 || mon > 11) {
+            panic(e("did you really mean month %d?", mon + 1));
+        }
+        if (mday < 1 || mday > 31) {
+            panic(e("I'm afraid that %d is not a valid day of the month",
+                    mday));
+        }
+        try(assign_date(ptv, mday, mon, year));
+        break;
+    }                   /* case */
+    return TIME_OK;
+}                       /* month */
+
+
+/* Global functions */
+
+
+/*
+ * rrd_parsetime() is the external interface that takes tspec, parses
+ * it and puts the result in the rrd_time_value structure *ptv.
+ * It can return either absolute times (these are ensured to be
+ * correct) or relative time references that are expected to be
+ * added to some absolute time value and then normalized by
+ * mktime() The return value is either TIME_OK (aka NULL) or
+ * the pointer to the error message in the case of problems
+ */
+char     *rrd_parsetime(
+    const char *tspec,
+    rrd_time_value_t * ptv)
+{
+    time_t    now = time(NULL);
+    int       hr = 0;
+
+    /* this MUST be initialized to zero for midnight/noon/teatime */
+
+    Specials = VariousWords;    /* initialize special words context */
+
+    try(init_scanner(1, &tspec));
+
+    /* establish the default time reference */
+    ptv->type = ABSOLUTE_TIME;
+    ptv->offset = 0;
+    ptv->tm = *localtime(&now);
+    ptv->tm.  tm_isdst = -1;    /* mk time can figure dst by default ... */
+
+    token();
+    switch (sc_tokid) {
+    case PLUS:
+    case MINUS:
+        break;          /* jump to OFFSET-SPEC part */
+
+    case START:
+        ptv->type = RELATIVE_TO_START_TIME;
+        goto KeepItRelative;
+    case END:
+        ptv->type = RELATIVE_TO_END_TIME;
+      KeepItRelative:
+        ptv->tm.  tm_sec = 0;
+        ptv->tm.  tm_min = 0;
+        ptv->tm.  tm_hour = 0;
+        ptv->tm.  tm_mday = 0;
+        ptv->tm.  tm_mon = 0;
+        ptv->tm.  tm_year = 0;
+
+        /* FALLTHRU */
+    case NOW:
+    {
+        int       time_reference = sc_tokid;
+
+        token();
+        if (sc_tokid == PLUS || sc_tokid == MINUS)
+            break;
+        if (time_reference != NOW) {
+            panic(e("'start' or 'end' MUST be followed by +|- offset"));
+        } else if (sc_tokid != EOF) {
+            panic(e("if 'now' is followed by a token it must be +|- offset"));
+        }
+    };
+        break;
+
+        /* Only absolute time specifications below */
+    case NUMBER:
+    {
+        long      hour_sv = ptv->tm.tm_hour;
+        long      year_sv = ptv->tm.tm_year;
+
+        ptv->tm.  tm_hour = 30;
+        ptv->tm.  tm_year = 30000;
+
+        try(tod(ptv))
+            try(day(ptv))
+            if (ptv->tm.tm_hour == 30 && ptv->tm.tm_year != 30000) {
+            try(tod(ptv))
+        }
+        if (ptv->tm.tm_hour == 30) {
+            ptv->tm.  tm_hour = hour_sv;
+        }
+        if (ptv->tm.tm_year == 30000) {
+            ptv->tm.  tm_year = year_sv;
+        }
+    };
+        break;
+        /* fix month parsing */
+    case JAN:
+    case FEB:
+    case MAR:
+    case APR:
+    case MAY:
+    case JUN:
+    case JUL:
+    case AUG:
+    case SEP:
+    case OCT:
+    case NOV:
+    case DEC:
+        try(day(ptv));
+        if (sc_tokid != NUMBER)
+            break;
+        try(tod(ptv))
+            break;
+
+        /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialized
+         * hr to zero up above, then fall into this case in such a
+         * way so we add +12 +4 hours to it for teatime, +12 hours
+         * to it for noon, and nothing at all for midnight, then
+         * set our rettime to that hour before leaping into the
+         * month scanner
+         */
+    case TEATIME:
+        hr += 4;
+        /* FALLTHRU */
+    case NOON:
+        hr += 12;
+        /* FALLTHRU */
+    case MIDNIGHT:
+        /* if (ptv->tm.tm_hour >= hr) {
+           ptv->tm.tm_mday++;
+           ptv->tm.tm_wday++;
+           } *//* shifting does not makes sense here ... noon is noon */
+        ptv->tm.  tm_hour = hr;
+        ptv->tm.  tm_min = 0;
+        ptv->tm.  tm_sec = 0;
+
+        token();
+        try(day(ptv));
+        break;
+    default:
+        panic(e("unparsable time: %s%s", sc_token, sct));
+        break;
+    }                   /* ugly case statement */
+
+    /*
+     * the OFFSET-SPEC part
+     *
+     * (NOTE, the sc_tokid was prefetched for us by the previous code)
+     */
+    if (sc_tokid == PLUS || sc_tokid == MINUS) {
+        Specials = TimeMultipliers; /* switch special words context */
+        while (sc_tokid == PLUS || sc_tokid == MINUS || sc_tokid == NUMBER) {
+            if (sc_tokid == NUMBER) {
+                try(plus_minus(ptv, PREVIOUS_OP));
+            } else
+                try(plus_minus(ptv, sc_tokid));
+            token();    /* We will get EOF eventually but that's OK, since
+                           token() will return us as many EOFs as needed */
+        }
+    }
+
+    /* now we should be at EOF */
+    if (sc_tokid != EOF) {
+        panic(e("unparsable trailing text: '...%s%s'", sc_token, sct));
+    }
+
+    if (ptv->type == ABSOLUTE_TIME)
+        if (mktime(&ptv->tm) == -1) {   /* normalize & check */
+            /* can happen for "nonexistent" times, e.g. around 3am */
+            /* when winter -> summer time correction eats a hour */
+            panic(e("the specified time is incorrect (out of range?)"));
+        }
+    EnsureMemFree();
+    return TIME_OK;
+}                       /* rrd_parsetime */
+
+
+int rrd_proc_start_end(
+    rrd_time_value_t * start_tv,
+    rrd_time_value_t * end_tv,
+    time_t *start,
+    time_t *end)
+{
+    if (start_tv->type == RELATIVE_TO_END_TIME &&   /* same as the line above */
+        end_tv->type == RELATIVE_TO_START_TIME) {
+        rrd_set_error("the start and end times cannot be specified "
+                      "relative to each other");
+        return -1;
+    }
+
+    if (start_tv->type == RELATIVE_TO_START_TIME) {
+        rrd_set_error
+            ("the start time cannot be specified relative to itself");
+        return -1;
+    }
+
+    if (end_tv->type == RELATIVE_TO_END_TIME) {
+        rrd_set_error("the end time cannot be specified relative to itself");
+        return -1;
+    }
+
+    if (start_tv->type == RELATIVE_TO_END_TIME) {
+        struct tm tmtmp;
+
+        *end = mktime(&(end_tv->tm)) + end_tv->offset;
+        tmtmp = *localtime(end);    /* reinit end including offset */
+        tmtmp.tm_mday += start_tv->tm.tm_mday;
+        tmtmp.tm_mon += start_tv->tm.tm_mon;
+        tmtmp.tm_year += start_tv->tm.tm_year;
+
+        *start = mktime(&tmtmp) + start_tv->offset;
+    } else {
+        *start = mktime(&(start_tv->tm)) + start_tv->offset;
+    }
+    if (end_tv->type == RELATIVE_TO_START_TIME) {
+        struct tm tmtmp;
+
+        *start = mktime(&(start_tv->tm)) + start_tv->offset;
+        tmtmp = *localtime(start);
+        tmtmp.tm_mday += end_tv->tm.tm_mday;
+        tmtmp.tm_mon += end_tv->tm.tm_mon;
+        tmtmp.tm_year += end_tv->tm.tm_year;
+
+        *end = mktime(&tmtmp) + end_tv->offset;
+    } else {
+        *end = mktime(&(end_tv->tm)) + end_tv->offset;
+    }
+    return 0;
+}                       /* rrd_proc_start_end */
diff --git a/program/src/rrd_parsetime.h b/program/src/rrd_parsetime.h
new file mode 100644 (file)
index 0000000..d9a34e8
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __PARSETIME_H__
+#define __PARSETIME_H__
+
+#include <stdio.h>
+
+#include "rrd.h"
+
+#endif
diff --git a/program/src/rrd_resize.c b/program/src/rrd_resize.c
new file mode 100644 (file)
index 0000000..8d8f532
--- /dev/null
@@ -0,0 +1,288 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_resize.c Alters size of an RRA
+ *****************************************************************************
+ * Initial version by Alex van den Bogaerdt
+ *****************************************************************************/
+
+#include <stdlib.h>
+
+#include "rrd_tool.h"
+
+int rrd_resize(
+    int argc,
+    char **argv)
+{
+    char     *infilename, outfilename[11] = "resize.rrd";
+    rrd_t     rrdold, rrdnew;
+    rrd_value_t buffer;
+    int       version;
+    unsigned long l, rra;
+    long      modify;
+    unsigned long target_rra;
+    int       grow = 0, shrink = 0;
+    char     *endptr;
+    rrd_file_t *rrd_file, *rrd_out_file;
+
+    infilename = argv[1];
+    if (!strcmp(infilename, "resize.rrd")) {
+        rrd_set_error("resize.rrd is a reserved name");
+        return (-1);
+    }
+    if (argc != 5) {
+        rrd_set_error("wrong number of parameters");
+        return (-1);
+    }
+
+    target_rra = strtol(argv[2], &endptr, 0);
+
+    if (!strcmp(argv[3], "GROW"))
+        grow = 1;
+    else if (!strcmp(argv[3], "SHRINK"))
+        shrink = 1;
+    else {
+        rrd_set_error("I can only GROW or SHRINK");
+        return (-1);
+    }
+
+    modify = strtol(argv[4], &endptr, 0);
+
+    if ((modify < 1)) {
+        rrd_set_error("Please grow or shrink with at least 1 row");
+        return (-1);
+    }
+
+    if (shrink)
+        modify = -modify;
+
+
+    rrd_init(&rrdold);
+    rrd_file = rrd_open(infilename, &rrdold, RRD_READWRITE | RRD_COPY);
+    if (rrd_file == NULL) {
+        rrd_free(&rrdold);
+        return (-1);
+    }
+
+    if (rrd_lock(rrd_file) != 0) {
+        rrd_set_error("could not lock original RRD");
+        rrd_free(&rrdold);
+        rrd_close(rrd_file);
+        return (-1);
+    }
+
+
+    if (target_rra >= rrdold.stat_head->rra_cnt) {
+        rrd_set_error("no such RRA in this RRD");
+        rrd_free(&rrdold);
+        rrd_close(rrd_file);
+        return (-1);
+    }
+
+    if (modify < 0)
+        if ((long) rrdold.rra_def[target_rra].row_cnt <= -modify) {
+            rrd_set_error("This RRA is not that big");
+            rrd_free(&rrdold);
+            rrd_close(rrd_file);
+            return (-1);
+        }
+
+    rrd_init(&rrdnew);
+    /* These need to be initialised before calling rrd_open() with 
+       the RRD_CREATE flag */
+
+    if ((rrdnew.stat_head = (stat_head_t*)calloc(1, sizeof(stat_head_t))) == NULL) {
+        rrd_set_error("allocating stat_head for new RRD");
+        rrd_free(&rrdold);
+        rrd_close(rrd_file);
+        return (-1);
+    }
+    memcpy(rrdnew.stat_head,rrdold.stat_head,sizeof(stat_head_t));
+
+    if ((rrdnew.rra_def = (rra_def_t *)malloc(sizeof(rra_def_t) * rrdold.stat_head->rra_cnt)) == NULL) {
+        rrd_set_error("allocating rra_def for new RRD");
+        rrd_free(&rrdnew);
+        rrd_free(&rrdold);
+        rrd_close(rrd_file);
+        return (-1);
+    }
+    memcpy(rrdnew.rra_def,rrdold.rra_def,sizeof(rra_def_t) * rrdold.stat_head->rra_cnt);
+
+    /* Set this so that the file will be created with the correct size */
+    rrdnew.rra_def[target_rra].row_cnt += modify;
+
+    rrd_out_file = rrd_open(outfilename, &rrdnew, RRD_READWRITE | RRD_CREAT);
+    if (rrd_out_file == NULL) {
+        rrd_set_error("Can't create '%s': %s", outfilename,
+                      rrd_strerror(errno));
+        rrd_free(&rrdnew);
+        rrd_free(&rrdold);
+        rrd_close(rrd_file);
+        rrd_close(rrd_out_file);
+        return (-1);
+    }
+    if (rrd_lock(rrd_out_file) != 0) {
+        rrd_set_error("could not lock new RRD");
+        rrd_free(&rrdnew);
+        rrd_free(&rrdold);
+        rrd_close(rrd_file);
+        rrd_close(rrd_out_file);
+        return (-1);
+    }
+/*XXX: do one write for those parts of header that are unchanged */
+    if ((rrdnew.rra_ptr = (rra_ptr_t *)malloc(sizeof(rra_ptr_t) * rrdold.stat_head->rra_cnt)) == NULL) {
+        rrd_set_error("allocating rra_ptr for new RRD");
+        rrd_free(&rrdnew);
+        rrd_free(&rrdold);
+        rrd_close(rrd_file);
+        rrd_close(rrd_out_file);
+        return (-1);
+    }
+
+    /* Put this back the way it was so that the rest of the algorithm
+       below remains unchanged, it will be corrected later */
+    rrdnew.rra_def[target_rra].row_cnt -= modify;
+
+    rrdnew.ds_def = rrdold.ds_def;
+    rrdnew.live_head = rrdold.live_head;
+    rrdnew.pdp_prep = rrdold.pdp_prep;
+    rrdnew.cdp_prep = rrdold.cdp_prep;
+    memcpy(rrdnew.rra_ptr,rrdold.rra_ptr,sizeof(rra_ptr_t) * rrdold.stat_head->rra_cnt);
+
+
+    version = atoi(rrdold.stat_head->version);
+    switch (version) {
+    case 4:
+        break;        
+    case 3:
+        break;
+    case 1:
+        rrdnew.stat_head->version[3] = '3';
+        break;
+    default:
+        rrd_set_error("Do not know how to handle RRD version %s",
+                      rrdold.stat_head->version);
+        rrd_free(&rrdnew);
+        rrd_free(&rrdold);
+        rrd_close(rrd_file);
+        rrd_close(rrd_out_file);
+        return (-1);
+        break;
+    }
+
+/* XXX: Error checking? */
+    rrd_write(rrd_out_file, rrdnew.stat_head, sizeof(stat_head_t) * 1);
+    rrd_write(rrd_out_file, rrdnew.ds_def,
+              sizeof(ds_def_t) * rrdnew.stat_head->ds_cnt);
+    rrd_write(rrd_out_file, rrdnew.rra_def,
+              sizeof(rra_def_t) * rrdnew.stat_head->rra_cnt);
+    rrd_write(rrd_out_file, rrdnew.live_head, sizeof(live_head_t) * 1);
+    rrd_write(rrd_out_file, rrdnew.pdp_prep,
+              sizeof(pdp_prep_t) * rrdnew.stat_head->ds_cnt);
+    rrd_write(rrd_out_file, rrdnew.cdp_prep,
+              sizeof(cdp_prep_t) * rrdnew.stat_head->ds_cnt *
+              rrdnew.stat_head->rra_cnt);
+    rrd_write(rrd_out_file, rrdnew.rra_ptr,
+              sizeof(rra_ptr_t) * rrdnew.stat_head->rra_cnt);
+
+    /* Move the CDPs from the old to the new database.
+     ** This can be made (much) faster but isn't worth the effort. Clarity
+     ** is much more important.
+     */
+
+    /* Move data in unmodified RRAs
+     */
+    l = 0;
+    for (rra = 0; rra < target_rra; rra++) {
+        l += rrdnew.stat_head->ds_cnt * rrdnew.rra_def[rra].row_cnt;
+    }
+    while (l > 0) {
+        rrd_read(rrd_file, &buffer, sizeof(rrd_value_t) * 1);
+        rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1);
+        l--;
+    }
+    /* Move data in this RRA, either removing or adding some rows
+     */
+    if (modify > 0) {
+        /* Adding extra rows; insert unknown values just after the
+         ** current row number.
+         */
+        l = rrdnew.stat_head->ds_cnt *
+            (rrdnew.rra_ptr[target_rra].cur_row + 1);
+        while (l > 0) {
+            rrd_read(rrd_file, &buffer, sizeof(rrd_value_t) * 1);
+            rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1);
+            l--;
+        }
+        buffer = DNAN;
+        l = rrdnew.stat_head->ds_cnt * modify;
+        while (l > 0) {
+            rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1);
+            l--;
+        }
+    } else {
+        /* Removing rows. Normally this would be just after the cursor
+         ** however this may also mean that we wrap to the beginning of
+         ** the array.
+         */
+        signed long int remove_end = 0;
+
+        remove_end =
+            (rrdnew.rra_ptr[target_rra].cur_row -
+             modify) % rrdnew.rra_def[target_rra].row_cnt;
+        if (remove_end <=
+            (signed long int) rrdnew.rra_ptr[target_rra].cur_row) {
+            while (remove_end >= 0) {
+                rrd_seek(rrd_file,
+                         sizeof(rrd_value_t) * rrdnew.stat_head->ds_cnt,
+                         SEEK_CUR);
+                rrdnew.rra_ptr[target_rra].cur_row--;
+                rrdnew.rra_def[target_rra].row_cnt--;
+                remove_end--;
+                modify++;
+            }
+            remove_end = rrdnew.rra_def[target_rra].row_cnt - 1;
+        }
+        for (l = 0; l <= rrdnew.rra_ptr[target_rra].cur_row; l++) {
+            unsigned int tmp;
+
+            for (tmp = 0; tmp < rrdnew.stat_head->ds_cnt; tmp++) {
+                rrd_read(rrd_file, &buffer, sizeof(rrd_value_t) * 1);
+                rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1);
+            }
+        }
+        while (modify < 0) {
+            rrd_seek(rrd_file,
+                     sizeof(rrd_value_t) * rrdnew.stat_head->ds_cnt,
+                     SEEK_CUR);
+            rrdnew.rra_def[target_rra].row_cnt--;
+            modify++;
+        }
+    }
+    /* Move the rest of the CDPs
+     */
+    while (1) {
+        if (rrd_read(rrd_file, &buffer, sizeof(rrd_value_t) * 1) <= 0)
+            break;
+        rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1);
+    }
+    rrdnew.rra_def[target_rra].row_cnt += modify;
+    rrd_seek(rrd_out_file,
+             sizeof(stat_head_t) +
+             sizeof(ds_def_t) * rrdnew.stat_head->ds_cnt, SEEK_SET);
+    rrd_write(rrd_out_file, rrdnew.rra_def,
+              sizeof(rra_def_t) * rrdnew.stat_head->rra_cnt);
+    rrd_seek(rrd_out_file, sizeof(live_head_t), SEEK_CUR);
+    rrd_seek(rrd_out_file, sizeof(pdp_prep_t) * rrdnew.stat_head->ds_cnt,
+             SEEK_CUR);
+    rrd_seek(rrd_out_file,
+             sizeof(cdp_prep_t) * rrdnew.stat_head->ds_cnt *
+             rrdnew.stat_head->rra_cnt, SEEK_CUR);
+    rrd_write(rrd_out_file, rrdnew.rra_ptr,
+              sizeof(rra_ptr_t) * rrdnew.stat_head->rra_cnt);
+    rrd_close(rrd_file);    
+    rrd_close(rrd_out_file);    
+    rrd_free(&rrdold);
+    rrd_free(&rrdnew);
+    return (0);
+}
diff --git a/program/src/rrd_restore.c b/program/src/rrd_restore.c
new file mode 100644 (file)
index 0000000..fde83ae
--- /dev/null
@@ -0,0 +1,1271 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010                    
+ *****************************************************************************
+ * rrd_restore.c  Contains logic to parse XML input and create an RRD file
+ * This file:
+ * Copyright (C) 2008  Florian octo Forster  (original libxml2 code)
+ * Copyright (C) 2008,2009 Tobias Oetiker
+ *****************************************************************************
+ * $Id$
+ *************************************************************************** */
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libxml/parser.h>
+#include <libxml/xmlreader.h>
+#include <locale.h>
+
+#ifndef WIN32
+#      include <unistd.h>     /* for off_t */
+#else
+       typedef size_t ssize_t;
+       typedef long off_t;
+#endif 
+
+#include <fcntl.h>
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+# include <io.h>
+# define open _open
+# define close _close
+#endif
+
+
+#define ARRAY_LENGTH(a) (sizeof (a) / sizeof ((a)[0]))
+
+static int opt_range_check = 0;
+static int opt_force_overwrite = 0;
+
+/*
+ * Helpers
+ */
+
+/* skip all but tags. complain if we do not get the right tag */
+/* dept -1 causes depth to be ignored */
+static xmlChar* get_xml_element (
+    xmlTextReaderPtr reader
+    )
+{
+    while(xmlTextReaderRead(reader)){
+        int type;
+        xmlChar *name;
+        type = xmlTextReaderNodeType(reader);
+        if (type == XML_READER_TYPE_TEXT){
+            xmlChar *value;
+            value = xmlTextReaderValue(reader);
+            rrd_set_error("line %d: expected element but found text '%s'",
+                          xmlTextReaderGetParserLineNumber(reader),value);
+            xmlFree(value);
+            return NULL;
+        }
+        /* skip all other non-elements */
+        if (type != XML_READER_TYPE_ELEMENT && type != XML_READER_TYPE_END_ELEMENT)
+            continue;
+
+        name = xmlTextReaderName(reader);
+        if (type == XML_READER_TYPE_END_ELEMENT){
+            xmlChar *temp;
+            xmlChar *temp2;            
+            temp = (xmlChar*)sprintf_alloc("/%s",name);
+            temp2 = xmlStrdup(temp);
+            free(temp);
+            xmlFree(name);            
+            return temp2;            
+        }
+        /* all seems well, return the happy news */
+        return name;
+    }
+    rrd_set_error("the xml ended while we were looking for an element");
+    return NULL;
+} /* get_xml_element */
+
+static void local_rrd_free (rrd_t *rrd)
+{    
+    free(rrd->live_head);
+    free(rrd->stat_head);
+    free(rrd->ds_def);
+    free(rrd->rra_def); 
+    free(rrd->rra_ptr);
+    free(rrd->pdp_prep);
+    free(rrd->cdp_prep);
+    free(rrd->rrd_value);
+    free(rrd);
+}
+
+
+static int expect_element (
+    xmlTextReaderPtr reader,
+    char *exp_name)
+{
+    xmlChar *name;
+    name = get_xml_element(reader);
+    if (!name)
+        return -1;    
+    if (xmlStrcasecmp(name,(xmlChar *)exp_name) != 0){
+        rrd_set_error("line %d: expected <%s> element but found <%s>",
+                      xmlTextReaderGetParserLineNumber(reader),name,exp_name);
+        xmlFree(name);            
+        return -1;            
+    }
+    xmlFree(name);    
+    return 0;    
+} /* expect_element */
+
+static int expect_element_end (
+    xmlTextReaderPtr reader,
+    char *exp_name)
+{
+    xmlChar *name;
+    /* maybe we are already on the end element ... lets see */
+    if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT){
+         xmlChar *temp;
+         xmlChar *temp2;            
+         temp = xmlTextReaderName(reader);
+         temp2 = (xmlChar*)sprintf_alloc("/%s", temp);
+         name = xmlStrdup(temp2);
+         xmlFree(temp);
+         free(temp2);            
+    } else {     
+         name = get_xml_element(reader);
+    }
+
+    if (name == NULL)
+        return -1;    
+    if (xmlStrcasecmp(name+1,(xmlChar *)exp_name) != 0 || name[0] != '/'){
+        rrd_set_error("line %d: expected </%s> end element but found <%s>",
+                      xmlTextReaderGetParserLineNumber(reader),exp_name,name);
+        xmlFree(name);            
+        return -1;            
+    }
+    xmlFree(name);    
+    return 0;    
+} /* expect_element_end */
+
+
+static xmlChar* get_xml_text (
+    xmlTextReaderPtr reader
+    )
+{
+    while(xmlTextReaderRead(reader)){
+        xmlChar  *ret;    
+        xmlChar  *text;
+        xmlChar  *begin_ptr;
+        xmlChar  *end_ptr;
+        int type;        
+        type = xmlTextReaderNodeType(reader);
+        if (type == XML_READER_TYPE_ELEMENT){
+            xmlChar *name;
+            name = xmlTextReaderName(reader);
+            rrd_set_error("line %d: expected a value but found a <%s> element",
+                          xmlTextReaderGetParserLineNumber(reader),
+                          name);
+            xmlFree(name);            
+            return NULL;            
+        }
+
+        /* trying to read text from <a></a> we end up here
+           lets return an empty string insead. This is a tad optimistic
+           since we do not check if it is actually </a> and not </b>
+           we got, but first we do not know if we expect </a> and second
+           we the whole implementation is on the optimistic side. */
+        if (type == XML_READER_TYPE_END_ELEMENT){
+            return  xmlStrdup(BAD_CAST "");
+        }        
+
+        /* skip all other non-text */
+        if (type != XML_READER_TYPE_TEXT)
+            continue;
+        
+        text = xmlTextReaderValue(reader);
+
+        begin_ptr = text;
+        while ((begin_ptr[0] != 0) && (isspace(begin_ptr[0])))
+            begin_ptr++;
+        if (begin_ptr[0] == 0) {
+            xmlFree(text);
+            return xmlStrdup(BAD_CAST "");
+        }        
+        end_ptr = begin_ptr;
+        while ((end_ptr[0] != 0) && (!isspace(end_ptr[0])))
+            end_ptr++;
+        end_ptr[0] = 0;
+        
+        ret = xmlStrdup(begin_ptr);
+        xmlFree(text);
+        return ret;
+    }
+    rrd_set_error("file ended while looking for text");
+    return NULL;
+}  /* get_xml_text */ 
+
+
+static int get_xml_string(
+    xmlTextReaderPtr reader,
+    char *value,
+    int max_len)
+{
+    xmlChar *str;
+    str = get_xml_text(reader);
+    if (str != NULL){
+        strncpy(value,(char *)str,max_len);
+        xmlFree(str);
+        return 0;        
+    }
+    else
+        return -1;    
+}
+
+static int get_xml_time_t(
+    xmlTextReaderPtr reader,
+    time_t *value)
+{    
+    xmlChar *text;
+    time_t temp;    
+    if ((text = get_xml_text(reader)) != NULL){
+        errno = 0;        
+#ifdef TIME_T_IS_32BIT
+        temp = strtol((char *)text,NULL, 0);
+#else
+#ifdef TIME_T_IS_64BIT
+        temp = strtoll((char *)text,NULL, 0);        
+#else
+        if (sizeof(time_t) == 4){
+            temp = strtol((char *)text,NULL, 0);
+        } else {
+            temp = strtoll((char *)text,NULL, 0);
+        }
+#endif
+#endif    
+        if (errno>0){
+            rrd_set_error("ling %d: get_xml_time_t from '%s' %s",
+                          xmlTextReaderGetParserLineNumber(reader),
+                          text,rrd_strerror(errno));
+            xmlFree(text);            
+            return -1;
+        }
+        xmlFree(text);            
+        *value = temp;
+        return 0;
+    }
+    return -1;
+} /* get_xml_time_t */
+
+static int get_xml_ulong(
+    xmlTextReaderPtr reader,
+    unsigned long *value)
+{
+    
+    xmlChar *text;
+    unsigned long temp;    
+    if ((text = get_xml_text(reader)) != NULL){
+        errno = 0;        
+        temp = strtoul((char *)text,NULL, 0);        
+        if (errno>0){
+            rrd_set_error("ling %d: get_xml_ulong from '%s' %s",
+                          xmlTextReaderGetParserLineNumber(reader),
+                          text,rrd_strerror(errno));
+            xmlFree(text);            
+            return -1;
+        }
+        xmlFree(text);
+        *value = temp;        
+        return 0;
+    }
+    return -1;
+} /* get_xml_ulong */
+
+static int get_xml_double(
+    xmlTextReaderPtr reader,
+    double *value)
+{
+    
+    xmlChar *text;
+    double temp;    
+    if ((text = get_xml_text(reader))!= NULL){
+        if (xmlStrcasestr(text,(xmlChar *)"nan")){
+            *value = DNAN;
+            xmlFree(text);
+            return 0;            
+        }
+        else if (xmlStrcasestr(text,(xmlChar *)"-inf")){
+            *value = -DINF;
+            xmlFree(text);
+            return 0;            
+        }
+        else if (xmlStrcasestr(text,(xmlChar *)"+inf")
+                 || xmlStrcasestr(text,(xmlChar *)"inf")){
+            *value = DINF;
+            xmlFree(text);
+            return 0;            
+        }        
+        errno = 0;
+        temp = strtod((char *)text,NULL);
+        if (errno>0){
+            rrd_set_error("ling %d: get_xml_double from '%s' %s",
+                          xmlTextReaderGetParserLineNumber(reader),
+                          text,rrd_strerror(errno));
+            xmlFree(text);        
+            return -1;
+        }
+        xmlFree(text);        
+        *value = temp;
+        return 0;
+    }
+    return -1;
+} /* get_xml_double */
+
+
+static int value_check_range(
+    rrd_value_t *rrd_value,
+    const ds_def_t *ds_def)
+{
+    double    min;
+    double    max;
+
+    if (opt_range_check == 0)
+        return (0);
+
+    min = ds_def->par[DS_min_val].u_val;
+    max = ds_def->par[DS_max_val].u_val;
+
+    if (((!isnan(min)) && (*rrd_value < min))
+        || ((!isnan(max)) && (*rrd_value > max)))
+        *rrd_value = DNAN;
+
+    return (0);
+} /* int value_check_range */
+
+/*
+ * Parse the <database> block within an RRA definition
+ */
+
+static int parse_tag_rra_database_row(
+    xmlTextReaderPtr reader,
+    rrd_t *rrd,
+    rrd_value_t *rrd_value)
+{
+    unsigned int values_count = 0;
+    int       status;
+    
+    status = 0;
+    for (values_count = 0;values_count <  rrd->stat_head->ds_cnt;values_count++){
+        if (expect_element(reader,"v") == 0){
+            status = get_xml_double(reader,rrd_value + values_count);
+            if (status == 0)
+                value_check_range(rrd_value + values_count,
+                                  rrd->ds_def + values_count);
+            else
+                break;            
+        } else
+            return -1;
+        if (expect_element(reader,"/v") == -1){
+            return -1;
+        }
+    }
+    return status;
+}                       /* int parse_tag_rra_database_row */
+
+static int parse_tag_rra_database(
+    xmlTextReaderPtr reader,
+    rrd_t *rrd )
+{
+    rra_def_t *cur_rra_def;
+    unsigned int total_row_cnt;
+    int       status;
+    int       i;
+    xmlChar *element;
+
+    total_row_cnt = 0;
+    for (i = 0; i < (((int) rrd->stat_head->rra_cnt) - 1); i++)
+        total_row_cnt += rrd->rra_def[i].row_cnt;
+
+    cur_rra_def = rrd->rra_def + i;
+
+    status = 0;
+    while ((element = get_xml_element(reader)) != NULL){        
+        if (xmlStrcasecmp(element,(const xmlChar *)"row") == 0){
+           rrd_value_t *temp;
+           rrd_value_t *cur_rrd_value;
+           unsigned int total_values_count = rrd->stat_head->ds_cnt
+               * (total_row_cnt + 1);
+
+            /* Allocate space for the new values.. */
+            temp = (rrd_value_t *) realloc(rrd->rrd_value,
+                                           sizeof(rrd_value_t) *
+                                           total_values_count);
+            if (temp == NULL) {
+                rrd_set_error("parse_tag_rra_database: realloc failed.");
+                status = -1;
+               break;
+            }
+            rrd->rrd_value = temp;
+            cur_rrd_value = rrd->rrd_value
+                + (rrd->stat_head->ds_cnt * total_row_cnt);
+            memset(cur_rrd_value, '\0',
+                   sizeof(rrd_value_t) * rrd->stat_head->ds_cnt);
+            total_row_cnt++;
+            cur_rra_def->row_cnt++;
+
+            status =
+                parse_tag_rra_database_row(reader, rrd, cur_rrd_value);
+            if (status == 0)
+                status =  expect_element(reader,"/row");
+        } /* if (xmlStrcasecmp(element,"row")) */
+        else {
+            if ( xmlStrcasecmp(element,(const xmlChar *)"/database") == 0){
+                xmlFree(element);                
+                break;
+            }
+            else {
+                rrd_set_error("line %d: found unexpected tag: %s",
+                              xmlTextReaderGetParserLineNumber(reader),element);
+                status = -1;
+            }
+        }
+        xmlFree(element);        
+        if (status != 0)
+            break;        
+    }
+    return (status);
+}                       /* int parse_tag_rra_database */
+
+/*
+ * Parse the <cdp_prep> block within an RRA definition
+ */
+static int parse_tag_rra_cdp_prep_ds_history(
+    xmlTextReaderPtr reader,
+    cdp_prep_t *cdp_prep)
+{
+    /* Make `history_buffer' the same size as the scratch area, plus the
+     * terminating NULL byte. */
+    xmlChar  *history;    
+    char     *history_ptr;
+    int       i;
+    if ((history = get_xml_text(reader)) != NULL){
+        history_ptr = (char *) (&cdp_prep->scratch[0]);
+        for (i = 0; history[i] != '\0'; i++)
+            history_ptr[i] = (history[i] == '1') ? 1 : 0;
+        xmlFree(history);        
+        return 0;        
+    }    
+    return -1;    
+}  /* int parse_tag_rra_cdp_prep_ds_history */
+
+static int parse_tag_rra_cdp_prep_ds(
+    xmlTextReaderPtr reader,
+    rrd_t *rrd,
+    cdp_prep_t *cdp_prep)
+{
+    int       status;
+    xmlChar *element;
+    memset(cdp_prep, '\0', sizeof(cdp_prep_t));
+
+    status = -1;
+    
+    if (atoi(rrd->stat_head->version) == 1) {
+        cdp_prep->scratch[CDP_primary_val].u_val = 0.0;
+        cdp_prep->scratch[CDP_secondary_val].u_val = 0.0;
+    }
+
+    while ((element = get_xml_element(reader)) != NULL){
+        if (xmlStrcasecmp(element, (const xmlChar *) "primary_value") == 0)
+            status =
+                get_xml_double(reader,&cdp_prep->scratch[CDP_primary_val].u_val);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "secondary_value") == 0)
+            status =
+                get_xml_double(reader,&cdp_prep->scratch[CDP_secondary_val].u_val);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "intercept") == 0)
+            status = get_xml_double(reader,
+                                          &cdp_prep->
+                                          scratch[CDP_hw_intercept].u_val);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "last_intercept") ==
+                 0)
+            status =
+                get_xml_double(reader,
+                                     &cdp_prep->
+                                     scratch[CDP_hw_last_intercept].u_val);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "slope") == 0)
+            status = get_xml_double(reader,
+                                    &cdp_prep->scratch[CDP_hw_slope].
+                                    u_val);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "last_slope") == 0)
+            status = get_xml_double(reader,
+                                    &cdp_prep->
+                                    scratch[CDP_hw_last_slope].u_val);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "nan_count") == 0)
+            status = get_xml_ulong(reader,
+                                   &cdp_prep->
+                                   scratch[CDP_null_count].u_cnt);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "last_nan_count") ==
+                 0)
+            status =
+                get_xml_ulong(reader,
+                              &cdp_prep->
+                              scratch[CDP_last_null_count].u_cnt);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "seasonal") == 0)
+            status = get_xml_double(reader,
+                                    &cdp_prep->scratch[CDP_hw_seasonal].
+                                    u_val);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "last_seasonal") ==
+                 0)
+            status =
+                get_xml_double(reader,
+                                     &cdp_prep->scratch[CDP_hw_last_seasonal].
+                                     u_val);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "init_flag") == 0)
+            status = get_xml_ulong(reader,
+                                        &cdp_prep->
+                                       scratch[CDP_init_seasonal].u_cnt);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "history") == 0)
+            status = parse_tag_rra_cdp_prep_ds_history(reader, cdp_prep);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "value") == 0)
+            status = get_xml_double(reader,
+                                    &cdp_prep->scratch[CDP_val].u_val);
+        else if (xmlStrcasecmp(element,
+                           (const xmlChar *) "unknown_datapoints") == 0)
+            status = get_xml_ulong(reader,
+                                        &cdp_prep->
+                                       scratch[CDP_unkn_pdp_cnt].u_cnt);
+        else if (xmlStrcasecmp(element,
+                               (const xmlChar *) "/ds") == 0){
+            xmlFree(element);            
+            break;
+        }        
+        else {
+            rrd_set_error("parse_tag_rra_cdp_prep: Unknown tag: %s",
+                          element);
+            status = -1;
+            xmlFree(element);            
+            break;            
+        }
+        if (status != 0){
+            xmlFree(element);
+            break;
+        }
+        status = expect_element_end(reader,(char *)element);
+        xmlFree(element);        
+        if (status != 0)
+            break;
+    }    
+    return (status);
+}                       /* int parse_tag_rra_cdp_prep_ds */
+
+static int parse_tag_rra_cdp_prep(
+    xmlTextReaderPtr reader,
+    rrd_t *rrd,
+    cdp_prep_t *cdp_prep)
+{
+    int       status;
+
+    unsigned int ds_count;
+
+    status = 0;
+    for ( ds_count = 0; ds_count < rrd->stat_head->ds_cnt;ds_count++){
+        if (expect_element(reader,"ds") == 0) {
+            status = parse_tag_rra_cdp_prep_ds(reader, rrd,
+                                               cdp_prep + ds_count);
+            if (status != 0)
+                break;
+        } else {
+            status = -1;            
+            break;
+        }        
+    }
+    if (status == 0)
+        status =  expect_element(reader,"/cdp_prep");
+    return (status);
+}                       /* int parse_tag_rra_cdp_prep */
+
+/*
+ * Parse the <params> block within an RRA definition
+ */
+static int parse_tag_rra_params(
+    xmlTextReaderPtr reader,
+    rra_def_t *rra_def)
+{
+    xmlChar *element;
+    int       status;
+
+    status = -1;
+    while ((element = get_xml_element(reader)) != NULL){
+        /*
+         * Parameters for CF_HWPREDICT
+         */
+        if (xmlStrcasecmp(element, (const xmlChar *) "hw_alpha") == 0)
+            status = get_xml_double(reader,
+                                          &rra_def->par[RRA_hw_alpha].u_val);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "hw_beta") == 0)
+            status = get_xml_double(reader,
+                                          &rra_def->par[RRA_hw_beta].u_val);
+        else if (xmlStrcasecmp(element,
+                           (const xmlChar *) "dependent_rra_idx") == 0)
+            status = get_xml_ulong(reader,
+                                        &rra_def->
+                                       par[RRA_dependent_rra_idx].u_cnt);
+        /*
+         * Parameters for CF_SEASONAL and CF_DEVSEASONAL
+         */
+        else if (xmlStrcasecmp(element, (const xmlChar *) "seasonal_gamma") ==
+                 0)
+            status =
+                get_xml_double(reader,
+                                     &rra_def->par[RRA_seasonal_gamma].u_val);
+        else if (xmlStrcasecmp
+                 (element, (const xmlChar *) "seasonal_smooth_idx") == 0)
+            status =
+                get_xml_ulong(reader,
+                                   &rra_def->
+                                  par[RRA_seasonal_smooth_idx].u_cnt);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "smoothing_window")
+                 == 0)
+            status =
+                get_xml_double(reader,
+                                     &rra_def->
+                                     par[RRA_seasonal_smoothing_window].
+                                     u_val);
+        /* else if (dependent_rra_idx) ...; */
+        /*
+         * Parameters for CF_FAILURES
+         */
+        else if (xmlStrcasecmp(element, (const xmlChar *) "delta_pos") == 0)
+            status = get_xml_double(reader,
+                                          &rra_def->par[RRA_delta_pos].u_val);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "delta_neg") == 0)
+            status = get_xml_double(reader,
+                                          &rra_def->par[RRA_delta_neg].u_val);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "window_len") == 0)
+            status = get_xml_ulong(reader,
+                                        &rra_def->par[RRA_window_len].
+                                       u_cnt);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "failure_threshold")
+                 == 0)
+            status =
+                get_xml_ulong(reader,
+                                   &rra_def->
+                                  par[RRA_failure_threshold].u_cnt);
+        /*
+         * Parameters for CF_AVERAGE, CF_MAXIMUM, CF_MINIMUM, and CF_LAST
+         */
+        else if (xmlStrcasecmp(element, (const xmlChar *) "xff") == 0)
+            status = get_xml_double(reader,
+                                          &rra_def->par[RRA_cdp_xff_val].
+                                          u_val);
+        /*
+         * Compatibility code for 1.0.49
+         */
+        else if (xmlStrcasecmp(element, (const xmlChar *) "value") == 0) {  /* {{{ */
+            unsigned int i = 0;
+
+            for (i=0;i<ARRAY_LENGTH(rra_def->par);i++){
+                if ((i == RRA_dependent_rra_idx)
+                    || (i == RRA_seasonal_smooth_idx)
+                    || (i == RRA_failure_threshold))
+                    status = get_xml_ulong(reader,
+                                                &rra_def->par[i].
+                                               u_cnt);
+                else
+                    status = get_xml_double(reader,
+                                                  &rra_def->par[i].u_val);
+
+                if (status != 0)
+                    break;
+                if ( i-1 < ARRAY_LENGTH(rra_def->par)){
+                    status = expect_element(reader,"/value");
+                    if (status == 0){
+                        status  = expect_element(reader,"value");
+                    }
+                }
+                if (status != 0){
+                    break;                    
+                }
+            }
+        }  /* }}} */        
+        else if (xmlStrcasecmp(element,(const xmlChar *) "/params") == 0){
+            xmlFree(element);            
+            return status;
+        }  /* }}} */        
+        else {
+            rrd_set_error("line %d: parse_tag_rra_params: Unknown tag: %s",
+                          xmlTextReaderGetParserLineNumber(reader),element);
+            status = -1;
+        }
+        status = expect_element_end(reader,(char *)element);
+        xmlFree(element);        
+        if (status != 0)
+            break;
+    }
+    return (status);
+}                       /* int parse_tag_rra_params */
+
+/*
+ * Parse an RRA definition
+ */
+static int parse_tag_rra_cf(
+    xmlTextReaderPtr reader,
+    rra_def_t *rra_def)
+{
+    int       status;
+
+    status = get_xml_string(reader,
+                                  rra_def->cf_nam, sizeof(rra_def->cf_nam));
+    if (status != 0)
+        return status;
+
+    status = cf_conv(rra_def->cf_nam);
+    if (status == -1) {
+        rrd_set_error("parse_tag_rra_cf: Unknown consolidation function: %s",
+                      rra_def->cf_nam);
+        return -1;
+    }
+
+    return 0;
+}                       /* int parse_tag_rra_cf */
+
+static int parse_tag_rra(
+    xmlTextReaderPtr reader,
+    rrd_t *rrd)
+{
+    int       status;
+    xmlChar *element;
+    
+    rra_def_t *cur_rra_def;
+    cdp_prep_t *cur_cdp_prep;
+    rra_ptr_t *cur_rra_ptr;
+
+    /* Allocate more rra_def space for this RRA */
+    {                   /* {{{ */
+        rra_def_t *temp;
+
+        temp = (rra_def_t *) realloc(rrd->rra_def,
+                                     sizeof(rra_def_t) *
+                                     (rrd->stat_head->rra_cnt + 1));
+        if (temp == NULL) {
+            rrd_set_error("parse_tag_rra: realloc failed.");
+            return (-1);
+        }
+        rrd->rra_def = temp;
+        cur_rra_def = rrd->rra_def + rrd->stat_head->rra_cnt;
+        memset(cur_rra_def, '\0', sizeof(rra_def_t));
+    }                   /* }}} */
+
+    /* allocate cdp_prep_t */
+    {                   /* {{{ */
+        cdp_prep_t *temp;
+
+        temp = (cdp_prep_t *) realloc(rrd->cdp_prep, sizeof(cdp_prep_t)
+                                      * rrd->stat_head->ds_cnt
+                                      * (rrd->stat_head->rra_cnt + 1));
+        if (temp == NULL) {
+            rrd_set_error("parse_tag_rra: realloc failed.");
+            return (-1);
+        }
+        rrd->cdp_prep = temp;
+        cur_cdp_prep = rrd->cdp_prep
+            + (rrd->stat_head->ds_cnt * rrd->stat_head->rra_cnt);
+        memset(cur_cdp_prep, '\0',
+               sizeof(cdp_prep_t) * rrd->stat_head->ds_cnt);
+    }                   /* }}} */
+
+    /* allocate rra_ptr_t */
+    {                   /* {{{ */
+        rra_ptr_t *temp;
+
+        temp = (rra_ptr_t *) realloc(rrd->rra_ptr,
+                                     sizeof(rra_ptr_t) *
+                                     (rrd->stat_head->rra_cnt + 1));
+        if (temp == NULL) {
+            rrd_set_error("parse_tag_rra: realloc failed.");
+            return (-1);
+        }
+        rrd->rra_ptr = temp;
+        cur_rra_ptr = rrd->rra_ptr + rrd->stat_head->rra_cnt;
+        memset(cur_rra_ptr, '\0', sizeof(rra_ptr_t));
+    }                   /* }}} */
+
+    /* All space successfully allocated, increment number of RRAs. */
+    rrd->stat_head->rra_cnt++;
+
+    status = 0;
+    while ((element = get_xml_element(reader)) != NULL){
+        if (xmlStrcasecmp(element, (const xmlChar *) "cf") == 0)
+            status = parse_tag_rra_cf(reader, cur_rra_def);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "pdp_per_row") == 0)
+            status = get_xml_ulong(reader,
+                                        &cur_rra_def->pdp_cnt);
+        else if (atoi(rrd->stat_head->version) == 1
+                 && xmlStrcasecmp(element, (const xmlChar *) "xff") == 0)
+            status = get_xml_double(reader,
+                                          (double *) &cur_rra_def->
+                                          par[RRA_cdp_xff_val].u_val);
+        else if (atoi(rrd->stat_head->version) >= 2
+                 && xmlStrcasecmp(element, (const xmlChar *) "params") == 0){            
+            xmlFree(element);
+            status = parse_tag_rra_params(reader, cur_rra_def);
+            if (status == 0)
+                continue;
+            else
+                return status;
+        }
+        else if (xmlStrcasecmp(element, (const xmlChar *) "cdp_prep") == 0){
+            xmlFree(element);
+            status = parse_tag_rra_cdp_prep(reader, rrd, cur_cdp_prep);
+            if (status == 0)
+                continue;
+            else
+                return status;
+        }        
+        else if (xmlStrcasecmp(element, (const xmlChar *) "database") == 0){            
+            xmlFree(element);
+            status = parse_tag_rra_database(reader, rrd);
+            if (status == 0)
+                continue;
+            else
+                return status;
+        }
+        else if (xmlStrcasecmp(element,(const xmlChar *) "/rra") == 0){
+            xmlFree(element);            
+            return status;
+        }  /* }}} */        
+       else {
+            rrd_set_error("line %d: parse_tag_rra: Unknown tag: %s",
+                          xmlTextReaderGetParserLineNumber(reader), element);
+            status = -1;            
+        }
+        if (status != 0) {
+            xmlFree(element);
+            return status;
+        }        
+        status = expect_element_end(reader,(char *)element);
+        xmlFree(element);
+        if (status != 0) {
+            return status;
+        }        
+    }    
+    /* Set the RRA pointer to a random location */
+    cur_rra_ptr->cur_row = rrd_random() % cur_rra_def->row_cnt;
+
+    return (status);
+}                       /* int parse_tag_rra */
+
+/*
+ * Parse a DS definition
+ */
+static int parse_tag_ds_cdef(
+    xmlTextReaderPtr reader,
+    rrd_t *rrd)
+{
+    xmlChar *cdef;
+
+    cdef = get_xml_text(reader);
+    if (cdef != NULL){
+        /* We're always working on the last DS that has been added to the structure
+         * when we get here */
+        parseCDEF_DS((char *)cdef, rrd, rrd->stat_head->ds_cnt - 1);
+        xmlFree(cdef);
+        if (rrd_test_error())
+            return -1;
+        else            
+            return 0;        
+    }
+    return -1;
+}                       /* int parse_tag_ds_cdef */
+
+static int parse_tag_ds_type(
+    xmlTextReaderPtr reader,
+    ds_def_t *ds_def)
+{
+    char *dst;
+    dst = (char *)get_xml_text(reader);
+    if (dst != NULL){
+        int status;
+        status = dst_conv(dst);
+        if (status == -1) {
+            rrd_set_error("parse_tag_ds_type: Unknown data source type: %s",
+                          dst);
+            return -1;
+        }
+        strncpy(ds_def->dst,dst,sizeof(ds_def->dst)-1);
+        ds_def->dst[sizeof(ds_def->dst)-1] = '\0';
+        xmlFree(dst);
+        return 0;        
+    }
+    return -1;
+}                       /* int parse_tag_ds_type */
+
+static int parse_tag_ds(
+    xmlTextReaderPtr reader,
+    rrd_t *rrd)
+{
+    int       status;
+    xmlChar  *element;
+    
+    ds_def_t *cur_ds_def;
+    pdp_prep_t *cur_pdp_prep;
+
+    /*
+     * If there are DS definitions after RRA definitions the number of values,
+     * cdp_prep areas and so on will be calculated wrong. Thus, enforce a
+     * specific order in this case.
+     */
+    if (rrd->stat_head->rra_cnt > 0) {
+        rrd_set_error("parse_tag_ds: All data source definitions MUST "
+                      "precede the RRA definitions!");
+        return (-1);
+    }
+
+    /* Allocate space for the new DS definition */
+    {                   /* {{{ */
+        ds_def_t *temp;
+
+        temp = (ds_def_t *) realloc(rrd->ds_def,
+                                    sizeof(ds_def_t) *
+                                    (rrd->stat_head->ds_cnt + 1));
+        if (temp == NULL) {
+            rrd_set_error("parse_tag_ds: malloc failed.");
+            return (-1);
+        }
+        rrd->ds_def = temp;
+        cur_ds_def = rrd->ds_def + rrd->stat_head->ds_cnt;
+        memset(cur_ds_def, '\0', sizeof(ds_def_t));
+    }                   /* }}} */
+
+    /* Allocate pdp_prep space for the new DS definition */
+    {                   /* {{{ */
+        pdp_prep_t *temp;
+
+        temp = (pdp_prep_t *) realloc(rrd->pdp_prep,
+                                      sizeof(pdp_prep_t) *
+                                      (rrd->stat_head->ds_cnt + 1));
+        if (temp == NULL) {
+            rrd_set_error("parse_tag_ds: malloc failed.");
+            return (-1);
+        }
+        rrd->pdp_prep = temp;
+        cur_pdp_prep = rrd->pdp_prep + rrd->stat_head->ds_cnt;
+        memset(cur_pdp_prep, '\0', sizeof(pdp_prep_t));
+    }                   /* }}} */
+
+    /* All allocations successful, let's increment the number of DSes. */
+    rrd->stat_head->ds_cnt++;
+
+    status = 0;
+    while ((element = get_xml_element(reader)) != NULL){
+        if (xmlStrcasecmp(element, (const xmlChar *) "name") == 0){
+            status = get_xml_string(reader,cur_ds_def->ds_nam,sizeof(cur_ds_def->ds_nam));
+        }
+        else if (xmlStrcasecmp(element, (const xmlChar *) "type") == 0)
+            status = parse_tag_ds_type(reader, cur_ds_def);
+        else if (xmlStrcasecmp(element,
+                           (const xmlChar *) "minimal_heartbeat") == 0)
+            status = get_xml_ulong(reader,
+                                        &cur_ds_def->par[DS_mrhb_cnt].
+                                       u_cnt);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "min") == 0)
+            status = get_xml_double(reader,
+                                          &cur_ds_def->par[DS_min_val].u_val);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "max") == 0)
+            status = get_xml_double(reader,
+                                          &cur_ds_def->par[DS_max_val].u_val);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "cdef") == 0)
+            status = parse_tag_ds_cdef(reader, rrd);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "last_ds") == 0)
+            status = get_xml_string(reader,
+                                          cur_pdp_prep->last_ds,
+                                          sizeof(cur_pdp_prep->last_ds));
+        else if (xmlStrcasecmp(element, (const xmlChar *) "value") == 0)
+            status = get_xml_double(reader,
+                                          &cur_pdp_prep->scratch[PDP_val].
+                                          u_val);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "unknown_sec") == 0)
+            status = get_xml_ulong(reader,
+                                        &cur_pdp_prep->
+                                       scratch[PDP_unkn_sec_cnt].u_cnt);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "/ds") == 0) {
+            xmlFree(element);            
+            break;
+        }        
+        else {
+            rrd_set_error("parse_tag_ds: Unknown tag: %s", element);
+            status = -1;
+        }        
+        if (status != 0) {            
+            xmlFree(element);        
+            break;
+        }
+        status = expect_element_end(reader,(char *)element);
+        xmlFree(element);        
+        if (status != 0)
+            break;        
+    }
+    
+    return (status);
+}                       /* int parse_tag_ds */
+
+/*
+ * Parse root nodes
+ */
+static int parse_tag_rrd(
+    xmlTextReaderPtr reader,
+    rrd_t *rrd)
+{
+    int       status;
+    xmlChar *element;
+    
+    status = 0;
+    while ((element = get_xml_element(reader)) != NULL ){
+        if (xmlStrcasecmp(element, (const xmlChar *) "version") == 0)
+            status = get_xml_string(reader,
+                                          rrd->stat_head->version,
+                                          sizeof(rrd->stat_head->version));
+        else if (xmlStrcasecmp(element, (const xmlChar *) "step") == 0)
+            status = get_xml_ulong(reader,
+                                        &rrd->stat_head->pdp_step);
+        else if (xmlStrcasecmp(element, (const xmlChar *) "lastupdate") == 0) {
+                status = get_xml_time_t(reader, &rrd->live_head->last_up);
+        }
+        else if (xmlStrcasecmp(element, (const xmlChar *) "ds") == 0){            
+            xmlFree(element);
+            status = parse_tag_ds(reader, rrd);
+            /* as we come back the </ds> tag is already gone */
+            if (status == 0)
+                continue;
+            else
+                return status;
+        }        
+        else if (xmlStrcasecmp(element, (const xmlChar *) "rra") == 0){            
+            xmlFree(element);
+            status = parse_tag_rra(reader, rrd);
+            if (status == 0)
+                continue;
+            else
+                return status;
+        }
+        else if (xmlStrcasecmp(element, (const xmlChar *) "/rrd") == 0) {
+            xmlFree(element);
+            return status;
+        }
+        else {
+            rrd_set_error("parse_tag_rrd: Unknown tag: %s", element);
+            status = -1;
+        }
+
+        if (status != 0){
+            xmlFree(element);
+            break;
+        }        
+        status = expect_element_end(reader,(char *)element);
+        xmlFree(element);        
+        if (status != 0)
+            break;        
+    }
+    return (status);
+}                       /* int parse_tag_rrd */
+
+static rrd_t *parse_file(
+    const char *filename)
+{
+    xmlTextReaderPtr reader;
+    int       status;
+
+    rrd_t    *rrd;
+
+    reader = xmlNewTextReaderFilename(filename);
+    if (reader == NULL) {
+        rrd_set_error("Could not create xml reader for: %s",filename);
+        return (NULL);
+    }
+
+    if (expect_element(reader,"rrd") != 0) {
+        xmlFreeTextReader(reader);
+        return (NULL);
+    }
+
+    rrd = (rrd_t *) malloc(sizeof(rrd_t));
+    if (rrd == NULL) {
+        rrd_set_error("parse_file: malloc failed.");
+        xmlFreeTextReader(reader);
+        return (NULL);
+    }
+    memset(rrd, '\0', sizeof(rrd_t));
+
+    rrd->stat_head = (stat_head_t *) malloc(sizeof(stat_head_t));
+    if (rrd->stat_head == NULL) {
+        rrd_set_error("parse_tag_rrd: malloc failed.");
+        xmlFreeTextReader(reader);
+        free(rrd);
+        return (NULL);
+    }
+    memset(rrd->stat_head, '\0', sizeof(stat_head_t));
+
+    strncpy(rrd->stat_head->cookie, "RRD", sizeof(rrd->stat_head->cookie));
+    rrd->stat_head->float_cookie = FLOAT_COOKIE;
+
+    rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
+    if (rrd->live_head == NULL) {
+        rrd_set_error("parse_tag_rrd: malloc failed.");
+        xmlFreeTextReader(reader);
+        free(rrd->stat_head);
+        free(rrd);
+        return (NULL);
+    }
+    memset(rrd->live_head, '\0', sizeof(live_head_t));
+
+    status = parse_tag_rrd(reader, rrd);
+
+    xmlFreeTextReader(reader);
+
+    if (status != 0) {
+        local_rrd_free(rrd);
+        rrd = NULL;
+    }
+
+    return (rrd);
+}                       /* rrd_t *parse_file */
+
+static int write_file(
+    const char *file_name,
+    rrd_t *rrd)
+{
+    FILE     *fh;
+    unsigned int i;
+    unsigned int rra_offset;
+
+    if (strcmp("-", file_name) == 0)
+        fh = stdout;
+    else {
+        int       fd_flags = O_WRONLY | O_CREAT;
+        int       fd;
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+        fd_flags |= O_BINARY;
+#endif
+
+        if (opt_force_overwrite == 0)
+            fd_flags |= O_EXCL;
+
+        fd = open(file_name, fd_flags, 0666);
+        if (fd == -1) {
+            rrd_set_error("creating '%s': %s", file_name,
+                          rrd_strerror(errno));
+            return (-1);
+        }
+
+        fh = fdopen(fd, "wb");
+        if (fh == NULL) {
+            rrd_set_error("fdopen failed: %s", rrd_strerror(errno));
+            close(fd);
+            return (-1);
+        }
+    }
+    if (atoi(rrd->stat_head->version) < 3) {
+        /* we output 3 or higher */
+        strcpy(rrd->stat_head->version, "0003");
+    }
+    fwrite(rrd->stat_head, sizeof(stat_head_t), 1, fh);
+    fwrite(rrd->ds_def, sizeof(ds_def_t), rrd->stat_head->ds_cnt, fh);
+    fwrite(rrd->rra_def, sizeof(rra_def_t), rrd->stat_head->rra_cnt, fh);
+    fwrite(rrd->live_head, sizeof(live_head_t), 1, fh);
+    fwrite(rrd->pdp_prep, sizeof(pdp_prep_t), rrd->stat_head->ds_cnt, fh);
+    fwrite(rrd->cdp_prep, sizeof(cdp_prep_t),
+           rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt, fh);
+    fwrite(rrd->rra_ptr, sizeof(rra_ptr_t), rrd->stat_head->rra_cnt, fh);
+
+    /* calculate the number of rrd_values to dump */
+    rra_offset = 0;
+    for (i = 0; i < rrd->stat_head->rra_cnt; i++) {
+        unsigned long num_rows = rrd->rra_def[i].row_cnt;
+        unsigned long cur_row = rrd->rra_ptr[i].cur_row;
+        unsigned long ds_cnt = rrd->stat_head->ds_cnt;
+
+        fwrite(rrd->rrd_value +
+               (rra_offset + num_rows - 1 - cur_row) * ds_cnt,
+               sizeof(rrd_value_t), (cur_row + 1) * ds_cnt, fh);
+
+        fwrite(rrd->rrd_value + rra_offset * ds_cnt,
+               sizeof(rrd_value_t), (num_rows - 1 - cur_row) * ds_cnt, fh);
+
+        rra_offset += num_rows;
+    }
+
+    /* lets see if we had an error */
+    if (ferror(fh)) {
+        rrd_set_error("a file error occurred while creating '%s'", file_name);
+        fclose(fh);
+        return (-1);
+    }
+
+    fclose(fh);
+    return (0);
+}                       /* int write_file */
+
+int rrd_restore(
+    int argc,
+    char **argv)
+{
+    rrd_t    *rrd;
+    char     *old_locale;
+    /* init rrd clean */
+    optind = 0;
+    opterr = 0;         /* initialize getopt */
+    while (42) {
+        int       opt;
+        int       option_index = 0;
+        static struct option long_options[] = {
+            {"range-check", no_argument, 0, 'r'},
+            {"force-overwrite", no_argument, 0, 'f'},
+            {0, 0, 0, 0}
+        };
+
+        opt = getopt_long(argc, argv, "rf", long_options, &option_index);
+
+        if (opt == EOF)
+            break;
+
+        switch (opt) {
+        case 'r':
+            opt_range_check = 1;
+            break;
+
+        case 'f':
+            opt_force_overwrite = 1;
+            break;
+
+        default:
+            rrd_set_error("usage rrdtool %s [--range-check|-r] "
+                          "[--force-overwrite/-f]  file.xml file.rrd",
+                          argv[0]);
+            return (-1);
+            break;
+        }
+    }                   /* while (42) */
+
+    if ((argc - optind) != 2) {
+        rrd_set_error("usage rrdtool %s [--range-check/-r] "
+                      "[--force-overwrite/-f] file.xml file.rrd", argv[0]);
+        return (-1);
+    }
+
+    old_locale = setlocale(LC_NUMERIC, "C");
+
+    rrd = parse_file(argv[optind]);
+
+    setlocale(LC_NUMERIC, old_locale);
+
+    if (rrd == NULL)
+        return (-1);
+    
+    if (write_file(argv[optind + 1], rrd) != 0) {
+        local_rrd_free(rrd);
+        return (-1);
+    }
+    local_rrd_free(rrd);
+
+
+    return (0);
+}                       /* int rrd_restore */
+
+/* vim: set sw=2 sts=2 ts=8 et fdm=marker : */
diff --git a/program/src/rrd_rpncalc.c b/program/src/rrd_rpncalc.c
new file mode 100644 (file)
index 0000000..e392a65
--- /dev/null
@@ -0,0 +1,978 @@
+/****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ ****************************************************************************
+ * rrd_rpncalc.c  RPN calculator functions
+ ****************************************************************************/
+
+#include <limits.h>
+#include <locale.h>
+#include <stdlib.h>
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+// #include "rrd_graph.h"
+
+short     addop2str(
+    enum op_en op,
+    enum op_en op_type,
+    char *op_str,
+    char **result_str,
+    unsigned short *offset);
+int       tzoffset(
+    time_t);            /* used to implement LTIME */
+
+short rpn_compact(
+    rpnp_t *rpnp,
+    rpn_cdefds_t **rpnc,
+    short *count)
+{
+    short     i;
+
+    *count = 0;
+    /* count the number of rpn nodes */
+    while (rpnp[*count].op != OP_END)
+        (*count)++;
+    if (++(*count) > DS_CDEF_MAX_RPN_NODES) {
+        rrd_set_error("Maximum %d RPN nodes permitted",
+                      DS_CDEF_MAX_RPN_NODES);
+        return -1;
+    }
+
+    /* allocate memory */
+    *rpnc = (rpn_cdefds_t *) calloc(*count, sizeof(rpn_cdefds_t));
+    for (i = 0; rpnp[i].op != OP_END; i++) {
+        (*rpnc)[i].op = (char) rpnp[i].op;
+        if (rpnp[i].op == OP_NUMBER) {
+            /* rpnp.val is a double, rpnc.val is a short */
+            double    temp = floor(rpnp[i].val);
+
+            if (temp < SHRT_MIN || temp > SHRT_MAX) {
+                rrd_set_error
+                    ("constants must be integers in the interval (%d, %d)",
+                     SHRT_MIN, SHRT_MAX);
+                free(*rpnc);
+                return -1;
+            }
+            (*rpnc)[i].val = (short) temp;
+        } else if (rpnp[i].op == OP_VARIABLE || rpnp[i].op == OP_PREV_OTHER) {
+            (*rpnc)[i].val = (short) rpnp[i].ptr;
+        }
+    }
+    /* terminate the sequence */
+    (*rpnc)[(*count) - 1].op = OP_END;
+    return 0;
+}
+
+rpnp_t   *rpn_expand(
+    rpn_cdefds_t *rpnc)
+{
+    short     i;
+    rpnp_t   *rpnp;
+
+    /* DS_CDEF_MAX_RPN_NODES is small, so at the expense of some wasted
+     * memory we avoid any reallocs */
+    rpnp = (rpnp_t *) calloc(DS_CDEF_MAX_RPN_NODES, sizeof(rpnp_t));
+    if (rpnp == NULL)
+        return NULL;
+    for (i = 0; rpnc[i].op != OP_END; ++i) {
+        rpnp[i].op = rpnc[i].op;
+        if (rpnp[i].op == OP_NUMBER) {
+            rpnp[i].val = (double) rpnc[i].val;
+        } else if (rpnp[i].op == OP_VARIABLE || rpnp[i].op == OP_PREV_OTHER) {
+            rpnp[i].ptr = (long) rpnc[i].val;
+        }
+    }
+    /* terminate the sequence */
+    rpnp[i].op = OP_END;
+    return rpnp;
+}
+
+/* rpn_compact2str: convert a compact sequence of RPN operator nodes back
+ * into a CDEF string. This function is used by rrd_dump.
+ * arguments:
+ *  rpnc: an array of compact RPN operator nodes
+ *  ds_def: a pointer to the data source definition section of an RRD header
+ *   for lookup of data source names by index
+ *  str: out string, memory is allocated by the function, must be freed by the
+ *   the caller */
+void rpn_compact2str(
+    rpn_cdefds_t *rpnc,
+    ds_def_t *ds_def,
+    char **str)
+{
+    unsigned short i, offset = 0;
+    char      buffer[7];    /* short as a string */
+
+    for (i = 0; rpnc[i].op != OP_END; i++) {
+        if (i > 0)
+            (*str)[offset++] = ',';
+
+#define add_op(VV,VVV) \
+         if (addop2str((enum op_en)(rpnc[i].op), VV, VVV, str, &offset) == 1) continue;
+
+        if (rpnc[i].op == OP_NUMBER) {
+            /* convert a short into a string */
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+            _itoa(rpnc[i].val, buffer, 10);
+#else
+            sprintf(buffer, "%d", rpnc[i].val);
+#endif
+            add_op(OP_NUMBER, buffer)
+        }
+
+        if (rpnc[i].op == OP_VARIABLE) {
+            char     *ds_name = ds_def[rpnc[i].val].ds_nam;
+
+            add_op(OP_VARIABLE, ds_name)
+        }
+
+        if (rpnc[i].op == OP_PREV_OTHER) {
+            char     *ds_name = ds_def[rpnc[i].val].ds_nam;
+
+            add_op(OP_VARIABLE, ds_name)
+        }
+#undef add_op
+
+#define add_op(VV,VVV) \
+         if (addop2str((enum op_en)rpnc[i].op, VV, #VVV, str, &offset) == 1) continue;
+
+        add_op(OP_ADD, +)
+            add_op(OP_SUB, -)
+            add_op(OP_MUL, *)
+            add_op(OP_DIV, /)
+            add_op(OP_MOD, %)
+            add_op(OP_SIN, SIN)
+            add_op(OP_COS, COS)
+            add_op(OP_LOG, LOG)
+            add_op(OP_FLOOR, FLOOR)
+            add_op(OP_CEIL, CEIL)
+            add_op(OP_EXP, EXP)
+            add_op(OP_DUP, DUP)
+            add_op(OP_EXC, EXC)
+            add_op(OP_POP, POP)
+            add_op(OP_LT, LT)
+            add_op(OP_LE, LE)
+            add_op(OP_GT, GT)
+            add_op(OP_GE, GE)
+            add_op(OP_EQ, EQ)
+            add_op(OP_IF, IF)
+            add_op(OP_MIN, MIN)
+            add_op(OP_MAX, MAX)
+            add_op(OP_LIMIT, LIMIT)
+            add_op(OP_UNKN, UNKN)
+            add_op(OP_UN, UN)
+            add_op(OP_NEGINF, NEGINF)
+            add_op(OP_NE, NE)
+            add_op(OP_PREV, PREV)
+            add_op(OP_INF, INF)
+            add_op(OP_ISINF, ISINF)
+            add_op(OP_NOW, NOW)
+            add_op(OP_LTIME, LTIME)
+            add_op(OP_TIME, TIME)
+            add_op(OP_ATAN2, ATAN2)
+            add_op(OP_ATAN, ATAN)
+            add_op(OP_SQRT, SQRT)
+            add_op(OP_SORT, SORT)
+            add_op(OP_REV, REV)
+            add_op(OP_TREND, TREND)
+            add_op(OP_TRENDNAN, TRENDNAN)
+            add_op(OP_PREDICT, PREDICT)
+            add_op(OP_PREDICTSIGMA, PREDICTSIGMA)
+            add_op(OP_RAD2DEG, RAD2DEG)
+            add_op(OP_DEG2RAD, DEG2RAD)
+            add_op(OP_AVG, AVG)
+            add_op(OP_ABS, ABS)
+            add_op(OP_ADDNAN, ADDNAN)
+#undef add_op
+    }
+    (*str)[offset] = '\0';
+
+}
+
+short addop2str(
+    enum op_en op,
+    enum op_en op_type,
+    char *op_str,
+    char **result_str,
+    unsigned short *offset)
+{
+    if (op == op_type) {
+        short     op_len;
+
+        op_len = strlen(op_str);
+        *result_str = (char *) rrd_realloc(*result_str,
+                                           (op_len + 1 +
+                                            *offset) * sizeof(char));
+        if (*result_str == NULL) {
+            rrd_set_error("failed to alloc memory in addop2str");
+            return -1;
+        }
+        strncpy(&((*result_str)[*offset]), op_str, op_len);
+        *offset += op_len;
+        return 1;
+    }
+    return 0;
+}
+
+void parseCDEF_DS(
+    const char *def,
+    rrd_t *rrd,
+    int ds_idx)
+{
+    rpnp_t   *rpnp = NULL;
+    rpn_cdefds_t *rpnc = NULL;
+    short     count, i;
+
+    rpnp = rpn_parse((void *) rrd, def, &lookup_DS);
+    if (rpnp == NULL) {
+        rrd_set_error("failed to parse computed data source");
+        return;
+    }
+    /* Check for OP nodes not permitted in COMPUTE DS.
+     * Moved this check from within rpn_compact() because it really is
+     * COMPUTE DS specific. This is less efficient, but creation doesn't
+     * occur too often. */
+    for (i = 0; rpnp[i].op != OP_END; i++) {
+        if (rpnp[i].op == OP_TIME || rpnp[i].op == OP_LTIME ||
+            rpnp[i].op == OP_PREV || rpnp[i].op == OP_COUNT) {
+            rrd_set_error
+                ("operators time, ltime, prev and count not supported with DS COMPUTE");
+            free(rpnp);
+            return;
+        }
+    }
+    if (rpn_compact(rpnp, &rpnc, &count) == -1) {
+        free(rpnp);
+        return;
+    }
+    /* copy the compact rpn representation over the ds_def par array */
+    memcpy((void *) &(rrd->ds_def[ds_idx].par[DS_cdef]),
+           (void *) rpnc, count * sizeof(rpn_cdefds_t));
+    free(rpnp);
+    free(rpnc);
+}
+
+/* lookup a data source name in the rrd struct and return the index,
+ * should use ds_match() here except:
+ * (1) need a void * pointer to the rrd
+ * (2) error handling is left to the caller
+ */
+long lookup_DS(
+    void *rrd_vptr,
+    char *ds_name)
+{
+    unsigned int i;
+    rrd_t    *rrd;
+
+    rrd = (rrd_t *) rrd_vptr;
+
+    for (i = 0; i < rrd->stat_head->ds_cnt; ++i) {
+        if (strcmp(ds_name, rrd->ds_def[i].ds_nam) == 0)
+            return i;
+    }
+    /* the caller handles a bad data source name in the rpn string */
+    return -1;
+}
+
+/* rpn_parse : parse a string and generate a rpnp array; modified
+ * str2rpn() originally included in rrd_graph.c
+ * arguments:
+ * key_hash: a transparent argument passed to lookup(); conceptually this
+ *    is a hash object for lookup of a numeric key given a variable name
+ * expr: the string RPN expression, including variable names
+ * lookup(): a function that retrieves a numeric key given a variable name
+ */
+rpnp_t   *rpn_parse(
+    void *key_hash,
+    const char *const expr_const,
+    long      (*lookup) (void *,
+                         char *))
+{
+    int       pos = 0;
+    char     *expr;
+    long      steps = -1;
+    rpnp_t   *rpnp;
+    char      vname[MAX_VNAME_LEN + 10];
+    char     *old_locale;
+
+    old_locale = setlocale(LC_NUMERIC, "C");
+
+    rpnp = NULL;
+    expr = (char *) expr_const;
+
+    while (*expr) {
+        if ((rpnp = (rpnp_t *) rrd_realloc(rpnp, (++steps + 2) *
+                                           sizeof(rpnp_t))) == NULL) {
+            setlocale(LC_NUMERIC, old_locale);
+            return NULL;
+        }
+
+        else if ((sscanf(expr, "%lf%n", &rpnp[steps].val, &pos) == 1)
+                 && (expr[pos] == ',')) {
+            rpnp[steps].op = OP_NUMBER;
+            expr += pos;
+        }
+#define match_op(VV,VVV) \
+        else if (strncmp(expr, #VVV, strlen(#VVV))==0 && ( expr[strlen(#VVV)] == ',' || expr[strlen(#VVV)] == '\0' )){ \
+            rpnp[steps].op = VV; \
+            expr+=strlen(#VVV); \
+       }
+
+#define match_op_param(VV,VVV) \
+        else if (sscanf(expr, #VVV "(" DEF_NAM_FMT ")",vname) == 1) { \
+          int length = 0; \
+          if ((length = strlen(#VVV)+strlen(vname)+2, \
+              expr[length] == ',' || expr[length] == '\0') ) { \
+             rpnp[steps].op = VV; \
+             rpnp[steps].ptr = (*lookup)(key_hash,vname); \
+             if (rpnp[steps].ptr < 0) { \
+                          free(rpnp); \
+                          return NULL; \
+                        } else expr+=length; \
+          } \
+        }
+
+        match_op(OP_ADD, +)
+            match_op(OP_SUB, -)
+            match_op(OP_MUL, *)
+            match_op(OP_DIV, /)
+            match_op(OP_MOD, %)
+            match_op(OP_SIN, SIN)
+            match_op(OP_COS, COS)
+            match_op(OP_LOG, LOG)
+            match_op(OP_FLOOR, FLOOR)
+            match_op(OP_CEIL, CEIL)
+            match_op(OP_EXP, EXP)
+            match_op(OP_DUP, DUP)
+            match_op(OP_EXC, EXC)
+            match_op(OP_POP, POP)
+            match_op(OP_LTIME, LTIME)
+            match_op(OP_LT, LT)
+            match_op(OP_LE, LE)
+            match_op(OP_GT, GT)
+            match_op(OP_GE, GE)
+            match_op(OP_EQ, EQ)
+            match_op(OP_IF, IF)
+            match_op(OP_MIN, MIN)
+            match_op(OP_MAX, MAX)
+            match_op(OP_LIMIT, LIMIT)
+            /* order is important here ! .. match longest first */
+            match_op(OP_UNKN, UNKN)
+            match_op(OP_UN, UN)
+            match_op(OP_NEGINF, NEGINF)
+            match_op(OP_NE, NE)
+            match_op(OP_COUNT, COUNT)
+            match_op_param(OP_PREV_OTHER, PREV)
+            match_op(OP_PREV, PREV)
+            match_op(OP_INF, INF)
+            match_op(OP_ISINF, ISINF)
+            match_op(OP_NOW, NOW)
+            match_op(OP_TIME, TIME)
+            match_op(OP_ATAN2, ATAN2)
+            match_op(OP_ATAN, ATAN)
+            match_op(OP_SQRT, SQRT)
+            match_op(OP_SORT, SORT)
+            match_op(OP_REV, REV)
+            match_op(OP_TREND, TREND)
+            match_op(OP_TRENDNAN, TRENDNAN)
+            match_op(OP_PREDICT, PREDICT)
+            match_op(OP_PREDICTSIGMA, PREDICTSIGMA)
+            match_op(OP_RAD2DEG, RAD2DEG)
+            match_op(OP_DEG2RAD, DEG2RAD)
+            match_op(OP_AVG, AVG)
+            match_op(OP_ABS, ABS)
+            match_op(OP_ADDNAN, ADDNAN)
+#undef match_op
+            else if ((sscanf(expr, DEF_NAM_FMT "%n", vname, &pos) == 1)
+                     && ((rpnp[steps].ptr = (*lookup) (key_hash, vname)) !=
+                         -1)) {
+            rpnp[steps].op = OP_VARIABLE;
+            expr += pos;
+        }
+
+        else {
+            setlocale(LC_NUMERIC, old_locale);
+            free(rpnp);
+            return NULL;
+        }
+
+        if (*expr == 0)
+            break;
+        if (*expr == ',')
+            expr++;
+        else {
+            setlocale(LC_NUMERIC, old_locale);
+            free(rpnp);
+            return NULL;
+        }
+    }
+    rpnp[steps + 1].op = OP_END;
+    setlocale(LC_NUMERIC, old_locale);
+    return rpnp;
+}
+
+void rpnstack_init(
+    rpnstack_t *rpnstack)
+{
+    rpnstack->s = NULL;
+    rpnstack->dc_stacksize = 0;
+    rpnstack->dc_stackblock = 100;
+}
+
+void rpnstack_free(
+    rpnstack_t *rpnstack)
+{
+    if (rpnstack->s != NULL)
+        free(rpnstack->s);
+    rpnstack->dc_stacksize = 0;
+}
+
+static int rpn_compare_double(
+    const void *x,
+    const void *y)
+{
+    double    diff = *((const double *) x) - *((const double *) y);
+
+    return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
+}
+
+/* rpn_calc: run the RPN calculator; also performs variable substitution;
+ * moved and modified from data_calc() originally included in rrd_graph.c 
+ * arguments:
+ * rpnp : an array of RPN operators (including variable references)
+ * rpnstack : the initialized stack
+ * data_idx : when data_idx is a multiple of rpnp.step, the rpnp.data pointer
+ *   is advanced by rpnp.ds_cnt; used only for variable substitution
+ * output : an array of output values; OP_PREV assumes this array contains
+ *   the "previous" value at index position output_idx-1; the definition of
+ *   "previous" depends on the calling environment
+ * output_idx : an index into the output array in which to store the output
+ *   of the RPN calculator
+ * returns: -1 if the computation failed (also calls rrd_set_error)
+ *           0 on success
+ */
+short rpn_calc(
+    rpnp_t *rpnp,
+    rpnstack_t *rpnstack,
+    long data_idx,
+    rrd_value_t *output,
+    int output_idx)
+{
+    int       rpi;
+    long      stptr = -1;
+
+    /* process each op from the rpn in turn */
+    for (rpi = 0; rpnp[rpi].op != OP_END; rpi++) {
+        /* allocate or grow the stack */
+        if (stptr + 5 > rpnstack->dc_stacksize) {
+            /* could move this to a separate function */
+            rpnstack->dc_stacksize += rpnstack->dc_stackblock;
+            rpnstack->s = (double*)rrd_realloc(rpnstack->s,
+                                      (rpnstack->dc_stacksize) *
+                                      sizeof(*(rpnstack->s)));
+            if (rpnstack->s == NULL) {
+                rrd_set_error("RPN stack overflow");
+                return -1;
+            }
+        }
+#define stackunderflow(MINSIZE)                                \
+       if(stptr<MINSIZE){                              \
+           rrd_set_error("RPN stack underflow");       \
+           return -1;                                  \
+       }
+
+        switch (rpnp[rpi].op) {
+        case OP_NUMBER:
+            rpnstack->s[++stptr] = rpnp[rpi].val;
+            break;
+        case OP_VARIABLE:
+        case OP_PREV_OTHER:
+            /* Sanity check: VDEFs shouldn't make it here */
+            if (rpnp[rpi].ds_cnt == 0) {
+                rrd_set_error("VDEF made it into rpn_calc... aborting");
+                return -1;
+            } else {
+                /* make sure we pull the correct value from
+                 * the *.data array. Adjust the pointer into
+                 * the array acordingly. Advance the ptr one
+                 * row in the rra (skip over non-relevant
+                 * data sources)
+                 */
+                if (rpnp[rpi].op == OP_VARIABLE) {
+                    rpnstack->s[++stptr] = *(rpnp[rpi].data);
+                } else {
+                    if ((output_idx) <= 0) {
+                        rpnstack->s[++stptr] = DNAN;
+                    } else {
+                        rpnstack->s[++stptr] =
+                            *(rpnp[rpi].data - rpnp[rpi].ds_cnt);
+                    }
+
+                }
+                if (data_idx % rpnp[rpi].step == 0) {
+                    rpnp[rpi].data += rpnp[rpi].ds_cnt;
+                }
+            }
+            break;
+        case OP_COUNT:
+            rpnstack->s[++stptr] = (output_idx + 1);    /* Note: Counter starts at 1 */
+            break;
+        case OP_PREV:
+            if ((output_idx) <= 0) {
+                rpnstack->s[++stptr] = DNAN;
+            } else {
+                rpnstack->s[++stptr] = output[output_idx - 1];
+            }
+            break;
+        case OP_UNKN:
+            rpnstack->s[++stptr] = DNAN;
+            break;
+        case OP_INF:
+            rpnstack->s[++stptr] = DINF;
+            break;
+        case OP_NEGINF:
+            rpnstack->s[++stptr] = -DINF;
+            break;
+        case OP_NOW:
+            rpnstack->s[++stptr] = (double) time(NULL);
+            break;
+        case OP_TIME:
+            /* HACK: this relies on the data_idx being the time,
+             ** which the within-function scope is unaware of */
+            rpnstack->s[++stptr] = (double) data_idx;
+            break;
+        case OP_LTIME:
+            rpnstack->s[++stptr] =
+                (double) tzoffset(data_idx) + (double) data_idx;
+            break;
+        case OP_ADD:
+            stackunderflow(1);
+            rpnstack->s[stptr - 1] = rpnstack->s[stptr - 1]
+                + rpnstack->s[stptr];
+            stptr--;
+            break;
+        case OP_ADDNAN:
+            stackunderflow(1);
+            if (isnan(rpnstack->s[stptr - 1])) {
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr];
+            } else if (isnan(rpnstack->s[stptr])) {
+                /* NOOP */
+                /* rpnstack->s[stptr - 1] = rpnstack->s[stptr - 1]; */
+            } else {
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr - 1]
+                    + rpnstack->s[stptr];
+            }
+
+            stptr--;
+            break;
+        case OP_SUB:
+            stackunderflow(1);
+            rpnstack->s[stptr - 1] = rpnstack->s[stptr - 1]
+                - rpnstack->s[stptr];
+            stptr--;
+            break;
+        case OP_MUL:
+            stackunderflow(1);
+            rpnstack->s[stptr - 1] = (rpnstack->s[stptr - 1])
+                * (rpnstack->s[stptr]);
+            stptr--;
+            break;
+        case OP_DIV:
+            stackunderflow(1);
+            rpnstack->s[stptr - 1] = rpnstack->s[stptr - 1]
+                / rpnstack->s[stptr];
+            stptr--;
+            break;
+        case OP_MOD:
+            stackunderflow(1);
+            rpnstack->s[stptr - 1] = fmod(rpnstack->s[stptr - 1]
+                                          , rpnstack->s[stptr]);
+            stptr--;
+            break;
+        case OP_SIN:
+            stackunderflow(0);
+            rpnstack->s[stptr] = sin(rpnstack->s[stptr]);
+            break;
+        case OP_ATAN:
+            stackunderflow(0);
+            rpnstack->s[stptr] = atan(rpnstack->s[stptr]);
+            break;
+        case OP_RAD2DEG:
+            stackunderflow(0);
+            rpnstack->s[stptr] = 57.29577951 * rpnstack->s[stptr];
+            break;
+        case OP_DEG2RAD:
+            stackunderflow(0);
+            rpnstack->s[stptr] = 0.0174532952 * rpnstack->s[stptr];
+            break;
+        case OP_ATAN2:
+            stackunderflow(1);
+            rpnstack->s[stptr - 1] = atan2(rpnstack->s[stptr - 1],
+                                           rpnstack->s[stptr]);
+            stptr--;
+            break;
+        case OP_COS:
+            stackunderflow(0);
+            rpnstack->s[stptr] = cos(rpnstack->s[stptr]);
+            break;
+        case OP_CEIL:
+            stackunderflow(0);
+            rpnstack->s[stptr] = ceil(rpnstack->s[stptr]);
+            break;
+        case OP_FLOOR:
+            stackunderflow(0);
+            rpnstack->s[stptr] = floor(rpnstack->s[stptr]);
+            break;
+        case OP_LOG:
+            stackunderflow(0);
+            rpnstack->s[stptr] = log(rpnstack->s[stptr]);
+            break;
+        case OP_DUP:
+            stackunderflow(0);
+            rpnstack->s[stptr + 1] = rpnstack->s[stptr];
+            stptr++;
+            break;
+        case OP_POP:
+            stackunderflow(0);
+            stptr--;
+            break;
+        case OP_EXC:
+            stackunderflow(1);
+            {
+                double    dummy;
+
+                dummy = rpnstack->s[stptr];
+                rpnstack->s[stptr] = rpnstack->s[stptr - 1];
+                rpnstack->s[stptr - 1] = dummy;
+            }
+            break;
+        case OP_EXP:
+            stackunderflow(0);
+            rpnstack->s[stptr] = exp(rpnstack->s[stptr]);
+            break;
+        case OP_LT:
+            stackunderflow(1);
+            if (isnan(rpnstack->s[stptr - 1]));
+            else if (isnan(rpnstack->s[stptr]))
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr];
+            else
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr - 1] <
+                    rpnstack->s[stptr] ? 1.0 : 0.0;
+            stptr--;
+            break;
+        case OP_LE:
+            stackunderflow(1);
+            if (isnan(rpnstack->s[stptr - 1]));
+            else if (isnan(rpnstack->s[stptr]))
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr];
+            else
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr - 1] <=
+                    rpnstack->s[stptr] ? 1.0 : 0.0;
+            stptr--;
+            break;
+        case OP_GT:
+            stackunderflow(1);
+            if (isnan(rpnstack->s[stptr - 1]));
+            else if (isnan(rpnstack->s[stptr]))
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr];
+            else
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr - 1] >
+                    rpnstack->s[stptr] ? 1.0 : 0.0;
+            stptr--;
+            break;
+        case OP_GE:
+            stackunderflow(1);
+            if (isnan(rpnstack->s[stptr - 1]));
+            else if (isnan(rpnstack->s[stptr]))
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr];
+            else
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr - 1] >=
+                    rpnstack->s[stptr] ? 1.0 : 0.0;
+            stptr--;
+            break;
+        case OP_NE:
+            stackunderflow(1);
+            if (isnan(rpnstack->s[stptr - 1]));
+            else if (isnan(rpnstack->s[stptr]))
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr];
+            else
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr - 1] ==
+                    rpnstack->s[stptr] ? 0.0 : 1.0;
+            stptr--;
+            break;
+        case OP_EQ:
+            stackunderflow(1);
+            if (isnan(rpnstack->s[stptr - 1]));
+            else if (isnan(rpnstack->s[stptr]))
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr];
+            else
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr - 1] ==
+                    rpnstack->s[stptr] ? 1.0 : 0.0;
+            stptr--;
+            break;
+        case OP_IF:
+            stackunderflow(2);
+            rpnstack->s[stptr - 2] = (isnan(rpnstack->s[stptr - 2])
+                                      || rpnstack->s[stptr - 2] ==
+                                      0.0) ? rpnstack->s[stptr] : rpnstack->
+                s[stptr - 1];
+            stptr--;
+            stptr--;
+            break;
+        case OP_MIN:
+            stackunderflow(1);
+            if (isnan(rpnstack->s[stptr - 1]));
+            else if (isnan(rpnstack->s[stptr]))
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr];
+            else if (rpnstack->s[stptr - 1] > rpnstack->s[stptr])
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr];
+            stptr--;
+            break;
+        case OP_MAX:
+            stackunderflow(1);
+            if (isnan(rpnstack->s[stptr - 1]));
+            else if (isnan(rpnstack->s[stptr]))
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr];
+            else if (rpnstack->s[stptr - 1] < rpnstack->s[stptr])
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr];
+            stptr--;
+            break;
+        case OP_LIMIT:
+            stackunderflow(2);
+            if (isnan(rpnstack->s[stptr - 2]));
+            else if (isnan(rpnstack->s[stptr - 1]))
+                rpnstack->s[stptr - 2] = rpnstack->s[stptr - 1];
+            else if (isnan(rpnstack->s[stptr]))
+                rpnstack->s[stptr - 2] = rpnstack->s[stptr];
+            else if (rpnstack->s[stptr - 2] < rpnstack->s[stptr - 1])
+                rpnstack->s[stptr - 2] = DNAN;
+            else if (rpnstack->s[stptr - 2] > rpnstack->s[stptr])
+                rpnstack->s[stptr - 2] = DNAN;
+            stptr -= 2;
+            break;
+        case OP_UN:
+            stackunderflow(0);
+            rpnstack->s[stptr] = isnan(rpnstack->s[stptr]) ? 1.0 : 0.0;
+            break;
+        case OP_ISINF:
+            stackunderflow(0);
+            rpnstack->s[stptr] = isinf(rpnstack->s[stptr]) ? 1.0 : 0.0;
+            break;
+        case OP_SQRT:
+            stackunderflow(0);
+            rpnstack->s[stptr] = sqrt(rpnstack->s[stptr]);
+            break;
+        case OP_SORT:
+            stackunderflow(0);
+            {
+                int       spn = (int) rpnstack->s[stptr--];
+
+                stackunderflow(spn - 1);
+                qsort(rpnstack->s + stptr - spn + 1, spn, sizeof(double),
+                      rpn_compare_double);
+            }
+            break;
+        case OP_REV:
+            stackunderflow(0);
+            {
+                int       spn = (int) rpnstack->s[stptr--];
+                double   *p, *q;
+
+                stackunderflow(spn - 1);
+
+                p = rpnstack->s + stptr - spn + 1;
+                q = rpnstack->s + stptr;
+                while (p < q) {
+                    double    x = *q;
+
+                    *q-- = *p;
+                    *p++ = x;
+                }
+            }
+            break;
+        case OP_PREDICT:
+        case OP_PREDICTSIGMA:
+            stackunderflow(2);
+           {
+               /* the local averaging window (similar to trend, but better here, as we get better statistics thru numbers)*/
+               int   locstepsize = rpnstack->s[--stptr];
+               /* the number of shifts and range-checking*/
+               int     shifts = rpnstack->s[--stptr];
+                stackunderflow(shifts);
+               // handle negative shifts special
+               if (shifts<0) {
+                   stptr--;
+               } else {
+                   stptr-=shifts;
+               }
+               /* the real calculation */
+               double val=DNAN;
+               /* the info on the datasource */
+               time_t  dsstep = (time_t) rpnp[rpi - 1].step;
+               int    dscount = rpnp[rpi - 1].ds_cnt;
+               int   locstep = (int)ceil((float)locstepsize/(float)dsstep);
+
+               /* the sums */
+                double    sum = 0;
+               double    sum2 = 0;
+                int       count = 0;
+               /* now loop for each position */
+               int doshifts=shifts;
+               if (shifts<0) { doshifts=-shifts; }
+               for(int loop=0;loop<doshifts;loop++) {
+                   /* calculate shift step */
+                   int shiftstep=1;
+                   if (shifts<0) {
+                       shiftstep = loop*rpnstack->s[stptr];
+                   } else { 
+                       shiftstep = rpnstack->s[stptr+loop]; 
+                   }
+                   if(shiftstep <0) {
+                       rrd_set_error("negative shift step not allowed: %i",shiftstep);
+                       return -1;
+                   }
+                   shiftstep=(int)ceil((float)shiftstep/(float)dsstep);
+                   /* loop all local shifts */
+                   for(int i=0;i<=locstep;i++) {
+                       /* now calculate offset into data-array - relative to output_idx*/
+                       int offset=shiftstep+i;
+                       /* and process if we have index 0 of above */
+                       if ((offset>=0)&&(offset<output_idx)) {
+                           /* get the value */
+                           val =rpnp[rpi - 1].data[-dscount * offset];
+                           /* and handle the non NAN case only*/
+                           if (! isnan(val)) {
+                               sum+=val;
+                               sum2+=val*val;
+                               count++;
+                           }
+                       }
+                   }
+               }
+               /* do the final calculations */
+               val=DNAN;
+               if (rpnp[rpi].op == OP_PREDICT) {  /* the average */
+                   if (count>0) {
+                       val = sum/(double)count;
+                   } 
+               } else {
+                   if (count>1) { /* the sigma case */
+                       val=count*sum2-sum*sum;
+                       if (val<0) {
+                           val=DNAN;
+                       } else {
+                           val=sqrt(val/((float)count*((float)count-1.0)));
+                       }
+                   }
+               }
+               rpnstack->s[stptr] = val;
+           }
+            break;
+        case OP_TREND:
+        case OP_TRENDNAN:
+            stackunderflow(1);
+            if ((rpi < 2) || (rpnp[rpi - 2].op != OP_VARIABLE)) {
+                rrd_set_error("malformed trend arguments");
+                return -1;
+            } else {
+                time_t    dur = (time_t) rpnstack->s[stptr];
+                time_t    step = (time_t) rpnp[rpi - 2].step;
+
+                if (output_idx > (int) ceil((float) dur / (float) step)) {
+                    int       ignorenan = (rpnp[rpi].op == OP_TREND);
+                    double    accum = 0.0;
+                    int       i = 0;
+                    int       count = 0;
+
+                    do {
+                        double    val =
+                            rpnp[rpi - 2].data[rpnp[rpi - 2].ds_cnt * i--];
+                        if (ignorenan || !isnan(val)) {
+                            accum += val;
+                            ++count;
+                        }
+
+                        dur -= step;
+                    } while (dur > 0);
+
+                    rpnstack->s[--stptr] =
+                        (count == 0) ? DNAN : (accum / count);
+                } else
+                    rpnstack->s[--stptr] = DNAN;
+            }
+            break;
+        case OP_AVG:
+            stackunderflow(0);
+            {
+                int       i = (int) rpnstack->s[stptr--];
+                double    sum = 0;
+                int       count = 0;
+
+                stackunderflow(i - 1);
+                while (i > 0) {
+                    double    val = rpnstack->s[stptr--];
+
+                    i--;
+                    if (isnan(val)) {
+                        continue;
+                    }
+                    count++;
+                    sum += val;
+                }
+                /* now push the result back on stack */
+                if (count > 0) {
+                    rpnstack->s[++stptr] = sum / count;
+                } else {
+                    rpnstack->s[++stptr] = DNAN;
+                }
+            }
+            break;
+        case OP_ABS:
+            stackunderflow(0);
+            rpnstack->s[stptr] = fabs(rpnstack->s[stptr]);
+            break;
+        case OP_END:
+            break;
+        }
+#undef stackunderflow
+    }
+    if (stptr != 0) {
+        rrd_set_error("RPN final stack size != 1");
+        return -1;
+    }
+
+    output[output_idx] = rpnstack->s[0];
+    return 0;
+}
+
+/* figure out what the local timezone offset for any point in
+   time was. Return it in seconds */
+int tzoffset(
+    time_t now)
+{
+    int       gm_sec, gm_min, gm_hour, gm_yday, gm_year,
+        l_sec, l_min, l_hour, l_yday, l_year;
+    struct tm t;
+    int       off;
+
+    gmtime_r(&now, &t);
+    gm_sec = t.tm_sec;
+    gm_min = t.tm_min;
+    gm_hour = t.tm_hour;
+    gm_yday = t.tm_yday;
+    gm_year = t.tm_year;
+    localtime_r(&now, &t);
+    l_sec = t.tm_sec;
+    l_min = t.tm_min;
+    l_hour = t.tm_hour;
+    l_yday = t.tm_yday;
+    l_year = t.tm_year;
+    off =
+        (l_sec - gm_sec) + (l_min - gm_min) * 60 + (l_hour - gm_hour) * 3600;
+    if (l_yday > gm_yday || l_year > gm_year) {
+        off += 24 * 3600;
+    } else if (l_yday < gm_yday || l_year < gm_year) {
+        off -= 24 * 3600;
+    }
+    return off;
+}
diff --git a/program/src/rrd_rpncalc.h b/program/src/rrd_rpncalc.h
new file mode 100644 (file)
index 0000000..61b8940
--- /dev/null
@@ -0,0 +1,87 @@
+/****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ ****************************************************************************
+ * rrd_rpncalc.h  RPN calculator functions
+ ****************************************************************************/
+#ifndef _RRD_RPNCALC_H
+#define _RRD_RPNCALC_H
+
+/* WARNING: if new operators are added, they MUST be added at the very end of the list.
+ * This is because COMPUTE (CDEF) DS store OP nodes by number (name is not
+ * an option due to limited par array size). OP nodes must have the same
+ * numeric values, otherwise the stored numbers will mean something different. */
+enum op_en { OP_NUMBER = 0, OP_VARIABLE, OP_INF, OP_PREV, OP_NEGINF,
+    OP_UNKN, OP_NOW, OP_TIME, OP_ADD, OP_MOD, OP_SUB, OP_MUL,
+    OP_DIV, OP_SIN, OP_DUP, OP_EXC, OP_POP,
+    OP_COS, OP_LOG, OP_EXP, OP_LT, OP_LE, OP_GT, OP_GE, OP_EQ, OP_IF,
+    OP_MIN, OP_MAX, OP_LIMIT, OP_FLOOR, OP_CEIL,
+    OP_UN, OP_END, OP_LTIME, OP_NE, OP_ISINF, OP_PREV_OTHER, OP_COUNT,
+    OP_ATAN, OP_SQRT, OP_SORT, OP_REV, OP_TREND, OP_TRENDNAN,
+    OP_ATAN2, OP_RAD2DEG, OP_DEG2RAD,
+    OP_PREDICT,OP_PREDICTSIGMA,
+    OP_AVG, OP_ABS, OP_ADDNAN
+};
+
+typedef struct rpnp_t {
+    enum op_en op;
+    double    val;      /* value for a OP_NUMBER */
+    long      ptr;      /* pointer into the gdes array for OP_VAR */
+    double   *data;     /* pointer to the current value from OP_VAR DAS */
+    long      ds_cnt;   /* data source count for data pointer */
+    long      step;     /* time step for OP_VAR das */
+} rpnp_t;
+
+/* a compact representation of rpnp_t for computed data sources */
+typedef struct rpn_cdefds_t {
+    char      op;       /* rpn operator type */
+    short     val;      /* used by OP_NUMBER and OP_VARIABLE */
+} rpn_cdefds_t;
+
+#define MAX_VNAME_LEN 255
+#define DEF_NAM_FMT "%255[-_A-Za-z0-9]"
+
+/* limit imposed by sizeof(rpn_cdefs_t) and rrd.ds_def.par */
+#define DS_CDEF_MAX_RPN_NODES 26
+
+typedef struct rpnstack_t {
+    double   *s;
+    long      dc_stacksize;
+    long      dc_stackblock;
+} rpnstack_t;
+
+void      rpnstack_init(
+    rpnstack_t *rpnstack);
+void      rpnstack_free(
+    rpnstack_t *rpnstack);
+
+void      parseCDEF_DS(
+    const char *def,
+    rrd_t *rrd,
+    int ds_idx);
+long      lookup_DS(
+    void *rrd_vptr,
+    char *ds_name);
+
+short     rpn_compact(
+    rpnp_t *rpnp,
+    rpn_cdefds_t **rpnc,
+    short *count);
+rpnp_t   *rpn_expand(
+    rpn_cdefds_t *rpnc);
+void      rpn_compact2str(
+    rpn_cdefds_t *rpnc,
+    ds_def_t *ds_def,
+    char **str);
+rpnp_t   *rpn_parse(
+    void *key_hash,
+    const char *const expr,
+    long      (*lookup) (void *,
+                         char *));
+short     rpn_calc(
+    rpnp_t *rpnp,
+    rpnstack_t *rpnstack,
+    long data_idx,
+    rrd_value_t *output,
+    int output_idx);
+
+#endif
diff --git a/program/src/rrd_thread_safe.c b/program/src/rrd_thread_safe.c
new file mode 100644 (file)
index 0000000..94af58b
--- /dev/null
@@ -0,0 +1,110 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ * This file:     Copyright 2003 Peter Stamfest <peter@stamfest.at> 
+ *                             & Tobias Oetiker
+ * Distributed under the GPL
+ *****************************************************************************
+ * rrd_thread_safe.c   Contains routines used when thread safety is required
+ *****************************************************************************
+ * $Id$
+ *************************************************************************** */
+
+#include <pthread.h>
+#include <string.h>
+/* #include <error.h> */
+#include "rrd.h"
+#include "rrd_tool.h"
+
+/* Key for the thread-specific rrd_context */
+static pthread_key_t context_key;
+
+/* Once-only initialisation of the key */
+static pthread_once_t context_key_once = PTHREAD_ONCE_INIT;
+
+/* Free the thread-specific rrd_context - we might actually use
+   rrd_free_context instead...
+ */
+static void context_destroy_context(
+    void *ctx_)
+{
+    rrd_context_t *ctx = ctx_;
+
+    if (ctx)
+        rrd_free_context(ctx);
+}
+
+/* Allocate the key */
+static void context_get_key(
+    void)
+{
+    pthread_key_create(&context_key, context_destroy_context);
+}
+
+rrd_context_t *rrd_get_context(
+    void)
+{
+    rrd_context_t *ctx;
+
+    pthread_once(&context_key_once, context_get_key);
+    ctx = pthread_getspecific(context_key);
+    if (!ctx) {
+        ctx = rrd_new_context();
+        pthread_setspecific(context_key, ctx);
+    }
+    return ctx;
+}
+
+#ifdef HAVE_STRERROR_R
+const char *rrd_strerror(
+    int err)
+{
+    rrd_context_t *ctx = rrd_get_context();
+    char *ret = "unknown error";
+
+    *ctx->lib_errstr = '\0';
+
+    /* Even though POSIX/XSI requires "strerror_r" to return an "int", some
+     * systems (e.g. the GNU libc) return a "char *" _and_ ignore the second
+     * argument ... -tokkee */
+#ifdef STRERROR_R_CHAR_P
+    ret = strerror_r(err, ctx->lib_errstr, sizeof(ctx->lib_errstr));
+    if ((! ret) || (*ret == '\0')) {
+        if (*ctx->lib_errstr != '\0')
+            ret = ctx->lib_errstr;
+        else {
+            /* according to the manpage this should not happen -
+               let's handle it somehow sanely anyway */
+            snprintf(ctx->lib_errstr, sizeof(ctx->lib_errstr),
+                    "unknown error %i - strerror_r did not return anything",
+                    err);
+            ctx->lib_errstr[sizeof(ctx->lib_errstr) - 1] = '\0';
+            ret = ctx->lib_errstr;
+        }
+    }
+#else /* ! STRERROR_R_CHAR_P */
+    if (strerror_r(err, ctx->lib_errstr, sizeof(ctx->lib_errstr))) {
+        snprintf(ctx->lib_errstr, sizeof(ctx->lib_errstr),
+                "unknown error %i - strerror_r returned with errno = %i",
+                err, errno);
+        ctx->lib_errstr[sizeof(ctx->lib_errstr) - 1] = '\0';
+    }
+    ret = ctx->lib_errstr;
+#endif
+    return ret;
+}
+#else
+#undef strerror
+const char *rrd_strerror(
+    int err)
+{
+    static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
+    rrd_context_t *ctx;
+
+    ctx = rrd_get_context();
+    pthread_mutex_lock(&mtx);
+    strncpy(ctx->lib_errstr, strerror(err), sizeof(ctx->lib_errstr));
+    ctx->lib_errstr[sizeof(ctx->lib_errstr) - 1] = '\0';
+    pthread_mutex_unlock(&mtx);
+    return ctx->lib_errstr;
+}
+#endif
diff --git a/program/src/rrd_thread_safe_nt.c b/program/src/rrd_thread_safe_nt.c
new file mode 100644 (file)
index 0000000..1072e54
--- /dev/null
@@ -0,0 +1,181 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ * This file:     Copyright 2003 Peter Stamfest <peter@stamfest.at> 
+ *                             & Tobias Oetiker
+ * Distributed under the GPL
+ *****************************************************************************
+ * rrd_thread_safe.c   Contains routines used when thread safety is required
+ *                     for win32
+ *****************************************************************************
+ * $Id$
+ *************************************************************************** */
+
+#include <windows.h>
+#include <string.h>
+/* #include <error.h> */
+#include "rrd.h"
+#include "rrd_tool.h"
+
+/* Key for the thread-specific rrd_context */
+static DWORD context_key;
+static CRITICAL_SECTION CriticalSection;
+
+
+/* Once-only initialisation of the key */
+static DWORD context_key_once = 0;
+
+
+/* Free the thread-specific rrd_context - we might actually use
+   rrd_free_context instead...
+ */
+static void context_destroy_context(
+    void)
+{
+    DeleteCriticalSection(&CriticalSection);
+    TlsFree(context_key);
+    context_key_once = 0;
+}
+static void context_init_context(
+    void)
+{
+    if (!InterlockedExchange((LONG*)(&context_key_once), 1)) {
+        context_key = TlsAlloc();
+        InitializeCriticalSection(&CriticalSection);
+        atexit(context_destroy_context);
+    }
+}
+rrd_context_t *rrd_get_context(
+    void)
+{
+    rrd_context_t *ctx;
+
+    context_init_context();
+
+    ctx = (rrd_context_t*)TlsGetValue(context_key);
+    if (!ctx) {
+        ctx = rrd_new_context();
+        TlsSetValue(context_key, ctx);
+    }
+    return ctx;
+}
+
+
+/* this was added by the win32 porters Christof.Wegmann@exitgames.com */
+
+rrd_context_t *rrd_force_new_context(
+    void)
+{
+    rrd_context_t *ctx;
+
+    context_init_context();
+
+    ctx = rrd_new_context();
+    TlsSetValue(context_key, ctx);
+
+    return ctx;
+}
+
+
+#undef strerror
+const char *rrd_strerror(
+    int err)
+{
+    rrd_context_t *ctx;
+
+    context_init_context();
+
+    ctx = rrd_get_context();
+
+    EnterCriticalSection(&CriticalSection);
+    strncpy(ctx->lib_errstr, strerror(err), sizeof(ctx->lib_errstr));
+    ctx->lib_errstr[sizeof(ctx->lib_errstr) - 1] = '\0';
+    LeaveCriticalSection(&CriticalSection);
+
+    return ctx->lib_errstr;
+}
+
+/*
+ * there much be a re-entrant version of these somewhere in win32 land
+ */
+struct tm *localtime_r(
+    const time_t *timep,
+    struct tm *result)
+{
+    struct tm *local;
+
+    context_init_context();
+
+    EnterCriticalSection(&CriticalSection);
+    local = localtime(timep);
+    memcpy(result, local, sizeof(struct tm));
+    LeaveCriticalSection(&CriticalSection);
+    return result;
+}
+
+char     *ctime_r(
+    const time_t *timep,
+    char *result)
+{
+    char     *local;
+
+    context_init_context();
+
+    EnterCriticalSection(&CriticalSection);
+    local = ctime(timep);
+    strcpy(result, local);
+    LeaveCriticalSection(&CriticalSection);
+    return result;
+}
+
+struct tm *gmtime_r(
+    const time_t *timep,
+    struct tm *result)
+{
+    struct tm *local;
+
+    context_init_context();
+
+    EnterCriticalSection(&CriticalSection);
+    local = gmtime(timep);
+    memcpy(result, local, sizeof(struct tm));
+    LeaveCriticalSection(&CriticalSection);
+    return result;
+}
+
+/* implementation from Apache's APR library */
+char     *strtok_r(
+    char *str,
+    const char *sep,
+    char **last)
+{
+    char     *token;
+
+    context_init_context();
+
+
+    if (!str)           /* subsequent call */
+        str = *last;    /* start where we left off */
+
+    /* skip characters in sep (will terminate at '\0') */
+    while (*str && strchr(sep, *str))
+        ++str;
+
+    if (!*str)          /* no more tokens */
+        return NULL;
+
+    token = str;
+
+    /* skip valid token characters to terminate token and
+     * prepare for the next call (will terminate at '\0) 
+     */
+    *last = token + 1;
+    while (**last && !strchr(sep, **last))
+        ++ * last;
+
+    if (**last) {
+        **last = '\0';
+        ++*last;
+    }
+
+    return token;
+}
diff --git a/program/src/rrd_tool.c b/program/src/rrd_tool.c
new file mode 100644 (file)
index 0000000..ea94ea1
--- /dev/null
@@ -0,0 +1,923 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_tool.c  Startup wrapper
+ *****************************************************************************/
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__) && !defined(HAVE_CONFIG_H)
+#include "../win32/config.h"
+#include <stdlib.h>
+#include <sys/stat.h>
+#else
+#ifdef HAVE_CONFIG_H
+#include "../rrd_config.h"
+#endif
+#endif
+
+#include "rrd_tool.h"
+#include "rrd_xport.h"
+#include "rrd_i18n.h"
+
+#include <locale.h>
+
+
+void      PrintUsage(
+    char *cmd);
+int       CountArgs(
+    char *aLine);
+int       CreateArgs(
+    char *,
+    char *,
+    char **);
+int       HandleInputLine(
+    int,
+    char **,
+    FILE *);
+int       RemoteMode = 0;
+int       ChangeRoot = 0;
+
+#define TRUE           1
+#define FALSE          0
+#define MAX_LENGTH     10000
+
+
+void PrintUsage(
+    char *cmd)
+{
+
+    const char *help_main =
+        N_("RRDtool %s"
+           "  Copyright 1997-2009 by Tobias Oetiker <tobi@oetiker.ch>\n"
+           "               Compiled %s %s\n\n"
+           "Usage: rrdtool [options] command command_options\n");
+
+    const char *help_list =
+        N_
+        ("Valid commands: create, update, updatev, graph, graphv,  dump, restore,\n"
+         "\t\tlast, lastupdate, first, info, fetch, tune,\n"
+         "\t\tresize, xport, flushcached\n");
+
+    const char *help_listremote =
+        N_("Valid remote commands: quit, ls, cd, mkdir, pwd\n");
+
+
+    const char *help_create =
+        N_("* create - create a new RRD\n\n"
+           "\trrdtool create filename [--start|-b start time]\n"
+           "\t\t[--step|-s step]\n"
+           "\t\t[--no-overwrite|-O]\n"
+           "\t\t[DS:ds-name:DST:dst arguments]\n"
+           "\t\t[RRA:CF:cf arguments]\n");
+
+    const char *help_dump =
+        N_("* dump - dump an RRD to XML\n\n"
+           "\trrdtool dump filename.rrd >filename.xml\n");
+
+    const char *help_info =
+        N_("* info - returns the configuration and status of the RRD\n\n"
+           "\trrdtool info filename.rrd\n");
+
+    const char *help_restore =
+        N_("* restore - restore an RRD file from its XML form\n\n"
+           "\trrdtool restore [--range-check|-r] [--force-overwrite|-f] filename.xml filename.rrd\n");
+
+    const char *help_last =
+        N_("* last - show last update time for RRD\n\n"
+           "\trrdtool last filename.rrd\n");
+
+    const char *help_lastupdate =
+        N_("* lastupdate - returns the most recent datum stored for\n"
+           "  each DS in an RRD\n\n" "\trrdtool lastupdate filename.rrd\n");
+
+    const char *help_first =
+        N_("* first - show first update time for RRA within an RRD\n\n"
+           "\trrdtool first filename.rrd [--rraindex number]\n");
+
+    const char *help_update =
+        N_("* update - update an RRD\n\n"
+           "\trrdtool update filename\n"
+           "\t\t[--template|-t ds-name:ds-name:...]\n"
+          "\t\t[--daemon <address>]\n"
+           "\t\ttime|N:value[:value...]\n\n"
+           "\t\tat-time@value[:value...]\n\n"
+           "\t\t[ time:value[:value...] ..]\n");
+
+    const char *help_updatev =
+        N_("* updatev - a verbose version of update\n"
+           "\treturns information about values, RRAs, and datasources updated\n\n"
+           "\trrdtool updatev filename\n"
+           "\t\t[--template|-t ds-name:ds-name:...]\n"
+           "\t\ttime|N:value[:value...]\n\n"
+           "\t\tat-time@value[:value...]\n\n"
+           "\t\t[ time:value[:value...] ..]\n");
+
+    const char *help_fetch =
+        N_("* fetch - fetch data out of an RRD\n\n"
+           "\trrdtool fetch filename.rrd CF\n"
+           "\t\t[-r|--resolution resolution]\n"
+           "\t\t[-s|--start start] [-e|--end end]\n"
+          "\t\t[--daemon <address>]\n");
+
+    const char *help_flushcached =
+        N_("* flushcached - flush cached data out to an RRD file\n\n"
+           "\trrdtool flushcached filename.rrd\n"
+          "\t\t[--daemon <address>]\n");
+
+/* break up very large strings (help_graph, help_tune) for ISO C89 compliance*/
+
+    const char *help_graph0 =
+        N_("* graph - generate a graph from one or several RRD\n\n"
+           "\trrdtool graph filename [-s|--start seconds] [-e|--end seconds]\n");
+    const char *help_graphv0 =
+        N_("* graphv - generate a graph from one or several RRD\n"
+           "           with meta data printed before the graph\n\n"
+           "\trrdtool graphv filename [-s|--start seconds] [-e|--end seconds]\n");
+    const char *help_graph1 =
+        N_("\t\t[-x|--x-grid x-axis grid and label]\n"
+           "\t\t[-Y|--alt-y-grid] [--full-size-mode]\n"
+           "\t\t[-y|--y-grid y-axis grid and label]\n"
+           "\t\t[-v|--vertical-label string] [-w|--width pixels]\n"
+           "\t\t[--right-axis scale:shift] [--right-axis-label label]\n"
+           "\t\t[--right-axis-format format]\n"
+           "\t\t[-h|--height pixels] [-o|--logarithmic]\n"
+           "\t\t[-u|--upper-limit value] [-z|--lazy]\n"
+           "\t\t[-l|--lower-limit value] [-r|--rigid]\n"
+           "\t\t[-g|--no-legend] [--daemon <address>]\n"
+           "\t\t[-F|--force-rules-legend]\n" "\t\t[-j|--only-graph]\n");
+    const char *help_graph2 =
+        N_("\t\t[-n|--font FONTTAG:size:font]\n"
+           "\t\t[-m|--zoom factor]\n"
+           "\t\t[-A|--alt-autoscale]\n"
+           "\t\t[-M|--alt-autoscale-max]\n"
+           "\t\t[-G|--graph-render-mode {normal,mono}]\n"
+           "\t\t[-R|--font-render-mode {normal,light,mono}]\n"
+           "\t\t[-B|--font-smoothing-threshold size]\n"
+           "\t\t[-T|--tabwidth width]\n"
+           "\t\t[-E|--slope-mode]\n"
+           "\t\t[-P|--pango-markup]\n"
+           "\t\t[-N|--no-gridfit]\n"
+           "\t\t[-X|--units-exponent value]\n"
+           "\t\t[-L|--units-length value]\n"
+           "\t\t[-S|--step seconds]\n"
+           "\t\t[-f|--imginfo printfstr]\n"
+           "\t\t[-a|--imgformat PNG]\n"
+           "\t\t[-c|--color COLORTAG#rrggbb[aa]]\n"
+           "\t\t[--border width\n"
+           "\t\t[-t|--title string]\n"
+           "\t\t[-W|--watermark string]\n"
+           "\t\t[DEF:vname=rrd:ds-name:CF]\n");
+    const char *help_graph3 =
+        N_("\t\t[CDEF:vname=rpn-expression]\n"
+           "\t\t[VDEF:vdefname=rpn-expression]\n"
+           "\t\t[PRINT:vdefname:format]\n"
+           "\t\t[GPRINT:vdefname:format]\n" "\t\t[COMMENT:text]\n"
+           "\t\t[SHIFT:vname:offset]\n"
+           "\t\t[TEXTALIGN:{left|right|justified|center}]\n"
+           "\t\t[TICK:vname#rrggbb[aa][:[fraction][:legend]]]\n"
+           "\t\t[HRULE:value#rrggbb[aa][:legend]]\n"
+           "\t\t[VRULE:value#rrggbb[aa][:legend]]\n"
+           "\t\t[LINE[width]:vname[#rrggbb[aa][:[legend][:STACK]]]]\n"
+           "\t\t[AREA:vname[#rrggbb[aa][:[legend][:STACK]]]]\n"
+           "\t\t[PRINT:vname:CF:format] (deprecated)\n"
+           "\t\t[GPRINT:vname:CF:format] (deprecated)\n"
+           "\t\t[STACK:vname[#rrggbb[aa][:legend]]] (deprecated)\n");
+    const char *help_tune1 =
+        N_(" * tune -  Modify some basic properties of an RRD\n\n"
+           "\trrdtool tune filename\n"
+           "\t\t[--heartbeat|-h ds-name:heartbeat]\n"
+           "\t\t[--data-source-type|-d ds-name:DST]\n"
+           "\t\t[--data-source-rename|-r old-name:new-name]\n"
+           "\t\t[--minimum|-i ds-name:min] [--maximum|-a ds-name:max]\n"
+           "\t\t[--deltapos scale-value] [--deltaneg scale-value]\n"
+           "\t\t[--failure-threshold integer]\n"
+           "\t\t[--window-length integer]\n"
+           "\t\t[--alpha adaptation-parameter]\n");
+    const char *help_tune2 =
+        N_("\t\t[--beta adaptation-parameter]\n"
+           "\t\t[--gamma adaptation-parameter]\n"
+           "\t\t[--gamma-deviation adaptation-parameter]\n"
+           "\t\t[--aberrant-reset ds-name]\n");
+    const char *help_resize =
+        N_
+        (" * resize - alter the length of one of the RRAs in an RRD\n\n"
+         "\trrdtool resize filename rranum GROW|SHRINK rows\n");
+    const char *help_xport =
+        N_("* xport - generate XML dump from one or several RRD\n\n"
+           "\trrdtool xport [-s|--start seconds] [-e|--end seconds]\n"
+           "\t\t[-m|--maxrows rows]\n" "\t\t[--step seconds]\n"
+           "\t\t[--enumds]\n" "\t\t[DEF:vname=rrd:ds-name:CF]\n"
+           "\t\t[CDEF:vname=rpn-expression]\n"
+           "\t\t[XPORT:vname:legend]\n");
+    const char *help_quit =
+        N_(" * quit - closing a session in remote mode\n\n"
+           "\trrdtool quit\n");
+    const char *help_ls =
+        N_(" * ls - lists all *.rrd files in current directory\n\n"
+           "\trrdtool ls\n");
+    const char *help_cd =
+        N_(" * cd - changes the current directory\n\n"
+           "\trrdtool cd new directory\n");
+    const char *help_mkdir =
+        N_(" * mkdir - creates a new directory\n\n"
+           "\trrdtool mkdir newdirectoryname\n");
+    const char *help_pwd =
+        N_(" * pwd - returns the current working directory\n\n"
+           "\trrdtool pwd\n");
+    const char *help_lic =
+        N_("RRDtool is distributed under the Terms of the GNU General\n"
+           "Public License Version 2. (www.gnu.org/copyleft/gpl.html)\n\n"
+           "For more information read the RRD manpages\n");
+    enum { C_NONE, C_CREATE, C_DUMP, C_INFO, C_RESTORE, C_LAST,
+        C_LASTUPDATE, C_FIRST, C_UPDATE, C_FETCH, C_GRAPH, C_GRAPHV,
+        C_TUNE,
+        C_RESIZE, C_XPORT, C_QUIT, C_LS, C_CD, C_MKDIR, C_PWD,
+        C_UPDATEV, C_FLUSHCACHED
+    };
+    int       help_cmd = C_NONE;
+
+    if (cmd) {
+        if (!strcmp(cmd, "create"))
+            help_cmd = C_CREATE;
+        else if (!strcmp(cmd, "dump"))
+            help_cmd = C_DUMP;
+        else if (!strcmp(cmd, "info"))
+            help_cmd = C_INFO;
+        else if (!strcmp(cmd, "restore"))
+            help_cmd = C_RESTORE;
+        else if (!strcmp(cmd, "last"))
+            help_cmd = C_LAST;
+        else if (!strcmp(cmd, "lastupdate"))
+            help_cmd = C_LASTUPDATE;
+        else if (!strcmp(cmd, "first"))
+            help_cmd = C_FIRST;
+        else if (!strcmp(cmd, "update"))
+            help_cmd = C_UPDATE;
+        else if (!strcmp(cmd, "updatev"))
+            help_cmd = C_UPDATEV;
+        else if (!strcmp(cmd, "fetch"))
+            help_cmd = C_FETCH;
+        else if (!strcmp(cmd, "flushcached"))
+            help_cmd = C_FLUSHCACHED;
+        else if (!strcmp(cmd, "graph"))
+            help_cmd = C_GRAPH;
+        else if (!strcmp(cmd, "graphv"))
+            help_cmd = C_GRAPHV;
+        else if (!strcmp(cmd, "tune"))
+            help_cmd = C_TUNE;
+        else if (!strcmp(cmd, "resize"))
+            help_cmd = C_RESIZE;
+        else if (!strcmp(cmd, "xport"))
+            help_cmd = C_XPORT;
+        else if (!strcmp(cmd, "quit"))
+            help_cmd = C_QUIT;
+        else if (!strcmp(cmd, "ls"))
+            help_cmd = C_LS;
+        else if (!strcmp(cmd, "cd"))
+            help_cmd = C_CD;
+        else if (!strcmp(cmd, "mkdir"))
+            help_cmd = C_MKDIR;
+        else if (!strcmp(cmd, "pwd"))
+            help_cmd = C_PWD;
+    }
+    fprintf(stdout, _(help_main), PACKAGE_VERSION, __DATE__, __TIME__);
+    fflush(stdout);
+    switch (help_cmd) {
+    case C_NONE:
+        puts(_(help_list));
+        if (RemoteMode) {
+            puts(_(help_listremote));
+        }
+        break;
+    case C_CREATE:
+        puts(_(help_create));
+        break;
+    case C_DUMP:
+        puts(_(help_dump));
+        break;
+    case C_INFO:
+        puts(_(help_info));
+        break;
+    case C_RESTORE:
+        puts(_(help_restore));
+        break;
+    case C_LAST:
+        puts(_(help_last));
+        break;
+    case C_LASTUPDATE:
+        puts(_(help_lastupdate));
+        break;
+    case C_FIRST:
+        puts(_(help_first));
+        break;
+    case C_UPDATE:
+        puts(_(help_update));
+        break;
+    case C_UPDATEV:
+        puts(_(help_updatev));
+        break;
+    case C_FETCH:
+        puts(_(help_fetch));
+        break;
+    case C_FLUSHCACHED:
+        puts(_(help_flushcached));
+        break;
+    case C_GRAPH:
+        puts(_(help_graph0));
+        puts(_(help_graph1));
+        puts(_(help_graph2));
+        puts(_(help_graph3));
+        break;
+    case C_GRAPHV:
+        puts(_(help_graphv0));
+        puts(_(help_graph1));
+        puts(_(help_graph2));
+        puts(_(help_graph3));
+        break;
+    case C_TUNE:
+        puts(_(help_tune1));
+        puts(_(help_tune2));
+        break;
+    case C_RESIZE:
+        puts(_(help_resize));
+        break;
+    case C_XPORT:
+        puts(_(help_xport));
+        break;
+    case C_QUIT:
+        puts(_(help_quit));
+        break;
+    case C_LS:
+        puts(_(help_ls));
+        break;
+    case C_CD:
+        puts(_(help_cd));
+        break;
+    case C_MKDIR:
+        puts(_(help_mkdir));
+        break;
+    case C_PWD:
+        puts(_(help_pwd));
+        break;
+    }
+    puts(_(help_lic));
+}
+
+static char *fgetslong(
+    char **aLinePtr,
+    FILE * stream)
+{
+    char     *linebuf;
+    size_t    bufsize = MAX_LENGTH;
+    int       eolpos = 0;
+
+    if (feof(stream))
+        return *aLinePtr = 0;
+    if (!(linebuf = malloc(bufsize))) {
+        perror("fgetslong: malloc");
+        exit(1);
+    }
+    linebuf[0] = '\0';
+    while (fgets(linebuf + eolpos, MAX_LENGTH, stream)) {
+        eolpos += strlen(linebuf + eolpos);
+        if (linebuf[eolpos - 1] == '\n')
+            return *aLinePtr = linebuf;
+        bufsize += MAX_LENGTH;
+        if (!(linebuf = realloc(linebuf, bufsize))) {
+            free(linebuf);
+            perror("fgetslong: realloc");
+            exit(1);
+        }
+    }
+    if (linebuf[0]){
+        return  *aLinePtr = linebuf;
+    }
+    free(linebuf);
+    return *aLinePtr = 0;
+}
+
+int main(
+    int argc,
+    char *argv[])
+{
+    char    **myargv;
+    char     *aLine;
+    char     *firstdir = "";
+
+#ifdef MUST_DISABLE_SIGFPE
+    signal(SIGFPE, SIG_IGN);
+#endif
+#ifdef MUST_DISABLE_FPMASK
+    fpsetmask(0);
+#endif
+
+    /* initialize locale settings
+       according to localeconv(3) */       
+    setlocale(LC_ALL, "");
+
+#if defined(HAVE_LIBINTL_H) && defined(BUILD_LIBINTL)
+    bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
+    textdomain(GETTEXT_PACKAGE);
+#endif
+    if (argc == 1) {
+        PrintUsage("");
+        return 0;
+    }
+
+    if (((argc == 2) || (argc == 3)) && !strcmp("-", argv[1])) {
+#if HAVE_GETRUSAGE
+        struct rusage myusage;
+        struct timeval starttime;
+        struct timeval currenttime;
+
+        gettimeofday(&starttime, NULL);
+#endif
+        RemoteMode = 1;
+        if ((argc == 3) && strcmp("", argv[2])) {
+
+            if (
+#ifdef HAVE_GETUID
+                   getuid()
+#else
+                   1
+#endif
+                   == 0) {
+
+#ifdef HAVE_CHROOT
+                if (chroot(argv[2]) != 0){
+                    fprintf(stderr, "ERROR: chroot %s: %s\n", argv[2],rrd_strerror(errno));
+                    exit(errno);
+                }
+                ChangeRoot = 1;
+                firstdir = "/";
+#else
+                fprintf(stderr,
+                        "ERROR: change root is not supported by your OS "
+                        "or at least by this copy of rrdtool\n");
+                exit(1);
+#endif
+            } else {
+                firstdir = argv[2];
+            }
+        }
+        if (strcmp(firstdir, "")) {
+            if (chdir(firstdir) != 0){
+                fprintf(stderr, "ERROR: chdir %s %s\n", firstdir,rrd_strerror(errno));
+                exit(errno);
+            }
+        }
+
+        while (fgetslong(&aLine, stdin)) {
+            char *aLineOrig = aLine;
+            if ((argc = CountArgs(aLine)) == 0) {
+                free(aLine);
+                printf("ERROR: not enough arguments\n");
+                continue;                
+            }
+            if ((myargv = (char **) malloc((argc + 1) *
+                                           sizeof(char *))) == NULL) {
+                perror("malloc");
+                exit(1);
+            }
+            if ((argc = CreateArgs(argv[0], aLine, myargv)) < 0) {
+                printf("ERROR: creating arguments\n");
+            } else {
+                if ( HandleInputLine(argc, myargv, stdout) == 0 ){
+#if HAVE_GETRUSAGE
+                    getrusage(RUSAGE_SELF, &myusage);
+                    gettimeofday(&currenttime, NULL);
+                    printf("OK u:%1.2f s:%1.2f r:%1.2f\n",
+                           (double) myusage.ru_utime.tv_sec +
+                           (double) myusage.ru_utime.tv_usec / 1000000.0,
+                           (double) myusage.ru_stime.tv_sec +
+                           (double) myusage.ru_stime.tv_usec / 1000000.0,
+                           (double) (currenttime.tv_sec - starttime.tv_sec)
+                           + (double) (currenttime.tv_usec -
+                                       starttime.tv_usec)
+                           / 1000000.0);
+#else
+                    printf("OK\n");
+#endif
+                }
+            }
+            fflush(stdout); /* this is important for pipes to work */
+            free(myargv);
+            free(aLineOrig);
+        }
+    } else if (argc == 2) {
+        PrintUsage(argv[1]);
+        exit(0);
+    } else if (argc == 3 && !strcmp(argv[1], "help")) {
+        PrintUsage(argv[2]);
+        exit(0);
+    } else {
+        exit(HandleInputLine(argc, argv, stderr));
+    }
+    return 0;
+}
+
+/* HandleInputLine is NOT thread safe - due to readdir issues,
+   resolving them portably is not really simple. */
+int HandleInputLine(
+    int argc,
+    char **argv,
+    FILE * out)
+{
+#if defined(HAVE_OPENDIR) && defined (HAVE_READDIR)
+    DIR      *curdir;   /* to read current dir with ls */
+    struct dirent *dent;
+#endif
+#if defined(HAVE_SYS_STAT_H)
+    struct stat st;
+#endif
+
+    /* Reset errno to 0 before we start.
+     */
+    if (RemoteMode) {
+        if (argc > 1 && strcmp("quit", argv[1]) == 0) {
+            if (argc > 2) {
+                printf("ERROR: invalid parameter count for quit\n");
+                return (1);
+            }
+            exit(0);
+        }
+#if defined(HAVE_OPENDIR) && defined(HAVE_READDIR) && defined(HAVE_CHDIR)
+        if (argc > 1 && strcmp("cd", argv[1]) == 0) {
+            if (argc > 3) {
+                printf("ERROR: invalid parameter count for cd\n");
+                return (1);
+            }
+#if ! defined(HAVE_CHROOT) || ! defined(HAVE_GETUID)
+            if (getuid() == 0 && !ChangeRoot) {
+                printf
+                    ("ERROR: chdir security problem - rrdtool is running as "
+                     "root but not chroot!\n");
+                return (1);
+            }
+#endif
+            if (chdir(argv[2]) != 0){
+                printf("ERROR: chdir %s %s\n", argv[2], rrd_strerror(errno));
+                return (1);
+            }
+            return (0);
+        }
+        if (argc > 1 && strcmp("pwd", argv[1]) == 0) {
+            char     *cwd;      /* To hold current working dir on call to pwd */
+            if (argc > 2) {
+                printf("ERROR: invalid parameter count for pwd\n");
+                return (1);
+            }
+            cwd = getcwd(NULL, MAXPATH);
+            if (cwd == NULL) {
+                printf("ERROR: getcwd %s\n", rrd_strerror(errno));
+                return (1);
+            }
+            printf("%s\n", cwd);
+            free(cwd);
+            return (0);
+        }
+        if (argc > 1 && strcmp("mkdir", argv[1]) == 0) {
+            if (argc > 3) {
+                printf("ERROR: invalid parameter count for mkdir\n");
+                return (1);
+            }
+#if ! defined(HAVE_CHROOT) || ! defined(HAVE_GETUID)
+            if (getuid() == 0 && !ChangeRoot) {
+                printf
+                    ("ERROR: mkdir security problem - rrdtool is running as "
+                     "root but not chroot!\n");
+                return (1);
+            }
+#endif
+            if(mkdir(argv[2], 0777)!=0){
+                printf("ERROR: mkdir %s: %s\n", argv[2],rrd_strerror(errno));
+                return (1);
+            }
+            return (0);
+        }
+        if (argc > 1 && strcmp("ls", argv[1]) == 0) {
+            if (argc > 2) {
+                printf("ERROR: invalid parameter count for ls\n");
+                return (1);
+            }
+            if ((curdir = opendir(".")) != NULL) {
+                while ((dent = readdir(curdir)) != NULL) {
+                    if (!stat(dent->d_name, &st)) {
+                        if (S_ISDIR(st.st_mode)) {
+                            printf("d %s\n", dent->d_name);
+                        }
+                        if (strlen(dent->d_name) > 4 && S_ISREG(st.st_mode)) {
+                            if (!strcmp
+                                (dent->d_name + NAMLEN(dent) - 4, ".rrd")
+                                || !strcmp(dent->d_name + NAMLEN(dent) - 4,
+                                           ".RRD")) {
+                                printf("- %s\n", dent->d_name);
+                            }
+                        }
+                    }
+                }
+                closedir(curdir);
+            } else {
+                printf("ERROR: opendir .: %s\n", rrd_strerror(errno));
+                return (errno);
+            }
+            return (0);
+        }
+#endif                          /* opendir and readdir */
+
+    }
+    if (argc < 3
+        || strcmp("help", argv[1]) == 0
+        || strcmp("--help", argv[1]) == 0
+        || strcmp("-help", argv[1]) == 0
+        || strcmp("-?", argv[1]) == 0 || strcmp("-h", argv[1]) == 0) {
+        PrintUsage("");
+        return 0;
+    }
+
+    if (strcmp("create", argv[1]) == 0)
+        rrd_create(argc - 1, &argv[1]);
+    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) {
+        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]);
+        rrd_info_print(data);
+        rrd_info_free(data);
+    }
+
+    else if (strcmp("--version", argv[1]) == 0 ||
+             strcmp("version", argv[1]) == 0 ||
+             strcmp("v", argv[1]) == 0 ||
+             strcmp("-v", argv[1]) == 0 || strcmp("-version", argv[1]) == 0)
+        printf("RRDtool " PACKAGE_VERSION
+               "  Copyright by Tobi Oetiker, 1997-2008 (%f)\n",
+               rrd_version());
+    else if (strcmp("restore", argv[1]) == 0)
+        rrd_restore(argc - 1, &argv[1]);
+    else if (strcmp("resize", argv[1]) == 0)
+        rrd_resize(argc - 1, &argv[1]);
+    else if (strcmp("last", argv[1]) == 0)
+        printf("%ld\n", rrd_last(argc - 1, &argv[1]));
+    else if (strcmp("lastupdate", argv[1]) == 0) {
+        rrd_lastupdate(argc - 1, &argv[1]);
+    } else if (strcmp("first", argv[1]) == 0)
+        printf("%ld\n", rrd_first(argc - 1, &argv[1]));
+    else if (strcmp("update", argv[1]) == 0)
+        rrd_update(argc - 1, &argv[1]);
+    else if (strcmp("fetch", argv[1]) == 0) {
+        time_t    start, end, ti;
+        unsigned long step, ds_cnt, i, ii;
+        rrd_value_t *data, *datai;
+        char    **ds_namv;
+
+        if (rrd_fetch
+            (argc - 1, &argv[1], &start, &end, &step, &ds_cnt, &ds_namv,
+             &data) != -1) {
+            datai = data;
+            printf("           ");
+            for (i = 0; i < ds_cnt; i++)
+                printf("%20s", ds_namv[i]);
+            printf("\n\n");
+            for (ti = start + step; ti <= end; ti += step) {
+                printf("%10lu:", ti);
+                for (ii = 0; ii < ds_cnt; ii++)
+                    printf(" %0.10e", *(datai++));
+                printf("\n");
+            }
+            for (i = 0; i < ds_cnt; i++)
+                free(ds_namv[i]);
+            free(ds_namv);
+            free(data);
+        }
+    } else if (strcmp("xport", argv[1]) == 0) {
+        int       xxsize;
+        unsigned long int j = 0;
+        time_t    start, end, ti;
+        unsigned long step, col_cnt, row_cnt;
+        rrd_value_t *data, *ptr;
+        char    **legend_v;
+        int       enumds = 0;
+        int       i;
+        size_t    vtag_s = strlen(COL_DATA_TAG) + 10;
+        char     *vtag = malloc(vtag_s);
+
+        for (i = 2; i < argc; i++) {
+            if (strcmp("--enumds", argv[i]) == 0)
+                enumds = 1;
+        }
+
+        if (rrd_xport
+            (argc - 1, &argv[1], &xxsize, &start, &end, &step, &col_cnt,
+             &legend_v, &data) != -1) {
+            char *old_locale = setlocale(LC_NUMERIC, "C");
+            row_cnt = (end - start) / step;
+            ptr = data;
+            printf("<?xml version=\"1.0\" encoding=\"%s\"?>\n\n",
+                   XML_ENCODING);
+            printf("<%s>\n", ROOT_TAG);
+            printf("  <%s>\n", META_TAG);
+            printf("    <%s>%lu</%s>\n", META_START_TAG,
+                   (unsigned long) start + step, META_START_TAG);
+            printf("    <%s>%lu</%s>\n", META_STEP_TAG, step, META_STEP_TAG);
+            printf("    <%s>%lu</%s>\n", META_END_TAG, (unsigned long) end,
+                   META_END_TAG);
+            printf("    <%s>%lu</%s>\n", META_ROWS_TAG, row_cnt,
+                   META_ROWS_TAG);
+            printf("    <%s>%lu</%s>\n", META_COLS_TAG, col_cnt,
+                   META_COLS_TAG);
+            printf("    <%s>\n", LEGEND_TAG);
+            for (j = 0; j < col_cnt; j++) {
+                char     *entry = NULL;
+
+                entry = legend_v[j];
+                printf("      <%s>%s</%s>\n", LEGEND_ENTRY_TAG, entry,
+                       LEGEND_ENTRY_TAG);
+                free(entry);
+            }
+            free(legend_v);
+            printf("    </%s>\n", LEGEND_TAG);
+            printf("  </%s>\n", META_TAG);
+            printf("  <%s>\n", DATA_TAG);
+            for (ti = start + step; ti <= end; ti += step) {
+                printf("    <%s>", DATA_ROW_TAG);
+                printf("<%s>%lu</%s>", COL_TIME_TAG, ti, COL_TIME_TAG);
+                for (j = 0; j < col_cnt; j++) {
+                    rrd_value_t newval = DNAN;
+
+                    if (enumds == 1)
+
+                        snprintf(vtag, vtag_s, "%s%lu", COL_DATA_TAG, j);
+                    else
+                        snprintf(vtag, vtag_s, "%s", COL_DATA_TAG);
+                    newval = *ptr;
+                    if (isnan(newval)) {
+                        printf("<%s>NaN</%s>", vtag, vtag);
+                    } else {
+                        printf("<%s>%0.10e</%s>", vtag, newval, vtag);
+                    };
+                    ptr++;
+                }
+                printf("</%s>\n", DATA_ROW_TAG);
+            }
+            free(data);
+            printf("  </%s>\n", DATA_TAG);
+            printf("</%s>\n", ROOT_TAG);
+            setlocale(LC_NUMERIC, old_locale);
+        }
+        free(vtag);
+    } else if (strcmp("graph", argv[1]) == 0) {
+        char    **calcpr;
+
+#ifdef notused /*XXX*/
+        const char *imgfile = argv[2];  /* rrd_graph changes argv pointer */
+#endif
+        int       xsize, ysize;
+        double    ymin, ymax;
+        int       i;
+        int       tostdout = (strcmp(argv[2], "-") == 0);
+        int       imginfo = 0;
+
+        for (i = 2; i < argc; i++) {
+            if (strcmp(argv[i], "--imginfo") == 0
+                || strcmp(argv[i], "-f") == 0) {
+                imginfo = 1;
+                break;
+            }
+        }
+        if (rrd_graph
+            (argc - 1, &argv[1], &calcpr, &xsize, &ysize, NULL, &ymin,
+             &ymax) != -1) {
+            if (!tostdout && !imginfo)
+                printf("%dx%d\n", xsize, ysize);
+            if (calcpr) {
+                for (i = 0; calcpr[i]; i++) {
+                    if (!tostdout)
+                        printf("%s\n", calcpr[i]);
+                    free(calcpr[i]);
+                }
+                free(calcpr);
+            }
+        }
+
+    } else if (strcmp("graphv", argv[1]) == 0) {
+        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) {
+            rrd_info_print(grinfo);
+            rrd_info_free(grinfo);
+        }
+
+    } else if (strcmp("tune", argv[1]) == 0)
+        rrd_tune(argc - 1, &argv[1]);
+    else if (strcmp("flushcached", argv[1]) == 0)
+        rrd_flushcached(argc - 1, &argv[1]);
+    else {
+        rrd_set_error("unknown function '%s'", argv[1]);
+    }
+    if (rrd_test_error()) {
+        fprintf(out, "ERROR: %s\n", rrd_get_error());
+        rrd_clear_error();
+        return 1;
+    }
+    return (0);
+}
+
+int CountArgs(
+    char *aLine)
+{
+    int       i = 0;
+    int       aCount = 0;
+    int       inarg = 0;
+
+    while (aLine[i] == ' ')
+        i++;
+    while (aLine[i] != 0) {
+        if ((aLine[i] == ' ') && inarg) {
+            inarg = 0;
+        }
+        if ((aLine[i] != ' ') && !inarg) {
+            inarg = 1;
+            aCount++;
+        }
+        i++;
+    }
+    return aCount;
+}
+
+/*
+ * CreateArgs - take a string (aLine) and tokenize
+ */
+int CreateArgs(
+    char *pName,
+    char *aLine,
+    char **argv)
+{
+    char     *getP, *putP;
+    char    **pargv = argv;
+    char      Quote = 0;
+    int       inArg = 0;
+    int       len;
+    int       argc = 1;
+
+    len = strlen(aLine);
+    /* remove trailing space and newlines */
+    while (len && aLine[len] <= ' ') {
+        aLine[len] = 0;
+        len--;
+    }
+    /* sikp leading blanks */
+    while (*aLine && *aLine <= ' ')
+        aLine++;
+    pargv[0] = pName;
+    argc = 1;
+    getP = aLine;
+    putP = aLine;
+    while (*getP) {
+        switch (*getP) {
+        case ' ':
+            if (Quote) {
+                *(putP++) = *getP;
+            } else if (inArg) {
+                *(putP++) = 0;
+                inArg = 0;
+            }
+            break;
+        case '"':
+        case '\'':
+            if (Quote != 0) {
+                if (Quote == *getP)
+                    Quote = 0;
+                else {
+                    *(putP++) = *getP;
+                }
+            } else {
+                if (!inArg) {
+                    pargv[argc++] = putP;
+                    inArg = 1;
+                }
+                Quote = *getP;
+            }
+            break;
+        default:
+            if (!inArg) {
+                pargv[argc++] = putP;
+                inArg = 1;
+            }
+            *(putP++) = *getP;
+            break;
+        }
+        getP++;
+    }
+
+    *putP = '\0';
+    if (Quote)
+        return -1;
+    else
+        return argc;
+}
diff --git a/program/src/rrd_tool.h b/program/src/rrd_tool.h
new file mode 100644 (file)
index 0000000..b58e04c
--- /dev/null
@@ -0,0 +1,129 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_tool.h   Common Header File
+ *****************************************************************************/
+#ifdef  __cplusplus
+extern    "C" {
+#endif
+
+#ifndef _RRD_TOOL_H
+#define _RRD_TOOL_H
+
+#ifdef HAVE_CONFIG_H
+#include "../rrd_config.h"
+#elif defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#include "../win32/config.h"
+#endif
+
+#include "rrd.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+
+/* Win32 only includes */
+
+#include <float.h>      /* for _isnan  */
+#include <io.h>         /* for chdir   */
+
+    struct tm *localtime_r(
+    const time_t *timep,
+    struct tm *result);
+    char     *ctime_r(
+    const time_t *timep,
+    char *result);
+    struct tm *gmtime_r(
+    const time_t *timep,
+    struct tm *result);
+    char     *strtok_r(
+    char *str,
+    const char *sep,
+    char **last);
+
+#else
+
+/* unix-only includes */
+#if !defined isnan && !defined HAVE_ISNAN
+    int       isnan(
+    double value);
+#endif
+
+#endif
+
+/* local include files -- need to be after the system ones */
+#ifdef HAVE_GETOPT_LONG
+#define _GNU_SOURCE
+#include <getopt.h>
+#else
+#include "rrd_getopt.h"
+#endif
+
+#include "rrd_format.h"
+
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define DIM(x) (sizeof(x)/sizeof(x[0]))
+
+    char     *sprintf_alloc(
+    char *,
+    ...);
+
+/* HELPER FUNCTIONS */
+
+    int       PngSize(
+    FILE *,
+    long *,
+    long *);
+
+    int       rrd_create_fn(
+    const char *file_name,
+    rrd_t *rrd);
+    int rrd_fetch_fn (const char *filename,
+            enum cf_en cf_idx,
+            time_t *start,
+            time_t *end,
+            unsigned long *step,
+            unsigned long *ds_cnt,
+            char ***ds_namv,
+            rrd_value_t **data);
+
+
+#ifdef HAVE_LIBDBI
+int rrd_fetch_fn_libdbi(const char *filename, enum cf_en cf_idx,
+                       time_t *start,time_t *end,
+                       unsigned long *step,
+                       unsigned long *ds_cnt,
+                       char        ***ds_namv,
+                       rrd_value_t **data);
+#endif
+
+#define RRD_READONLY    (1<<0)
+#define RRD_READWRITE   (1<<1)
+#define RRD_CREAT       (1<<2)
+#define RRD_READAHEAD   (1<<3)
+#define RRD_COPY        (1<<4)
+#define RRD_EXCL        (1<<5)
+
+    enum cf_en cf_conv(
+    const char *string);
+    enum dst_en dst_conv(
+    char *string);
+    long      ds_match(
+    rrd_t *rrd,
+    char *ds_nam);
+    off_t rrd_get_header_size(
+    rrd_t *rrd);
+    double    rrd_diff(
+    char *a,
+    char *b);
+
+#endif /* _RRD_TOOL_H */
+
+#ifdef  __cplusplus
+}
+#endif
diff --git a/program/src/rrd_tune.c b/program/src/rrd_tune.c
new file mode 100644 (file)
index 0000000..80c2de0
--- /dev/null
@@ -0,0 +1,510 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * change header parameters of an rrd
+ *****************************************************************************
+ * $Id$
+ * $Log$
+ * Revision 1.6  2004/05/26 22:11:12  oetiker
+ * reduce compiler warnings. Many small fixes. -- Mike Slifcak <slif@bellsouth.net>
+ *
+ * Revision 1.5  2002/02/01 20:34:49  oetiker
+ * fixed version number and date/time
+ *
+ * Revision 1.4  2001/08/22 22:29:07  jake
+ * Contents of this patch:
+ * (1) Adds/revises documentation for rrd tune in rrd_tool.c and pod files.
+ * (2) Moves some initialization code from rrd_create.c to rrd_hw.c.
+ * (3) Adds another pass to smoothing for SEASONAL and DEVSEASONAL RRAs.
+ * This pass computes the coefficients as deviations from an average; the
+ * average is added the baseline coefficient of HWPREDICT. Statistical texts
+ * suggest this to preserve algorithm stability. It will not invalidate
+ * RRD files created and smoothed with the old code.
+ * (4) Adds the aberrant-reset flag to rrd tune. This operation, which is
+ * specified for a single data source, causes the holt-winters algorithm to
+ * forget everthing it has learned and start over.
+ * (5) Fixes a few out-of-date code comments.
+ *
+ * Revision 1.3  2001/03/07 21:21:54  oetiker
+ * complete rewrite of rrdgraph documentation. This also includs info
+ * on upcomming/planned changes to the rrdgraph interface and functionality
+ * -- Alex van den Bogaerdt <alex@slot.hollandcasino.nl>
+ *
+ * Revision 1.2  2001/03/04 13:01:55  oetiker
+ * Aberrant Behavior Detection support. A brief overview added to rrdtool.pod.
+ * Major updates to rrd_update.c, rrd_create.c. Minor update to other core files.
+ * This is backwards compatible! But new files using the Aberrant stuff are not readable
+ * by old rrdtool versions. See http://cricket.sourceforge.net/aberrant/rrd_hw.htm
+ * -- Jake Brutlag <jakeb@corp.webtv.net>
+ *
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <locale.h>
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include "rrd_hw.h"
+
+int       set_hwarg(
+    rrd_t *rrd,
+    enum cf_en cf,
+    enum rra_par_en rra_par,
+    char *arg);
+int       set_deltaarg(
+    rrd_t *rrd,
+    enum rra_par_en rra_par,
+    char *arg);
+int       set_windowarg(
+    rrd_t *rrd,
+    enum rra_par_en,
+    char *arg);
+
+int set_hwsmootharg(
+    rrd_t *rrd,
+    enum cf_en cf,
+    enum rra_par_en rra_par,
+    char *arg);
+
+int rrd_tune(
+    int argc,
+    char **argv)
+{
+    rrd_t     rrd;
+    int       matches;
+    int       optcnt = 0;
+    long      ds;
+    char      ds_nam[DS_NAM_SIZE];
+    char      ds_new[DS_NAM_SIZE];
+    long      heartbeat;
+    double    min;
+    double    max;
+    char      dst[DST_SIZE];
+    rrd_file_t *rrd_file;
+    struct option long_options[] = {
+        {"heartbeat", required_argument, 0, 'h'},
+        {"minimum", required_argument, 0, 'i'},
+        {"maximum", required_argument, 0, 'a'},
+        {"data-source-type", required_argument, 0, 'd'},
+        {"data-source-rename", required_argument, 0, 'r'},
+        /* added parameter tuning options for aberrant behavior detection */
+        {"deltapos", required_argument, 0, 'p'},
+        {"deltaneg", required_argument, 0, 'n'},
+        {"window-length", required_argument, 0, 'w'},
+        {"failure-threshold", required_argument, 0, 'f'},
+        {"alpha", required_argument, 0, 'x'},
+        {"beta", required_argument, 0, 'y'},
+        {"gamma", required_argument, 0, 'z'},
+        {"gamma-deviation", required_argument, 0, 'v'},
+        {"smoothing-window", required_argument, 0, 's'},
+        {"smoothing-window-deviation", required_argument, 0, 'S'},
+        {"aberrant-reset", required_argument, 0, 'b'},
+        {0, 0, 0, 0}
+    };
+
+    optind = 0;
+    opterr = 0;         /* initialize getopt */
+
+
+    rrd_init(&rrd);
+    rrd_file = rrd_open(argv[1], &rrd, RRD_READWRITE);
+    if (rrd_file == NULL) {
+        rrd_free(&rrd);
+        return -1;
+    }
+
+    while (1) {
+        int       option_index = 0;
+        int       opt;
+        char     *old_locale = "";
+
+        opt = getopt_long(argc, argv, "h:i:a:d:r:p:n:w:f:x:y:z:v:b:",
+                          long_options, &option_index);
+        if (opt == EOF)
+            break;
+
+        optcnt++;
+        switch (opt) {
+        case 'h':
+            old_locale = setlocale(LC_NUMERIC, "C");
+            if ((matches =
+                 sscanf(optarg, DS_NAM_FMT ":%ld", ds_nam,
+                        &heartbeat)) != 2) {
+                rrd_set_error("invalid arguments for heartbeat");
+                rrd_free(&rrd);
+                rrd_close(rrd_file);
+                setlocale(LC_NUMERIC, old_locale);
+                return -1;
+            }
+            setlocale(LC_NUMERIC, old_locale);
+            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
+                rrd_free(&rrd);
+                rrd_close(rrd_file);
+                return -1;
+            }
+            rrd.ds_def[ds].par[DS_mrhb_cnt].u_cnt = heartbeat;
+            break;
+
+        case 'i':
+            old_locale = setlocale(LC_NUMERIC, "C");
+            if ((matches =
+                 sscanf(optarg, DS_NAM_FMT ":%lf", ds_nam, &min)) < 1) {
+                rrd_set_error("invalid arguments for minimum ds value");
+                rrd_free(&rrd);
+                rrd_close(rrd_file);
+                setlocale(LC_NUMERIC, old_locale);
+                return -1;
+            }
+            setlocale(LC_NUMERIC, old_locale);
+            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
+                rrd_free(&rrd);
+                rrd_close(rrd_file);
+                return -1;
+            }
+
+            if (matches == 1)
+                min = DNAN;
+            rrd.ds_def[ds].par[DS_min_val].u_val = min;
+            break;
+
+        case 'a':
+            old_locale = setlocale(LC_NUMERIC, "C");
+            if ((matches =
+                 sscanf(optarg, DS_NAM_FMT ":%lf", ds_nam, &max)) < 1) {
+                rrd_set_error("invalid arguments for maximum ds value");
+                rrd_free(&rrd);
+                rrd_close(rrd_file);
+                setlocale(LC_NUMERIC, old_locale);
+                return -1;
+            }
+            setlocale(LC_NUMERIC, old_locale);
+            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
+                rrd_free(&rrd);
+                rrd_close(rrd_file);
+                return -1;
+            }
+            if (matches == 1)
+                max = DNAN;
+            rrd.ds_def[ds].par[DS_max_val].u_val = max;
+            break;
+
+        case 'd':
+            if ((matches =
+                 sscanf(optarg, DS_NAM_FMT ":" DST_FMT, ds_nam, dst)) != 2) {
+                rrd_set_error("invalid arguments for data source type");
+                rrd_free(&rrd);
+                rrd_close(rrd_file);
+                return -1;
+            }
+            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
+                rrd_free(&rrd);
+                rrd_close(rrd_file);
+                return -1;
+            }
+            if ((int) dst_conv(dst) == -1) {
+                rrd_free(&rrd);
+                rrd_close(rrd_file);
+                return -1;
+            }
+            /* only reset when something is changed */
+            if (strncmp(rrd.ds_def[ds].dst, dst, DST_SIZE - 1) != 0) {
+                strncpy(rrd.ds_def[ds].dst, dst, DST_SIZE - 1);
+                rrd.ds_def[ds].dst[DST_SIZE - 1] = '\0';
+
+                rrd.pdp_prep[ds].last_ds[0] = 'U';
+                rrd.pdp_prep[ds].last_ds[1] = 'N';
+                rrd.pdp_prep[ds].last_ds[2] = 'K';
+                rrd.pdp_prep[ds].last_ds[3] = 'N';
+                rrd.pdp_prep[ds].last_ds[4] = '\0';
+            }
+            break;
+        case 'r':
+            if ((matches =
+                 sscanf(optarg, DS_NAM_FMT ":" DS_NAM_FMT, ds_nam,
+                        ds_new)) != 2) {
+                rrd_set_error("invalid arguments for data source type");
+                rrd_free(&rrd);
+                rrd_close(rrd_file);
+                return -1;
+            }
+            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
+                rrd_free(&rrd);
+                rrd_close(rrd_file);
+                return -1;
+            }
+            strncpy(rrd.ds_def[ds].ds_nam, ds_new, DS_NAM_SIZE - 1);
+            rrd.ds_def[ds].ds_nam[DS_NAM_SIZE - 1] = '\0';
+            break;
+        case 'p':
+            if (set_deltaarg(&rrd, RRA_delta_pos, optarg)) {
+                rrd_free(&rrd);
+                return -1;
+            }
+            break;
+        case 'n':
+            if (set_deltaarg(&rrd, RRA_delta_neg, optarg)) {
+                rrd_free(&rrd);
+                return -1;
+            }
+            break;
+        case 'f':
+            if (set_windowarg(&rrd, RRA_failure_threshold, optarg)) {
+                rrd_free(&rrd);
+                return -1;
+            }
+            break;
+        case 'w':
+            if (set_windowarg(&rrd, RRA_window_len, optarg)) {
+                rrd_free(&rrd);
+                return -1;
+            }
+            break;
+        case 'x':
+            if (set_hwarg(&rrd, CF_HWPREDICT, RRA_hw_alpha, optarg)) {
+                if (set_hwarg(&rrd, CF_MHWPREDICT, RRA_hw_alpha, optarg)) {
+                    rrd_free(&rrd);
+                    return -1;
+                }
+                rrd_clear_error();
+            }
+            break;
+        case 'y':
+            if (set_hwarg(&rrd, CF_HWPREDICT, RRA_hw_beta, optarg)) {
+                if (set_hwarg(&rrd, CF_MHWPREDICT, RRA_hw_beta, optarg)) {
+                    rrd_free(&rrd);
+                    return -1;
+                }
+                rrd_clear_error();
+            }
+            break;
+        case 'z':
+            if (set_hwarg(&rrd, CF_SEASONAL, RRA_seasonal_gamma, optarg)) {
+                rrd_free(&rrd);
+                return -1;
+            }
+            break;
+        case 'v':
+            if (set_hwarg(&rrd, CF_DEVSEASONAL, RRA_seasonal_gamma, optarg)) {
+                rrd_free(&rrd);
+                return -1;
+            }
+            break;
+        case 'b':
+            if (sscanf(optarg, DS_NAM_FMT, ds_nam) != 1) {
+                rrd_set_error("invalid argument for aberrant-reset");
+                rrd_free(&rrd);
+                rrd_close(rrd_file);
+                return -1;
+            }
+            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
+                /* ds_match handles it own errors */
+                rrd_free(&rrd);
+                rrd_close(rrd_file);
+                return -1;
+            }
+            reset_aberrant_coefficients(&rrd, rrd_file, (unsigned long) ds);
+            if (rrd_test_error()) {
+                rrd_free(&rrd);
+                rrd_close(rrd_file);
+                return -1;
+            }
+            break;
+        case 's':
+            strcpy(rrd.stat_head->version, RRD_VERSION);    /* smoothing_window causes Version 4 */
+            if (set_hwsmootharg
+                (&rrd, CF_SEASONAL, RRA_seasonal_smoothing_window, optarg)) {
+                rrd_free(&rrd);
+                return -1;
+            }
+            break;
+        case 'S':
+            strcpy(rrd.stat_head->version, RRD_VERSION);    /* smoothing_window causes Version 4 */
+            if (set_hwsmootharg
+                (&rrd, CF_DEVSEASONAL, RRA_seasonal_smoothing_window,
+                 optarg)) {
+                rrd_free(&rrd);
+                return -1;
+            }
+            break;
+        case '?':
+            if (optopt != 0)
+                rrd_set_error("unknown option '%c'", optopt);
+            else
+                rrd_set_error("unknown option '%s'", argv[optind - 1]);
+            rrd_free(&rrd);
+            rrd_close(rrd_file);
+            return -1;
+        }
+    }
+    if (optcnt > 0) {
+        rrd_seek(rrd_file, 0, SEEK_SET);
+        rrd_write(rrd_file, rrd.stat_head, sizeof(stat_head_t) * 1);
+        rrd_write(rrd_file, rrd.ds_def,
+                  sizeof(ds_def_t) * rrd.stat_head->ds_cnt);
+        /* need to write rra_defs for RRA parameter changes */
+        rrd_write(rrd_file, rrd.rra_def,
+                  sizeof(rra_def_t) * rrd.stat_head->rra_cnt);
+    } else {
+        int       i;
+
+        for (i = 0; i < (int) rrd.stat_head->ds_cnt; i++)
+            if (dst_conv(rrd.ds_def[i].dst) != DST_CDEF) {
+                printf("DS[%s] typ: %s\thbt: %ld\tmin: %1.4f\tmax: %1.4f\n",
+                       rrd.ds_def[i].ds_nam,
+                       rrd.ds_def[i].dst,
+                       rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt,
+                       rrd.ds_def[i].par[DS_min_val].u_val,
+                       rrd.ds_def[i].par[DS_max_val].u_val);
+            } else {
+                char     *buffer = NULL;
+
+                rpn_compact2str((rpn_cdefds_t *) &
+                                (rrd.ds_def[i].par[DS_cdef]), rrd.ds_def,
+                                &buffer);
+                printf("DS[%s] typ: %s\tcdef: %s\n", rrd.ds_def[i].ds_nam,
+                       rrd.ds_def[i].dst, buffer);
+                free(buffer);
+            }
+    }
+    rrd_close(rrd_file);
+    rrd_free(&rrd);
+    return 0;
+}
+
+int set_hwarg(
+    rrd_t *rrd,
+    enum cf_en cf,
+    enum rra_par_en rra_par,
+    char *arg)
+{
+    double    param;
+    unsigned long i;
+    signed short rra_idx = -1;
+
+    /* read the value */
+    param = atof(arg);
+    if (param <= 0.0 || param >= 1.0) {
+        rrd_set_error("Holt-Winters parameter must be between 0 and 1");
+        return -1;
+    }
+    /* does the appropriate RRA exist?  */
+    for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
+        if (cf_conv(rrd->rra_def[i].cf_nam) == cf) {
+            rra_idx = i;
+            break;
+        }
+    }
+    if (rra_idx == -1) {
+        rrd_set_error("Holt-Winters RRA does not exist in this RRD");
+        return -1;
+    }
+
+    /* set the value */
+    rrd->rra_def[rra_idx].par[rra_par].u_val = param;
+    return 0;
+}
+
+int set_hwsmootharg(
+    rrd_t *rrd,
+    enum cf_en cf,
+    enum rra_par_en rra_par,
+    char *arg)
+{
+    double    param;
+    unsigned long i;
+    signed short rra_idx = -1;
+
+    /* read the value */
+    param = atof(arg);
+    /* in order to avoid smoothing of SEASONAL or DEVSEASONAL, we need to 
+     * the 0.0 value*/
+    if (param < 0.0 || param > 1.0) {
+        rrd_set_error("Holt-Winters parameter must be between 0 and 1");
+        return -1;
+    }
+    /* does the appropriate RRA exist?  */
+    for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
+        if (cf_conv(rrd->rra_def[i].cf_nam) == cf) {
+            rra_idx = i;
+            break;
+        }
+    }
+    if (rra_idx == -1) {
+        rrd_set_error("Holt-Winters RRA does not exist in this RRD");
+        return -1;
+    }
+
+    /* set the value */
+    rrd->rra_def[rra_idx].par[rra_par].u_val = param;
+    return 0;
+}
+
+int set_deltaarg(
+    rrd_t *rrd,
+    enum rra_par_en rra_par,
+    char *arg)
+{
+    rrd_value_t param;
+    unsigned long i;
+    signed short rra_idx = -1;
+
+    param = atof(arg);
+    if (param < 0.1) {
+        rrd_set_error("Parameter specified is too small");
+        return -1;
+    }
+    /* does the appropriate RRA exist?  */
+    for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
+        if (cf_conv(rrd->rra_def[i].cf_nam) == CF_FAILURES) {
+            rra_idx = i;
+            break;
+        }
+    }
+    if (rra_idx == -1) {
+        rrd_set_error("Failures RRA does not exist in this RRD");
+        return -1;
+    }
+
+    /* set the value */
+    rrd->rra_def[rra_idx].par[rra_par].u_val = param;
+    return 0;
+}
+
+int set_windowarg(
+    rrd_t *rrd,
+    enum rra_par_en rra_par,
+    char *arg)
+{
+    unsigned long param;
+    unsigned long i, cdp_idx;
+    signed short rra_idx = -1;
+
+    /* read the value */
+    param = atoi(arg);
+    if (param < 1 || param > MAX_FAILURES_WINDOW_LEN) {
+        rrd_set_error("Parameter must be between %d and %d",
+                      1, MAX_FAILURES_WINDOW_LEN);
+        return -1;
+    }
+    /* does the appropriate RRA exist?  */
+    for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
+        if (cf_conv(rrd->rra_def[i].cf_nam) == CF_FAILURES) {
+            rra_idx = i;
+            break;
+        }
+    }
+    if (rra_idx == -1) {
+        rrd_set_error("Failures RRA does not exist in this RRD");
+        return -1;
+    }
+
+    /* set the value */
+    rrd->rra_def[rra_idx].par[rra_par].u_cnt = param;
+
+    /* erase existing violations */
+    for (i = 0; i < rrd->stat_head->ds_cnt; i++) {
+        cdp_idx = rra_idx * (rrd->stat_head->ds_cnt) + i;
+        erase_violations(rrd, cdp_idx, rra_idx);
+    }
+    return 0;
+}
diff --git a/program/src/rrd_update.c b/program/src/rrd_update.c
new file mode 100644 (file)
index 0000000..4016d75
--- /dev/null
@@ -0,0 +1,2102 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *                Copyright by Florian Forster, 2008
+ *****************************************************************************
+ * rrd_update.c  RRD Update Function
+ *****************************************************************************
+ * $Id$
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#include <sys/locking.h>
+#include <sys/stat.h>
+#include <io.h>
+#endif
+
+#include <locale.h>
+
+#include "rrd_hw.h"
+#include "rrd_rpncalc.h"
+
+#include "rrd_is_thread_safe.h"
+#include "unused.h"
+
+#include "rrd_client.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+/*
+ * WIN32 does not have gettimeofday    and struct timeval. This is a quick and dirty
+ * replacement.
+ */
+#include <sys/timeb.h>
+
+#ifndef __MINGW32__
+struct timeval {
+    time_t    tv_sec;   /* seconds */
+    long      tv_usec;  /* microseconds */
+};
+#endif
+
+struct __timezone {
+    int       tz_minuteswest;   /* minutes W of Greenwich */
+    int       tz_dsttime;   /* type of dst correction */
+};
+
+static int gettimeofday(
+    struct timeval *t,
+    struct __timezone *tz)
+{
+
+    struct _timeb current_time;
+
+    _ftime(&current_time);
+
+    t->tv_sec = current_time.time;
+    t->tv_usec = current_time.millitm * 1000;
+
+    return 0;
+}
+
+#endif
+
+/* FUNCTION PROTOTYPES */
+
+int       rrd_update_r(
+    const char *filename,
+    const char *tmplt,
+    int argc,
+    const char **argv);
+int       _rrd_update(
+    const char *filename,
+    const char *tmplt,
+    int argc,
+    const char **argv,
+    rrd_info_t *);
+
+static int allocate_data_structures(
+    rrd_t *rrd,
+    char ***updvals,
+    rrd_value_t **pdp_temp,
+    const char *tmplt,
+    long **tmpl_idx,
+    unsigned long *tmpl_cnt,
+    unsigned long **rra_step_cnt,
+    unsigned long **skip_update,
+    rrd_value_t **pdp_new);
+
+static int parse_template(
+    rrd_t *rrd,
+    const char *tmplt,
+    unsigned long *tmpl_cnt,
+    long *tmpl_idx);
+
+static int process_arg(
+    char *step_start,
+    rrd_t *rrd,
+    rrd_file_t *rrd_file,
+    unsigned long rra_begin,
+    time_t *current_time,
+    unsigned long *current_time_usec,
+    rrd_value_t *pdp_temp,
+    rrd_value_t *pdp_new,
+    unsigned long *rra_step_cnt,
+    char **updvals,
+    long *tmpl_idx,
+    unsigned long tmpl_cnt,
+    rrd_info_t ** pcdp_summary,
+    int version,
+    unsigned long *skip_update,
+    int *schedule_smooth);
+
+static int parse_ds(
+    rrd_t *rrd,
+    char **updvals,
+    long *tmpl_idx,
+    char *input,
+    unsigned long tmpl_cnt,
+    time_t *current_time,
+    unsigned long *current_time_usec,
+    int version);
+
+static int get_time_from_reading(
+    rrd_t *rrd,
+    char timesyntax,
+    char **updvals,
+    time_t *current_time,
+    unsigned long *current_time_usec,
+    int version);
+
+static int update_pdp_prep(
+    rrd_t *rrd,
+    char **updvals,
+    rrd_value_t *pdp_new,
+    double interval);
+
+static int calculate_elapsed_steps(
+    rrd_t *rrd,
+    unsigned long current_time,
+    unsigned long current_time_usec,
+    double interval,
+    double *pre_int,
+    double *post_int,
+    unsigned long *proc_pdp_cnt);
+
+static void simple_update(
+    rrd_t *rrd,
+    double interval,
+    rrd_value_t *pdp_new);
+
+static int process_all_pdp_st(
+    rrd_t *rrd,
+    double interval,
+    double pre_int,
+    double post_int,
+    unsigned long elapsed_pdp_st,
+    rrd_value_t *pdp_new,
+    rrd_value_t *pdp_temp);
+
+static int process_pdp_st(
+    rrd_t *rrd,
+    unsigned long ds_idx,
+    double interval,
+    double pre_int,
+    double post_int,
+    long diff_pdp_st,
+    rrd_value_t *pdp_new,
+    rrd_value_t *pdp_temp);
+
+static int update_all_cdp_prep(
+    rrd_t *rrd,
+    unsigned long *rra_step_cnt,
+    unsigned long rra_begin,
+    rrd_file_t *rrd_file,
+    unsigned long elapsed_pdp_st,
+    unsigned long proc_pdp_cnt,
+    rrd_value_t **last_seasonal_coef,
+    rrd_value_t **seasonal_coef,
+    rrd_value_t *pdp_temp,
+    unsigned long *skip_update,
+    int *schedule_smooth);
+
+static int do_schedule_smooth(
+    rrd_t *rrd,
+    unsigned long rra_idx,
+    unsigned long elapsed_pdp_st);
+
+static int update_cdp_prep(
+    rrd_t *rrd,
+    unsigned long elapsed_pdp_st,
+    unsigned long start_pdp_offset,
+    unsigned long *rra_step_cnt,
+    int rra_idx,
+    rrd_value_t *pdp_temp,
+    rrd_value_t *last_seasonal_coef,
+    rrd_value_t *seasonal_coef,
+    int current_cf);
+
+static void update_cdp(
+    unival *scratch,
+    int current_cf,
+    rrd_value_t pdp_temp_val,
+    unsigned long rra_step_cnt,
+    unsigned long elapsed_pdp_st,
+    unsigned long start_pdp_offset,
+    unsigned long pdp_cnt,
+    rrd_value_t xff,
+    int i,
+    int ii);
+
+static void initialize_cdp_val(
+    unival *scratch,
+    int current_cf,
+    rrd_value_t pdp_temp_val,
+    unsigned long start_pdp_offset,
+    unsigned long pdp_cnt);
+
+static void reset_cdp(
+    rrd_t *rrd,
+    unsigned long elapsed_pdp_st,
+    rrd_value_t *pdp_temp,
+    rrd_value_t *last_seasonal_coef,
+    rrd_value_t *seasonal_coef,
+    int rra_idx,
+    int ds_idx,
+    int cdp_idx,
+    enum cf_en current_cf);
+
+static rrd_value_t initialize_carry_over(
+    rrd_value_t pdp_temp_val,
+    int         current_cf,
+    unsigned long elapsed_pdp_st,
+    unsigned long start_pdp_offset,
+    unsigned long pdp_cnt);
+
+static rrd_value_t calculate_cdp_val(
+    rrd_value_t cdp_val,
+    rrd_value_t pdp_temp_val,
+    unsigned long elapsed_pdp_st,
+    int current_cf,
+    int i,
+    int ii);
+
+static int update_aberrant_cdps(
+    rrd_t *rrd,
+    rrd_file_t *rrd_file,
+    unsigned long rra_begin,
+    unsigned long elapsed_pdp_st,
+    rrd_value_t *pdp_temp,
+    rrd_value_t **seasonal_coef);
+
+static int write_to_rras(
+    rrd_t *rrd,
+    rrd_file_t *rrd_file,
+    unsigned long *rra_step_cnt,
+    unsigned long rra_begin,
+    time_t current_time,
+    unsigned long *skip_update,
+    rrd_info_t ** pcdp_summary);
+
+static int write_RRA_row(
+    rrd_file_t *rrd_file,
+    rrd_t *rrd,
+    unsigned long rra_idx,
+    unsigned short CDP_scratch_idx,
+    rrd_info_t ** pcdp_summary,
+    time_t rra_time);
+
+static int smooth_all_rras(
+    rrd_t *rrd,
+    rrd_file_t *rrd_file,
+    unsigned long rra_begin);
+
+#ifndef HAVE_MMAP
+static int write_changes_to_disk(
+    rrd_t *rrd,
+    rrd_file_t *rrd_file,
+    int version);
+#endif
+
+/*
+ * normalize time as returned by gettimeofday. usec part must
+ * be always >= 0
+ */
+static void normalize_time(
+    struct timeval *t)
+{
+    if (t->tv_usec < 0) {
+        t->tv_sec--;
+        t->tv_usec += 1e6L;
+    }
+}
+
+/*
+ * Sets current_time and current_time_usec based on the current time.
+ * current_time_usec is set to 0 if the version number is 1 or 2.
+ */
+static void initialize_time(
+    time_t *current_time,
+    unsigned long *current_time_usec,
+    int version)
+{
+    struct timeval tmp_time;    /* used for time conversion */
+
+    gettimeofday(&tmp_time, 0);
+    normalize_time(&tmp_time);
+    *current_time = tmp_time.tv_sec;
+    if (version >= 3) {
+        *current_time_usec = tmp_time.tv_usec;
+    } else {
+        *current_time_usec = 0;
+    }
+}
+
+#define IFDNAN(X,Y) (isnan(X) ? (Y) : (X));
+
+rrd_info_t *rrd_update_v(
+    int argc,
+    char **argv)
+{
+    char     *tmplt = NULL;
+    rrd_info_t *result = NULL;
+    rrd_infoval_t rc;
+    char *opt_daemon = NULL;
+    struct option long_options[] = {
+        {"template", required_argument, 0, 't'},
+        {0, 0, 0, 0}
+    };
+
+    rc.u_int = -1;
+    optind = 0;
+    opterr = 0;         /* initialize getopt */
+
+    while (1) {
+        int       option_index = 0;
+        int       opt;
+
+        opt = getopt_long(argc, argv, "t:", long_options, &option_index);
+
+        if (opt == EOF)
+            break;
+
+        switch (opt) {
+        case 't':
+            tmplt = optarg;
+            break;
+
+        case '?':
+            rrd_set_error("unknown option '%s'", argv[optind - 1]);
+            goto end_tag;
+        }
+    }
+
+    opt_daemon = getenv (ENV_RRDCACHED_ADDRESS);
+    if (opt_daemon != NULL) {
+        rrd_set_error ("The \"%s\" environment variable is defined, "
+                "but \"%s\" cannot work with rrdcached. Either unset "
+                "the environment variable or use \"update\" instead.",
+                ENV_RRDCACHED_ADDRESS, argv[0]);
+        goto end_tag;
+    }
+
+    /* need at least 2 arguments: filename, data. */
+    if (argc - optind < 2) {
+        rrd_set_error("Not enough arguments");
+        goto end_tag;
+    }
+    rc.u_int = 0;
+    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);
+    result->value.u_int = rc.u_int;
+  end_tag:
+    return result;
+}
+
+int rrd_update(
+    int argc,
+    char **argv)
+{
+    struct option long_options[] = {
+        {"template", required_argument, 0, 't'},
+        {"daemon",   required_argument, 0, 'd'},
+        {0, 0, 0, 0}
+    };
+    int       option_index = 0;
+    int       opt;
+    char     *tmplt = NULL;
+    int       rc = -1;
+    char     *opt_daemon = NULL;
+
+    optind = 0;
+    opterr = 0;         /* initialize getopt */
+
+    while (1) {
+        opt = getopt_long(argc, argv, "t:d:", long_options, &option_index);
+
+        if (opt == EOF)
+            break;
+
+        switch (opt) {
+        case 't':
+            tmplt = strdup(optarg);
+            break;
+
+        case 'd':
+            if (opt_daemon != NULL)
+                free (opt_daemon);
+            opt_daemon = strdup (optarg);
+            if (opt_daemon == NULL)
+            {
+                rrd_set_error("strdup failed.");
+                goto out;
+            }
+            break;
+
+        case '?':
+            rrd_set_error("unknown option '%s'", argv[optind - 1]);
+            goto out;
+        }
+    }
+
+    /* need at least 2 arguments: filename, data. */
+    if (argc - optind < 2) {
+        rrd_set_error("Not enough arguments");
+        goto out;
+    }
+
+    {   /* try to connect to rrdcached */
+        int status = rrdc_connect(opt_daemon);
+        if (status != 0) return status;
+    }
+
+    if ((tmplt != NULL) && rrdc_is_connected(opt_daemon))
+    {
+        rrd_set_error("The caching daemon cannot be used together with "
+                "templates yet.");
+        goto out;
+    }
+
+    if (! rrdc_is_connected(opt_daemon))
+    {
+      rc = rrd_update_r(argv[optind], tmplt,
+                        argc - optind - 1, (const char **) (argv + optind + 1));
+    }
+    else /* we are connected */
+    {
+        rc = rrdc_update (argv[optind], /* file */
+                          argc - optind - 1, /* values_num */
+                          (const char *const *) (argv + optind + 1)); /* values */
+        if (rc > 0)
+            rrd_set_error("Failed sending the values to rrdcached: %s",
+                          rrd_strerror (rc));
+    }
+
+  out:
+    if (tmplt != NULL)
+    {
+        free(tmplt);
+        tmplt = NULL;
+    }
+    if (opt_daemon != NULL)
+    {
+        free (opt_daemon);
+        opt_daemon = NULL;
+    }
+    return rc;
+}
+
+int rrd_update_r(
+    const char *filename,
+    const char *tmplt,
+    int argc,
+    const char **argv)
+{
+    return _rrd_update(filename, tmplt, argc, argv, NULL);
+}
+
+int _rrd_update(
+    const char *filename,
+    const char *tmplt,
+    int argc,
+    const char **argv,
+    rrd_info_t * pcdp_summary)
+{
+
+    int       arg_i = 2;
+
+    unsigned long rra_begin;    /* byte pointer to the rra
+                                 * area in the rrd file.  this
+                                 * pointer never changes value */
+    rrd_value_t *pdp_new;   /* prepare the incoming data to be added 
+                             * to the existing entry */
+    rrd_value_t *pdp_temp;  /* prepare the pdp values to be added 
+                             * to the cdp values */
+
+    long     *tmpl_idx; /* index representing the settings
+                         * transported by the tmplt index */
+    unsigned long tmpl_cnt = 2; /* time and data */
+    rrd_t     rrd;
+    time_t    current_time = 0;
+    unsigned long current_time_usec = 0;    /* microseconds part of current time */
+    char    **updvals;
+    int       schedule_smooth = 0;
+
+    /* number of elapsed PDP steps since last update */
+    unsigned long *rra_step_cnt = NULL;
+
+    int       version;  /* rrd version */
+    rrd_file_t *rrd_file;
+    char     *arg_copy; /* for processing the argv */
+    unsigned long *skip_update; /* RRAs to advance but not write */
+
+    /* need at least 1 arguments: data. */
+    if (argc < 1) {
+        rrd_set_error("Not enough arguments");
+        goto err_out;
+    }
+
+    rrd_init(&rrd);
+    if ((rrd_file = rrd_open(filename, &rrd, RRD_READWRITE)) == NULL) {
+        goto err_free;
+    }
+    /* We are now at the beginning of the rra's */
+    rra_begin = rrd_file->header_len;
+
+    version = atoi(rrd.stat_head->version);
+
+    initialize_time(&current_time, &current_time_usec, version);
+
+    /* get exclusive lock to whole file.
+     * lock gets removed when we close the file.
+     */
+    if (rrd_lock(rrd_file) != 0) {
+        rrd_set_error("could not lock RRD");
+        goto err_close;
+    }
+
+    if (allocate_data_structures(&rrd, &updvals,
+                                 &pdp_temp, tmplt, &tmpl_idx, &tmpl_cnt,
+                                 &rra_step_cnt, &skip_update,
+                                 &pdp_new) == -1) {
+        goto err_close;
+    }
+
+    /* loop through the arguments. */
+    for (arg_i = 0; arg_i < argc; arg_i++) {
+        if ((arg_copy = strdup(argv[arg_i])) == NULL) {
+            rrd_set_error("failed duplication argv entry");
+            break;
+        }
+        if (process_arg(arg_copy, &rrd, rrd_file, rra_begin,
+                        &current_time, &current_time_usec, pdp_temp, pdp_new,
+                        rra_step_cnt, updvals, tmpl_idx, tmpl_cnt,
+                        &pcdp_summary, version, skip_update,
+                        &schedule_smooth) == -1) {
+            if (rrd_test_error()) { /* Should have error string always here */
+                char     *save_error;
+
+                /* Prepend file name to error message */
+                if ((save_error = strdup(rrd_get_error())) != NULL) {
+                    rrd_set_error("%s: %s", filename, save_error);
+                    free(save_error);
+                }
+            }
+            free(arg_copy);
+            break;
+        }
+        free(arg_copy);
+    }
+
+    free(rra_step_cnt);
+
+    /* if we got here and if there is an error and if the file has not been
+     * written to, then close things up and return. */
+    if (rrd_test_error()) {
+        goto err_free_structures;
+    }
+#ifndef HAVE_MMAP
+    if (write_changes_to_disk(&rrd, rrd_file, version) == -1) {
+        goto err_free_structures;
+    }
+#endif
+
+    /* calling the smoothing code here guarantees at most one smoothing
+     * operation per rrd_update call. Unfortunately, it is possible with bulk
+     * updates, or a long-delayed update for smoothing to occur off-schedule.
+     * This really isn't critical except during the burn-in cycles. */
+    if (schedule_smooth) {
+        smooth_all_rras(&rrd, rrd_file, rra_begin);
+    }
+
+/*    rrd_dontneed(rrd_file,&rrd); */
+    rrd_free(&rrd);
+    rrd_close(rrd_file);
+
+    free(pdp_new);
+    free(tmpl_idx);
+    free(pdp_temp);
+    free(skip_update);
+    free(updvals);
+    return 0;
+
+  err_free_structures:
+    free(pdp_new);
+    free(tmpl_idx);
+    free(pdp_temp);
+    free(skip_update);
+    free(updvals);
+  err_close:
+    rrd_close(rrd_file);
+  err_free:
+    rrd_free(&rrd);
+  err_out:
+    return -1;
+}
+
+/*
+ * Allocate some important arrays used, and initialize the template.
+ *
+ * When it returns, either all of the structures are allocated
+ * or none of them are.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static int allocate_data_structures(
+    rrd_t *rrd,
+    char ***updvals,
+    rrd_value_t **pdp_temp,
+    const char *tmplt,
+    long **tmpl_idx,
+    unsigned long *tmpl_cnt,
+    unsigned long **rra_step_cnt,
+    unsigned long **skip_update,
+    rrd_value_t **pdp_new)
+{
+    unsigned  i, ii;
+    if ((*updvals = (char **) malloc(sizeof(char *)
+                                     * (rrd->stat_head->ds_cnt + 1))) == NULL) {
+        rrd_set_error("allocating updvals pointer array.");
+        return -1;
+    }
+    if ((*pdp_temp = (rrd_value_t *) malloc(sizeof(rrd_value_t)
+                                            * rrd->stat_head->ds_cnt)) ==
+        NULL) {
+        rrd_set_error("allocating pdp_temp.");
+        goto err_free_updvals;
+    }
+    if ((*skip_update = (unsigned long *) malloc(sizeof(unsigned long)
+                                                 *
+                                                 rrd->stat_head->rra_cnt)) ==
+        NULL) {
+        rrd_set_error("allocating skip_update.");
+        goto err_free_pdp_temp;
+    }
+    if ((*tmpl_idx = (long *) malloc(sizeof(unsigned long)
+                                     * (rrd->stat_head->ds_cnt + 1))) == NULL) {
+        rrd_set_error("allocating tmpl_idx.");
+        goto err_free_skip_update;
+    }
+    if ((*rra_step_cnt = (unsigned long *) malloc(sizeof(unsigned long)
+                                                  *
+                                                  (rrd->stat_head->
+                                                   rra_cnt))) == NULL) {
+        rrd_set_error("allocating rra_step_cnt.");
+        goto err_free_tmpl_idx;
+    }
+
+    /* initialize tmplt redirector */
+    /* default config example (assume DS 1 is a CDEF DS)
+       tmpl_idx[0] -> 0; (time)
+       tmpl_idx[1] -> 1; (DS 0)
+       tmpl_idx[2] -> 3; (DS 2)
+       tmpl_idx[3] -> 4; (DS 3) */
+    (*tmpl_idx)[0] = 0; /* time */
+    for (i = 1, ii = 1; i <= rrd->stat_head->ds_cnt; i++) {
+        if (dst_conv(rrd->ds_def[i - 1].dst) != DST_CDEF)
+            (*tmpl_idx)[ii++] = i;
+    }
+    *tmpl_cnt = ii;
+
+    if (tmplt != NULL) {
+        if (parse_template(rrd, tmplt, tmpl_cnt, *tmpl_idx) == -1) {
+            goto err_free_rra_step_cnt;
+        }
+    }
+
+    if ((*pdp_new = (rrd_value_t *) malloc(sizeof(rrd_value_t)
+                                           * rrd->stat_head->ds_cnt)) == NULL) {
+        rrd_set_error("allocating pdp_new.");
+        goto err_free_rra_step_cnt;
+    }
+
+    return 0;
+
+  err_free_rra_step_cnt:
+    free(*rra_step_cnt);
+  err_free_tmpl_idx:
+    free(*tmpl_idx);
+  err_free_skip_update:
+    free(*skip_update);
+  err_free_pdp_temp:
+    free(*pdp_temp);
+  err_free_updvals:
+    free(*updvals);
+    return -1;
+}
+
+/*
+ * Parses tmplt and puts an ordered list of DS's into tmpl_idx.
+ *
+ * Returns 0 on success.
+ */
+static int parse_template(
+    rrd_t *rrd,
+    const char *tmplt,
+    unsigned long *tmpl_cnt,
+    long *tmpl_idx)
+{
+    char     *dsname, *tmplt_copy;
+    unsigned int tmpl_len, i;
+    int       ret = 0;
+
+    *tmpl_cnt = 1;      /* the first entry is the time */
+
+    /* we should work on a writeable copy here */
+    if ((tmplt_copy = strdup(tmplt)) == NULL) {
+        rrd_set_error("error copying tmplt '%s'", tmplt);
+        ret = -1;
+        goto out;
+    }
+
+    dsname = tmplt_copy;
+    tmpl_len = strlen(tmplt_copy);
+    for (i = 0; i <= tmpl_len; i++) {
+        if (tmplt_copy[i] == ':' || tmplt_copy[i] == '\0') {
+            tmplt_copy[i] = '\0';
+            if (*tmpl_cnt > rrd->stat_head->ds_cnt) {
+                rrd_set_error("tmplt contains more DS definitions than RRD");
+                ret = -1;
+                goto out_free_tmpl_copy;
+            }
+            if ((tmpl_idx[(*tmpl_cnt)++] = ds_match(rrd, dsname) + 1) == 0) {
+                rrd_set_error("unknown DS name '%s'", dsname);
+                ret = -1;
+                goto out_free_tmpl_copy;
+            }
+            /* go to the next entry on the tmplt_copy */
+            if (i < tmpl_len)
+                dsname = &tmplt_copy[i + 1];
+        }
+    }
+  out_free_tmpl_copy:
+    free(tmplt_copy);
+  out:
+    return ret;
+}
+
+/*
+ * Parse an update string, updates the primary data points (PDPs)
+ * and consolidated data points (CDPs), and writes changes to the RRAs.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static int process_arg(
+    char *step_start,
+    rrd_t *rrd,
+    rrd_file_t *rrd_file,
+    unsigned long rra_begin,
+    time_t *current_time,
+    unsigned long *current_time_usec,
+    rrd_value_t *pdp_temp,
+    rrd_value_t *pdp_new,
+    unsigned long *rra_step_cnt,
+    char **updvals,
+    long *tmpl_idx,
+    unsigned long tmpl_cnt,
+    rrd_info_t ** pcdp_summary,
+    int version,
+    unsigned long *skip_update,
+    int *schedule_smooth)
+{
+    rrd_value_t *seasonal_coef = NULL, *last_seasonal_coef = NULL;
+
+    /* a vector of future Holt-Winters seasonal coefs */
+    unsigned long elapsed_pdp_st;
+
+    double    interval, pre_int, post_int;  /* interval between this and
+                                             * the last run */
+    unsigned long proc_pdp_cnt;
+
+    if (parse_ds(rrd, updvals, tmpl_idx, step_start, tmpl_cnt,
+                 current_time, current_time_usec, version) == -1) {
+        return -1;
+    }
+
+    interval = (double) (*current_time - rrd->live_head->last_up)
+        + (double) ((long) *current_time_usec -
+                    (long) rrd->live_head->last_up_usec) / 1e6f;
+
+    /* process the data sources and update the pdp_prep 
+     * area accordingly */
+    if (update_pdp_prep(rrd, updvals, pdp_new, interval) == -1) {
+        return -1;
+    }
+
+    elapsed_pdp_st = calculate_elapsed_steps(rrd,
+                                             *current_time,
+                                             *current_time_usec, interval,
+                                             &pre_int, &post_int,
+                                             &proc_pdp_cnt);
+
+    /* has a pdp_st moment occurred since the last run ? */
+    if (elapsed_pdp_st == 0) {
+        /* no we have not passed a pdp_st moment. therefore update is simple */
+        simple_update(rrd, interval, pdp_new);
+    } else {
+        /* an pdp_st has occurred. */
+        if (process_all_pdp_st(rrd, interval,
+                               pre_int, post_int,
+                               elapsed_pdp_st, pdp_new, pdp_temp) == -1) {
+            return -1;
+        }
+        if (update_all_cdp_prep(rrd, rra_step_cnt,
+                                rra_begin, rrd_file,
+                                elapsed_pdp_st,
+                                proc_pdp_cnt,
+                                &last_seasonal_coef,
+                                &seasonal_coef,
+                                pdp_temp,
+                                skip_update, schedule_smooth) == -1) {
+            goto err_free_coefficients;
+        }
+        if (update_aberrant_cdps(rrd, rrd_file, rra_begin,
+                                 elapsed_pdp_st, pdp_temp,
+                                 &seasonal_coef) == -1) {
+            goto err_free_coefficients;
+        }
+        if (write_to_rras(rrd, rrd_file, rra_step_cnt, rra_begin,
+                          *current_time, skip_update,
+                          pcdp_summary) == -1) {
+            goto err_free_coefficients;
+        }
+    }                   /* endif a pdp_st has occurred */
+    rrd->live_head->last_up = *current_time;
+    rrd->live_head->last_up_usec = *current_time_usec;
+
+    if (version < 3) {
+        *rrd->legacy_last_up = rrd->live_head->last_up;
+    }
+    free(seasonal_coef);
+    free(last_seasonal_coef);
+    return 0;
+
+  err_free_coefficients:
+    free(seasonal_coef);
+    free(last_seasonal_coef);
+    return -1;
+}
+
+/*
+ * Parse a DS string (time + colon-separated values), storing the
+ * results in current_time, current_time_usec, and updvals.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static int parse_ds(
+    rrd_t *rrd,
+    char **updvals,
+    long *tmpl_idx,
+    char *input,
+    unsigned long tmpl_cnt,
+    time_t *current_time,
+    unsigned long *current_time_usec,
+    int version)
+{
+    char     *p;
+    unsigned long i;
+    char      timesyntax;
+
+    updvals[0] = input;
+    /* initialize all ds input to unknown except the first one
+       which has always got to be set */
+    for (i = 1; i <= rrd->stat_head->ds_cnt; i++)
+        updvals[i] = "U";
+
+    /* separate all ds elements; first must be examined separately
+       due to alternate time syntax */
+    if ((p = strchr(input, '@')) != NULL) {
+        timesyntax = '@';
+    } else if ((p = strchr(input, ':')) != NULL) {
+        timesyntax = ':';
+    } else {
+        rrd_set_error("expected timestamp not found in data source from %s",
+                      input);
+        return -1;
+    }
+    *p = '\0';
+    i = 1;
+    updvals[tmpl_idx[i++]] = p + 1;
+    while (*(++p)) {
+        if (*p == ':') {
+            *p = '\0';
+            if (i < tmpl_cnt) {
+                updvals[tmpl_idx[i++]] = p + 1;
+            }
+            else {
+                rrd_set_error("found extra data on update argument: %s",p+1);
+                return -1;
+            }                
+        }
+    }
+
+    if (i != tmpl_cnt) {
+        rrd_set_error("expected %lu data source readings (got %lu) from %s",
+                      tmpl_cnt - 1, i - 1, input);
+        return -1;
+    }
+
+    if (get_time_from_reading(rrd, timesyntax, updvals,
+                              current_time, current_time_usec,
+                              version) == -1) {
+        return -1;
+    }
+    return 0;
+}
+
+/*
+ * Parse the time in a DS string, store it in current_time and 
+ * current_time_usec and verify that it's later than the last
+ * update for this DS.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static int get_time_from_reading(
+    rrd_t *rrd,
+    char timesyntax,
+    char **updvals,
+    time_t *current_time,
+    unsigned long *current_time_usec,
+    int version)
+{
+    double    tmp;
+    char     *parsetime_error = NULL;
+    char     *old_locale;
+    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 = rrd_parsetime(updvals[0], &ds_tv))) {
+            rrd_set_error("ds time: %s: %s", updvals[0], parsetime_error);
+            return -1;
+        }
+        if (ds_tv.type == RELATIVE_TO_END_TIME ||
+            ds_tv.type == RELATIVE_TO_START_TIME) {
+            rrd_set_error("specifying time relative to the 'start' "
+                          "or 'end' makes no sense here: %s", updvals[0]);
+            return -1;
+        }
+        *current_time = mktime(&ds_tv.tm) +ds_tv.offset;
+        *current_time_usec = 0; /* FIXME: how to handle usecs here ? */
+    } else if (strcmp(updvals[0], "N") == 0) {
+        gettimeofday(&tmp_time, 0);
+        normalize_time(&tmp_time);
+        *current_time = tmp_time.tv_sec;
+        *current_time_usec = tmp_time.tv_usec;
+    } else {
+        old_locale = setlocale(LC_NUMERIC, "C");
+        errno = 0;
+        tmp = strtod(updvals[0], 0);
+        if (errno > 0) {
+            rrd_set_error("converting '%s' to float: %s",
+                updvals[0], rrd_strerror(errno));
+            return -1;
+        };
+        setlocale(LC_NUMERIC, old_locale);
+        if (tmp < 0.0){
+            gettimeofday(&tmp_time, 0);
+            tmp = (double)tmp_time.tv_sec + (double)tmp_time.tv_usec * 1e-6f + tmp;
+        }
+
+        *current_time = floor(tmp);
+        *current_time_usec = (long) ((tmp - (double) *current_time) * 1e6f);
+    }
+    /* dont do any correction for old version RRDs */
+    if (version < 3)
+        *current_time_usec = 0;
+
+    if (*current_time < rrd->live_head->last_up ||
+        (*current_time == rrd->live_head->last_up &&
+         (long) *current_time_usec <= (long) rrd->live_head->last_up_usec)) {
+        rrd_set_error("illegal attempt to update using time %ld when "
+                      "last update time is %ld (minimum one second step)",
+                      *current_time, rrd->live_head->last_up);
+        return -1;
+    }
+    return 0;
+}
+
+/*
+ * Update pdp_new by interpreting the updvals according to the DS type
+ * (COUNTER, GAUGE, etc.).
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static int update_pdp_prep(
+    rrd_t *rrd,
+    char **updvals,
+    rrd_value_t *pdp_new,
+    double interval)
+{
+    unsigned long ds_idx;
+    int       ii;
+    char     *endptr;   /* used in the conversion */
+    double    rate;
+    char     *old_locale;
+    enum dst_en dst_idx;
+
+    for (ds_idx = 0; ds_idx < rrd->stat_head->ds_cnt; ds_idx++) {
+        dst_idx = dst_conv(rrd->ds_def[ds_idx].dst);
+
+        /* make sure we do not build diffs with old last_ds values */
+        if (rrd->ds_def[ds_idx].par[DS_mrhb_cnt].u_cnt < interval) {
+            strncpy(rrd->pdp_prep[ds_idx].last_ds, "U", LAST_DS_LEN - 1);
+            rrd->pdp_prep[ds_idx].last_ds[LAST_DS_LEN - 1] = '\0';
+        }
+
+        /* NOTE: DST_CDEF should never enter this if block, because
+         * updvals[ds_idx+1][0] is initialized to 'U'; unless the caller
+         * accidently specified a value for the DST_CDEF. To handle this case,
+         * an extra check is required. */
+
+        if ((updvals[ds_idx + 1][0] != 'U') &&
+            (dst_idx != DST_CDEF) &&
+            rrd->ds_def[ds_idx].par[DS_mrhb_cnt].u_cnt >= interval) {
+            rate = DNAN;
+
+            /* pdp_new contains rate * time ... eg the bytes transferred during
+             * the interval. Doing it this way saves a lot of math operations
+             */
+            switch (dst_idx) {
+            case DST_COUNTER:
+            case DST_DERIVE:
+                /* Check if this is a valid integer. `U' is already handled in
+                 * another branch. */
+                for (ii = 0; updvals[ds_idx + 1][ii] != 0; ii++) {
+                    if ((ii == 0) && (dst_idx == DST_DERIVE)
+                            && (updvals[ds_idx + 1][ii] == '-'))
+                        continue;
+
+                    if ((updvals[ds_idx + 1][ii] < '0')
+                            || (updvals[ds_idx + 1][ii] > '9')) {
+                        rrd_set_error("not a simple %s integer: '%s'",
+                                (dst_idx == DST_DERIVE) ? "signed" : "unsigned",
+                                updvals[ds_idx + 1]);
+                        return -1;
+                    }
+                } /* for (ii = 0; updvals[ds_idx + 1][ii] != 0; ii++) */
+
+                if (rrd->pdp_prep[ds_idx].last_ds[0] != 'U') {
+                    pdp_new[ds_idx] =
+                        rrd_diff(updvals[ds_idx + 1],
+                                 rrd->pdp_prep[ds_idx].last_ds);
+                    if (dst_idx == DST_COUNTER) {
+                        /* simple overflow catcher. This will fail
+                         * terribly for non 32 or 64 bit counters
+                         * ... are there any others in SNMP land?
+                         */
+                        if (pdp_new[ds_idx] < (double) 0.0)
+                            pdp_new[ds_idx] += (double) 4294967296.0;   /* 2^32 */
+                        if (pdp_new[ds_idx] < (double) 0.0)
+                            pdp_new[ds_idx] += (double) 18446744069414584320.0; /* 2^64-2^32 */
+                    }
+                    rate = pdp_new[ds_idx] / interval;
+                } else {
+                    pdp_new[ds_idx] = DNAN;
+                }
+                break;
+            case DST_ABSOLUTE:
+                old_locale = setlocale(LC_NUMERIC, "C");
+                errno = 0;
+                pdp_new[ds_idx] = strtod(updvals[ds_idx + 1], &endptr);
+                if (errno > 0) {
+                    rrd_set_error("converting '%s' to float: %s",
+                                  updvals[ds_idx + 1], rrd_strerror(errno));
+                    return -1;
+                };
+                setlocale(LC_NUMERIC, old_locale);
+                if (endptr[0] != '\0') {
+                    rrd_set_error
+                        ("conversion of '%s' to float not complete: tail '%s'",
+                         updvals[ds_idx + 1], endptr);
+                    return -1;
+                }
+                rate = pdp_new[ds_idx] / interval;
+                break;
+            case DST_GAUGE:
+                old_locale = setlocale(LC_NUMERIC, "C");
+                errno = 0;
+                pdp_new[ds_idx] =
+                    strtod(updvals[ds_idx + 1], &endptr) * interval;
+                if (errno) {
+                    rrd_set_error("converting '%s' to float: %s",
+                                  updvals[ds_idx + 1], rrd_strerror(errno));
+                    return -1;
+                };
+                setlocale(LC_NUMERIC, old_locale);
+                if (endptr[0] != '\0') {
+                    rrd_set_error
+                        ("conversion of '%s' to float not complete: tail '%s'",
+                         updvals[ds_idx + 1], endptr);
+                    return -1;
+                }
+                rate = pdp_new[ds_idx] / interval;
+                break;
+            default:
+                rrd_set_error("rrd contains unknown DS type : '%s'",
+                              rrd->ds_def[ds_idx].dst);
+                return -1;
+            }
+            /* break out of this for loop if the error string is set */
+            if (rrd_test_error()) {
+                return -1;
+            }
+            /* make sure pdp_temp is neither too large or too small
+             * if any of these occur it becomes unknown ...
+             * sorry folks ... */
+            if (!isnan(rate) &&
+                ((!isnan(rrd->ds_def[ds_idx].par[DS_max_val].u_val) &&
+                  rate > rrd->ds_def[ds_idx].par[DS_max_val].u_val) ||
+                 (!isnan(rrd->ds_def[ds_idx].par[DS_min_val].u_val) &&
+                  rate < rrd->ds_def[ds_idx].par[DS_min_val].u_val))) {
+                pdp_new[ds_idx] = DNAN;
+            }
+        } else {
+            /* no news is news all the same */
+            pdp_new[ds_idx] = DNAN;
+        }
+
+
+        /* make a copy of the command line argument for the next run */
+#ifdef DEBUG
+        fprintf(stderr, "prep ds[%lu]\t"
+                "last_arg '%s'\t"
+                "this_arg '%s'\t"
+                "pdp_new %10.2f\n",
+                ds_idx, rrd->pdp_prep[ds_idx].last_ds, updvals[ds_idx + 1],
+                pdp_new[ds_idx]);
+#endif
+        strncpy(rrd->pdp_prep[ds_idx].last_ds, updvals[ds_idx + 1],
+                LAST_DS_LEN - 1);
+        rrd->pdp_prep[ds_idx].last_ds[LAST_DS_LEN - 1] = '\0';
+    }
+    return 0;
+}
+
+/*
+ * How many PDP steps have elapsed since the last update? Returns the answer,
+ * and stores the time between the last update and the last PDP in pre_time,
+ * and the time between the last PDP and the current time in post_int.
+ */
+static int calculate_elapsed_steps(
+    rrd_t *rrd,
+    unsigned long current_time,
+    unsigned long current_time_usec,
+    double interval,
+    double *pre_int,
+    double *post_int,
+    unsigned long *proc_pdp_cnt)
+{
+    unsigned long proc_pdp_st;  /* which pdp_st was the last to be processed */
+    unsigned long occu_pdp_st;  /* when was the pdp_st before the last update
+                                 * time */
+    unsigned long proc_pdp_age; /* how old was the data in the pdp prep area 
+                                 * when it was last updated */
+    unsigned long occu_pdp_age; /* how long ago was the last pdp_step time */
+
+    /* when was the current pdp started */
+    proc_pdp_age = rrd->live_head->last_up % rrd->stat_head->pdp_step;
+    proc_pdp_st = rrd->live_head->last_up - proc_pdp_age;
+
+    /* when did the last pdp_st occur */
+    occu_pdp_age = current_time % rrd->stat_head->pdp_step;
+    occu_pdp_st = current_time - occu_pdp_age;
+
+    if (occu_pdp_st > proc_pdp_st) {
+        /* OK we passed the pdp_st moment */
+        *pre_int = (long) occu_pdp_st - rrd->live_head->last_up;    /* how much of the input data
+                                                                     * occurred before the latest
+                                                                     * pdp_st moment*/
+        *pre_int -= ((double) rrd->live_head->last_up_usec) / 1e6f; /* adjust usecs */
+        *post_int = occu_pdp_age;   /* how much after it */
+        *post_int += ((double) current_time_usec) / 1e6f;   /* adjust usecs */
+    } else {
+        *pre_int = interval;
+        *post_int = 0;
+    }
+
+    *proc_pdp_cnt = proc_pdp_st / rrd->stat_head->pdp_step;
+
+#ifdef DEBUG
+    printf("proc_pdp_age %lu\t"
+           "proc_pdp_st %lu\t"
+           "occu_pfp_age %lu\t"
+           "occu_pdp_st %lu\t"
+           "int %lf\t"
+           "pre_int %lf\t"
+           "post_int %lf\n", proc_pdp_age, proc_pdp_st,
+           occu_pdp_age, occu_pdp_st, interval, *pre_int, *post_int);
+#endif
+
+    /* compute the number of elapsed pdp_st moments */
+    return (occu_pdp_st - proc_pdp_st) / rrd->stat_head->pdp_step;
+}
+
+/*
+ * Increment the PDP values by the values in pdp_new, or else initialize them.
+ */
+static void simple_update(
+    rrd_t *rrd,
+    double interval,
+    rrd_value_t *pdp_new)
+{
+    int       i;
+
+    for (i = 0; i < (signed) rrd->stat_head->ds_cnt; i++) {
+        if (isnan(pdp_new[i])) {
+            /* this is not really accurate if we use subsecond data arrival time
+               should have thought of it when going subsecond resolution ...
+               sorry next format change we will have it! */
+            rrd->pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt +=
+                floor(interval);
+        } else {
+            if (isnan(rrd->pdp_prep[i].scratch[PDP_val].u_val)) {
+                rrd->pdp_prep[i].scratch[PDP_val].u_val = pdp_new[i];
+            } else {
+                rrd->pdp_prep[i].scratch[PDP_val].u_val += pdp_new[i];
+            }
+        }
+#ifdef DEBUG
+        fprintf(stderr,
+                "NO PDP  ds[%i]\t"
+                "value %10.2f\t"
+                "unkn_sec %5lu\n",
+                i,
+                rrd->pdp_prep[i].scratch[PDP_val].u_val,
+                rrd->pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
+#endif
+    }
+}
+
+/*
+ * Call process_pdp_st for each DS.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static int process_all_pdp_st(
+    rrd_t *rrd,
+    double interval,
+    double pre_int,
+    double post_int,
+    unsigned long elapsed_pdp_st,
+    rrd_value_t *pdp_new,
+    rrd_value_t *pdp_temp)
+{
+    unsigned long ds_idx;
+
+    /* in pdp_prep[].scratch[PDP_val].u_val we have collected
+       rate*seconds which occurred up to the last run.
+       pdp_new[] contains rate*seconds from the latest run.
+       pdp_temp[] will contain the rate for cdp */
+
+    for (ds_idx = 0; ds_idx < rrd->stat_head->ds_cnt; ds_idx++) {
+        if (process_pdp_st(rrd, ds_idx, interval, pre_int, post_int,
+                           elapsed_pdp_st * rrd->stat_head->pdp_step,
+                           pdp_new, pdp_temp) == -1) {
+            return -1;
+        }
+#ifdef DEBUG
+        fprintf(stderr, "PDP UPD ds[%lu]\t"
+                "elapsed_pdp_st %lu\t"
+                "pdp_temp %10.2f\t"
+                "new_prep %10.2f\t"
+                "new_unkn_sec %5lu\n",
+                ds_idx,
+                elapsed_pdp_st,
+                pdp_temp[ds_idx],
+                rrd->pdp_prep[ds_idx].scratch[PDP_val].u_val,
+                rrd->pdp_prep[ds_idx].scratch[PDP_unkn_sec_cnt].u_cnt);
+#endif
+    }
+    return 0;
+}
+
+/*
+ * Process an update that occurs after one of the PDP moments.
+ * Increments the PDP value, sets NAN if time greater than the
+ * heartbeats have elapsed, processes CDEFs.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static int process_pdp_st(
+    rrd_t *rrd,
+    unsigned long ds_idx,
+    double interval,
+    double pre_int,
+    double post_int,
+    long diff_pdp_st,   /* number of seconds in full steps passed since last update */
+    rrd_value_t *pdp_new,
+    rrd_value_t *pdp_temp)
+{
+    int       i;
+
+    /* update pdp_prep to the current pdp_st. */
+    double    pre_unknown = 0.0;
+    unival   *scratch = rrd->pdp_prep[ds_idx].scratch;
+    unsigned long mrhb = rrd->ds_def[ds_idx].par[DS_mrhb_cnt].u_cnt;
+
+    rpnstack_t rpnstack;    /* used for COMPUTE DS */
+
+    rpnstack_init(&rpnstack);
+
+
+    if (isnan(pdp_new[ds_idx])) {
+        /* a final bit of unknown to be added before calculation
+           we use a temporary variable for this so that we
+           don't have to turn integer lines before using the value */
+        pre_unknown = pre_int;
+    } else {
+        if (isnan(scratch[PDP_val].u_val)) {
+            scratch[PDP_val].u_val = 0;
+        }
+        scratch[PDP_val].u_val += pdp_new[ds_idx] / interval * pre_int;
+    }
+
+    /* if too much of the pdp_prep is unknown we dump it */
+    /* if the interval is larger thatn mrhb we get NAN */
+    if ((interval > mrhb) ||
+        (rrd->stat_head->pdp_step / 2.0 <
+         (signed) scratch[PDP_unkn_sec_cnt].u_cnt)) {
+        pdp_temp[ds_idx] = DNAN;
+    } else {
+        pdp_temp[ds_idx] = scratch[PDP_val].u_val /
+            ((double) (diff_pdp_st - scratch[PDP_unkn_sec_cnt].u_cnt) -
+             pre_unknown);
+    }
+
+    /* process CDEF data sources; remember each CDEF DS can
+     * only reference other DS with a lower index number */
+    if (dst_conv(rrd->ds_def[ds_idx].dst) == DST_CDEF) {
+        rpnp_t   *rpnp;
+
+        rpnp =
+            rpn_expand((rpn_cdefds_t *) &(rrd->ds_def[ds_idx].par[DS_cdef]));
+        /* substitute data values for OP_VARIABLE nodes */
+        for (i = 0; rpnp[i].op != OP_END; i++) {
+            if (rpnp[i].op == OP_VARIABLE) {
+                rpnp[i].op = OP_NUMBER;
+                rpnp[i].val = pdp_temp[rpnp[i].ptr];
+            }
+        }
+        /* run the rpn calculator */
+        if (rpn_calc(rpnp, &rpnstack, 0, pdp_temp, ds_idx) == -1) {
+            free(rpnp);
+            rpnstack_free(&rpnstack);
+            return -1;
+        }
+    }
+
+    /* make pdp_prep ready for the next run */
+    if (isnan(pdp_new[ds_idx])) {
+        /* this is not realy accurate if we use subsecond data arival time
+           should have thought of it when going subsecond resolution ...
+           sorry next format change we will have it! */
+        scratch[PDP_unkn_sec_cnt].u_cnt = floor(post_int);
+        scratch[PDP_val].u_val = DNAN;
+    } else {
+        scratch[PDP_unkn_sec_cnt].u_cnt = 0;
+        scratch[PDP_val].u_val = pdp_new[ds_idx] / interval * post_int;
+    }
+    rpnstack_free(&rpnstack);
+    return 0;
+}
+
+/*
+ * Iterate over all the RRAs for a given DS and:
+ * 1. Decide whether to schedule a smooth later
+ * 2. Decide whether to skip updating SEASONAL and DEVSEASONAL
+ * 3. Update the CDP
+ *
+ * Returns 0 on success, -1 on error
+ */
+static int update_all_cdp_prep(
+    rrd_t *rrd,
+    unsigned long *rra_step_cnt,
+    unsigned long rra_begin,
+    rrd_file_t *rrd_file,
+    unsigned long elapsed_pdp_st,
+    unsigned long proc_pdp_cnt,
+    rrd_value_t **last_seasonal_coef,
+    rrd_value_t **seasonal_coef,
+    rrd_value_t *pdp_temp,
+    unsigned long *skip_update,
+    int *schedule_smooth)
+{
+    unsigned long rra_idx;
+
+    /* index into the CDP scratch array */
+    enum cf_en current_cf;
+    unsigned long rra_start;
+
+    /* number of rows to be updated in an RRA for a data value. */
+    unsigned long start_pdp_offset;
+
+    rra_start = rra_begin;
+    for (rra_idx = 0; rra_idx < rrd->stat_head->rra_cnt; rra_idx++) {
+        current_cf = cf_conv(rrd->rra_def[rra_idx].cf_nam);
+        start_pdp_offset =
+            rrd->rra_def[rra_idx].pdp_cnt -
+            proc_pdp_cnt % rrd->rra_def[rra_idx].pdp_cnt;
+        skip_update[rra_idx] = 0;
+        if (start_pdp_offset <= elapsed_pdp_st) {
+            rra_step_cnt[rra_idx] = (elapsed_pdp_st - start_pdp_offset) /
+                rrd->rra_def[rra_idx].pdp_cnt + 1;
+        } else {
+            rra_step_cnt[rra_idx] = 0;
+        }
+
+        if (current_cf == CF_SEASONAL || current_cf == CF_DEVSEASONAL) {
+            /* If this is a bulk update, we need to skip ahead in the seasonal arrays
+             * so that they will be correct for the next observed value; note that for
+             * the bulk update itself, no update will occur to DEVSEASONAL or SEASONAL;
+             * futhermore, HWPREDICT and DEVPREDICT will be set to DNAN. */
+            if (rra_step_cnt[rra_idx] > 1) {
+                skip_update[rra_idx] = 1;
+                lookup_seasonal(rrd, rra_idx, rra_start, rrd_file,
+                                elapsed_pdp_st, last_seasonal_coef);
+                lookup_seasonal(rrd, rra_idx, rra_start, rrd_file,
+                                elapsed_pdp_st + 1, seasonal_coef);
+            }
+            /* periodically run a smoother for seasonal effects */
+            if (do_schedule_smooth(rrd, rra_idx, elapsed_pdp_st)) {
+#ifdef DEBUG
+                fprintf(stderr,
+                        "schedule_smooth: cur_row %lu, elapsed_pdp_st %lu, smooth idx %lu\n",
+                        rrd->rra_ptr[rra_idx].cur_row, elapsed_pdp_st,
+                        rrd->rra_def[rra_idx].par[RRA_seasonal_smooth_idx].
+                        u_cnt);
+#endif
+                *schedule_smooth = 1;
+            }
+        }
+        if (rrd_test_error())
+            return -1;
+
+        if (update_cdp_prep
+            (rrd, elapsed_pdp_st, start_pdp_offset, rra_step_cnt, rra_idx,
+             pdp_temp, *last_seasonal_coef, *seasonal_coef,
+             current_cf) == -1) {
+            return -1;
+        }
+        rra_start +=
+            rrd->rra_def[rra_idx].row_cnt * rrd->stat_head->ds_cnt *
+            sizeof(rrd_value_t);
+    }
+    return 0;
+}
+
+/* 
+ * Are we due for a smooth? Also increments our position in the burn-in cycle.
+ */
+static int do_schedule_smooth(
+    rrd_t *rrd,
+    unsigned long rra_idx,
+    unsigned long elapsed_pdp_st)
+{
+    unsigned long cdp_idx = rra_idx * (rrd->stat_head->ds_cnt);
+    unsigned long cur_row = rrd->rra_ptr[rra_idx].cur_row;
+    unsigned long row_cnt = rrd->rra_def[rra_idx].row_cnt;
+    unsigned long seasonal_smooth_idx =
+        rrd->rra_def[rra_idx].par[RRA_seasonal_smooth_idx].u_cnt;
+    unsigned long *init_seasonal =
+        &(rrd->cdp_prep[cdp_idx].scratch[CDP_init_seasonal].u_cnt);
+
+    /* Need to use first cdp parameter buffer to track burnin (burnin requires
+     * a specific smoothing schedule).  The CDP_init_seasonal parameter is
+     * really an RRA level, not a data source within RRA level parameter, but
+     * the rra_def is read only for rrd_update (not flushed to disk). */
+    if (*init_seasonal > BURNIN_CYCLES) {
+        /* someone has no doubt invented a trick to deal with this wrap around,
+         * but at least this code is clear. */
+        if (seasonal_smooth_idx > cur_row) {
+            /* here elapsed_pdp_st = rra_step_cnt[rra_idx] because of 1-1 mapping
+             * between PDP and CDP */
+            return (cur_row + elapsed_pdp_st >= seasonal_smooth_idx);
+        }
+        /* can't rely on negative numbers because we are working with
+         * unsigned values */
+        return (cur_row + elapsed_pdp_st >= row_cnt
+                && cur_row + elapsed_pdp_st >= row_cnt + seasonal_smooth_idx);
+    }
+    /* mark off one of the burn-in cycles */
+    return (cur_row + elapsed_pdp_st >= row_cnt && ++(*init_seasonal));
+}
+
+/*
+ * For a given RRA, iterate over the data sources and call the appropriate
+ * consolidation function.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static int update_cdp_prep(
+    rrd_t *rrd,
+    unsigned long elapsed_pdp_st,
+    unsigned long start_pdp_offset,
+    unsigned long *rra_step_cnt,
+    int rra_idx,
+    rrd_value_t *pdp_temp,
+    rrd_value_t *last_seasonal_coef,
+    rrd_value_t *seasonal_coef,
+    int current_cf)
+{
+    unsigned long ds_idx, cdp_idx;
+
+    /* update CDP_PREP areas */
+    /* loop over data soures within each RRA */
+    for (ds_idx = 0; ds_idx < rrd->stat_head->ds_cnt; ds_idx++) {
+
+        cdp_idx = rra_idx * rrd->stat_head->ds_cnt + ds_idx;
+
+        if (rrd->rra_def[rra_idx].pdp_cnt > 1) {
+            update_cdp(rrd->cdp_prep[cdp_idx].scratch, current_cf,
+                       pdp_temp[ds_idx], rra_step_cnt[rra_idx],
+                       elapsed_pdp_st, start_pdp_offset,
+                       rrd->rra_def[rra_idx].pdp_cnt,
+                       rrd->rra_def[rra_idx].par[RRA_cdp_xff_val].u_val,
+                       rra_idx, ds_idx);
+        } else {
+            /* Nothing to consolidate if there's one PDP per CDP. However, if
+             * we've missed some PDPs, let's update null counters etc. */
+            if (elapsed_pdp_st > 2) {
+                reset_cdp(rrd, elapsed_pdp_st, pdp_temp, last_seasonal_coef,
+                          seasonal_coef, rra_idx, ds_idx, cdp_idx,
+                          current_cf);
+            }
+        }
+
+        if (rrd_test_error())
+            return -1;
+    }                   /* endif data sources loop */
+    return 0;
+}
+
+/*
+ * Given the new reading (pdp_temp_val), update or initialize the CDP value,
+ * primary value, secondary value, and # of unknowns.
+ */
+static void update_cdp(
+    unival *scratch,
+    int current_cf,
+    rrd_value_t pdp_temp_val,
+    unsigned long rra_step_cnt,
+    unsigned long elapsed_pdp_st,
+    unsigned long start_pdp_offset,
+    unsigned long pdp_cnt,
+    rrd_value_t xff,
+    int i,
+    int ii)
+{
+    /* shorthand variables */
+    rrd_value_t *cdp_val = &scratch[CDP_val].u_val;
+    rrd_value_t *cdp_primary_val = &scratch[CDP_primary_val].u_val;
+    rrd_value_t *cdp_secondary_val = &scratch[CDP_secondary_val].u_val;
+    unsigned long *cdp_unkn_pdp_cnt = &scratch[CDP_unkn_pdp_cnt].u_cnt;
+
+    if (rra_step_cnt) {
+        /* If we are in this block, as least 1 CDP value will be written to
+         * disk, this is the CDP_primary_val entry. If more than 1 value needs
+         * to be written, then the "fill in" value is the CDP_secondary_val
+         * entry. */
+        if (isnan(pdp_temp_val)) {
+            *cdp_unkn_pdp_cnt += start_pdp_offset;
+            *cdp_secondary_val = DNAN;
+        } else {
+            /* CDP_secondary value is the RRA "fill in" value for intermediary
+             * CDP data entries. No matter the CF, the value is the same because
+             * the average, max, min, and last of a list of identical values is
+             * the same, namely, the value itself. */
+            *cdp_secondary_val = pdp_temp_val;
+        }
+
+        if (*cdp_unkn_pdp_cnt > pdp_cnt * xff) {
+            *cdp_primary_val = DNAN;
+        } else {
+            initialize_cdp_val(scratch, current_cf, pdp_temp_val,
+                               start_pdp_offset, pdp_cnt);
+        }
+        *cdp_val =
+            initialize_carry_over(pdp_temp_val,current_cf,
+                                  elapsed_pdp_st,
+                                  start_pdp_offset, pdp_cnt);
+               /* endif meets xff value requirement for a valid value */
+        /* initialize carry over CDP_unkn_pdp_cnt, this must after CDP_primary_val
+         * is set because CDP_unkn_pdp_cnt is required to compute that value. */
+        if (isnan(pdp_temp_val))
+            *cdp_unkn_pdp_cnt = (elapsed_pdp_st - start_pdp_offset) % pdp_cnt;
+        else
+            *cdp_unkn_pdp_cnt = 0;
+    } else {            /* rra_step_cnt[i]  == 0 */
+
+#ifdef DEBUG
+        if (isnan(*cdp_val)) {
+            fprintf(stderr, "schedule CDP_val update, RRA %d DS %d, DNAN\n",
+                    i, ii);
+        } else {
+            fprintf(stderr, "schedule CDP_val update, RRA %d DS %d, %10.2f\n",
+                    i, ii, *cdp_val);
+        }
+#endif
+        if (isnan(pdp_temp_val)) {
+            *cdp_unkn_pdp_cnt += elapsed_pdp_st;
+        } else {
+            *cdp_val =
+                calculate_cdp_val(*cdp_val, pdp_temp_val, elapsed_pdp_st,
+                                  current_cf, i, ii);
+        }
+    }
+}
+
+/*
+ * Set the CDP_primary_val and CDP_val to the appropriate initial value based
+ * on the type of consolidation function.
+ */
+static void initialize_cdp_val(
+    unival *scratch,
+    int current_cf,
+    rrd_value_t pdp_temp_val,
+    unsigned long start_pdp_offset,
+    unsigned long pdp_cnt)
+{
+    rrd_value_t cum_val, cur_val;
+
+    switch (current_cf) {
+    case CF_AVERAGE:
+        cum_val = IFDNAN(scratch[CDP_val].u_val, 0.0);
+        cur_val = IFDNAN(pdp_temp_val, 0.0);
+        scratch[CDP_primary_val].u_val =
+            (cum_val + cur_val * start_pdp_offset) /
+            (pdp_cnt - scratch[CDP_unkn_pdp_cnt].u_cnt);
+        break;
+    case CF_MAXIMUM: 
+        cum_val = IFDNAN(scratch[CDP_val].u_val, -DINF);
+        cur_val = IFDNAN(pdp_temp_val, -DINF);
+
+#if 0
+#ifdef DEBUG
+        if (isnan(scratch[CDP_val].u_val) && isnan(pdp_temp)) {
+            fprintf(stderr,
+                    "RRA %lu, DS %lu, both CDP_val and pdp_temp are DNAN!",
+                    i, ii);
+            exit(-1);
+        }
+#endif
+#endif
+        if (cur_val > cum_val)
+            scratch[CDP_primary_val].u_val = cur_val;
+        else
+            scratch[CDP_primary_val].u_val = cum_val;
+        break;
+    case CF_MINIMUM:
+        cum_val = IFDNAN(scratch[CDP_val].u_val, DINF);
+        cur_val = IFDNAN(pdp_temp_val, DINF);
+#if 0
+#ifdef DEBUG
+        if (isnan(scratch[CDP_val].u_val) && isnan(pdp_temp)) {
+            fprintf(stderr,
+                    "RRA %lu, DS %lu, both CDP_val and pdp_temp are DNAN!", i,
+                    ii);
+            exit(-1);
+        }
+#endif
+#endif
+        if (cur_val < cum_val)
+            scratch[CDP_primary_val].u_val = cur_val;
+        else
+            scratch[CDP_primary_val].u_val = cum_val;
+        break;
+    case CF_LAST:
+    default:
+        scratch[CDP_primary_val].u_val = pdp_temp_val;
+        break;
+    }
+}
+
+/*
+ * Update the consolidation function for Holt-Winters functions as
+ * well as other functions that don't actually consolidate multiple
+ * PDPs.
+ */
+static void reset_cdp(
+    rrd_t *rrd,
+    unsigned long elapsed_pdp_st,
+    rrd_value_t *pdp_temp,
+    rrd_value_t *last_seasonal_coef,
+    rrd_value_t *seasonal_coef,
+    int rra_idx,
+    int ds_idx,
+    int cdp_idx,
+    enum cf_en current_cf)
+{
+    unival   *scratch = rrd->cdp_prep[cdp_idx].scratch;
+
+    switch (current_cf) {
+    case CF_AVERAGE:
+    default:
+        scratch[CDP_primary_val].u_val = pdp_temp[ds_idx];
+        scratch[CDP_secondary_val].u_val = pdp_temp[ds_idx];
+        break;
+    case CF_SEASONAL:
+    case CF_DEVSEASONAL:
+        /* need to update cached seasonal values, so they are consistent
+         * with the bulk update */
+        /* WARNING: code relies on the fact that CDP_hw_last_seasonal and
+         * CDP_last_deviation are the same. */
+        scratch[CDP_hw_last_seasonal].u_val = last_seasonal_coef[ds_idx];
+        scratch[CDP_hw_seasonal].u_val = seasonal_coef[ds_idx];
+        break;
+    case CF_HWPREDICT:
+    case CF_MHWPREDICT:
+        /* need to update the null_count and last_null_count.
+         * even do this for non-DNAN pdp_temp because the
+         * algorithm is not learning from batch updates. */
+        scratch[CDP_null_count].u_cnt += elapsed_pdp_st;
+        scratch[CDP_last_null_count].u_cnt += elapsed_pdp_st - 1;
+        /* fall through */
+    case CF_DEVPREDICT:
+        scratch[CDP_primary_val].u_val = DNAN;
+        scratch[CDP_secondary_val].u_val = DNAN;
+        break;
+    case CF_FAILURES:
+        /* do not count missed bulk values as failures */
+        scratch[CDP_primary_val].u_val = 0;
+        scratch[CDP_secondary_val].u_val = 0;
+        /* need to reset violations buffer.
+         * could do this more carefully, but for now, just
+         * assume a bulk update wipes away all violations. */
+        erase_violations(rrd, cdp_idx, rra_idx);
+        break;
+    }
+}
+
+static rrd_value_t initialize_carry_over(
+    rrd_value_t pdp_temp_val,
+    int current_cf,
+    unsigned long elapsed_pdp_st,
+    unsigned long start_pdp_offset,
+    unsigned long pdp_cnt)
+{
+    unsigned long pdp_into_cdp_cnt = ((elapsed_pdp_st - start_pdp_offset) % pdp_cnt);
+    if ( pdp_into_cdp_cnt == 0 || isnan(pdp_temp_val)){
+        switch (current_cf) {
+        case CF_MAXIMUM:
+            return -DINF;
+        case CF_MINIMUM:
+            return DINF;
+        case CF_AVERAGE:
+            return 0;
+        default:
+            return DNAN;
+        }        
+    } 
+    else {
+        switch (current_cf) {
+        case CF_AVERAGE:
+            return pdp_temp_val *  pdp_into_cdp_cnt ;
+        default:
+            return pdp_temp_val;
+        }        
+    }        
+}
+
+/*
+ * Update or initialize a CDP value based on the consolidation
+ * function.
+ *
+ * Returns the new value.
+ */
+static rrd_value_t calculate_cdp_val(
+    rrd_value_t cdp_val,
+    rrd_value_t pdp_temp_val,
+    unsigned long elapsed_pdp_st,
+    int current_cf,
+#ifdef DEBUG
+    int i,
+    int ii
+#else
+    int UNUSED(i),
+    int UNUSED(ii)
+#endif
+    )
+{
+    if (isnan(cdp_val)) {
+        if (current_cf == CF_AVERAGE) {
+            pdp_temp_val *= elapsed_pdp_st;
+        }
+#ifdef DEBUG
+        fprintf(stderr, "Initialize CDP_val for RRA %d DS %d: %10.2f\n",
+                i, ii, pdp_temp_val);
+#endif
+        return pdp_temp_val;
+    }
+    if (current_cf == CF_AVERAGE)
+        return cdp_val + pdp_temp_val * elapsed_pdp_st;
+    if (current_cf == CF_MINIMUM)
+        return (pdp_temp_val < cdp_val) ? pdp_temp_val : cdp_val;
+    if (current_cf == CF_MAXIMUM)
+        return (pdp_temp_val > cdp_val) ? pdp_temp_val : cdp_val;
+
+    return pdp_temp_val;
+}
+
+/*
+ * For each RRA, update the seasonal values and then call update_aberrant_CF
+ * for each data source.
+ *
+ * Return 0 on success, -1 on error.
+ */
+static int update_aberrant_cdps(
+    rrd_t *rrd,
+    rrd_file_t *rrd_file,
+    unsigned long rra_begin,
+    unsigned long elapsed_pdp_st,
+    rrd_value_t *pdp_temp,
+    rrd_value_t **seasonal_coef)
+{
+    unsigned long rra_idx, ds_idx, j;
+
+    /* number of PDP steps since the last update that
+     * are assigned to the first CDP to be generated
+     * since the last update. */
+    unsigned short scratch_idx;
+    unsigned long rra_start;
+    enum cf_en current_cf;
+
+    /* this loop is only entered if elapsed_pdp_st < 3 */
+    for (j = elapsed_pdp_st, scratch_idx = CDP_primary_val;
+         j > 0 && j < 3; j--, scratch_idx = CDP_secondary_val) {
+        rra_start = rra_begin;
+        for (rra_idx = 0; rra_idx < rrd->stat_head->rra_cnt; rra_idx++) {
+            if (rrd->rra_def[rra_idx].pdp_cnt == 1) {
+                current_cf = cf_conv(rrd->rra_def[rra_idx].cf_nam);
+                if (current_cf == CF_SEASONAL || current_cf == CF_DEVSEASONAL) {
+                    if (scratch_idx == CDP_primary_val) {
+                        lookup_seasonal(rrd, rra_idx, rra_start, rrd_file,
+                                        elapsed_pdp_st + 1, seasonal_coef);
+                    } else {
+                        lookup_seasonal(rrd, rra_idx, rra_start, rrd_file,
+                                        elapsed_pdp_st + 2, seasonal_coef);
+                    }
+                }
+                if (rrd_test_error())
+                    return -1;
+                /* loop over data soures within each RRA */
+                for (ds_idx = 0; ds_idx < rrd->stat_head->ds_cnt; ds_idx++) {
+                    update_aberrant_CF(rrd, pdp_temp[ds_idx], current_cf,
+                                       rra_idx * (rrd->stat_head->ds_cnt) +
+                                       ds_idx, rra_idx, ds_idx, scratch_idx,
+                                       *seasonal_coef);
+                }
+            }
+            rra_start += rrd->rra_def[rra_idx].row_cnt
+                * rrd->stat_head->ds_cnt * sizeof(rrd_value_t);
+        }
+    }
+    return 0;
+}
+
+/* 
+ * Move sequentially through the file, writing one RRA at a time.  Note this
+ * architecture divorces the computation of CDP with flushing updated RRA
+ * entries to disk.
+ *
+ * Return 0 on success, -1 on error.
+ */
+static int write_to_rras(
+    rrd_t *rrd,
+    rrd_file_t *rrd_file,
+    unsigned long *rra_step_cnt,
+    unsigned long rra_begin,
+    time_t current_time,
+    unsigned long *skip_update,
+    rrd_info_t ** pcdp_summary)
+{
+    unsigned long rra_idx;
+    unsigned long rra_start;
+    time_t    rra_time = 0; /* time of update for a RRA */
+
+    unsigned long ds_cnt = rrd->stat_head->ds_cnt;
+    
+    /* Ready to write to disk */
+    rra_start = rra_begin;
+
+    for (rra_idx = 0; rra_idx < rrd->stat_head->rra_cnt; rra_idx++) {
+        rra_def_t *rra_def = &rrd->rra_def[rra_idx];
+        rra_ptr_t *rra_ptr = &rrd->rra_ptr[rra_idx];
+
+        /* for cdp_prep */
+        unsigned short scratch_idx;
+        unsigned long step_subtract;
+
+        for (scratch_idx = CDP_primary_val,
+                 step_subtract = 1;
+             rra_step_cnt[rra_idx] > 0;
+             rra_step_cnt[rra_idx]--,
+                 scratch_idx = CDP_secondary_val,
+                 step_subtract = 2) {
+
+            size_t rra_pos_new;
+#ifdef DEBUG
+            fprintf(stderr, "  -- RRA Preseek %ld\n", rrd_file->pos);
+#endif
+            /* increment, with wrap-around */
+            if (++rra_ptr->cur_row >= rra_def->row_cnt)
+              rra_ptr->cur_row = 0;
+
+            /* we know what our position should be */
+            rra_pos_new = rra_start
+              + ds_cnt * rra_ptr->cur_row * sizeof(rrd_value_t);
+
+            /* re-seek if the position is wrong or we wrapped around */
+            if ((size_t)rra_pos_new != rrd_file->pos) {
+                if (rrd_seek(rrd_file, rra_pos_new, SEEK_SET) != 0) {
+                    rrd_set_error("seek error in rrd");
+                    return -1;
+                }
+            }
+#ifdef DEBUG
+            fprintf(stderr, "  -- RRA Postseek %ld\n", rrd_file->pos);
+#endif
+
+            if (skip_update[rra_idx])
+                continue;
+
+            if (*pcdp_summary != NULL) {
+                unsigned long step_time = rra_def->pdp_cnt * rrd->stat_head->pdp_step;
+
+                rra_time = (current_time - current_time % step_time)
+                    - ((rra_step_cnt[rra_idx] - step_subtract) * step_time);
+            }
+
+            if (write_RRA_row
+                (rrd_file, rrd, rra_idx, scratch_idx,
+                 pcdp_summary, rra_time) == -1)
+                return -1;
+
+            rrd_notify_row(rrd_file, rra_idx, rra_pos_new, rra_time);
+        }
+
+        rra_start += rra_def->row_cnt * ds_cnt * sizeof(rrd_value_t);
+    } /* RRA LOOP */
+
+    return 0;
+}
+
+/*
+ * Write out one row of values (one value per DS) to the archive.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static int write_RRA_row(
+    rrd_file_t *rrd_file,
+    rrd_t *rrd,
+    unsigned long rra_idx,
+    unsigned short CDP_scratch_idx,
+    rrd_info_t ** pcdp_summary,
+    time_t rra_time)
+{
+    unsigned long ds_idx, cdp_idx;
+    rrd_infoval_t iv;
+
+    for (ds_idx = 0; ds_idx < rrd->stat_head->ds_cnt; ds_idx++) {
+        /* compute the cdp index */
+        cdp_idx = rra_idx * (rrd->stat_head->ds_cnt) + ds_idx;
+#ifdef DEBUG
+        fprintf(stderr, "  -- RRA WRITE VALUE %e, at %ld CF:%s\n",
+                rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val,
+                rrd_file->pos, rrd->rra_def[rra_idx].cf_nam);
+#endif
+        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 = rrd_info_push(*pcdp_summary,
+                                          sprintf_alloc
+                                          ("[%lli]RRA[%s][%lu]DS[%s]", 
+                                           (long long)rra_time,
+                                           rrd->rra_def[rra_idx].cf_nam,
+                                           rrd->rra_def[rra_idx].pdp_cnt,
+                                           rrd->ds_def[ds_idx].ds_nam),
+                                           RD_I_VAL, iv);
+        }
+        errno = 0;
+        if (rrd_write(rrd_file,
+                      &(rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].
+                        u_val), sizeof(rrd_value_t)) != sizeof(rrd_value_t)) {
+            rrd_set_error("writing rrd: %s", rrd_strerror(errno));
+            return -1;
+        }
+    }
+    return 0;
+}
+
+/*
+ * Call apply_smoother for all DEVSEASONAL and SEASONAL RRAs.
+ *
+ * Returns 0 on success, -1 otherwise
+ */
+static int smooth_all_rras(
+    rrd_t *rrd,
+    rrd_file_t *rrd_file,
+    unsigned long rra_begin)
+{
+    unsigned long rra_start = rra_begin;
+    unsigned long rra_idx;
+
+    for (rra_idx = 0; rra_idx < rrd->stat_head->rra_cnt; ++rra_idx) {
+        if (cf_conv(rrd->rra_def[rra_idx].cf_nam) == CF_DEVSEASONAL ||
+            cf_conv(rrd->rra_def[rra_idx].cf_nam) == CF_SEASONAL) {
+#ifdef DEBUG
+            fprintf(stderr, "Running smoother for rra %lu\n", rra_idx);
+#endif
+            apply_smoother(rrd, rra_idx, rra_start, rrd_file);
+            if (rrd_test_error())
+                return -1;
+        }
+        rra_start += rrd->rra_def[rra_idx].row_cnt
+            * rrd->stat_head->ds_cnt * sizeof(rrd_value_t);
+    }
+    return 0;
+}
+
+#ifndef HAVE_MMAP
+/*
+ * Flush changes to disk (unless we're using mmap)
+ *
+ * Returns 0 on success, -1 otherwise
+ */
+static int write_changes_to_disk(
+    rrd_t *rrd,
+    rrd_file_t *rrd_file,
+    int version)
+{
+    /* we just need to write back the live header portion now */
+    if (rrd_seek(rrd_file, (sizeof(stat_head_t)
+                            + sizeof(ds_def_t) * rrd->stat_head->ds_cnt
+                            + sizeof(rra_def_t) * rrd->stat_head->rra_cnt),
+                 SEEK_SET) != 0) {
+        rrd_set_error("seek rrd for live header writeback");
+        return -1;
+    }
+    if (version >= 3) {
+        if (rrd_write(rrd_file, rrd->live_head,
+                      sizeof(live_head_t) * 1) != sizeof(live_head_t) * 1) {
+            rrd_set_error("rrd_write live_head to rrd");
+            return -1;
+        }
+    } else {
+        if (rrd_write(rrd_file, rrd->legacy_last_up,
+                      sizeof(time_t) * 1) != sizeof(time_t) * 1) {
+            rrd_set_error("rrd_write live_head to rrd");
+            return -1;
+        }
+    }
+
+
+    if (rrd_write(rrd_file, rrd->pdp_prep,
+                  sizeof(pdp_prep_t) * rrd->stat_head->ds_cnt)
+        != (ssize_t) (sizeof(pdp_prep_t) * rrd->stat_head->ds_cnt)) {
+        rrd_set_error("rrd_write pdp_prep to rrd");
+        return -1;
+    }
+
+    if (rrd_write(rrd_file, rrd->cdp_prep,
+                  sizeof(cdp_prep_t) * rrd->stat_head->rra_cnt *
+                  rrd->stat_head->ds_cnt)
+        != (ssize_t) (sizeof(cdp_prep_t) * rrd->stat_head->rra_cnt *
+                      rrd->stat_head->ds_cnt)) {
+
+        rrd_set_error("rrd_write cdp_prep to rrd");
+        return -1;
+    }
+
+    if (rrd_write(rrd_file, rrd->rra_ptr,
+                  sizeof(rra_ptr_t) * rrd->stat_head->rra_cnt)
+        != (ssize_t) (sizeof(rra_ptr_t) * rrd->stat_head->rra_cnt)) {
+        rrd_set_error("rrd_write rra_ptr to rrd");
+        return -1;
+    }
+    return 0;
+}
+#endif
diff --git a/program/src/rrd_utils.c b/program/src/rrd_utils.c
new file mode 100644 (file)
index 0000000..3936cff
--- /dev/null
@@ -0,0 +1,159 @@
+/**
+ * RRDtool - src/rrd_utils.c
+ * Copyright (C) 2009 Kevin Brintnall
+ * Copyright (C) 2008 Sebastian Harl
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   kevin brintnall <kbrint@rufus.net>
+ *   Sebastian Harl <sh@tokkee.org>
+ **/
+
+#include "rrd_tool.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#ifdef WIN32
+#      define random() rand()
+#      define srandom(x) srand(x)
+#      define getpid() 0
+#endif /* WIN32 */
+
+/* make sure that the random number generator seeded EXACTLY ONCE */
+long rrd_random(void)
+{
+    static int rand_init = 0;
+    if (!rand_init) {
+        srandom((unsigned int) time(NULL) + (unsigned int) getpid());
+        rand_init++;
+    }
+
+    return random();
+}
+
+/* rrd_add_ptr: add a pointer to a dynamically sized array of pointers,
+ * realloc as necessary.  returns 1 on success, 0 on failure.
+ */
+
+int rrd_add_ptr(void ***dest, size_t *dest_size, void *src)
+{
+    void **temp;
+
+    assert(dest != NULL);
+
+    temp = (void **) rrd_realloc(*dest, (*dest_size+1) * sizeof(*dest));
+    if (!temp)
+        return 0;
+
+    *dest = temp;
+    temp[*dest_size] = src;
+    (*dest_size)++;
+
+    return 1;
+}
+
+/* like rrd_add_ptr, but calls strdup() on a string first. */
+int rrd_add_strdup(char ***dest, size_t *dest_size, char *src)
+{
+    char *dup_src;
+    int add_ok;
+
+    assert(dest != NULL);
+    assert(src  != NULL);
+
+    dup_src = strdup(src);
+    if (!dup_src)
+        return 0;
+
+    add_ok = rrd_add_ptr((void ***)dest, dest_size, (void *)dup_src);
+    if (!add_ok)
+        free(dup_src);
+
+    return add_ok;
+}
+
+void rrd_free_ptrs(void ***src, size_t *cnt)
+{
+    void **sp;
+
+    assert(src != NULL);
+    sp = *src;
+
+    if (sp == NULL)
+        return;
+
+    while (*cnt > 0) {
+        (*cnt)--;
+        free(sp[*cnt]);
+    }
+
+    free (sp);
+    *src = NULL;
+}
+
+/* recursively create the directory named by 'pathname'
+ * (similar to "mkdir -p" on the command line) */
+int rrd_mkdir_p(const char *pathname, mode_t mode)
+{
+    struct stat sb;
+
+    char *pathname_copy;
+    char *base_dir;
+
+    if ((NULL == pathname) || ('\0' == *pathname)) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    if (0 == stat(pathname, &sb)) {
+        if (! S_ISDIR(sb.st_mode)) {
+            errno = ENOTDIR;
+            return -1;
+        }
+        return 0;
+    }
+
+    /* keep errno as set by stat() */
+    if (ENOENT != errno)
+        return -1;
+
+    /* dirname might modify its first argument */
+    if (NULL == (pathname_copy = strdup(pathname)))
+        return -1;
+
+    base_dir = dirname(pathname_copy);
+
+    if (0 != rrd_mkdir_p(base_dir, mode)) {
+        int orig_errno = errno;
+        free(pathname_copy);
+        errno = orig_errno;
+        return -1;
+    }
+
+    free(pathname_copy);
+
+    /* keep errno as set by mkdir() */
+    if (0 != mkdir(pathname, mode))
+        return -1;
+    return 0;
+} /* rrd_mkdir_p */
+
diff --git a/program/src/rrd_version.c b/program/src/rrd_version.c
new file mode 100644 (file)
index 0000000..13f8c0d
--- /dev/null
@@ -0,0 +1,21 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrd_version Return
+ *****************************************************************************
+ * Initial version by Burton Strauss, ntopSupport.com - 5/2005
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+
+double rrd_version(
+    void)
+{
+    return NUMVERS;
+}
+
+char     *rrd_strversion(
+    void)
+{
+    return PACKAGE_VERSION;
+}
diff --git a/program/src/rrd_xport.c b/program/src/rrd_xport.c
new file mode 100644 (file)
index 0000000..063982e
--- /dev/null
@@ -0,0 +1,323 @@
+/****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ ****************************************************************************
+ * rrd_xport.c  export RRD data 
+ ****************************************************************************/
+
+#include <sys/stat.h>
+
+#include "rrd_tool.h"
+#include "rrd_graph.h"
+#include "rrd_xport.h"
+#include "unused.h"
+#include "rrd_client.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#include <io.h>
+#include <fcntl.h>
+#endif
+
+
+int       rrd_xport(
+    int,
+    char **,
+    int *,
+    time_t *,
+    time_t *,
+    unsigned long *,
+    unsigned long *,
+    char ***,
+    rrd_value_t **);
+
+int       rrd_xport_fn(
+    image_desc_t *,
+    time_t *,
+    time_t *,
+    unsigned long *,
+    unsigned long *,
+    char ***,
+    rrd_value_t **);
+
+
+
+
+int rrd_xport(
+    int argc,
+    char **argv,
+    int UNUSED(*xsize),
+    time_t *start,
+    time_t *end,        /* which time frame do you want ?
+                         * will be changed to represent reality */
+    unsigned long *step,    /* which stepsize do you want? 
+                             * will be changed to represent reality */
+    unsigned long *col_cnt, /* number of data columns in the result */
+    char ***legend_v,   /* legend entries */
+    rrd_value_t **data)
+{                       /* two dimensional array containing the data */
+    image_desc_t im;
+    time_t    start_tmp = 0, end_tmp = 0;
+    rrd_time_value_t start_tv, end_tv;
+    char     *parsetime_error = NULL;
+    char     *opt_daemon = NULL;
+
+    struct option long_options[] = {
+        {"start", required_argument, 0, 's'},
+        {"end", required_argument, 0, 'e'},
+        {"maxrows", required_argument, 0, 'm'},
+        {"step", required_argument, 0, 261},
+        {"enumds", no_argument, 0, 262},    /* these are handled in the frontend ... */
+        {"daemon", required_argument, 0, 'd'},
+        {0, 0, 0, 0}
+    };
+
+    optind = 0;
+    opterr = 0;         /* initialize getopt */
+
+    rrd_graph_init(&im);
+
+    rrd_parsetime("end-24h", &start_tv);
+    rrd_parsetime("now", &end_tv);
+
+    while (1) {
+        int       option_index = 0;
+        int       opt;
+
+        opt = getopt_long(argc, argv, "s:e:m:d:", long_options, &option_index);
+
+        if (opt == EOF)
+            break;
+
+        switch (opt) {
+        case 261:
+            im.step = atoi(optarg);
+            break;
+        case 262:
+            break;
+        case 's':
+            if ((parsetime_error = rrd_parsetime(optarg, &start_tv))) {
+                rrd_set_error("start time: %s", parsetime_error);
+                return -1;
+            }
+            break;
+        case 'e':
+            if ((parsetime_error = rrd_parsetime(optarg, &end_tv))) {
+                rrd_set_error("end time: %s", parsetime_error);
+                return -1;
+            }
+            break;
+        case 'm':
+            im.xsize = atol(optarg);
+            if (im.xsize < 10) {
+                rrd_set_error("maxrows below 10 rows");
+                return -1;
+            }
+            break;
+        case 'd':
+        {
+            if (opt_daemon != NULL)
+            {
+                rrd_set_error ("You cannot specify --daemon "
+                        "more than once.");
+                return (-1);
+            }
+
+            opt_daemon = strdup(optarg);
+            if (opt_daemon == NULL)
+            {
+                rrd_set_error("strdup error");
+                return -1;
+            }
+            break;
+        }
+
+        case '?':
+            rrd_set_error("unknown option '%s'", argv[optind - 1]);
+            return -1;
+        }
+    }
+
+    if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+        return -1;
+    }
+
+    if (start_tmp < 3600 * 24 * 365 * 10) {
+        rrd_set_error("the first entry to fetch should be after 1980 (%ld)",
+                      start_tmp);
+        return -1;
+    }
+
+    if (end_tmp < start_tmp) {
+        rrd_set_error("start (%ld) should be less than end (%ld)",
+                      start_tmp, end_tmp);
+        return -1;
+    }
+
+    im.start = start_tmp;
+    im.end = end_tmp;
+    im.step = max((long) im.step, (im.end - im.start) / im.xsize);
+
+    rrd_graph_script(argc, argv, &im, 0);
+    if (rrd_test_error()) {
+        im_free(&im);
+        return -1;
+    }
+
+    if (im.gdes_c == 0) {
+        rrd_set_error("can't make an xport without contents");
+        im_free(&im);
+        return (-1);
+    }
+
+    {   /* try to connect to rrdcached */
+        int status = rrdc_connect(opt_daemon);
+        if (opt_daemon) free(opt_daemon);
+        if (status != 0) return status;
+    }
+
+    if (rrd_xport_fn(&im, start, end, step, col_cnt, legend_v, data) == -1) {
+        im_free(&im);
+        return -1;
+    }
+
+    im_free(&im);
+    return 0;
+}
+
+
+
+int rrd_xport_fn(
+    image_desc_t *im,
+    time_t *start,
+    time_t *end,        /* which time frame do you want ?
+                         * will be changed to represent reality */
+    unsigned long *step,    /* which stepsize do you want? 
+                             * will be changed to represent reality */
+    unsigned long *col_cnt, /* number of data columns in the result */
+    char ***legend_v,   /* legend entries */
+    rrd_value_t **data)
+{                       /* two dimensional array containing the data */
+
+    int       i = 0, j = 0;
+    unsigned long dst_row, row_cnt;
+    rrd_value_t  *dstptr;
+
+    unsigned long xport_counter = 0;
+    int      *ref_list;
+    long     *step_list;
+    long     *step_list_ptr;    
+    char    **legend_list;
+
+
+    /* pull the data from the rrd files ... */
+    if (data_fetch(im) == -1)
+        return -1;
+
+    /* evaluate CDEF  operations ... */
+    if (data_calc(im) == -1)
+        return -1;
+
+    /* how many xports? */
+    *col_cnt = 0;    
+    for (i = 0; i < im->gdes_c; i++) {
+        switch (im->gdes[i].gf) {
+        case GF_XPORT:
+            (*col_cnt)++;
+            break;
+        default:
+            break;
+        }
+    }
+    if ((*col_cnt) == 0) {
+        rrd_set_error("no XPORT found, nothing to do");
+        return -1;
+    }
+
+    /* a list of referenced gdes */
+    ref_list = (int*)malloc(sizeof(int) * (*col_cnt));
+    if (ref_list == NULL)
+        return -1;
+
+    /* a list to save pointers to the column's legend entry */
+    /* this is a return value! */
+    legend_list = (char**)malloc(sizeof(char *) * (*col_cnt));
+    if (legend_list == NULL) {
+        free(ref_list);
+        return -1;
+    }
+
+    /* lets find the step size we have to use for xport */
+    step_list = (long*)malloc(sizeof(long)*((*col_cnt)+1));
+    step_list_ptr = step_list;
+    j = 0;
+    for (i = 0; i < im->gdes_c; i++) {
+        switch (im->gdes[i].gf) {
+        case GF_XPORT:
+            ref_list[xport_counter++] = i;
+            *step_list_ptr = im->gdes[im->gdes[i].vidx].step;
+            /* printf("%s:%lu\n",im->gdes[i].legend,*step_list_ptr); */
+            step_list_ptr++;
+            /* reserve room for one legend entry */
+            /* is FMT_LEG_LEN + 5 the correct size? */
+            if ((legend_list[j] =
+                (char*)malloc(sizeof(char) * (FMT_LEG_LEN + 5))) == NULL) {
+                free(ref_list);
+                *data = NULL;
+                while (--j > -1)
+                    free(legend_list[j]);
+                free(legend_list);
+                rrd_set_error("malloc xport legend entry");
+                return (-1);
+            }
+
+            if (im->gdes[i].legend)
+                /* omit bounds check, should have the same size */
+                strcpy(legend_list[j++], im->gdes[i].legend);
+            else
+                legend_list[j++][0] = '\0';
+            break;
+        default:
+            break;            
+        }
+    }
+    *step_list_ptr=0;    
+    /* find a common step */
+    *step = lcd(step_list);
+    /* printf("step: %lu\n",*step); */
+    free(step_list);
+    
+    *start =  im->start - im->start % (*step);
+    *end = im->end - im->end % (*step) + (*step);
+    
+
+    /* room for rearranged data */
+    /* this is a return value! */
+    row_cnt = ((*end) - (*start)) / (*step);
+    if (((*data) =
+        (rrd_value_t*)malloc((*col_cnt) * row_cnt * sizeof(rrd_value_t))) == NULL) {
+        free(ref_list);
+        free(legend_list);
+        rrd_set_error("malloc xport data area");
+        return (-1);
+    }
+    dstptr = (*data);
+
+    /* fill data structure */
+    for (dst_row = 0; (int) dst_row < (int) row_cnt; dst_row++) {
+        for (i = 0; i < (int) (*col_cnt); i++) {
+            long vidx = im->gdes[ref_list[i]].vidx;
+            time_t now = *start + dst_row * *step;
+            (*dstptr++) = im->gdes[vidx].data[(unsigned long)
+                                              floor((double)
+                                                    (now - im->gdes[vidx].start)
+                                                    /im->gdes[vidx].step)
+                                              * im->gdes[vidx].ds_cnt +
+                                              im->gdes[vidx].ds];
+
+        }
+    }
+
+    *legend_v = legend_list;
+    free(ref_list);
+    return 0;
+
+}
diff --git a/program/src/rrd_xport.h b/program/src/rrd_xport.h
new file mode 100644 (file)
index 0000000..f7d5fdc
--- /dev/null
@@ -0,0 +1,34 @@
+/****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ ****************************************************************************
+ * rrd_xport.h  contains XML related constants
+ ****************************************************************************/
+#ifdef  __cplusplus
+extern    "C" {
+#endif
+
+#ifndef _RRD_XPORT_H
+#define _RRD_XPORT_H
+
+#define XML_ENCODING     "ISO-8859-1"
+#define ROOT_TAG         "xport"
+#define META_TAG         "meta"
+#define META_START_TAG   "start"
+#define META_STEP_TAG    "step"
+#define META_END_TAG     "end"
+#define META_ROWS_TAG    "rows"
+#define META_COLS_TAG    "columns"
+#define LEGEND_TAG       "legend"
+#define LEGEND_ENTRY_TAG "entry"
+#define DATA_TAG         "data"
+#define DATA_ROW_TAG     "row"
+#define COL_TIME_TAG     "t"
+#define COL_DATA_TAG     "v"
+
+
+#endif
+
+
+#ifdef  __cplusplus
+}
+#endif
diff --git a/program/src/rrdupdate.c b/program/src/rrdupdate.c
new file mode 100644 (file)
index 0000000..ad1d77b
--- /dev/null
@@ -0,0 +1,38 @@
+/*****************************************************************************
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *****************************************************************************
+ * rrdupdate.c  Main program for the (standalone) rrdupdate utility
+ *****************************************************************************
+ * $Id$
+ *****************************************************************************/
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__) && !defined(HAVE_CONFIG_H)
+#include "../win32/config.h"
+#else
+#ifdef HAVE_CONFIG_H
+#include "../rrd_config.h"
+#endif
+#endif
+
+#include "rrd.h"
+
+int main(
+    int argc,
+    char **argv)
+{
+    rrd_update(argc, argv);
+    if (rrd_test_error()) {
+        printf("RRDtool " PACKAGE_VERSION
+               "  Copyright by Tobi Oetiker, 1997-2008\n\n"
+               "Usage: rrdupdate filename\n"
+               "\t\t\t[--template|-t ds-name:ds-name:...]\n"
+               "\t\t\ttime|N:value[:value...]\n\n"
+               "\t\t\tat-time@value[:value...]\n\n"
+               "\t\t\t[ time:value[:value...] ..]\n\n");
+
+        printf("ERROR: %s\n", rrd_get_error());
+        rrd_clear_error();
+        return 1;
+    }
+    return 0;
+}
diff --git a/program/src/strftime.c b/program/src/strftime.c
new file mode 100644 (file)
index 0000000..3928a6f
--- /dev/null
@@ -0,0 +1,359 @@
+/**
+ *
+ * strftime.c
+ *
+ * implements the ansi c function strftime()
+ *
+ * written 6 september 1989 by jim nutt
+ * released into the public domain by jim nutt
+ *
+ * modified 21-Oct-89 by Rob Duff
+ *
+ * modified 08-Dec-04 by Tobi Oetiker (added %V)
+**/
+
+#include <stddef.h>     /* for size_t */
+#include <stdarg.h>     /* for va_arg */
+#include <time.h>       /* for struct tm */
+#include "strftime.h"
+
+/* Define your own defaults in config.h if necessary */
+#if defined(TZNAME_STD) && defined(TZNAME_DST)
+char     *tzname_[2] = { TZNAME_STD, TZNAME_DST };
+#else
+#define tzname_ tzname
+#endif
+
+static char *aday[] = {
+    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+static char *day[] = {
+    "Sunday", "Monday", "Tuesday", "Wednesday",
+    "Thursday", "Friday", "Saturday"
+};
+
+static char *amonth[] = {
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+static char *month[] = {
+    "January", "February", "March", "April", "May", "June",
+    "July", "August", "September", "October", "November", "December"
+};
+
+static char buf[26];
+
+static void strfmt(
+    char *str,
+    const char *fmt,
+    ...);
+
+/**
+ *
+ * size_t strftime_(char *str,
+ *                  size_t maxs,
+ *                  const char *fmt,
+ *                  const struct tm *t)
+ *
+ *      this functions acts much like a sprintf for time/date output.
+ *      given a pointer to an output buffer, a format string and a
+ *      time, it copies the time to the output buffer formatted in
+ *      accordance with the format string.  the parameters are used
+ *      as follows:
+ *
+ *          str is a pointer to the output buffer, there should
+ *          be at least maxs characters available at the address
+ *          pointed to by str.
+ *
+ *          maxs is the maximum number of characters to be copied
+ *          into the output buffer, included the '\0' terminator
+ *
+ *          fmt is the format string.  a percent sign (%) is used
+ *          to indicate that the following character is a special
+ *          format character.  the following are valid format
+ *          characters:
+ *
+ *              %A      full weekday name (Monday)
+ *              %a      abbreviated weekday name (Mon)
+ *              %B      full month name (January)
+ *              %b      abbreviated month name (Jan)
+ *              %c      standard date and time representation
+ *              %d      day-of-month (01-31)
+ *              %H      hour (24 hour clock) (00-23)
+ *              %I      hour (12 hour clock) (01-12)
+ *              %j      day-of-year (001-366)
+ *              %M      minute (00-59)
+ *              %m      month (01-12)
+ *              %p      local equivalent of AM or PM
+ *              %S      second (00-59)
+ *              %U      week-of-year, first day sunday (00-53)
+ *              %W      week-of-year, first day monday (00-53)
+ *              %V      ISO 8601 Week number 
+ *              %w      weekday (0-6, sunday is 0)
+ *              %X      standard time representation
+ *              %x      standard date representation
+ *              %Y      year with century
+ *              %y      year without century (00-99)
+ *              %Z      timezone name
+ *              %%      percent sign
+ *
+ *      the standard date string is equivalent to:
+ *
+ *          %a %b %d %Y
+ *
+ *      the standard time string is equivalent to:
+ *
+ *          %H:%M:%S
+ *
+ *      the standard date and time string is equivalent to:
+ *
+ *          %a %b %d %H:%M:%S %Y
+ *
+ *      strftime_() returns the number of characters placed in the
+ *      buffer, not including the terminating \0, or zero if more
+ *      than maxs characters were produced.
+ *
+**/
+
+size_t strftime_(
+    char *s,
+    size_t maxs,
+    const char *f,
+    const struct tm *t)
+{
+    int       w, d;
+    char     *p, *q, *r;
+
+    p = s;
+    q = s + maxs - 1;
+    while ((*f != '\0')) {
+        if (*f++ == '%') {
+            r = buf;
+            switch (*f++) {
+            case '%':
+                r = "%";
+                break;
+
+            case 'a':
+                r = aday[t->tm_wday];
+                break;
+
+            case 'A':
+                r = day[t->tm_wday];
+                break;
+
+            case 'b':
+                r = amonth[t->tm_mon];
+                break;
+
+            case 'B':
+                r = month[t->tm_mon];
+                break;
+
+            case 'c':
+                strfmt(r, "%0 %0 %2 %2:%2:%2 %4",
+                       aday[t->tm_wday], amonth[t->tm_mon],
+                       t->tm_mday, t->tm_hour, t->tm_min,
+                       t->tm_sec, t->tm_year + 1900);
+                break;
+
+            case 'd':
+                strfmt(r, "%2", t->tm_mday);
+                break;
+
+            case 'H':
+                strfmt(r, "%2", t->tm_hour);
+                break;
+
+            case 'I':
+                strfmt(r, "%2", (t->tm_hour % 12) ? t->tm_hour % 12 : 12);
+                break;
+
+            case 'j':
+                strfmt(r, "%3", t->tm_yday + 1);
+                break;
+
+            case 'm':
+                strfmt(r, "%2", t->tm_mon + 1);
+                break;
+
+            case 'M':
+                strfmt(r, "%2", t->tm_min);
+                break;
+
+            case 'p':
+                r = (t->tm_hour > 11) ? "PM" : "AM";
+                break;
+
+            case 'S':
+                strfmt(r, "%2", t->tm_sec);
+                break;
+
+            case 'U':
+                w = t->tm_yday / 7;
+                if (t->tm_yday % 7 > t->tm_wday)
+                    w++;
+                strfmt(r, "%2", w);
+                break;
+
+            case 'W':
+                w = t->tm_yday / 7;
+                if (t->tm_yday % 7 > (t->tm_wday + 6) % 7)
+                    w++;
+                strfmt(r, "%2", w);
+                break;
+
+            case 'V':
+
+                /* ISO 8601 Week Of Year:
+                   If the week (Monday - Sunday) containing January 1 has four or more
+                   days in the new year, then it is week 1; otherwise it is week 53 of
+                   the previous year and the next week is week one. */
+
+                w = (t->tm_yday + 7 - (t->tm_wday ? t->tm_wday - 1 : 6)) / 7;
+                d = (t->tm_yday + 7 - (t->tm_wday ? t->tm_wday - 1 : 6)) % 7;
+
+                if (d >= 4) {
+                    w++;
+                } else if (w == 0) {
+                    w = 53;
+                }
+                strfmt(r, "%2", w);
+                break;
+
+            case 'w':
+                strfmt(r, "%1", t->tm_wday);
+                break;
+
+            case 'x':
+                strfmt(r, "%3s %3s %2 %4", aday[t->tm_wday],
+                       amonth[t->tm_mon], t->tm_mday, t->tm_year + 1900);
+                break;
+
+            case 'X':
+                strfmt(r, "%2:%2:%2", t->tm_hour, t->tm_min, t->tm_sec);
+                break;
+
+            case 'y':
+                strfmt(r, "%2", t->tm_year % 100);
+                break;
+
+            case 'Y':
+                strfmt(r, "%4", t->tm_year + 1900);
+                break;
+
+            case 'Z':
+                r = (t->tm_isdst && tzname_[1][0]) ? tzname_[1] : tzname_[0];
+                break;
+
+            default:
+                buf[0] = '%';   /* reconstruct the format */
+                buf[1] = f[-1];
+                buf[2] = '\0';
+                if (buf[1] == 0)
+                    f--;    /* back up if at end of string */
+            }
+            while (*r) {
+                if (p == q) {
+                    *q = '\0';
+                    return 0;
+                }
+                *p++ = *r++;
+            }
+        } else {
+            if (p == q) {
+                *q = '\0';
+                return 0;
+            }
+            *p++ = f[-1];
+        }
+    }
+    *p = '\0';
+    return p - s;
+}
+
+/*
+ *  stdarg.h
+ *
+typedef void *va_list;
+#define va_start(vp,v) (vp=((char*)&v)+sizeof(v))
+#define va_arg(vp,t) (*((t*)(vp))++)
+#define va_end(vp)
+ *
+ */
+
+static int powers[5] = { 1, 10, 100, 1000, 10000 };
+
+/**
+ * static void strfmt(char *str, char *fmt);
+ *
+ * simple sprintf for strftime
+ *
+ * each format descriptor is of the form %n
+ * where n goes from zero to four
+ *
+ * 0    -- string %s
+ * 1..4 -- int %?.?d
+ *
+**/
+
+static void strfmt(
+    char *str,
+    const char *fmt,
+    ...)
+{
+    int       ival, ilen;
+    char     *sval;
+    va_list   vp;
+
+    va_start(vp, fmt);
+    while (*fmt) {
+        if (*fmt++ == '%') {
+            ilen = *fmt++ - '0';
+            if (ilen == 0) {    /* zero means string arg */
+                sval = va_arg(vp, char *);
+
+                while (*sval)
+                    *str++ = *sval++;
+            } else {    /* always leading zeros */
+
+                ival = va_arg(vp, int);
+
+                while (ilen) {
+                    ival %= powers[ilen--];
+                    *str++ = (char) ('0' + ival / powers[ilen]);
+                }
+            }
+        } else
+            *str++ = fmt[-1];
+    }
+    *str = '\0';
+    va_end(vp);
+}
+
+#ifdef TEST
+
+#include <stdio.h>      /* for printf */
+#include <time.h>       /* for strftime */
+
+char      test[80];
+
+int main(
+    int argc,
+    char *argv[])
+{
+    int       len;
+    char     *fmt;
+    time_t    now;
+
+    time(&now);
+
+    fmt = (argc == 1) ? "%I:%M %p\n%c\n" : argv[1];
+    len = strftime_(test, sizeof test, fmt, localtime(&now));
+    printf("%d: %s\n", len, test);
+    return !len;
+}
+
+#endif                          /* TEST */
diff --git a/program/src/strftime.h b/program/src/strftime.h
new file mode 100644 (file)
index 0000000..485dbdd
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+**  STRFTIME.H - For older compilers which lack strftime()
+**
+**  Note: To avoid name collision with newer compilers, the function name
+**         strftime_() is used.
+*/
+
+#ifndef STRFTIME__H
+#define STRFTIME__H
+
+#include <stddef.h>     /* for size_t */
+#include <time.h>       /* for struct tm */
+
+size_t    strftime_(
+    char *s,
+    size_t maxs,
+    const char *f,
+    const struct tm *t);
+
+#if defined(TZNAME_STD) && defined(TZNAME_DST)
+extern char *tzname_[2];
+#endif
+
+#endif                          /* STRFTIME__H */
diff --git a/program/src/unused.h b/program/src/unused.h
new file mode 100644 (file)
index 0000000..ecaa4de
--- /dev/null
@@ -0,0 +1,14 @@
+/* define a macro to wrap variables that would
+   otherwise generate UNUSED variable warnings
+   Note that GCC's attribute unused only supresses the warning, so
+   it is perfectly safe to declare something unused although it is not.
+*/
+
+#ifdef UNUSED
+#elif defined(__GNUC__)
+# define UNUSED(x) x __attribute__((unused))
+#elif defined(__LCLINT__)
+# define UNUSED(x) /*@unused@*/ x
+#else
+# define UNUSED(x) x
+#endif
diff --git a/program/src/win32comp.c b/program/src/win32comp.c
new file mode 100644 (file)
index 0000000..457e69e
--- /dev/null
@@ -0,0 +1,82 @@
+/* compatibility routines, non reentrant .... */
+
+#include <string.h>
+#include <time.h>
+
+struct tm *localtime_r(
+    const time_t *t,
+    struct tm *r)
+{
+    struct tm *temp;
+
+    temp = localtime(t);
+    memcpy(r, temp, sizeof(struct tm));
+    return (r);
+}
+
+struct tm *gmtime_r(
+    const time_t *t,
+    struct tm *r)
+{
+    struct tm *temp;
+
+    temp = gmtime(t);
+    memcpy(r, temp, sizeof(struct tm));
+    return r;
+}
+
+char     *ctime_r(
+    const time_t *t,
+    char *buf)
+{
+    char     *temp;
+
+    temp = asctime(localtime(t));
+    strcpy(buf, temp);
+    return (buf);
+}
+
+/*
+       s  
+       Points to the string from which to extract tokens. 
+
+       delim  
+       Points to a null-terminated set of delimiter characters. 
+
+       save_ptr
+       Is a value-return parameter used by strtok_r() to record its progress through s1. 
+*/
+
+
+char     *strtok_r(
+    char *s,
+    const char *delim,
+    char **save_ptr)
+{
+    char     *token;
+
+    if (s == NULL)
+        s = *save_ptr;
+
+    /* Scan leading delimiters.  */
+    s += strspn(s, delim);
+    if (*s == '\0') {
+        *save_ptr = s;
+        return NULL;
+    }
+
+    /* Find the end of the token.  */
+    token = s;
+    s = strpbrk(token, delim);
+    if (s == NULL) {
+        /* This token finishes the string.  */
+        *save_ptr = token;
+        while (**save_ptr != '\0')
+            (*save_ptr)++;
+    } else {
+        /* Terminate the token and make *SAVE_PTR point past it.  */
+        *s = '\0';
+        *save_ptr = s + 1;
+    }
+    return token;
+}
diff --git a/program/svn2cl.xsl b/program/svn2cl.xsl
new file mode 100644 (file)
index 0000000..f4226b5
--- /dev/null
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+
+   svn2cl.xsl - xslt stylesheet for converting svn log to a normal
+                changelog
+
+   This file is based on several implementations of this conversion
+   that I was not completely happy with and some other common
+   xslt constructs found on the web.
+
+   Copyright (C) 2004 Arthur de Jong.
+
+   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. Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in
+      the documentation and/or other materials provided with the
+      distribution.
+   3. The name of the author 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 ``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 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, WHETHER
+   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.
+
+-->
+
+<!--
+   TODO
+   - make external lookups of author names possible
+   - find a place for revision numbers
+   - mark deleted files as such
+   - combine paths
+   - make stripping of characters nicer
+-->
+
+<xsl:stylesheet
+  version="1.0"
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  xmlns="http://www.w3.org/1999/xhtml">
+
+ <xsl:output
+   method="text"
+   encoding="iso-8859-15"
+   media-type="text/plain"
+   omit-xml-declaration="yes"
+   standalone="yes"
+   indent="no" />
+
+ <xsl:strip-space elements="*" />
+
+ <!-- the prefix of pathnames to strip -->
+ <xsl:param name="strip-prefix" select="'/'" />
+
+ <!-- format one entry from the log -->
+ <xsl:template match="logentry">
+  <!-- date -->
+  <xsl:apply-templates select="date" />
+  <!-- two spaces -->
+  <xsl:text>  </xsl:text>
+  <!-- author's name -->
+  <xsl:apply-templates select="author" />
+  <!-- two newlines -->
+  <xsl:text>
+
+</xsl:text>
+  <!-- the log message -->
+  <xsl:apply-templates select="msg" />
+  <!-- another two newlines -->
+  <xsl:text>
+
+</xsl:text>
+ </xsl:template>
+
+ <!-- format date -->
+ <xsl:template match="date">
+  <xsl:variable name="date" select="normalize-space(.)" />
+  <xsl:value-of select="substring($date,1,10)" />
+  <xsl:text> </xsl:text>
+  <xsl:value-of select="substring($date,12,5)" />
+ </xsl:template>
+
+ <!-- format author -->
+ <xsl:template match="author">
+  <xsl:value-of select="normalize-space(.)" />
+ </xsl:template>
+
+ <!-- format log message -->
+ <xsl:template match="msg">
+  <!-- first line is indented (other indents are done in wrap template) -->
+  <xsl:text>   * </xsl:text>
+  <!-- get paths string -->
+  <xsl:variable name="paths">
+   <xsl:apply-templates select="../paths" />
+  </xsl:variable>
+  <!-- print the paths and message nicely wrapped -->
+  <xsl:call-template name="wrap">
+   <xsl:with-param name="txt" select="concat($paths,': ',normalize-space(.))" />
+  </xsl:call-template>
+ </xsl:template>
+
+ <!-- present paths nice -->
+ <xsl:template match="paths">
+  <xsl:for-each select="path">
+   <xsl:sort select="normalize-space(.)" data-type="text" />
+   <xsl:if test="not(position()=1)">
+    <xsl:text>, </xsl:text>
+   </xsl:if>
+   <xsl:variable name="p1" select="normalize-space(.)" />
+   <xsl:variable name="p2">
+    <xsl:choose>
+     <xsl:when test="starts-with($p1,'/')">
+      <xsl:value-of select="substring($p1,2)" />
+     </xsl:when>
+     <xsl:otherwise>
+      <xsl:value-of select="$p1" />
+     </xsl:otherwise>
+    </xsl:choose>
+   </xsl:variable>
+   <xsl:variable name="p3">
+    <xsl:choose>
+     <xsl:when test="starts-with($p2,$strip-prefix)">
+      <xsl:value-of select="substring($p2,1+string-length($strip-prefix))" />
+     </xsl:when>
+     <xsl:otherwise>
+      <xsl:value-of select="$p2" />
+     </xsl:otherwise>
+    </xsl:choose>
+   </xsl:variable>
+   <xsl:variable name="p4">
+    <xsl:choose>
+     <xsl:when test="starts-with($p3,'/')">
+      <xsl:value-of select="substring($p3,2)" />
+     </xsl:when>
+     <xsl:otherwise>
+      <xsl:value-of select="$p3" />
+     </xsl:otherwise>
+    </xsl:choose>
+   </xsl:variable>
+   <xsl:choose>
+    <xsl:when test="$p4 = ''">
+     <xsl:value-of select="'.'" />
+    </xsl:when>
+    <xsl:otherwise>
+     <xsl:value-of select="$p4" />
+    </xsl:otherwise>
+   </xsl:choose>
+  </xsl:for-each>
+ </xsl:template>
+
+ <!-- string-wrapping template -->
+ <xsl:template name="wrap">
+  <xsl:param name="txt" />
+  <xsl:variable name="linelen" select="67" />
+  <xsl:choose>
+   <xsl:when test="(string-length($txt) &lt; $linelen) or not(contains($txt,' '))">
+    <!-- this is easy, nothing to do -->
+    <xsl:value-of select="$txt" />
+   </xsl:when>
+   <xsl:otherwise>
+    <!-- find the first line -->
+    <xsl:variable name="tmp" select="substring($txt,1,$linelen)" />
+    <xsl:variable name="line">
+     <xsl:choose>
+      <xsl:when test="contains($tmp,' ')">
+       <xsl:call-template name="find-line">
+        <xsl:with-param name="txt" select="$tmp" />
+       </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise>
+       <xsl:value-of select="substring-before($txt,' ')" />
+      </xsl:otherwise>
+     </xsl:choose>
+    </xsl:variable>
+    <!-- print line and newline -->
+    <xsl:value-of select="$line" />
+    <xsl:text>
+         </xsl:text>
+    <!-- wrap the rest of the text -->
+    <xsl:call-template name="wrap">
+     <xsl:with-param name="txt" select="normalize-space(substring($txt,string-length($line)+1))" />
+    </xsl:call-template>
+   </xsl:otherwise>
+  </xsl:choose>
+ </xsl:template>
+
+ <!-- template to trim line to contain space as last char -->
+ <xsl:template name="find-line">
+  <xsl:param name="txt" />
+  <xsl:choose>
+   <xsl:when test="substring($txt,string-length($txt),1) = ' '">
+    <xsl:value-of select="normalize-space($txt)" />
+   </xsl:when>
+   <xsl:otherwise>
+    <xsl:call-template name="find-line">
+     <xsl:with-param name="txt" select="substring($txt,1,string-length($txt)-1)" />
+    </xsl:call-template>
+   </xsl:otherwise>
+  </xsl:choose>
+ </xsl:template>
+
+</xsl:stylesheet>
diff --git a/program/win32/Makefile b/program/win32/Makefile
new file mode 100644 (file)
index 0000000..8c392d5
--- /dev/null
@@ -0,0 +1,460 @@
+# Gnu Makefile for Win32 target
+# for use with MingW32 gcc or Metrowerks CodeWarrior compiler
+# use with: make -f Makefile [help|all|clean|dev|devclean|dist|distclean]
+#
+# $id: $
+#
+
+DESCR  = Round Robin Database Tool
+COPYR  = Copyright (c) 1997-2007 by Tobias Oetiker
+WWWURL = http://www.rrdtool.org/
+ICON   = $(PROOT)/favicon.ico
+
+# You can set the default font used in graphs.
+# If not set here RRD defaults to DejaVuSansMono-Roman.ttf
+#RRD_DEFAULT_FONT = "arial.ttf"
+#RRD_DEFAULT_FONT = "VeraMono.ttf"
+
+# Vertical label angle: 90.0 (default) or 270.0
+RRDGRAPH_YLEGEND_ANGLE = 90.0
+
+# Set to one if you want to have piecharts.
+WITH_PIECHART = 0
+
+# Set the extension used for rrdcgi.
+ifndef CGIEXT
+CGIEXT = exe
+endif
+
+# Base for the lib sources
+ifndef LIBBASE
+LIBBASE        = ../..
+endif
+# All library code is statically linked to avoid problems with other lib DLLs.
+# Edit the path below to point to your libpng sources or set environment var.
+ifndef LIBPNG
+LIBPNG = $(LIBBASE)/libpng-1.2.16
+endif
+# Edit the path below to point to your freetype sources or set environment var.
+ifndef LIBFT2
+#LIBFT2        = $(LIBBASE)/freetype-2.3.4
+LIBFT2 = $(LIBBASE)/../mingw32/freetype-2.3.4
+endif
+# Edit the path below to point to your libart sources or set environment var.
+ifndef LIBART
+LIBART = $(LIBBASE)/libart_lgpl-2.3.17
+endif
+# Edit the path below to point to your zlib sources or set environment var.
+ifndef ZLIBSDK
+ZLIBSDK        = $(LIBBASE)/zlib-1.2.3
+endif
+
+# Edit the path below to point to your distribution folder.
+ifndef DISTDIR
+DISTDIR        = rrdtool-$(RRD_VERSION_STR)-w32
+endif
+DISTARC = $(DISTDIR).zip
+
+# Edit the path below to point to your distribution folder.
+ifndef DEVLDIR
+DEVLDIR        = rrdtool-$(RRD_VERSION_STR)-sdk-w32
+endif
+DEVLARC = $(DEVLDIR).zip
+
+# whatever...
+NO_NULL_REALLOC = 1
+
+# The following line defines your compiler.
+ifdef METROWERKS
+       CC = mwcc
+else
+       CC = gcc
+endif
+# RM   = rm -f
+CP     = cp -afv
+# Here you can find a native Win32 binary of the original awk:
+# http://www.gknw.net/development/prgtools/awk.zip
+AWK    = awk
+ZIP    = zip -qzr9
+
+# must be equal to DEBUG or NDEBUG
+DB     = NDEBUG
+# DB   = DEBUG
+# Optimization: -O<n> or debugging: -g
+ifeq ($(DB),NDEBUG)
+       OPT     = -O2
+       OBJDIR  = release
+else
+       OPT     = -g
+       OBJDIR  = debug
+endif
+
+# Project root
+PROOT  = ..
+
+# Include the version info retrieved from source.
+-include $(OBJDIR)/version.inc
+
+# Global flags for all compilers
+CFLAGS = $(OPT) -D$(DB) -DHAVE_CONFIG_H
+
+ifeq ($(CC),mwcc)
+LD     = mwld
+RC     = mwwinrc
+LDFLAGS        = -nostdlib
+AR     = $(LD)
+ARFLAGS        = -type library -w nocmdline $(OBJS) -o
+LIBEXT = lib
+LIBPATH        += -lr "$(METROWERKS)/MSL" -lr "$(METROWERKS)/Win32-x86 Support"
+LDLIBS += -lkernel32.lib -luser32.lib
+LDLIBS += -lMSL_Runtime_x86.lib -lMSL_C_x86.lib -lMSL_Extras_x86.lib
+RCFLAGS        =
+CFLAGS += -DWIN32
+CFLAGS += -nostdinc -gccinc -msgstyle gcc -inline off -opt nointrinsics -proc 586
+CFLAGS += -ir "$(METROWERKS)/MSL" -ir "$(METROWERKS)/Win32-x86 Support"
+CFLAGS += -w on,nounused,nounusedexpr # -ansi strict
+else
+LD     = gcc
+RC     = windres
+LDFLAGS        = -s
+AR     = ar
+ARFLAGS        = -cq
+LIBEXT = a
+RCFLAGS        = -O coff -i
+CFLAGS += -fno-strict-aliasing
+CFLAGS += -Wall -Wno-unused # -pedantic
+endif
+
+ifeq ($(findstring msys,$(OSTYPE)),msys)
+DL     = '
+DS     = /
+else
+DS     = \\
+endif
+
+ifndef DESCR
+       DESCR = $(notdir $(@:.rc=)) Command Extension
+endif
+DESCR += - $(CC) build
+
+INCLUDES += -I$(PROOT) -I$(PROOT)/src -I$(LIBPNG) -I$(LIBFT2)/include -I$(LIBART) -I$(ZLIBSDK)
+
+CFLAGS += $(INCLUDES)
+
+vpath %.c $(PROOT)/src $(LIBPNG) $(LIBART)/libart_lgpl $(ZLIBSDK)
+
+RRDLIBOBJS     = \
+       $(OBJDIR)/rrd_afm.o \
+       $(OBJDIR)/rrd_afm_data.o \
+       $(OBJDIR)/rrd_create.o \
+       $(OBJDIR)/rrd_diff.o \
+       $(OBJDIR)/rrd_dump.o \
+       $(OBJDIR)/rrd_error.o \
+       $(OBJDIR)/rrd_fetch.o \
+       $(OBJDIR)/rrd_first.o \
+       $(OBJDIR)/rrd_format.o \
+       $(OBJDIR)/rrd_gfx.o \
+       $(OBJDIR)/rrd_graph.o \
+       $(OBJDIR)/rrd_graph_helper.o \
+       $(OBJDIR)/rrd_hw.o \
+       $(OBJDIR)/rrd_info.o \
+       $(OBJDIR)/rrd_last.o \
+       $(OBJDIR)/rrd_lastupdate.o \
+       $(OBJDIR)/rrd_nan_inf.o \
+       $(OBJDIR)/rrd_open.o \
+       $(OBJDIR)/rrd_resize.o \
+       $(OBJDIR)/rrd_restore.o \
+       $(OBJDIR)/rrd_rpncalc.o \
+       $(OBJDIR)/rrd_tune.o \
+       $(OBJDIR)/rrd_update.o \
+       $(OBJDIR)/rrd_version.o \
+       $(OBJDIR)/rrd_xport.o \
+       $(OBJDIR)/rrd_thread_safe_nt.o \
+       $(EOLIST)
+
+XLIBOBJS       = \
+       $(OBJDIR)/rrd_getopt.o \
+       $(OBJDIR)/rrd_getopt1.o \
+       $(OBJDIR)/art_rgba_svp.o \
+       $(OBJDIR)/hash_32.o \
+       $(OBJDIR)/parsetime.o \
+       $(OBJDIR)/pngsize.o \
+       $(OBJDIR)/strftime.o \
+       $(EOLIST)
+
+PNGLIBOBJS     = \
+       $(OBJDIR)/png.o \
+       $(OBJDIR)/pngerror.o \
+       $(OBJDIR)/pngget.o \
+       $(OBJDIR)/pngmem.o \
+       $(OBJDIR)/pngpread.o \
+       $(OBJDIR)/pngread.o \
+       $(OBJDIR)/pngrio.o \
+       $(OBJDIR)/pngrtran.o \
+       $(OBJDIR)/pngrutil.o \
+       $(OBJDIR)/pngset.o \
+       $(OBJDIR)/pngtrans.o \
+       $(OBJDIR)/pngwio.o \
+       $(OBJDIR)/pngwrite.o \
+       $(OBJDIR)/pngwtran.o \
+       $(OBJDIR)/pngwutil.o \
+       $(EOLIST)
+ifeq "$(wildcard $(LIBPNG)/pnggccrd.c)" "$(LIBPNG)/pnggccrd.c"
+PNGLIBOBJS     += \
+       $(OBJDIR)/pnggccrd.o \
+       $(OBJDIR)/pngvcrd.o \
+       $(EOLIST)
+endif
+
+ZLIBOBJS       = \
+       $(OBJDIR)/adler32.o \
+       $(OBJDIR)/compress.o \
+       $(OBJDIR)/crc32.o \
+       $(OBJDIR)/deflate.o \
+       $(OBJDIR)/inflate.o \
+       $(OBJDIR)/inffast.o \
+       $(OBJDIR)/inftrees.o \
+       $(OBJDIR)/trees.o \
+       $(OBJDIR)/zutil.o \
+       $(EOLIST)
+ifeq "$(wildcard $(ZLIBSDK)/infblock.c)" "$(ZLIBSDK)/infblock.c"
+ZLIBOBJS       += \
+       $(OBJDIR)/infblock.o \
+       $(OBJDIR)/infcodes.o \
+       $(OBJDIR)/infutil.o \
+       $(EOLIST)
+endif
+
+ARTLIBOBJS     = \
+       $(patsubst $(LIBART)/libart_lgpl/%.c,$(OBJDIR)/%.o,$(wildcard $(LIBART)/libart_lgpl/art_*.c))
+
+OBJS   := $(RRDLIBOBJS) $(XLIBOBJS) $(PNGLIBOBJS) $(ARTLIBOBJS) $(ZLIBOBJS)
+OBJCGI := $(OBJS) $(OBJDIR)/rrd_cgi.o
+OBJTOOL        := $(OBJS) $(OBJDIR)/rrd_tool.o
+
+LDLIBS += $(LIBFT2)/objs/freetype.$(LIBEXT)
+
+
+all: rrdtool rrdcgi
+
+rrdtool: $(OBJDIR) $(PROOT)/rrd_config.h $(OBJDIR)/rrdtool.exe
+rrdcgi: $(OBJDIR) $(PROOT)/rrd_config.h $(OBJDIR)/rrdcgi.$(CGIEXT)
+librrd: $(OBJDIR) $(PROOT)/rrd_config.h $(OBJDIR)/librrd.$(LIBEXT)
+
+FORCE: ;
+
+dist: all $(DISTDIR) $(DISTDIR)/readme.txt
+       @-$(CP) $(OBJDIR)/rrdcgi.$(CGIEXT) $(DISTDIR)
+       @-$(CP) $(OBJDIR)/rrdtool.exe $(DISTDIR)
+       @-$(CP) $(PROOT)/src/*.ttf $(DISTDIR)
+       @-$(CP) $(PROOT)/CHANGES $(DISTDIR)
+       @-$(CP) $(PROOT)/COPYING $(DISTDIR)
+       @-$(CP) $(PROOT)/COPYRIGHT $(DISTDIR)
+       @-$(CP) $(PROOT)/NEWS $(DISTDIR)
+       @-$(CP) $(PROOT)/README $(DISTDIR)
+       @echo Creating $(DISTARC)
+       @$(ZIP) $(DISTARC) $(DISTDIR)/* < $(DISTDIR)/readme.txt
+
+dev: librrd $(DEVLDIR) $(DEVLDIR)/readme.txt
+       @-mkdir $(DEVLDIR)$(DS)include
+       @-mkdir $(DEVLDIR)$(DS)lib
+       @-mkdir $(DEVLDIR)$(DS)src
+       @-$(CP) $(OBJDIR)/librrd.$(LIBEXT) $(DEVLDIR)/lib
+       @-$(CP) $(PROOT)/rrd_config.h $(DEVLDIR)/include
+       @-$(CP) $(PROOT)/src/rrd.h $(DEVLDIR)/include
+       @-$(CP) $(PROOT)/src/*.ttf $(DEVLDIR)/src
+       @-$(CP) $(PROOT)/CHANGES $(DEVLDIR)
+       @-$(CP) $(PROOT)/COPYING $(DEVLDIR)
+       @-$(CP) $(PROOT)/COPYRIGHT $(DEVLDIR)
+       @-$(CP) $(PROOT)/NEWS $(DEVLDIR)
+       @-$(CP) $(PROOT)/README $(DEVLDIR)
+       @echo Creating $(DEVLARC)
+       @$(ZIP) $(DEVLARC) $(DEVLDIR)/* < $(DEVLDIR)/readme.txt
+
+clean:
+       -$(RM) -r $(OBJDIR)
+       -$(RM) $(PROOT)/rrd_config.h
+
+distclean: clean
+       -$(RM) -r $(DISTDIR)
+       -$(RM) $(DISTARC)
+
+devclean: clean
+       -$(RM) -r $(DEVLDIR)
+       -$(RM) $(DEVLARC)
+
+$(OBJDIR):
+       @mkdir $@
+
+$(DISTDIR):
+       @mkdir $@
+
+$(DEVLDIR):
+       @mkdir $@
+
+$(OBJDIR)/version.inc: $(PROOT)/configure.ac $(OBJDIR) $(PROOT)/src/get_ver.awk
+       @echo Creating $@
+       @$(AWK) -f $(PROOT)/src/get_ver.awk $< > $@
+
+$(OBJDIR)/%.o: %.c
+       @echo Compiling $<
+       @$(CC) $(CFLAGS) -c $< -o $@
+
+$(OBJDIR)/rrdcgi.$(CGIEXT): $(OBJCGI) $(OBJDIR)/rrdcgi.res
+       @echo Linking $@
+       @-$(RM) $@
+       @$(LD) $(LDFLAGS) $^ -o $@ $(LIBPATH) $(LDLIBS)
+
+$(OBJDIR)/rrdtool.exe: $(OBJTOOL) $(OBJDIR)/rrdtool.res
+       @echo Linking $@
+       @-$(RM) $@
+       @$(LD) $(LDFLAGS) $^ -o $@ $(LIBPATH) $(LDLIBS)
+
+$(OBJDIR)/librrd.$(LIBEXT): $(OBJS)
+       @echo Creating $@
+       @-$(RM) $@
+       @$(AR) $(ARFLAGS) $@ $^
+
+$(OBJDIR)/%.res: $(OBJDIR)/%.rc
+       @echo Creating $@
+       @$(RC) $(RCFLAGS) $< -o $@
+
+$(OBJDIR)/%.rc: Makefile $(OBJDIR)/version.inc
+       @echo $(DL)1 VERSIONINFO$(DL) > $@
+       @echo $(DL) FILEVERSION $(RRD_VERSION),0$(DL) >> $@
+       @echo $(DL) PRODUCTVERSION $(RRD_VERSION),0$(DL) >> $@
+       @echo $(DL) FILEFLAGSMASK 0x3fL$(DL) >> $@
+       @echo $(DL) FILEOS 0x40004L$(DL) >> $@
+       @echo $(DL) FILEFLAGS 0x0L$(DL) >> $@
+       @echo $(DL) FILETYPE 0x1L$(DL) >> $@
+       @echo $(DL) FILESUBTYPE 0x0L$(DL) >> $@
+       @echo $(DL)BEGIN$(DL) >> $@
+       @echo $(DL)  BLOCK "StringFileInfo"$(DL) >> $@
+       @echo $(DL)  BEGIN$(DL) >> $@
+       @echo $(DL)    BLOCK "040904E4"$(DL) >> $@
+       @echo $(DL)    BEGIN$(DL) >> $@
+       @echo $(DL)      VALUE "LegalCopyright","$(COPYR)\0"$(DL) >> $@
+ifdef COMPANY
+       @echo $(DL)      VALUE "CompanyName","$(COMPANY)\0"$(DL) >> $@
+endif
+       @echo $(DL)      VALUE "ProductName","$(notdir $(@:.rc=.exe))\0"$(DL) >> $@
+       @echo $(DL)      VALUE "ProductVersion","$(RRD_VERSION_STR)\0"$(DL) >> $@
+       @echo $(DL)      VALUE "License","Released under GPL.\0"$(DL) >> $@
+       @echo $(DL)      VALUE "FileDescription","$(DESCR)\0"$(DL) >> $@
+       @echo $(DL)      VALUE "FileVersion","$(RRD_VERSION_STR)\0"$(DL) >> $@
+       @echo $(DL)      VALUE "InternalName","$(notdir $(@:.rc=))\0"$(DL) >> $@
+       @echo $(DL)      VALUE "OriginalFilename","$(notdir $(@:.rc=.exe))\0"$(DL) >> $@
+       @echo $(DL)      VALUE "WWW","$(WWWURL)\0"$(DL) >> $@
+       @echo $(DL)    END$(DL) >> $@
+       @echo $(DL)  END$(DL) >> $@
+       @echo $(DL)  BLOCK "VarFileInfo"$(DL) >> $@
+       @echo $(DL)  BEGIN$(DL) >> $@
+       @echo $(DL)    VALUE "Translation", 0x409, 1252$(DL) >> $@
+       @echo $(DL)  END$(DL) >> $@
+       @echo $(DL)END$(DL) >> $@
+ifdef ICON
+       @echo $(DL)10 ICON DISCARDABLE "$(ICON)"$(DL) >> $@
+endif
+
+$(PROOT)/rrd_config.h: FORCE Makefile $(OBJDIR)/version.inc
+       @echo Creating $@
+       @echo $(DL)/* $(notdir $@) for Win32 target.$(DL) > $@
+       @echo $(DL)** Do not edit this file - it is created by make!$(DL) >> $@
+       @echo $(DL)** All your changes will be lost!!$(DL) >> $@
+       @echo $(DL)*/$(DL) >> $@
+       @echo $(DL)#ifndef WIN32$(DL) >> $@
+       @echo $(DL)#error This $(notdir $@) is created for Win32 platform!$(DL) >> $@
+       @echo $(DL)#endif$(DL) >> $@
+       @echo $(DL)#ifndef RRD_CONFIG_H$(DL) >> $@
+       @echo $(DL)#define RRD_CONFIG_H$(DL) >> $@
+       @echo $(DL)#define OS "i586-pc-Win32"$(DL) >> $@
+       @echo $(DL)#define PACKAGE_VERSION "$(RRD_VERSION_STR)"$(DL) >> $@
+       @echo $(DL)#define PACKAGE_BUGREPORT "tobi@oetiker.ch"$(DL) >> $@
+       @echo $(DL)#define NUMVERS $(RRD_NUMVERS)$(DL) >> $@
+       @echo $(DL)#define HAVE_ASSERT_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_DLFCN_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_DLOPEN 1$(DL) >> $@
+       @echo $(DL)#define HAVE_ERRNO_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_FCNTL_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_FIONBIO 1$(DL) >> $@
+       @echo $(DL)#define HAVE_INTTYPES_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_LIMITS_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_LONGLONG 1$(DL) >> $@
+       @echo $(DL)#define HAVE_LOCALE_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_MALLOC_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_MATH_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_MBSTOWCS 1$(DL) >> $@
+       @echo $(DL)#define HAVE_SETJMP_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_SNPRINTF 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STDARG_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STDDEF_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STDINT_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STDLIB_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STRCASECMP 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STRDUP 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STRFTIME 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STRING_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STRLCAT 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STRLCPY 1$(DL) >> $@
+       @echo $(DL)#define HAVE_STRSTR 1$(DL) >> $@
+       @echo $(DL)#define HAVE_SYS_PARAM_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_SYS_STAT_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_SYS_TIME_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_TIME_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_VSNPRINTF 1$(DL) >> $@
+       @echo $(DL)#define STDC_HEADERS 1$(DL) >> $@
+       @echo $(DL)#define TIME_WITH_SYS_TIME 1$(DL) >> $@
+       @echo $(DL)#define HAVE_ZLIB_H 1$(DL) >> $@
+       @echo $(DL)#define HAVE_LIBZ 1$(DL) >> $@
+ifdef NO_NULL_REALLOC
+       @echo $(DL)#define NO_NULL_REALLOC 1$(DL) >> $@
+       @echo $(DL)#define rrd_realloc(a,b) ( (a) == NULL ? malloc( (b) ) : realloc( (a) , (b) ))$(DL) >> $@
+else
+       @echo $(DL)#define rrd_realloc(a,b) realloc((a), (b))$(DL) >> $@
+endif
+ifdef RRD_DEFAULT_FONT
+       @echo $(DL)#define RRD_DEFAULT_FONT $(RRD_DEFAULT_FONT)$(DL) >> $@
+endif
+       @echo $(DL)#define RRDGRAPH_YLEGEND_ANGLE $(RRDGRAPH_YLEGEND_ANGLE)$(DL) >> $@
+       @echo $(DL)#define strftime strftime_$(DL) >> $@
+ifdef WITH_PIECHART
+       @echo $(DL)#define WITH_PIECHART $(WITH_PIECHART)$(DL) >> $@
+endif
+       @echo $(DL)#endif /* RRD_CONFIG_H */$(DL) >> $@
+
+$(DISTDIR)/readme.txt: Makefile
+       @echo Creating $@
+       @echo $(DL)This is a binary distribution for Win32 platform.$(DL) > $@
+       @echo $(DL)RRDTool version $(RRD_VERSION_STR)$(DL) >> $@
+       @echo $(DL)Please download the complete RRDTool package for$(DL) >> $@
+       @echo $(DL)any further documentation:$(DL) >> $@
+       @echo $(DL)$(WWWURL)$(DL) >> $@
+
+$(DEVLDIR)/readme.txt: Makefile
+       @echo Creating $@
+       @echo $(DL)This is a development distribution for Win32 platform.$(DL) > $@
+       @echo $(DL)RRDTool version $(RRD_VERSION_STR)$(DL) >> $@
+       @echo $(DL)Please download the complete RRDTool package for$(DL) >> $@
+       @echo $(DL)any further documentation:$(DL) >> $@
+       @echo $(DL)$(WWWURL)$(DL) >> $@
+
+help:
+       @echo $(DL)===========================================================$(DL)
+       @echo $(DL)libpng Source   = $(LIBPNG)$(DL)
+       @echo $(DL)libart Source   = $(LIBART)$(DL)
+       @echo $(DL)Freetype 2 SDK  = $(LIBFT2)$(DL)
+       @echo $(DL)Zlib SDK        = $(ZLIBSDK)$(DL)
+       @echo $(DL)===========================================================$(DL)
+       @echo $(DL)RRDTool $(RRD_VERSION_STR) - available targets are:$(DL)
+       @echo $(DL)$(MAKE) all$(DL)
+       @echo $(DL)$(MAKE) rrdtool$(DL)
+       @echo $(DL)$(MAKE) rrdcgi$(DL)
+       @echo $(DL)$(MAKE) librrd$(DL)
+       @echo $(DL)$(MAKE) clean$(DL)
+       @echo $(DL)$(MAKE) dev$(DL)
+       @echo $(DL)$(MAKE) devclean$(DL)
+       @echo $(DL)$(MAKE) dist$(DL)
+       @echo $(DL)$(MAKE) distclean$(DL)
+       @echo $(DL)===========================================================$(DL)
+
+
diff --git a/program/win32/config.h b/program/win32/config.h
new file mode 100644 (file)
index 0000000..6199234
--- /dev/null
@@ -0,0 +1,57 @@
+/* config.h.msvc.  Hand-tweaked config.h for MSVC compiler.  */
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <math.h>
+#include <float.h>
+#include <direct.h>
+
+/* realloc does not support NULL as argument */
+
+#define HAVE_STRFTIME 1
+#define HAVE_TIME_H 1
+#define HAVE_LOCALE_H 1
+#define HAVE_TZSET 1
+#define HAVE_SETLOCALE 1
+#define HAVE_MATH_H 1
+#define HAVE_FLOAT_H 1
+#define HAVE_MEMMOVE 1
+#define HAVE_MALLOC_H 1
+#define HAVE_MKTIME 1
+#define HAVE_STRFTIME 1
+#define HAVE_STRING_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_VSNPRINTF 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+#define NUMVERS 1.3020
+#define PACKAGE_NAME "rrdtool"
+#define PACKAGE_VERSION "1.3.2"
+#define PACKAGE_STRING PACKAGE_NAME " " PACKAGE_VERSION
+
+#define isinf(a) (_fpclass(a) == _FPCLASS_NINF || _fpclass(a) == _FPCLASS_PINF)
+#define isnan _isnan
+#define finite _finite
+#define snprintf _snprintf
+//#define vsnprintf _vsnprintf
+//#define strftime strftime_
+
+#define NO_NULL_REALLOC 1
+#if NO_NULL_REALLOC
+# define rrd_realloc(a,b) ( (a) == NULL ? malloc( (b) ) : realloc( (a) , (b) ))
+#else
+# define rrd_realloc(a,b) realloc((a), (b))
+#endif
+
+/* Vertical label angle: 90.0 (default) or 270.0 */
+#define RRDGRAPH_YLEGEND_ANGLE 90.0
+
+#define RRD_DEFAULT_FONT "Courier"
+
+/* #define DEBUG 1 */
+
+#endif                          /* CONFIG_H */
diff --git a/program/win32/rrd.dsp b/program/win32/rrd.dsp
new file mode 100644 (file)
index 0000000..a5cd6c3
--- /dev/null
@@ -0,0 +1,247 @@
+# Microsoft Developer Studio Project File - Name="rrd" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Static Library" 0x0104\r
+\r
+CFG=rrd - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "rrd.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "rrd.mak" CFG="rrd - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "rrd - Win32 Release" (based on "Win32 (x86) Static Library")\r
+!MESSAGE "rrd - Win32 Debug" (based on "Win32 (x86) Static Library")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "rrd - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "rrd___Wi"\r
+# PROP BASE Intermediate_Dir "rrd___Wi"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "release"\r
+# PROP Intermediate_Dir "release"\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c\r
+# ADD CPP /nologo /MD /W3 /GX /I "../src" /I "../../zlib-1.2.3" /I "../../libpng-1.2.16" /I "../../libart_lgpl-2.3.17" /I "../../freetype-2.3.1/include" /D "HAVE_CONFIG_H" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_CTYPE_DISABLE_MACROS" /FD /c\r
+# SUBTRACT CPP /X /YX\r
+# ADD BASE RSC /l 0x100c\r
+# ADD RSC /l 0x409\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LIB32=link.exe -lib\r
+# ADD BASE LIB32 /nologo\r
+# ADD LIB32 /nologo\r
+\r
+!ELSEIF  "$(CFG)" == "rrd - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "rrd___W0"\r
+# PROP BASE Intermediate_Dir "rrd___W0"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "debug"\r
+# PROP Intermediate_Dir "debug"\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c\r
+# ADD CPP /nologo /MD /W3 /Gm /GX /ZI /Od /I "../src" /I "../../zlib-1.2.3" /I "../../libpng-1.2.16" /I "../../libart_lgpl-2.3.17" /I "../../freetype-2.3.1/include" /D "HAVE_CONFIG_H" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "_CTYPE_DISABLE_MACROS" /FR /FD /c\r
+# SUBTRACT CPP /X /YX\r
+# ADD BASE RSC /l 0x100c\r
+# ADD RSC /l 0x409\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo /o"rrd.bsc"\r
+LIB32=link.exe -lib\r
+# ADD BASE LIB32 /nologo\r
+# ADD LIB32 /nologo\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "rrd - Win32 Release"\r
+# Name "rrd - Win32 Debug"\r
+# Begin Source File\r
+\r
+SOURCE="..\src\get_ver.awk"\r
+\r
+!IF  "$(CFG)" == "rrd - Win32 Release"\r
+\r
+# PROP Ignore_Default_Tool 1\r
+# Begin Custom Build - Creating ..\rrd_config.h\r
+InputPath="..\src\get_ver.awk"\r
+\r
+"..\rrd_config.h" : $(SOURCE) "..\configure.ac" "..\win32\rrd_config.h.msvc"\r
+       awk -f ..\src\get_ver.awk ..\configure.ac ..\win32\rrd_config.h.msvc > ..\rrd_config.h\r
+\r
+# End Custom Build\r
+\r
+!ELSEIF  "$(CFG)" == "rrd - Win32 Debug"\r
+\r
+# PROP Ignore_Default_Tool 1\r
+# Begin Custom Build - Creating ..\rrd_config.h\r
+InputPath="..\src\get_ver.awk"\r
+\r
+"..\rrd_config.h" : $(SOURCE) "..\configure.ac" "..\win32\rrd_config.h.msvc"\r
+       awk -f ..\src\get_ver.awk ..\configure.ac ..\win32\rrd_config.h.msvc > ..\rrd_config.h\r
+\r
+# End Custom Build\r
+\r
+!ENDIF\r
+\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_afm.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_afm_data.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_create.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_diff.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_dump.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_error.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_fetch.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_first.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_format.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_gfx.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_graph.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_graph_helper.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_hw.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_info.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_last.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_lastupdate.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_nan_inf.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_open.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_resize.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_restore.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_rpncalc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_thread_safe_nt.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_tune.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_update.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_version.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_xport.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_getopt.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_getopt1.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\art_rgba_svp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\hash_32.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_parsetime.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\pngsize.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\strftime.c\r
+# End Source File\r
+# End Target\r
+# End Project\r
diff --git a/program/win32/rrd.sln b/program/win32/rrd.sln
new file mode 100644 (file)
index 0000000..7f07ad3
--- /dev/null
@@ -0,0 +1,29 @@
+\r
+Microsoft Visual Studio Solution File, Format Version 10.00\r
+# Visual Studio 2008\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rrdlib", "rrdlib.vcproj", "{CC158E1D-1364-43CA-9B2D-4AF54225C7CA}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rrdtool", "rrdtool.vcproj", "{11CD05F8-E5E1-476E-A75F-A112655D4E94}"\r
+       ProjectSection(ProjectDependencies) = postProject\r
+               {CC158E1D-1364-43CA-9B2D-4AF54225C7CA} = {CC158E1D-1364-43CA-9B2D-4AF54225C7CA}\r
+       EndProjectSection\r
+EndProject\r
+Global\r
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+               Debug|Win32 = Debug|Win32\r
+               Release|Win32 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+               {CC158E1D-1364-43CA-9B2D-4AF54225C7CA}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {CC158E1D-1364-43CA-9B2D-4AF54225C7CA}.Debug|Win32.Build.0 = Debug|Win32\r
+               {CC158E1D-1364-43CA-9B2D-4AF54225C7CA}.Release|Win32.ActiveCfg = Release|Win32\r
+               {CC158E1D-1364-43CA-9B2D-4AF54225C7CA}.Release|Win32.Build.0 = Release|Win32\r
+               {11CD05F8-E5E1-476E-A75F-A112655D4E94}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {11CD05F8-E5E1-476E-A75F-A112655D4E94}.Debug|Win32.Build.0 = Debug|Win32\r
+               {11CD05F8-E5E1-476E-A75F-A112655D4E94}.Release|Win32.ActiveCfg = Release|Win32\r
+               {11CD05F8-E5E1-476E-A75F-A112655D4E94}.Release|Win32.Build.0 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(SolutionProperties) = preSolution\r
+               HideSolutionNode = FALSE\r
+       EndGlobalSection\r
+EndGlobal\r
diff --git a/program/win32/rrd.vcproj b/program/win32/rrd.vcproj
new file mode 100644 (file)
index 0000000..b9822a9
--- /dev/null
@@ -0,0 +1,648 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="7.10"\r
+       Name="rrd"\r
+       SccProjectName=""\r
+       SccLocalPath="">\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"/>\r
+       </Platforms>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory=".\release"\r
+                       IntermediateDirectory=".\release"\r
+                       ConfigurationType="4"\r
+                       UseOfMFC="0"\r
+                       ATLMinimizesCRunTimeLibraryUsage="FALSE"\r
+                       CharacterSet="2">\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="4"\r
+                               AdditionalIncludeDirectories="\Program Files\GnuWin32\include,\Program Files\GnuWin32\include\freetype2"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_CTYPE_DISABLE_MACROS"\r
+                               RuntimeLibrary="2"\r
+                               PrecompiledHeaderFile=".\release/rrd.pch"\r
+                               AssemblerListingLocation=".\release/"\r
+                               ObjectFile=".\release/"\r
+                               ProgramDataBaseFileName=".\release/"\r
+                               WarningLevel="3"\r
+                               SuppressStartupBanner="TRUE"\r
+                               CompileAs="0"/>\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"/>\r
+                       <Tool\r
+                               Name="VCLibrarianTool"\r
+                               OutputFile=".\release\rrd.lib"\r
+                               SuppressStartupBanner="TRUE"/>\r
+                       <Tool\r
+                               Name="VCMIDLTool"/>\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"/>\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"/>\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"/>\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                               Culture="4108"/>\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"/>\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"/>\r
+                       <Tool\r
+                               Name="VCManagedWrapperGeneratorTool"/>\r
+                       <Tool\r
+                               Name="VCAuxiliaryManagedWrapperGeneratorTool"/>\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory=".\debug"\r
+                       IntermediateDirectory=".\debug"\r
+                       ConfigurationType="4"\r
+                       UseOfMFC="0"\r
+                       ATLMinimizesCRunTimeLibraryUsage="FALSE"\r
+                       CharacterSet="2">\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories="\Program Files\GnuWin32\include\freetype2,\Program Files\GnuWin32\include"\r
+                               PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_CTYPE_DISABLE_MACROS"\r
+                               RuntimeLibrary="2"\r
+                               PrecompiledHeaderFile=".\debug/rrd.pch"\r
+                               AssemblerListingLocation=".\debug/"\r
+                               ObjectFile=".\debug/"\r
+                               ProgramDataBaseFileName=".\debug/"\r
+                               BrowseInformation="1"\r
+                               WarningLevel="3"\r
+                               SuppressStartupBanner="TRUE"\r
+                               DebugInformationFormat="4"\r
+                               CompileAs="0"/>\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"/>\r
+                       <Tool\r
+                               Name="VCLibrarianTool"\r
+                               OutputFile=".\debug\rrd.lib"\r
+                               SuppressStartupBanner="TRUE"/>\r
+                       <Tool\r
+                               Name="VCMIDLTool"/>\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"/>\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"/>\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"/>\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                               Culture="4108"/>\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"/>\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"/>\r
+                       <Tool\r
+                               Name="VCManagedWrapperGeneratorTool"/>\r
+                       <Tool\r
+                               Name="VCAuxiliaryManagedWrapperGeneratorTool"/>\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <File\r
+                       RelativePath="getopt.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="getopt1.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="hash_32.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_parsetime.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="pngsize.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_afm.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_afm_data.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_create.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_diff.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_dump.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_error.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_fetch.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_format.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_gfx.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_graph.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_graph_helper.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_hw.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_info.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_last.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_nan_inf.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_open.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_resize.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_restore.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_rpncalc.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_thread_safe_nt.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_tune.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_update.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
+                       RelativePath="rrd_xport.c">\r
+                       <FileConfiguration\r
+                               Name="Release|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""/>\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32">\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                                       Optimization="0"\r
+                                       AdditionalIncludeDirectories=""\r
+                                       PreprocessorDefinitions=""\r
+                                       BrowseInformation="1"/>\r
+                       </FileConfiguration>\r
+               </File>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/program/win32/rrd_config.h.msvc b/program/win32/rrd_config.h.msvc
new file mode 100644 (file)
index 0000000..7c55d5a
--- /dev/null
@@ -0,0 +1,66 @@
+/* rrd_config.h.msvc. Hand-tweaked rrd_config.h for MSVC compiler. */\r
+#ifndef WIN32 \r
+#error This rrd_config.h is created for Win32 platform! \r
+#endif \r
+#ifndef RRD_CONFIG_H\r
+#define RRD_CONFIG_H\r
+\r
+#include <math.h>\r
+#include <float.h>\r
+#include <direct.h>\r
+\r
+/* the placeholders will be filled in by get_ver.awk */\r
+/* http://cm.bell-labs.com/cm/cs/awkbook/index.html */\r
+#define NUMVERS @@NUMVERS@@\r
+#define PACKAGE_VERSION "@@PACKAGE_VERSION@@"\r
+\r
+#define PACKAGE_NAME "rrdtool"\r
+#define PACKAGE_STRING PACKAGE_NAME " " PACKAGE_VERSION\r
+\r
+#define HAVE_STRFTIME 1\r
+#define HAVE_TIME_H 1\r
+#define HAVE_LOCALE_H 1\r
+#define HAVE_TZSET 1\r
+#define HAVE_SETLOCALE 1\r
+#define HAVE_MATH_H 1\r
+#define HAVE_FLOAT_H 1\r
+#define HAVE_MEMMOVE 1\r
+#define HAVE_MALLOC_H 1\r
+#define HAVE_MKTIME 1\r
+#define HAVE_STRFTIME 1\r
+#define HAVE_STRING_H 1\r
+#define HAVE_VSNPRINTF 1\r
+#define HAVE_SYS_TYPES_H 1\r
+#define HAVE_SYS_STAT_H 1\r
+\r
+/* Define to 1 if you have the ANSI C header files. */\r
+#define STDC_HEADERS 1\r
+\r
+#define isinf(a) (_fpclass(a) == _FPCLASS_NINF || _fpclass(a) == _FPCLASS_PINF)\r
+#define isnan _isnan\r
+#define finite _finite\r
+#define snprintf _snprintf\r
+#define vsnprintf _vsnprintf\r
+#define strftime strftime_ \r
+#define strtoll(p, e, b) _strtoi64(p, e, b)\r
+\r
+/* realloc does not support NULL as argument */\r
+#define NO_NULL_REALLOC 1\r
+#if NO_NULL_REALLOC\r
+# define rrd_realloc(a,b) ( (a) == NULL ? malloc( (b) ) : realloc( (a) , (b) ))\r
+#else\r
+# define rrd_realloc(a,b) realloc((a), (b))\r
+#endif      \r
+\r
+/* Vertical label angle: 90.0 (default) or 270.0 */\r
+#define RRDGRAPH_YLEGEND_ANGLE 90.0\r
+\r
+#define RRD_DEFAULT_FONT "arial.ttf"\r
+/* #define RRD_DEFAULT_FONT "DejaVuSansMono-Roman.ttf" */\r
+\r
+/* #define WITH_PIECHART 1 */\r
+\r
+/* #define DEBUG 1 */\r
+\r
+#endif /* RRD_CONFIG_H */\r
+\r
diff --git a/program/win32/rrdlib.vcproj b/program/win32/rrdlib.vcproj
new file mode 100644 (file)
index 0000000..dd5947e
--- /dev/null
@@ -0,0 +1,375 @@
+<?xml version="1.0" encoding="windows-1251"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="9,00"\r
+       Name="rrdlib"\r
+       ProjectGUID="{CC158E1D-1364-43CA-9B2D-4AF54225C7CA}"\r
+       RootNamespace="rrdlib"\r
+       Keyword="Win32Proj"\r
+       TargetFrameworkVersion="196613"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="4"\r
+                       CharacterSet="1"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories=".;../../contrib/cairo/include/cairo;&quot;../../contrib/pango/include/pango-1.0&quot;;&quot;../../contrib/glib/include/glib-2.0&quot;;&quot;../../contrib/glib/lib/glib-2.0/include&quot;;../../contrib/libpng/include;../../contrib/zlib/include;../../contrib/libxml2/include/libxml2"\r
+                               PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"\r
+                               MinimalRebuild="true"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="3"\r
+                               UsePrecompiledHeader="0"\r
+                               PrecompiledHeaderThrough="precompiled.h"\r
+                               WarningLevel="3"\r
+                               DebugInformationFormat="3"\r
+                               CompileAs="2"\r
+                               DisableSpecificWarnings="4996"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLibrarianTool"\r
+                               AdditionalDependencies="cairo.lib pango-1.0.lib pangocairo-1.0.lib libpng.lib zdll.lib glib-2.0.lib gobject-2.0.lib libxml2.lib"\r
+                               AdditionalLibraryDirectories="../../contrib/cairo/lib;../../contrib/pango/lib;../../contrib/glib/lib;../../contrib/libpng/lib;../../contrib/zlib/lib;../../contrib/libxml2/lib"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="4"\r
+                       CharacterSet="1"\r
+                       WholeProgramOptimization="1"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="2"\r
+                               EnableIntrinsicFunctions="true"\r
+                               AdditionalIncludeDirectories=".;../../contrib/cairo/include/cairo;&quot;../../contrib/pango/include/pango-1.0&quot;;&quot;../../contrib/glib/include/glib-2.0&quot;;&quot;../../contrib/glib/lib/glib-2.0/include&quot;;../../contrib/libpng/include;../../contrib/zlib/include;../../contrib/libxml2/include/libxml2"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;"\r
+                               RuntimeLibrary="2"\r
+                               EnableFunctionLevelLinking="true"\r
+                               UsePrecompiledHeader="0"\r
+                               PrecompiledHeaderThrough="precompiled.h"\r
+                               WarningLevel="3"\r
+                               DebugInformationFormat="3"\r
+                               CompileAs="2"\r
+                               DisableSpecificWarnings="4996"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLibrarianTool"\r
+                               AdditionalDependencies="cairo.lib pango-1.0.lib pangocairo-1.0.lib libpng.lib zdll.lib glib-2.0.lib gobject-2.0.lib libxml2.lib"\r
+                               AdditionalLibraryDirectories="../../contrib/cairo/lib;../../contrib/pango/lib;../../contrib/glib/lib;../../contrib/libpng/lib;../../contrib/zlib/lib;../../contrib/libxml2/lib"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="Source Files"\r
+                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\src\hash_32.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\plbasename.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\pngsize.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_create.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_diff.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_dump.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_error.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_fetch.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_first.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_format.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_getopt.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_getopt1.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_gfx.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_graph.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_graph_helper.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_hw.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_hw_math.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_hw_update.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_info.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_last.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_lastupdate.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_nan_inf.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_open.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_parsetime.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_resize.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_restore.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_rpncalc.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_thread_safe_nt.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_tune.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_update.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_version.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_xport.c"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Header Files"\r
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\win32\config.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\fnv.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\plbasename.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_format.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_getopt.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_graph.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_hw.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_hw_math.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_hw_update.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_i18n.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_is_thread_safe.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_parsetime.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_rpncalc.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_tool.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_xport.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\unused.h"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Resource Files"\r
+                       Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"\r
+                       UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+                       >\r
+               </Filter>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/program/win32/rrdtool.dsp b/program/win32/rrdtool.dsp
new file mode 100644 (file)
index 0000000..1a192fe
--- /dev/null
@@ -0,0 +1,92 @@
+# Microsoft Developer Studio Project File - Name="rrdtool" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Console Application" 0x0103\r
+\r
+CFG=rrdtool - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "rrdtool.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "rrdtool.mak" CFG="rrdtool - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "rrdtool - Win32 Release" (based on "Win32 (x86) Console Application")\r
+!MESSAGE "rrdtool - Win32 Debug" (based on "Win32 (x86) Console Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "rrdtool - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "rrdtool_"\r
+# PROP BASE Intermediate_Dir "rrdtool_"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "toolrelease"\r
+# PROP Intermediate_Dir "toolrelease"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /MD /W3 /GX /I "../src" /I "../../zlib-1.2.3" /I "../../libpng-1.2.16" /I "../../libart_lgpl-2.3.17" /I "../../freetype-2.3.1/include" /D "HAVE_CONFIG_H" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_CTYPE_DISABLE_MACROS" /FD /c\r
+# SUBTRACT CPP /YX\r
+# ADD BASE RSC /l 0x100c /d "NDEBUG"\r
+# ADD RSC /l 0x409 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386\r
+# ADD LINK32 libpng.lib zlib.lib libart.lib freetype231MT.lib kernel32.lib user32.lib /nologo /subsystem:console /incremental:yes /debug /machine:I386 /libpath:"../../libpng-1.2.16/projects/visualc6/Win32_LIB_Release" /libpath:"../../zlib-1.2.3" /libpath:"../../libart_lgpl-2.3.17/win32/release" /libpath:"../../freetype-2.3.1/objs"\r
+\r
+!ELSEIF  "$(CFG)" == "rrdtool - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "rrdtool0"\r
+# PROP BASE Intermediate_Dir "rrdtool0"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "tooldebug"\r
+# PROP Intermediate_Dir "tooldebug"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /MD /W3 /Gm /GX /ZI /Od /I "../src" /I "../../zlib-1.2.3" /I "../../libpng-1.2.16" /I "../../libart_lgpl-2.3.17" /I "../../freetype-2.3.1/include" /D "HAVE_CONFIG_H" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "_CTYPE_DISABLE_MACROS" /FR /FD /c\r
+# SUBTRACT CPP /YX\r
+# ADD BASE RSC /l 0x100c /d "_DEBUG"\r
+# ADD RSC /l 0x409 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo /o"rrdtool.bsc"\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 libpng.lib zlib.lib libart.lib freetype231MT.lib kernel32.lib user32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"../../libpng-1.2.16/projects/visualc6/Win32_LIB_Release" /libpath:"../../zlib-1.2.3" /libpath:"../../libart_lgpl-2.3.17/win32/release" /libpath:"../../freetype-2.3.1/objs"\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "rrdtool - Win32 Release"\r
+# Name "rrdtool - Win32 Debug"\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_tool.c\r
+# End Source File\r
+# End Target\r
+# End Project\r
diff --git a/program/win32/rrdtool.dsw b/program/win32/rrdtool.dsw
new file mode 100644 (file)
index 0000000..07103ba
--- /dev/null
@@ -0,0 +1,44 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00\r
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\r
+\r
+###############################################################################\r
+\r
+Project: "rrd"=".\rrd.dsp" - Package Owner=<4>\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<4>\r
+{{{\r
+}}}\r
+\r
+###############################################################################\r
+\r
+Project: "rrdtool"=".\rrdtool.dsp" - Package Owner=<4>\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<4>\r
+{{{\r
+    Begin Project Dependency\r
+    Project_Dep_Name rrd\r
+    End Project Dependency\r
+}}}\r
+\r
+###############################################################################\r
+\r
+Global:\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<3>\r
+{{{\r
+}}}\r
+\r
+###############################################################################\r
+\r
diff --git a/program/win32/rrdtool.vcproj b/program/win32/rrdtool.vcproj
new file mode 100644 (file)
index 0000000..e98dd8b
--- /dev/null
@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="9,00"\r
+       Name="rrdtool"\r
+       ProjectGUID="{11CD05F8-E5E1-476E-A75F-A112655D4E94}"\r
+       RootNamespace="rrdtool"\r
+       TargetFrameworkVersion="196613"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="1"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories=".;../../contrib/cairo/include/cairo;&quot;../../contrib/pango/include/pango-1.0&quot;;&quot;../../contrib/glib/include/glib-2.0&quot;;&quot;../../contrib/glib/lib/glib-2.0/include&quot;;../../contrib/libpng/include;../../contrib/zlib/include;../../contrib/libxml2/include/libxml2"\r
+                               PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"\r
+                               MinimalRebuild="true"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="3"\r
+                               WarningLevel="3"\r
+                               DebugInformationFormat="4"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="rrdlib.lib cairo.lib pango-1.0.lib pangocairo-1.0.lib libpng.lib zdll.lib glib-2.0.lib gobject-2.0.lib libxml2.lib"\r
+                               AdditionalLibraryDirectories="$(ConfigurationName); ../../contrib/cairo/lib;../../contrib/pango/lib;../../contrib/glib/lib;../../contrib/libpng/lib;../../contrib/zlib/lib;../../contrib/libxml2/lib"\r
+                               GenerateDebugInformation="true"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                               Description="get all the dlls, needed for running"\r
+                               CommandLine="copy $(ProjectDir)\..\..\contrib\cairo\bin\libcairo-2.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\glib\bin\libglib-2.0-0.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\glib\bin\libgmodule-2.0-0.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\glib\bin\libgobject-2.0-0.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\libpng\bin\libpng12-0.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\pango\bin\libpango-1.0-0.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\pango\bin\libpangocairo-1.0-0.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\pango\bin\libpangowin32-1.0-0.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\zlib\zlib1.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\libxml2\lib\libxml2.dll $(TargetDir)\&#x0D;&#x0A;"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="1"\r
+                       CharacterSet="2"\r
+                       WholeProgramOptimization="1"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="2"\r
+                               EnableIntrinsicFunctions="true"\r
+                               AdditionalIncludeDirectories=".;../../contrib/cairo/include/cairo;&quot;../../contrib/pango/include/pango-1.0&quot;;&quot;../../contrib/glib/include/glib-2.0&quot;;&quot;../../contrib/glib/lib/glib-2.0/include&quot;;../../contrib/libpng/include;../../contrib/zlib/include;../../contrib/libxml2/include/libxml2"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;"\r
+                               RuntimeLibrary="2"\r
+                               EnableFunctionLevelLinking="true"\r
+                               WarningLevel="3"\r
+                               DebugInformationFormat="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="rrdlib.lib cairo.lib pango-1.0.lib pangocairo-1.0.lib libpng.lib zdll.lib glib-2.0.lib gobject-2.0.lib libxml2.lib"\r
+                               AdditionalLibraryDirectories="$(ConfigurationName); ../../contrib/cairo/lib;../../contrib/pango/lib;../../contrib/glib/lib;../../contrib/libpng/lib;../../contrib/zlib/lib;../../contrib/libxml2/lib"\r
+                               GenerateDebugInformation="true"\r
+                               OptimizeReferences="2"\r
+                               EnableCOMDATFolding="2"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                               Description="get all the dlls, needed for running"\r
+                               CommandLine="copy $(ProjectDir)\..\..\contrib\cairo\bin\libcairo-2.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\glib\bin\libglib-2.0-0.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\glib\bin\libgmodule-2.0-0.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\glib\bin\libgobject-2.0-0.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\libpng\bin\libpng12-0.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\pango\bin\libpango-1.0-0.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\pango\bin\libpangocairo-1.0-0.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\pango\bin\libpangowin32-1.0-0.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\zlib\zlib1.dll $(TargetDir)\&#x0D;&#x0A;copy $(ProjectDir)\..\..\contrib\libxml2\lib\libxml2.dll $(TargetDir)\&#x0D;&#x0A;"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="Source Files"\r
+                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\src\rrd_tool.c"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Header Files"\r
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+                       >\r
+                       <File\r
+                               RelativePath=".\config.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\src\rrd_tool.h"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Resource Files"\r
+                       Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"\r
+                       UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+                       >\r
+               </Filter>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r