Code

new trunk based on current 1.2
authoroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Wed, 23 May 2007 16:08:14 +0000 (16:08 +0000)
committeroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Wed, 23 May 2007 16:08:14 +0000 (16:08 +0000)
git-svn-id: svn://svn.oetiker.ch/rrdtool/trunk@1073 a5681a0c-68f1-0310-ab6d-d61299d08faa

203 files changed:
program/00README [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/PROJECTS [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/acinclude.m4 [new file with mode: 0644]
program/bindings/Makefile.am [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.pl [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/rrd_extra.h [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.Debian [new file with mode: 0644]
program/debian/build_freetype.sh [new file with mode: 0755]
program/debian/changelog [new file with mode: 0644]
program/debian/control [new file with mode: 0644]
program/debian/copyright [new file with mode: 0644]
program/debian/librrd0-dev.files [new file with mode: 0644]
program/debian/librrd0.files [new file with mode: 0644]
program/debian/librrd0.postinst [new file with mode: 0644]
program/debian/librrd0.postrm [new file with mode: 0644]
program/debian/librrd0.shlibs [new file with mode: 0644]
program/debian/librrdp-perl.files [new file with mode: 0644]
program/debian/librrds-perl.files [new file with mode: 0644]
program/debian/rrdtool-tcl.files [new file with mode: 0644]
program/debian/rrdtool.files [new file with mode: 0644]
program/debian/rules [new file with mode: 0755]
program/debian/watch [new file with mode: 0644]
program/doc/Makefile.am [new file with mode: 0644]
program/doc/bin_dec_hex.pod [new file with mode: 0644]
program/doc/cdeftutorial.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/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/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_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/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-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/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/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/libraries/Makefile.am [new file with mode: 0644]
program/libraries/afm/COPYRIGHT.txt [new file with mode: 0644]
program/libraries/afm/Courier-Bold.afm [new file with mode: 0644]
program/libraries/afm/Courier-BoldOblique.afm [new file with mode: 0644]
program/libraries/afm/Courier-Oblique.afm [new file with mode: 0644]
program/libraries/afm/Courier.afm [new file with mode: 0644]
program/libraries/afm/Helvetica-Bold.afm [new file with mode: 0644]
program/libraries/afm/Helvetica-BoldOblique.afm [new file with mode: 0644]
program/libraries/afm/Helvetica-Oblique.afm [new file with mode: 0644]
program/libraries/afm/Helvetica.afm [new file with mode: 0644]
program/libraries/afm/Makefile.am [new file with mode: 0644]
program/libraries/afm/Symbol.afm [new file with mode: 0644]
program/libraries/afm/Times-Bold.afm [new file with mode: 0644]
program/libraries/afm/Times-BoldItalic.afm [new file with mode: 0644]
program/libraries/afm/Times-Italic.afm [new file with mode: 0644]
program/libraries/afm/Times-Roman.afm [new file with mode: 0644]
program/libraries/afm/ZapfDingbats.afm [new file with mode: 0644]
program/libraries/afm/compile_afm.pl [new file with mode: 0644]
program/libraries/afm/glyphlist.txt [new file with mode: 0644]
program/libraries/afm/test-afm.c [new file with mode: 0644]
program/netware/Makefile [new file with mode: 0644]
program/rrdtool-1.2-release [new file with mode: 0755]
program/rrdtool.spec [new file with mode: 0644]
program/src/DejaVuSansMono-Roman.ttf [new file with mode: 0644]
program/src/Makefile.am [new file with mode: 0644]
program/src/VeraMono.ttf [new file with mode: 0644]
program/src/art_rgba_svp.c [new file with mode: 0644]
program/src/art_rgba_svp.h [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/gdpng.c [new file with mode: 0644]
program/src/get_ver.awk [new file with mode: 0644]
program/src/hash_32.c [new file with mode: 0644]
program/src/parsetime.c [new file with mode: 0644]
program/src/parsetime.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_afm.c [new file with mode: 0644]
program/src/rrd_afm.h [new file with mode: 0644]
program/src/rrd_afm_data.c [new file with mode: 0644]
program/src/rrd_afm_data.h [new file with mode: 0644]
program/src/rrd_cgi.c [new file with mode: 0644]
program/src/rrd_create.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_first.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_gfx.h [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_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_nan_inf.h [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_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_stat.c [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_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/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.vcproj [new file with mode: 0644]
program/win32/rrd_config.h.msvc [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/00README b/program/00README
new file mode 100644 (file)
index 0000000..5f197b3
--- /dev/null
@@ -0,0 +1,6 @@
+Title: RRDtool
+Date: 2005-04-04
+Owner: Tobias Oetiker <tobi@oetiker.ch>
+Group: Software
+
+Round Robin Database Tool
diff --git a/program/CONTRIBUTORS b/program/CONTRIBUTORS
new file mode 100644 (file)
index 0000000..aaf7718
--- /dev/null
@@ -0,0 +1,74 @@
+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)
+Bernard Fischer <bfischer with syslog.ch> 64bit stuff and --alt-autoscale-max
+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>
+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
+Frank Strauss <strauss with escape.de> TCL 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>
+Larry Leszczynski <larryl with furph.com>
+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)
+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>
+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>
+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>
+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>
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..ab5c695
--- /dev/null
@@ -0,0 +1,90 @@
+ RRDTOOL - Round Robin Database Tool
+ A tool for fast logging of numerical data graphical display
+ of this data.
+
+ Copyright (c) 1998-2006 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
diff --git a/program/MakeMakefile b/program/MakeMakefile
new file mode 100755 (executable)
index 0000000..c936d00
--- /dev/null
@@ -0,0 +1,70 @@
+#!/bin/sh
+#
+# Run this script after the first cvs checkout to build
+# makefiles and friends
+
+PATH="/usr/sepp/bin:$PATH"
+export PATH
+
+vcheck (){
+  perl <<PERL
+@t = split /\./, "$1";
+@v = map { int \$_ } split /\./, (split /\s+/, \`$2\`)[3];
+print "$2 = ", (join ".",@v), "  (expected $1)\n";
+\$v = \$t[0]*1000000+\$t[1]*1000+\$t[2] <= \$v[0]*1000000+\$v[1]*1000+\$v[2];
+exit \$v
+PERL
+}
+
+ERROR=0
+
+if vcheck 1.5.14 "libtool --version"
+then
+  echo "get a copy of GNU libtool >= 1.5.14"
+  ERROR=1
+fi
+
+if vcheck 1.9.5  "automake  --version"
+then
+  if vcheck 1.9.5  "automake-1.9 --version"
+  then
+    echo "get a copy of GNU automake >= 1.9.5"
+    ERROR=1
+  else
+    automake=automake-1.9
+    aclocal="aclocal-1.9"
+    for d in /usr/pack/automake-1.9.5-to/share/aclocal-1.9 /usr/share/aclocal-1.9; do
+      [ -d $d ] && aclocal="$aclocal -I $d"
+    done
+  fi
+else
+    automake="automake"
+    aclocal="aclocal"
+#    aclocal="aclocal -I /usr/pack/libtool-1.5.14-to/share/aclocal"
+fi
+
+if vcheck 2.59 "autoconf --version"
+then
+  echo "get a copy of GNU autoconf >= 2.59"
+  ERROR=1
+fi
+
+if [ $ERROR -ne 0 ]
+then
+  exit 1
+fi
+
+# cleanup
+set -x
+find . -name Makefile | grep -v win32 | grep -v netware | xargs rm -f _UNKNONW_
+find . -name "*.la" | xargs rm -f _UNKNOWN_
+find . -name Makefile.in | xargs rm -f _UNKNONW_
+find . -name .libs | xargs rm -r 
+find . -name .debs | xargs rm -r
+
+$aclocal
+libtoolize --copy --force
+autoheader --force  
+$aclocal
+$automake --foreign --add-missing --force-missing --copy 
+autoconf --force
diff --git a/program/Makefile.am b/program/Makefile.am
new file mode 100644 (file)
index 0000000..4bbab3e
--- /dev/null
@@ -0,0 +1,50 @@
+## Process this file with automake to produce Makefile.in
+RSYNC = rsync --rsh=ssh
+
+# build the following subdirectories
+SUBDIRS = src doc examples 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/config.h win32/rrd.dsp win32/rrd.vcproj \
+            win32/rrdtool.dsp win32/rrdtool.dsw win32/rrdtool.vcproj win32/Makefile \
+            win32/rrd_config.h.msvc netware/Makefile
+
+            
+
+CLEANFILES = config.cache
+
+# use relaxed rules when building dists
+AUTOMAKE_OPTIONS= foreign 
+
+# where we keep local rules for automake
+
+ACLOCAL_M4= $(top_srcdir)/aclocal.m4
+#AUTOHEADER = @AUTOHEADER@ --localdir=$(top_srcdir)/config
+#AUTOCONF = @AUTOCONF@ --localdir=$(top_srcdir)/config
+
+to-docs: to-versync
+       (cd doc && $(MAKE) clean && $(MAKE) && $(MAKE) pdf)
+
+to-dist: to-docs dist
+       mv $(PACKAGE)-$(VERSION).tar.gz archive
+
+to-scp: to-dist
+       cp CHANGES  archive/$(PACKAGE)-$(VERSION).tar.gz /home/oetiker/public_html/webtools/rrdtool/pub/
+       (cd /home/oetiker/public_html/webtools/rrdtool/pub; rm $(PACKAGE).tar.gz; ln -s $(PACKAGE)-$(VERSION).tar.gz $(PACKAGE).tar.gz)
+
+#      $(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
+
+##END##
diff --git a/program/NEWS b/program/NEWS
new file mode 100644 (file)
index 0000000..156864e
--- /dev/null
@@ -0,0 +1,62 @@
+RRDTOOL NEWS
+============
+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/PROJECTS b/program/PROJECTS
new file mode 100644 (file)
index 0000000..68a5fcf
--- /dev/null
@@ -0,0 +1,43 @@
+NEW RRD DATAFORMAT with Accessor Functions
+==========================================
+
+Interested:
+
+Tobias Oetiker <tobi@oetiker.ch>
+Jake Brutlag <jakeb@microsoft.com>  
+- updating rrd_update to use accessor fks
+
+Plan:
+
+Encapsulating access to the RRD files through
+special accessor functions which provide
+access to the datastructures within the
+RRDfiles. 
+
+pseudo code by Jake
+
+For example, here is a current code block from rrd_update.c:
+
+        for (i=0;i<rrd.stat_head->ds_cnt;i++) {
+        if(isnan(pdp_new[i]))
+            rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt += interval;
+        else
+            rrd.pdp_prep[i].scratch[PDP_val].u_val+= pdp_new[i];
+          }
+
+This could read:
+
+        for (i=0 ; i < getDSCount(&rrd) ;i++) {
+        if(isnan(pdp_new[i])) {
+            temp = getPDPParam(&rrd, i, PDP_unkn_sec_cnt);
+            temp.u_cnt += interval;
+            setPDPParam(&rrd, i, PDP_unkn_sec_cnt, temp);
+        } else {
+            temp = getPDPParam(&rrd, i, PDP_val);
+            temp.u_val += pdp_new[i];
+            setPDPParam(&rrd, i, PDP_val, temp);
+        }
+          }
+
+
+
diff --git a/program/README b/program/README
new file mode 100644 (file)
index 0000000..cba88fd
--- /dev/null
@@ -0,0 +1,75 @@
+Round Robin Database Tools
+==========================
+
+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..baa64f7
--- /dev/null
@@ -0,0 +1,68 @@
+What has to be done before the 1.2.0 Release
+--------------------------------------------
+* Sync Docs with reality (there are things described which have not been
+  implemented)
+
+* Write Changes Document with all new features
+
+* Update Tutorials for new Release.
+
+Future Ideas
+------------
+reverse order of stacked graph entries prior to plotting ... this is to make
+plotting order more naturally fit with the ordering of the legend ...
+
+make it possible to define order of legend items independant of their order
+on the commandline ...
+
+use XDR to write architecture independant date
+
+have drawing methode modifier for the LINE, AREA, STACK functions which allows
+to select wether the data points should be presented as stepes (todays state)
+or connected with lines.
+
+LINEx:ds[#color][/method]:description
+
+be more clever in the fetch function when no complete data coverage for the
+desired area is possible.
+
+Let the user define the base resolution of the graph independently of
+the pixel resolution. If it is smaller than one pixel it will simply
+be ignored
+
+allow sub second precision for the step definition
+
+modularise consolidation and acquisition functions
+
+Allow the user to define vertical scaling of graphs in a more flexible way.
+some thing like --inner-range 0-1000 and --outer-range 0-10000 which means
+that the grapher can choose its vertical scaling in the given range ... 
+this would replace upper and lower limit
+
+make the inclusion of G?PRINT like output in to the lables of other graph 
+elements possible
+
+add axis on the right and on top of the graph ... 
+
+add configurable counter wrap
+
+build derivatives while plotting
+
+show trend by building moving average and represent it as an arrow
+
+offer side by side tiny box plots for each succesive hour, or some other
+means of seeing the variation rather than just the mean level
+
+circular periodic plots ... (polar)
+
+analyse data in non time domain ... 
+
+add smothing functions to grapher, using rolling average ... 
+
+make VRULE and HRULE with 123 versions ... 
+
+have configurable role-over limits for counters
+
+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..7d731c8
--- /dev/null
@@ -0,0 +1,335 @@
+Compiling RRDtool 1.1.x on Win32 with Microsoft Visual C++:
+---------------------------------------------------------------
+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.pl
+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/acinclude.m4 b/program/acinclude.m4
new file mode 100644 (file)
index 0000000..b71fe8e
--- /dev/null
@@ -0,0 +1,548 @@
+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_$1_$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*)
+            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))
+#  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))
+#  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
+
+#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 ---------------------------------------------------------------------------
+dnl CF_DISABLE_ECHO version: 10 updated: 2003/04/17 22:27:11
+dnl ---------------
+dnl stolen from xterm aclocal.m4
+dnl
+dnl You can always use "make -n" to see the actual options, but it's hard to
+dnl pick out/analyze warning messages when the compile-line is long.
+dnl
+dnl Sets:
+dnl     ECHO_LT - symbol to control if libtool is verbose
+dnl     ECHO_LD - symbol to prefix "cc -o" lines
+dnl     RULE_CC - symbol to put before implicit "cc -c" lines (e.g., .c.o)
+dnl     SHOW_CC - symbol to put before explicit "cc -c" lines
+dnl     ECHO_CC - symbol to put before any "cc" line
+dnl
+AC_DEFUN([CF_DISABLE_ECHO],[
+AC_MSG_CHECKING(if you want to see long compiling messages)
+CF_ARG_DISABLE(echo,
+        [  --disable-echo          display "compiling" commands],
+        [
+    ECHO_LT='--silent'
+    ECHO_LD='@echo linking [$]@;'
+    RULE_CC='   @echo compiling [$]<'
+    SHOW_CC='   @echo compiling [$]@'
+    ECHO_CC='@'
+],[
+    ECHO_LT=''
+    ECHO_LD=''
+    RULE_CC='# compiling'
+    SHOW_CC='# compiling'
+    ECHO_CC=''
+])
+AC_MSG_RESULT($enableval)
+AC_SUBST(ECHO_LT)
+AC_SUBST(ECHO_LD)
+AC_SUBST(RULE_CC)
+AC_SUBST(SHOW_CC)
+AC_SUBST(ECHO_CC)
+])dnl
diff --git a/program/bindings/Makefile.am b/program/bindings/Makefile.am
new file mode 100644 (file)
index 0000000..a0e0907
--- /dev/null
@@ -0,0 +1,56 @@
+.PHONY: python ruby
+
+if BUILD_TCL
+SUB_tcl = tcl
+endif
+
+SUBDIRS = $(SUB_tcl)
+
+# 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.pl 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/rrd_extra.h python/rrdtoolmodule.c python/setup.py
+
+
+# add the following to the all target
+all-local:  @COMP_PERL@ @COMP_RUBY@ @COMP_PYTHON@
+
+install-data-local:
+       test -f perl-piped/Makefile && cd perl-piped && $(MAKE) install || true
+       test -f perl-shared/Makefile && cd perl-shared && $(MAKE) install || true
+       test -f ruby/Makefile && cd ruby && $(MAKE) EPREFIX=$(DESTDIR)$(exec_prefix) $(RUBY_MAKE_OPTIONS) install || true
+       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 buildung 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: 
+       cd ruby && $(RUBY) extconf.rb && $(MAKE) EPREFIX=$(exec_prefix) $(RUBY_MAKE_OPTIONS) RUBYARCHDIR=
+
+# rules for buildung the pyton module
+python:
+       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
+       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/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..e61a28b
--- /dev/null
@@ -0,0 +1,200 @@
+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.
+
+=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.2023;
+
+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;
+  my $ERR = 0;
+  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;
+       $ERR = 1;
+      } 
+      elsif ($line =~ m|^OK u:([\d\.]+) s:([\d\.]+) r:([\d\.]+)|){
+       ($RRDp::sys,$RRDp::user,$RRDp::real)=($1,$2,$3);
+       return $ERR == 1 ? undef : \$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;
+  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..664d231
--- /dev/null
@@ -0,0 +1,7 @@
+ntmake.pl
+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..863444c
--- /dev/null
@@ -0,0 +1,37 @@
+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"};
+                /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 = "-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"},
+    'realclean'    => {FILES => 't/demo?.rrd t/demo?.png' }
+);
+
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..9542421
--- /dev/null
@@ -0,0 +1,145 @@
+package RRDs;
+
+use strict;
+use vars qw(@ISA $VERSION);
+
+@ISA = qw(DynaLoader);
+
+require DynaLoader;
+
+$VERSION=1.2023;
+
+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 ...
+
+=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 commandline 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 pointer to an ARRAY containing the x-size and y-size of the
+created image and results of the PRINT arguments.
+
+ ($averages,$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::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";
+  foreach my $line (@$data) {
+    print "  ", scalar localtime($start), " ($start) ";
+    $start += $step;
+    foreach 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 posixs
+function tzset 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..f84efef
--- /dev/null
@@ -0,0 +1,433 @@
+#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(); \
+                while (data) { \
+                   save=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)); \
+                       rrd_freemem(data->value.u_str); \
+                       break; \
+                   } \
+                   rrd_freemem(data->key); \
+                   data = data->next; \
+                   rrd_freemem(save); \
+                   } \
+            rrd_freemem(data); \
+            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
+
+
+void
+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)));
+
+void
+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)));
+
+void
+rrd_times(start, end)
+         char *start
+         char *end
+       PREINIT:
+               struct  rrd_time_value start_tv, end_tv;
+               char    *parsetime_error = NULL;
+               time_t  start_tmp, end_tmp;
+       PPCODE:
+               rrd_clear_error();
+               if( (parsetime_error = parsetime( start, &start_tv))) {
+                       rrd_set_error( "start time: %s", parsetime_error);
+                       XSRETURN_UNDEF;
+               }
+               if( (parsetime_error = parsetime( end, &end_tv))) {
+                       rrd_set_error( "end time: %s", parsetime_error);
+                       XSRETURN_UNDEF;
+               }
+               if( proc_start_end( &start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+                       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:
+               info_t *data,*save;
+                int i;
+                char **argv;
+               HV *hash;
+       CODE:
+               rrdinfocode(rrd_info);  
+    OUTPUT:
+          RETVAL
+
+SV*
+rrd_updatev(...)
+       PROTOTYPE: @    
+       PREINIT:
+               info_t *data,*save;
+                int i;
+                char **argv;
+               HV *hash;
+       CODE:
+               rrdinfocode(rrd_update_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
+
diff --git a/program/bindings/perl-shared/ntmake.pl b/program/bindings/perl-shared/ntmake.pl
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..265244f
--- /dev/null
@@ -0,0 +1,28 @@
+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
+
+
+Author
+------
+
+Hye-Shik Chang <perky@FreeBSD.org>
+
+Any comments, suggestions, and/or patches are very welcome.
+Thank you for using py-rrdtool!
diff --git a/program/bindings/python/rrd_extra.h b/program/bindings/python/rrd_extra.h
new file mode 100644 (file)
index 0000000..99b5aa0
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *  This file is part of RRDtool.
+ *
+ *  RRDtool 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.
+ *
+ *  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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU 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
+ */
+
+/*****************************************************************************
+ * RRDtool 1.0.37  Copyright Tobias Oetiker, 1997 - 2000
+ *****************************************************************************
+ * rrd_tool.h   Common Header File
+ *****************************************************************************
+ * Id: rrd_tool.h,v 1.1.1.1 2002/02/26 10:21:37 oetiker Exp
+ * Log: rrd_tool.h,v
+ * Revision 1.1.1.1  2002/02/26 10:21:37  oetiker
+ * Intial Import
+ *
+ *****************************************************************************/
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#ifndef _RRD_EXTRA_H
+#define _RRD_EXTRA_H
+
+#include "rrd_format.h"
+
+#ifndef WIN32
+#ifndef isnan /* POSIX */
+int isnan(double value);
+#endif
+#else /* Windows only */
+#include <float.h>
+#define isnan _isnan
+#endif
+
+void rrd_free(rrd_t *rrd);
+void rrd_init(rrd_t *rrd);
+
+int rrd_open(char *file_name, FILE **in_file, rrd_t *rrd, int rdwr);
+int readfile(char *file, char **buffer, int skipfirst);
+
+#define RRD_READONLY    0
+#define RRD_READWRITE   1
+
+#endif
+
+#ifdef  __cplusplus
+}
+#endif
diff --git a/program/bindings/python/rrdtoolmodule.c b/program/bindings/python/rrdtoolmodule.c
new file mode 100644 (file)
index 0000000..6f5b36c
--- /dev/null
@@ -0,0 +1,542 @@
+/*
+ * 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
+
+static const char *__version__ = "$Revision: 1.14 $";
+
+#include "Python.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;
+    int              size, i;
+    
+    size    = PyTuple_Size(args);
+    *argv   = PyMem_New(char *, size + 1);
+    if (*argv == NULL)
+        return -1;
+
+    for (i = 0; i < size; i++) {
+        o = PyTuple_GET_ITEM(args, i);
+        if (PyString_Check(o))
+            (*argv)[i + 1] = PyString_AS_STRING(o);
+        else {
+            PyMem_Del(*argv);
+            PyErr_Format(PyExc_TypeError, "argument %d must be string", i);
+            return -1;
+        }
+    }
+    (*argv)[0] = command;
+    *argc = size + 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 + 1);
+
+        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++)
+            free(ds_namv[i]);
+        free(ds_namv); /* rrdtool don't use PyMem_Malloc :) */
+        free(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);
+                free(calcpr[i]);
+            }
+            free(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 char PyRRD_info__doc__[] =
+"info(filename): extract header information from an rrd";
+
+static PyObject *
+PyRRD_info(PyObject UNUSED(*self), PyObject *args)
+{
+    PyObject        *r, *t, *ds;
+    rrd_t            rrd;
+    FILE            *in_file;
+    char            *filename;
+    unsigned long   i, j;
+
+    if (! PyArg_ParseTuple(args, "s:info", &filename))
+        return NULL;
+
+    if (rrd_open(filename, &in_file, &rrd, RRD_READONLY) == -1) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        return NULL;
+    }
+    fclose(in_file);
+
+#define DICTSET_STR(dict, name, value) \
+    t = PyString_FromString(value); \
+    PyDict_SetItemString(dict, name, t); \
+    Py_DECREF(t);
+
+#define DICTSET_CNT(dict, name, value) \
+    t = PyInt_FromLong((long)value); \
+    PyDict_SetItemString(dict, name, t); \
+    Py_DECREF(t);
+
+#define DICTSET_VAL(dict, name, value) \
+    t = isnan(value) ? (Py_INCREF(Py_None), Py_None) :  \
+        PyFloat_FromDouble((double)value); \
+    PyDict_SetItemString(dict, name, t); \
+    Py_DECREF(t);
+
+    r = PyDict_New();
+
+    DICTSET_STR(r, "filename", filename);
+    DICTSET_STR(r, "rrd_version", rrd.stat_head->version);
+    DICTSET_CNT(r, "step", rrd.stat_head->pdp_step);
+    DICTSET_CNT(r, "last_update", rrd.live_head->last_up);
+
+    ds = PyDict_New();
+    PyDict_SetItemString(r, "ds", ds);
+    Py_DECREF(ds);
+
+    for (i = 0; i < rrd.stat_head->ds_cnt; i++) {
+        PyObject    *d;
+
+        d = PyDict_New();
+        PyDict_SetItemString(ds, rrd.ds_def[i].ds_nam, d);
+        Py_DECREF(d);
+
+        DICTSET_STR(d, "ds_name", rrd.ds_def[i].ds_nam);
+        DICTSET_STR(d, "type", rrd.ds_def[i].dst);
+        DICTSET_CNT(d, "minimal_heartbeat", rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt);
+        DICTSET_VAL(d, "min", rrd.ds_def[i].par[DS_min_val].u_val);
+        DICTSET_VAL(d, "max", rrd.ds_def[i].par[DS_max_val].u_val);
+        DICTSET_STR(d, "last_ds", rrd.pdp_prep[i].last_ds);
+        DICTSET_VAL(d, "value", rrd.pdp_prep[i].scratch[PDP_val].u_val);
+        DICTSET_CNT(d, "unknown_sec", rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
+    }
+
+    ds = PyList_New(rrd.stat_head->rra_cnt);
+    PyDict_SetItemString(r, "rra", ds);
+    Py_DECREF(ds);
+
+    for (i = 0; i < rrd.stat_head->rra_cnt; i++) {
+        PyObject    *d, *cdp;
+
+        d = PyDict_New();
+        PyList_SET_ITEM(ds, i, d);
+
+        DICTSET_STR(d, "cf", rrd.rra_def[i].cf_nam);
+        DICTSET_CNT(d, "rows", rrd.rra_def[i].row_cnt);
+        DICTSET_CNT(d, "pdp_per_row", rrd.rra_def[i].pdp_cnt);
+        DICTSET_VAL(d, "xff", rrd.rra_def[i].par[RRA_cdp_xff_val].u_val);
+
+        cdp = PyList_New(rrd.stat_head->ds_cnt);
+        PyDict_SetItemString(d, "cdp_prep", cdp);
+        Py_DECREF(cdp);
+
+        for (j = 0; j < rrd.stat_head->ds_cnt; j++) {
+            PyObject    *cdd;
+
+            cdd = PyDict_New();
+            PyList_SET_ITEM(cdp, j, cdd);
+
+            DICTSET_VAL(cdd, "value",
+                    rrd.cdp_prep[i*rrd.stat_head->ds_cnt+j].scratch[CDP_val].u_val);
+            DICTSET_CNT(cdd, "unknown_datapoints",
+                    rrd.cdp_prep[i*rrd.stat_head->ds_cnt+j].scratch[CDP_unkn_pdp_cnt].u_cnt);
+        }
+    }
+
+    rrd_free(&rrd);
+
+    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__),
+    {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..7a41a11
--- /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, 'lib'))
+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..c84ff29
--- /dev/null
@@ -0,0 +1,3 @@
+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..b2eaa68
--- /dev/null
@@ -0,0 +1,259 @@
+/* $Id$
+ * Substantial penalty for early withdrawal.
+ */
+
+#include <unistd.h>
+#include <ruby.h>
+#include <rrd.h>
+
+typedef struct string_arr_t {
+    int len;
+    char **strings;
+} string_arr;
+
+VALUE mRRD;
+VALUE rb_eRRDError;
+
+typedef int (*RRDFUNC)(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(rb_strings)->len + 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");
+            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();
+}
+
+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_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]));
+        free(raw_names[i]);
+    }
+    free(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);
+    }
+    free(raw_data);
+   
+    result = rb_ary_new2(4);
+    rb_ary_store(result, 0, INT2FIX(start));
+    rb_ary_store(result, 1, INT2FIX(end));
+    rb_ary_store(result, 2, names);
+    rb_ary_store(result, 2, data);
+    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));
+        free(*p);
+    }
+    free(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_info(VALUE self, VALUE args)
+{
+    string_arr a;
+    info_t *p;
+    VALUE result;
+
+    a = string_arr_new(args);
+    data = rrd_info(a.len, a.strings);
+    string_arr_delete(a);
+
+    RRD_CHECK_ERROR
+
+    result = rb_hash_new();
+    while (data) {
+        VALUE key = rb_str_new2(data->key);
+        switch (data->type) {
+        case RD_I_VAL:
+            if (isnan(data->u_val)) {
+                rb_hash_aset(result, key, Qnil);
+            }
+            else {
+                rb_hash_aset(result, key, rb_float_new(data->u_val));
+            }
+            break;
+        case RD_I_CNT:
+            rb_hash_aset(result, key, INT2FIX(data->u_cnt));
+            break;
+        case RD_I_STR:
+            rb_hash_aset(result, key, rb_str_new2(data->u_str));
+            free(data->u_str);
+            break;
+        }
+        p = data;
+        data = data->next;
+        free(p);
+    }
+    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, INT2FIX(last));
+}
+
+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);
+}
+
+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);
+}
diff --git a/program/bindings/ruby/test.rb b/program/bindings/ruby/test.rb
new file mode 100755 (executable)
index 0000000..4853326
--- /dev/null
@@ -0,0 +1,52 @@
+#!/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.now.to_i
+
+puts "creating #{rrd}"
+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) = 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
+
+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..c73a6e2
--- /dev/null
@@ -0,0 +1,56 @@
+
+EXTRA_DIST = README tclrrd.c
+
+VERSION = @VERSION@
+
+AM_CFLAGS = @CFLAGS@
+
+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@
+
+CLEANFILES = tclrrd.o tclrrd.so
+
+SRC_DIR            = $(top_srcdir)/src
+AM_CPPFLAGS        = -I$(TCL_PREFIX)/include -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 $< -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..11d25df
--- /dev/null
@@ -0,0 +1,677 @@
+/*
+ * 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 "../../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(argv2, argn)
+    char *argv2[];
+    int  argn;
+{
+    if (argv2[argn] != NULL) {
+       free(argv2[argn]);
+       argv2[argn] = NULL;
+    }
+}
+
+static void getopt_squieeze(argc, argv2)
+    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 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;
+    struct rrd_time_value      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 = 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, 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 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_Last(ClientData 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 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, 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 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;
+
+   argv2 = getopt_init(argc, argv);
+   if (rrd_lastupdate(argc-1, argv2, &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 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 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 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 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 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::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..09e0260
--- /dev/null
@@ -0,0 +1,676 @@
+dnl RRDtool AutoConf script ... 
+dnl ---------------------------
+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 the official version number is
+dnl a.b.c
+AC_INIT([rrdtool],[1.2.23])
+dnl for testing a numberical version number comes handy
+dnl the released version are
+dnl a.bccc
+dnl the devl versions will be something like
+dnl a.b999yymmddhh 
+NUMVERS=1.2023
+AC_SUBST(NUMVERS)
+AC_CANONICAL_TARGET
+AM_INIT_AUTOMAKE
+AC_CONFIG_HEADERS([rrd_config.h])
+
+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( /usr/local/rrdtool-$PACKAGE_VERSION )
+
+dnl Minimum Autoconf version required.
+AC_PREREQ(2.59)
+
+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
+
+ ])
+
+AH_BOTTOM([
+/* enable posix_fadvise on linux */
+#if defined(HAVE_POSIX_FADVISE) && defined(HAVE_FCNTL_H)
+#define _XOPEN_SOURCE 600
+#include <fcntl.h>
+#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 NEED_MALLOC_MALLOC_H
+#  include <malloc/malloc.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
+#  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))
+#  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))
+#  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
+
+#endif /* RRD_CONFIG_H */
+])
+
+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 270.0])
+AC_DEFINE_UNQUOTED(RRDGRAPH_YLEGEND_ANGLE,${RRDGRAPH_YLEGEND_ANGLE:-90.0},
+ [Vertical label angle: 90.0 (default) or 270.0])
+
+AC_ARG_ENABLE(rrdcgi,[  --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='$(fontsdir)/$(fonts_DATA)'
+  fi
+])
+
+dnl Use mmap in rrd_update instead of seek+write
+AC_ARG_ENABLE([mmap],
+[  --disable-mmap          disable mmap in rrd_update, use seek+write instead],
+[],
+[enable_mmap=yes])
+
+
+ AC_ARG_ENABLE(pthread,[  --disable-pthread       disable multithread support],
+[],[enable_pthread=yes])
+
+
+
+CONFIGURE_PART(Audit Compilation Environment)
+
+
+dnl Check for the compiler and static/shared library creation.
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_LIBTOOL
+
+dnl which flags does the compile support?
+if test "$GCC" = "yes"; then
+  for flag in -fno-strict-aliasing -Wall -std=gnu99 -pedantic -Wshadow -Wpointer-arith -Wcast-align -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -Winline -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(sys/stat.h sys/types.h fcntl.h locale.h fp_class.h malloc.h unistd.h ieeefp.h math.h sys/times.h sys/param.h sys/resource.h float.h)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_HEADER_TIME
+AC_STRUCT_TM
+
+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"
+
+CONFIGURE_PART(Test Library Functions)
+
+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(tzset mbstowcs opendir readdir chdir chroot getuid setlocale strerror strerror_r snprintf vsnprintf fpclass class fp_class isnan memmove strchr mktime getrusage gettimeofday posix_fadvise madvise)
+
+AC_CHECK_DECLS(fdatasync, [], [], [#include <unistd.h>])
+AC_CHECK_DECLS(posix_fadvise, [], [], [#define _XOPEN_SOURCE 600
+#include <fcntl.h>])
+AC_CHECK_DECLS(madvise, [], [], [#include <sys/mman.h>])
+
+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 [ "x${ac_cv_func_mmap}" = xyes ]; then
+         ac_cv_func_mmap_fixed_mapped=yes
+       fi
+    ;;
+    *)
+       AC_FUNC_MMAP
+    ;;
+  esac
+fi
+
+
+CONFIGURE_PART(IEEE Math Checks)
+dnl HP-UX 11.00 does not have finite but does have isfinite as a macro so we need
+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(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)])])])
+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)])])
+
+AC_FULL_IEEE
+
+CONFIGURE_PART(Resolve Portability Issues)
+
+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(NEED_MALLOC_MALLOC_H)
+           AC_MSG_RESULT([yes we do])],
+          [AC_MSG_ERROR([Can not figure how to compile malloc])]
+      )
+    ]  
+)
+AC_LANG_POP(C)
+
+CONFIGURE_PART(Find 3rd-Party Libraries)
+
+
+AM_CONDITIONAL(BUILD_RRDCGI,[test $enable_rrdcgi != no])
+
+CORE_LIBS="$LIBS"
+
+EX_CHECK_ALL(art_lgpl_2, art_vpath_add_point,       libart_lgpl/libart.h,   libart-2.0,  2.3.17, ftp://ftp.gnome.org/pub/GNOME/sources/libart_lgpl/2.3/, /usr/include/libart-2.0)
+EX_CHECK_ALL(z,          zlibVersion,               zlib.h,                 zlib,        1.2.3,  http://www.gzip.org/zlib/, "")
+EX_CHECK_ALL(png,        png_access_version_number, png.h,                  libpng,      1.2.10,  http://prdownloads.sourceforge.net/libpng/, "")
+EX_CHECK_ALL(freetype,   FT_Init_FreeType,          ft2build.h,                    freetype2,   2.1.10,  http://prdownloads.sourceforge.net/freetype/, /usr/include/freetype2)
+
+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.
+AC_PATH_PROG(PERL, perl, no)
+
+AC_ARG_ENABLE(perl,[  --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, [[same as PERLCC] 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
+ppref=$prefix
+test "$ppref" = "NONE" && ppref=$ac_default_prefix
+
+PERL_MAKE_OPTIONS="PREFIX=$ppref LIB=$ppref/lib/perl/$PERL_VERSION"
+
+dnl pass additional perl options when generating Makefile from Makefile.PL
+AC_ARG_ENABLE(perl-site-install,
+[  --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,[  --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,
+[  --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 you $: variable
+                          for ruby to find the RRD.so file.],
+[RUBY_MAKE_OPTIONS=],[RUBY_MAKE_OPTIONS="sitedir=$prefix/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)
+
+
+enable_tcl_site=no
+
+AC_ARG_ENABLE(tcl,[  --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; 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 won't be built])
+  else
+       . $tcl_config
+       TCL_PACKAGE_DIR="$TCL_PACKAGE_PATH/tclrrd$VERSION"
+  fi
+  AC_ARG_ENABLE(tcl,[  --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_ARG_ENABLE(python,[  --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([doc/Makefile])
+AC_CONFIG_FILES([src/Makefile])
+AC_CONFIG_FILES([bindings/Makefile])
+AC_CONFIG_FILES([bindings/tcl/Makefile])
+AC_CONFIG_FILES([bindings/tcl/ifOctets.tcl])
+AC_CONFIG_FILES([Makefile])          
+
+AC_CONFIG_COMMANDS([default],[[ chmod +x examples/*.pl]],[[]])
+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: $ac_cv_func_mmap_fixed_mapped"
+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 Tcl Bindings: $enable_tcl"
+echo " Build Python Bindings: $enable_python"
+echo "          Build rrdcgi: $enable_rrdcgi"
+echo "       Build librrd MT: $enable_pthread"
+echo
+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.Debian b/program/debian/README.Debian
new file mode 100644 (file)
index 0000000..ad7db63
--- /dev/null
@@ -0,0 +1,33 @@
+rrdtool for Debian
+----------------------
+
+The RRDtool distribution is split into several packages:
+
+
+Package: rrdtool
+       Command-line utilities and documentation
+
+Package: librrd0
+       Shared library
+
+Package: librrd0-dev
+       Static library
+
+Package: librrds-perl
+       Perl interface using the shared library (RRDs)
+
+Package: librrdp-perl
+       Perl interface using command pipes to the rrdtool program (RRDp)
+
+Package: rrdtool-tcl
+       TCL interface using the shared library (librrd)
+
+
+Original by 
+
+ -- Matt Zimmerman <mdz@csh.rit.edu>, Fri, 10 Sep 1999 10:53:19 -0700
+
+Hacked for 1.1.x by
+
+ -- Mike Slifcak <slif@bellsouth.net>, Tue , 18 May 2004 20:38:40 +0200
+
diff --git a/program/debian/build_freetype.sh b/program/debian/build_freetype.sh
new file mode 100755 (executable)
index 0000000..8edc669
--- /dev/null
@@ -0,0 +1,42 @@
+:
+# Build freetype2 for rrdtool on Debian/Linux
+# 12-May-2004 Mike Slifcak
+
+FOUND=`find /lib /usr/lib /usr/local/lib -name libfreetype.a | wc -l`
+FOUND=`echo $FOUND`
+if [ $FOUND -lt 1 ] ; then
+########################################
+## Build the independent object freetype2
+########################################
+cd /tmp
+rm -rf freetype*/
+if [ ! -e freetype*gz ] ; then
+  echo "get freetype-2.1.8 or stable from http://freetype.sf.net/"
+  exit 1
+fi
+tar tzf freetype*gz > /dev/null 2>&1
+RC=$?
+if [ $RC -ne 0 ] ; then
+  echo "Need one good freetype*gz.  Just one. In /tmp.  Thanks!"
+  exit 1
+fi
+
+echo -n "Testing "
+ls  freetype*gz
+tar xzf freetype*gz
+echo "Building freetype"
+cd freetype-*/
+./configure --disable-shared > cfg.out 2>&1
+make > make.out 2>&1
+make install > inst.out 2>&1
+grep Error *.out
+if [ $? -ne 1 ] ; then
+   echo  "Building freetype failed. See `pwd`/*.out for details"
+   exit 1
+fi
+cd ..
+fi ## skip freetype build
+
+echo "Building freetype succeeded."
+
+exit 0 
diff --git a/program/debian/changelog b/program/debian/changelog
new file mode 100644 (file)
index 0000000..4d86445
--- /dev/null
@@ -0,0 +1,379 @@
+rrdtool (1.1.0-1) unstable; urgency=low
+
+  * Fixed build dependencies
+  * Added libfreetype6-dev to build dependencies
+  * Changed rules file to compile against Debian version of freetype.
+
+ -- Peter Hirdina <Peter.Hirdina@gmx.net>  Mon,  7 Jun 2004 17:10:10 +0200
+
+rrdtool (1.1.0) unstable; urgency=low
+
+  * grafted "debian" directory from 1.0.46 source 
+  * no tcl package support due to build failures (see ##NO_TCL )
+
+ -- Mike Slifcak <>  Wed, 12 May 2004 20:53:16 -0400
+
+rrdtool (1.0.46-3) unstable; urgency=low
+
+  * Add dependencies to librrd0-dev to ensure that static linking is
+    possible: libgd-gif1-dev, zlib1g-dev
+  * #include <stdio.h> in rrd.h (Closes: #238849)
+  * Only link rrdcgi with -lcgi, not librrd
+
+ -- Matt Zimmerman <mdz@debian.org>  Fri, 19 Mar 2004 11:38:39 -0800
+
+rrdtool (1.0.46-2) unstable; urgency=medium
+
+  * Fix tcl module installation (Closes: #231171)
+
+ -- Matt Zimmerman <mdz@debian.org>  Wed,  4 Feb 2004 15:35:46 -0800
+
+rrdtool (1.0.46-1) unstable; urgency=low
+
+  * New upstream release
+
+ -- Matt Zimmerman <mdz@debian.org>  Mon, 12 Jan 2004 23:22:09 -0800
+
+rrdtool (1.0.45-1) unstable; urgency=low
+
+  * New upstream release
+  * Deal with changed tclrrd naming scheme
+  * Build with tcl8.4
+  * Remove old dpkg-dev build-dependency; even woody is new enough
+
+ -- Matt Zimmerman <mdz@debian.org>  Wed, 17 Dec 2003 14:09:11 -0800
+
+rrdtool (1.0.42-2) unstable; urgency=low
+
+  * Build-Depends: libpng12-dev instead of libpng3-dev (when will the madness end?)
+    (Closes: #195224
+     I hope this will also fix weird version mismatch problems (Closes: #194900)
+
+ -- Matt Zimmerman <mdz@debian.org>  Sat, 31 May 2003 15:06:42 -0400
+
+rrdtool (1.0.42-1) unstable; urgency=low
+
+  * New upstream release
+  * librrd0-dev Section: libdevel
+  * librrd[sp]-perl Section: perl
+  * Build with libpng3 (Closes: #189493)
+
+ -- Matt Zimmerman <mdz@debian.org>  Thu, 15 May 2003 19:44:37 -0400
+
+rrdtool (1.0.40-2) unstable; urgency=low
+
+  * Correctly suppress non-image output when writing image to stdout
+    (Closes: #182217)
+
+ -- Matt Zimmerman <mdz@debian.org>  Sat,  1 Mar 2003 16:59:39 -0500
+
+rrdtool (1.0.40-1) unstable; urgency=low
+
+  * New upstream release
+  * Remove unnecessary rrd_free to avoid crash on invalid option
+    (Closes: #166156)
+  * Clean example source code; some libtool cruft was getting installed in
+    /usr/share/doc/examples
+
+ -- Matt Zimmerman <mdz@debian.org>  Tue, 17 Dec 2002 22:11:46 -0500
+
+rrdtool (1.0.39-2) unstable; urgency=low
+
+  * Rebuild for perl 5.8 (Closes: #158728)
+
+ -- Matt Zimmerman <mdz@debian.org>  Sun, 25 Aug 2002 17:29:41 -0400
+
+rrdtool (1.0.39-1) unstable; urgency=low
+
+  * New upstream release
+
+ -- Matt Zimmerman <mdz@debian.org>  Wed, 31 Jul 2002 00:40:00 -0400
+
+rrdtool (1.0.38-1) unstable; urgency=low
+
+  * New upstream release (Closes: #148486)
+
+ -- Matt Zimmerman <mdz@debian.org>  Wed, 29 May 2002 12:28:28 -0400
+
+rrdtool (1.0.35-2) unstable; urgency=low
+
+  * Remove CVS directories with rm -rf.  This broke autobuilds entirely.
+    (Closes: #140075)
+
+ -- Matt Zimmerman <mdz@debian.org>  Tue, 26 Mar 2002 17:15:01 -0500
+
+rrdtool (1.0.35-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Matt Zimmerman <mdz@debian.org>  Sun, 24 Mar 2002 11:29:10 -0500
+
+rrdtool (1.0.33-9) unstable; urgency=low
+
+  * Call dh_shlibdeps with -l to allow it to find dependencies correctly
+    (Closes: #118629)
+  * Add call to ldconfig in postrm of librrd0
+  * Remove .cvsignore files from installed examples
+
+ -- Matt Zimmerman <mdz@debian.org>  Sat, 10 Nov 2001 14:14:51 -0500
+
+rrdtool (1.0.33-8) unstable; urgency=low
+
+  * Use AM_MAINTAINER_MODE to keep the makefiles from trying to rebuild 
+    autoconf/automake stuff during the build (Closes: #117599)
+  * Regenerated everything AGAIN.
+  * Use DESTDIR instead of prefix= during "make install"
+
+ -- Matt Zimmerman <mdz@debian.org>  Mon, 29 Oct 2001 22:22:07 -0500
+
+rrdtool (1.0.33-7) unstable; urgency=low
+
+  * Add an example to the rrddump documentation showing what is necessary
+    to transfer an RRD from one architecture to another. (Closes: #117147)
+  * Regenerated autoconf/automake configs
+
+ -- Matt Zimmerman <mdz@debian.org>  Mon, 29 Oct 2001 02:28:16 -0500
+
+rrdtool (1.0.33-6) unstable; urgency=low
+
+  * Spelling corrections from Martin Schulze (Closes: #110784)
+
+ -- Matt Zimmerman <mdz@debian.org>  Sat,  8 Sep 2001 13:13:22 -0400
+
+rrdtool (1.0.33-5) unstable; urgency=low
+
+  * Ship with pre-generated files from autoconf2.50/automake, rather than
+    pulling them in at build-time, in order to work around the current
+    libtool mess.  This makes the diff less manageable, but should make
+    the build more stable.
+  * debian/control: remove build-dependencies on autoconf/automake/libtool
+  * debian/rules: don't call autoconf/automake
+  * Fix shlibs file (it was trying to use a substitution)
+  * configure.in: remove makefiles that no longer exist from AC_OUTPUT.
+    Apparently this is an error in autoconf 2.50.
+  * configure.in: Remove wacky logic to pull PIC flags out of libtool, and
+    let libtool do the right thing.
+  * debian/rules: shlibs.local hackery is no longer necessary, removed
+
+ -- Matt Zimmerman <mdz@debian.org>  Fri,  3 Aug 2001 17:36:43 -0400
+
+rrdtool (1.0.33-4) unstable; urgency=low
+
+  * Add build-dependencies on libtool and autoconf (Closes: #105655)
+
+ -- Matt Zimmerman <mdz@debian.org>  Tue, 17 Jul 2001 15:44:25 -0400
+
+rrdtool (1.0.33-3) unstable; urgency=low
+
+  * Update config.{sub,guess} from autotools-dev 20010702.1
+    (Closes: #105037)
+  * Update ltmain.sh for good measure, from libtool 1.4-1
+  * Build-Depend on autoconf, and run autoconf and aclocal from
+    debian/rules (in addition to automake)
+
+ -- Matt Zimmerman <mdz@debian.org>  Sat, 14 Jul 2001 03:19:22 -0400
+
+rrdtool (1.0.33-2) unstable; urgency=low
+
+  * Rebuild with latest tcl8.3-dev, to fix shared object extension
+
+ -- Matt Zimmerman <mdz@debian.org>  Sat,  3 Mar 2001 17:28:24 -0500
+
+rrdtool (1.0.33-1) unstable; urgency=low
+
+  * New upstream version.
+  * This is yet another patch release to fix some distribution/compilation
+    problems.  There should be no visible changes for Debian users from
+    1.0.32-3.
+
+ -- Matt Zimmerman <mdz@debian.org>  Thu,  1 Mar 2001 02:27:53 -0500
+
+rrdtool (1.0.32-3) unstable; urgency=low
+
+  * Fix the tcl extension module to build against tcl8.3, rather than
+    tcl8.0 (Closes: #87357)
+  * debian/control: Build-depend on tcl8.3-dev
+  * debian/rules: tclconfigdir = /usr/lib/tcl8.3
+  * tcl/Makefile.am: (upstream) -I/usr/include/tcl@TCL_VERSION@
+  * configure.in: (upstream) AC_SUBST(TCL_VERSION)
+
+ -- Matt Zimmerman <mdz@debian.org>  Sat, 24 Feb 2001 02:25:46 -0500
+
+rrdtool (1.0.32-2) unstable; urgency=low
+
+  * List changes to upstream source in the copyright file (as well as the
+    changelog)
+  * Link against libgd-gif instead of libgd, to restore GIF support
+
+ -- Matt Zimmerman <mdz@debian.org>  Fri, 23 Feb 2001 15:27:37 -0500
+
+rrdtool (1.0.32-1) unstable; urgency=low
+
+  * New upstream version.  Hopefully this one will last more than 24
+    hours (Closes: #75771)
+
+ -- Matt Zimmerman <mdz@debian.org>  Wed, 21 Feb 2001 20:13:09 -0500
+
+rrdtool (1.0.27-7) unstable; urgency=low
+
+  * debian/rules: Updated for new Perl policy
+  * debian/rules: Don't use install-stamp; it causes more problems than it
+    prevents
+  * debian/rules: Don't call dh_suidregister; we didn't use it anyway
+  * debian/control: Build-depend on the new debhelper for dh_perl
+  * debian/control: rrdtool-tcl only seems to work with 8.0, so depend on
+    tcl8.0
+  * debian/control: Build-depend on perl 5.6.0+
+  * debian/librrd?-perl: Updated for new Perl filesystem layout
+
+ -- Matt Zimmerman <mdz@debian.org>  Thu, 15 Feb 2001 19:04:11 -0500
+
+rrdtool (1.0.27-6) unstable; urgency=low
+
+  * debian/rules: Handle DEB_BUILD_OPTIONS debug, nostrip
+  * debian/rules: Separate out "configure" target
+  * debian/rules: Miscellaneous cleanup
+  * Recompile librrds-perl against perl-5.6
+
+ -- Matt Zimmerman <mdz@debian.org>  Tue, 19 Dec 2000 21:23:55 -0500
+
+rrdtool (1.0.27-5) unstable; urgency=low
+
+  * Fix Build-Depends to reflect our dependency on dpkg-dev >= 1.7.0 |
+    librrd0.  The old dpkg can only calculate our dependencies correctly
+    if the runtime library is installed on the system, so potato users
+    must either install the potato librrd0 or the new dpkg-dev
+    (Closes: #77479)
+
+ -- Matt Zimmerman <mdz@debian.org>  Mon, 20 Nov 2000 20:39:55 -0500
+
+rrdtool (1.0.27-4) unstable; urgency=low
+
+  * Updated to comply with policy 3.2.1.0:
+    - Don't build with -g by default
+  * Build-Depend on tcl8.0-dev (not tcl-dev), since that is the only
+    version we currently work with.
+  * Added automake to Build-Depends (oops)
+
+ -- Matt Zimmerman <mdz@debian.org>  Sat, 18 Nov 2000 22:29:02 -0500
+
+rrdtool (1.0.27-3) unstable; urgency=low
+
+  * Big packaging cleanup release.
+  * Fixed perl module compilation to link with the shared library, rather
+    than including a copy of the static library inside the .so (also fixes
+    a lintian error, shlib-with-non-pic-code in librrds-perl)
+  * Fixed tcl module compilation to link with the shared library, rather
+    than including a copy of the static library inside the .so (also fixes
+    a lintian error, shlib-with-non-pic-code in rrdtool-tcl)
+  * The above shrunk the size of the perl and tcl library packages from
+    60k each to about 20k (perl) and 16k (tcl)
+  * We can now use shlibdeps to calculate the dependencies for
+    librrds-perl, so those will be more correct now
+  * Rather than include Makefile.in changes in the .diff.gz, just keep our
+    modifications to Makefile.am and run automake in debian/rules.  This
+    makes the .diff.gz much smaller and cleaner
+  * Fixed shared library dependencies all around to be smarter
+  * rrdtool package now Suggests librrds-perl (rather than depending on
+    perl), as it is only needed for an auxiliary script for converting
+    mrtg data
+
+ -- Matt Zimmerman <mdz@debian.org>  Fri, 10 Nov 2000 19:34:05 -0500
+
+rrdtool (1.0.27-2) unstable; urgency=low
+
+  * Updated maintainer email address (now official maintainer)
+
+ -- Matt Zimmerman <mdz@debian.org>  Wed,  1 Nov 2000 22:44:41 -0500
+
+rrdtool (1.0.27-1) unstable; urgency=low
+
+  * New upstream version.
+
+ -- Matt Zimmerman <mdz@csh.rit.edu>  Wed, 13 Sep 2000 13:10:14 -0400
+
+rrdtool (1.0.26-1) unstable; urgency=low
+
+  * New upstream version.
+
+ -- Matt Zimmerman <mdz@csh.rit.edu>  Sun, 10 Sep 2000 16:34:08 -0400
+
+rrdtool (1.0.25-1) unstable; urgency=low
+
+  * New upstream version.
+
+ -- Matt Zimmerman <mdz@csh.rit.edu>  Sun, 23 Jul 2000 02:23:53 -0400
+
+rrdtool (1.0.24-2) unstable; urgency=low
+
+  * Updated for libgd 1.8.3 (build depends, recompile)
+
+ -- Matt Zimmerman <mdz@csh.rit.edu>  Fri,  7 Jul 2000 19:33:29 -0400
+
+rrdtool (1.0.24-1) unstable; urgency=low
+
+  * New upstream version (Closes: #61997).
+
+ -- Matt Zimmerman <mdz@csh.rit.edu>  Fri, 30 Jun 2000 13:53:27 -0400
+
+rrdtool (1.0.17-1) unstable; urgency=low
+
+  * New upstream version.
+  * Now creates rrdtool-tcl, containing tcl bindings.
+
+ -- Matt Zimmerman <mdz@csh.rit.edu>  Wed, 26 Apr 2000 13:16:24 -0700
+
+rrdtool (1.0.16-1) unstable; urgency=low
+
+  * New upstream version.
+  * Not released.
+
+ -- Matt Zimmerman <mdz@csh.rit.edu>  Wed, 26 Apr 2000 10:53:36 -0700
+
+rrdtool (1.0.13-2) unstable; urgency=low
+
+  * Build-Depends: cgilib, zlib1g-dev, libpng2-dev, libgd1g-dev, groff,
+    debhelper.  Hopefully that's everything.
+
+ -- Matt Zimmerman <mdz@csh.rit.edu>  Fri,  7 Apr 2000 14:16:55 -0700
+
+rrdtool (1.0.13-1) unstable; urgency=low
+
+  * New upstream version (Closes: #58583)
+  * Added Build-Depends for cgilib (Closes: #55270)
+
+ -- Matt Zimmerman <mdz@csh.rit.edu>  Thu, 23 Mar 2000 15:55:37 -0800
+
+rrdtool (1.0.10-1) unstable; urgency=low
+
+  * New upstream version. 
+
+ -- Matt Zimmerman <mdz@csh.rit.edu>  Mon, 10 Jan 2000 16:46:33 -0800
+
+rrdtool (1.0.7-3) unstable; urgency=low
+
+  * Non-i386 fixes, thanks to Christopher C Chimelis <chris@debian.org>:
+  * Changed 'clean' target in debian/rules to remove the auto-generated
+    Makefiles for the Perl modules
+  * Changed Depends: for librrds-perl to use shlibdeps
+
+ -- Matt Zimmerman <mdz@csh.rit.edu>  Mon, 22 Nov 1999 10:47:13 -0800
+
+rrdtool (1.0.7-2) unstable; urgency=low
+
+  * Linked RRDs.so against libpng, closes: #49701
+
+ -- Matt Zimmerman <mdz@csh.rit.edu>  Tue,  9 Nov 1999 15:02:40 -0800
+
+rrdtool (1.0.7-1) unstable; urgency=low
+
+  * Initial Release.
+  * Upstream source ships with cgilib-0.4, gd-1.3, libpng-1.0.3, and
+    zlib-1.3.3 (!).  Build process is modified to skip building these, and
+    instead link with the Debian versions.
+  * Modifications for gd 1.6.1 included removal of GIF support (no longer
+    included with gd as of 1.6)
+
+ -- Matt Zimmerman <mdz@csh.rit.edu>  Sat, 11 Sep 1999 13:29:29 -0700
+
+
diff --git a/program/debian/control b/program/debian/control
new file mode 100644 (file)
index 0000000..3a80bc5
--- /dev/null
@@ -0,0 +1,98 @@
+Source: rrdtool
+Section: utils
+Priority: extra
+Maintainer: Matt Zimmerman <mdz@debian.org>
+Standards-Version: 3.2.1
+Build-Depends: debhelper (>= 3.0.5), cgilib (>= 0.5), zlib1g-dev (>= 1.2.1), tcl8.4-dev, perl (>= 5.8.0), libart-2.0-dev (>= 2.3.16), libpng12-dev (>= 1.2.5), libfreetype6-dev (>= 2.1.7)
+
+Package: rrdtool
+Architecture: any
+Depends: ${shlibs:Depends}
+Suggests: librrds-perl
+Description: Time-series data storage and display system (programs)
+ 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 friendly user interface on it.
+ .
+ This package contains command line programs used to access and manipulate
+ RRDs.
+
+Package: librrd0
+Architecture: any
+Section: libs
+Priority: optional
+Depends: ${shlibs:Depends}
+Description: Time-series data storage and display system (runtime)
+ 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 friendly user interface on it.
+ .
+ This package contains shared libraries used to access and manipulate RRDs.
+
+Package: librrd0-dev
+Architecture: any
+Section: libdevel
+Depends: librrd0 (= ${Source-Version}), ${shlibs:Depends}
+Description: Time-series data storage and display system (development)
+ 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 friendly user interface on it.
+ .
+ This package contains libraries used to develop software that uses RRDs.
+
+Package: librrds-perl
+Architecture: any
+Section: perl
+Depends: ${perl:Depends}, ${shlibs:Depends}
+Description: Time-series data storage and display system (perl-shared)
+ 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 friendly user interface on it.
+ .
+ This package contains a Perl interface to RRDs using a shared library.
+
+Package: librrdp-perl
+Architecture: all
+Section: perl
+Depends: ${perl:Depends}, rrdtool
+Description: Time-series data storage and display system (perl-piped)
+ 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 friendly user interface on it.
+ .
+ This package contains a Perl interface to RRDs using command pipes.
+
+#NO_TCL Package: rrdtool-tcl
+#NO_TCL Architecture: any
+#NO_TCL Section: utils
+#NO_TCL Depends: ${shlibs:Depends}, tcl8.4
+#NO_TCL Description: Time-series data storage and display system (tcl)
+#NO_TCL  RRD is the Acronym for Round Robin Database. RRD is a system to store and
+#NO_TCL  display time-series data (i.e. network bandwidth, machine-room temperature,
+#NO_TCL  server load average). It stores the data in a very compact way that will
+#NO_TCL  not expand over time, and it presents useful graphs by processing the data
+#NO_TCL  to enforce a certain data density. It can be used either via simple wrapper
+#NO_TCL  scripts (from shell or Perl) or via frontends that poll network devices and
+#NO_TCL  put friendly user interface on it.
+#NO_TCL  .
+#NO_TCL  This package contains a tcl interface to RRDs.
diff --git a/program/debian/copyright b/program/debian/copyright
new file mode 100644 (file)
index 0000000..4af7fb0
--- /dev/null
@@ -0,0 +1,30 @@
+This package was debianized by Matt Zimmerman <mdz@debian.org> on
+Fri, 10 Sep 1999 10:53:19 -0700.
+
+Copied from 1.0.46 to 1.1.0 snapshot by Mike Slifcak on Wed May 12 20:53:16 EDT 2004
+
+The source package was downloaded from 
+   http://oss.oetiker.ch/rrdtool/pub/
+
+Upstream Author(s): Tobias Oetiker <tobi@oetiker.ch>
+
+Modifications to upstream source:
+       - Build and install shared libraries
+       - Link everything dynamically
+       - Link with Debian versions of libraries rather than using the
+         supplied (old) versions
+
+Copyright:
+
+ RRDTOOL - Round Robin Database Tool
+ A tool for fast logging of numerical data graphical display
+ of this data.
+
+ Copyright (c) 1998, 1999 Tobias Oetiker
+ All rights reserved.
+
+ RRDtool is distributed under the terms of the GNU General Public License,
+ version 2.
+
+ On Debian GNU/Linux systems, you can find a copy of the GNU General Public
+ License in `/usr/share/common-licenses/GPL'.
diff --git a/program/debian/librrd0-dev.files b/program/debian/librrd0-dev.files
new file mode 100644 (file)
index 0000000..957ce46
--- /dev/null
@@ -0,0 +1,5 @@
+usr/lib/librrd.a
+usr/lib/librrd.so
+usr/lib/librrd_th.a
+usr/lib/librrd_th.so
+usr/include/*
diff --git a/program/debian/librrd0.files b/program/debian/librrd0.files
new file mode 100644 (file)
index 0000000..c198f71
--- /dev/null
@@ -0,0 +1,6 @@
+usr/lib/librrd.so.1.0.0
+usr/lib/librrd.so.1
+usr/lib/librrd.la
+usr/lib/librrd_th.so.1.0.0
+usr/lib/librrd_th.so.1
+usr/lib/librrd_th.la
diff --git a/program/debian/librrd0.postinst b/program/debian/librrd0.postinst
new file mode 100644 (file)
index 0000000..8b92694
--- /dev/null
@@ -0,0 +1,49 @@
+#! /bin/sh
+# postinst script for #PACKAGE#
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <postinst> `configure' <most-recently-configured-version>
+#        * <old-postinst> `abort-upgrade' <new version>
+#        * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+#          <new-version>
+#        * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+#          <failed-install-package> <version> `removing'
+#          <conflicting-package> <version>
+# for details, see /usr/doc/packaging-manual/
+#
+# quoting from the policy:
+#     Any necessary prompting should almost always be confined to the
+#     post-installation script, and should be protected with a conditional
+#     so that unnecessary prompting doesn't happen if a package's
+#     installation fails and the `postinst' is called with `abort-upgrade',
+#     `abort-remove' or `abort-deconfigure'.
+
+case "$1" in
+    configure)
+
+    ldconfig
+
+    ;;
+
+    abort-upgrade|abort-remove|abort-deconfigure)
+
+    ;;
+
+    *)
+        echo "postinst called with unknown argument \`$1'" >&2
+        exit 0
+    ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/program/debian/librrd0.postrm b/program/debian/librrd0.postrm
new file mode 100644 (file)
index 0000000..a828228
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/sh
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <postrm> `remove'
+#        * <postrm> `purge'
+#        * <old-postrm> `upgrade' <new-version>
+#        * <new-postrm> `failed-upgrade' <old-version>
+#        * <new-postrm> `abort-install'
+#        * <new-postrm> `abort-install' <old-version>
+#        * <new-postrm> `abort-upgrade' <old-version>
+#        * <disappearer's-postrm> `disappear' <r>overwrit>r> <new-version>
+# for details, see /usr/share/doc/packaging-manual/
+
+case "$1" in
+    remove)
+        ldconfig
+        ;;
+
+    purge|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+        ;;
+
+    *)
+        echo "postrm called with unknown argument \`$1'" >&2
+        exit 0
+
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+
diff --git a/program/debian/librrd0.shlibs b/program/debian/librrd0.shlibs
new file mode 100644 (file)
index 0000000..d0e06dd
--- /dev/null
@@ -0,0 +1 @@
+librrd 0       librrd0 (>= 1.0.10-1)
diff --git a/program/debian/librrdp-perl.files b/program/debian/librrdp-perl.files
new file mode 100644 (file)
index 0000000..75adff6
--- /dev/null
@@ -0,0 +1,2 @@
+usr/lib/perl5/RRDp.pm
+usr/share/man/man3/RRDp.3pm
diff --git a/program/debian/librrds-perl.files b/program/debian/librrds-perl.files
new file mode 100644 (file)
index 0000000..5e50edb
--- /dev/null
@@ -0,0 +1,3 @@
+usr/lib/perl5/auto
+usr/lib/perl5/RRDs.pm
+usr/share/man/man3/RRDs.3pm
diff --git a/program/debian/rrdtool-tcl.files b/program/debian/rrdtool-tcl.files
new file mode 100644 (file)
index 0000000..af83bca
--- /dev/null
@@ -0,0 +1 @@
+usr/lib/tclrrd*
diff --git a/program/debian/rrdtool.files b/program/debian/rrdtool.files
new file mode 100644 (file)
index 0000000..b08aa5d
--- /dev/null
@@ -0,0 +1,4 @@
+usr/bin
+usr/share/man/man1
+usr/share/doc/rrdtool
+usr/share/rrdtool
diff --git a/program/debian/rules b/program/debian/rules
new file mode 100755 (executable)
index 0000000..6ddfafe
--- /dev/null
@@ -0,0 +1,178 @@
+#!/usr/bin/make -f
+#-*- makefile -*-
+# Made with the aid of dh_make, by Craig Small
+# Sample debian/rules that uses debhelper. GNU copyright 1997 by Joey Hess.
+# This version is for a hypothetical package that builds an
+# architecture-dependant package, as well as an architecture-independant
+# package.
+
+package:=rrdtool
+
+top_srcdir:=$(shell pwd)
+tmp:=$(top_srcdir)/debian/tmp
+
+# TCL installation stuff (must match tcl/Makefile.am)
+tclconfigdir = /usr/lib/tcl8.4
+
+# Uncomment this to turn on verbose mode. 
+#export DH_VERBOSE=1
+
+# This is the debhelper compatability version to use.
+export DH_COMPAT=2
+
+# Defaults
+CFLAGS := -O2
+
+# Handle DEB_BUILD_OPTIONS
+ifneq "$(findstring debug,$(DEB_BUILD_OPTIONS))" ""
+CFLAGS += -g
+endif
+
+# Whack the flags to compile additional dependent pieces
+CFLAGS += -I/usr/include/libart-2.0 -I/usr/include/freetype2
+
+##NO_TCL CFLAGS += -I/usr/include/tcl8.4
+
+configure: configure-stamp
+configure-stamp:
+       dh_testdir
+       # Make sure we can find tcl stuff (configure won't fail)
+       test -f $(tclconfigdir)/tclConfig.sh
+
+       # match the configure script to the build environment, as needed
+       #aclocal
+       #automake
+       #autoconf
+
+       # Configure C and Tcl stuff
+##NO_TCL       CFLAGS="$(CFLAGS)" ./configure --prefix=/usr --enable-shared=yes --enable-static=yes --with-tcllib=$(tclconfigdir) --enable-local-libpng=yes --enable-local-zlib=yes
+       CFLAGS="$(CFLAGS)" ./configure --prefix=/usr --enable-shared=yes --enable-static=yes --enable-local-libpng=yes --enable-local-zlib=yes
+
+       # Configure Perl stuff
+       cd bindings/perl-piped && \
+               rm -f Makefile && \
+               perl Makefile.PL INSTALLDIRS=vendor
+
+       cd bindings/perl-shared && \
+               rm -f Makefile && \
+               perl Makefile.PL INSTALLDIRS=vendor
+
+       touch configure-stamp
+
+build: build-stamp
+build-stamp: configure-stamp
+       dh_testdir
+
+       # Build most stuff
+       $(MAKE)
+
+       # We now build perl-piped and perl-shared separately
+       cd bindings/perl-piped && make OPTIMIZE="$(CFLAGS)"
+       cd bindings/perl-shared && make OPTIMIZE="$(CFLAGS)"
+
+       touch build-stamp
+
+clean:
+       dh_testdir
+       dh_testroot
+       rm -f build-stamp configure-stamp
+
+       # Add here commands to clean up after the build process.
+       -$(MAKE) distclean
+
+       -rm bindings/perl*/Makefile.old bindings/perl*/Makefile
+
+       dh_clean
+
+install: build-stamp
+       dh_testdir
+       dh_testroot
+       dh_clean -k
+       dh_installdirs
+
+       # Add here commands to install the package into debian/tmp.
+##NO_TCL       $(MAKE) install site-tcl-install DESTDIR=$(tmp)
+       $(MAKE) install DESTDIR=$(tmp)
+
+       mkdir -p $(tmp)/usr/share/doc
+       mv $(tmp)/usr/doc $(tmp)/usr/share/doc/rrdtool
+       mkdir -p $(tmp)/usr/share/rrdtool
+       mv $(tmp)/usr/examples $(tmp)/usr/share/rrdtool
+       mv $(tmp)/usr/html $(tmp)/usr/share/rrdtool
+
+       ## touch-up perl docs
+       mv $(tmp)/usr/man $(tmp)/usr/share
+       mkdir -p $(tmp)/usr/share/man/man3
+       mv $(tmp)/usr/share/man/man1/RRDs.1 $(tmp)/usr/share/man/man3/RRDs.3pm
+       mv $(tmp)/usr/share/man/man1/RRDp.1 $(tmp)/usr/share/man/man3/RRDp.3pm
+       mv $(tmp)/usr/lib/perl $(tmp)/usr/lib/perl5
+
+       ## trim off the doc sources
+       find $(tmp) -name "*.pod" | xargs rm -f
+
+       dh_movefiles
+
+# Build architecture-independent files here.
+binary-indep: build install
+#      dh_testversion
+       dh_testdir -i
+       dh_testroot -i
+       dh_installdocs -i
+#      dh_installexamples -i
+#      dh_installmenu -i
+#      dh_installemacsen -i
+#      dh_installpam -i
+#      dh_installinit -i
+#      dh_installcron -i
+#      dh_installmanpages -i
+#      dh_installinfo -i
+#      dh_undocumented
+#LATER dh_installchangelogs -i ChangeLog
+       dh_link -i
+       dh_compress -i
+       dh_fixperms -i
+       # You may want to make some executables suid here.
+#      dh_suidregister -i
+       dh_installdeb -i
+       dh_perl -i
+       dh_gencontrol -i
+       dh_md5sums -i
+       dh_builddeb -i
+
+# Build architecture-dependent files here.
+binary-arch: build install
+#      dh_testversion
+       dh_testdir -a
+       dh_testroot -a
+       dh_installdocs -a
+#      dh_installexamples -a
+#      dh_installmenu -a
+#      dh_installemacsen -a
+#      dh_installpam -a
+#      dh_installinit -a
+#      dh_installcron -a
+#      dh_installmanpages -a
+#      dh_installinfo -a
+#      dh_undocumented
+#LATER dh_installchangelogs -a ChangeLog
+ifeq "$(findstring nostrip,$(DEB_BUILD_OPTIONS))" ""
+       dh_strip -a
+endif
+       dh_link -a
+       dh_compress -a -Xexamples/
+       dh_fixperms -a
+       # You may want to make some executables suid here.
+#      dh_suidregister -a
+#      dh_makeshlibs -a
+       dh_installdeb -a
+       dh_perl -a
+       dh_shlibdeps -a -ldebian/librrd0/usr/lib
+       dh_gencontrol -a
+       dh_md5sums -a
+       dh_builddeb -a
+
+source diff:                                                                  
+       @echo >&2 'source and diff are obsolete - use dpkg-source -b'; false
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install
diff --git a/program/debian/watch b/program/debian/watch
new file mode 100644 (file)
index 0000000..bf1b626
--- /dev/null
@@ -0,0 +1 @@
+http://oss.oetiker.ch  /rrdtool/pub    rrdtool-(.*)\.tar\.gz   debian
diff --git a/program/doc/Makefile.am b/program/doc/Makefile.am
new file mode 100644 (file)
index 0000000..70f456c
--- /dev/null
@@ -0,0 +1,72 @@
+## Process this file with automake to produce Makefile.in
+
+SUFFIXES = .pod .1 .man .html .txt .pm .pdf .inc
+
+#AUTOMAKE_OPTIONS        =  foreign
+
+#ACLOCAL_M4 = $(top_srcdir)/config/aclocal.m4
+
+CLEANFILES = *.1 *.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                           \
+      rrd-beginners.pod      rrdinfo.pod            rrdtune.pod            rrdbuild.pod                          \
+      rrdcgi.pod             rrdgraph.pod           rrdlast.pod            rrdlastupdate.pod                     \
+      rrdcreate.pod          rrdgraph_data.pod      rrdresize.pod          rrdtutorial.pod                       
+
+
+PMP = RRDs.pod RRDp.pod
+
+MAN = $(POD:.pod=.1)
+TXT = $(MAN:.1=.txt)
+HTML = $(POD:.pod=.html) $(PMP:.pod=.html)
+PDF = $(MAN:.1=.pdf)
+
+# what should go into the distribution
+EXTRA_DIST= $(POD) $(HTML) $(MAN) $(TXT) rrdtool-dump.dtd rrdtool-xport.dtd
+
+idocdir = $(RRDDOCDIR)/txt
+idoc_DATA = $(POD) $(TXT)
+ihtmldir = $(RRDDOCDIR)/html
+ihtml_DATA = $(HTML)
+imandir = $(mandir)/man1
+iman_DATA = $(MAN)
+
+all-local: link txt man 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:
+       pod2man --release=$(VERSION) --center=rrdtool $<  > $@
+
+.1.txt:
+       GROFF_NO_SGR=1 @NROFF@ -man -Tlp $< > $@
+
+.1.pdf:
+       @TROFF@ -man $< | ps2pdf - $@
+
+.pm.html .pod.html .pl.html:
+       pod2html --infile=$< --outfile=$@ --noindex --htmlroot=. --podpath=. --title=$*
+
+RRDs.pod:
+       $(LN_S) $(top_srcdir)/bindings/perl-shared/RRDs.pm RRDs.pod
+
+RRDp.pod:
+       $(LN_S) $(top_srcdir)/bindings/perl-piped/RRDp.pm RRDp.pod
+
+link: RRDp.pod RRDs.pod
+
+man: $(MAN)
+
+html-local: $(HTML)
+
+txt: $(TXT)
+
+pdf-local: $(PDF)
+
+pod: $(POD)
+
+install-data-hook:
+       cd $(DESTDIR)$(ihtmldir) && rm -f index.html && $(LN_S) rrdtool.html index.html
diff --git a/program/doc/bin_dec_hex.pod b/program/doc/bin_dec_hex.pod
new file mode 100644 (file)
index 0000000..7a2adf5
--- /dev/null
@@ -0,0 +1,371 @@
+=head1 NAME
+
+bin_dec_hex - How to use binary, decimal, and hexadecimal notation.
+
+=for html <div align="right"><a href="bin_dec_hex.pdf">PDF</a> version.</div>
+
+=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 represenatation (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 ub 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@ergens.op.het.netE<gt>
diff --git a/program/doc/cdeftutorial.pod b/program/doc/cdeftutorial.pod
new file mode 100644 (file)
index 0000000..6a45ef4
--- /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@ergens.op.het.netE<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,GE,TIME,endtime,LE,*,UNKN,ds0,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@ergens.op.het.netE<gt>
+
+Remember: B<No feedback equals no changes!>
+
+=head1 SEE ALSO
+
+The RRDtool manpages
+
+=head1 AUTHOR
+
+Alex van den Bogaerdt
+E<lt>alex@ergens.op.het.netE<gt>
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..b3beac1
--- /dev/null
@@ -0,0 +1,198 @@
+=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 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,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 againg and--voila!--you have a 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..5ac7997
--- /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 datapoints 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 diskspace 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..7a24a82
--- /dev/null
@@ -0,0 +1,205 @@
+=head1 NAME
+
+rrdbuild - Instructions for building RRDtool
+
+=head1 DESCRIPTION
+
+=head2 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 thrid 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.
+
+=head2 Building
+
+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.
+Depending on the shell you are using, you can do either (bash,zsh):
+
+ BUILD_DIR=/tmp/rrdbuild
+ INSTALL_DIR=/usr/local/rrdtool-1.2.23
+
+Or if you run tcsh:
+
+ set BUILD_DIR=/tmp/rrdbuild
+ set INSTALL_DIR=/usr/local/rrdtool-1.2.23
+
+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. Note that these instructions 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.
+
+ wget http://oss.oetiker.ch/rrdtool/pub/rrdtool-1.2.23.tar.gz
+ tar zxf rrdtool-1.2.23.tar.gz
+ cd rrdtool-1.2.23
+ ./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. If you are on a
+Linux or *bsd system you may want to just install the missing bits from your
+software repository. When you do that, make sure you also get the B<-dev>
+package for each library you install. Once you have the missing bits on
+board, just re-run the last line of the instructions above.
+
+But again this may have been too optimistic, and you actually have to
+compile your own copies of the required libraries.
+
+=head3 Build Tipps for AIX
+
+If you are woking with AIX, you may find the 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 Building Libraries
+
+In order to build the libraries you need a compiler on your system.
+Unfortunately compilers are not all alike. This has an effect on the CFLAGS
+you want to set. The examples below are for the popular GCC compiler suite.
+If you have an other compile you have to use the following settings:
+
+=over
+
+=item Sun Forte
+
+ CFLAGS="-xO3 -kPIC"
+
+=back
+
+=over 
+
+=item Building zlib
+
+ cd $BUILD_DIR
+ wget http://oss.oetiker.ch/rrdtool/pub/libs/zlib-1.2.3.tar.gz
+ tar  zxf zlib-1.2.3.tar.gz
+ cd zlib-1.2.3
+ env CFLAGS="-O3 -fPIC" ./configure --prefix=$BUILD_DIR/lb
+ make
+ make install
+
+=item 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 likley) 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.10.tar.gz
+ tar zxvf libpng-1.2.10.tar.gz
+ cd libpng-1.2.10
+ env CPPFLAGS="-I$BUILD_DIR/lb/include" LDFLAGS="-L$BUILD_DIR/lb/lib" CFLAGS="-O3 -fPIC" \
+     ./configure --disable-shared --prefix=$BUILD_DIR/lb
+ make
+ make install
+
+=item Building freetype
+
+ cd $BUILD_DIR
+ wget http://oss.oetiker.ch/rrdtool/pub/libs/freetype-2.1.10.tar.bz2
+ tar jxvf freetype-2.1.10.tar.bz2
+ cd freetype-2.1.10
+ env CPPFLAGS="-I$BUILD_DIR/lb/include" LDFLAGS="-L$BUILD_DIR/lb/lib" CFLAGS="-O3 -fPIC" \
+     ./configure --disable-shared --prefix=$BUILD_DIR/lb
+ make
+ make install
+
+If you run into problems building freetype on Solaris, you may want to try to
+add the following at the end of the configure line:
+
+ GNUMAKE=gmake EGREP=egrep
+
+=item Building libart_lgpl
+
+ cd $BUILD_DIR
+ wget http://oss.oetiker.ch/rrdtool/pub/libs/libart_lgpl-2.3.17.tar.gz
+ tar zxvf libart_lgpl-2.3.17.tar.gz
+ cd libart_lgpl-2.3.17
+ env CFLAGS="-O3 -fPIC" ./configure --disable-shared --prefix=$BUILD_DIR/lb
+ make
+ make install
+
+=back
+
+Now all the dependent libraries are built and you can try again. Since these
+are static libraries, you may have to use F<ranlib> to make them accessible.
+Especially BSD systems like Mac OS X may require this, Linux and Solaris
+will do just fine without since their F<ar> command does ranlibs job as well.
+
+ ranlib $BUILD_DIR/lb/lib/*.a
+
+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. Under csh/tcsh you use:
+
+ set IR=-I$BUILD_DIR/lb/include
+ setenv CPPFLAGS "$IR $IR/libart-2.0 $IR/freetype2 $IR/libpng"
+ setenv LDFLAGS  -L$BUILD_DIR/lb/lib
+ setenv CFLAGS -O3
+
+If you are running bash/sh/ash/ksh/zsh use this:
+
+ IR=-I$BUILD_DIR/lb/include
+ CPPFLAGS="$IR $IR/libart-2.0 $IR/freetype2 $IR/libpng"
+ LDFLAGS="-L$BUILD_DIR/lb/lib"
+ CFLAGS=-O3
+ export CPPFLAGS LDFLAGS CFLAGS
+
+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.2.23
+ ./configure --prefix=$INSTALL_DIR --disable-python --disable-tcl
+ 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/rrdcgi.pod b/program/doc/rrdcgi.pod
new file mode 100644 (file)
index 0000000..4c81e01
--- /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 seperate 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..27ef702
--- /dev/null
@@ -0,0 +1,553 @@
+=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<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.
+
+=over 8
+
+=item 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.
+
+=item 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.
+
+=item 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>.
+
+=item 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 4
+
+=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.
+
+=over
+
+=item 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.
+
+=back
+
+=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.
+
+=item 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>. 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>.
+
+=back
+
+=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<SEASONAL>B<:>I<seasonal period>B<:>I<gamma>B<:>I<rra-num>
+
+=item *
+
+B<RRA:>I<DEVSEASONAL>B<:>I<seasonal period>B<:>I<gamma>B<:>I<rra-num>
+
+=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 HWPREDICT, SEASONAL, DEVSEASONAL, and
+DEVPREDICT must exist. Generating smoothed values of the primary data points
+requires both a HWPREDICT B<RRA> and SEASONAL B<RRA>. Aberrant behavior
+detection requires FAILURES, HWPREDICT, DEVSEASONAL, and SEASONAL.
+
+The actual predicted, or smoothed, values are stored in the HWPREDICT
+B<RRA>. 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<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 at arbitrary times. From these it builds Primary
+Data Points (PDPs) at exact times on every "step" interval. The PDPs are
+then accumulated into RRAs.
+
+The "heartbeat" defines the maximum acceptable interval between
+samples. 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 even an "unknown" input sample.
+
+The known rates during a PDP's "step" interval are used to calculate
+an average rate for that PDP. Also, if the total "unknown" time during
+the "step" interval exceeds the "heartbeat", 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 "unknown"
+time to exceed "heartbeat" and hence mark the whole PDP "unknown". So
+"heartbeat" is not only the maximum acceptable interval between
+samples, but also the maximum acceptable amount of "unknown" time per
+PDP (obviously this is only significant if you have "heartbeat" less
+than "step").
+
+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 > "hb"
+        |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 mailserver 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..2e4cd4b
--- /dev/null
@@ -0,0 +1,62 @@
+=head1 NAME
+
+rrddump - dump the contents of an RRD to XML format
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<dump> I<filename.rrd> E<gt> I<filename.xml>
+
+or 
+
+B<rrdtool> B<dump> I<filename.rrd> 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.
+
+=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 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..51b5ccd
--- /dev/null
@@ -0,0 +1,262 @@
+=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>]>
+
+=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.
+
+=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 AUTHOR
+
+Tobias Oetiker <tobi@oetiker.ch>
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/rrdgraph-old.pod b/program/doc/rrdgraph-old.pod
new file mode 100644 (file)
index 0000000..f35e0cd
--- /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 autoconfiguration 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> gridstep, 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 behaviour. 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..9060bd8
--- /dev/null
@@ -0,0 +1,398 @@
+=head1 NAME
+
+rrdgraph - Round Robin Database tool grapher functions
+
+=head1 SYNOPSIS
+
+B<rrdtool graph> 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 datapoint 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.
+
+=head1 OPTIONS
+
+=over 4
+
+=item 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.
+
+=item 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 behaviour.
+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.
+
+=item 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.
+
+=item Size
+
+[B<-w>|B<--width> I<pixels>]
+[B<-h>|B<--height> I<pixels>]
+[B<-j>|B<--only-graph>]
+
+The width and height of the B<canvas> (the part of the graph with
+the actual data and such). This defaults to 400 pixels by 100 pixels.
+
+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.
+
+=item 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 behaviour 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 effects gridlines are placed on
+integer pixel values. This is by default done by extending
+the scale so that gridlines happens to be spaced using an
+integer number of pixels and also start on an integer pixel value.
+This might extend the scale too much for some logarithmic scales
+and for linear scales where B<--alt-autoscale> is needed.
+Using B<--no-gridfit> disables modification of the scale.
+
+=item Grid
+
+=over 4
+
+=item 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 autoconfiguration
+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:0:%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.
+
+=item 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 supressed, 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 autoscaler 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
+fideling 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.
+
+=back
+
+=item Miscellaneous
+
+[B<-z>|B<--lazy>]
+
+Only generate the graph if the current graph is out of date or not
+existent.
+
+[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<--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.
+
+Use Times for the title: C<--font TITLE:13:/usr/lib/fonts/times.ttf>
+
+If you do not give a font string you can modify just the sice 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 usefull for altering the default font without
+resetting the default fontsizes: C<--font DEFAULT:0:/usr/lib/fonts/times.ttf>.
+
+RRDtool comes with a preset default font. You can set the environment
+variable C<RRD_DEFAULT_FONT> if you want to change this.
+
+Truetype fonts are only supported for PNG output. See below.
+
+[B<-R>|B<--font-render-mode> {I<normal>,I<light>,I<mono>}]
+
+This lets you customize the strength of the font smoothing,
+or disable it entirely using I<mono>. By default, I<normal>
+font smoothing is used.
+
+[B<-B>|B<--font-smoothing-threshold> I<size>]
+
+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<-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>]
+
+If images are interlaced they become visible on browsers more quickly.
+
+[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
+behaviour of pre 1.0.42 versions).
+
+[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 centred, at the bottom 
+of the graph.
+
+=item 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.
+
+=item 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.
+
+=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@ergens.op.het.netE<gt>
+
diff --git a/program/doc/rrdgraph_data.pod b/program/doc/rrdgraph_data.pod
new file mode 100644 (file)
index 0000000..6c8b28a
--- /dev/null
@@ -0,0 +1,106 @@
+=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 save bet it to use lowercase or
+mixedcase 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 behaviour 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 agregation 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 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@ergens.op.het.netE<gt>
diff --git a/program/doc/rrdgraph_examples.pod b/program/doc/rrdgraph_examples.pod
new file mode 100644 (file)
index 0000000..64e3840
--- /dev/null
@@ -0,0 +1,137 @@
+=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 4
+
+=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 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
+
+=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.
+
+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@ergens.op.het.netE<gt>
diff --git a/program/doc/rrdgraph_graph.pod b/program/doc/rrdgraph_graph.pod
new file mode 100644 (file)
index 0000000..6546ab5
--- /dev/null
@@ -0,0 +1,379 @@
+=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<HRULE>B<:>I<value>B<#>I<color>[B<:>I<legend>]
+
+B<LINE>[I<width>]B<:>I<value>[B<#>I<color>][B<:>[I<legend>][B<:STACK>]]
+
+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>
+
+=cut
+
+#
+#B<PART>B<:>I<vname>B<#>I<rrggbb>[I<aa>][B<:>I<legend>]
+#
+
+=pod
+
+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.
+
+=head1 PRINT
+
+=over 4
+
+
+=item 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 4
+
+=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 clibrary. 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 4
+
+=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<%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
+
+=item 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.
+
+=back
+
+=head1 GRAPH
+
+=over 4
+
+=item B<GPRINT>B<:>I<vname>B<:>I<format>
+
+This is the same as C<PRINT>, but printed inside the graph.
+
+=item 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.
+
+=item 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<'\:'>.
+
+=item B<VRULE>B<:>I<time>B<#>I<color> [B<:>I<legend> ]
+
+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.
+
+=item B<HRULE>B<:>I<value>B<#>I<color> [ :I<legend> ]
+
+Draw a horyzontal 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.
+
+=item B<LINE>[I<width>]B<:>I<value>[B<#>I<color>][B<:>[I<legend>][B<:STACK>]]
+
+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>.
+
+When you do not specify a color, you cannot specify a legend.  Should
+you want to use STACK, use the "LINEx:<value>::STACK" form.
+
+=item 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.
+
+=item 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 normaly
+start at the lower edge of the graphing area. If the fraction is negative they start
+at the upper border of the graphing area.
+
+=item 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.
+
+=cut
+
+# This section describes the curruently 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
+
+=item B<STACK>B<:>I<vname>B<#>I<color>[B<:>I<legend>]
+
+I<Deprecated.  Use the B<STACK> modifiers on the other commands.>
+
+=back
+
+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 behaviour 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 are using the proportional font in your graph, you can use tab
+characters or the sequence B<\t> to line-up legend elements. Note that
+the tabs inserted are relative to the start of the current legend
+element!
+
+=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@ergens.op.het.netE<gt>
diff --git a/program/doc/rrdgraph_rpn.pod b/program/doc/rrdgraph_rpn.pod
new file mode 100644 (file)
index 0000000..b2a84dd
--- /dev/null
@@ -0,0 +1,312 @@
+=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>. 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<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>
+
+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)
+
+=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 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 transfered
+when you have logged bytes per second. The time component returns
+the number of seconds.
+
+Example: C<VDEF:total=mydata,TOTAL>
+
+=item PERCENT
+
+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.
+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>
+
+=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@ergens.op.het.netE<gt>
diff --git a/program/doc/rrdinfo.pod b/program/doc/rrdinfo.pod
new file mode 100644 (file)
index 0000000..e83d8d6
--- /dev/null
@@ -0,0 +1,62 @@
+=head1 NAME
+
+rrdinfo - extract header information from an RRD
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<info> I<filename.rrd>
+
+=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.
+
+=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
+ 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
+
+=over 8
+
+=item I<filename.rrd>
+
+The name of the B<RRD> you want to examine.
+
+=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..a3eb7fe
--- /dev/null
@@ -0,0 +1,27 @@
+=head1 NAME
+
+rrdlast - Return the date of the last data sample in an RRD
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<last> I<filename>
+
+=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.
+
+=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..37013cf
--- /dev/null
@@ -0,0 +1,27 @@
+=head1 NAME
+
+rrdlastupdate - Return the most recent update to an RRD
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<lastupdate> I<filename>
+
+=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.
+
+=back
+
+=head1 AUTHOR
+
+Andy Riebs <andy.riebs@hp.com>
+
+
+
diff --git a/program/doc/rrdpython.pod b/program/doc/rrdpython.pod
new file mode 100644 (file)
index 0000000..dc329ec
--- /dev/null
@@ -0,0 +1,59 @@
+=head1 NAME
+
+rrdpython - About the RRD Python bindings
+
+=head1 SYNOPSIS
+
+ import rrdtool
+ rrdtool.create('/tmp/test.rrd', 'DS:foo:GUAGE: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 simply expects string parameters to the functions.  Please refer
+to the other B<rrdtool> documentation for functions and valid arguments.
+
+=head1 EXAMPLE
+
+ sys.path.append('/path/to/rrdtool/lib/python2.3/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']
+
+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..917f3fe
--- /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@ergens.op.het.net>
+
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..9633447
--- /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) = 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..ace7ee8
--- /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<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-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..cd3c785
--- /dev/null
@@ -0,0 +1,312 @@
+=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<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 logfile at any time you
+want. It will automatically interpolate the value of the data-source
+(B<DS>) at the latest official time-slot (intervall) 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 realy 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 SEE ALSO
+
+rrdcreate, rrdupdate, rrdgraph, rrddump, rrdfetch, rrdtune, rrdlast, rrdxport
+
+=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..5e9029b
--- /dev/null
@@ -0,0 +1,170 @@
+=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<--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<--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 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 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..16173f0
--- /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">Versión <a href="rrdtutorial.es.pdf">PDF</a></div>
+
+=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.
+
+Alex van den Bogaerdt <alex@ergens.op.het.net>
diff --git a/program/doc/rrdtutorial.pod b/program/doc/rrdtutorial.pod
new file mode 100644 (file)
index 0000000..385ed08
--- /dev/null
@@ -0,0 +1,1178 @@
+=head1 NAME
+
+rrdtutorial - Alex van den Bogaerdt's RRDtool tutorial
+
+=head1 DESCRIPTION
+
+RRDtool is written by Tobias Oetiker <tobi@oetiker.ch> with
+contributions from many people all around the world. This document is
+written by Alex van den Bogaerdt <alex@ergens.op.het.net> 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.
+
+=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)
+transfered 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 you 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 transfered. 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 4'294'967'295. 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 transfered ***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 (= 1'000 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 12'345 KM until that moment.
+At 12:10 you look again, it reads 12'357 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 12'000 meters. We did that in five
+minutes or 300 seconds. Our speed is 12'000M / 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 1'000.
+
+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 920'804'400 seconds as explained
+below). Our database holds one data source (DS) named "speed" that
+represents a counter. This counter is read every five minutes
+(default).  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.  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.
+
+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 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 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 (12'357-12'345) and divided
+by 300 this makes 0.04, which is displayed by RRDtool as "40 m"
+meaning "40/1'000". The "m" (milli) has nothing to do with meters,
+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
+(12'357'000-12'345'000)/300 = 12'000/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: Make sure not to forget the backslash \ in front of the
+multiplication operator * above. The backslash is needed to "escape"
+the * as some operating systems might interpret and expand * instead
+of passing it to the rrdtool command.
+
+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 1'000, 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 * 3'600
+ Calculate kilometers per hour: value / 1'000
+ Together this makes:           value * (3'600/1'000) or value * 3.6
+
+In our example database we made a mistake and we need to compensate for
+this by multiplying with 1'000. Applying that correction:
+
+ value * 3.6  * 1'000 == value * 3'600
+
+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: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"
+
+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 "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.
+
+Most (?) people interested in RRDtool will use the counter that keeps track
+of octets (bytes) transfered 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=86'400, we
+start at now minus 86'400 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 ana 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 resulte instead of the raw data.  You know
+about the difference between averages and maxima, 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 (12'345, 12'357, 12'363, 12'363) 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 2'400 and 0, with 1'800 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 999'987. We travel 20 KM and
+the counter should go to 1'000'007. Unfortunately, there are only six digits
+on our counter so it really shows 000'007. If we would plot that on a type
+DERIVE, it would mean that the counter was set back 999'980 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 - 999'987 = -999'980    (instead of 1'000'007-999'987=20)
+
+ Real delta = -999'980 + 999'999 + 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 ..           4'294'967'295
+ - 64 bits: 0 .. 18'446'744'073'709'551'615
+
+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: (4'294'967'295 + 1) =                                4'294'967'296
+ - 64 bits: (18'446'744'073'709'551'615 + 1)
+                                   - correction1 = 18'446'744'069'414'584'320
+
+ Before:        4'294'967'200
+ Increase:                100
+ Should become: 4'294'967'300
+ But really is:             4
+ Delta:        -4'294'967'196
+ Correction1:  -4'294'967'196 + 4'294'967'296 = 100
+
+ Before:        18'446'744'073'709'551'000
+ Increase:                             800
+ Should become: 18'446'744'073'709'551'800
+ But really is:                        184
+ Delta:        -18'446'744'073'709'550'816
+ Correction1:  -18'446'744'073'709'550'816
+                               + 4'294'967'296 = -18'446'744'069'414'583'520
+ Correction2:  -18'446'744'069'414'583'520
+                  + 18'446'744'069'414'584'320 = 800
+
+ Before:        18'446'744'073'709'551'615 ( maximum value )
+ Increase:      18'446'744'069'414'584'320 ( absurd increase, minimum for
+ Should become: 36'893'488'143'124'135'935             this example to work )
+ But really is: 18'446'744'069'414'584'319
+ Delta:                     -4'294'967'296
+ Correction1:  -4'294'967'296 + 4'294'967'296 = 0
+ (not negative -> no correction2)
+
+ Before:        18'446'744'073'709'551'615 ( maximum value )
+ Increase:      18'446'744'069'414'584'319 ( one less increase )
+ Should become: 36'893'488'143'124'135'934
+ But really is: 18'446'744'069'414'584'318
+ Delta:                     -4'294'967'297
+ Correction1:  -4'294'967'297 + 4'294'967'296 = -1
+ Correction2:  -1 + 18'446'744'069'414'584'320 = 18'446'744'069'414'584'319
+
+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 920'805'000
+to 920'805'900 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
+easly 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@ergens.op.het.netE<gt>
+
diff --git a/program/doc/rrdupdate.pod b/program/doc/rrdupdate.pod
new file mode 100644 (file)
index 0000000..cc0b452
--- /dev/null
@@ -0,0 +1,101 @@
+=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<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.
+
+=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<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>.
+
+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 EXAMPLE
+
+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.
+
+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.
+
+=head1 AUTHOR
+
+Tobias Oetiker <tobi@oetiker.ch>
+
diff --git a/program/doc/rrdxport.pod b/program/doc/rrdxport.pod
new file mode 100644 (file)
index 0000000..a668a20
--- /dev/null
@@ -0,0 +1,144 @@
+=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<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<--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 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
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..9e7dc84
--- /dev/null
@@ -0,0 +1,16 @@
+## Process this file with automake to produce Makefile.in
+
+#AUTOMAKE_OPTIONS        =  foreign
+
+#ACLOCAL_M4 = $(top_srcdir)/config/aclocal.m4
+
+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: cgi-demo.cgi.in $(top_builddir)/config.status
+       sed 's,@''exec_prefix@,$(exec_prefix),' 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..b10f59f
--- /dev/null
@@ -0,0 +1,193 @@
+#! @PERL@
+#
+# $Id:$
+#
+# Created By Tobi Oetiker <tobi@oetiker.ch>
+# Date 2006-10-27
+#
+#makes programm work AFTER install
+
+use lib qw( @prefix@/lib/perl );
+
+print <<NOTE;
+
+RRDtool Performance Tester
+--------------------------
+Runnion 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 in your machine.
+
+This test tries to cater for this. It works like this:
+
+1) Create 100 RRD files (and sync them to disk)
+
+2) Update the 100 RRD file three times in a row.
+   We run the Update several times to see the difference
+   it makes in the cache.
+
+3) Go back to 1)
+
+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");
+#  print join("",map {"  $_ " . $ret->{$_}."\n" } grep /AVERAGE.\[1\]/, sort keys %$ret)."\n** $time\n\n";
+  # sync updates to disk immediately  
+#  usleep(1) if (rand(3) <1 );
+  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;
+    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;
+       print STDERR ".";
+    }
+   for (@files){ 
+       my $fd = new IO::File("$_.rrd","r");
+       if (defined $fd) {
+           $fd->sync;
+           $fd->close;
+        } else {
+            warn "failed to sync $_\n";
+        }        
+    }
+    return $count;
+}
+    
+    
+sub main (){
+    mkdir "db-$$" or die $!;
+    chdir "db-$$";
+
+    my $step = 100000; # 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;
+    while (1) {
+        # enhance the track
+           $time += 300;
+        $tracksize += makerrds $step,$tracksize,\@path,$time;            
+        # run benchmark
+        for (0..10){
+           $time += 300;
+            my $count = 0;
+            my $sum = 0;
+            my $squaresum = 0;
+            for (my $i = 0; $i<$tracksize;$i ++){
+               my $elapsed = update($path[$i],$time); 
+               $sum += $elapsed;
+               $squaresum += $elapsed**2;
+               $count++;
+            };
+#            for (my $i = 0; $i<$tracksize;$i ++){
+#             my $fh = new IO::File "$path[$i].rrd","r";
+#             if (defined $fh) {
+#                 $fh->sync;
+#                 $fh->close;
+#                } else {
+#                 warn "failed to sync $path[$i]\n";
+#              }       
+#            }
+            my $ups = $count/$sum;
+            my $sdv = stddev($sum,$squaresum,$count);
+            printf STDERR "%4d %6.0f Up/s (%6.5f sdv)\n",$count,$ups,$sdv;
+        }
+       print STDERR "\n";
+       exit ;
+    }
+}
+
+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/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/libraries/Makefile.am b/program/libraries/Makefile.am
new file mode 100644 (file)
index 0000000..c39d60c
--- /dev/null
@@ -0,0 +1,2 @@
+SUBDIRS=afm
+DIST_SUBDIRS=afm
diff --git a/program/libraries/afm/COPYRIGHT.txt b/program/libraries/afm/COPYRIGHT.txt
new file mode 100644 (file)
index 0000000..1dc7bb4
--- /dev/null
@@ -0,0 +1,17 @@
+For info on copyright, afm format, etc, go to:
+http://partners.adobe.com/asn/developer/technotes/fonts.html
+This page includes the afm file format specification.
+
+The afm files have been fetched from:
+http://partners.adobe.com/asn/developer/technotes/fontinfo/Core14_AFMs.tar
+
+Adobe copyright:
+This file and the 14 PostScript(R) AFM files it accompanies 
+may be used, copied, and distributed for any purpose and 
+without charge, with or without modification, provided that 
+all copyright notices are retained; that the AFM files are not 
+distributed without this file; that all modifications to this 
+file or any of the AFM files are prominently noted in the 
+modified file(s); and that this paragraph is not modified. 
+Adobe Systems has no responsibility or obligation to 
+support the use of the AFM files. 
diff --git a/program/libraries/afm/Courier-Bold.afm b/program/libraries/afm/Courier-Bold.afm
new file mode 100644 (file)
index 0000000..eb80542
--- /dev/null
@@ -0,0 +1,342 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated.  All Rights Reserved.
+Comment Creation Date: Mon Jun 23 16:28:00 1997
+Comment UniqueID 43048
+Comment VMusage 41139 52164
+FontName Courier-Bold
+FullName Courier Bold
+FamilyName Courier
+Weight Bold
+ItalicAngle 0
+IsFixedPitch true
+CharacterSet ExtendedRoman
+FontBBox -113 -250 749 801 
+UnderlinePosition -100
+UnderlineThickness 50
+Version 003.000
+Notice Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated.  All Rights Reserved.
+EncodingScheme AdobeStandardEncoding
+CapHeight 562
+XHeight 439
+Ascender 629
+Descender -157
+StdHW 84
+StdVW 106
+StartCharMetrics 315
+C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 600 ; N exclam ; B 202 -15 398 572 ;
+C 34 ; WX 600 ; N quotedbl ; B 135 277 465 562 ;
+C 35 ; WX 600 ; N numbersign ; B 56 -45 544 651 ;
+C 36 ; WX 600 ; N dollar ; B 82 -126 519 666 ;
+C 37 ; WX 600 ; N percent ; B 5 -15 595 616 ;
+C 38 ; WX 600 ; N ampersand ; B 36 -15 546 543 ;
+C 39 ; WX 600 ; N quoteright ; B 171 277 423 562 ;
+C 40 ; WX 600 ; N parenleft ; B 219 -102 461 616 ;
+C 41 ; WX 600 ; N parenright ; B 139 -102 381 616 ;
+C 42 ; WX 600 ; N asterisk ; B 91 219 509 601 ;
+C 43 ; WX 600 ; N plus ; B 71 39 529 478 ;
+C 44 ; WX 600 ; N comma ; B 123 -111 393 174 ;
+C 45 ; WX 600 ; N hyphen ; B 100 203 500 313 ;
+C 46 ; WX 600 ; N period ; B 192 -15 408 171 ;
+C 47 ; WX 600 ; N slash ; B 98 -77 502 626 ;
+C 48 ; WX 600 ; N zero ; B 87 -15 513 616 ;
+C 49 ; WX 600 ; N one ; B 81 0 539 616 ;
+C 50 ; WX 600 ; N two ; B 61 0 499 616 ;
+C 51 ; WX 600 ; N three ; B 63 -15 501 616 ;
+C 52 ; WX 600 ; N four ; B 53 0 507 616 ;
+C 53 ; WX 600 ; N five ; B 70 -15 521 601 ;
+C 54 ; WX 600 ; N six ; B 90 -15 521 616 ;
+C 55 ; WX 600 ; N seven ; B 55 0 494 601 ;
+C 56 ; WX 600 ; N eight ; B 83 -15 517 616 ;
+C 57 ; WX 600 ; N nine ; B 79 -15 510 616 ;
+C 58 ; WX 600 ; N colon ; B 191 -15 407 425 ;
+C 59 ; WX 600 ; N semicolon ; B 123 -111 408 425 ;
+C 60 ; WX 600 ; N less ; B 66 15 523 501 ;
+C 61 ; WX 600 ; N equal ; B 71 118 529 398 ;
+C 62 ; WX 600 ; N greater ; B 77 15 534 501 ;
+C 63 ; WX 600 ; N question ; B 98 -14 501 580 ;
+C 64 ; WX 600 ; N at ; B 16 -15 584 616 ;
+C 65 ; WX 600 ; N A ; B -9 0 609 562 ;
+C 66 ; WX 600 ; N B ; B 30 0 573 562 ;
+C 67 ; WX 600 ; N C ; B 22 -18 560 580 ;
+C 68 ; WX 600 ; N D ; B 30 0 594 562 ;
+C 69 ; WX 600 ; N E ; B 25 0 560 562 ;
+C 70 ; WX 600 ; N F ; B 39 0 570 562 ;
+C 71 ; WX 600 ; N G ; B 22 -18 594 580 ;
+C 72 ; WX 600 ; N H ; B 20 0 580 562 ;
+C 73 ; WX 600 ; N I ; B 77 0 523 562 ;
+C 74 ; WX 600 ; N J ; B 37 -18 601 562 ;
+C 75 ; WX 600 ; N K ; B 21 0 599 562 ;
+C 76 ; WX 600 ; N L ; B 39 0 578 562 ;
+C 77 ; WX 600 ; N M ; B -2 0 602 562 ;
+C 78 ; WX 600 ; N N ; B 8 -12 610 562 ;
+C 79 ; WX 600 ; N O ; B 22 -18 578 580 ;
+C 80 ; WX 600 ; N P ; B 48 0 559 562 ;
+C 81 ; WX 600 ; N Q ; B 32 -138 578 580 ;
+C 82 ; WX 600 ; N R ; B 24 0 599 562 ;
+C 83 ; WX 600 ; N S ; B 47 -22 553 582 ;
+C 84 ; WX 600 ; N T ; B 21 0 579 562 ;
+C 85 ; WX 600 ; N U ; B 4 -18 596 562 ;
+C 86 ; WX 600 ; N V ; B -13 0 613 562 ;
+C 87 ; WX 600 ; N W ; B -18 0 618 562 ;
+C 88 ; WX 600 ; N X ; B 12 0 588 562 ;
+C 89 ; WX 600 ; N Y ; B 12 0 589 562 ;
+C 90 ; WX 600 ; N Z ; B 62 0 539 562 ;
+C 91 ; WX 600 ; N bracketleft ; B 245 -102 475 616 ;
+C 92 ; WX 600 ; N backslash ; B 99 -77 503 626 ;
+C 93 ; WX 600 ; N bracketright ; B 125 -102 355 616 ;
+C 94 ; WX 600 ; N asciicircum ; B 108 250 492 616 ;
+C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ;
+C 96 ; WX 600 ; N quoteleft ; B 178 277 428 562 ;
+C 97 ; WX 600 ; N a ; B 35 -15 570 454 ;
+C 98 ; WX 600 ; N b ; B 0 -15 584 626 ;
+C 99 ; WX 600 ; N c ; B 40 -15 545 459 ;
+C 100 ; WX 600 ; N d ; B 20 -15 591 626 ;
+C 101 ; WX 600 ; N e ; B 40 -15 563 454 ;
+C 102 ; WX 600 ; N f ; B 83 0 547 626 ; L i fi ; L l fl ;
+C 103 ; WX 600 ; N g ; B 30 -146 580 454 ;
+C 104 ; WX 600 ; N h ; B 5 0 592 626 ;
+C 105 ; WX 600 ; N i ; B 77 0 523 658 ;
+C 106 ; WX 600 ; N j ; B 63 -146 440 658 ;
+C 107 ; WX 600 ; N k ; B 20 0 585 626 ;
+C 108 ; WX 600 ; N l ; B 77 0 523 626 ;
+C 109 ; WX 600 ; N m ; B -22 0 626 454 ;
+C 110 ; WX 600 ; N n ; B 18 0 592 454 ;
+C 111 ; WX 600 ; N o ; B 30 -15 570 454 ;
+C 112 ; WX 600 ; N p ; B -1 -142 570 454 ;
+C 113 ; WX 600 ; N q ; B 20 -142 591 454 ;
+C 114 ; WX 600 ; N r ; B 47 0 580 454 ;
+C 115 ; WX 600 ; N s ; B 68 -17 535 459 ;
+C 116 ; WX 600 ; N t ; B 47 -15 532 562 ;
+C 117 ; WX 600 ; N u ; B -1 -15 569 439 ;
+C 118 ; WX 600 ; N v ; B -1 0 601 439 ;
+C 119 ; WX 600 ; N w ; B -18 0 618 439 ;
+C 120 ; WX 600 ; N x ; B 6 0 594 439 ;
+C 121 ; WX 600 ; N y ; B -4 -142 601 439 ;
+C 122 ; WX 600 ; N z ; B 81 0 520 439 ;
+C 123 ; WX 600 ; N braceleft ; B 160 -102 464 616 ;
+C 124 ; WX 600 ; N bar ; B 255 -250 345 750 ;
+C 125 ; WX 600 ; N braceright ; B 136 -102 440 616 ;
+C 126 ; WX 600 ; N asciitilde ; B 71 153 530 356 ;
+C 161 ; WX 600 ; N exclamdown ; B 202 -146 398 449 ;
+C 162 ; WX 600 ; N cent ; B 66 -49 518 614 ;
+C 163 ; WX 600 ; N sterling ; B 72 -28 558 611 ;
+C 164 ; WX 600 ; N fraction ; B 25 -60 576 661 ;
+C 165 ; WX 600 ; N yen ; B 10 0 590 562 ;
+C 166 ; WX 600 ; N florin ; B -30 -131 572 616 ;
+C 167 ; WX 600 ; N section ; B 83 -70 517 580 ;
+C 168 ; WX 600 ; N currency ; B 54 49 546 517 ;
+C 169 ; WX 600 ; N quotesingle ; B 227 277 373 562 ;
+C 170 ; WX 600 ; N quotedblleft ; B 71 277 535 562 ;
+C 171 ; WX 600 ; N guillemotleft ; B 8 70 553 446 ;
+C 172 ; WX 600 ; N guilsinglleft ; B 141 70 459 446 ;
+C 173 ; WX 600 ; N guilsinglright ; B 141 70 459 446 ;
+C 174 ; WX 600 ; N fi ; B 12 0 593 626 ;
+C 175 ; WX 600 ; N fl ; B 12 0 593 626 ;
+C 177 ; WX 600 ; N endash ; B 65 203 535 313 ;
+C 178 ; WX 600 ; N dagger ; B 106 -70 494 580 ;
+C 179 ; WX 600 ; N daggerdbl ; B 106 -70 494 580 ;
+C 180 ; WX 600 ; N periodcentered ; B 196 165 404 351 ;
+C 182 ; WX 600 ; N paragraph ; B 6 -70 576 580 ;
+C 183 ; WX 600 ; N bullet ; B 140 132 460 430 ;
+C 184 ; WX 600 ; N quotesinglbase ; B 175 -142 427 143 ;
+C 185 ; WX 600 ; N quotedblbase ; B 65 -142 529 143 ;
+C 186 ; WX 600 ; N quotedblright ; B 61 277 525 562 ;
+C 187 ; WX 600 ; N guillemotright ; B 47 70 592 446 ;
+C 188 ; WX 600 ; N ellipsis ; B 26 -15 574 116 ;
+C 189 ; WX 600 ; N perthousand ; B -113 -15 713 616 ;
+C 191 ; WX 600 ; N questiondown ; B 99 -146 502 449 ;
+C 193 ; WX 600 ; N grave ; B 132 508 395 661 ;
+C 194 ; WX 600 ; N acute ; B 205 508 468 661 ;
+C 195 ; WX 600 ; N circumflex ; B 103 483 497 657 ;
+C 196 ; WX 600 ; N tilde ; B 89 493 512 636 ;
+C 197 ; WX 600 ; N macron ; B 88 505 512 585 ;
+C 198 ; WX 600 ; N breve ; B 83 468 517 631 ;
+C 199 ; WX 600 ; N dotaccent ; B 230 498 370 638 ;
+C 200 ; WX 600 ; N dieresis ; B 128 498 472 638 ;
+C 202 ; WX 600 ; N ring ; B 198 481 402 678 ;
+C 203 ; WX 600 ; N cedilla ; B 205 -206 387 0 ;
+C 205 ; WX 600 ; N hungarumlaut ; B 68 488 588 661 ;
+C 206 ; WX 600 ; N ogonek ; B 169 -199 400 0 ;
+C 207 ; WX 600 ; N caron ; B 103 493 497 667 ;
+C 208 ; WX 600 ; N emdash ; B -10 203 610 313 ;
+C 225 ; WX 600 ; N AE ; B -29 0 602 562 ;
+C 227 ; WX 600 ; N ordfeminine ; B 147 196 453 580 ;
+C 232 ; WX 600 ; N Lslash ; B 39 0 578 562 ;
+C 233 ; WX 600 ; N Oslash ; B 22 -22 578 584 ;
+C 234 ; WX 600 ; N OE ; B -25 0 595 562 ;
+C 235 ; WX 600 ; N ordmasculine ; B 147 196 453 580 ;
+C 241 ; WX 600 ; N ae ; B -4 -15 601 454 ;
+C 245 ; WX 600 ; N dotlessi ; B 77 0 523 439 ;
+C 248 ; WX 600 ; N lslash ; B 77 0 523 626 ;
+C 249 ; WX 600 ; N oslash ; B 30 -24 570 463 ;
+C 250 ; WX 600 ; N oe ; B -18 -15 611 454 ;
+C 251 ; WX 600 ; N germandbls ; B 22 -15 596 626 ;
+C -1 ; WX 600 ; N Idieresis ; B 77 0 523 761 ;
+C -1 ; WX 600 ; N eacute ; B 40 -15 563 661 ;
+C -1 ; WX 600 ; N abreve ; B 35 -15 570 661 ;
+C -1 ; WX 600 ; N uhungarumlaut ; B -1 -15 628 661 ;
+C -1 ; WX 600 ; N ecaron ; B 40 -15 563 667 ;
+C -1 ; WX 600 ; N Ydieresis ; B 12 0 589 761 ;
+C -1 ; WX 600 ; N divide ; B 71 16 529 500 ;
+C -1 ; WX 600 ; N Yacute ; B 12 0 589 784 ;
+C -1 ; WX 600 ; N Acircumflex ; B -9 0 609 780 ;
+C -1 ; WX 600 ; N aacute ; B 35 -15 570 661 ;
+C -1 ; WX 600 ; N Ucircumflex ; B 4 -18 596 780 ;
+C -1 ; WX 600 ; N yacute ; B -4 -142 601 661 ;
+C -1 ; WX 600 ; N scommaaccent ; B 68 -250 535 459 ;
+C -1 ; WX 600 ; N ecircumflex ; B 40 -15 563 657 ;
+C -1 ; WX 600 ; N Uring ; B 4 -18 596 801 ;
+C -1 ; WX 600 ; N Udieresis ; B 4 -18 596 761 ;
+C -1 ; WX 600 ; N aogonek ; B 35 -199 586 454 ;
+C -1 ; WX 600 ; N Uacute ; B 4 -18 596 784 ;
+C -1 ; WX 600 ; N uogonek ; B -1 -199 585 439 ;
+C -1 ; WX 600 ; N Edieresis ; B 25 0 560 761 ;
+C -1 ; WX 600 ; N Dcroat ; B 30 0 594 562 ;
+C -1 ; WX 600 ; N commaaccent ; B 205 -250 397 -57 ;
+C -1 ; WX 600 ; N copyright ; B 0 -18 600 580 ;
+C -1 ; WX 600 ; N Emacron ; B 25 0 560 708 ;
+C -1 ; WX 600 ; N ccaron ; B 40 -15 545 667 ;
+C -1 ; WX 600 ; N aring ; B 35 -15 570 678 ;
+C -1 ; WX 600 ; N Ncommaaccent ; B 8 -250 610 562 ;
+C -1 ; WX 600 ; N lacute ; B 77 0 523 801 ;
+C -1 ; WX 600 ; N agrave ; B 35 -15 570 661 ;
+C -1 ; WX 600 ; N Tcommaaccent ; B 21 -250 579 562 ;
+C -1 ; WX 600 ; N Cacute ; B 22 -18 560 784 ;
+C -1 ; WX 600 ; N atilde ; B 35 -15 570 636 ;
+C -1 ; WX 600 ; N Edotaccent ; B 25 0 560 761 ;
+C -1 ; WX 600 ; N scaron ; B 68 -17 535 667 ;
+C -1 ; WX 600 ; N scedilla ; B 68 -206 535 459 ;
+C -1 ; WX 600 ; N iacute ; B 77 0 523 661 ;
+C -1 ; WX 600 ; N lozenge ; B 66 0 534 740 ;
+C -1 ; WX 600 ; N Rcaron ; B 24 0 599 790 ;
+C -1 ; WX 600 ; N Gcommaaccent ; B 22 -250 594 580 ;
+C -1 ; WX 600 ; N ucircumflex ; B -1 -15 569 657 ;
+C -1 ; WX 600 ; N acircumflex ; B 35 -15 570 657 ;
+C -1 ; WX 600 ; N Amacron ; B -9 0 609 708 ;
+C -1 ; WX 600 ; N rcaron ; B 47 0 580 667 ;
+C -1 ; WX 600 ; N ccedilla ; B 40 -206 545 459 ;
+C -1 ; WX 600 ; N Zdotaccent ; B 62 0 539 761 ;
+C -1 ; WX 600 ; N Thorn ; B 48 0 557 562 ;
+C -1 ; WX 600 ; N Omacron ; B 22 -18 578 708 ;
+C -1 ; WX 600 ; N Racute ; B 24 0 599 784 ;
+C -1 ; WX 600 ; N Sacute ; B 47 -22 553 784 ;
+C -1 ; WX 600 ; N dcaron ; B 20 -15 727 626 ;
+C -1 ; WX 600 ; N Umacron ; B 4 -18 596 708 ;
+C -1 ; WX 600 ; N uring ; B -1 -15 569 678 ;
+C -1 ; WX 600 ; N threesuperior ; B 138 222 433 616 ;
+C -1 ; WX 600 ; N Ograve ; B 22 -18 578 784 ;
+C -1 ; WX 600 ; N Agrave ; B -9 0 609 784 ;
+C -1 ; WX 600 ; N Abreve ; B -9 0 609 784 ;
+C -1 ; WX 600 ; N multiply ; B 81 39 520 478 ;
+C -1 ; WX 600 ; N uacute ; B -1 -15 569 661 ;
+C -1 ; WX 600 ; N Tcaron ; B 21 0 579 790 ;
+C -1 ; WX 600 ; N partialdiff ; B 63 -38 537 728 ;
+C -1 ; WX 600 ; N ydieresis ; B -4 -142 601 638 ;
+C -1 ; WX 600 ; N Nacute ; B 8 -12 610 784 ;
+C -1 ; WX 600 ; N icircumflex ; B 73 0 523 657 ;
+C -1 ; WX 600 ; N Ecircumflex ; B 25 0 560 780 ;
+C -1 ; WX 600 ; N adieresis ; B 35 -15 570 638 ;
+C -1 ; WX 600 ; N edieresis ; B 40 -15 563 638 ;
+C -1 ; WX 600 ; N cacute ; B 40 -15 545 661 ;
+C -1 ; WX 600 ; N nacute ; B 18 0 592 661 ;
+C -1 ; WX 600 ; N umacron ; B -1 -15 569 585 ;
+C -1 ; WX 600 ; N Ncaron ; B 8 -12 610 790 ;
+C -1 ; WX 600 ; N Iacute ; B 77 0 523 784 ;
+C -1 ; WX 600 ; N plusminus ; B 71 24 529 515 ;
+C -1 ; WX 600 ; N brokenbar ; B 255 -175 345 675 ;
+C -1 ; WX 600 ; N registered ; B 0 -18 600 580 ;
+C -1 ; WX 600 ; N Gbreve ; B 22 -18 594 784 ;
+C -1 ; WX 600 ; N Idotaccent ; B 77 0 523 761 ;
+C -1 ; WX 600 ; N summation ; B 15 -10 586 706 ;
+C -1 ; WX 600 ; N Egrave ; B 25 0 560 784 ;
+C -1 ; WX 600 ; N racute ; B 47 0 580 661 ;
+C -1 ; WX 600 ; N omacron ; B 30 -15 570 585 ;
+C -1 ; WX 600 ; N Zacute ; B 62 0 539 784 ;
+C -1 ; WX 600 ; N Zcaron ; B 62 0 539 790 ;
+C -1 ; WX 600 ; N greaterequal ; B 26 0 523 696 ;
+C -1 ; WX 600 ; N Eth ; B 30 0 594 562 ;
+C -1 ; WX 600 ; N Ccedilla ; B 22 -206 560 580 ;
+C -1 ; WX 600 ; N lcommaaccent ; B 77 -250 523 626 ;
+C -1 ; WX 600 ; N tcaron ; B 47 -15 532 703 ;
+C -1 ; WX 600 ; N eogonek ; B 40 -199 563 454 ;
+C -1 ; WX 600 ; N Uogonek ; B 4 -199 596 562 ;
+C -1 ; WX 600 ; N Aacute ; B -9 0 609 784 ;
+C -1 ; WX 600 ; N Adieresis ; B -9 0 609 761 ;
+C -1 ; WX 600 ; N egrave ; B 40 -15 563 661 ;
+C -1 ; WX 600 ; N zacute ; B 81 0 520 661 ;
+C -1 ; WX 600 ; N iogonek ; B 77 -199 523 658 ;
+C -1 ; WX 600 ; N Oacute ; B 22 -18 578 784 ;
+C -1 ; WX 600 ; N oacute ; B 30 -15 570 661 ;
+C -1 ; WX 600 ; N amacron ; B 35 -15 570 585 ;
+C -1 ; WX 600 ; N sacute ; B 68 -17 535 661 ;
+C -1 ; WX 600 ; N idieresis ; B 77 0 523 618 ;
+C -1 ; WX 600 ; N Ocircumflex ; B 22 -18 578 780 ;
+C -1 ; WX 600 ; N Ugrave ; B 4 -18 596 784 ;
+C -1 ; WX 600 ; N Delta ; B 6 0 594 688 ;
+C -1 ; WX 600 ; N thorn ; B -14 -142 570 626 ;
+C -1 ; WX 600 ; N twosuperior ; B 143 230 436 616 ;
+C -1 ; WX 600 ; N Odieresis ; B 22 -18 578 761 ;
+C -1 ; WX 600 ; N mu ; B -1 -142 569 439 ;
+C -1 ; WX 600 ; N igrave ; B 77 0 523 661 ;
+C -1 ; WX 600 ; N ohungarumlaut ; B 30 -15 668 661 ;
+C -1 ; WX 600 ; N Eogonek ; B 25 -199 576 562 ;
+C -1 ; WX 600 ; N dcroat ; B 20 -15 591 626 ;
+C -1 ; WX 600 ; N threequarters ; B -47 -60 648 661 ;
+C -1 ; WX 600 ; N Scedilla ; B 47 -206 553 582 ;
+C -1 ; WX 600 ; N lcaron ; B 77 0 597 626 ;
+C -1 ; WX 600 ; N Kcommaaccent ; B 21 -250 599 562 ;
+C -1 ; WX 600 ; N Lacute ; B 39 0 578 784 ;
+C -1 ; WX 600 ; N trademark ; B -9 230 749 562 ;
+C -1 ; WX 600 ; N edotaccent ; B 40 -15 563 638 ;
+C -1 ; WX 600 ; N Igrave ; B 77 0 523 784 ;
+C -1 ; WX 600 ; N Imacron ; B 77 0 523 708 ;
+C -1 ; WX 600 ; N Lcaron ; B 39 0 637 562 ;
+C -1 ; WX 600 ; N onehalf ; B -47 -60 648 661 ;
+C -1 ; WX 600 ; N lessequal ; B 26 0 523 696 ;
+C -1 ; WX 600 ; N ocircumflex ; B 30 -15 570 657 ;
+C -1 ; WX 600 ; N ntilde ; B 18 0 592 636 ;
+C -1 ; WX 600 ; N Uhungarumlaut ; B 4 -18 638 784 ;
+C -1 ; WX 600 ; N Eacute ; B 25 0 560 784 ;
+C -1 ; WX 600 ; N emacron ; B 40 -15 563 585 ;
+C -1 ; WX 600 ; N gbreve ; B 30 -146 580 661 ;
+C -1 ; WX 600 ; N onequarter ; B -56 -60 656 661 ;
+C -1 ; WX 600 ; N Scaron ; B 47 -22 553 790 ;
+C -1 ; WX 600 ; N Scommaaccent ; B 47 -250 553 582 ;
+C -1 ; WX 600 ; N Ohungarumlaut ; B 22 -18 628 784 ;
+C -1 ; WX 600 ; N degree ; B 86 243 474 616 ;
+C -1 ; WX 600 ; N ograve ; B 30 -15 570 661 ;
+C -1 ; WX 600 ; N Ccaron ; B 22 -18 560 790 ;
+C -1 ; WX 600 ; N ugrave ; B -1 -15 569 661 ;
+C -1 ; WX 600 ; N radical ; B -19 -104 473 778 ;
+C -1 ; WX 600 ; N Dcaron ; B 30 0 594 790 ;
+C -1 ; WX 600 ; N rcommaaccent ; B 47 -250 580 454 ;
+C -1 ; WX 600 ; N Ntilde ; B 8 -12 610 759 ;
+C -1 ; WX 600 ; N otilde ; B 30 -15 570 636 ;
+C -1 ; WX 600 ; N Rcommaaccent ; B 24 -250 599 562 ;
+C -1 ; WX 600 ; N Lcommaaccent ; B 39 -250 578 562 ;
+C -1 ; WX 600 ; N Atilde ; B -9 0 609 759 ;
+C -1 ; WX 600 ; N Aogonek ; B -9 -199 625 562 ;
+C -1 ; WX 600 ; N Aring ; B -9 0 609 801 ;
+C -1 ; WX 600 ; N Otilde ; B 22 -18 578 759 ;
+C -1 ; WX 600 ; N zdotaccent ; B 81 0 520 638 ;
+C -1 ; WX 600 ; N Ecaron ; B 25 0 560 790 ;
+C -1 ; WX 600 ; N Iogonek ; B 77 -199 523 562 ;
+C -1 ; WX 600 ; N kcommaaccent ; B 20 -250 585 626 ;
+C -1 ; WX 600 ; N minus ; B 71 203 529 313 ;
+C -1 ; WX 600 ; N Icircumflex ; B 77 0 523 780 ;
+C -1 ; WX 600 ; N ncaron ; B 18 0 592 667 ;
+C -1 ; WX 600 ; N tcommaaccent ; B 47 -250 532 562 ;
+C -1 ; WX 600 ; N logicalnot ; B 71 103 529 413 ;
+C -1 ; WX 600 ; N odieresis ; B 30 -15 570 638 ;
+C -1 ; WX 600 ; N udieresis ; B -1 -15 569 638 ;
+C -1 ; WX 600 ; N notequal ; B 12 -47 537 563 ;
+C -1 ; WX 600 ; N gcommaaccent ; B 30 -146 580 714 ;
+C -1 ; WX 600 ; N eth ; B 58 -27 543 626 ;
+C -1 ; WX 600 ; N zcaron ; B 81 0 520 667 ;
+C -1 ; WX 600 ; N ncommaaccent ; B 18 -250 592 454 ;
+C -1 ; WX 600 ; N onesuperior ; B 153 230 447 616 ;
+C -1 ; WX 600 ; N imacron ; B 77 0 523 585 ;
+C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+EndFontMetrics
diff --git a/program/libraries/afm/Courier-BoldOblique.afm b/program/libraries/afm/Courier-BoldOblique.afm
new file mode 100644 (file)
index 0000000..29d3b8b
--- /dev/null
@@ -0,0 +1,342 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated.  All Rights Reserved.
+Comment Creation Date: Mon Jun 23 16:28:46 1997
+Comment UniqueID 43049
+Comment VMusage 17529 79244
+FontName Courier-BoldOblique
+FullName Courier Bold Oblique
+FamilyName Courier
+Weight Bold
+ItalicAngle -12
+IsFixedPitch true
+CharacterSet ExtendedRoman
+FontBBox -57 -250 869 801 
+UnderlinePosition -100
+UnderlineThickness 50
+Version 003.000
+Notice Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated.  All Rights Reserved.
+EncodingScheme AdobeStandardEncoding
+CapHeight 562
+XHeight 439
+Ascender 629
+Descender -157
+StdHW 84
+StdVW 106
+StartCharMetrics 315
+C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 600 ; N exclam ; B 215 -15 495 572 ;
+C 34 ; WX 600 ; N quotedbl ; B 211 277 585 562 ;
+C 35 ; WX 600 ; N numbersign ; B 88 -45 641 651 ;
+C 36 ; WX 600 ; N dollar ; B 87 -126 630 666 ;
+C 37 ; WX 600 ; N percent ; B 101 -15 625 616 ;
+C 38 ; WX 600 ; N ampersand ; B 61 -15 595 543 ;
+C 39 ; WX 600 ; N quoteright ; B 229 277 543 562 ;
+C 40 ; WX 600 ; N parenleft ; B 265 -102 592 616 ;
+C 41 ; WX 600 ; N parenright ; B 117 -102 444 616 ;
+C 42 ; WX 600 ; N asterisk ; B 179 219 598 601 ;
+C 43 ; WX 600 ; N plus ; B 114 39 596 478 ;
+C 44 ; WX 600 ; N comma ; B 99 -111 430 174 ;
+C 45 ; WX 600 ; N hyphen ; B 143 203 567 313 ;
+C 46 ; WX 600 ; N period ; B 206 -15 427 171 ;
+C 47 ; WX 600 ; N slash ; B 90 -77 626 626 ;
+C 48 ; WX 600 ; N zero ; B 135 -15 593 616 ;
+C 49 ; WX 600 ; N one ; B 93 0 562 616 ;
+C 50 ; WX 600 ; N two ; B 61 0 594 616 ;
+C 51 ; WX 600 ; N three ; B 71 -15 571 616 ;
+C 52 ; WX 600 ; N four ; B 81 0 559 616 ;
+C 53 ; WX 600 ; N five ; B 77 -15 621 601 ;
+C 54 ; WX 600 ; N six ; B 135 -15 652 616 ;
+C 55 ; WX 600 ; N seven ; B 147 0 622 601 ;
+C 56 ; WX 600 ; N eight ; B 115 -15 604 616 ;
+C 57 ; WX 600 ; N nine ; B 75 -15 592 616 ;
+C 58 ; WX 600 ; N colon ; B 205 -15 480 425 ;
+C 59 ; WX 600 ; N semicolon ; B 99 -111 481 425 ;
+C 60 ; WX 600 ; N less ; B 120 15 613 501 ;
+C 61 ; WX 600 ; N equal ; B 96 118 614 398 ;
+C 62 ; WX 600 ; N greater ; B 97 15 589 501 ;
+C 63 ; WX 600 ; N question ; B 183 -14 592 580 ;
+C 64 ; WX 600 ; N at ; B 65 -15 642 616 ;
+C 65 ; WX 600 ; N A ; B -9 0 632 562 ;
+C 66 ; WX 600 ; N B ; B 30 0 630 562 ;
+C 67 ; WX 600 ; N C ; B 74 -18 675 580 ;
+C 68 ; WX 600 ; N D ; B 30 0 664 562 ;
+C 69 ; WX 600 ; N E ; B 25 0 670 562 ;
+C 70 ; WX 600 ; N F ; B 39 0 684 562 ;
+C 71 ; WX 600 ; N G ; B 74 -18 675 580 ;
+C 72 ; WX 600 ; N H ; B 20 0 700 562 ;
+C 73 ; WX 600 ; N I ; B 77 0 643 562 ;
+C 74 ; WX 600 ; N J ; B 58 -18 721 562 ;
+C 75 ; WX 600 ; N K ; B 21 0 692 562 ;
+C 76 ; WX 600 ; N L ; B 39 0 636 562 ;
+C 77 ; WX 600 ; N M ; B -2 0 722 562 ;
+C 78 ; WX 600 ; N N ; B 8 -12 730 562 ;
+C 79 ; WX 600 ; N O ; B 74 -18 645 580 ;
+C 80 ; WX 600 ; N P ; B 48 0 643 562 ;
+C 81 ; WX 600 ; N Q ; B 83 -138 636 580 ;
+C 82 ; WX 600 ; N R ; B 24 0 617 562 ;
+C 83 ; WX 600 ; N S ; B 54 -22 673 582 ;
+C 84 ; WX 600 ; N T ; B 86 0 679 562 ;
+C 85 ; WX 600 ; N U ; B 101 -18 716 562 ;
+C 86 ; WX 600 ; N V ; B 84 0 733 562 ;
+C 87 ; WX 600 ; N W ; B 79 0 738 562 ;
+C 88 ; WX 600 ; N X ; B 12 0 690 562 ;
+C 89 ; WX 600 ; N Y ; B 109 0 709 562 ;
+C 90 ; WX 600 ; N Z ; B 62 0 637 562 ;
+C 91 ; WX 600 ; N bracketleft ; B 223 -102 606 616 ;
+C 92 ; WX 600 ; N backslash ; B 222 -77 496 626 ;
+C 93 ; WX 600 ; N bracketright ; B 103 -102 486 616 ;
+C 94 ; WX 600 ; N asciicircum ; B 171 250 556 616 ;
+C 95 ; WX 600 ; N underscore ; B -27 -125 585 -75 ;
+C 96 ; WX 600 ; N quoteleft ; B 297 277 487 562 ;
+C 97 ; WX 600 ; N a ; B 61 -15 593 454 ;
+C 98 ; WX 600 ; N b ; B 13 -15 636 626 ;
+C 99 ; WX 600 ; N c ; B 81 -15 631 459 ;
+C 100 ; WX 600 ; N d ; B 60 -15 645 626 ;
+C 101 ; WX 600 ; N e ; B 81 -15 605 454 ;
+C 102 ; WX 600 ; N f ; B 83 0 677 626 ; L i fi ; L l fl ;
+C 103 ; WX 600 ; N g ; B 40 -146 674 454 ;
+C 104 ; WX 600 ; N h ; B 18 0 615 626 ;
+C 105 ; WX 600 ; N i ; B 77 0 546 658 ;
+C 106 ; WX 600 ; N j ; B 36 -146 580 658 ;
+C 107 ; WX 600 ; N k ; B 33 0 643 626 ;
+C 108 ; WX 600 ; N l ; B 77 0 546 626 ;
+C 109 ; WX 600 ; N m ; B -22 0 649 454 ;
+C 110 ; WX 600 ; N n ; B 18 0 615 454 ;
+C 111 ; WX 600 ; N o ; B 71 -15 622 454 ;
+C 112 ; WX 600 ; N p ; B -32 -142 622 454 ;
+C 113 ; WX 600 ; N q ; B 60 -142 685 454 ;
+C 114 ; WX 600 ; N r ; B 47 0 655 454 ;
+C 115 ; WX 600 ; N s ; B 66 -17 608 459 ;
+C 116 ; WX 600 ; N t ; B 118 -15 567 562 ;
+C 117 ; WX 600 ; N u ; B 70 -15 592 439 ;
+C 118 ; WX 600 ; N v ; B 70 0 695 439 ;
+C 119 ; WX 600 ; N w ; B 53 0 712 439 ;
+C 120 ; WX 600 ; N x ; B 6 0 671 439 ;
+C 121 ; WX 600 ; N y ; B -21 -142 695 439 ;
+C 122 ; WX 600 ; N z ; B 81 0 614 439 ;
+C 123 ; WX 600 ; N braceleft ; B 203 -102 595 616 ;
+C 124 ; WX 600 ; N bar ; B 201 -250 505 750 ;
+C 125 ; WX 600 ; N braceright ; B 114 -102 506 616 ;
+C 126 ; WX 600 ; N asciitilde ; B 120 153 590 356 ;
+C 161 ; WX 600 ; N exclamdown ; B 196 -146 477 449 ;
+C 162 ; WX 600 ; N cent ; B 121 -49 605 614 ;
+C 163 ; WX 600 ; N sterling ; B 106 -28 650 611 ;
+C 164 ; WX 600 ; N fraction ; B 22 -60 708 661 ;
+C 165 ; WX 600 ; N yen ; B 98 0 710 562 ;
+C 166 ; WX 600 ; N florin ; B -57 -131 702 616 ;
+C 167 ; WX 600 ; N section ; B 74 -70 620 580 ;
+C 168 ; WX 600 ; N currency ; B 77 49 644 517 ;
+C 169 ; WX 600 ; N quotesingle ; B 303 277 493 562 ;
+C 170 ; WX 600 ; N quotedblleft ; B 190 277 594 562 ;
+C 171 ; WX 600 ; N guillemotleft ; B 62 70 639 446 ;
+C 172 ; WX 600 ; N guilsinglleft ; B 195 70 545 446 ;
+C 173 ; WX 600 ; N guilsinglright ; B 165 70 514 446 ;
+C 174 ; WX 600 ; N fi ; B 12 0 644 626 ;
+C 175 ; WX 600 ; N fl ; B 12 0 644 626 ;
+C 177 ; WX 600 ; N endash ; B 108 203 602 313 ;
+C 178 ; WX 600 ; N dagger ; B 175 -70 586 580 ;
+C 179 ; WX 600 ; N daggerdbl ; B 121 -70 587 580 ;
+C 180 ; WX 600 ; N periodcentered ; B 248 165 461 351 ;
+C 182 ; WX 600 ; N paragraph ; B 61 -70 700 580 ;
+C 183 ; WX 600 ; N bullet ; B 196 132 523 430 ;
+C 184 ; WX 600 ; N quotesinglbase ; B 144 -142 458 143 ;
+C 185 ; WX 600 ; N quotedblbase ; B 34 -142 560 143 ;
+C 186 ; WX 600 ; N quotedblright ; B 119 277 645 562 ;
+C 187 ; WX 600 ; N guillemotright ; B 71 70 647 446 ;
+C 188 ; WX 600 ; N ellipsis ; B 35 -15 587 116 ;
+C 189 ; WX 600 ; N perthousand ; B -45 -15 743 616 ;
+C 191 ; WX 600 ; N questiondown ; B 100 -146 509 449 ;
+C 193 ; WX 600 ; N grave ; B 272 508 503 661 ;
+C 194 ; WX 600 ; N acute ; B 312 508 609 661 ;
+C 195 ; WX 600 ; N circumflex ; B 212 483 607 657 ;
+C 196 ; WX 600 ; N tilde ; B 199 493 643 636 ;
+C 197 ; WX 600 ; N macron ; B 195 505 637 585 ;
+C 198 ; WX 600 ; N breve ; B 217 468 652 631 ;
+C 199 ; WX 600 ; N dotaccent ; B 348 498 493 638 ;
+C 200 ; WX 600 ; N dieresis ; B 246 498 595 638 ;
+C 202 ; WX 600 ; N ring ; B 319 481 528 678 ;
+C 203 ; WX 600 ; N cedilla ; B 168 -206 368 0 ;
+C 205 ; WX 600 ; N hungarumlaut ; B 171 488 729 661 ;
+C 206 ; WX 600 ; N ogonek ; B 143 -199 367 0 ;
+C 207 ; WX 600 ; N caron ; B 238 493 633 667 ;
+C 208 ; WX 600 ; N emdash ; B 33 203 677 313 ;
+C 225 ; WX 600 ; N AE ; B -29 0 708 562 ;
+C 227 ; WX 600 ; N ordfeminine ; B 188 196 526 580 ;
+C 232 ; WX 600 ; N Lslash ; B 39 0 636 562 ;
+C 233 ; WX 600 ; N Oslash ; B 48 -22 673 584 ;
+C 234 ; WX 600 ; N OE ; B 26 0 701 562 ;
+C 235 ; WX 600 ; N ordmasculine ; B 188 196 543 580 ;
+C 241 ; WX 600 ; N ae ; B 21 -15 652 454 ;
+C 245 ; WX 600 ; N dotlessi ; B 77 0 546 439 ;
+C 248 ; WX 600 ; N lslash ; B 77 0 587 626 ;
+C 249 ; WX 600 ; N oslash ; B 54 -24 638 463 ;
+C 250 ; WX 600 ; N oe ; B 18 -15 662 454 ;
+C 251 ; WX 600 ; N germandbls ; B 22 -15 629 626 ;
+C -1 ; WX 600 ; N Idieresis ; B 77 0 643 761 ;
+C -1 ; WX 600 ; N eacute ; B 81 -15 609 661 ;
+C -1 ; WX 600 ; N abreve ; B 61 -15 658 661 ;
+C -1 ; WX 600 ; N uhungarumlaut ; B 70 -15 769 661 ;
+C -1 ; WX 600 ; N ecaron ; B 81 -15 633 667 ;
+C -1 ; WX 600 ; N Ydieresis ; B 109 0 709 761 ;
+C -1 ; WX 600 ; N divide ; B 114 16 596 500 ;
+C -1 ; WX 600 ; N Yacute ; B 109 0 709 784 ;
+C -1 ; WX 600 ; N Acircumflex ; B -9 0 632 780 ;
+C -1 ; WX 600 ; N aacute ; B 61 -15 609 661 ;
+C -1 ; WX 600 ; N Ucircumflex ; B 101 -18 716 780 ;
+C -1 ; WX 600 ; N yacute ; B -21 -142 695 661 ;
+C -1 ; WX 600 ; N scommaaccent ; B 66 -250 608 459 ;
+C -1 ; WX 600 ; N ecircumflex ; B 81 -15 607 657 ;
+C -1 ; WX 600 ; N Uring ; B 101 -18 716 801 ;
+C -1 ; WX 600 ; N Udieresis ; B 101 -18 716 761 ;
+C -1 ; WX 600 ; N aogonek ; B 61 -199 593 454 ;
+C -1 ; WX 600 ; N Uacute ; B 101 -18 716 784 ;
+C -1 ; WX 600 ; N uogonek ; B 70 -199 592 439 ;
+C -1 ; WX 600 ; N Edieresis ; B 25 0 670 761 ;
+C -1 ; WX 600 ; N Dcroat ; B 30 0 664 562 ;
+C -1 ; WX 600 ; N commaaccent ; B 151 -250 385 -57 ;
+C -1 ; WX 600 ; N copyright ; B 53 -18 667 580 ;
+C -1 ; WX 600 ; N Emacron ; B 25 0 670 708 ;
+C -1 ; WX 600 ; N ccaron ; B 81 -15 633 667 ;
+C -1 ; WX 600 ; N aring ; B 61 -15 593 678 ;
+C -1 ; WX 600 ; N Ncommaaccent ; B 8 -250 730 562 ;
+C -1 ; WX 600 ; N lacute ; B 77 0 639 801 ;
+C -1 ; WX 600 ; N agrave ; B 61 -15 593 661 ;
+C -1 ; WX 600 ; N Tcommaaccent ; B 86 -250 679 562 ;
+C -1 ; WX 600 ; N Cacute ; B 74 -18 675 784 ;
+C -1 ; WX 600 ; N atilde ; B 61 -15 643 636 ;
+C -1 ; WX 600 ; N Edotaccent ; B 25 0 670 761 ;
+C -1 ; WX 600 ; N scaron ; B 66 -17 633 667 ;
+C -1 ; WX 600 ; N scedilla ; B 66 -206 608 459 ;
+C -1 ; WX 600 ; N iacute ; B 77 0 609 661 ;
+C -1 ; WX 600 ; N lozenge ; B 145 0 614 740 ;
+C -1 ; WX 600 ; N Rcaron ; B 24 0 659 790 ;
+C -1 ; WX 600 ; N Gcommaaccent ; B 74 -250 675 580 ;
+C -1 ; WX 600 ; N ucircumflex ; B 70 -15 597 657 ;
+C -1 ; WX 600 ; N acircumflex ; B 61 -15 607 657 ;
+C -1 ; WX 600 ; N Amacron ; B -9 0 633 708 ;
+C -1 ; WX 600 ; N rcaron ; B 47 0 655 667 ;
+C -1 ; WX 600 ; N ccedilla ; B 81 -206 631 459 ;
+C -1 ; WX 600 ; N Zdotaccent ; B 62 0 637 761 ;
+C -1 ; WX 600 ; N Thorn ; B 48 0 620 562 ;
+C -1 ; WX 600 ; N Omacron ; B 74 -18 663 708 ;
+C -1 ; WX 600 ; N Racute ; B 24 0 665 784 ;
+C -1 ; WX 600 ; N Sacute ; B 54 -22 673 784 ;
+C -1 ; WX 600 ; N dcaron ; B 60 -15 861 626 ;
+C -1 ; WX 600 ; N Umacron ; B 101 -18 716 708 ;
+C -1 ; WX 600 ; N uring ; B 70 -15 592 678 ;
+C -1 ; WX 600 ; N threesuperior ; B 193 222 526 616 ;
+C -1 ; WX 600 ; N Ograve ; B 74 -18 645 784 ;
+C -1 ; WX 600 ; N Agrave ; B -9 0 632 784 ;
+C -1 ; WX 600 ; N Abreve ; B -9 0 684 784 ;
+C -1 ; WX 600 ; N multiply ; B 104 39 606 478 ;
+C -1 ; WX 600 ; N uacute ; B 70 -15 599 661 ;
+C -1 ; WX 600 ; N Tcaron ; B 86 0 679 790 ;
+C -1 ; WX 600 ; N partialdiff ; B 91 -38 627 728 ;
+C -1 ; WX 600 ; N ydieresis ; B -21 -142 695 638 ;
+C -1 ; WX 600 ; N Nacute ; B 8 -12 730 784 ;
+C -1 ; WX 600 ; N icircumflex ; B 77 0 577 657 ;
+C -1 ; WX 600 ; N Ecircumflex ; B 25 0 670 780 ;
+C -1 ; WX 600 ; N adieresis ; B 61 -15 595 638 ;
+C -1 ; WX 600 ; N edieresis ; B 81 -15 605 638 ;
+C -1 ; WX 600 ; N cacute ; B 81 -15 649 661 ;
+C -1 ; WX 600 ; N nacute ; B 18 0 639 661 ;
+C -1 ; WX 600 ; N umacron ; B 70 -15 637 585 ;
+C -1 ; WX 600 ; N Ncaron ; B 8 -12 730 790 ;
+C -1 ; WX 600 ; N Iacute ; B 77 0 643 784 ;
+C -1 ; WX 600 ; N plusminus ; B 76 24 614 515 ;
+C -1 ; WX 600 ; N brokenbar ; B 217 -175 489 675 ;
+C -1 ; WX 600 ; N registered ; B 53 -18 667 580 ;
+C -1 ; WX 600 ; N Gbreve ; B 74 -18 684 784 ;
+C -1 ; WX 600 ; N Idotaccent ; B 77 0 643 761 ;
+C -1 ; WX 600 ; N summation ; B 15 -10 672 706 ;
+C -1 ; WX 600 ; N Egrave ; B 25 0 670 784 ;
+C -1 ; WX 600 ; N racute ; B 47 0 655 661 ;
+C -1 ; WX 600 ; N omacron ; B 71 -15 637 585 ;
+C -1 ; WX 600 ; N Zacute ; B 62 0 665 784 ;
+C -1 ; WX 600 ; N Zcaron ; B 62 0 659 790 ;
+C -1 ; WX 600 ; N greaterequal ; B 26 0 627 696 ;
+C -1 ; WX 600 ; N Eth ; B 30 0 664 562 ;
+C -1 ; WX 600 ; N Ccedilla ; B 74 -206 675 580 ;
+C -1 ; WX 600 ; N lcommaaccent ; B 77 -250 546 626 ;
+C -1 ; WX 600 ; N tcaron ; B 118 -15 627 703 ;
+C -1 ; WX 600 ; N eogonek ; B 81 -199 605 454 ;
+C -1 ; WX 600 ; N Uogonek ; B 101 -199 716 562 ;
+C -1 ; WX 600 ; N Aacute ; B -9 0 655 784 ;
+C -1 ; WX 600 ; N Adieresis ; B -9 0 632 761 ;
+C -1 ; WX 600 ; N egrave ; B 81 -15 605 661 ;
+C -1 ; WX 600 ; N zacute ; B 81 0 614 661 ;
+C -1 ; WX 600 ; N iogonek ; B 77 -199 546 658 ;
+C -1 ; WX 600 ; N Oacute ; B 74 -18 645 784 ;
+C -1 ; WX 600 ; N oacute ; B 71 -15 649 661 ;
+C -1 ; WX 600 ; N amacron ; B 61 -15 637 585 ;
+C -1 ; WX 600 ; N sacute ; B 66 -17 609 661 ;
+C -1 ; WX 600 ; N idieresis ; B 77 0 561 618 ;
+C -1 ; WX 600 ; N Ocircumflex ; B 74 -18 645 780 ;
+C -1 ; WX 600 ; N Ugrave ; B 101 -18 716 784 ;
+C -1 ; WX 600 ; N Delta ; B 6 0 594 688 ;
+C -1 ; WX 600 ; N thorn ; B -32 -142 622 626 ;
+C -1 ; WX 600 ; N twosuperior ; B 191 230 542 616 ;
+C -1 ; WX 600 ; N Odieresis ; B 74 -18 645 761 ;
+C -1 ; WX 600 ; N mu ; B 49 -142 592 439 ;
+C -1 ; WX 600 ; N igrave ; B 77 0 546 661 ;
+C -1 ; WX 600 ; N ohungarumlaut ; B 71 -15 809 661 ;
+C -1 ; WX 600 ; N Eogonek ; B 25 -199 670 562 ;
+C -1 ; WX 600 ; N dcroat ; B 60 -15 712 626 ;
+C -1 ; WX 600 ; N threequarters ; B 8 -60 699 661 ;
+C -1 ; WX 600 ; N Scedilla ; B 54 -206 673 582 ;
+C -1 ; WX 600 ; N lcaron ; B 77 0 731 626 ;
+C -1 ; WX 600 ; N Kcommaaccent ; B 21 -250 692 562 ;
+C -1 ; WX 600 ; N Lacute ; B 39 0 636 784 ;
+C -1 ; WX 600 ; N trademark ; B 86 230 869 562 ;
+C -1 ; WX 600 ; N edotaccent ; B 81 -15 605 638 ;
+C -1 ; WX 600 ; N Igrave ; B 77 0 643 784 ;
+C -1 ; WX 600 ; N Imacron ; B 77 0 663 708 ;
+C -1 ; WX 600 ; N Lcaron ; B 39 0 757 562 ;
+C -1 ; WX 600 ; N onehalf ; B 22 -60 716 661 ;
+C -1 ; WX 600 ; N lessequal ; B 26 0 671 696 ;
+C -1 ; WX 600 ; N ocircumflex ; B 71 -15 622 657 ;
+C -1 ; WX 600 ; N ntilde ; B 18 0 643 636 ;
+C -1 ; WX 600 ; N Uhungarumlaut ; B 101 -18 805 784 ;
+C -1 ; WX 600 ; N Eacute ; B 25 0 670 784 ;
+C -1 ; WX 600 ; N emacron ; B 81 -15 637 585 ;
+C -1 ; WX 600 ; N gbreve ; B 40 -146 674 661 ;
+C -1 ; WX 600 ; N onequarter ; B 13 -60 707 661 ;
+C -1 ; WX 600 ; N Scaron ; B 54 -22 689 790 ;
+C -1 ; WX 600 ; N Scommaaccent ; B 54 -250 673 582 ;
+C -1 ; WX 600 ; N Ohungarumlaut ; B 74 -18 795 784 ;
+C -1 ; WX 600 ; N degree ; B 173 243 570 616 ;
+C -1 ; WX 600 ; N ograve ; B 71 -15 622 661 ;
+C -1 ; WX 600 ; N Ccaron ; B 74 -18 689 790 ;
+C -1 ; WX 600 ; N ugrave ; B 70 -15 592 661 ;
+C -1 ; WX 600 ; N radical ; B 67 -104 635 778 ;
+C -1 ; WX 600 ; N Dcaron ; B 30 0 664 790 ;
+C -1 ; WX 600 ; N rcommaaccent ; B 47 -250 655 454 ;
+C -1 ; WX 600 ; N Ntilde ; B 8 -12 730 759 ;
+C -1 ; WX 600 ; N otilde ; B 71 -15 643 636 ;
+C -1 ; WX 600 ; N Rcommaaccent ; B 24 -250 617 562 ;
+C -1 ; WX 600 ; N Lcommaaccent ; B 39 -250 636 562 ;
+C -1 ; WX 600 ; N Atilde ; B -9 0 669 759 ;
+C -1 ; WX 600 ; N Aogonek ; B -9 -199 632 562 ;
+C -1 ; WX 600 ; N Aring ; B -9 0 632 801 ;
+C -1 ; WX 600 ; N Otilde ; B 74 -18 669 759 ;
+C -1 ; WX 600 ; N zdotaccent ; B 81 0 614 638 ;
+C -1 ; WX 600 ; N Ecaron ; B 25 0 670 790 ;
+C -1 ; WX 600 ; N Iogonek ; B 77 -199 643 562 ;
+C -1 ; WX 600 ; N kcommaaccent ; B 33 -250 643 626 ;
+C -1 ; WX 600 ; N minus ; B 114 203 596 313 ;
+C -1 ; WX 600 ; N Icircumflex ; B 77 0 643 780 ;
+C -1 ; WX 600 ; N ncaron ; B 18 0 633 667 ;
+C -1 ; WX 600 ; N tcommaaccent ; B 118 -250 567 562 ;
+C -1 ; WX 600 ; N logicalnot ; B 135 103 617 413 ;
+C -1 ; WX 600 ; N odieresis ; B 71 -15 622 638 ;
+C -1 ; WX 600 ; N udieresis ; B 70 -15 595 638 ;
+C -1 ; WX 600 ; N notequal ; B 30 -47 626 563 ;
+C -1 ; WX 600 ; N gcommaaccent ; B 40 -146 674 714 ;
+C -1 ; WX 600 ; N eth ; B 93 -27 661 626 ;
+C -1 ; WX 600 ; N zcaron ; B 81 0 643 667 ;
+C -1 ; WX 600 ; N ncommaaccent ; B 18 -250 615 454 ;
+C -1 ; WX 600 ; N onesuperior ; B 212 230 514 616 ;
+C -1 ; WX 600 ; N imacron ; B 77 0 575 585 ;
+C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+EndFontMetrics
diff --git a/program/libraries/afm/Courier-Oblique.afm b/program/libraries/afm/Courier-Oblique.afm
new file mode 100644 (file)
index 0000000..3dc163f
--- /dev/null
@@ -0,0 +1,342 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated.  All Rights Reserved.
+Comment Creation Date: Thu May  1 17:37:52 1997
+Comment UniqueID 43051
+Comment VMusage 16248 75829
+FontName Courier-Oblique
+FullName Courier Oblique
+FamilyName Courier
+Weight Medium
+ItalicAngle -12
+IsFixedPitch true
+CharacterSet ExtendedRoman
+FontBBox -27 -250 849 805 
+UnderlinePosition -100
+UnderlineThickness 50
+Version 003.000
+Notice Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated.  All Rights Reserved.
+EncodingScheme AdobeStandardEncoding
+CapHeight 562
+XHeight 426
+Ascender 629
+Descender -157
+StdHW 51
+StdVW 51
+StartCharMetrics 315
+C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 600 ; N exclam ; B 243 -15 464 572 ;
+C 34 ; WX 600 ; N quotedbl ; B 273 328 532 562 ;
+C 35 ; WX 600 ; N numbersign ; B 133 -32 596 639 ;
+C 36 ; WX 600 ; N dollar ; B 108 -126 596 662 ;
+C 37 ; WX 600 ; N percent ; B 134 -15 599 622 ;
+C 38 ; WX 600 ; N ampersand ; B 87 -15 580 543 ;
+C 39 ; WX 600 ; N quoteright ; B 283 328 495 562 ;
+C 40 ; WX 600 ; N parenleft ; B 313 -108 572 622 ;
+C 41 ; WX 600 ; N parenright ; B 137 -108 396 622 ;
+C 42 ; WX 600 ; N asterisk ; B 212 257 580 607 ;
+C 43 ; WX 600 ; N plus ; B 129 44 580 470 ;
+C 44 ; WX 600 ; N comma ; B 157 -112 370 122 ;
+C 45 ; WX 600 ; N hyphen ; B 152 231 558 285 ;
+C 46 ; WX 600 ; N period ; B 238 -15 382 109 ;
+C 47 ; WX 600 ; N slash ; B 112 -80 604 629 ;
+C 48 ; WX 600 ; N zero ; B 154 -15 575 622 ;
+C 49 ; WX 600 ; N one ; B 98 0 515 622 ;
+C 50 ; WX 600 ; N two ; B 70 0 568 622 ;
+C 51 ; WX 600 ; N three ; B 82 -15 538 622 ;
+C 52 ; WX 600 ; N four ; B 108 0 541 622 ;
+C 53 ; WX 600 ; N five ; B 99 -15 589 607 ;
+C 54 ; WX 600 ; N six ; B 155 -15 629 622 ;
+C 55 ; WX 600 ; N seven ; B 182 0 612 607 ;
+C 56 ; WX 600 ; N eight ; B 132 -15 588 622 ;
+C 57 ; WX 600 ; N nine ; B 93 -15 574 622 ;
+C 58 ; WX 600 ; N colon ; B 238 -15 441 385 ;
+C 59 ; WX 600 ; N semicolon ; B 157 -112 441 385 ;
+C 60 ; WX 600 ; N less ; B 96 42 610 472 ;
+C 61 ; WX 600 ; N equal ; B 109 138 600 376 ;
+C 62 ; WX 600 ; N greater ; B 85 42 599 472 ;
+C 63 ; WX 600 ; N question ; B 222 -15 583 572 ;
+C 64 ; WX 600 ; N at ; B 127 -15 582 622 ;
+C 65 ; WX 600 ; N A ; B 3 0 607 562 ;
+C 66 ; WX 600 ; N B ; B 43 0 616 562 ;
+C 67 ; WX 600 ; N C ; B 93 -18 655 580 ;
+C 68 ; WX 600 ; N D ; B 43 0 645 562 ;
+C 69 ; WX 600 ; N E ; B 53 0 660 562 ;
+C 70 ; WX 600 ; N F ; B 53 0 660 562 ;
+C 71 ; WX 600 ; N G ; B 83 -18 645 580 ;
+C 72 ; WX 600 ; N H ; B 32 0 687 562 ;
+C 73 ; WX 600 ; N I ; B 96 0 623 562 ;
+C 74 ; WX 600 ; N J ; B 52 -18 685 562 ;
+C 75 ; WX 600 ; N K ; B 38 0 671 562 ;
+C 76 ; WX 600 ; N L ; B 47 0 607 562 ;
+C 77 ; WX 600 ; N M ; B 4 0 715 562 ;
+C 78 ; WX 600 ; N N ; B 7 -13 712 562 ;
+C 79 ; WX 600 ; N O ; B 94 -18 625 580 ;
+C 80 ; WX 600 ; N P ; B 79 0 644 562 ;
+C 81 ; WX 600 ; N Q ; B 95 -138 625 580 ;
+C 82 ; WX 600 ; N R ; B 38 0 598 562 ;
+C 83 ; WX 600 ; N S ; B 76 -20 650 580 ;
+C 84 ; WX 600 ; N T ; B 108 0 665 562 ;
+C 85 ; WX 600 ; N U ; B 125 -18 702 562 ;
+C 86 ; WX 600 ; N V ; B 105 -13 723 562 ;
+C 87 ; WX 600 ; N W ; B 106 -13 722 562 ;
+C 88 ; WX 600 ; N X ; B 23 0 675 562 ;
+C 89 ; WX 600 ; N Y ; B 133 0 695 562 ;
+C 90 ; WX 600 ; N Z ; B 86 0 610 562 ;
+C 91 ; WX 600 ; N bracketleft ; B 246 -108 574 622 ;
+C 92 ; WX 600 ; N backslash ; B 249 -80 468 629 ;
+C 93 ; WX 600 ; N bracketright ; B 135 -108 463 622 ;
+C 94 ; WX 600 ; N asciicircum ; B 175 354 587 622 ;
+C 95 ; WX 600 ; N underscore ; B -27 -125 584 -75 ;
+C 96 ; WX 600 ; N quoteleft ; B 343 328 457 562 ;
+C 97 ; WX 600 ; N a ; B 76 -15 569 441 ;
+C 98 ; WX 600 ; N b ; B 29 -15 625 629 ;
+C 99 ; WX 600 ; N c ; B 106 -15 608 441 ;
+C 100 ; WX 600 ; N d ; B 85 -15 640 629 ;
+C 101 ; WX 600 ; N e ; B 106 -15 598 441 ;
+C 102 ; WX 600 ; N f ; B 114 0 662 629 ; L i fi ; L l fl ;
+C 103 ; WX 600 ; N g ; B 61 -157 657 441 ;
+C 104 ; WX 600 ; N h ; B 33 0 592 629 ;
+C 105 ; WX 600 ; N i ; B 95 0 515 657 ;
+C 106 ; WX 600 ; N j ; B 52 -157 550 657 ;
+C 107 ; WX 600 ; N k ; B 58 0 633 629 ;
+C 108 ; WX 600 ; N l ; B 95 0 515 629 ;
+C 109 ; WX 600 ; N m ; B -5 0 615 441 ;
+C 110 ; WX 600 ; N n ; B 26 0 585 441 ;
+C 111 ; WX 600 ; N o ; B 102 -15 588 441 ;
+C 112 ; WX 600 ; N p ; B -24 -157 605 441 ;
+C 113 ; WX 600 ; N q ; B 85 -157 682 441 ;
+C 114 ; WX 600 ; N r ; B 60 0 636 441 ;
+C 115 ; WX 600 ; N s ; B 78 -15 584 441 ;
+C 116 ; WX 600 ; N t ; B 167 -15 561 561 ;
+C 117 ; WX 600 ; N u ; B 101 -15 572 426 ;
+C 118 ; WX 600 ; N v ; B 90 -10 681 426 ;
+C 119 ; WX 600 ; N w ; B 76 -10 695 426 ;
+C 120 ; WX 600 ; N x ; B 20 0 655 426 ;
+C 121 ; WX 600 ; N y ; B -4 -157 683 426 ;
+C 122 ; WX 600 ; N z ; B 99 0 593 426 ;
+C 123 ; WX 600 ; N braceleft ; B 233 -108 569 622 ;
+C 124 ; WX 600 ; N bar ; B 222 -250 485 750 ;
+C 125 ; WX 600 ; N braceright ; B 140 -108 477 622 ;
+C 126 ; WX 600 ; N asciitilde ; B 116 197 600 320 ;
+C 161 ; WX 600 ; N exclamdown ; B 225 -157 445 430 ;
+C 162 ; WX 600 ; N cent ; B 151 -49 588 614 ;
+C 163 ; WX 600 ; N sterling ; B 124 -21 621 611 ;
+C 164 ; WX 600 ; N fraction ; B 84 -57 646 665 ;
+C 165 ; WX 600 ; N yen ; B 120 0 693 562 ;
+C 166 ; WX 600 ; N florin ; B -26 -143 671 622 ;
+C 167 ; WX 600 ; N section ; B 104 -78 590 580 ;
+C 168 ; WX 600 ; N currency ; B 94 58 628 506 ;
+C 169 ; WX 600 ; N quotesingle ; B 345 328 460 562 ;
+C 170 ; WX 600 ; N quotedblleft ; B 262 328 541 562 ;
+C 171 ; WX 600 ; N guillemotleft ; B 92 70 652 446 ;
+C 172 ; WX 600 ; N guilsinglleft ; B 204 70 540 446 ;
+C 173 ; WX 600 ; N guilsinglright ; B 170 70 506 446 ;
+C 174 ; WX 600 ; N fi ; B 3 0 619 629 ;
+C 175 ; WX 600 ; N fl ; B 3 0 619 629 ;
+C 177 ; WX 600 ; N endash ; B 124 231 586 285 ;
+C 178 ; WX 600 ; N dagger ; B 217 -78 546 580 ;
+C 179 ; WX 600 ; N daggerdbl ; B 163 -78 546 580 ;
+C 180 ; WX 600 ; N periodcentered ; B 275 189 434 327 ;
+C 182 ; WX 600 ; N paragraph ; B 100 -78 630 562 ;
+C 183 ; WX 600 ; N bullet ; B 224 130 485 383 ;
+C 184 ; WX 600 ; N quotesinglbase ; B 185 -134 397 100 ;
+C 185 ; WX 600 ; N quotedblbase ; B 115 -134 478 100 ;
+C 186 ; WX 600 ; N quotedblright ; B 213 328 576 562 ;
+C 187 ; WX 600 ; N guillemotright ; B 58 70 618 446 ;
+C 188 ; WX 600 ; N ellipsis ; B 46 -15 575 111 ;
+C 189 ; WX 600 ; N perthousand ; B 59 -15 627 622 ;
+C 191 ; WX 600 ; N questiondown ; B 105 -157 466 430 ;
+C 193 ; WX 600 ; N grave ; B 294 497 484 672 ;
+C 194 ; WX 600 ; N acute ; B 348 497 612 672 ;
+C 195 ; WX 600 ; N circumflex ; B 229 477 581 654 ;
+C 196 ; WX 600 ; N tilde ; B 212 489 629 606 ;
+C 197 ; WX 600 ; N macron ; B 232 525 600 565 ;
+C 198 ; WX 600 ; N breve ; B 279 501 576 609 ;
+C 199 ; WX 600 ; N dotaccent ; B 373 537 478 640 ;
+C 200 ; WX 600 ; N dieresis ; B 272 537 579 640 ;
+C 202 ; WX 600 ; N ring ; B 332 463 500 627 ;
+C 203 ; WX 600 ; N cedilla ; B 197 -151 344 10 ;
+C 205 ; WX 600 ; N hungarumlaut ; B 239 497 683 672 ;
+C 206 ; WX 600 ; N ogonek ; B 189 -172 377 4 ;
+C 207 ; WX 600 ; N caron ; B 262 492 614 669 ;
+C 208 ; WX 600 ; N emdash ; B 49 231 661 285 ;
+C 225 ; WX 600 ; N AE ; B 3 0 655 562 ;
+C 227 ; WX 600 ; N ordfeminine ; B 209 249 512 580 ;
+C 232 ; WX 600 ; N Lslash ; B 47 0 607 562 ;
+C 233 ; WX 600 ; N Oslash ; B 94 -80 625 629 ;
+C 234 ; WX 600 ; N OE ; B 59 0 672 562 ;
+C 235 ; WX 600 ; N ordmasculine ; B 210 249 535 580 ;
+C 241 ; WX 600 ; N ae ; B 41 -15 626 441 ;
+C 245 ; WX 600 ; N dotlessi ; B 95 0 515 426 ;
+C 248 ; WX 600 ; N lslash ; B 95 0 587 629 ;
+C 249 ; WX 600 ; N oslash ; B 102 -80 588 506 ;
+C 250 ; WX 600 ; N oe ; B 54 -15 615 441 ;
+C 251 ; WX 600 ; N germandbls ; B 48 -15 617 629 ;
+C -1 ; WX 600 ; N Idieresis ; B 96 0 623 753 ;
+C -1 ; WX 600 ; N eacute ; B 106 -15 612 672 ;
+C -1 ; WX 600 ; N abreve ; B 76 -15 576 609 ;
+C -1 ; WX 600 ; N uhungarumlaut ; B 101 -15 723 672 ;
+C -1 ; WX 600 ; N ecaron ; B 106 -15 614 669 ;
+C -1 ; WX 600 ; N Ydieresis ; B 133 0 695 753 ;
+C -1 ; WX 600 ; N divide ; B 136 48 573 467 ;
+C -1 ; WX 600 ; N Yacute ; B 133 0 695 805 ;
+C -1 ; WX 600 ; N Acircumflex ; B 3 0 607 787 ;
+C -1 ; WX 600 ; N aacute ; B 76 -15 612 672 ;
+C -1 ; WX 600 ; N Ucircumflex ; B 125 -18 702 787 ;
+C -1 ; WX 600 ; N yacute ; B -4 -157 683 672 ;
+C -1 ; WX 600 ; N scommaaccent ; B 78 -250 584 441 ;
+C -1 ; WX 600 ; N ecircumflex ; B 106 -15 598 654 ;
+C -1 ; WX 600 ; N Uring ; B 125 -18 702 760 ;
+C -1 ; WX 600 ; N Udieresis ; B 125 -18 702 753 ;
+C -1 ; WX 600 ; N aogonek ; B 76 -172 569 441 ;
+C -1 ; WX 600 ; N Uacute ; B 125 -18 702 805 ;
+C -1 ; WX 600 ; N uogonek ; B 101 -172 572 426 ;
+C -1 ; WX 600 ; N Edieresis ; B 53 0 660 753 ;
+C -1 ; WX 600 ; N Dcroat ; B 43 0 645 562 ;
+C -1 ; WX 600 ; N commaaccent ; B 145 -250 323 -58 ;
+C -1 ; WX 600 ; N copyright ; B 53 -18 667 580 ;
+C -1 ; WX 600 ; N Emacron ; B 53 0 660 698 ;
+C -1 ; WX 600 ; N ccaron ; B 106 -15 614 669 ;
+C -1 ; WX 600 ; N aring ; B 76 -15 569 627 ;
+C -1 ; WX 600 ; N Ncommaaccent ; B 7 -250 712 562 ;
+C -1 ; WX 600 ; N lacute ; B 95 0 640 805 ;
+C -1 ; WX 600 ; N agrave ; B 76 -15 569 672 ;
+C -1 ; WX 600 ; N Tcommaaccent ; B 108 -250 665 562 ;
+C -1 ; WX 600 ; N Cacute ; B 93 -18 655 805 ;
+C -1 ; WX 600 ; N atilde ; B 76 -15 629 606 ;
+C -1 ; WX 600 ; N Edotaccent ; B 53 0 660 753 ;
+C -1 ; WX 600 ; N scaron ; B 78 -15 614 669 ;
+C -1 ; WX 600 ; N scedilla ; B 78 -151 584 441 ;
+C -1 ; WX 600 ; N iacute ; B 95 0 612 672 ;
+C -1 ; WX 600 ; N lozenge ; B 94 0 519 706 ;
+C -1 ; WX 600 ; N Rcaron ; B 38 0 642 802 ;
+C -1 ; WX 600 ; N Gcommaaccent ; B 83 -250 645 580 ;
+C -1 ; WX 600 ; N ucircumflex ; B 101 -15 572 654 ;
+C -1 ; WX 600 ; N acircumflex ; B 76 -15 581 654 ;
+C -1 ; WX 600 ; N Amacron ; B 3 0 607 698 ;
+C -1 ; WX 600 ; N rcaron ; B 60 0 636 669 ;
+C -1 ; WX 600 ; N ccedilla ; B 106 -151 614 441 ;
+C -1 ; WX 600 ; N Zdotaccent ; B 86 0 610 753 ;
+C -1 ; WX 600 ; N Thorn ; B 79 0 606 562 ;
+C -1 ; WX 600 ; N Omacron ; B 94 -18 628 698 ;
+C -1 ; WX 600 ; N Racute ; B 38 0 670 805 ;
+C -1 ; WX 600 ; N Sacute ; B 76 -20 650 805 ;
+C -1 ; WX 600 ; N dcaron ; B 85 -15 849 629 ;
+C -1 ; WX 600 ; N Umacron ; B 125 -18 702 698 ;
+C -1 ; WX 600 ; N uring ; B 101 -15 572 627 ;
+C -1 ; WX 600 ; N threesuperior ; B 213 240 501 622 ;
+C -1 ; WX 600 ; N Ograve ; B 94 -18 625 805 ;
+C -1 ; WX 600 ; N Agrave ; B 3 0 607 805 ;
+C -1 ; WX 600 ; N Abreve ; B 3 0 607 732 ;
+C -1 ; WX 600 ; N multiply ; B 103 43 607 470 ;
+C -1 ; WX 600 ; N uacute ; B 101 -15 602 672 ;
+C -1 ; WX 600 ; N Tcaron ; B 108 0 665 802 ;
+C -1 ; WX 600 ; N partialdiff ; B 45 -38 546 710 ;
+C -1 ; WX 600 ; N ydieresis ; B -4 -157 683 620 ;
+C -1 ; WX 600 ; N Nacute ; B 7 -13 712 805 ;
+C -1 ; WX 600 ; N icircumflex ; B 95 0 551 654 ;
+C -1 ; WX 600 ; N Ecircumflex ; B 53 0 660 787 ;
+C -1 ; WX 600 ; N adieresis ; B 76 -15 575 620 ;
+C -1 ; WX 600 ; N edieresis ; B 106 -15 598 620 ;
+C -1 ; WX 600 ; N cacute ; B 106 -15 612 672 ;
+C -1 ; WX 600 ; N nacute ; B 26 0 602 672 ;
+C -1 ; WX 600 ; N umacron ; B 101 -15 600 565 ;
+C -1 ; WX 600 ; N Ncaron ; B 7 -13 712 802 ;
+C -1 ; WX 600 ; N Iacute ; B 96 0 640 805 ;
+C -1 ; WX 600 ; N plusminus ; B 96 44 594 558 ;
+C -1 ; WX 600 ; N brokenbar ; B 238 -175 469 675 ;
+C -1 ; WX 600 ; N registered ; B 53 -18 667 580 ;
+C -1 ; WX 600 ; N Gbreve ; B 83 -18 645 732 ;
+C -1 ; WX 600 ; N Idotaccent ; B 96 0 623 753 ;
+C -1 ; WX 600 ; N summation ; B 15 -10 670 706 ;
+C -1 ; WX 600 ; N Egrave ; B 53 0 660 805 ;
+C -1 ; WX 600 ; N racute ; B 60 0 636 672 ;
+C -1 ; WX 600 ; N omacron ; B 102 -15 600 565 ;
+C -1 ; WX 600 ; N Zacute ; B 86 0 670 805 ;
+C -1 ; WX 600 ; N Zcaron ; B 86 0 642 802 ;
+C -1 ; WX 600 ; N greaterequal ; B 98 0 594 710 ;
+C -1 ; WX 600 ; N Eth ; B 43 0 645 562 ;
+C -1 ; WX 600 ; N Ccedilla ; B 93 -151 658 580 ;
+C -1 ; WX 600 ; N lcommaaccent ; B 95 -250 515 629 ;
+C -1 ; WX 600 ; N tcaron ; B 167 -15 587 717 ;
+C -1 ; WX 600 ; N eogonek ; B 106 -172 598 441 ;
+C -1 ; WX 600 ; N Uogonek ; B 124 -172 702 562 ;
+C -1 ; WX 600 ; N Aacute ; B 3 0 660 805 ;
+C -1 ; WX 600 ; N Adieresis ; B 3 0 607 753 ;
+C -1 ; WX 600 ; N egrave ; B 106 -15 598 672 ;
+C -1 ; WX 600 ; N zacute ; B 99 0 612 672 ;
+C -1 ; WX 600 ; N iogonek ; B 95 -172 515 657 ;
+C -1 ; WX 600 ; N Oacute ; B 94 -18 640 805 ;
+C -1 ; WX 600 ; N oacute ; B 102 -15 612 672 ;
+C -1 ; WX 600 ; N amacron ; B 76 -15 600 565 ;
+C -1 ; WX 600 ; N sacute ; B 78 -15 612 672 ;
+C -1 ; WX 600 ; N idieresis ; B 95 0 545 620 ;
+C -1 ; WX 600 ; N Ocircumflex ; B 94 -18 625 787 ;
+C -1 ; WX 600 ; N Ugrave ; B 125 -18 702 805 ;
+C -1 ; WX 600 ; N Delta ; B 6 0 598 688 ;
+C -1 ; WX 600 ; N thorn ; B -24 -157 605 629 ;
+C -1 ; WX 600 ; N twosuperior ; B 230 249 535 622 ;
+C -1 ; WX 600 ; N Odieresis ; B 94 -18 625 753 ;
+C -1 ; WX 600 ; N mu ; B 72 -157 572 426 ;
+C -1 ; WX 600 ; N igrave ; B 95 0 515 672 ;
+C -1 ; WX 600 ; N ohungarumlaut ; B 102 -15 723 672 ;
+C -1 ; WX 600 ; N Eogonek ; B 53 -172 660 562 ;
+C -1 ; WX 600 ; N dcroat ; B 85 -15 704 629 ;
+C -1 ; WX 600 ; N threequarters ; B 73 -56 659 666 ;
+C -1 ; WX 600 ; N Scedilla ; B 76 -151 650 580 ;
+C -1 ; WX 600 ; N lcaron ; B 95 0 667 629 ;
+C -1 ; WX 600 ; N Kcommaaccent ; B 38 -250 671 562 ;
+C -1 ; WX 600 ; N Lacute ; B 47 0 607 805 ;
+C -1 ; WX 600 ; N trademark ; B 75 263 742 562 ;
+C -1 ; WX 600 ; N edotaccent ; B 106 -15 598 620 ;
+C -1 ; WX 600 ; N Igrave ; B 96 0 623 805 ;
+C -1 ; WX 600 ; N Imacron ; B 96 0 628 698 ;
+C -1 ; WX 600 ; N Lcaron ; B 47 0 632 562 ;
+C -1 ; WX 600 ; N onehalf ; B 65 -57 669 665 ;
+C -1 ; WX 600 ; N lessequal ; B 98 0 645 710 ;
+C -1 ; WX 600 ; N ocircumflex ; B 102 -15 588 654 ;
+C -1 ; WX 600 ; N ntilde ; B 26 0 629 606 ;
+C -1 ; WX 600 ; N Uhungarumlaut ; B 125 -18 761 805 ;
+C -1 ; WX 600 ; N Eacute ; B 53 0 670 805 ;
+C -1 ; WX 600 ; N emacron ; B 106 -15 600 565 ;
+C -1 ; WX 600 ; N gbreve ; B 61 -157 657 609 ;
+C -1 ; WX 600 ; N onequarter ; B 65 -57 674 665 ;
+C -1 ; WX 600 ; N Scaron ; B 76 -20 672 802 ;
+C -1 ; WX 600 ; N Scommaaccent ; B 76 -250 650 580 ;
+C -1 ; WX 600 ; N Ohungarumlaut ; B 94 -18 751 805 ;
+C -1 ; WX 600 ; N degree ; B 214 269 576 622 ;
+C -1 ; WX 600 ; N ograve ; B 102 -15 588 672 ;
+C -1 ; WX 600 ; N Ccaron ; B 93 -18 672 802 ;
+C -1 ; WX 600 ; N ugrave ; B 101 -15 572 672 ;
+C -1 ; WX 600 ; N radical ; B 85 -15 765 792 ;
+C -1 ; WX 600 ; N Dcaron ; B 43 0 645 802 ;
+C -1 ; WX 600 ; N rcommaaccent ; B 60 -250 636 441 ;
+C -1 ; WX 600 ; N Ntilde ; B 7 -13 712 729 ;
+C -1 ; WX 600 ; N otilde ; B 102 -15 629 606 ;
+C -1 ; WX 600 ; N Rcommaaccent ; B 38 -250 598 562 ;
+C -1 ; WX 600 ; N Lcommaaccent ; B 47 -250 607 562 ;
+C -1 ; WX 600 ; N Atilde ; B 3 0 655 729 ;
+C -1 ; WX 600 ; N Aogonek ; B 3 -172 607 562 ;
+C -1 ; WX 600 ; N Aring ; B 3 0 607 750 ;
+C -1 ; WX 600 ; N Otilde ; B 94 -18 655 729 ;
+C -1 ; WX 600 ; N zdotaccent ; B 99 0 593 620 ;
+C -1 ; WX 600 ; N Ecaron ; B 53 0 660 802 ;
+C -1 ; WX 600 ; N Iogonek ; B 96 -172 623 562 ;
+C -1 ; WX 600 ; N kcommaaccent ; B 58 -250 633 629 ;
+C -1 ; WX 600 ; N minus ; B 129 232 580 283 ;
+C -1 ; WX 600 ; N Icircumflex ; B 96 0 623 787 ;
+C -1 ; WX 600 ; N ncaron ; B 26 0 614 669 ;
+C -1 ; WX 600 ; N tcommaaccent ; B 165 -250 561 561 ;
+C -1 ; WX 600 ; N logicalnot ; B 155 108 591 369 ;
+C -1 ; WX 600 ; N odieresis ; B 102 -15 588 620 ;
+C -1 ; WX 600 ; N udieresis ; B 101 -15 575 620 ;
+C -1 ; WX 600 ; N notequal ; B 43 -16 621 529 ;
+C -1 ; WX 600 ; N gcommaaccent ; B 61 -157 657 708 ;
+C -1 ; WX 600 ; N eth ; B 102 -15 639 629 ;
+C -1 ; WX 600 ; N zcaron ; B 99 0 624 669 ;
+C -1 ; WX 600 ; N ncommaaccent ; B 26 -250 585 441 ;
+C -1 ; WX 600 ; N onesuperior ; B 231 249 491 622 ;
+C -1 ; WX 600 ; N imacron ; B 95 0 543 565 ;
+C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+EndFontMetrics
diff --git a/program/libraries/afm/Courier.afm b/program/libraries/afm/Courier.afm
new file mode 100644 (file)
index 0000000..2f7be81
--- /dev/null
@@ -0,0 +1,342 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated.  All Rights Reserved.
+Comment Creation Date: Thu May  1 17:27:09 1997
+Comment UniqueID 43050
+Comment VMusage 39754 50779
+FontName Courier
+FullName Courier
+FamilyName Courier
+Weight Medium
+ItalicAngle 0
+IsFixedPitch true
+CharacterSet ExtendedRoman
+FontBBox -23 -250 715 805 
+UnderlinePosition -100
+UnderlineThickness 50
+Version 003.000
+Notice Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated.  All Rights Reserved.
+EncodingScheme AdobeStandardEncoding
+CapHeight 562
+XHeight 426
+Ascender 629
+Descender -157
+StdHW 51
+StdVW 51
+StartCharMetrics 315
+C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 600 ; N exclam ; B 236 -15 364 572 ;
+C 34 ; WX 600 ; N quotedbl ; B 187 328 413 562 ;
+C 35 ; WX 600 ; N numbersign ; B 93 -32 507 639 ;
+C 36 ; WX 600 ; N dollar ; B 105 -126 496 662 ;
+C 37 ; WX 600 ; N percent ; B 81 -15 518 622 ;
+C 38 ; WX 600 ; N ampersand ; B 63 -15 538 543 ;
+C 39 ; WX 600 ; N quoteright ; B 213 328 376 562 ;
+C 40 ; WX 600 ; N parenleft ; B 269 -108 440 622 ;
+C 41 ; WX 600 ; N parenright ; B 160 -108 331 622 ;
+C 42 ; WX 600 ; N asterisk ; B 116 257 484 607 ;
+C 43 ; WX 600 ; N plus ; B 80 44 520 470 ;
+C 44 ; WX 600 ; N comma ; B 181 -112 344 122 ;
+C 45 ; WX 600 ; N hyphen ; B 103 231 497 285 ;
+C 46 ; WX 600 ; N period ; B 229 -15 371 109 ;
+C 47 ; WX 600 ; N slash ; B 125 -80 475 629 ;
+C 48 ; WX 600 ; N zero ; B 106 -15 494 622 ;
+C 49 ; WX 600 ; N one ; B 96 0 505 622 ;
+C 50 ; WX 600 ; N two ; B 70 0 471 622 ;
+C 51 ; WX 600 ; N three ; B 75 -15 466 622 ;
+C 52 ; WX 600 ; N four ; B 78 0 500 622 ;
+C 53 ; WX 600 ; N five ; B 92 -15 497 607 ;
+C 54 ; WX 600 ; N six ; B 111 -15 497 622 ;
+C 55 ; WX 600 ; N seven ; B 82 0 483 607 ;
+C 56 ; WX 600 ; N eight ; B 102 -15 498 622 ;
+C 57 ; WX 600 ; N nine ; B 96 -15 489 622 ;
+C 58 ; WX 600 ; N colon ; B 229 -15 371 385 ;
+C 59 ; WX 600 ; N semicolon ; B 181 -112 371 385 ;
+C 60 ; WX 600 ; N less ; B 41 42 519 472 ;
+C 61 ; WX 600 ; N equal ; B 80 138 520 376 ;
+C 62 ; WX 600 ; N greater ; B 66 42 544 472 ;
+C 63 ; WX 600 ; N question ; B 129 -15 492 572 ;
+C 64 ; WX 600 ; N at ; B 77 -15 533 622 ;
+C 65 ; WX 600 ; N A ; B 3 0 597 562 ;
+C 66 ; WX 600 ; N B ; B 43 0 559 562 ;
+C 67 ; WX 600 ; N C ; B 41 -18 540 580 ;
+C 68 ; WX 600 ; N D ; B 43 0 574 562 ;
+C 69 ; WX 600 ; N E ; B 53 0 550 562 ;
+C 70 ; WX 600 ; N F ; B 53 0 545 562 ;
+C 71 ; WX 600 ; N G ; B 31 -18 575 580 ;
+C 72 ; WX 600 ; N H ; B 32 0 568 562 ;
+C 73 ; WX 600 ; N I ; B 96 0 504 562 ;
+C 74 ; WX 600 ; N J ; B 34 -18 566 562 ;
+C 75 ; WX 600 ; N K ; B 38 0 582 562 ;
+C 76 ; WX 600 ; N L ; B 47 0 554 562 ;
+C 77 ; WX 600 ; N M ; B 4 0 596 562 ;
+C 78 ; WX 600 ; N N ; B 7 -13 593 562 ;
+C 79 ; WX 600 ; N O ; B 43 -18 557 580 ;
+C 80 ; WX 600 ; N P ; B 79 0 558 562 ;
+C 81 ; WX 600 ; N Q ; B 43 -138 557 580 ;
+C 82 ; WX 600 ; N R ; B 38 0 588 562 ;
+C 83 ; WX 600 ; N S ; B 72 -20 529 580 ;
+C 84 ; WX 600 ; N T ; B 38 0 563 562 ;
+C 85 ; WX 600 ; N U ; B 17 -18 583 562 ;
+C 86 ; WX 600 ; N V ; B -4 -13 604 562 ;
+C 87 ; WX 600 ; N W ; B -3 -13 603 562 ;
+C 88 ; WX 600 ; N X ; B 23 0 577 562 ;
+C 89 ; WX 600 ; N Y ; B 24 0 576 562 ;
+C 90 ; WX 600 ; N Z ; B 86 0 514 562 ;
+C 91 ; WX 600 ; N bracketleft ; B 269 -108 442 622 ;
+C 92 ; WX 600 ; N backslash ; B 118 -80 482 629 ;
+C 93 ; WX 600 ; N bracketright ; B 158 -108 331 622 ;
+C 94 ; WX 600 ; N asciicircum ; B 94 354 506 622 ;
+C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ;
+C 96 ; WX 600 ; N quoteleft ; B 224 328 387 562 ;
+C 97 ; WX 600 ; N a ; B 53 -15 559 441 ;
+C 98 ; WX 600 ; N b ; B 14 -15 575 629 ;
+C 99 ; WX 600 ; N c ; B 66 -15 529 441 ;
+C 100 ; WX 600 ; N d ; B 45 -15 591 629 ;
+C 101 ; WX 600 ; N e ; B 66 -15 548 441 ;
+C 102 ; WX 600 ; N f ; B 114 0 531 629 ; L i fi ; L l fl ;
+C 103 ; WX 600 ; N g ; B 45 -157 566 441 ;
+C 104 ; WX 600 ; N h ; B 18 0 582 629 ;
+C 105 ; WX 600 ; N i ; B 95 0 505 657 ;
+C 106 ; WX 600 ; N j ; B 82 -157 410 657 ;
+C 107 ; WX 600 ; N k ; B 43 0 580 629 ;
+C 108 ; WX 600 ; N l ; B 95 0 505 629 ;
+C 109 ; WX 600 ; N m ; B -5 0 605 441 ;
+C 110 ; WX 600 ; N n ; B 26 0 575 441 ;
+C 111 ; WX 600 ; N o ; B 62 -15 538 441 ;
+C 112 ; WX 600 ; N p ; B 9 -157 555 441 ;
+C 113 ; WX 600 ; N q ; B 45 -157 591 441 ;
+C 114 ; WX 600 ; N r ; B 60 0 559 441 ;
+C 115 ; WX 600 ; N s ; B 80 -15 513 441 ;
+C 116 ; WX 600 ; N t ; B 87 -15 530 561 ;
+C 117 ; WX 600 ; N u ; B 21 -15 562 426 ;
+C 118 ; WX 600 ; N v ; B 10 -10 590 426 ;
+C 119 ; WX 600 ; N w ; B -4 -10 604 426 ;
+C 120 ; WX 600 ; N x ; B 20 0 580 426 ;
+C 121 ; WX 600 ; N y ; B 7 -157 592 426 ;
+C 122 ; WX 600 ; N z ; B 99 0 502 426 ;
+C 123 ; WX 600 ; N braceleft ; B 182 -108 437 622 ;
+C 124 ; WX 600 ; N bar ; B 275 -250 326 750 ;
+C 125 ; WX 600 ; N braceright ; B 163 -108 418 622 ;
+C 126 ; WX 600 ; N asciitilde ; B 63 197 540 320 ;
+C 161 ; WX 600 ; N exclamdown ; B 236 -157 364 430 ;
+C 162 ; WX 600 ; N cent ; B 96 -49 500 614 ;
+C 163 ; WX 600 ; N sterling ; B 84 -21 521 611 ;
+C 164 ; WX 600 ; N fraction ; B 92 -57 509 665 ;
+C 165 ; WX 600 ; N yen ; B 26 0 574 562 ;
+C 166 ; WX 600 ; N florin ; B 4 -143 539 622 ;
+C 167 ; WX 600 ; N section ; B 113 -78 488 580 ;
+C 168 ; WX 600 ; N currency ; B 73 58 527 506 ;
+C 169 ; WX 600 ; N quotesingle ; B 259 328 341 562 ;
+C 170 ; WX 600 ; N quotedblleft ; B 143 328 471 562 ;
+C 171 ; WX 600 ; N guillemotleft ; B 37 70 563 446 ;
+C 172 ; WX 600 ; N guilsinglleft ; B 149 70 451 446 ;
+C 173 ; WX 600 ; N guilsinglright ; B 149 70 451 446 ;
+C 174 ; WX 600 ; N fi ; B 3 0 597 629 ;
+C 175 ; WX 600 ; N fl ; B 3 0 597 629 ;
+C 177 ; WX 600 ; N endash ; B 75 231 525 285 ;
+C 178 ; WX 600 ; N dagger ; B 141 -78 459 580 ;
+C 179 ; WX 600 ; N daggerdbl ; B 141 -78 459 580 ;
+C 180 ; WX 600 ; N periodcentered ; B 222 189 378 327 ;
+C 182 ; WX 600 ; N paragraph ; B 50 -78 511 562 ;
+C 183 ; WX 600 ; N bullet ; B 172 130 428 383 ;
+C 184 ; WX 600 ; N quotesinglbase ; B 213 -134 376 100 ;
+C 185 ; WX 600 ; N quotedblbase ; B 143 -134 457 100 ;
+C 186 ; WX 600 ; N quotedblright ; B 143 328 457 562 ;
+C 187 ; WX 600 ; N guillemotright ; B 37 70 563 446 ;
+C 188 ; WX 600 ; N ellipsis ; B 37 -15 563 111 ;
+C 189 ; WX 600 ; N perthousand ; B 3 -15 600 622 ;
+C 191 ; WX 600 ; N questiondown ; B 108 -157 471 430 ;
+C 193 ; WX 600 ; N grave ; B 151 497 378 672 ;
+C 194 ; WX 600 ; N acute ; B 242 497 469 672 ;
+C 195 ; WX 600 ; N circumflex ; B 124 477 476 654 ;
+C 196 ; WX 600 ; N tilde ; B 105 489 503 606 ;
+C 197 ; WX 600 ; N macron ; B 120 525 480 565 ;
+C 198 ; WX 600 ; N breve ; B 153 501 447 609 ;
+C 199 ; WX 600 ; N dotaccent ; B 249 537 352 640 ;
+C 200 ; WX 600 ; N dieresis ; B 148 537 453 640 ;
+C 202 ; WX 600 ; N ring ; B 218 463 382 627 ;
+C 203 ; WX 600 ; N cedilla ; B 224 -151 362 10 ;
+C 205 ; WX 600 ; N hungarumlaut ; B 133 497 540 672 ;
+C 206 ; WX 600 ; N ogonek ; B 211 -172 407 4 ;
+C 207 ; WX 600 ; N caron ; B 124 492 476 669 ;
+C 208 ; WX 600 ; N emdash ; B 0 231 600 285 ;
+C 225 ; WX 600 ; N AE ; B 3 0 550 562 ;
+C 227 ; WX 600 ; N ordfeminine ; B 156 249 442 580 ;
+C 232 ; WX 600 ; N Lslash ; B 47 0 554 562 ;
+C 233 ; WX 600 ; N Oslash ; B 43 -80 557 629 ;
+C 234 ; WX 600 ; N OE ; B 7 0 567 562 ;
+C 235 ; WX 600 ; N ordmasculine ; B 157 249 443 580 ;
+C 241 ; WX 600 ; N ae ; B 19 -15 570 441 ;
+C 245 ; WX 600 ; N dotlessi ; B 95 0 505 426 ;
+C 248 ; WX 600 ; N lslash ; B 95 0 505 629 ;
+C 249 ; WX 600 ; N oslash ; B 62 -80 538 506 ;
+C 250 ; WX 600 ; N oe ; B 19 -15 559 441 ;
+C 251 ; WX 600 ; N germandbls ; B 48 -15 588 629 ;
+C -1 ; WX 600 ; N Idieresis ; B 96 0 504 753 ;
+C -1 ; WX 600 ; N eacute ; B 66 -15 548 672 ;
+C -1 ; WX 600 ; N abreve ; B 53 -15 559 609 ;
+C -1 ; WX 600 ; N uhungarumlaut ; B 21 -15 580 672 ;
+C -1 ; WX 600 ; N ecaron ; B 66 -15 548 669 ;
+C -1 ; WX 600 ; N Ydieresis ; B 24 0 576 753 ;
+C -1 ; WX 600 ; N divide ; B 87 48 513 467 ;
+C -1 ; WX 600 ; N Yacute ; B 24 0 576 805 ;
+C -1 ; WX 600 ; N Acircumflex ; B 3 0 597 787 ;
+C -1 ; WX 600 ; N aacute ; B 53 -15 559 672 ;
+C -1 ; WX 600 ; N Ucircumflex ; B 17 -18 583 787 ;
+C -1 ; WX 600 ; N yacute ; B 7 -157 592 672 ;
+C -1 ; WX 600 ; N scommaaccent ; B 80 -250 513 441 ;
+C -1 ; WX 600 ; N ecircumflex ; B 66 -15 548 654 ;
+C -1 ; WX 600 ; N Uring ; B 17 -18 583 760 ;
+C -1 ; WX 600 ; N Udieresis ; B 17 -18 583 753 ;
+C -1 ; WX 600 ; N aogonek ; B 53 -172 587 441 ;
+C -1 ; WX 600 ; N Uacute ; B 17 -18 583 805 ;
+C -1 ; WX 600 ; N uogonek ; B 21 -172 590 426 ;
+C -1 ; WX 600 ; N Edieresis ; B 53 0 550 753 ;
+C -1 ; WX 600 ; N Dcroat ; B 30 0 574 562 ;
+C -1 ; WX 600 ; N commaaccent ; B 198 -250 335 -58 ;
+C -1 ; WX 600 ; N copyright ; B 0 -18 600 580 ;
+C -1 ; WX 600 ; N Emacron ; B 53 0 550 698 ;
+C -1 ; WX 600 ; N ccaron ; B 66 -15 529 669 ;
+C -1 ; WX 600 ; N aring ; B 53 -15 559 627 ;
+C -1 ; WX 600 ; N Ncommaaccent ; B 7 -250 593 562 ;
+C -1 ; WX 600 ; N lacute ; B 95 0 505 805 ;
+C -1 ; WX 600 ; N agrave ; B 53 -15 559 672 ;
+C -1 ; WX 600 ; N Tcommaaccent ; B 38 -250 563 562 ;
+C -1 ; WX 600 ; N Cacute ; B 41 -18 540 805 ;
+C -1 ; WX 600 ; N atilde ; B 53 -15 559 606 ;
+C -1 ; WX 600 ; N Edotaccent ; B 53 0 550 753 ;
+C -1 ; WX 600 ; N scaron ; B 80 -15 513 669 ;
+C -1 ; WX 600 ; N scedilla ; B 80 -151 513 441 ;
+C -1 ; WX 600 ; N iacute ; B 95 0 505 672 ;
+C -1 ; WX 600 ; N lozenge ; B 18 0 443 706 ;
+C -1 ; WX 600 ; N Rcaron ; B 38 0 588 802 ;
+C -1 ; WX 600 ; N Gcommaaccent ; B 31 -250 575 580 ;
+C -1 ; WX 600 ; N ucircumflex ; B 21 -15 562 654 ;
+C -1 ; WX 600 ; N acircumflex ; B 53 -15 559 654 ;
+C -1 ; WX 600 ; N Amacron ; B 3 0 597 698 ;
+C -1 ; WX 600 ; N rcaron ; B 60 0 559 669 ;
+C -1 ; WX 600 ; N ccedilla ; B 66 -151 529 441 ;
+C -1 ; WX 600 ; N Zdotaccent ; B 86 0 514 753 ;
+C -1 ; WX 600 ; N Thorn ; B 79 0 538 562 ;
+C -1 ; WX 600 ; N Omacron ; B 43 -18 557 698 ;
+C -1 ; WX 600 ; N Racute ; B 38 0 588 805 ;
+C -1 ; WX 600 ; N Sacute ; B 72 -20 529 805 ;
+C -1 ; WX 600 ; N dcaron ; B 45 -15 715 629 ;
+C -1 ; WX 600 ; N Umacron ; B 17 -18 583 698 ;
+C -1 ; WX 600 ; N uring ; B 21 -15 562 627 ;
+C -1 ; WX 600 ; N threesuperior ; B 155 240 406 622 ;
+C -1 ; WX 600 ; N Ograve ; B 43 -18 557 805 ;
+C -1 ; WX 600 ; N Agrave ; B 3 0 597 805 ;
+C -1 ; WX 600 ; N Abreve ; B 3 0 597 732 ;
+C -1 ; WX 600 ; N multiply ; B 87 43 515 470 ;
+C -1 ; WX 600 ; N uacute ; B 21 -15 562 672 ;
+C -1 ; WX 600 ; N Tcaron ; B 38 0 563 802 ;
+C -1 ; WX 600 ; N partialdiff ; B 17 -38 459 710 ;
+C -1 ; WX 600 ; N ydieresis ; B 7 -157 592 620 ;
+C -1 ; WX 600 ; N Nacute ; B 7 -13 593 805 ;
+C -1 ; WX 600 ; N icircumflex ; B 94 0 505 654 ;
+C -1 ; WX 600 ; N Ecircumflex ; B 53 0 550 787 ;
+C -1 ; WX 600 ; N adieresis ; B 53 -15 559 620 ;
+C -1 ; WX 600 ; N edieresis ; B 66 -15 548 620 ;
+C -1 ; WX 600 ; N cacute ; B 66 -15 529 672 ;
+C -1 ; WX 600 ; N nacute ; B 26 0 575 672 ;
+C -1 ; WX 600 ; N umacron ; B 21 -15 562 565 ;
+C -1 ; WX 600 ; N Ncaron ; B 7 -13 593 802 ;
+C -1 ; WX 600 ; N Iacute ; B 96 0 504 805 ;
+C -1 ; WX 600 ; N plusminus ; B 87 44 513 558 ;
+C -1 ; WX 600 ; N brokenbar ; B 275 -175 326 675 ;
+C -1 ; WX 600 ; N registered ; B 0 -18 600 580 ;
+C -1 ; WX 600 ; N Gbreve ; B 31 -18 575 732 ;
+C -1 ; WX 600 ; N Idotaccent ; B 96 0 504 753 ;
+C -1 ; WX 600 ; N summation ; B 15 -10 585 706 ;
+C -1 ; WX 600 ; N Egrave ; B 53 0 550 805 ;
+C -1 ; WX 600 ; N racute ; B 60 0 559 672 ;
+C -1 ; WX 600 ; N omacron ; B 62 -15 538 565 ;
+C -1 ; WX 600 ; N Zacute ; B 86 0 514 805 ;
+C -1 ; WX 600 ; N Zcaron ; B 86 0 514 802 ;
+C -1 ; WX 600 ; N greaterequal ; B 98 0 502 710 ;
+C -1 ; WX 600 ; N Eth ; B 30 0 574 562 ;
+C -1 ; WX 600 ; N Ccedilla ; B 41 -151 540 580 ;
+C -1 ; WX 600 ; N lcommaaccent ; B 95 -250 505 629 ;
+C -1 ; WX 600 ; N tcaron ; B 87 -15 530 717 ;
+C -1 ; WX 600 ; N eogonek ; B 66 -172 548 441 ;
+C -1 ; WX 600 ; N Uogonek ; B 17 -172 583 562 ;
+C -1 ; WX 600 ; N Aacute ; B 3 0 597 805 ;
+C -1 ; WX 600 ; N Adieresis ; B 3 0 597 753 ;
+C -1 ; WX 600 ; N egrave ; B 66 -15 548 672 ;
+C -1 ; WX 600 ; N zacute ; B 99 0 502 672 ;
+C -1 ; WX 600 ; N iogonek ; B 95 -172 505 657 ;
+C -1 ; WX 600 ; N Oacute ; B 43 -18 557 805 ;
+C -1 ; WX 600 ; N oacute ; B 62 -15 538 672 ;
+C -1 ; WX 600 ; N amacron ; B 53 -15 559 565 ;
+C -1 ; WX 600 ; N sacute ; B 80 -15 513 672 ;
+C -1 ; WX 600 ; N idieresis ; B 95 0 505 620 ;
+C -1 ; WX 600 ; N Ocircumflex ; B 43 -18 557 787 ;
+C -1 ; WX 600 ; N Ugrave ; B 17 -18 583 805 ;
+C -1 ; WX 600 ; N Delta ; B 6 0 598 688 ;
+C -1 ; WX 600 ; N thorn ; B -6 -157 555 629 ;
+C -1 ; WX 600 ; N twosuperior ; B 177 249 424 622 ;
+C -1 ; WX 600 ; N Odieresis ; B 43 -18 557 753 ;
+C -1 ; WX 600 ; N mu ; B 21 -157 562 426 ;
+C -1 ; WX 600 ; N igrave ; B 95 0 505 672 ;
+C -1 ; WX 600 ; N ohungarumlaut ; B 62 -15 580 672 ;
+C -1 ; WX 600 ; N Eogonek ; B 53 -172 561 562 ;
+C -1 ; WX 600 ; N dcroat ; B 45 -15 591 629 ;
+C -1 ; WX 600 ; N threequarters ; B 8 -56 593 666 ;
+C -1 ; WX 600 ; N Scedilla ; B 72 -151 529 580 ;
+C -1 ; WX 600 ; N lcaron ; B 95 0 533 629 ;
+C -1 ; WX 600 ; N Kcommaaccent ; B 38 -250 582 562 ;
+C -1 ; WX 600 ; N Lacute ; B 47 0 554 805 ;
+C -1 ; WX 600 ; N trademark ; B -23 263 623 562 ;
+C -1 ; WX 600 ; N edotaccent ; B 66 -15 548 620 ;
+C -1 ; WX 600 ; N Igrave ; B 96 0 504 805 ;
+C -1 ; WX 600 ; N Imacron ; B 96 0 504 698 ;
+C -1 ; WX 600 ; N Lcaron ; B 47 0 554 562 ;
+C -1 ; WX 600 ; N onehalf ; B 0 -57 611 665 ;
+C -1 ; WX 600 ; N lessequal ; B 98 0 502 710 ;
+C -1 ; WX 600 ; N ocircumflex ; B 62 -15 538 654 ;
+C -1 ; WX 600 ; N ntilde ; B 26 0 575 606 ;
+C -1 ; WX 600 ; N Uhungarumlaut ; B 17 -18 590 805 ;
+C -1 ; WX 600 ; N Eacute ; B 53 0 550 805 ;
+C -1 ; WX 600 ; N emacron ; B 66 -15 548 565 ;
+C -1 ; WX 600 ; N gbreve ; B 45 -157 566 609 ;
+C -1 ; WX 600 ; N onequarter ; B 0 -57 600 665 ;
+C -1 ; WX 600 ; N Scaron ; B 72 -20 529 802 ;
+C -1 ; WX 600 ; N Scommaaccent ; B 72 -250 529 580 ;
+C -1 ; WX 600 ; N Ohungarumlaut ; B 43 -18 580 805 ;
+C -1 ; WX 600 ; N degree ; B 123 269 477 622 ;
+C -1 ; WX 600 ; N ograve ; B 62 -15 538 672 ;
+C -1 ; WX 600 ; N Ccaron ; B 41 -18 540 802 ;
+C -1 ; WX 600 ; N ugrave ; B 21 -15 562 672 ;
+C -1 ; WX 600 ; N radical ; B 3 -15 597 792 ;
+C -1 ; WX 600 ; N Dcaron ; B 43 0 574 802 ;
+C -1 ; WX 600 ; N rcommaaccent ; B 60 -250 559 441 ;
+C -1 ; WX 600 ; N Ntilde ; B 7 -13 593 729 ;
+C -1 ; WX 600 ; N otilde ; B 62 -15 538 606 ;
+C -1 ; WX 600 ; N Rcommaaccent ; B 38 -250 588 562 ;
+C -1 ; WX 600 ; N Lcommaaccent ; B 47 -250 554 562 ;
+C -1 ; WX 600 ; N Atilde ; B 3 0 597 729 ;
+C -1 ; WX 600 ; N Aogonek ; B 3 -172 608 562 ;
+C -1 ; WX 600 ; N Aring ; B 3 0 597 750 ;
+C -1 ; WX 600 ; N Otilde ; B 43 -18 557 729 ;
+C -1 ; WX 600 ; N zdotaccent ; B 99 0 502 620 ;
+C -1 ; WX 600 ; N Ecaron ; B 53 0 550 802 ;
+C -1 ; WX 600 ; N Iogonek ; B 96 -172 504 562 ;
+C -1 ; WX 600 ; N kcommaaccent ; B 43 -250 580 629 ;
+C -1 ; WX 600 ; N minus ; B 80 232 520 283 ;
+C -1 ; WX 600 ; N Icircumflex ; B 96 0 504 787 ;
+C -1 ; WX 600 ; N ncaron ; B 26 0 575 669 ;
+C -1 ; WX 600 ; N tcommaaccent ; B 87 -250 530 561 ;
+C -1 ; WX 600 ; N logicalnot ; B 87 108 513 369 ;
+C -1 ; WX 600 ; N odieresis ; B 62 -15 538 620 ;
+C -1 ; WX 600 ; N udieresis ; B 21 -15 562 620 ;
+C -1 ; WX 600 ; N notequal ; B 15 -16 540 529 ;
+C -1 ; WX 600 ; N gcommaaccent ; B 45 -157 566 708 ;
+C -1 ; WX 600 ; N eth ; B 62 -15 538 629 ;
+C -1 ; WX 600 ; N zcaron ; B 99 0 502 669 ;
+C -1 ; WX 600 ; N ncommaaccent ; B 26 -250 575 441 ;
+C -1 ; WX 600 ; N onesuperior ; B 172 249 428 622 ;
+C -1 ; WX 600 ; N imacron ; B 95 0 505 565 ;
+C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+EndFontMetrics
diff --git a/program/libraries/afm/Helvetica-Bold.afm b/program/libraries/afm/Helvetica-Bold.afm
new file mode 100644 (file)
index 0000000..837c594
--- /dev/null
@@ -0,0 +1,2827 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated.  All Rights Reserved.
+Comment Creation Date: Thu May  1 12:43:52 1997
+Comment UniqueID 43052
+Comment VMusage 37169 48194
+FontName Helvetica-Bold
+FullName Helvetica Bold
+FamilyName Helvetica
+Weight Bold
+ItalicAngle 0
+IsFixedPitch false
+CharacterSet ExtendedRoman
+FontBBox -170 -228 1003 962 
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated.  All Rights Reserved.Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries.
+EncodingScheme AdobeStandardEncoding
+CapHeight 718
+XHeight 532
+Ascender 718
+Descender -207
+StdHW 118
+StdVW 140
+StartCharMetrics 315
+C 32 ; WX 278 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 333 ; N exclam ; B 90 0 244 718 ;
+C 34 ; WX 474 ; N quotedbl ; B 98 447 376 718 ;
+C 35 ; WX 556 ; N numbersign ; B 18 0 538 698 ;
+C 36 ; WX 556 ; N dollar ; B 30 -115 523 775 ;
+C 37 ; WX 889 ; N percent ; B 28 -19 861 710 ;
+C 38 ; WX 722 ; N ampersand ; B 54 -19 701 718 ;
+C 39 ; WX 278 ; N quoteright ; B 69 445 209 718 ;
+C 40 ; WX 333 ; N parenleft ; B 35 -208 314 734 ;
+C 41 ; WX 333 ; N parenright ; B 19 -208 298 734 ;
+C 42 ; WX 389 ; N asterisk ; B 27 387 362 718 ;
+C 43 ; WX 584 ; N plus ; B 40 0 544 506 ;
+C 44 ; WX 278 ; N comma ; B 64 -168 214 146 ;
+C 45 ; WX 333 ; N hyphen ; B 27 215 306 345 ;
+C 46 ; WX 278 ; N period ; B 64 0 214 146 ;
+C 47 ; WX 278 ; N slash ; B -33 -19 311 737 ;
+C 48 ; WX 556 ; N zero ; B 32 -19 524 710 ;
+C 49 ; WX 556 ; N one ; B 69 0 378 710 ;
+C 50 ; WX 556 ; N two ; B 26 0 511 710 ;
+C 51 ; WX 556 ; N three ; B 27 -19 516 710 ;
+C 52 ; WX 556 ; N four ; B 27 0 526 710 ;
+C 53 ; WX 556 ; N five ; B 27 -19 516 698 ;
+C 54 ; WX 556 ; N six ; B 31 -19 520 710 ;
+C 55 ; WX 556 ; N seven ; B 25 0 528 698 ;
+C 56 ; WX 556 ; N eight ; B 32 -19 524 710 ;
+C 57 ; WX 556 ; N nine ; B 30 -19 522 710 ;
+C 58 ; WX 333 ; N colon ; B 92 0 242 512 ;
+C 59 ; WX 333 ; N semicolon ; B 92 -168 242 512 ;
+C 60 ; WX 584 ; N less ; B 38 -8 546 514 ;
+C 61 ; WX 584 ; N equal ; B 40 87 544 419 ;
+C 62 ; WX 584 ; N greater ; B 38 -8 546 514 ;
+C 63 ; WX 611 ; N question ; B 60 0 556 727 ;
+C 64 ; WX 975 ; N at ; B 118 -19 856 737 ;
+C 65 ; WX 722 ; N A ; B 20 0 702 718 ;
+C 66 ; WX 722 ; N B ; B 76 0 669 718 ;
+C 67 ; WX 722 ; N C ; B 44 -19 684 737 ;
+C 68 ; WX 722 ; N D ; B 76 0 685 718 ;
+C 69 ; WX 667 ; N E ; B 76 0 621 718 ;
+C 70 ; WX 611 ; N F ; B 76 0 587 718 ;
+C 71 ; WX 778 ; N G ; B 44 -19 713 737 ;
+C 72 ; WX 722 ; N H ; B 71 0 651 718 ;
+C 73 ; WX 278 ; N I ; B 64 0 214 718 ;
+C 74 ; WX 556 ; N J ; B 22 -18 484 718 ;
+C 75 ; WX 722 ; N K ; B 87 0 722 718 ;
+C 76 ; WX 611 ; N L ; B 76 0 583 718 ;
+C 77 ; WX 833 ; N M ; B 69 0 765 718 ;
+C 78 ; WX 722 ; N N ; B 69 0 654 718 ;
+C 79 ; WX 778 ; N O ; B 44 -19 734 737 ;
+C 80 ; WX 667 ; N P ; B 76 0 627 718 ;
+C 81 ; WX 778 ; N Q ; B 44 -52 737 737 ;
+C 82 ; WX 722 ; N R ; B 76 0 677 718 ;
+C 83 ; WX 667 ; N S ; B 39 -19 629 737 ;
+C 84 ; WX 611 ; N T ; B 14 0 598 718 ;
+C 85 ; WX 722 ; N U ; B 72 -19 651 718 ;
+C 86 ; WX 667 ; N V ; B 19 0 648 718 ;
+C 87 ; WX 944 ; N W ; B 16 0 929 718 ;
+C 88 ; WX 667 ; N X ; B 14 0 653 718 ;
+C 89 ; WX 667 ; N Y ; B 15 0 653 718 ;
+C 90 ; WX 611 ; N Z ; B 25 0 586 718 ;
+C 91 ; WX 333 ; N bracketleft ; B 63 -196 309 722 ;
+C 92 ; WX 278 ; N backslash ; B -33 -19 311 737 ;
+C 93 ; WX 333 ; N bracketright ; B 24 -196 270 722 ;
+C 94 ; WX 584 ; N asciicircum ; B 62 323 522 698 ;
+C 95 ; WX 556 ; N underscore ; B 0 -125 556 -75 ;
+C 96 ; WX 278 ; N quoteleft ; B 69 454 209 727 ;
+C 97 ; WX 556 ; N a ; B 29 -14 527 546 ;
+C 98 ; WX 611 ; N b ; B 61 -14 578 718 ;
+C 99 ; WX 556 ; N c ; B 34 -14 524 546 ;
+C 100 ; WX 611 ; N d ; B 34 -14 551 718 ;
+C 101 ; WX 556 ; N e ; B 23 -14 528 546 ;
+C 102 ; WX 333 ; N f ; B 10 0 318 727 ; L i fi ; L l fl ;
+C 103 ; WX 611 ; N g ; B 40 -217 553 546 ;
+C 104 ; WX 611 ; N h ; B 65 0 546 718 ;
+C 105 ; WX 278 ; N i ; B 69 0 209 725 ;
+C 106 ; WX 278 ; N j ; B 3 -214 209 725 ;
+C 107 ; WX 556 ; N k ; B 69 0 562 718 ;
+C 108 ; WX 278 ; N l ; B 69 0 209 718 ;
+C 109 ; WX 889 ; N m ; B 64 0 826 546 ;
+C 110 ; WX 611 ; N n ; B 65 0 546 546 ;
+C 111 ; WX 611 ; N o ; B 34 -14 578 546 ;
+C 112 ; WX 611 ; N p ; B 62 -207 578 546 ;
+C 113 ; WX 611 ; N q ; B 34 -207 552 546 ;
+C 114 ; WX 389 ; N r ; B 64 0 373 546 ;
+C 115 ; WX 556 ; N s ; B 30 -14 519 546 ;
+C 116 ; WX 333 ; N t ; B 10 -6 309 676 ;
+C 117 ; WX 611 ; N u ; B 66 -14 545 532 ;
+C 118 ; WX 556 ; N v ; B 13 0 543 532 ;
+C 119 ; WX 778 ; N w ; B 10 0 769 532 ;
+C 120 ; WX 556 ; N x ; B 15 0 541 532 ;
+C 121 ; WX 556 ; N y ; B 10 -214 539 532 ;
+C 122 ; WX 500 ; N z ; B 20 0 480 532 ;
+C 123 ; WX 389 ; N braceleft ; B 48 -196 365 722 ;
+C 124 ; WX 280 ; N bar ; B 84 -225 196 775 ;
+C 125 ; WX 389 ; N braceright ; B 24 -196 341 722 ;
+C 126 ; WX 584 ; N asciitilde ; B 61 163 523 343 ;
+C 161 ; WX 333 ; N exclamdown ; B 90 -186 244 532 ;
+C 162 ; WX 556 ; N cent ; B 34 -118 524 628 ;
+C 163 ; WX 556 ; N sterling ; B 28 -16 541 718 ;
+C 164 ; WX 167 ; N fraction ; B -170 -19 336 710 ;
+C 165 ; WX 556 ; N yen ; B -9 0 565 698 ;
+C 166 ; WX 556 ; N florin ; B -10 -210 516 737 ;
+C 167 ; WX 556 ; N section ; B 34 -184 522 727 ;
+C 168 ; WX 556 ; N currency ; B -3 76 559 636 ;
+C 169 ; WX 238 ; N quotesingle ; B 70 447 168 718 ;
+C 170 ; WX 500 ; N quotedblleft ; B 64 454 436 727 ;
+C 171 ; WX 556 ; N guillemotleft ; B 88 76 468 484 ;
+C 172 ; WX 333 ; N guilsinglleft ; B 83 76 250 484 ;
+C 173 ; WX 333 ; N guilsinglright ; B 83 76 250 484 ;
+C 174 ; WX 611 ; N fi ; B 10 0 542 727 ;
+C 175 ; WX 611 ; N fl ; B 10 0 542 727 ;
+C 177 ; WX 556 ; N endash ; B 0 227 556 333 ;
+C 178 ; WX 556 ; N dagger ; B 36 -171 520 718 ;
+C 179 ; WX 556 ; N daggerdbl ; B 36 -171 520 718 ;
+C 180 ; WX 278 ; N periodcentered ; B 58 172 220 334 ;
+C 182 ; WX 556 ; N paragraph ; B -8 -191 539 700 ;
+C 183 ; WX 350 ; N bullet ; B 10 194 340 524 ;
+C 184 ; WX 278 ; N quotesinglbase ; B 69 -146 209 127 ;
+C 185 ; WX 500 ; N quotedblbase ; B 64 -146 436 127 ;
+C 186 ; WX 500 ; N quotedblright ; B 64 445 436 718 ;
+C 187 ; WX 556 ; N guillemotright ; B 88 76 468 484 ;
+C 188 ; WX 1000 ; N ellipsis ; B 92 0 908 146 ;
+C 189 ; WX 1000 ; N perthousand ; B -3 -19 1003 710 ;
+C 191 ; WX 611 ; N questiondown ; B 55 -195 551 532 ;
+C 193 ; WX 333 ; N grave ; B -23 604 225 750 ;
+C 194 ; WX 333 ; N acute ; B 108 604 356 750 ;
+C 195 ; WX 333 ; N circumflex ; B -10 604 343 750 ;
+C 196 ; WX 333 ; N tilde ; B -17 610 350 737 ;
+C 197 ; WX 333 ; N macron ; B -6 604 339 678 ;
+C 198 ; WX 333 ; N breve ; B -2 604 335 750 ;
+C 199 ; WX 333 ; N dotaccent ; B 104 614 230 729 ;
+C 200 ; WX 333 ; N dieresis ; B 6 614 327 729 ;
+C 202 ; WX 333 ; N ring ; B 59 568 275 776 ;
+C 203 ; WX 333 ; N cedilla ; B 6 -228 245 0 ;
+C 205 ; WX 333 ; N hungarumlaut ; B 9 604 486 750 ;
+C 206 ; WX 333 ; N ogonek ; B 71 -228 304 0 ;
+C 207 ; WX 333 ; N caron ; B -10 604 343 750 ;
+C 208 ; WX 1000 ; N emdash ; B 0 227 1000 333 ;
+C 225 ; WX 1000 ; N AE ; B 5 0 954 718 ;
+C 227 ; WX 370 ; N ordfeminine ; B 22 401 347 737 ;
+C 232 ; WX 611 ; N Lslash ; B -20 0 583 718 ;
+C 233 ; WX 778 ; N Oslash ; B 33 -27 744 745 ;
+C 234 ; WX 1000 ; N OE ; B 37 -19 961 737 ;
+C 235 ; WX 365 ; N ordmasculine ; B 6 401 360 737 ;
+C 241 ; WX 889 ; N ae ; B 29 -14 858 546 ;
+C 245 ; WX 278 ; N dotlessi ; B 69 0 209 532 ;
+C 248 ; WX 278 ; N lslash ; B -18 0 296 718 ;
+C 249 ; WX 611 ; N oslash ; B 22 -29 589 560 ;
+C 250 ; WX 944 ; N oe ; B 34 -14 912 546 ;
+C 251 ; WX 611 ; N germandbls ; B 69 -14 579 731 ;
+C -1 ; WX 278 ; N Idieresis ; B -21 0 300 915 ;
+C -1 ; WX 556 ; N eacute ; B 23 -14 528 750 ;
+C -1 ; WX 556 ; N abreve ; B 29 -14 527 750 ;
+C -1 ; WX 611 ; N uhungarumlaut ; B 66 -14 625 750 ;
+C -1 ; WX 556 ; N ecaron ; B 23 -14 528 750 ;
+C -1 ; WX 667 ; N Ydieresis ; B 15 0 653 915 ;
+C -1 ; WX 584 ; N divide ; B 40 -42 544 548 ;
+C -1 ; WX 667 ; N Yacute ; B 15 0 653 936 ;
+C -1 ; WX 722 ; N Acircumflex ; B 20 0 702 936 ;
+C -1 ; WX 556 ; N aacute ; B 29 -14 527 750 ;
+C -1 ; WX 722 ; N Ucircumflex ; B 72 -19 651 936 ;
+C -1 ; WX 556 ; N yacute ; B 10 -214 539 750 ;
+C -1 ; WX 556 ; N scommaaccent ; B 30 -228 519 546 ;
+C -1 ; WX 556 ; N ecircumflex ; B 23 -14 528 750 ;
+C -1 ; WX 722 ; N Uring ; B 72 -19 651 962 ;
+C -1 ; WX 722 ; N Udieresis ; B 72 -19 651 915 ;
+C -1 ; WX 556 ; N aogonek ; B 29 -224 545 546 ;
+C -1 ; WX 722 ; N Uacute ; B 72 -19 651 936 ;
+C -1 ; WX 611 ; N uogonek ; B 66 -228 545 532 ;
+C -1 ; WX 667 ; N Edieresis ; B 76 0 621 915 ;
+C -1 ; WX 722 ; N Dcroat ; B -5 0 685 718 ;
+C -1 ; WX 250 ; N commaaccent ; B 64 -228 199 -50 ;
+C -1 ; WX 737 ; N copyright ; B -11 -19 749 737 ;
+C -1 ; WX 667 ; N Emacron ; B 76 0 621 864 ;
+C -1 ; WX 556 ; N ccaron ; B 34 -14 524 750 ;
+C -1 ; WX 556 ; N aring ; B 29 -14 527 776 ;
+C -1 ; WX 722 ; N Ncommaaccent ; B 69 -228 654 718 ;
+C -1 ; WX 278 ; N lacute ; B 69 0 329 936 ;
+C -1 ; WX 556 ; N agrave ; B 29 -14 527 750 ;
+C -1 ; WX 611 ; N Tcommaaccent ; B 14 -228 598 718 ;
+C -1 ; WX 722 ; N Cacute ; B 44 -19 684 936 ;
+C -1 ; WX 556 ; N atilde ; B 29 -14 527 737 ;
+C -1 ; WX 667 ; N Edotaccent ; B 76 0 621 915 ;
+C -1 ; WX 556 ; N scaron ; B 30 -14 519 750 ;
+C -1 ; WX 556 ; N scedilla ; B 30 -228 519 546 ;
+C -1 ; WX 278 ; N iacute ; B 69 0 329 750 ;
+C -1 ; WX 494 ; N lozenge ; B 10 0 484 745 ;
+C -1 ; WX 722 ; N Rcaron ; B 76 0 677 936 ;
+C -1 ; WX 778 ; N Gcommaaccent ; B 44 -228 713 737 ;
+C -1 ; WX 611 ; N ucircumflex ; B 66 -14 545 750 ;
+C -1 ; WX 556 ; N acircumflex ; B 29 -14 527 750 ;
+C -1 ; WX 722 ; N Amacron ; B 20 0 702 864 ;
+C -1 ; WX 389 ; N rcaron ; B 18 0 373 750 ;
+C -1 ; WX 556 ; N ccedilla ; B 34 -228 524 546 ;
+C -1 ; WX 611 ; N Zdotaccent ; B 25 0 586 915 ;
+C -1 ; WX 667 ; N Thorn ; B 76 0 627 718 ;
+C -1 ; WX 778 ; N Omacron ; B 44 -19 734 864 ;
+C -1 ; WX 722 ; N Racute ; B 76 0 677 936 ;
+C -1 ; WX 667 ; N Sacute ; B 39 -19 629 936 ;
+C -1 ; WX 743 ; N dcaron ; B 34 -14 750 718 ;
+C -1 ; WX 722 ; N Umacron ; B 72 -19 651 864 ;
+C -1 ; WX 611 ; N uring ; B 66 -14 545 776 ;
+C -1 ; WX 333 ; N threesuperior ; B 8 271 326 710 ;
+C -1 ; WX 778 ; N Ograve ; B 44 -19 734 936 ;
+C -1 ; WX 722 ; N Agrave ; B 20 0 702 936 ;
+C -1 ; WX 722 ; N Abreve ; B 20 0 702 936 ;
+C -1 ; WX 584 ; N multiply ; B 40 1 545 505 ;
+C -1 ; WX 611 ; N uacute ; B 66 -14 545 750 ;
+C -1 ; WX 611 ; N Tcaron ; B 14 0 598 936 ;
+C -1 ; WX 494 ; N partialdiff ; B 11 -21 494 750 ;
+C -1 ; WX 556 ; N ydieresis ; B 10 -214 539 729 ;
+C -1 ; WX 722 ; N Nacute ; B 69 0 654 936 ;
+C -1 ; WX 278 ; N icircumflex ; B -37 0 316 750 ;
+C -1 ; WX 667 ; N Ecircumflex ; B 76 0 621 936 ;
+C -1 ; WX 556 ; N adieresis ; B 29 -14 527 729 ;
+C -1 ; WX 556 ; N edieresis ; B 23 -14 528 729 ;
+C -1 ; WX 556 ; N cacute ; B 34 -14 524 750 ;
+C -1 ; WX 611 ; N nacute ; B 65 0 546 750 ;
+C -1 ; WX 611 ; N umacron ; B 66 -14 545 678 ;
+C -1 ; WX 722 ; N Ncaron ; B 69 0 654 936 ;
+C -1 ; WX 278 ; N Iacute ; B 64 0 329 936 ;
+C -1 ; WX 584 ; N plusminus ; B 40 0 544 506 ;
+C -1 ; WX 280 ; N brokenbar ; B 84 -150 196 700 ;
+C -1 ; WX 737 ; N registered ; B -11 -19 748 737 ;
+C -1 ; WX 778 ; N Gbreve ; B 44 -19 713 936 ;
+C -1 ; WX 278 ; N Idotaccent ; B 64 0 214 915 ;
+C -1 ; WX 600 ; N summation ; B 14 -10 585 706 ;
+C -1 ; WX 667 ; N Egrave ; B 76 0 621 936 ;
+C -1 ; WX 389 ; N racute ; B 64 0 384 750 ;
+C -1 ; WX 611 ; N omacron ; B 34 -14 578 678 ;
+C -1 ; WX 611 ; N Zacute ; B 25 0 586 936 ;
+C -1 ; WX 611 ; N Zcaron ; B 25 0 586 936 ;
+C -1 ; WX 549 ; N greaterequal ; B 26 0 523 704 ;
+C -1 ; WX 722 ; N Eth ; B -5 0 685 718 ;
+C -1 ; WX 722 ; N Ccedilla ; B 44 -228 684 737 ;
+C -1 ; WX 278 ; N lcommaaccent ; B 69 -228 213 718 ;
+C -1 ; WX 389 ; N tcaron ; B 10 -6 421 878 ;
+C -1 ; WX 556 ; N eogonek ; B 23 -228 528 546 ;
+C -1 ; WX 722 ; N Uogonek ; B 72 -228 651 718 ;
+C -1 ; WX 722 ; N Aacute ; B 20 0 702 936 ;
+C -1 ; WX 722 ; N Adieresis ; B 20 0 702 915 ;
+C -1 ; WX 556 ; N egrave ; B 23 -14 528 750 ;
+C -1 ; WX 500 ; N zacute ; B 20 0 480 750 ;
+C -1 ; WX 278 ; N iogonek ; B 16 -224 249 725 ;
+C -1 ; WX 778 ; N Oacute ; B 44 -19 734 936 ;
+C -1 ; WX 611 ; N oacute ; B 34 -14 578 750 ;
+C -1 ; WX 556 ; N amacron ; B 29 -14 527 678 ;
+C -1 ; WX 556 ; N sacute ; B 30 -14 519 750 ;
+C -1 ; WX 278 ; N idieresis ; B -21 0 300 729 ;
+C -1 ; WX 778 ; N Ocircumflex ; B 44 -19 734 936 ;
+C -1 ; WX 722 ; N Ugrave ; B 72 -19 651 936 ;
+C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C -1 ; WX 611 ; N thorn ; B 62 -208 578 718 ;
+C -1 ; WX 333 ; N twosuperior ; B 9 283 324 710 ;
+C -1 ; WX 778 ; N Odieresis ; B 44 -19 734 915 ;
+C -1 ; WX 611 ; N mu ; B 66 -207 545 532 ;
+C -1 ; WX 278 ; N igrave ; B -50 0 209 750 ;
+C -1 ; WX 611 ; N ohungarumlaut ; B 34 -14 625 750 ;
+C -1 ; WX 667 ; N Eogonek ; B 76 -224 639 718 ;
+C -1 ; WX 611 ; N dcroat ; B 34 -14 650 718 ;
+C -1 ; WX 834 ; N threequarters ; B 16 -19 799 710 ;
+C -1 ; WX 667 ; N Scedilla ; B 39 -228 629 737 ;
+C -1 ; WX 400 ; N lcaron ; B 69 0 408 718 ;
+C -1 ; WX 722 ; N Kcommaaccent ; B 87 -228 722 718 ;
+C -1 ; WX 611 ; N Lacute ; B 76 0 583 936 ;
+C -1 ; WX 1000 ; N trademark ; B 44 306 956 718 ;
+C -1 ; WX 556 ; N edotaccent ; B 23 -14 528 729 ;
+C -1 ; WX 278 ; N Igrave ; B -50 0 214 936 ;
+C -1 ; WX 278 ; N Imacron ; B -33 0 312 864 ;
+C -1 ; WX 611 ; N Lcaron ; B 76 0 583 718 ;
+C -1 ; WX 834 ; N onehalf ; B 26 -19 794 710 ;
+C -1 ; WX 549 ; N lessequal ; B 29 0 526 704 ;
+C -1 ; WX 611 ; N ocircumflex ; B 34 -14 578 750 ;
+C -1 ; WX 611 ; N ntilde ; B 65 0 546 737 ;
+C -1 ; WX 722 ; N Uhungarumlaut ; B 72 -19 681 936 ;
+C -1 ; WX 667 ; N Eacute ; B 76 0 621 936 ;
+C -1 ; WX 556 ; N emacron ; B 23 -14 528 678 ;
+C -1 ; WX 611 ; N gbreve ; B 40 -217 553 750 ;
+C -1 ; WX 834 ; N onequarter ; B 26 -19 766 710 ;
+C -1 ; WX 667 ; N Scaron ; B 39 -19 629 936 ;
+C -1 ; WX 667 ; N Scommaaccent ; B 39 -228 629 737 ;
+C -1 ; WX 778 ; N Ohungarumlaut ; B 44 -19 734 936 ;
+C -1 ; WX 400 ; N degree ; B 57 426 343 712 ;
+C -1 ; WX 611 ; N ograve ; B 34 -14 578 750 ;
+C -1 ; WX 722 ; N Ccaron ; B 44 -19 684 936 ;
+C -1 ; WX 611 ; N ugrave ; B 66 -14 545 750 ;
+C -1 ; WX 549 ; N radical ; B 10 -46 512 850 ;
+C -1 ; WX 722 ; N Dcaron ; B 76 0 685 936 ;
+C -1 ; WX 389 ; N rcommaaccent ; B 64 -228 373 546 ;
+C -1 ; WX 722 ; N Ntilde ; B 69 0 654 923 ;
+C -1 ; WX 611 ; N otilde ; B 34 -14 578 737 ;
+C -1 ; WX 722 ; N Rcommaaccent ; B 76 -228 677 718 ;
+C -1 ; WX 611 ; N Lcommaaccent ; B 76 -228 583 718 ;
+C -1 ; WX 722 ; N Atilde ; B 20 0 702 923 ;
+C -1 ; WX 722 ; N Aogonek ; B 20 -224 742 718 ;
+C -1 ; WX 722 ; N Aring ; B 20 0 702 962 ;
+C -1 ; WX 778 ; N Otilde ; B 44 -19 734 923 ;
+C -1 ; WX 500 ; N zdotaccent ; B 20 0 480 729 ;
+C -1 ; WX 667 ; N Ecaron ; B 76 0 621 936 ;
+C -1 ; WX 278 ; N Iogonek ; B -11 -228 222 718 ;
+C -1 ; WX 556 ; N kcommaaccent ; B 69 -228 562 718 ;
+C -1 ; WX 584 ; N minus ; B 40 197 544 309 ;
+C -1 ; WX 278 ; N Icircumflex ; B -37 0 316 936 ;
+C -1 ; WX 611 ; N ncaron ; B 65 0 546 750 ;
+C -1 ; WX 333 ; N tcommaaccent ; B 10 -228 309 676 ;
+C -1 ; WX 584 ; N logicalnot ; B 40 108 544 419 ;
+C -1 ; WX 611 ; N odieresis ; B 34 -14 578 729 ;
+C -1 ; WX 611 ; N udieresis ; B 66 -14 545 729 ;
+C -1 ; WX 549 ; N notequal ; B 15 -49 540 570 ;
+C -1 ; WX 611 ; N gcommaaccent ; B 40 -217 553 850 ;
+C -1 ; WX 611 ; N eth ; B 34 -14 578 737 ;
+C -1 ; WX 500 ; N zcaron ; B 20 0 480 750 ;
+C -1 ; WX 611 ; N ncommaaccent ; B 65 -228 546 546 ;
+C -1 ; WX 333 ; N onesuperior ; B 26 283 237 710 ;
+C -1 ; WX 278 ; N imacron ; B -8 0 285 678 ;
+C -1 ; WX 556 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+StartKernData
+StartKernPairs 2481
+KPX A C -40
+KPX A Cacute -40
+KPX A Ccaron -40
+KPX A Ccedilla -40
+KPX A G -50
+KPX A Gbreve -50
+KPX A Gcommaaccent -50
+KPX A O -40
+KPX A Oacute -40
+KPX A Ocircumflex -40
+KPX A Odieresis -40
+KPX A Ograve -40
+KPX A Ohungarumlaut -40
+KPX A Omacron -40
+KPX A Oslash -40
+KPX A Otilde -40
+KPX A Q -40
+KPX A T -90
+KPX A Tcaron -90
+KPX A Tcommaaccent -90
+KPX A U -50
+KPX A Uacute -50
+KPX A Ucircumflex -50
+KPX A Udieresis -50
+KPX A Ugrave -50
+KPX A Uhungarumlaut -50
+KPX A Umacron -50
+KPX A Uogonek -50
+KPX A Uring -50
+KPX A V -80
+KPX A W -60
+KPX A Y -110
+KPX A Yacute -110
+KPX A Ydieresis -110
+KPX A u -30
+KPX A uacute -30
+KPX A ucircumflex -30
+KPX A udieresis -30
+KPX A ugrave -30
+KPX A uhungarumlaut -30
+KPX A umacron -30
+KPX A uogonek -30
+KPX A uring -30
+KPX A v -40
+KPX A w -30
+KPX A y -30
+KPX A yacute -30
+KPX A ydieresis -30
+KPX Aacute C -40
+KPX Aacute Cacute -40
+KPX Aacute Ccaron -40
+KPX Aacute Ccedilla -40
+KPX Aacute G -50
+KPX Aacute Gbreve -50
+KPX Aacute Gcommaaccent -50
+KPX Aacute O -40
+KPX Aacute Oacute -40
+KPX Aacute Ocircumflex -40
+KPX Aacute Odieresis -40
+KPX Aacute Ograve -40
+KPX Aacute Ohungarumlaut -40
+KPX Aacute Omacron -40
+KPX Aacute Oslash -40
+KPX Aacute Otilde -40
+KPX Aacute Q -40
+KPX Aacute T -90
+KPX Aacute Tcaron -90
+KPX Aacute Tcommaaccent -90
+KPX Aacute U -50
+KPX Aacute Uacute -50
+KPX Aacute Ucircumflex -50
+KPX Aacute Udieresis -50
+KPX Aacute Ugrave -50
+KPX Aacute Uhungarumlaut -50
+KPX Aacute Umacron -50
+KPX Aacute Uogonek -50
+KPX Aacute Uring -50
+KPX Aacute V -80
+KPX Aacute W -60
+KPX Aacute Y -110
+KPX Aacute Yacute -110
+KPX Aacute Ydieresis -110
+KPX Aacute u -30
+KPX Aacute uacute -30
+KPX Aacute ucircumflex -30
+KPX Aacute udieresis -30
+KPX Aacute ugrave -30
+KPX Aacute uhungarumlaut -30
+KPX Aacute umacron -30
+KPX Aacute uogonek -30
+KPX Aacute uring -30
+KPX Aacute v -40
+KPX Aacute w -30
+KPX Aacute y -30
+KPX Aacute yacute -30
+KPX Aacute ydieresis -30
+KPX Abreve C -40
+KPX Abreve Cacute -40
+KPX Abreve Ccaron -40
+KPX Abreve Ccedilla -40
+KPX Abreve G -50
+KPX Abreve Gbreve -50
+KPX Abreve Gcommaaccent -50
+KPX Abreve O -40
+KPX Abreve Oacute -40
+KPX Abreve Ocircumflex -40
+KPX Abreve Odieresis -40
+KPX Abreve Ograve -40
+KPX Abreve Ohungarumlaut -40
+KPX Abreve Omacron -40
+KPX Abreve Oslash -40
+KPX Abreve Otilde -40
+KPX Abreve Q -40
+KPX Abreve T -90
+KPX Abreve Tcaron -90
+KPX Abreve Tcommaaccent -90
+KPX Abreve U -50
+KPX Abreve Uacute -50
+KPX Abreve Ucircumflex -50
+KPX Abreve Udieresis -50
+KPX Abreve Ugrave -50
+KPX Abreve Uhungarumlaut -50
+KPX Abreve Umacron -50
+KPX Abreve Uogonek -50
+KPX Abreve Uring -50
+KPX Abreve V -80
+KPX Abreve W -60
+KPX Abreve Y -110
+KPX Abreve Yacute -110
+KPX Abreve Ydieresis -110
+KPX Abreve u -30
+KPX Abreve uacute -30
+KPX Abreve ucircumflex -30
+KPX Abreve udieresis -30
+KPX Abreve ugrave -30
+KPX Abreve uhungarumlaut -30
+KPX Abreve umacron -30
+KPX Abreve uogonek -30
+KPX Abreve uring -30
+KPX Abreve v -40
+KPX Abreve w -30
+KPX Abreve y -30
+KPX Abreve yacute -30
+KPX Abreve ydieresis -30
+KPX Acircumflex C -40
+KPX Acircumflex Cacute -40
+KPX Acircumflex Ccaron -40
+KPX Acircumflex Ccedilla -40
+KPX Acircumflex G -50
+KPX Acircumflex Gbreve -50
+KPX Acircumflex Gcommaaccent -50
+KPX Acircumflex O -40
+KPX Acircumflex Oacute -40
+KPX Acircumflex Ocircumflex -40
+KPX Acircumflex Odieresis -40
+KPX Acircumflex Ograve -40
+KPX Acircumflex Ohungarumlaut -40
+KPX Acircumflex Omacron -40
+KPX Acircumflex Oslash -40
+KPX Acircumflex Otilde -40
+KPX Acircumflex Q -40
+KPX Acircumflex T -90
+KPX Acircumflex Tcaron -90
+KPX Acircumflex Tcommaaccent -90
+KPX Acircumflex U -50
+KPX Acircumflex Uacute -50
+KPX Acircumflex Ucircumflex -50
+KPX Acircumflex Udieresis -50
+KPX Acircumflex Ugrave -50
+KPX Acircumflex Uhungarumlaut -50
+KPX Acircumflex Umacron -50
+KPX Acircumflex Uogonek -50
+KPX Acircumflex Uring -50
+KPX Acircumflex V -80
+KPX Acircumflex W -60
+KPX Acircumflex Y -110
+KPX Acircumflex Yacute -110
+KPX Acircumflex Ydieresis -110
+KPX Acircumflex u -30
+KPX Acircumflex uacute -30
+KPX Acircumflex ucircumflex -30
+KPX Acircumflex udieresis -30
+KPX Acircumflex ugrave -30
+KPX Acircumflex uhungarumlaut -30
+KPX Acircumflex umacron -30
+KPX Acircumflex uogonek -30
+KPX Acircumflex uring -30
+KPX Acircumflex v -40
+KPX Acircumflex w -30
+KPX Acircumflex y -30
+KPX Acircumflex yacute -30
+KPX Acircumflex ydieresis -30
+KPX Adieresis C -40
+KPX Adieresis Cacute -40
+KPX Adieresis Ccaron -40
+KPX Adieresis Ccedilla -40
+KPX Adieresis G -50
+KPX Adieresis Gbreve -50
+KPX Adieresis Gcommaaccent -50
+KPX Adieresis O -40
+KPX Adieresis Oacute -40
+KPX Adieresis Ocircumflex -40
+KPX Adieresis Odieresis -40
+KPX Adieresis Ograve -40
+KPX Adieresis Ohungarumlaut -40
+KPX Adieresis Omacron -40
+KPX Adieresis Oslash -40
+KPX Adieresis Otilde -40
+KPX Adieresis Q -40
+KPX Adieresis T -90
+KPX Adieresis Tcaron -90
+KPX Adieresis Tcommaaccent -90
+KPX Adieresis U -50
+KPX Adieresis Uacute -50
+KPX Adieresis Ucircumflex -50
+KPX Adieresis Udieresis -50
+KPX Adieresis Ugrave -50
+KPX Adieresis Uhungarumlaut -50
+KPX Adieresis Umacron -50
+KPX Adieresis Uogonek -50
+KPX Adieresis Uring -50
+KPX Adieresis V -80
+KPX Adieresis W -60
+KPX Adieresis Y -110
+KPX Adieresis Yacute -110
+KPX Adieresis Ydieresis -110
+KPX Adieresis u -30
+KPX Adieresis uacute -30
+KPX Adieresis ucircumflex -30
+KPX Adieresis udieresis -30
+KPX Adieresis ugrave -30
+KPX Adieresis uhungarumlaut -30
+KPX Adieresis umacron -30
+KPX Adieresis uogonek -30
+KPX Adieresis uring -30
+KPX Adieresis v -40
+KPX Adieresis w -30
+KPX Adieresis y -30
+KPX Adieresis yacute -30
+KPX Adieresis ydieresis -30
+KPX Agrave C -40
+KPX Agrave Cacute -40
+KPX Agrave Ccaron -40
+KPX Agrave Ccedilla -40
+KPX Agrave G -50
+KPX Agrave Gbreve -50
+KPX Agrave Gcommaaccent -50
+KPX Agrave O -40
+KPX Agrave Oacute -40
+KPX Agrave Ocircumflex -40
+KPX Agrave Odieresis -40
+KPX Agrave Ograve -40
+KPX Agrave Ohungarumlaut -40
+KPX Agrave Omacron -40
+KPX Agrave Oslash -40
+KPX Agrave Otilde -40
+KPX Agrave Q -40
+KPX Agrave T -90
+KPX Agrave Tcaron -90
+KPX Agrave Tcommaaccent -90
+KPX Agrave U -50
+KPX Agrave Uacute -50
+KPX Agrave Ucircumflex -50
+KPX Agrave Udieresis -50
+KPX Agrave Ugrave -50
+KPX Agrave Uhungarumlaut -50
+KPX Agrave Umacron -50
+KPX Agrave Uogonek -50
+KPX Agrave Uring -50
+KPX Agrave V -80
+KPX Agrave W -60
+KPX Agrave Y -110
+KPX Agrave Yacute -110
+KPX Agrave Ydieresis -110
+KPX Agrave u -30
+KPX Agrave uacute -30
+KPX Agrave ucircumflex -30
+KPX Agrave udieresis -30
+KPX Agrave ugrave -30
+KPX Agrave uhungarumlaut -30
+KPX Agrave umacron -30
+KPX Agrave uogonek -30
+KPX Agrave uring -30
+KPX Agrave v -40
+KPX Agrave w -30
+KPX Agrave y -30
+KPX Agrave yacute -30
+KPX Agrave ydieresis -30
+KPX Amacron C -40
+KPX Amacron Cacute -40
+KPX Amacron Ccaron -40
+KPX Amacron Ccedilla -40
+KPX Amacron G -50
+KPX Amacron Gbreve -50
+KPX Amacron Gcommaaccent -50
+KPX Amacron O -40
+KPX Amacron Oacute -40
+KPX Amacron Ocircumflex -40
+KPX Amacron Odieresis -40
+KPX Amacron Ograve -40
+KPX Amacron Ohungarumlaut -40
+KPX Amacron Omacron -40
+KPX Amacron Oslash -40
+KPX Amacron Otilde -40
+KPX Amacron Q -40
+KPX Amacron T -90
+KPX Amacron Tcaron -90
+KPX Amacron Tcommaaccent -90
+KPX Amacron U -50
+KPX Amacron Uacute -50
+KPX Amacron Ucircumflex -50
+KPX Amacron Udieresis -50
+KPX Amacron Ugrave -50
+KPX Amacron Uhungarumlaut -50
+KPX Amacron Umacron -50
+KPX Amacron Uogonek -50
+KPX Amacron Uring -50
+KPX Amacron V -80
+KPX Amacron W -60
+KPX Amacron Y -110
+KPX Amacron Yacute -110
+KPX Amacron Ydieresis -110
+KPX Amacron u -30
+KPX Amacron uacute -30
+KPX Amacron ucircumflex -30
+KPX Amacron udieresis -30
+KPX Amacron ugrave -30
+KPX Amacron uhungarumlaut -30
+KPX Amacron umacron -30
+KPX Amacron uogonek -30
+KPX Amacron uring -30
+KPX Amacron v -40
+KPX Amacron w -30
+KPX Amacron y -30
+KPX Amacron yacute -30
+KPX Amacron ydieresis -30
+KPX Aogonek C -40
+KPX Aogonek Cacute -40
+KPX Aogonek Ccaron -40
+KPX Aogonek Ccedilla -40
+KPX Aogonek G -50
+KPX Aogonek Gbreve -50
+KPX Aogonek Gcommaaccent -50
+KPX Aogonek O -40
+KPX Aogonek Oacute -40
+KPX Aogonek Ocircumflex -40
+KPX Aogonek Odieresis -40
+KPX Aogonek Ograve -40
+KPX Aogonek Ohungarumlaut -40
+KPX Aogonek Omacron -40
+KPX Aogonek Oslash -40
+KPX Aogonek Otilde -40
+KPX Aogonek Q -40
+KPX Aogonek T -90
+KPX Aogonek Tcaron -90
+KPX Aogonek Tcommaaccent -90
+KPX Aogonek U -50
+KPX Aogonek Uacute -50
+KPX Aogonek Ucircumflex -50
+KPX Aogonek Udieresis -50
+KPX Aogonek Ugrave -50
+KPX Aogonek Uhungarumlaut -50
+KPX Aogonek Umacron -50
+KPX Aogonek Uogonek -50
+KPX Aogonek Uring -50
+KPX Aogonek V -80
+KPX Aogonek W -60
+KPX Aogonek Y -110
+KPX Aogonek Yacute -110
+KPX Aogonek Ydieresis -110
+KPX Aogonek u -30
+KPX Aogonek uacute -30
+KPX Aogonek ucircumflex -30
+KPX Aogonek udieresis -30
+KPX Aogonek ugrave -30
+KPX Aogonek uhungarumlaut -30
+KPX Aogonek umacron -30
+KPX Aogonek uogonek -30
+KPX Aogonek uring -30
+KPX Aogonek v -40
+KPX Aogonek w -30
+KPX Aogonek y -30
+KPX Aogonek yacute -30
+KPX Aogonek ydieresis -30
+KPX Aring C -40
+KPX Aring Cacute -40
+KPX Aring Ccaron -40
+KPX Aring Ccedilla -40
+KPX Aring G -50
+KPX Aring Gbreve -50
+KPX Aring Gcommaaccent -50
+KPX Aring O -40
+KPX Aring Oacute -40
+KPX Aring Ocircumflex -40
+KPX Aring Odieresis -40
+KPX Aring Ograve -40
+KPX Aring Ohungarumlaut -40
+KPX Aring Omacron -40
+KPX Aring Oslash -40
+KPX Aring Otilde -40
+KPX Aring Q -40
+KPX Aring T -90
+KPX Aring Tcaron -90
+KPX Aring Tcommaaccent -90
+KPX Aring U -50
+KPX Aring Uacute -50
+KPX Aring Ucircumflex -50
+KPX Aring Udieresis -50
+KPX Aring Ugrave -50
+KPX Aring Uhungarumlaut -50
+KPX Aring Umacron -50
+KPX Aring Uogonek -50
+KPX Aring Uring -50
+KPX Aring V -80
+KPX Aring W -60
+KPX Aring Y -110
+KPX Aring Yacute -110
+KPX Aring Ydieresis -110
+KPX Aring u -30
+KPX Aring uacute -30
+KPX Aring ucircumflex -30
+KPX Aring udieresis -30
+KPX Aring ugrave -30
+KPX Aring uhungarumlaut -30
+KPX Aring umacron -30
+KPX Aring uogonek -30
+KPX Aring uring -30
+KPX Aring v -40
+KPX Aring w -30
+KPX Aring y -30
+KPX Aring yacute -30
+KPX Aring ydieresis -30
+KPX Atilde C -40
+KPX Atilde Cacute -40
+KPX Atilde Ccaron -40
+KPX Atilde Ccedilla -40
+KPX Atilde G -50
+KPX Atilde Gbreve -50
+KPX Atilde Gcommaaccent -50
+KPX Atilde O -40
+KPX Atilde Oacute -40
+KPX Atilde Ocircumflex -40
+KPX Atilde Odieresis -40
+KPX Atilde Ograve -40
+KPX Atilde Ohungarumlaut -40
+KPX Atilde Omacron -40
+KPX Atilde Oslash -40
+KPX Atilde Otilde -40
+KPX Atilde Q -40
+KPX Atilde T -90
+KPX Atilde Tcaron -90
+KPX Atilde Tcommaaccent -90
+KPX Atilde U -50
+KPX Atilde Uacute -50
+KPX Atilde Ucircumflex -50
+KPX Atilde Udieresis -50
+KPX Atilde Ugrave -50
+KPX Atilde Uhungarumlaut -50
+KPX Atilde Umacron -50
+KPX Atilde Uogonek -50
+KPX Atilde Uring -50
+KPX Atilde V -80
+KPX Atilde W -60
+KPX Atilde Y -110
+KPX Atilde Yacute -110
+KPX Atilde Ydieresis -110
+KPX Atilde u -30
+KPX Atilde uacute -30
+KPX Atilde ucircumflex -30
+KPX Atilde udieresis -30
+KPX Atilde ugrave -30
+KPX Atilde uhungarumlaut -30
+KPX Atilde umacron -30
+KPX Atilde uogonek -30
+KPX Atilde uring -30
+KPX Atilde v -40
+KPX Atilde w -30
+KPX Atilde y -30
+KPX Atilde yacute -30
+KPX Atilde ydieresis -30
+KPX B A -30
+KPX B Aacute -30
+KPX B Abreve -30
+KPX B Acircumflex -30
+KPX B Adieresis -30
+KPX B Agrave -30
+KPX B Amacron -30
+KPX B Aogonek -30
+KPX B Aring -30
+KPX B Atilde -30
+KPX B U -10
+KPX B Uacute -10
+KPX B Ucircumflex -10
+KPX B Udieresis -10
+KPX B Ugrave -10
+KPX B Uhungarumlaut -10
+KPX B Umacron -10
+KPX B Uogonek -10
+KPX B Uring -10
+KPX D A -40
+KPX D Aacute -40
+KPX D Abreve -40
+KPX D Acircumflex -40
+KPX D Adieresis -40
+KPX D Agrave -40
+KPX D Amacron -40
+KPX D Aogonek -40
+KPX D Aring -40
+KPX D Atilde -40
+KPX D V -40
+KPX D W -40
+KPX D Y -70
+KPX D Yacute -70
+KPX D Ydieresis -70
+KPX D comma -30
+KPX D period -30
+KPX Dcaron A -40
+KPX Dcaron Aacute -40
+KPX Dcaron Abreve -40
+KPX Dcaron Acircumflex -40
+KPX Dcaron Adieresis -40
+KPX Dcaron Agrave -40
+KPX Dcaron Amacron -40
+KPX Dcaron Aogonek -40
+KPX Dcaron Aring -40
+KPX Dcaron Atilde -40
+KPX Dcaron V -40
+KPX Dcaron W -40
+KPX Dcaron Y -70
+KPX Dcaron Yacute -70
+KPX Dcaron Ydieresis -70
+KPX Dcaron comma -30
+KPX Dcaron period -30
+KPX Dcroat A -40
+KPX Dcroat Aacute -40
+KPX Dcroat Abreve -40
+KPX Dcroat Acircumflex -40
+KPX Dcroat Adieresis -40
+KPX Dcroat Agrave -40
+KPX Dcroat Amacron -40
+KPX Dcroat Aogonek -40
+KPX Dcroat Aring -40
+KPX Dcroat Atilde -40
+KPX Dcroat V -40
+KPX Dcroat W -40
+KPX Dcroat Y -70
+KPX Dcroat Yacute -70
+KPX Dcroat Ydieresis -70
+KPX Dcroat comma -30
+KPX Dcroat period -30
+KPX F A -80
+KPX F Aacute -80
+KPX F Abreve -80
+KPX F Acircumflex -80
+KPX F Adieresis -80
+KPX F Agrave -80
+KPX F Amacron -80
+KPX F Aogonek -80
+KPX F Aring -80
+KPX F Atilde -80
+KPX F a -20
+KPX F aacute -20
+KPX F abreve -20
+KPX F acircumflex -20
+KPX F adieresis -20
+KPX F agrave -20
+KPX F amacron -20
+KPX F aogonek -20
+KPX F aring -20
+KPX F atilde -20
+KPX F comma -100
+KPX F period -100
+KPX J A -20
+KPX J Aacute -20
+KPX J Abreve -20
+KPX J Acircumflex -20
+KPX J Adieresis -20
+KPX J Agrave -20
+KPX J Amacron -20
+KPX J Aogonek -20
+KPX J Aring -20
+KPX J Atilde -20
+KPX J comma -20
+KPX J period -20
+KPX J u -20
+KPX J uacute -20
+KPX J ucircumflex -20
+KPX J udieresis -20
+KPX J ugrave -20
+KPX J uhungarumlaut -20
+KPX J umacron -20
+KPX J uogonek -20
+KPX J uring -20
+KPX K O -30
+KPX K Oacute -30
+KPX K Ocircumflex -30
+KPX K Odieresis -30
+KPX K Ograve -30
+KPX K Ohungarumlaut -30
+KPX K Omacron -30
+KPX K Oslash -30
+KPX K Otilde -30
+KPX K e -15
+KPX K eacute -15
+KPX K ecaron -15
+KPX K ecircumflex -15
+KPX K edieresis -15
+KPX K edotaccent -15
+KPX K egrave -15
+KPX K emacron -15
+KPX K eogonek -15
+KPX K o -35
+KPX K oacute -35
+KPX K ocircumflex -35
+KPX K odieresis -35
+KPX K ograve -35
+KPX K ohungarumlaut -35
+KPX K omacron -35
+KPX K oslash -35
+KPX K otilde -35
+KPX K u -30
+KPX K uacute -30
+KPX K ucircumflex -30
+KPX K udieresis -30
+KPX K ugrave -30
+KPX K uhungarumlaut -30
+KPX K umacron -30
+KPX K uogonek -30
+KPX K uring -30
+KPX K y -40
+KPX K yacute -40
+KPX K ydieresis -40
+KPX Kcommaaccent O -30
+KPX Kcommaaccent Oacute -30
+KPX Kcommaaccent Ocircumflex -30
+KPX Kcommaaccent Odieresis -30
+KPX Kcommaaccent Ograve -30
+KPX Kcommaaccent Ohungarumlaut -30
+KPX Kcommaaccent Omacron -30
+KPX Kcommaaccent Oslash -30
+KPX Kcommaaccent Otilde -30
+KPX Kcommaaccent e -15
+KPX Kcommaaccent eacute -15
+KPX Kcommaaccent ecaron -15
+KPX Kcommaaccent ecircumflex -15
+KPX Kcommaaccent edieresis -15
+KPX Kcommaaccent edotaccent -15
+KPX Kcommaaccent egrave -15
+KPX Kcommaaccent emacron -15
+KPX Kcommaaccent eogonek -15
+KPX Kcommaaccent o -35
+KPX Kcommaaccent oacute -35
+KPX Kcommaaccent ocircumflex -35
+KPX Kcommaaccent odieresis -35
+KPX Kcommaaccent ograve -35
+KPX Kcommaaccent ohungarumlaut -35
+KPX Kcommaaccent omacron -35
+KPX Kcommaaccent oslash -35
+KPX Kcommaaccent otilde -35
+KPX Kcommaaccent u -30
+KPX Kcommaaccent uacute -30
+KPX Kcommaaccent ucircumflex -30
+KPX Kcommaaccent udieresis -30
+KPX Kcommaaccent ugrave -30
+KPX Kcommaaccent uhungarumlaut -30
+KPX Kcommaaccent umacron -30
+KPX Kcommaaccent uogonek -30
+KPX Kcommaaccent uring -30
+KPX Kcommaaccent y -40
+KPX Kcommaaccent yacute -40
+KPX Kcommaaccent ydieresis -40
+KPX L T -90
+KPX L Tcaron -90
+KPX L Tcommaaccent -90
+KPX L V -110
+KPX L W -80
+KPX L Y -120
+KPX L Yacute -120
+KPX L Ydieresis -120
+KPX L quotedblright -140
+KPX L quoteright -140
+KPX L y -30
+KPX L yacute -30
+KPX L ydieresis -30
+KPX Lacute T -90
+KPX Lacute Tcaron -90
+KPX Lacute Tcommaaccent -90
+KPX Lacute V -110
+KPX Lacute W -80
+KPX Lacute Y -120
+KPX Lacute Yacute -120
+KPX Lacute Ydieresis -120
+KPX Lacute quotedblright -140
+KPX Lacute quoteright -140
+KPX Lacute y -30
+KPX Lacute yacute -30
+KPX Lacute ydieresis -30
+KPX Lcommaaccent T -90
+KPX Lcommaaccent Tcaron -90
+KPX Lcommaaccent Tcommaaccent -90
+KPX Lcommaaccent V -110
+KPX Lcommaaccent W -80
+KPX Lcommaaccent Y -120
+KPX Lcommaaccent Yacute -120
+KPX Lcommaaccent Ydieresis -120
+KPX Lcommaaccent quotedblright -140
+KPX Lcommaaccent quoteright -140
+KPX Lcommaaccent y -30
+KPX Lcommaaccent yacute -30
+KPX Lcommaaccent ydieresis -30
+KPX Lslash T -90
+KPX Lslash Tcaron -90
+KPX Lslash Tcommaaccent -90
+KPX Lslash V -110
+KPX Lslash W -80
+KPX Lslash Y -120
+KPX Lslash Yacute -120
+KPX Lslash Ydieresis -120
+KPX Lslash quotedblright -140
+KPX Lslash quoteright -140
+KPX Lslash y -30
+KPX Lslash yacute -30
+KPX Lslash ydieresis -30
+KPX O A -50
+KPX O Aacute -50
+KPX O Abreve -50
+KPX O Acircumflex -50
+KPX O Adieresis -50
+KPX O Agrave -50
+KPX O Amacron -50
+KPX O Aogonek -50
+KPX O Aring -50
+KPX O Atilde -50
+KPX O T -40
+KPX O Tcaron -40
+KPX O Tcommaaccent -40
+KPX O V -50
+KPX O W -50
+KPX O X -50
+KPX O Y -70
+KPX O Yacute -70
+KPX O Ydieresis -70
+KPX O comma -40
+KPX O period -40
+KPX Oacute A -50
+KPX Oacute Aacute -50
+KPX Oacute Abreve -50
+KPX Oacute Acircumflex -50
+KPX Oacute Adieresis -50
+KPX Oacute Agrave -50
+KPX Oacute Amacron -50
+KPX Oacute Aogonek -50
+KPX Oacute Aring -50
+KPX Oacute Atilde -50
+KPX Oacute T -40
+KPX Oacute Tcaron -40
+KPX Oacute Tcommaaccent -40
+KPX Oacute V -50
+KPX Oacute W -50
+KPX Oacute X -50
+KPX Oacute Y -70
+KPX Oacute Yacute -70
+KPX Oacute Ydieresis -70
+KPX Oacute comma -40
+KPX Oacute period -40
+KPX Ocircumflex A -50
+KPX Ocircumflex Aacute -50
+KPX Ocircumflex Abreve -50
+KPX Ocircumflex Acircumflex -50
+KPX Ocircumflex Adieresis -50
+KPX Ocircumflex Agrave -50
+KPX Ocircumflex Amacron -50
+KPX Ocircumflex Aogonek -50
+KPX Ocircumflex Aring -50
+KPX Ocircumflex Atilde -50
+KPX Ocircumflex T -40
+KPX Ocircumflex Tcaron -40
+KPX Ocircumflex Tcommaaccent -40
+KPX Ocircumflex V -50
+KPX Ocircumflex W -50
+KPX Ocircumflex X -50
+KPX Ocircumflex Y -70
+KPX Ocircumflex Yacute -70
+KPX Ocircumflex Ydieresis -70
+KPX Ocircumflex comma -40
+KPX Ocircumflex period -40
+KPX Odieresis A -50
+KPX Odieresis Aacute -50
+KPX Odieresis Abreve -50
+KPX Odieresis Acircumflex -50
+KPX Odieresis Adieresis -50
+KPX Odieresis Agrave -50
+KPX Odieresis Amacron -50
+KPX Odieresis Aogonek -50
+KPX Odieresis Aring -50
+KPX Odieresis Atilde -50
+KPX Odieresis T -40
+KPX Odieresis Tcaron -40
+KPX Odieresis Tcommaaccent -40
+KPX Odieresis V -50
+KPX Odieresis W -50
+KPX Odieresis X -50
+KPX Odieresis Y -70
+KPX Odieresis Yacute -70
+KPX Odieresis Ydieresis -70
+KPX Odieresis comma -40
+KPX Odieresis period -40
+KPX Ograve A -50
+KPX Ograve Aacute -50
+KPX Ograve Abreve -50
+KPX Ograve Acircumflex -50
+KPX Ograve Adieresis -50
+KPX Ograve Agrave -50
+KPX Ograve Amacron -50
+KPX Ograve Aogonek -50
+KPX Ograve Aring -50
+KPX Ograve Atilde -50
+KPX Ograve T -40
+KPX Ograve Tcaron -40
+KPX Ograve Tcommaaccent -40
+KPX Ograve V -50
+KPX Ograve W -50
+KPX Ograve X -50
+KPX Ograve Y -70
+KPX Ograve Yacute -70
+KPX Ograve Ydieresis -70
+KPX Ograve comma -40
+KPX Ograve period -40
+KPX Ohungarumlaut A -50
+KPX Ohungarumlaut Aacute -50
+KPX Ohungarumlaut Abreve -50
+KPX Ohungarumlaut Acircumflex -50
+KPX Ohungarumlaut Adieresis -50
+KPX Ohungarumlaut Agrave -50
+KPX Ohungarumlaut Amacron -50
+KPX Ohungarumlaut Aogonek -50
+KPX Ohungarumlaut Aring -50
+KPX Ohungarumlaut Atilde -50
+KPX Ohungarumlaut T -40
+KPX Ohungarumlaut Tcaron -40
+KPX Ohungarumlaut Tcommaaccent -40
+KPX Ohungarumlaut V -50
+KPX Ohungarumlaut W -50
+KPX Ohungarumlaut X -50
+KPX Ohungarumlaut Y -70
+KPX Ohungarumlaut Yacute -70
+KPX Ohungarumlaut Ydieresis -70
+KPX Ohungarumlaut comma -40
+KPX Ohungarumlaut period -40
+KPX Omacron A -50
+KPX Omacron Aacute -50
+KPX Omacron Abreve -50
+KPX Omacron Acircumflex -50
+KPX Omacron Adieresis -50
+KPX Omacron Agrave -50
+KPX Omacron Amacron -50
+KPX Omacron Aogonek -50
+KPX Omacron Aring -50
+KPX Omacron Atilde -50
+KPX Omacron T -40
+KPX Omacron Tcaron -40
+KPX Omacron Tcommaaccent -40
+KPX Omacron V -50
+KPX Omacron W -50
+KPX Omacron X -50
+KPX Omacron Y -70
+KPX Omacron Yacute -70
+KPX Omacron Ydieresis -70
+KPX Omacron comma -40
+KPX Omacron period -40
+KPX Oslash A -50
+KPX Oslash Aacute -50
+KPX Oslash Abreve -50
+KPX Oslash Acircumflex -50
+KPX Oslash Adieresis -50
+KPX Oslash Agrave -50
+KPX Oslash Amacron -50
+KPX Oslash Aogonek -50
+KPX Oslash Aring -50
+KPX Oslash Atilde -50
+KPX Oslash T -40
+KPX Oslash Tcaron -40
+KPX Oslash Tcommaaccent -40
+KPX Oslash V -50
+KPX Oslash W -50
+KPX Oslash X -50
+KPX Oslash Y -70
+KPX Oslash Yacute -70
+KPX Oslash Ydieresis -70
+KPX Oslash comma -40
+KPX Oslash period -40
+KPX Otilde A -50
+KPX Otilde Aacute -50
+KPX Otilde Abreve -50
+KPX Otilde Acircumflex -50
+KPX Otilde Adieresis -50
+KPX Otilde Agrave -50
+KPX Otilde Amacron -50
+KPX Otilde Aogonek -50
+KPX Otilde Aring -50
+KPX Otilde Atilde -50
+KPX Otilde T -40
+KPX Otilde Tcaron -40
+KPX Otilde Tcommaaccent -40
+KPX Otilde V -50
+KPX Otilde W -50
+KPX Otilde X -50
+KPX Otilde Y -70
+KPX Otilde Yacute -70
+KPX Otilde Ydieresis -70
+KPX Otilde comma -40
+KPX Otilde period -40
+KPX P A -100
+KPX P Aacute -100
+KPX P Abreve -100
+KPX P Acircumflex -100
+KPX P Adieresis -100
+KPX P Agrave -100
+KPX P Amacron -100
+KPX P Aogonek -100
+KPX P Aring -100
+KPX P Atilde -100
+KPX P a -30
+KPX P aacute -30
+KPX P abreve -30
+KPX P acircumflex -30
+KPX P adieresis -30
+KPX P agrave -30
+KPX P amacron -30
+KPX P aogonek -30
+KPX P aring -30
+KPX P atilde -30
+KPX P comma -120
+KPX P e -30
+KPX P eacute -30
+KPX P ecaron -30
+KPX P ecircumflex -30
+KPX P edieresis -30
+KPX P edotaccent -30
+KPX P egrave -30
+KPX P emacron -30
+KPX P eogonek -30
+KPX P o -40
+KPX P oacute -40
+KPX P ocircumflex -40
+KPX P odieresis -40
+KPX P ograve -40
+KPX P ohungarumlaut -40
+KPX P omacron -40
+KPX P oslash -40
+KPX P otilde -40
+KPX P period -120
+KPX Q U -10
+KPX Q Uacute -10
+KPX Q Ucircumflex -10
+KPX Q Udieresis -10
+KPX Q Ugrave -10
+KPX Q Uhungarumlaut -10
+KPX Q Umacron -10
+KPX Q Uogonek -10
+KPX Q Uring -10
+KPX Q comma 20
+KPX Q period 20
+KPX R O -20
+KPX R Oacute -20
+KPX R Ocircumflex -20
+KPX R Odieresis -20
+KPX R Ograve -20
+KPX R Ohungarumlaut -20
+KPX R Omacron -20
+KPX R Oslash -20
+KPX R Otilde -20
+KPX R T -20
+KPX R Tcaron -20
+KPX R Tcommaaccent -20
+KPX R U -20
+KPX R Uacute -20
+KPX R Ucircumflex -20
+KPX R Udieresis -20
+KPX R Ugrave -20
+KPX R Uhungarumlaut -20
+KPX R Umacron -20
+KPX R Uogonek -20
+KPX R Uring -20
+KPX R V -50
+KPX R W -40
+KPX R Y -50
+KPX R Yacute -50
+KPX R Ydieresis -50
+KPX Racute O -20
+KPX Racute Oacute -20
+KPX Racute Ocircumflex -20
+KPX Racute Odieresis -20
+KPX Racute Ograve -20
+KPX Racute Ohungarumlaut -20
+KPX Racute Omacron -20
+KPX Racute Oslash -20
+KPX Racute Otilde -20
+KPX Racute T -20
+KPX Racute Tcaron -20
+KPX Racute Tcommaaccent -20
+KPX Racute U -20
+KPX Racute Uacute -20
+KPX Racute Ucircumflex -20
+KPX Racute Udieresis -20
+KPX Racute Ugrave -20
+KPX Racute Uhungarumlaut -20
+KPX Racute Umacron -20
+KPX Racute Uogonek -20
+KPX Racute Uring -20
+KPX Racute V -50
+KPX Racute W -40
+KPX Racute Y -50
+KPX Racute Yacute -50
+KPX Racute Ydieresis -50
+KPX Rcaron O -20
+KPX Rcaron Oacute -20
+KPX Rcaron Ocircumflex -20
+KPX Rcaron Odieresis -20
+KPX Rcaron Ograve -20
+KPX Rcaron Ohungarumlaut -20
+KPX Rcaron Omacron -20
+KPX Rcaron Oslash -20
+KPX Rcaron Otilde -20
+KPX Rcaron T -20
+KPX Rcaron Tcaron -20
+KPX Rcaron Tcommaaccent -20
+KPX Rcaron U -20
+KPX Rcaron Uacute -20
+KPX Rcaron Ucircumflex -20
+KPX Rcaron Udieresis -20
+KPX Rcaron Ugrave -20
+KPX Rcaron Uhungarumlaut -20
+KPX Rcaron Umacron -20
+KPX Rcaron Uogonek -20
+KPX Rcaron Uring -20
+KPX Rcaron V -50
+KPX Rcaron W -40
+KPX Rcaron Y -50
+KPX Rcaron Yacute -50
+KPX Rcaron Ydieresis -50
+KPX Rcommaaccent O -20
+KPX Rcommaaccent Oacute -20
+KPX Rcommaaccent Ocircumflex -20
+KPX Rcommaaccent Odieresis -20
+KPX Rcommaaccent Ograve -20
+KPX Rcommaaccent Ohungarumlaut -20
+KPX Rcommaaccent Omacron -20
+KPX Rcommaaccent Oslash -20
+KPX Rcommaaccent Otilde -20
+KPX Rcommaaccent T -20
+KPX Rcommaaccent Tcaron -20
+KPX Rcommaaccent Tcommaaccent -20
+KPX Rcommaaccent U -20
+KPX Rcommaaccent Uacute -20
+KPX Rcommaaccent Ucircumflex -20
+KPX Rcommaaccent Udieresis -20
+KPX Rcommaaccent Ugrave -20
+KPX Rcommaaccent Uhungarumlaut -20
+KPX Rcommaaccent Umacron -20
+KPX Rcommaaccent Uogonek -20
+KPX Rcommaaccent Uring -20
+KPX Rcommaaccent V -50
+KPX Rcommaaccent W -40
+KPX Rcommaaccent Y -50
+KPX Rcommaaccent Yacute -50
+KPX Rcommaaccent Ydieresis -50
+KPX T A -90
+KPX T Aacute -90
+KPX T Abreve -90
+KPX T Acircumflex -90
+KPX T Adieresis -90
+KPX T Agrave -90
+KPX T Amacron -90
+KPX T Aogonek -90
+KPX T Aring -90
+KPX T Atilde -90
+KPX T O -40
+KPX T Oacute -40
+KPX T Ocircumflex -40
+KPX T Odieresis -40
+KPX T Ograve -40
+KPX T Ohungarumlaut -40
+KPX T Omacron -40
+KPX T Oslash -40
+KPX T Otilde -40
+KPX T a -80
+KPX T aacute -80
+KPX T abreve -80
+KPX T acircumflex -80
+KPX T adieresis -80
+KPX T agrave -80
+KPX T amacron -80
+KPX T aogonek -80
+KPX T aring -80
+KPX T atilde -80
+KPX T colon -40
+KPX T comma -80
+KPX T e -60
+KPX T eacute -60
+KPX T ecaron -60
+KPX T ecircumflex -60
+KPX T edieresis -60
+KPX T edotaccent -60
+KPX T egrave -60
+KPX T emacron -60
+KPX T eogonek -60
+KPX T hyphen -120
+KPX T o -80
+KPX T oacute -80
+KPX T ocircumflex -80
+KPX T odieresis -80
+KPX T ograve -80
+KPX T ohungarumlaut -80
+KPX T omacron -80
+KPX T oslash -80
+KPX T otilde -80
+KPX T period -80
+KPX T r -80
+KPX T racute -80
+KPX T rcommaaccent -80
+KPX T semicolon -40
+KPX T u -90
+KPX T uacute -90
+KPX T ucircumflex -90
+KPX T udieresis -90
+KPX T ugrave -90
+KPX T uhungarumlaut -90
+KPX T umacron -90
+KPX T uogonek -90
+KPX T uring -90
+KPX T w -60
+KPX T y -60
+KPX T yacute -60
+KPX T ydieresis -60
+KPX Tcaron A -90
+KPX Tcaron Aacute -90
+KPX Tcaron Abreve -90
+KPX Tcaron Acircumflex -90
+KPX Tcaron Adieresis -90
+KPX Tcaron Agrave -90
+KPX Tcaron Amacron -90
+KPX Tcaron Aogonek -90
+KPX Tcaron Aring -90
+KPX Tcaron Atilde -90
+KPX Tcaron O -40
+KPX Tcaron Oacute -40
+KPX Tcaron Ocircumflex -40
+KPX Tcaron Odieresis -40
+KPX Tcaron Ograve -40
+KPX Tcaron Ohungarumlaut -40
+KPX Tcaron Omacron -40
+KPX Tcaron Oslash -40
+KPX Tcaron Otilde -40
+KPX Tcaron a -80
+KPX Tcaron aacute -80
+KPX Tcaron abreve -80
+KPX Tcaron acircumflex -80
+KPX Tcaron adieresis -80
+KPX Tcaron agrave -80
+KPX Tcaron amacron -80
+KPX Tcaron aogonek -80
+KPX Tcaron aring -80
+KPX Tcaron atilde -80
+KPX Tcaron colon -40
+KPX Tcaron comma -80
+KPX Tcaron e -60
+KPX Tcaron eacute -60
+KPX Tcaron ecaron -60
+KPX Tcaron ecircumflex -60
+KPX Tcaron edieresis -60
+KPX Tcaron edotaccent -60
+KPX Tcaron egrave -60
+KPX Tcaron emacron -60
+KPX Tcaron eogonek -60
+KPX Tcaron hyphen -120
+KPX Tcaron o -80
+KPX Tcaron oacute -80
+KPX Tcaron ocircumflex -80
+KPX Tcaron odieresis -80
+KPX Tcaron ograve -80
+KPX Tcaron ohungarumlaut -80
+KPX Tcaron omacron -80
+KPX Tcaron oslash -80
+KPX Tcaron otilde -80
+KPX Tcaron period -80
+KPX Tcaron r -80
+KPX Tcaron racute -80
+KPX Tcaron rcommaaccent -80
+KPX Tcaron semicolon -40
+KPX Tcaron u -90
+KPX Tcaron uacute -90
+KPX Tcaron ucircumflex -90
+KPX Tcaron udieresis -90
+KPX Tcaron ugrave -90
+KPX Tcaron uhungarumlaut -90
+KPX Tcaron umacron -90
+KPX Tcaron uogonek -90
+KPX Tcaron uring -90
+KPX Tcaron w -60
+KPX Tcaron y -60
+KPX Tcaron yacute -60
+KPX Tcaron ydieresis -60
+KPX Tcommaaccent A -90
+KPX Tcommaaccent Aacute -90
+KPX Tcommaaccent Abreve -90
+KPX Tcommaaccent Acircumflex -90
+KPX Tcommaaccent Adieresis -90
+KPX Tcommaaccent Agrave -90
+KPX Tcommaaccent Amacron -90
+KPX Tcommaaccent Aogonek -90
+KPX Tcommaaccent Aring -90
+KPX Tcommaaccent Atilde -90
+KPX Tcommaaccent O -40
+KPX Tcommaaccent Oacute -40
+KPX Tcommaaccent Ocircumflex -40
+KPX Tcommaaccent Odieresis -40
+KPX Tcommaaccent Ograve -40
+KPX Tcommaaccent Ohungarumlaut -40
+KPX Tcommaaccent Omacron -40
+KPX Tcommaaccent Oslash -40
+KPX Tcommaaccent Otilde -40
+KPX Tcommaaccent a -80
+KPX Tcommaaccent aacute -80
+KPX Tcommaaccent abreve -80
+KPX Tcommaaccent acircumflex -80
+KPX Tcommaaccent adieresis -80
+KPX Tcommaaccent agrave -80
+KPX Tcommaaccent amacron -80
+KPX Tcommaaccent aogonek -80
+KPX Tcommaaccent aring -80
+KPX Tcommaaccent atilde -80
+KPX Tcommaaccent colon -40
+KPX Tcommaaccent comma -80
+KPX Tcommaaccent e -60
+KPX Tcommaaccent eacute -60
+KPX Tcommaaccent ecaron -60
+KPX Tcommaaccent ecircumflex -60
+KPX Tcommaaccent edieresis -60
+KPX Tcommaaccent edotaccent -60
+KPX Tcommaaccent egrave -60
+KPX Tcommaaccent emacron -60
+KPX Tcommaaccent eogonek -60
+KPX Tcommaaccent hyphen -120
+KPX Tcommaaccent o -80
+KPX Tcommaaccent oacute -80
+KPX Tcommaaccent ocircumflex -80
+KPX Tcommaaccent odieresis -80
+KPX Tcommaaccent ograve -80
+KPX Tcommaaccent ohungarumlaut -80
+KPX Tcommaaccent omacron -80
+KPX Tcommaaccent oslash -80
+KPX Tcommaaccent otilde -80
+KPX Tcommaaccent period -80
+KPX Tcommaaccent r -80
+KPX Tcommaaccent racute -80
+KPX Tcommaaccent rcommaaccent -80
+KPX Tcommaaccent semicolon -40
+KPX Tcommaaccent u -90
+KPX Tcommaaccent uacute -90
+KPX Tcommaaccent ucircumflex -90
+KPX Tcommaaccent udieresis -90
+KPX Tcommaaccent ugrave -90
+KPX Tcommaaccent uhungarumlaut -90
+KPX Tcommaaccent umacron -90
+KPX Tcommaaccent uogonek -90
+KPX Tcommaaccent uring -90
+KPX Tcommaaccent w -60
+KPX Tcommaaccent y -60
+KPX Tcommaaccent yacute -60
+KPX Tcommaaccent ydieresis -60
+KPX U A -50
+KPX U Aacute -50
+KPX U Abreve -50
+KPX U Acircumflex -50
+KPX U Adieresis -50
+KPX U Agrave -50
+KPX U Amacron -50
+KPX U Aogonek -50
+KPX U Aring -50
+KPX U Atilde -50
+KPX U comma -30
+KPX U period -30
+KPX Uacute A -50
+KPX Uacute Aacute -50
+KPX Uacute Abreve -50
+KPX Uacute Acircumflex -50
+KPX Uacute Adieresis -50
+KPX Uacute Agrave -50
+KPX Uacute Amacron -50
+KPX Uacute Aogonek -50
+KPX Uacute Aring -50
+KPX Uacute Atilde -50
+KPX Uacute comma -30
+KPX Uacute period -30
+KPX Ucircumflex A -50
+KPX Ucircumflex Aacute -50
+KPX Ucircumflex Abreve -50
+KPX Ucircumflex Acircumflex -50
+KPX Ucircumflex Adieresis -50
+KPX Ucircumflex Agrave -50
+KPX Ucircumflex Amacron -50
+KPX Ucircumflex Aogonek -50
+KPX Ucircumflex Aring -50
+KPX Ucircumflex Atilde -50
+KPX Ucircumflex comma -30
+KPX Ucircumflex period -30
+KPX Udieresis A -50
+KPX Udieresis Aacute -50
+KPX Udieresis Abreve -50
+KPX Udieresis Acircumflex -50
+KPX Udieresis Adieresis -50
+KPX Udieresis Agrave -50
+KPX Udieresis Amacron -50
+KPX Udieresis Aogonek -50
+KPX Udieresis Aring -50
+KPX Udieresis Atilde -50
+KPX Udieresis comma -30
+KPX Udieresis period -30
+KPX Ugrave A -50
+KPX Ugrave Aacute -50
+KPX Ugrave Abreve -50
+KPX Ugrave Acircumflex -50
+KPX Ugrave Adieresis -50
+KPX Ugrave Agrave -50
+KPX Ugrave Amacron -50
+KPX Ugrave Aogonek -50
+KPX Ugrave Aring -50
+KPX Ugrave Atilde -50
+KPX Ugrave comma -30
+KPX Ugrave period -30
+KPX Uhungarumlaut A -50
+KPX Uhungarumlaut Aacute -50
+KPX Uhungarumlaut Abreve -50
+KPX Uhungarumlaut Acircumflex -50
+KPX Uhungarumlaut Adieresis -50
+KPX Uhungarumlaut Agrave -50
+KPX Uhungarumlaut Amacron -50
+KPX Uhungarumlaut Aogonek -50
+KPX Uhungarumlaut Aring -50
+KPX Uhungarumlaut Atilde -50
+KPX Uhungarumlaut comma -30
+KPX Uhungarumlaut period -30
+KPX Umacron A -50
+KPX Umacron Aacute -50
+KPX Umacron Abreve -50
+KPX Umacron Acircumflex -50
+KPX Umacron Adieresis -50
+KPX Umacron Agrave -50
+KPX Umacron Amacron -50
+KPX Umacron Aogonek -50
+KPX Umacron Aring -50
+KPX Umacron Atilde -50
+KPX Umacron comma -30
+KPX Umacron period -30
+KPX Uogonek A -50
+KPX Uogonek Aacute -50
+KPX Uogonek Abreve -50
+KPX Uogonek Acircumflex -50
+KPX Uogonek Adieresis -50
+KPX Uogonek Agrave -50
+KPX Uogonek Amacron -50
+KPX Uogonek Aogonek -50
+KPX Uogonek Aring -50
+KPX Uogonek Atilde -50
+KPX Uogonek comma -30
+KPX Uogonek period -30
+KPX Uring A -50
+KPX Uring Aacute -50
+KPX Uring Abreve -50
+KPX Uring Acircumflex -50
+KPX Uring Adieresis -50
+KPX Uring Agrave -50
+KPX Uring Amacron -50
+KPX Uring Aogonek -50
+KPX Uring Aring -50
+KPX Uring Atilde -50
+KPX Uring comma -30
+KPX Uring period -30
+KPX V A -80
+KPX V Aacute -80
+KPX V Abreve -80
+KPX V Acircumflex -80
+KPX V Adieresis -80
+KPX V Agrave -80
+KPX V Amacron -80
+KPX V Aogonek -80
+KPX V Aring -80
+KPX V Atilde -80
+KPX V G -50
+KPX V Gbreve -50
+KPX V Gcommaaccent -50
+KPX V O -50
+KPX V Oacute -50
+KPX V Ocircumflex -50
+KPX V Odieresis -50
+KPX V Ograve -50
+KPX V Ohungarumlaut -50
+KPX V Omacron -50
+KPX V Oslash -50
+KPX V Otilde -50
+KPX V a -60
+KPX V aacute -60
+KPX V abreve -60
+KPX V acircumflex -60
+KPX V adieresis -60
+KPX V agrave -60
+KPX V amacron -60
+KPX V aogonek -60
+KPX V aring -60
+KPX V atilde -60
+KPX V colon -40
+KPX V comma -120
+KPX V e -50
+KPX V eacute -50
+KPX V ecaron -50
+KPX V ecircumflex -50
+KPX V edieresis -50
+KPX V edotaccent -50
+KPX V egrave -50
+KPX V emacron -50
+KPX V eogonek -50
+KPX V hyphen -80
+KPX V o -90
+KPX V oacute -90
+KPX V ocircumflex -90
+KPX V odieresis -90
+KPX V ograve -90
+KPX V ohungarumlaut -90
+KPX V omacron -90
+KPX V oslash -90
+KPX V otilde -90
+KPX V period -120
+KPX V semicolon -40
+KPX V u -60
+KPX V uacute -60
+KPX V ucircumflex -60
+KPX V udieresis -60
+KPX V ugrave -60
+KPX V uhungarumlaut -60
+KPX V umacron -60
+KPX V uogonek -60
+KPX V uring -60
+KPX W A -60
+KPX W Aacute -60
+KPX W Abreve -60
+KPX W Acircumflex -60
+KPX W Adieresis -60
+KPX W Agrave -60
+KPX W Amacron -60
+KPX W Aogonek -60
+KPX W Aring -60
+KPX W Atilde -60
+KPX W O -20
+KPX W Oacute -20
+KPX W Ocircumflex -20
+KPX W Odieresis -20
+KPX W Ograve -20
+KPX W Ohungarumlaut -20
+KPX W Omacron -20
+KPX W Oslash -20
+KPX W Otilde -20
+KPX W a -40
+KPX W aacute -40
+KPX W abreve -40
+KPX W acircumflex -40
+KPX W adieresis -40
+KPX W agrave -40
+KPX W amacron -40
+KPX W aogonek -40
+KPX W aring -40
+KPX W atilde -40
+KPX W colon -10
+KPX W comma -80
+KPX W e -35
+KPX W eacute -35
+KPX W ecaron -35
+KPX W ecircumflex -35
+KPX W edieresis -35
+KPX W edotaccent -35
+KPX W egrave -35
+KPX W emacron -35
+KPX W eogonek -35
+KPX W hyphen -40
+KPX W o -60
+KPX W oacute -60
+KPX W ocircumflex -60
+KPX W odieresis -60
+KPX W ograve -60
+KPX W ohungarumlaut -60
+KPX W omacron -60
+KPX W oslash -60
+KPX W otilde -60
+KPX W period -80
+KPX W semicolon -10
+KPX W u -45
+KPX W uacute -45
+KPX W ucircumflex -45
+KPX W udieresis -45
+KPX W ugrave -45
+KPX W uhungarumlaut -45
+KPX W umacron -45
+KPX W uogonek -45
+KPX W uring -45
+KPX W y -20
+KPX W yacute -20
+KPX W ydieresis -20
+KPX Y A -110
+KPX Y Aacute -110
+KPX Y Abreve -110
+KPX Y Acircumflex -110
+KPX Y Adieresis -110
+KPX Y Agrave -110
+KPX Y Amacron -110
+KPX Y Aogonek -110
+KPX Y Aring -110
+KPX Y Atilde -110
+KPX Y O -70
+KPX Y Oacute -70
+KPX Y Ocircumflex -70
+KPX Y Odieresis -70
+KPX Y Ograve -70
+KPX Y Ohungarumlaut -70
+KPX Y Omacron -70
+KPX Y Oslash -70
+KPX Y Otilde -70
+KPX Y a -90
+KPX Y aacute -90
+KPX Y abreve -90
+KPX Y acircumflex -90
+KPX Y adieresis -90
+KPX Y agrave -90
+KPX Y amacron -90
+KPX Y aogonek -90
+KPX Y aring -90
+KPX Y atilde -90
+KPX Y colon -50
+KPX Y comma -100
+KPX Y e -80
+KPX Y eacute -80
+KPX Y ecaron -80
+KPX Y ecircumflex -80
+KPX Y edieresis -80
+KPX Y edotaccent -80
+KPX Y egrave -80
+KPX Y emacron -80
+KPX Y eogonek -80
+KPX Y o -100
+KPX Y oacute -100
+KPX Y ocircumflex -100
+KPX Y odieresis -100
+KPX Y ograve -100
+KPX Y ohungarumlaut -100
+KPX Y omacron -100
+KPX Y oslash -100
+KPX Y otilde -100
+KPX Y period -100
+KPX Y semicolon -50
+KPX Y u -100
+KPX Y uacute -100
+KPX Y ucircumflex -100
+KPX Y udieresis -100
+KPX Y ugrave -100
+KPX Y uhungarumlaut -100
+KPX Y umacron -100
+KPX Y uogonek -100
+KPX Y uring -100
+KPX Yacute A -110
+KPX Yacute Aacute -110
+KPX Yacute Abreve -110
+KPX Yacute Acircumflex -110
+KPX Yacute Adieresis -110
+KPX Yacute Agrave -110
+KPX Yacute Amacron -110
+KPX Yacute Aogonek -110
+KPX Yacute Aring -110
+KPX Yacute Atilde -110
+KPX Yacute O -70
+KPX Yacute Oacute -70
+KPX Yacute Ocircumflex -70
+KPX Yacute Odieresis -70
+KPX Yacute Ograve -70
+KPX Yacute Ohungarumlaut -70
+KPX Yacute Omacron -70
+KPX Yacute Oslash -70
+KPX Yacute Otilde -70
+KPX Yacute a -90
+KPX Yacute aacute -90
+KPX Yacute abreve -90
+KPX Yacute acircumflex -90
+KPX Yacute adieresis -90
+KPX Yacute agrave -90
+KPX Yacute amacron -90
+KPX Yacute aogonek -90
+KPX Yacute aring -90
+KPX Yacute atilde -90
+KPX Yacute colon -50
+KPX Yacute comma -100
+KPX Yacute e -80
+KPX Yacute eacute -80
+KPX Yacute ecaron -80
+KPX Yacute ecircumflex -80
+KPX Yacute edieresis -80
+KPX Yacute edotaccent -80
+KPX Yacute egrave -80
+KPX Yacute emacron -80
+KPX Yacute eogonek -80
+KPX Yacute o -100
+KPX Yacute oacute -100
+KPX Yacute ocircumflex -100
+KPX Yacute odieresis -100
+KPX Yacute ograve -100
+KPX Yacute ohungarumlaut -100
+KPX Yacute omacron -100
+KPX Yacute oslash -100
+KPX Yacute otilde -100
+KPX Yacute period -100
+KPX Yacute semicolon -50
+KPX Yacute u -100
+KPX Yacute uacute -100
+KPX Yacute ucircumflex -100
+KPX Yacute udieresis -100
+KPX Yacute ugrave -100
+KPX Yacute uhungarumlaut -100
+KPX Yacute umacron -100
+KPX Yacute uogonek -100
+KPX Yacute uring -100
+KPX Ydieresis A -110
+KPX Ydieresis Aacute -110
+KPX Ydieresis Abreve -110
+KPX Ydieresis Acircumflex -110
+KPX Ydieresis Adieresis -110
+KPX Ydieresis Agrave -110
+KPX Ydieresis Amacron -110
+KPX Ydieresis Aogonek -110
+KPX Ydieresis Aring -110
+KPX Ydieresis Atilde -110
+KPX Ydieresis O -70
+KPX Ydieresis Oacute -70
+KPX Ydieresis Ocircumflex -70
+KPX Ydieresis Odieresis -70
+KPX Ydieresis Ograve -70
+KPX Ydieresis Ohungarumlaut -70
+KPX Ydieresis Omacron -70
+KPX Ydieresis Oslash -70
+KPX Ydieresis Otilde -70
+KPX Ydieresis a -90
+KPX Ydieresis aacute -90
+KPX Ydieresis abreve -90
+KPX Ydieresis acircumflex -90
+KPX Ydieresis adieresis -90
+KPX Ydieresis agrave -90
+KPX Ydieresis amacron -90
+KPX Ydieresis aogonek -90
+KPX Ydieresis aring -90
+KPX Ydieresis atilde -90
+KPX Ydieresis colon -50
+KPX Ydieresis comma -100
+KPX Ydieresis e -80
+KPX Ydieresis eacute -80
+KPX Ydieresis ecaron -80
+KPX Ydieresis ecircumflex -80
+KPX Ydieresis edieresis -80
+KPX Ydieresis edotaccent -80
+KPX Ydieresis egrave -80
+KPX Ydieresis emacron -80
+KPX Ydieresis eogonek -80
+KPX Ydieresis o -100
+KPX Ydieresis oacute -100
+KPX Ydieresis ocircumflex -100
+KPX Ydieresis odieresis -100
+KPX Ydieresis ograve -100
+KPX Ydieresis ohungarumlaut -100
+KPX Ydieresis omacron -100
+KPX Ydieresis oslash -100
+KPX Ydieresis otilde -100
+KPX Ydieresis period -100
+KPX Ydieresis semicolon -50
+KPX Ydieresis u -100
+KPX Ydieresis uacute -100
+KPX Ydieresis ucircumflex -100
+KPX Ydieresis udieresis -100
+KPX Ydieresis ugrave -100
+KPX Ydieresis uhungarumlaut -100
+KPX Ydieresis umacron -100
+KPX Ydieresis uogonek -100
+KPX Ydieresis uring -100
+KPX a g -10
+KPX a gbreve -10
+KPX a gcommaaccent -10
+KPX a v -15
+KPX a w -15
+KPX a y -20
+KPX a yacute -20
+KPX a ydieresis -20
+KPX aacute g -10
+KPX aacute gbreve -10
+KPX aacute gcommaaccent -10
+KPX aacute v -15
+KPX aacute w -15
+KPX aacute y -20
+KPX aacute yacute -20
+KPX aacute ydieresis -20
+KPX abreve g -10
+KPX abreve gbreve -10
+KPX abreve gcommaaccent -10
+KPX abreve v -15
+KPX abreve w -15
+KPX abreve y -20
+KPX abreve yacute -20
+KPX abreve ydieresis -20
+KPX acircumflex g -10
+KPX acircumflex gbreve -10
+KPX acircumflex gcommaaccent -10
+KPX acircumflex v -15
+KPX acircumflex w -15
+KPX acircumflex y -20
+KPX acircumflex yacute -20
+KPX acircumflex ydieresis -20
+KPX adieresis g -10
+KPX adieresis gbreve -10
+KPX adieresis gcommaaccent -10
+KPX adieresis v -15
+KPX adieresis w -15
+KPX adieresis y -20
+KPX adieresis yacute -20
+KPX adieresis ydieresis -20
+KPX agrave g -10
+KPX agrave gbreve -10
+KPX agrave gcommaaccent -10
+KPX agrave v -15
+KPX agrave w -15
+KPX agrave y -20
+KPX agrave yacute -20
+KPX agrave ydieresis -20
+KPX amacron g -10
+KPX amacron gbreve -10
+KPX amacron gcommaaccent -10
+KPX amacron v -15
+KPX amacron w -15
+KPX amacron y -20
+KPX amacron yacute -20
+KPX amacron ydieresis -20
+KPX aogonek g -10
+KPX aogonek gbreve -10
+KPX aogonek gcommaaccent -10
+KPX aogonek v -15
+KPX aogonek w -15
+KPX aogonek y -20
+KPX aogonek yacute -20
+KPX aogonek ydieresis -20
+KPX aring g -10
+KPX aring gbreve -10
+KPX aring gcommaaccent -10
+KPX aring v -15
+KPX aring w -15
+KPX aring y -20
+KPX aring yacute -20
+KPX aring ydieresis -20
+KPX atilde g -10
+KPX atilde gbreve -10
+KPX atilde gcommaaccent -10
+KPX atilde v -15
+KPX atilde w -15
+KPX atilde y -20
+KPX atilde yacute -20
+KPX atilde ydieresis -20
+KPX b l -10
+KPX b lacute -10
+KPX b lcommaaccent -10
+KPX b lslash -10
+KPX b u -20
+KPX b uacute -20
+KPX b ucircumflex -20
+KPX b udieresis -20
+KPX b ugrave -20
+KPX b uhungarumlaut -20
+KPX b umacron -20
+KPX b uogonek -20
+KPX b uring -20
+KPX b v -20
+KPX b y -20
+KPX b yacute -20
+KPX b ydieresis -20
+KPX c h -10
+KPX c k -20
+KPX c kcommaaccent -20
+KPX c l -20
+KPX c lacute -20
+KPX c lcommaaccent -20
+KPX c lslash -20
+KPX c y -10
+KPX c yacute -10
+KPX c ydieresis -10
+KPX cacute h -10
+KPX cacute k -20
+KPX cacute kcommaaccent -20
+KPX cacute l -20
+KPX cacute lacute -20
+KPX cacute lcommaaccent -20
+KPX cacute lslash -20
+KPX cacute y -10
+KPX cacute yacute -10
+KPX cacute ydieresis -10
+KPX ccaron h -10
+KPX ccaron k -20
+KPX ccaron kcommaaccent -20
+KPX ccaron l -20
+KPX ccaron lacute -20
+KPX ccaron lcommaaccent -20
+KPX ccaron lslash -20
+KPX ccaron y -10
+KPX ccaron yacute -10
+KPX ccaron ydieresis -10
+KPX ccedilla h -10
+KPX ccedilla k -20
+KPX ccedilla kcommaaccent -20
+KPX ccedilla l -20
+KPX ccedilla lacute -20
+KPX ccedilla lcommaaccent -20
+KPX ccedilla lslash -20
+KPX ccedilla y -10
+KPX ccedilla yacute -10
+KPX ccedilla ydieresis -10
+KPX colon space -40
+KPX comma quotedblright -120
+KPX comma quoteright -120
+KPX comma space -40
+KPX d d -10
+KPX d dcroat -10
+KPX d v -15
+KPX d w -15
+KPX d y -15
+KPX d yacute -15
+KPX d ydieresis -15
+KPX dcroat d -10
+KPX dcroat dcroat -10
+KPX dcroat v -15
+KPX dcroat w -15
+KPX dcroat y -15
+KPX dcroat yacute -15
+KPX dcroat ydieresis -15
+KPX e comma 10
+KPX e period 20
+KPX e v -15
+KPX e w -15
+KPX e x -15
+KPX e y -15
+KPX e yacute -15
+KPX e ydieresis -15
+KPX eacute comma 10
+KPX eacute period 20
+KPX eacute v -15
+KPX eacute w -15
+KPX eacute x -15
+KPX eacute y -15
+KPX eacute yacute -15
+KPX eacute ydieresis -15
+KPX ecaron comma 10
+KPX ecaron period 20
+KPX ecaron v -15
+KPX ecaron w -15
+KPX ecaron x -15
+KPX ecaron y -15
+KPX ecaron yacute -15
+KPX ecaron ydieresis -15
+KPX ecircumflex comma 10
+KPX ecircumflex period 20
+KPX ecircumflex v -15
+KPX ecircumflex w -15
+KPX ecircumflex x -15
+KPX ecircumflex y -15
+KPX ecircumflex yacute -15
+KPX ecircumflex ydieresis -15
+KPX edieresis comma 10
+KPX edieresis period 20
+KPX edieresis v -15
+KPX edieresis w -15
+KPX edieresis x -15
+KPX edieresis y -15
+KPX edieresis yacute -15
+KPX edieresis ydieresis -15
+KPX edotaccent comma 10
+KPX edotaccent period 20
+KPX edotaccent v -15
+KPX edotaccent w -15
+KPX edotaccent x -15
+KPX edotaccent y -15
+KPX edotaccent yacute -15
+KPX edotaccent ydieresis -15
+KPX egrave comma 10
+KPX egrave period 20
+KPX egrave v -15
+KPX egrave w -15
+KPX egrave x -15
+KPX egrave y -15
+KPX egrave yacute -15
+KPX egrave ydieresis -15
+KPX emacron comma 10
+KPX emacron period 20
+KPX emacron v -15
+KPX emacron w -15
+KPX emacron x -15
+KPX emacron y -15
+KPX emacron yacute -15
+KPX emacron ydieresis -15
+KPX eogonek comma 10
+KPX eogonek period 20
+KPX eogonek v -15
+KPX eogonek w -15
+KPX eogonek x -15
+KPX eogonek y -15
+KPX eogonek yacute -15
+KPX eogonek ydieresis -15
+KPX f comma -10
+KPX f e -10
+KPX f eacute -10
+KPX f ecaron -10
+KPX f ecircumflex -10
+KPX f edieresis -10
+KPX f edotaccent -10
+KPX f egrave -10
+KPX f emacron -10
+KPX f eogonek -10
+KPX f o -20
+KPX f oacute -20
+KPX f ocircumflex -20
+KPX f odieresis -20
+KPX f ograve -20
+KPX f ohungarumlaut -20
+KPX f omacron -20
+KPX f oslash -20
+KPX f otilde -20
+KPX f period -10
+KPX f quotedblright 30
+KPX f quoteright 30
+KPX g e 10
+KPX g eacute 10
+KPX g ecaron 10
+KPX g ecircumflex 10
+KPX g edieresis 10
+KPX g edotaccent 10
+KPX g egrave 10
+KPX g emacron 10
+KPX g eogonek 10
+KPX g g -10
+KPX g gbreve -10
+KPX g gcommaaccent -10
+KPX gbreve e 10
+KPX gbreve eacute 10
+KPX gbreve ecaron 10
+KPX gbreve ecircumflex 10
+KPX gbreve edieresis 10
+KPX gbreve edotaccent 10
+KPX gbreve egrave 10
+KPX gbreve emacron 10
+KPX gbreve eogonek 10
+KPX gbreve g -10
+KPX gbreve gbreve -10
+KPX gbreve gcommaaccent -10
+KPX gcommaaccent e 10
+KPX gcommaaccent eacute 10
+KPX gcommaaccent ecaron 10
+KPX gcommaaccent ecircumflex 10
+KPX gcommaaccent edieresis 10
+KPX gcommaaccent edotaccent 10
+KPX gcommaaccent egrave 10
+KPX gcommaaccent emacron 10
+KPX gcommaaccent eogonek 10
+KPX gcommaaccent g -10
+KPX gcommaaccent gbreve -10
+KPX gcommaaccent gcommaaccent -10
+KPX h y -20
+KPX h yacute -20
+KPX h ydieresis -20
+KPX k o -15
+KPX k oacute -15
+KPX k ocircumflex -15
+KPX k odieresis -15
+KPX k ograve -15
+KPX k ohungarumlaut -15
+KPX k omacron -15
+KPX k oslash -15
+KPX k otilde -15
+KPX kcommaaccent o -15
+KPX kcommaaccent oacute -15
+KPX kcommaaccent ocircumflex -15
+KPX kcommaaccent odieresis -15
+KPX kcommaaccent ograve -15
+KPX kcommaaccent ohungarumlaut -15
+KPX kcommaaccent omacron -15
+KPX kcommaaccent oslash -15
+KPX kcommaaccent otilde -15
+KPX l w -15
+KPX l y -15
+KPX l yacute -15
+KPX l ydieresis -15
+KPX lacute w -15
+KPX lacute y -15
+KPX lacute yacute -15
+KPX lacute ydieresis -15
+KPX lcommaaccent w -15
+KPX lcommaaccent y -15
+KPX lcommaaccent yacute -15
+KPX lcommaaccent ydieresis -15
+KPX lslash w -15
+KPX lslash y -15
+KPX lslash yacute -15
+KPX lslash ydieresis -15
+KPX m u -20
+KPX m uacute -20
+KPX m ucircumflex -20
+KPX m udieresis -20
+KPX m ugrave -20
+KPX m uhungarumlaut -20
+KPX m umacron -20
+KPX m uogonek -20
+KPX m uring -20
+KPX m y -30
+KPX m yacute -30
+KPX m ydieresis -30
+KPX n u -10
+KPX n uacute -10
+KPX n ucircumflex -10
+KPX n udieresis -10
+KPX n ugrave -10
+KPX n uhungarumlaut -10
+KPX n umacron -10
+KPX n uogonek -10
+KPX n uring -10
+KPX n v -40
+KPX n y -20
+KPX n yacute -20
+KPX n ydieresis -20
+KPX nacute u -10
+KPX nacute uacute -10
+KPX nacute ucircumflex -10
+KPX nacute udieresis -10
+KPX nacute ugrave -10
+KPX nacute uhungarumlaut -10
+KPX nacute umacron -10
+KPX nacute uogonek -10
+KPX nacute uring -10
+KPX nacute v -40
+KPX nacute y -20
+KPX nacute yacute -20
+KPX nacute ydieresis -20
+KPX ncaron u -10
+KPX ncaron uacute -10
+KPX ncaron ucircumflex -10
+KPX ncaron udieresis -10
+KPX ncaron ugrave -10
+KPX ncaron uhungarumlaut -10
+KPX ncaron umacron -10
+KPX ncaron uogonek -10
+KPX ncaron uring -10
+KPX ncaron v -40
+KPX ncaron y -20
+KPX ncaron yacute -20
+KPX ncaron ydieresis -20
+KPX ncommaaccent u -10
+KPX ncommaaccent uacute -10
+KPX ncommaaccent ucircumflex -10
+KPX ncommaaccent udieresis -10
+KPX ncommaaccent ugrave -10
+KPX ncommaaccent uhungarumlaut -10
+KPX ncommaaccent umacron -10
+KPX ncommaaccent uogonek -10
+KPX ncommaaccent uring -10
+KPX ncommaaccent v -40
+KPX ncommaaccent y -20
+KPX ncommaaccent yacute -20
+KPX ncommaaccent ydieresis -20
+KPX ntilde u -10
+KPX ntilde uacute -10
+KPX ntilde ucircumflex -10
+KPX ntilde udieresis -10
+KPX ntilde ugrave -10
+KPX ntilde uhungarumlaut -10
+KPX ntilde umacron -10
+KPX ntilde uogonek -10
+KPX ntilde uring -10
+KPX ntilde v -40
+KPX ntilde y -20
+KPX ntilde yacute -20
+KPX ntilde ydieresis -20
+KPX o v -20
+KPX o w -15
+KPX o x -30
+KPX o y -20
+KPX o yacute -20
+KPX o ydieresis -20
+KPX oacute v -20
+KPX oacute w -15
+KPX oacute x -30
+KPX oacute y -20
+KPX oacute yacute -20
+KPX oacute ydieresis -20
+KPX ocircumflex v -20
+KPX ocircumflex w -15
+KPX ocircumflex x -30
+KPX ocircumflex y -20
+KPX ocircumflex yacute -20
+KPX ocircumflex ydieresis -20
+KPX odieresis v -20
+KPX odieresis w -15
+KPX odieresis x -30
+KPX odieresis y -20
+KPX odieresis yacute -20
+KPX odieresis ydieresis -20
+KPX ograve v -20
+KPX ograve w -15
+KPX ograve x -30
+KPX ograve y -20
+KPX ograve yacute -20
+KPX ograve ydieresis -20
+KPX ohungarumlaut v -20
+KPX ohungarumlaut w -15
+KPX ohungarumlaut x -30
+KPX ohungarumlaut y -20
+KPX ohungarumlaut yacute -20
+KPX ohungarumlaut ydieresis -20
+KPX omacron v -20
+KPX omacron w -15
+KPX omacron x -30
+KPX omacron y -20
+KPX omacron yacute -20
+KPX omacron ydieresis -20
+KPX oslash v -20
+KPX oslash w -15
+KPX oslash x -30
+KPX oslash y -20
+KPX oslash yacute -20
+KPX oslash ydieresis -20
+KPX otilde v -20
+KPX otilde w -15
+KPX otilde x -30
+KPX otilde y -20
+KPX otilde yacute -20
+KPX otilde ydieresis -20
+KPX p y -15
+KPX p yacute -15
+KPX p ydieresis -15
+KPX period quotedblright -120
+KPX period quoteright -120
+KPX period space -40
+KPX quotedblright space -80
+KPX quoteleft quoteleft -46
+KPX quoteright d -80
+KPX quoteright dcroat -80
+KPX quoteright l -20
+KPX quoteright lacute -20
+KPX quoteright lcommaaccent -20
+KPX quoteright lslash -20
+KPX quoteright quoteright -46
+KPX quoteright r -40
+KPX quoteright racute -40
+KPX quoteright rcaron -40
+KPX quoteright rcommaaccent -40
+KPX quoteright s -60
+KPX quoteright sacute -60
+KPX quoteright scaron -60
+KPX quoteright scedilla -60
+KPX quoteright scommaaccent -60
+KPX quoteright space -80
+KPX quoteright v -20
+KPX r c -20
+KPX r cacute -20
+KPX r ccaron -20
+KPX r ccedilla -20
+KPX r comma -60
+KPX r d -20
+KPX r dcroat -20
+KPX r g -15
+KPX r gbreve -15
+KPX r gcommaaccent -15
+KPX r hyphen -20
+KPX r o -20
+KPX r oacute -20
+KPX r ocircumflex -20
+KPX r odieresis -20
+KPX r ograve -20
+KPX r ohungarumlaut -20
+KPX r omacron -20
+KPX r oslash -20
+KPX r otilde -20
+KPX r period -60
+KPX r q -20
+KPX r s -15
+KPX r sacute -15
+KPX r scaron -15
+KPX r scedilla -15
+KPX r scommaaccent -15
+KPX r t 20
+KPX r tcommaaccent 20
+KPX r v 10
+KPX r y 10
+KPX r yacute 10
+KPX r ydieresis 10
+KPX racute c -20
+KPX racute cacute -20
+KPX racute ccaron -20
+KPX racute ccedilla -20
+KPX racute comma -60
+KPX racute d -20
+KPX racute dcroat -20
+KPX racute g -15
+KPX racute gbreve -15
+KPX racute gcommaaccent -15
+KPX racute hyphen -20
+KPX racute o -20
+KPX racute oacute -20
+KPX racute ocircumflex -20
+KPX racute odieresis -20
+KPX racute ograve -20
+KPX racute ohungarumlaut -20
+KPX racute omacron -20
+KPX racute oslash -20
+KPX racute otilde -20
+KPX racute period -60
+KPX racute q -20
+KPX racute s -15
+KPX racute sacute -15
+KPX racute scaron -15
+KPX racute scedilla -15
+KPX racute scommaaccent -15
+KPX racute t 20
+KPX racute tcommaaccent 20
+KPX racute v 10
+KPX racute y 10
+KPX racute yacute 10
+KPX racute ydieresis 10
+KPX rcaron c -20
+KPX rcaron cacute -20
+KPX rcaron ccaron -20
+KPX rcaron ccedilla -20
+KPX rcaron comma -60
+KPX rcaron d -20
+KPX rcaron dcroat -20
+KPX rcaron g -15
+KPX rcaron gbreve -15
+KPX rcaron gcommaaccent -15
+KPX rcaron hyphen -20
+KPX rcaron o -20
+KPX rcaron oacute -20
+KPX rcaron ocircumflex -20
+KPX rcaron odieresis -20
+KPX rcaron ograve -20
+KPX rcaron ohungarumlaut -20
+KPX rcaron omacron -20
+KPX rcaron oslash -20
+KPX rcaron otilde -20
+KPX rcaron period -60
+KPX rcaron q -20
+KPX rcaron s -15
+KPX rcaron sacute -15
+KPX rcaron scaron -15
+KPX rcaron scedilla -15
+KPX rcaron scommaaccent -15
+KPX rcaron t 20
+KPX rcaron tcommaaccent 20
+KPX rcaron v 10
+KPX rcaron y 10
+KPX rcaron yacute 10
+KPX rcaron ydieresis 10
+KPX rcommaaccent c -20
+KPX rcommaaccent cacute -20
+KPX rcommaaccent ccaron -20
+KPX rcommaaccent ccedilla -20
+KPX rcommaaccent comma -60
+KPX rcommaaccent d -20
+KPX rcommaaccent dcroat -20
+KPX rcommaaccent g -15
+KPX rcommaaccent gbreve -15
+KPX rcommaaccent gcommaaccent -15
+KPX rcommaaccent hyphen -20
+KPX rcommaaccent o -20
+KPX rcommaaccent oacute -20
+KPX rcommaaccent ocircumflex -20
+KPX rcommaaccent odieresis -20
+KPX rcommaaccent ograve -20
+KPX rcommaaccent ohungarumlaut -20
+KPX rcommaaccent omacron -20
+KPX rcommaaccent oslash -20
+KPX rcommaaccent otilde -20
+KPX rcommaaccent period -60
+KPX rcommaaccent q -20
+KPX rcommaaccent s -15
+KPX rcommaaccent sacute -15
+KPX rcommaaccent scaron -15
+KPX rcommaaccent scedilla -15
+KPX rcommaaccent scommaaccent -15
+KPX rcommaaccent t 20
+KPX rcommaaccent tcommaaccent 20
+KPX rcommaaccent v 10
+KPX rcommaaccent y 10
+KPX rcommaaccent yacute 10
+KPX rcommaaccent ydieresis 10
+KPX s w -15
+KPX sacute w -15
+KPX scaron w -15
+KPX scedilla w -15
+KPX scommaaccent w -15
+KPX semicolon space -40
+KPX space T -100
+KPX space Tcaron -100
+KPX space Tcommaaccent -100
+KPX space V -80
+KPX space W -80
+KPX space Y -120
+KPX space Yacute -120
+KPX space Ydieresis -120
+KPX space quotedblleft -80
+KPX space quoteleft -60
+KPX v a -20
+KPX v aacute -20
+KPX v abreve -20
+KPX v acircumflex -20
+KPX v adieresis -20
+KPX v agrave -20
+KPX v amacron -20
+KPX v aogonek -20
+KPX v aring -20
+KPX v atilde -20
+KPX v comma -80
+KPX v o -30
+KPX v oacute -30
+KPX v ocircumflex -30
+KPX v odieresis -30
+KPX v ograve -30
+KPX v ohungarumlaut -30
+KPX v omacron -30
+KPX v oslash -30
+KPX v otilde -30
+KPX v period -80
+KPX w comma -40
+KPX w o -20
+KPX w oacute -20
+KPX w ocircumflex -20
+KPX w odieresis -20
+KPX w ograve -20
+KPX w ohungarumlaut -20
+KPX w omacron -20
+KPX w oslash -20
+KPX w otilde -20
+KPX w period -40
+KPX x e -10
+KPX x eacute -10
+KPX x ecaron -10
+KPX x ecircumflex -10
+KPX x edieresis -10
+KPX x edotaccent -10
+KPX x egrave -10
+KPX x emacron -10
+KPX x eogonek -10
+KPX y a -30
+KPX y aacute -30
+KPX y abreve -30
+KPX y acircumflex -30
+KPX y adieresis -30
+KPX y agrave -30
+KPX y amacron -30
+KPX y aogonek -30
+KPX y aring -30
+KPX y atilde -30
+KPX y comma -80
+KPX y e -10
+KPX y eacute -10
+KPX y ecaron -10
+KPX y ecircumflex -10
+KPX y edieresis -10
+KPX y edotaccent -10
+KPX y egrave -10
+KPX y emacron -10
+KPX y eogonek -10
+KPX y o -25
+KPX y oacute -25
+KPX y ocircumflex -25
+KPX y odieresis -25
+KPX y ograve -25
+KPX y ohungarumlaut -25
+KPX y omacron -25
+KPX y oslash -25
+KPX y otilde -25
+KPX y period -80
+KPX yacute a -30
+KPX yacute aacute -30
+KPX yacute abreve -30
+KPX yacute acircumflex -30
+KPX yacute adieresis -30
+KPX yacute agrave -30
+KPX yacute amacron -30
+KPX yacute aogonek -30
+KPX yacute aring -30
+KPX yacute atilde -30
+KPX yacute comma -80
+KPX yacute e -10
+KPX yacute eacute -10
+KPX yacute ecaron -10
+KPX yacute ecircumflex -10
+KPX yacute edieresis -10
+KPX yacute edotaccent -10
+KPX yacute egrave -10
+KPX yacute emacron -10
+KPX yacute eogonek -10
+KPX yacute o -25
+KPX yacute oacute -25
+KPX yacute ocircumflex -25
+KPX yacute odieresis -25
+KPX yacute ograve -25
+KPX yacute ohungarumlaut -25
+KPX yacute omacron -25
+KPX yacute oslash -25
+KPX yacute otilde -25
+KPX yacute period -80
+KPX ydieresis a -30
+KPX ydieresis aacute -30
+KPX ydieresis abreve -30
+KPX ydieresis acircumflex -30
+KPX ydieresis adieresis -30
+KPX ydieresis agrave -30
+KPX ydieresis amacron -30
+KPX ydieresis aogonek -30
+KPX ydieresis aring -30
+KPX ydieresis atilde -30
+KPX ydieresis comma -80
+KPX ydieresis e -10
+KPX ydieresis eacute -10
+KPX ydieresis ecaron -10
+KPX ydieresis ecircumflex -10
+KPX ydieresis edieresis -10
+KPX ydieresis edotaccent -10
+KPX ydieresis egrave -10
+KPX ydieresis emacron -10
+KPX ydieresis eogonek -10
+KPX ydieresis o -25
+KPX ydieresis oacute -25
+KPX ydieresis ocircumflex -25
+KPX ydieresis odieresis -25
+KPX ydieresis ograve -25
+KPX ydieresis ohungarumlaut -25
+KPX ydieresis omacron -25
+KPX ydieresis oslash -25
+KPX ydieresis otilde -25
+KPX ydieresis period -80
+KPX z e 10
+KPX z eacute 10
+KPX z ecaron 10
+KPX z ecircumflex 10
+KPX z edieresis 10
+KPX z edotaccent 10
+KPX z egrave 10
+KPX z emacron 10
+KPX z eogonek 10
+KPX zacute e 10
+KPX zacute eacute 10
+KPX zacute ecaron 10
+KPX zacute ecircumflex 10
+KPX zacute edieresis 10
+KPX zacute edotaccent 10
+KPX zacute egrave 10
+KPX zacute emacron 10
+KPX zacute eogonek 10
+KPX zcaron e 10
+KPX zcaron eacute 10
+KPX zcaron ecaron 10
+KPX zcaron ecircumflex 10
+KPX zcaron edieresis 10
+KPX zcaron edotaccent 10
+KPX zcaron egrave 10
+KPX zcaron emacron 10
+KPX zcaron eogonek 10
+KPX zdotaccent e 10
+KPX zdotaccent eacute 10
+KPX zdotaccent ecaron 10
+KPX zdotaccent ecircumflex 10
+KPX zdotaccent edieresis 10
+KPX zdotaccent edotaccent 10
+KPX zdotaccent egrave 10
+KPX zdotaccent emacron 10
+KPX zdotaccent eogonek 10
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/program/libraries/afm/Helvetica-BoldOblique.afm b/program/libraries/afm/Helvetica-BoldOblique.afm
new file mode 100644 (file)
index 0000000..1715b21
--- /dev/null
@@ -0,0 +1,2827 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated.  All Rights Reserved.
+Comment Creation Date: Thu May  1 12:45:12 1997
+Comment UniqueID 43053
+Comment VMusage 14482 68586
+FontName Helvetica-BoldOblique
+FullName Helvetica Bold Oblique
+FamilyName Helvetica
+Weight Bold
+ItalicAngle -12
+IsFixedPitch false
+CharacterSet ExtendedRoman
+FontBBox -174 -228 1114 962 
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated.  All Rights Reserved.Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries.
+EncodingScheme AdobeStandardEncoding
+CapHeight 718
+XHeight 532
+Ascender 718
+Descender -207
+StdHW 118
+StdVW 140
+StartCharMetrics 315
+C 32 ; WX 278 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 333 ; N exclam ; B 94 0 397 718 ;
+C 34 ; WX 474 ; N quotedbl ; B 193 447 529 718 ;
+C 35 ; WX 556 ; N numbersign ; B 60 0 644 698 ;
+C 36 ; WX 556 ; N dollar ; B 67 -115 622 775 ;
+C 37 ; WX 889 ; N percent ; B 136 -19 901 710 ;
+C 38 ; WX 722 ; N ampersand ; B 89 -19 732 718 ;
+C 39 ; WX 278 ; N quoteright ; B 167 445 362 718 ;
+C 40 ; WX 333 ; N parenleft ; B 76 -208 470 734 ;
+C 41 ; WX 333 ; N parenright ; B -25 -208 369 734 ;
+C 42 ; WX 389 ; N asterisk ; B 146 387 481 718 ;
+C 43 ; WX 584 ; N plus ; B 82 0 610 506 ;
+C 44 ; WX 278 ; N comma ; B 28 -168 245 146 ;
+C 45 ; WX 333 ; N hyphen ; B 73 215 379 345 ;
+C 46 ; WX 278 ; N period ; B 64 0 245 146 ;
+C 47 ; WX 278 ; N slash ; B -37 -19 468 737 ;
+C 48 ; WX 556 ; N zero ; B 86 -19 617 710 ;
+C 49 ; WX 556 ; N one ; B 173 0 529 710 ;
+C 50 ; WX 556 ; N two ; B 26 0 619 710 ;
+C 51 ; WX 556 ; N three ; B 65 -19 608 710 ;
+C 52 ; WX 556 ; N four ; B 60 0 598 710 ;
+C 53 ; WX 556 ; N five ; B 64 -19 636 698 ;
+C 54 ; WX 556 ; N six ; B 85 -19 619 710 ;
+C 55 ; WX 556 ; N seven ; B 125 0 676 698 ;
+C 56 ; WX 556 ; N eight ; B 69 -19 616 710 ;
+C 57 ; WX 556 ; N nine ; B 78 -19 615 710 ;
+C 58 ; WX 333 ; N colon ; B 92 0 351 512 ;
+C 59 ; WX 333 ; N semicolon ; B 56 -168 351 512 ;
+C 60 ; WX 584 ; N less ; B 82 -8 655 514 ;
+C 61 ; WX 584 ; N equal ; B 58 87 633 419 ;
+C 62 ; WX 584 ; N greater ; B 36 -8 609 514 ;
+C 63 ; WX 611 ; N question ; B 165 0 671 727 ;
+C 64 ; WX 975 ; N at ; B 186 -19 954 737 ;
+C 65 ; WX 722 ; N A ; B 20 0 702 718 ;
+C 66 ; WX 722 ; N B ; B 76 0 764 718 ;
+C 67 ; WX 722 ; N C ; B 107 -19 789 737 ;
+C 68 ; WX 722 ; N D ; B 76 0 777 718 ;
+C 69 ; WX 667 ; N E ; B 76 0 757 718 ;
+C 70 ; WX 611 ; N F ; B 76 0 740 718 ;
+C 71 ; WX 778 ; N G ; B 108 -19 817 737 ;
+C 72 ; WX 722 ; N H ; B 71 0 804 718 ;
+C 73 ; WX 278 ; N I ; B 64 0 367 718 ;
+C 74 ; WX 556 ; N J ; B 60 -18 637 718 ;
+C 75 ; WX 722 ; N K ; B 87 0 858 718 ;
+C 76 ; WX 611 ; N L ; B 76 0 611 718 ;
+C 77 ; WX 833 ; N M ; B 69 0 918 718 ;
+C 78 ; WX 722 ; N N ; B 69 0 807 718 ;
+C 79 ; WX 778 ; N O ; B 107 -19 823 737 ;
+C 80 ; WX 667 ; N P ; B 76 0 738 718 ;
+C 81 ; WX 778 ; N Q ; B 107 -52 823 737 ;
+C 82 ; WX 722 ; N R ; B 76 0 778 718 ;
+C 83 ; WX 667 ; N S ; B 81 -19 718 737 ;
+C 84 ; WX 611 ; N T ; B 140 0 751 718 ;
+C 85 ; WX 722 ; N U ; B 116 -19 804 718 ;
+C 86 ; WX 667 ; N V ; B 172 0 801 718 ;
+C 87 ; WX 944 ; N W ; B 169 0 1082 718 ;
+C 88 ; WX 667 ; N X ; B 14 0 791 718 ;
+C 89 ; WX 667 ; N Y ; B 168 0 806 718 ;
+C 90 ; WX 611 ; N Z ; B 25 0 737 718 ;
+C 91 ; WX 333 ; N bracketleft ; B 21 -196 462 722 ;
+C 92 ; WX 278 ; N backslash ; B 124 -19 307 737 ;
+C 93 ; WX 333 ; N bracketright ; B -18 -196 423 722 ;
+C 94 ; WX 584 ; N asciicircum ; B 131 323 591 698 ;
+C 95 ; WX 556 ; N underscore ; B -27 -125 540 -75 ;
+C 96 ; WX 278 ; N quoteleft ; B 165 454 361 727 ;
+C 97 ; WX 556 ; N a ; B 55 -14 583 546 ;
+C 98 ; WX 611 ; N b ; B 61 -14 645 718 ;
+C 99 ; WX 556 ; N c ; B 79 -14 599 546 ;
+C 100 ; WX 611 ; N d ; B 82 -14 704 718 ;
+C 101 ; WX 556 ; N e ; B 70 -14 593 546 ;
+C 102 ; WX 333 ; N f ; B 87 0 469 727 ; L i fi ; L l fl ;
+C 103 ; WX 611 ; N g ; B 38 -217 666 546 ;
+C 104 ; WX 611 ; N h ; B 65 0 629 718 ;
+C 105 ; WX 278 ; N i ; B 69 0 363 725 ;
+C 106 ; WX 278 ; N j ; B -42 -214 363 725 ;
+C 107 ; WX 556 ; N k ; B 69 0 670 718 ;
+C 108 ; WX 278 ; N l ; B 69 0 362 718 ;
+C 109 ; WX 889 ; N m ; B 64 0 909 546 ;
+C 110 ; WX 611 ; N n ; B 65 0 629 546 ;
+C 111 ; WX 611 ; N o ; B 82 -14 643 546 ;
+C 112 ; WX 611 ; N p ; B 18 -207 645 546 ;
+C 113 ; WX 611 ; N q ; B 80 -207 665 546 ;
+C 114 ; WX 389 ; N r ; B 64 0 489 546 ;
+C 115 ; WX 556 ; N s ; B 63 -14 584 546 ;
+C 116 ; WX 333 ; N t ; B 100 -6 422 676 ;
+C 117 ; WX 611 ; N u ; B 98 -14 658 532 ;
+C 118 ; WX 556 ; N v ; B 126 0 656 532 ;
+C 119 ; WX 778 ; N w ; B 123 0 882 532 ;
+C 120 ; WX 556 ; N x ; B 15 0 648 532 ;
+C 121 ; WX 556 ; N y ; B 42 -214 652 532 ;
+C 122 ; WX 500 ; N z ; B 20 0 583 532 ;
+C 123 ; WX 389 ; N braceleft ; B 94 -196 518 722 ;
+C 124 ; WX 280 ; N bar ; B 36 -225 361 775 ;
+C 125 ; WX 389 ; N braceright ; B -18 -196 407 722 ;
+C 126 ; WX 584 ; N asciitilde ; B 115 163 577 343 ;
+C 161 ; WX 333 ; N exclamdown ; B 50 -186 353 532 ;
+C 162 ; WX 556 ; N cent ; B 79 -118 599 628 ;
+C 163 ; WX 556 ; N sterling ; B 50 -16 635 718 ;
+C 164 ; WX 167 ; N fraction ; B -174 -19 487 710 ;
+C 165 ; WX 556 ; N yen ; B 60 0 713 698 ;
+C 166 ; WX 556 ; N florin ; B -50 -210 669 737 ;
+C 167 ; WX 556 ; N section ; B 61 -184 598 727 ;
+C 168 ; WX 556 ; N currency ; B 27 76 680 636 ;
+C 169 ; WX 238 ; N quotesingle ; B 165 447 321 718 ;
+C 170 ; WX 500 ; N quotedblleft ; B 160 454 588 727 ;
+C 171 ; WX 556 ; N guillemotleft ; B 135 76 571 484 ;
+C 172 ; WX 333 ; N guilsinglleft ; B 130 76 353 484 ;
+C 173 ; WX 333 ; N guilsinglright ; B 99 76 322 484 ;
+C 174 ; WX 611 ; N fi ; B 87 0 696 727 ;
+C 175 ; WX 611 ; N fl ; B 87 0 695 727 ;
+C 177 ; WX 556 ; N endash ; B 48 227 627 333 ;
+C 178 ; WX 556 ; N dagger ; B 118 -171 626 718 ;
+C 179 ; WX 556 ; N daggerdbl ; B 46 -171 628 718 ;
+C 180 ; WX 278 ; N periodcentered ; B 110 172 276 334 ;
+C 182 ; WX 556 ; N paragraph ; B 98 -191 688 700 ;
+C 183 ; WX 350 ; N bullet ; B 83 194 420 524 ;
+C 184 ; WX 278 ; N quotesinglbase ; B 41 -146 236 127 ;
+C 185 ; WX 500 ; N quotedblbase ; B 36 -146 463 127 ;
+C 186 ; WX 500 ; N quotedblright ; B 162 445 589 718 ;
+C 187 ; WX 556 ; N guillemotright ; B 104 76 540 484 ;
+C 188 ; WX 1000 ; N ellipsis ; B 92 0 939 146 ;
+C 189 ; WX 1000 ; N perthousand ; B 76 -19 1038 710 ;
+C 191 ; WX 611 ; N questiondown ; B 53 -195 559 532 ;
+C 193 ; WX 333 ; N grave ; B 136 604 353 750 ;
+C 194 ; WX 333 ; N acute ; B 236 604 515 750 ;
+C 195 ; WX 333 ; N circumflex ; B 118 604 471 750 ;
+C 196 ; WX 333 ; N tilde ; B 113 610 507 737 ;
+C 197 ; WX 333 ; N macron ; B 122 604 483 678 ;
+C 198 ; WX 333 ; N breve ; B 156 604 494 750 ;
+C 199 ; WX 333 ; N dotaccent ; B 235 614 385 729 ;
+C 200 ; WX 333 ; N dieresis ; B 137 614 482 729 ;
+C 202 ; WX 333 ; N ring ; B 200 568 420 776 ;
+C 203 ; WX 333 ; N cedilla ; B -37 -228 220 0 ;
+C 205 ; WX 333 ; N hungarumlaut ; B 137 604 645 750 ;
+C 206 ; WX 333 ; N ogonek ; B 41 -228 264 0 ;
+C 207 ; WX 333 ; N caron ; B 149 604 502 750 ;
+C 208 ; WX 1000 ; N emdash ; B 48 227 1071 333 ;
+C 225 ; WX 1000 ; N AE ; B 5 0 1100 718 ;
+C 227 ; WX 370 ; N ordfeminine ; B 125 401 465 737 ;
+C 232 ; WX 611 ; N Lslash ; B 34 0 611 718 ;
+C 233 ; WX 778 ; N Oslash ; B 35 -27 894 745 ;
+C 234 ; WX 1000 ; N OE ; B 99 -19 1114 737 ;
+C 235 ; WX 365 ; N ordmasculine ; B 123 401 485 737 ;
+C 241 ; WX 889 ; N ae ; B 56 -14 923 546 ;
+C 245 ; WX 278 ; N dotlessi ; B 69 0 322 532 ;
+C 248 ; WX 278 ; N lslash ; B 40 0 407 718 ;
+C 249 ; WX 611 ; N oslash ; B 22 -29 701 560 ;
+C 250 ; WX 944 ; N oe ; B 82 -14 977 546 ;
+C 251 ; WX 611 ; N germandbls ; B 69 -14 657 731 ;
+C -1 ; WX 278 ; N Idieresis ; B 64 0 494 915 ;
+C -1 ; WX 556 ; N eacute ; B 70 -14 627 750 ;
+C -1 ; WX 556 ; N abreve ; B 55 -14 606 750 ;
+C -1 ; WX 611 ; N uhungarumlaut ; B 98 -14 784 750 ;
+C -1 ; WX 556 ; N ecaron ; B 70 -14 614 750 ;
+C -1 ; WX 667 ; N Ydieresis ; B 168 0 806 915 ;
+C -1 ; WX 584 ; N divide ; B 82 -42 610 548 ;
+C -1 ; WX 667 ; N Yacute ; B 168 0 806 936 ;
+C -1 ; WX 722 ; N Acircumflex ; B 20 0 706 936 ;
+C -1 ; WX 556 ; N aacute ; B 55 -14 627 750 ;
+C -1 ; WX 722 ; N Ucircumflex ; B 116 -19 804 936 ;
+C -1 ; WX 556 ; N yacute ; B 42 -214 652 750 ;
+C -1 ; WX 556 ; N scommaaccent ; B 63 -228 584 546 ;
+C -1 ; WX 556 ; N ecircumflex ; B 70 -14 593 750 ;
+C -1 ; WX 722 ; N Uring ; B 116 -19 804 962 ;
+C -1 ; WX 722 ; N Udieresis ; B 116 -19 804 915 ;
+C -1 ; WX 556 ; N aogonek ; B 55 -224 583 546 ;
+C -1 ; WX 722 ; N Uacute ; B 116 -19 804 936 ;
+C -1 ; WX 611 ; N uogonek ; B 98 -228 658 532 ;
+C -1 ; WX 667 ; N Edieresis ; B 76 0 757 915 ;
+C -1 ; WX 722 ; N Dcroat ; B 62 0 777 718 ;
+C -1 ; WX 250 ; N commaaccent ; B 16 -228 188 -50 ;
+C -1 ; WX 737 ; N copyright ; B 56 -19 835 737 ;
+C -1 ; WX 667 ; N Emacron ; B 76 0 757 864 ;
+C -1 ; WX 556 ; N ccaron ; B 79 -14 614 750 ;
+C -1 ; WX 556 ; N aring ; B 55 -14 583 776 ;
+C -1 ; WX 722 ; N Ncommaaccent ; B 69 -228 807 718 ;
+C -1 ; WX 278 ; N lacute ; B 69 0 528 936 ;
+C -1 ; WX 556 ; N agrave ; B 55 -14 583 750 ;
+C -1 ; WX 611 ; N Tcommaaccent ; B 140 -228 751 718 ;
+C -1 ; WX 722 ; N Cacute ; B 107 -19 789 936 ;
+C -1 ; WX 556 ; N atilde ; B 55 -14 619 737 ;
+C -1 ; WX 667 ; N Edotaccent ; B 76 0 757 915 ;
+C -1 ; WX 556 ; N scaron ; B 63 -14 614 750 ;
+C -1 ; WX 556 ; N scedilla ; B 63 -228 584 546 ;
+C -1 ; WX 278 ; N iacute ; B 69 0 488 750 ;
+C -1 ; WX 494 ; N lozenge ; B 90 0 564 745 ;
+C -1 ; WX 722 ; N Rcaron ; B 76 0 778 936 ;
+C -1 ; WX 778 ; N Gcommaaccent ; B 108 -228 817 737 ;
+C -1 ; WX 611 ; N ucircumflex ; B 98 -14 658 750 ;
+C -1 ; WX 556 ; N acircumflex ; B 55 -14 583 750 ;
+C -1 ; WX 722 ; N Amacron ; B 20 0 718 864 ;
+C -1 ; WX 389 ; N rcaron ; B 64 0 530 750 ;
+C -1 ; WX 556 ; N ccedilla ; B 79 -228 599 546 ;
+C -1 ; WX 611 ; N Zdotaccent ; B 25 0 737 915 ;
+C -1 ; WX 667 ; N Thorn ; B 76 0 716 718 ;
+C -1 ; WX 778 ; N Omacron ; B 107 -19 823 864 ;
+C -1 ; WX 722 ; N Racute ; B 76 0 778 936 ;
+C -1 ; WX 667 ; N Sacute ; B 81 -19 722 936 ;
+C -1 ; WX 743 ; N dcaron ; B 82 -14 903 718 ;
+C -1 ; WX 722 ; N Umacron ; B 116 -19 804 864 ;
+C -1 ; WX 611 ; N uring ; B 98 -14 658 776 ;
+C -1 ; WX 333 ; N threesuperior ; B 91 271 441 710 ;
+C -1 ; WX 778 ; N Ograve ; B 107 -19 823 936 ;
+C -1 ; WX 722 ; N Agrave ; B 20 0 702 936 ;
+C -1 ; WX 722 ; N Abreve ; B 20 0 729 936 ;
+C -1 ; WX 584 ; N multiply ; B 57 1 635 505 ;
+C -1 ; WX 611 ; N uacute ; B 98 -14 658 750 ;
+C -1 ; WX 611 ; N Tcaron ; B 140 0 751 936 ;
+C -1 ; WX 494 ; N partialdiff ; B 43 -21 585 750 ;
+C -1 ; WX 556 ; N ydieresis ; B 42 -214 652 729 ;
+C -1 ; WX 722 ; N Nacute ; B 69 0 807 936 ;
+C -1 ; WX 278 ; N icircumflex ; B 69 0 444 750 ;
+C -1 ; WX 667 ; N Ecircumflex ; B 76 0 757 936 ;
+C -1 ; WX 556 ; N adieresis ; B 55 -14 594 729 ;
+C -1 ; WX 556 ; N edieresis ; B 70 -14 594 729 ;
+C -1 ; WX 556 ; N cacute ; B 79 -14 627 750 ;
+C -1 ; WX 611 ; N nacute ; B 65 0 654 750 ;
+C -1 ; WX 611 ; N umacron ; B 98 -14 658 678 ;
+C -1 ; WX 722 ; N Ncaron ; B 69 0 807 936 ;
+C -1 ; WX 278 ; N Iacute ; B 64 0 528 936 ;
+C -1 ; WX 584 ; N plusminus ; B 40 0 625 506 ;
+C -1 ; WX 280 ; N brokenbar ; B 52 -150 345 700 ;
+C -1 ; WX 737 ; N registered ; B 55 -19 834 737 ;
+C -1 ; WX 778 ; N Gbreve ; B 108 -19 817 936 ;
+C -1 ; WX 278 ; N Idotaccent ; B 64 0 397 915 ;
+C -1 ; WX 600 ; N summation ; B 14 -10 670 706 ;
+C -1 ; WX 667 ; N Egrave ; B 76 0 757 936 ;
+C -1 ; WX 389 ; N racute ; B 64 0 543 750 ;
+C -1 ; WX 611 ; N omacron ; B 82 -14 643 678 ;
+C -1 ; WX 611 ; N Zacute ; B 25 0 737 936 ;
+C -1 ; WX 611 ; N Zcaron ; B 25 0 737 936 ;
+C -1 ; WX 549 ; N greaterequal ; B 26 0 629 704 ;
+C -1 ; WX 722 ; N Eth ; B 62 0 777 718 ;
+C -1 ; WX 722 ; N Ccedilla ; B 107 -228 789 737 ;
+C -1 ; WX 278 ; N lcommaaccent ; B 30 -228 362 718 ;
+C -1 ; WX 389 ; N tcaron ; B 100 -6 608 878 ;
+C -1 ; WX 556 ; N eogonek ; B 70 -228 593 546 ;
+C -1 ; WX 722 ; N Uogonek ; B 116 -228 804 718 ;
+C -1 ; WX 722 ; N Aacute ; B 20 0 750 936 ;
+C -1 ; WX 722 ; N Adieresis ; B 20 0 716 915 ;
+C -1 ; WX 556 ; N egrave ; B 70 -14 593 750 ;
+C -1 ; WX 500 ; N zacute ; B 20 0 599 750 ;
+C -1 ; WX 278 ; N iogonek ; B -14 -224 363 725 ;
+C -1 ; WX 778 ; N Oacute ; B 107 -19 823 936 ;
+C -1 ; WX 611 ; N oacute ; B 82 -14 654 750 ;
+C -1 ; WX 556 ; N amacron ; B 55 -14 595 678 ;
+C -1 ; WX 556 ; N sacute ; B 63 -14 627 750 ;
+C -1 ; WX 278 ; N idieresis ; B 69 0 455 729 ;
+C -1 ; WX 778 ; N Ocircumflex ; B 107 -19 823 936 ;
+C -1 ; WX 722 ; N Ugrave ; B 116 -19 804 936 ;
+C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C -1 ; WX 611 ; N thorn ; B 18 -208 645 718 ;
+C -1 ; WX 333 ; N twosuperior ; B 69 283 449 710 ;
+C -1 ; WX 778 ; N Odieresis ; B 107 -19 823 915 ;
+C -1 ; WX 611 ; N mu ; B 22 -207 658 532 ;
+C -1 ; WX 278 ; N igrave ; B 69 0 326 750 ;
+C -1 ; WX 611 ; N ohungarumlaut ; B 82 -14 784 750 ;
+C -1 ; WX 667 ; N Eogonek ; B 76 -224 757 718 ;
+C -1 ; WX 611 ; N dcroat ; B 82 -14 789 718 ;
+C -1 ; WX 834 ; N threequarters ; B 99 -19 839 710 ;
+C -1 ; WX 667 ; N Scedilla ; B 81 -228 718 737 ;
+C -1 ; WX 400 ; N lcaron ; B 69 0 561 718 ;
+C -1 ; WX 722 ; N Kcommaaccent ; B 87 -228 858 718 ;
+C -1 ; WX 611 ; N Lacute ; B 76 0 611 936 ;
+C -1 ; WX 1000 ; N trademark ; B 179 306 1109 718 ;
+C -1 ; WX 556 ; N edotaccent ; B 70 -14 593 729 ;
+C -1 ; WX 278 ; N Igrave ; B 64 0 367 936 ;
+C -1 ; WX 278 ; N Imacron ; B 64 0 496 864 ;
+C -1 ; WX 611 ; N Lcaron ; B 76 0 643 718 ;
+C -1 ; WX 834 ; N onehalf ; B 132 -19 858 710 ;
+C -1 ; WX 549 ; N lessequal ; B 29 0 676 704 ;
+C -1 ; WX 611 ; N ocircumflex ; B 82 -14 643 750 ;
+C -1 ; WX 611 ; N ntilde ; B 65 0 646 737 ;
+C -1 ; WX 722 ; N Uhungarumlaut ; B 116 -19 880 936 ;
+C -1 ; WX 667 ; N Eacute ; B 76 0 757 936 ;
+C -1 ; WX 556 ; N emacron ; B 70 -14 595 678 ;
+C -1 ; WX 611 ; N gbreve ; B 38 -217 666 750 ;
+C -1 ; WX 834 ; N onequarter ; B 132 -19 806 710 ;
+C -1 ; WX 667 ; N Scaron ; B 81 -19 718 936 ;
+C -1 ; WX 667 ; N Scommaaccent ; B 81 -228 718 737 ;
+C -1 ; WX 778 ; N Ohungarumlaut ; B 107 -19 908 936 ;
+C -1 ; WX 400 ; N degree ; B 175 426 467 712 ;
+C -1 ; WX 611 ; N ograve ; B 82 -14 643 750 ;
+C -1 ; WX 722 ; N Ccaron ; B 107 -19 789 936 ;
+C -1 ; WX 611 ; N ugrave ; B 98 -14 658 750 ;
+C -1 ; WX 549 ; N radical ; B 112 -46 689 850 ;
+C -1 ; WX 722 ; N Dcaron ; B 76 0 777 936 ;
+C -1 ; WX 389 ; N rcommaaccent ; B 26 -228 489 546 ;
+C -1 ; WX 722 ; N Ntilde ; B 69 0 807 923 ;
+C -1 ; WX 611 ; N otilde ; B 82 -14 646 737 ;
+C -1 ; WX 722 ; N Rcommaaccent ; B 76 -228 778 718 ;
+C -1 ; WX 611 ; N Lcommaaccent ; B 76 -228 611 718 ;
+C -1 ; WX 722 ; N Atilde ; B 20 0 741 923 ;
+C -1 ; WX 722 ; N Aogonek ; B 20 -224 702 718 ;
+C -1 ; WX 722 ; N Aring ; B 20 0 702 962 ;
+C -1 ; WX 778 ; N Otilde ; B 107 -19 823 923 ;
+C -1 ; WX 500 ; N zdotaccent ; B 20 0 583 729 ;
+C -1 ; WX 667 ; N Ecaron ; B 76 0 757 936 ;
+C -1 ; WX 278 ; N Iogonek ; B -41 -228 367 718 ;
+C -1 ; WX 556 ; N kcommaaccent ; B 69 -228 670 718 ;
+C -1 ; WX 584 ; N minus ; B 82 197 610 309 ;
+C -1 ; WX 278 ; N Icircumflex ; B 64 0 484 936 ;
+C -1 ; WX 611 ; N ncaron ; B 65 0 641 750 ;
+C -1 ; WX 333 ; N tcommaaccent ; B 58 -228 422 676 ;
+C -1 ; WX 584 ; N logicalnot ; B 105 108 633 419 ;
+C -1 ; WX 611 ; N odieresis ; B 82 -14 643 729 ;
+C -1 ; WX 611 ; N udieresis ; B 98 -14 658 729 ;
+C -1 ; WX 549 ; N notequal ; B 32 -49 630 570 ;
+C -1 ; WX 611 ; N gcommaaccent ; B 38 -217 666 850 ;
+C -1 ; WX 611 ; N eth ; B 82 -14 670 737 ;
+C -1 ; WX 500 ; N zcaron ; B 20 0 586 750 ;
+C -1 ; WX 611 ; N ncommaaccent ; B 65 -228 629 546 ;
+C -1 ; WX 333 ; N onesuperior ; B 148 283 388 710 ;
+C -1 ; WX 278 ; N imacron ; B 69 0 429 678 ;
+C -1 ; WX 556 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+StartKernData
+StartKernPairs 2481
+KPX A C -40
+KPX A Cacute -40
+KPX A Ccaron -40
+KPX A Ccedilla -40
+KPX A G -50
+KPX A Gbreve -50
+KPX A Gcommaaccent -50
+KPX A O -40
+KPX A Oacute -40
+KPX A Ocircumflex -40
+KPX A Odieresis -40
+KPX A Ograve -40
+KPX A Ohungarumlaut -40
+KPX A Omacron -40
+KPX A Oslash -40
+KPX A Otilde -40
+KPX A Q -40
+KPX A T -90
+KPX A Tcaron -90
+KPX A Tcommaaccent -90
+KPX A U -50
+KPX A Uacute -50
+KPX A Ucircumflex -50
+KPX A Udieresis -50
+KPX A Ugrave -50
+KPX A Uhungarumlaut -50
+KPX A Umacron -50
+KPX A Uogonek -50
+KPX A Uring -50
+KPX A V -80
+KPX A W -60
+KPX A Y -110
+KPX A Yacute -110
+KPX A Ydieresis -110
+KPX A u -30
+KPX A uacute -30
+KPX A ucircumflex -30
+KPX A udieresis -30
+KPX A ugrave -30
+KPX A uhungarumlaut -30
+KPX A umacron -30
+KPX A uogonek -30
+KPX A uring -30
+KPX A v -40
+KPX A w -30
+KPX A y -30
+KPX A yacute -30
+KPX A ydieresis -30
+KPX Aacute C -40
+KPX Aacute Cacute -40
+KPX Aacute Ccaron -40
+KPX Aacute Ccedilla -40
+KPX Aacute G -50
+KPX Aacute Gbreve -50
+KPX Aacute Gcommaaccent -50
+KPX Aacute O -40
+KPX Aacute Oacute -40
+KPX Aacute Ocircumflex -40
+KPX Aacute Odieresis -40
+KPX Aacute Ograve -40
+KPX Aacute Ohungarumlaut -40
+KPX Aacute Omacron -40
+KPX Aacute Oslash -40
+KPX Aacute Otilde -40
+KPX Aacute Q -40
+KPX Aacute T -90
+KPX Aacute Tcaron -90
+KPX Aacute Tcommaaccent -90
+KPX Aacute U -50
+KPX Aacute Uacute -50
+KPX Aacute Ucircumflex -50
+KPX Aacute Udieresis -50
+KPX Aacute Ugrave -50
+KPX Aacute Uhungarumlaut -50
+KPX Aacute Umacron -50
+KPX Aacute Uogonek -50
+KPX Aacute Uring -50
+KPX Aacute V -80
+KPX Aacute W -60
+KPX Aacute Y -110
+KPX Aacute Yacute -110
+KPX Aacute Ydieresis -110
+KPX Aacute u -30
+KPX Aacute uacute -30
+KPX Aacute ucircumflex -30
+KPX Aacute udieresis -30
+KPX Aacute ugrave -30
+KPX Aacute uhungarumlaut -30
+KPX Aacute umacron -30
+KPX Aacute uogonek -30
+KPX Aacute uring -30
+KPX Aacute v -40
+KPX Aacute w -30
+KPX Aacute y -30
+KPX Aacute yacute -30
+KPX Aacute ydieresis -30
+KPX Abreve C -40
+KPX Abreve Cacute -40
+KPX Abreve Ccaron -40
+KPX Abreve Ccedilla -40
+KPX Abreve G -50
+KPX Abreve Gbreve -50
+KPX Abreve Gcommaaccent -50
+KPX Abreve O -40
+KPX Abreve Oacute -40
+KPX Abreve Ocircumflex -40
+KPX Abreve Odieresis -40
+KPX Abreve Ograve -40
+KPX Abreve Ohungarumlaut -40
+KPX Abreve Omacron -40
+KPX Abreve Oslash -40
+KPX Abreve Otilde -40
+KPX Abreve Q -40
+KPX Abreve T -90
+KPX Abreve Tcaron -90
+KPX Abreve Tcommaaccent -90
+KPX Abreve U -50
+KPX Abreve Uacute -50
+KPX Abreve Ucircumflex -50
+KPX Abreve Udieresis -50
+KPX Abreve Ugrave -50
+KPX Abreve Uhungarumlaut -50
+KPX Abreve Umacron -50
+KPX Abreve Uogonek -50
+KPX Abreve Uring -50
+KPX Abreve V -80
+KPX Abreve W -60
+KPX Abreve Y -110
+KPX Abreve Yacute -110
+KPX Abreve Ydieresis -110
+KPX Abreve u -30
+KPX Abreve uacute -30
+KPX Abreve ucircumflex -30
+KPX Abreve udieresis -30
+KPX Abreve ugrave -30
+KPX Abreve uhungarumlaut -30
+KPX Abreve umacron -30
+KPX Abreve uogonek -30
+KPX Abreve uring -30
+KPX Abreve v -40
+KPX Abreve w -30
+KPX Abreve y -30
+KPX Abreve yacute -30
+KPX Abreve ydieresis -30
+KPX Acircumflex C -40
+KPX Acircumflex Cacute -40
+KPX Acircumflex Ccaron -40
+KPX Acircumflex Ccedilla -40
+KPX Acircumflex G -50
+KPX Acircumflex Gbreve -50
+KPX Acircumflex Gcommaaccent -50
+KPX Acircumflex O -40
+KPX Acircumflex Oacute -40
+KPX Acircumflex Ocircumflex -40
+KPX Acircumflex Odieresis -40
+KPX Acircumflex Ograve -40
+KPX Acircumflex Ohungarumlaut -40
+KPX Acircumflex Omacron -40
+KPX Acircumflex Oslash -40
+KPX Acircumflex Otilde -40
+KPX Acircumflex Q -40
+KPX Acircumflex T -90
+KPX Acircumflex Tcaron -90
+KPX Acircumflex Tcommaaccent -90
+KPX Acircumflex U -50
+KPX Acircumflex Uacute -50
+KPX Acircumflex Ucircumflex -50
+KPX Acircumflex Udieresis -50
+KPX Acircumflex Ugrave -50
+KPX Acircumflex Uhungarumlaut -50
+KPX Acircumflex Umacron -50
+KPX Acircumflex Uogonek -50
+KPX Acircumflex Uring -50
+KPX Acircumflex V -80
+KPX Acircumflex W -60
+KPX Acircumflex Y -110
+KPX Acircumflex Yacute -110
+KPX Acircumflex Ydieresis -110
+KPX Acircumflex u -30
+KPX Acircumflex uacute -30
+KPX Acircumflex ucircumflex -30
+KPX Acircumflex udieresis -30
+KPX Acircumflex ugrave -30
+KPX Acircumflex uhungarumlaut -30
+KPX Acircumflex umacron -30
+KPX Acircumflex uogonek -30
+KPX Acircumflex uring -30
+KPX Acircumflex v -40
+KPX Acircumflex w -30
+KPX Acircumflex y -30
+KPX Acircumflex yacute -30
+KPX Acircumflex ydieresis -30
+KPX Adieresis C -40
+KPX Adieresis Cacute -40
+KPX Adieresis Ccaron -40
+KPX Adieresis Ccedilla -40
+KPX Adieresis G -50
+KPX Adieresis Gbreve -50
+KPX Adieresis Gcommaaccent -50
+KPX Adieresis O -40
+KPX Adieresis Oacute -40
+KPX Adieresis Ocircumflex -40
+KPX Adieresis Odieresis -40
+KPX Adieresis Ograve -40
+KPX Adieresis Ohungarumlaut -40
+KPX Adieresis Omacron -40
+KPX Adieresis Oslash -40
+KPX Adieresis Otilde -40
+KPX Adieresis Q -40
+KPX Adieresis T -90
+KPX Adieresis Tcaron -90
+KPX Adieresis Tcommaaccent -90
+KPX Adieresis U -50
+KPX Adieresis Uacute -50
+KPX Adieresis Ucircumflex -50
+KPX Adieresis Udieresis -50
+KPX Adieresis Ugrave -50
+KPX Adieresis Uhungarumlaut -50
+KPX Adieresis Umacron -50
+KPX Adieresis Uogonek -50
+KPX Adieresis Uring -50
+KPX Adieresis V -80
+KPX Adieresis W -60
+KPX Adieresis Y -110
+KPX Adieresis Yacute -110
+KPX Adieresis Ydieresis -110
+KPX Adieresis u -30
+KPX Adieresis uacute -30
+KPX Adieresis ucircumflex -30
+KPX Adieresis udieresis -30
+KPX Adieresis ugrave -30
+KPX Adieresis uhungarumlaut -30
+KPX Adieresis umacron -30
+KPX Adieresis uogonek -30
+KPX Adieresis uring -30
+KPX Adieresis v -40
+KPX Adieresis w -30
+KPX Adieresis y -30
+KPX Adieresis yacute -30
+KPX Adieresis ydieresis -30
+KPX Agrave C -40
+KPX Agrave Cacute -40
+KPX Agrave Ccaron -40
+KPX Agrave Ccedilla -40
+KPX Agrave G -50
+KPX Agrave Gbreve -50
+KPX Agrave Gcommaaccent -50
+KPX Agrave O -40
+KPX Agrave Oacute -40
+KPX Agrave Ocircumflex -40
+KPX Agrave Odieresis -40
+KPX Agrave Ograve -40
+KPX Agrave Ohungarumlaut -40
+KPX Agrave Omacron -40
+KPX Agrave Oslash -40
+KPX Agrave Otilde -40
+KPX Agrave Q -40
+KPX Agrave T -90
+KPX Agrave Tcaron -90
+KPX Agrave Tcommaaccent -90
+KPX Agrave U -50
+KPX Agrave Uacute -50
+KPX Agrave Ucircumflex -50
+KPX Agrave Udieresis -50
+KPX Agrave Ugrave -50
+KPX Agrave Uhungarumlaut -50
+KPX Agrave Umacron -50
+KPX Agrave Uogonek -50
+KPX Agrave Uring -50
+KPX Agrave V -80
+KPX Agrave W -60
+KPX Agrave Y -110
+KPX Agrave Yacute -110
+KPX Agrave Ydieresis -110
+KPX Agrave u -30
+KPX Agrave uacute -30
+KPX Agrave ucircumflex -30
+KPX Agrave udieresis -30
+KPX Agrave ugrave -30
+KPX Agrave uhungarumlaut -30
+KPX Agrave umacron -30
+KPX Agrave uogonek -30
+KPX Agrave uring -30
+KPX Agrave v -40
+KPX Agrave w -30
+KPX Agrave y -30
+KPX Agrave yacute -30
+KPX Agrave ydieresis -30
+KPX Amacron C -40
+KPX Amacron Cacute -40
+KPX Amacron Ccaron -40
+KPX Amacron Ccedilla -40
+KPX Amacron G -50
+KPX Amacron Gbreve -50
+KPX Amacron Gcommaaccent -50
+KPX Amacron O -40
+KPX Amacron Oacute -40
+KPX Amacron Ocircumflex -40
+KPX Amacron Odieresis -40
+KPX Amacron Ograve -40
+KPX Amacron Ohungarumlaut -40
+KPX Amacron Omacron -40
+KPX Amacron Oslash -40
+KPX Amacron Otilde -40
+KPX Amacron Q -40
+KPX Amacron T -90
+KPX Amacron Tcaron -90
+KPX Amacron Tcommaaccent -90
+KPX Amacron U -50
+KPX Amacron Uacute -50
+KPX Amacron Ucircumflex -50
+KPX Amacron Udieresis -50
+KPX Amacron Ugrave -50
+KPX Amacron Uhungarumlaut -50
+KPX Amacron Umacron -50
+KPX Amacron Uogonek -50
+KPX Amacron Uring -50
+KPX Amacron V -80
+KPX Amacron W -60
+KPX Amacron Y -110
+KPX Amacron Yacute -110
+KPX Amacron Ydieresis -110
+KPX Amacron u -30
+KPX Amacron uacute -30
+KPX Amacron ucircumflex -30
+KPX Amacron udieresis -30
+KPX Amacron ugrave -30
+KPX Amacron uhungarumlaut -30
+KPX Amacron umacron -30
+KPX Amacron uogonek -30
+KPX Amacron uring -30
+KPX Amacron v -40
+KPX Amacron w -30
+KPX Amacron y -30
+KPX Amacron yacute -30
+KPX Amacron ydieresis -30
+KPX Aogonek C -40
+KPX Aogonek Cacute -40
+KPX Aogonek Ccaron -40
+KPX Aogonek Ccedilla -40
+KPX Aogonek G -50
+KPX Aogonek Gbreve -50
+KPX Aogonek Gcommaaccent -50
+KPX Aogonek O -40
+KPX Aogonek Oacute -40
+KPX Aogonek Ocircumflex -40
+KPX Aogonek Odieresis -40
+KPX Aogonek Ograve -40
+KPX Aogonek Ohungarumlaut -40
+KPX Aogonek Omacron -40
+KPX Aogonek Oslash -40
+KPX Aogonek Otilde -40
+KPX Aogonek Q -40
+KPX Aogonek T -90
+KPX Aogonek Tcaron -90
+KPX Aogonek Tcommaaccent -90
+KPX Aogonek U -50
+KPX Aogonek Uacute -50
+KPX Aogonek Ucircumflex -50
+KPX Aogonek Udieresis -50
+KPX Aogonek Ugrave -50
+KPX Aogonek Uhungarumlaut -50
+KPX Aogonek Umacron -50
+KPX Aogonek Uogonek -50
+KPX Aogonek Uring -50
+KPX Aogonek V -80
+KPX Aogonek W -60
+KPX Aogonek Y -110
+KPX Aogonek Yacute -110
+KPX Aogonek Ydieresis -110
+KPX Aogonek u -30
+KPX Aogonek uacute -30
+KPX Aogonek ucircumflex -30
+KPX Aogonek udieresis -30
+KPX Aogonek ugrave -30
+KPX Aogonek uhungarumlaut -30
+KPX Aogonek umacron -30
+KPX Aogonek uogonek -30
+KPX Aogonek uring -30
+KPX Aogonek v -40
+KPX Aogonek w -30
+KPX Aogonek y -30
+KPX Aogonek yacute -30
+KPX Aogonek ydieresis -30
+KPX Aring C -40
+KPX Aring Cacute -40
+KPX Aring Ccaron -40
+KPX Aring Ccedilla -40
+KPX Aring G -50
+KPX Aring Gbreve -50
+KPX Aring Gcommaaccent -50
+KPX Aring O -40
+KPX Aring Oacute -40
+KPX Aring Ocircumflex -40
+KPX Aring Odieresis -40
+KPX Aring Ograve -40
+KPX Aring Ohungarumlaut -40
+KPX Aring Omacron -40
+KPX Aring Oslash -40
+KPX Aring Otilde -40
+KPX Aring Q -40
+KPX Aring T -90
+KPX Aring Tcaron -90
+KPX Aring Tcommaaccent -90
+KPX Aring U -50
+KPX Aring Uacute -50
+KPX Aring Ucircumflex -50
+KPX Aring Udieresis -50
+KPX Aring Ugrave -50
+KPX Aring Uhungarumlaut -50
+KPX Aring Umacron -50
+KPX Aring Uogonek -50
+KPX Aring Uring -50
+KPX Aring V -80
+KPX Aring W -60
+KPX Aring Y -110
+KPX Aring Yacute -110
+KPX Aring Ydieresis -110
+KPX Aring u -30
+KPX Aring uacute -30
+KPX Aring ucircumflex -30
+KPX Aring udieresis -30
+KPX Aring ugrave -30
+KPX Aring uhungarumlaut -30
+KPX Aring umacron -30
+KPX Aring uogonek -30
+KPX Aring uring -30
+KPX Aring v -40
+KPX Aring w -30
+KPX Aring y -30
+KPX Aring yacute -30
+KPX Aring ydieresis -30
+KPX Atilde C -40
+KPX Atilde Cacute -40
+KPX Atilde Ccaron -40
+KPX Atilde Ccedilla -40
+KPX Atilde G -50
+KPX Atilde Gbreve -50
+KPX Atilde Gcommaaccent -50
+KPX Atilde O -40
+KPX Atilde Oacute -40
+KPX Atilde Ocircumflex -40
+KPX Atilde Odieresis -40
+KPX Atilde Ograve -40
+KPX Atilde Ohungarumlaut -40
+KPX Atilde Omacron -40
+KPX Atilde Oslash -40
+KPX Atilde Otilde -40
+KPX Atilde Q -40
+KPX Atilde T -90
+KPX Atilde Tcaron -90
+KPX Atilde Tcommaaccent -90
+KPX Atilde U -50
+KPX Atilde Uacute -50
+KPX Atilde Ucircumflex -50
+KPX Atilde Udieresis -50
+KPX Atilde Ugrave -50
+KPX Atilde Uhungarumlaut -50
+KPX Atilde Umacron -50
+KPX Atilde Uogonek -50
+KPX Atilde Uring -50
+KPX Atilde V -80
+KPX Atilde W -60
+KPX Atilde Y -110
+KPX Atilde Yacute -110
+KPX Atilde Ydieresis -110
+KPX Atilde u -30
+KPX Atilde uacute -30
+KPX Atilde ucircumflex -30
+KPX Atilde udieresis -30
+KPX Atilde ugrave -30
+KPX Atilde uhungarumlaut -30
+KPX Atilde umacron -30
+KPX Atilde uogonek -30
+KPX Atilde uring -30
+KPX Atilde v -40
+KPX Atilde w -30
+KPX Atilde y -30
+KPX Atilde yacute -30
+KPX Atilde ydieresis -30
+KPX B A -30
+KPX B Aacute -30
+KPX B Abreve -30
+KPX B Acircumflex -30
+KPX B Adieresis -30
+KPX B Agrave -30
+KPX B Amacron -30
+KPX B Aogonek -30
+KPX B Aring -30
+KPX B Atilde -30
+KPX B U -10
+KPX B Uacute -10
+KPX B Ucircumflex -10
+KPX B Udieresis -10
+KPX B Ugrave -10
+KPX B Uhungarumlaut -10
+KPX B Umacron -10
+KPX B Uogonek -10
+KPX B Uring -10
+KPX D A -40
+KPX D Aacute -40
+KPX D Abreve -40
+KPX D Acircumflex -40
+KPX D Adieresis -40
+KPX D Agrave -40
+KPX D Amacron -40
+KPX D Aogonek -40
+KPX D Aring -40
+KPX D Atilde -40
+KPX D V -40
+KPX D W -40
+KPX D Y -70
+KPX D Yacute -70
+KPX D Ydieresis -70
+KPX D comma -30
+KPX D period -30
+KPX Dcaron A -40
+KPX Dcaron Aacute -40
+KPX Dcaron Abreve -40
+KPX Dcaron Acircumflex -40
+KPX Dcaron Adieresis -40
+KPX Dcaron Agrave -40
+KPX Dcaron Amacron -40
+KPX Dcaron Aogonek -40
+KPX Dcaron Aring -40
+KPX Dcaron Atilde -40
+KPX Dcaron V -40
+KPX Dcaron W -40
+KPX Dcaron Y -70
+KPX Dcaron Yacute -70
+KPX Dcaron Ydieresis -70
+KPX Dcaron comma -30
+KPX Dcaron period -30
+KPX Dcroat A -40
+KPX Dcroat Aacute -40
+KPX Dcroat Abreve -40
+KPX Dcroat Acircumflex -40
+KPX Dcroat Adieresis -40
+KPX Dcroat Agrave -40
+KPX Dcroat Amacron -40
+KPX Dcroat Aogonek -40
+KPX Dcroat Aring -40
+KPX Dcroat Atilde -40
+KPX Dcroat V -40
+KPX Dcroat W -40
+KPX Dcroat Y -70
+KPX Dcroat Yacute -70
+KPX Dcroat Ydieresis -70
+KPX Dcroat comma -30
+KPX Dcroat period -30
+KPX F A -80
+KPX F Aacute -80
+KPX F Abreve -80
+KPX F Acircumflex -80
+KPX F Adieresis -80
+KPX F Agrave -80
+KPX F Amacron -80
+KPX F Aogonek -80
+KPX F Aring -80
+KPX F Atilde -80
+KPX F a -20
+KPX F aacute -20
+KPX F abreve -20
+KPX F acircumflex -20
+KPX F adieresis -20
+KPX F agrave -20
+KPX F amacron -20
+KPX F aogonek -20
+KPX F aring -20
+KPX F atilde -20
+KPX F comma -100
+KPX F period -100
+KPX J A -20
+KPX J Aacute -20
+KPX J Abreve -20
+KPX J Acircumflex -20
+KPX J Adieresis -20
+KPX J Agrave -20
+KPX J Amacron -20
+KPX J Aogonek -20
+KPX J Aring -20
+KPX J Atilde -20
+KPX J comma -20
+KPX J period -20
+KPX J u -20
+KPX J uacute -20
+KPX J ucircumflex -20
+KPX J udieresis -20
+KPX J ugrave -20
+KPX J uhungarumlaut -20
+KPX J umacron -20
+KPX J uogonek -20
+KPX J uring -20
+KPX K O -30
+KPX K Oacute -30
+KPX K Ocircumflex -30
+KPX K Odieresis -30
+KPX K Ograve -30
+KPX K Ohungarumlaut -30
+KPX K Omacron -30
+KPX K Oslash -30
+KPX K Otilde -30
+KPX K e -15
+KPX K eacute -15
+KPX K ecaron -15
+KPX K ecircumflex -15
+KPX K edieresis -15
+KPX K edotaccent -15
+KPX K egrave -15
+KPX K emacron -15
+KPX K eogonek -15
+KPX K o -35
+KPX K oacute -35
+KPX K ocircumflex -35
+KPX K odieresis -35
+KPX K ograve -35
+KPX K ohungarumlaut -35
+KPX K omacron -35
+KPX K oslash -35
+KPX K otilde -35
+KPX K u -30
+KPX K uacute -30
+KPX K ucircumflex -30
+KPX K udieresis -30
+KPX K ugrave -30
+KPX K uhungarumlaut -30
+KPX K umacron -30
+KPX K uogonek -30
+KPX K uring -30
+KPX K y -40
+KPX K yacute -40
+KPX K ydieresis -40
+KPX Kcommaaccent O -30
+KPX Kcommaaccent Oacute -30
+KPX Kcommaaccent Ocircumflex -30
+KPX Kcommaaccent Odieresis -30
+KPX Kcommaaccent Ograve -30
+KPX Kcommaaccent Ohungarumlaut -30
+KPX Kcommaaccent Omacron -30
+KPX Kcommaaccent Oslash -30
+KPX Kcommaaccent Otilde -30
+KPX Kcommaaccent e -15
+KPX Kcommaaccent eacute -15
+KPX Kcommaaccent ecaron -15
+KPX Kcommaaccent ecircumflex -15
+KPX Kcommaaccent edieresis -15
+KPX Kcommaaccent edotaccent -15
+KPX Kcommaaccent egrave -15
+KPX Kcommaaccent emacron -15
+KPX Kcommaaccent eogonek -15
+KPX Kcommaaccent o -35
+KPX Kcommaaccent oacute -35
+KPX Kcommaaccent ocircumflex -35
+KPX Kcommaaccent odieresis -35
+KPX Kcommaaccent ograve -35
+KPX Kcommaaccent ohungarumlaut -35
+KPX Kcommaaccent omacron -35
+KPX Kcommaaccent oslash -35
+KPX Kcommaaccent otilde -35
+KPX Kcommaaccent u -30
+KPX Kcommaaccent uacute -30
+KPX Kcommaaccent ucircumflex -30
+KPX Kcommaaccent udieresis -30
+KPX Kcommaaccent ugrave -30
+KPX Kcommaaccent uhungarumlaut -30
+KPX Kcommaaccent umacron -30
+KPX Kcommaaccent uogonek -30
+KPX Kcommaaccent uring -30
+KPX Kcommaaccent y -40
+KPX Kcommaaccent yacute -40
+KPX Kcommaaccent ydieresis -40
+KPX L T -90
+KPX L Tcaron -90
+KPX L Tcommaaccent -90
+KPX L V -110
+KPX L W -80
+KPX L Y -120
+KPX L Yacute -120
+KPX L Ydieresis -120
+KPX L quotedblright -140
+KPX L quoteright -140
+KPX L y -30
+KPX L yacute -30
+KPX L ydieresis -30
+KPX Lacute T -90
+KPX Lacute Tcaron -90
+KPX Lacute Tcommaaccent -90
+KPX Lacute V -110
+KPX Lacute W -80
+KPX Lacute Y -120
+KPX Lacute Yacute -120
+KPX Lacute Ydieresis -120
+KPX Lacute quotedblright -140
+KPX Lacute quoteright -140
+KPX Lacute y -30
+KPX Lacute yacute -30
+KPX Lacute ydieresis -30
+KPX Lcommaaccent T -90
+KPX Lcommaaccent Tcaron -90
+KPX Lcommaaccent Tcommaaccent -90
+KPX Lcommaaccent V -110
+KPX Lcommaaccent W -80
+KPX Lcommaaccent Y -120
+KPX Lcommaaccent Yacute -120
+KPX Lcommaaccent Ydieresis -120
+KPX Lcommaaccent quotedblright -140
+KPX Lcommaaccent quoteright -140
+KPX Lcommaaccent y -30
+KPX Lcommaaccent yacute -30
+KPX Lcommaaccent ydieresis -30
+KPX Lslash T -90
+KPX Lslash Tcaron -90
+KPX Lslash Tcommaaccent -90
+KPX Lslash V -110
+KPX Lslash W -80
+KPX Lslash Y -120
+KPX Lslash Yacute -120
+KPX Lslash Ydieresis -120
+KPX Lslash quotedblright -140
+KPX Lslash quoteright -140
+KPX Lslash y -30
+KPX Lslash yacute -30
+KPX Lslash ydieresis -30
+KPX O A -50
+KPX O Aacute -50
+KPX O Abreve -50
+KPX O Acircumflex -50
+KPX O Adieresis -50
+KPX O Agrave -50
+KPX O Amacron -50
+KPX O Aogonek -50
+KPX O Aring -50
+KPX O Atilde -50
+KPX O T -40
+KPX O Tcaron -40
+KPX O Tcommaaccent -40
+KPX O V -50
+KPX O W -50
+KPX O X -50
+KPX O Y -70
+KPX O Yacute -70
+KPX O Ydieresis -70
+KPX O comma -40
+KPX O period -40
+KPX Oacute A -50
+KPX Oacute Aacute -50
+KPX Oacute Abreve -50
+KPX Oacute Acircumflex -50
+KPX Oacute Adieresis -50
+KPX Oacute Agrave -50
+KPX Oacute Amacron -50
+KPX Oacute Aogonek -50
+KPX Oacute Aring -50
+KPX Oacute Atilde -50
+KPX Oacute T -40
+KPX Oacute Tcaron -40
+KPX Oacute Tcommaaccent -40
+KPX Oacute V -50
+KPX Oacute W -50
+KPX Oacute X -50
+KPX Oacute Y -70
+KPX Oacute Yacute -70
+KPX Oacute Ydieresis -70
+KPX Oacute comma -40
+KPX Oacute period -40
+KPX Ocircumflex A -50
+KPX Ocircumflex Aacute -50
+KPX Ocircumflex Abreve -50
+KPX Ocircumflex Acircumflex -50
+KPX Ocircumflex Adieresis -50
+KPX Ocircumflex Agrave -50
+KPX Ocircumflex Amacron -50
+KPX Ocircumflex Aogonek -50
+KPX Ocircumflex Aring -50
+KPX Ocircumflex Atilde -50
+KPX Ocircumflex T -40
+KPX Ocircumflex Tcaron -40
+KPX Ocircumflex Tcommaaccent -40
+KPX Ocircumflex V -50
+KPX Ocircumflex W -50
+KPX Ocircumflex X -50
+KPX Ocircumflex Y -70
+KPX Ocircumflex Yacute -70
+KPX Ocircumflex Ydieresis -70
+KPX Ocircumflex comma -40
+KPX Ocircumflex period -40
+KPX Odieresis A -50
+KPX Odieresis Aacute -50
+KPX Odieresis Abreve -50
+KPX Odieresis Acircumflex -50
+KPX Odieresis Adieresis -50
+KPX Odieresis Agrave -50
+KPX Odieresis Amacron -50
+KPX Odieresis Aogonek -50
+KPX Odieresis Aring -50
+KPX Odieresis Atilde -50
+KPX Odieresis T -40
+KPX Odieresis Tcaron -40
+KPX Odieresis Tcommaaccent -40
+KPX Odieresis V -50
+KPX Odieresis W -50
+KPX Odieresis X -50
+KPX Odieresis Y -70
+KPX Odieresis Yacute -70
+KPX Odieresis Ydieresis -70
+KPX Odieresis comma -40
+KPX Odieresis period -40
+KPX Ograve A -50
+KPX Ograve Aacute -50
+KPX Ograve Abreve -50
+KPX Ograve Acircumflex -50
+KPX Ograve Adieresis -50
+KPX Ograve Agrave -50
+KPX Ograve Amacron -50
+KPX Ograve Aogonek -50
+KPX Ograve Aring -50
+KPX Ograve Atilde -50
+KPX Ograve T -40
+KPX Ograve Tcaron -40
+KPX Ograve Tcommaaccent -40
+KPX Ograve V -50
+KPX Ograve W -50
+KPX Ograve X -50
+KPX Ograve Y -70
+KPX Ograve Yacute -70
+KPX Ograve Ydieresis -70
+KPX Ograve comma -40
+KPX Ograve period -40
+KPX Ohungarumlaut A -50
+KPX Ohungarumlaut Aacute -50
+KPX Ohungarumlaut Abreve -50
+KPX Ohungarumlaut Acircumflex -50
+KPX Ohungarumlaut Adieresis -50
+KPX Ohungarumlaut Agrave -50
+KPX Ohungarumlaut Amacron -50
+KPX Ohungarumlaut Aogonek -50
+KPX Ohungarumlaut Aring -50
+KPX Ohungarumlaut Atilde -50
+KPX Ohungarumlaut T -40
+KPX Ohungarumlaut Tcaron -40
+KPX Ohungarumlaut Tcommaaccent -40
+KPX Ohungarumlaut V -50
+KPX Ohungarumlaut W -50
+KPX Ohungarumlaut X -50
+KPX Ohungarumlaut Y -70
+KPX Ohungarumlaut Yacute -70
+KPX Ohungarumlaut Ydieresis -70
+KPX Ohungarumlaut comma -40
+KPX Ohungarumlaut period -40
+KPX Omacron A -50
+KPX Omacron Aacute -50
+KPX Omacron Abreve -50
+KPX Omacron Acircumflex -50
+KPX Omacron Adieresis -50
+KPX Omacron Agrave -50
+KPX Omacron Amacron -50
+KPX Omacron Aogonek -50
+KPX Omacron Aring -50
+KPX Omacron Atilde -50
+KPX Omacron T -40
+KPX Omacron Tcaron -40
+KPX Omacron Tcommaaccent -40
+KPX Omacron V -50
+KPX Omacron W -50
+KPX Omacron X -50
+KPX Omacron Y -70
+KPX Omacron Yacute -70
+KPX Omacron Ydieresis -70
+KPX Omacron comma -40
+KPX Omacron period -40
+KPX Oslash A -50
+KPX Oslash Aacute -50
+KPX Oslash Abreve -50
+KPX Oslash Acircumflex -50
+KPX Oslash Adieresis -50
+KPX Oslash Agrave -50
+KPX Oslash Amacron -50
+KPX Oslash Aogonek -50
+KPX Oslash Aring -50
+KPX Oslash Atilde -50
+KPX Oslash T -40
+KPX Oslash Tcaron -40
+KPX Oslash Tcommaaccent -40
+KPX Oslash V -50
+KPX Oslash W -50
+KPX Oslash X -50
+KPX Oslash Y -70
+KPX Oslash Yacute -70
+KPX Oslash Ydieresis -70
+KPX Oslash comma -40
+KPX Oslash period -40
+KPX Otilde A -50
+KPX Otilde Aacute -50
+KPX Otilde Abreve -50
+KPX Otilde Acircumflex -50
+KPX Otilde Adieresis -50
+KPX Otilde Agrave -50
+KPX Otilde Amacron -50
+KPX Otilde Aogonek -50
+KPX Otilde Aring -50
+KPX Otilde Atilde -50
+KPX Otilde T -40
+KPX Otilde Tcaron -40
+KPX Otilde Tcommaaccent -40
+KPX Otilde V -50
+KPX Otilde W -50
+KPX Otilde X -50
+KPX Otilde Y -70
+KPX Otilde Yacute -70
+KPX Otilde Ydieresis -70
+KPX Otilde comma -40
+KPX Otilde period -40
+KPX P A -100
+KPX P Aacute -100
+KPX P Abreve -100
+KPX P Acircumflex -100
+KPX P Adieresis -100
+KPX P Agrave -100
+KPX P Amacron -100
+KPX P Aogonek -100
+KPX P Aring -100
+KPX P Atilde -100
+KPX P a -30
+KPX P aacute -30
+KPX P abreve -30
+KPX P acircumflex -30
+KPX P adieresis -30
+KPX P agrave -30
+KPX P amacron -30
+KPX P aogonek -30
+KPX P aring -30
+KPX P atilde -30
+KPX P comma -120
+KPX P e -30
+KPX P eacute -30
+KPX P ecaron -30
+KPX P ecircumflex -30
+KPX P edieresis -30
+KPX P edotaccent -30
+KPX P egrave -30
+KPX P emacron -30
+KPX P eogonek -30
+KPX P o -40
+KPX P oacute -40
+KPX P ocircumflex -40
+KPX P odieresis -40
+KPX P ograve -40
+KPX P ohungarumlaut -40
+KPX P omacron -40
+KPX P oslash -40
+KPX P otilde -40
+KPX P period -120
+KPX Q U -10
+KPX Q Uacute -10
+KPX Q Ucircumflex -10
+KPX Q Udieresis -10
+KPX Q Ugrave -10
+KPX Q Uhungarumlaut -10
+KPX Q Umacron -10
+KPX Q Uogonek -10
+KPX Q Uring -10
+KPX Q comma 20
+KPX Q period 20
+KPX R O -20
+KPX R Oacute -20
+KPX R Ocircumflex -20
+KPX R Odieresis -20
+KPX R Ograve -20
+KPX R Ohungarumlaut -20
+KPX R Omacron -20
+KPX R Oslash -20
+KPX R Otilde -20
+KPX R T -20
+KPX R Tcaron -20
+KPX R Tcommaaccent -20
+KPX R U -20
+KPX R Uacute -20
+KPX R Ucircumflex -20
+KPX R Udieresis -20
+KPX R Ugrave -20
+KPX R Uhungarumlaut -20
+KPX R Umacron -20
+KPX R Uogonek -20
+KPX R Uring -20
+KPX R V -50
+KPX R W -40
+KPX R Y -50
+KPX R Yacute -50
+KPX R Ydieresis -50
+KPX Racute O -20
+KPX Racute Oacute -20
+KPX Racute Ocircumflex -20
+KPX Racute Odieresis -20
+KPX Racute Ograve -20
+KPX Racute Ohungarumlaut -20
+KPX Racute Omacron -20
+KPX Racute Oslash -20
+KPX Racute Otilde -20
+KPX Racute T -20
+KPX Racute Tcaron -20
+KPX Racute Tcommaaccent -20
+KPX Racute U -20
+KPX Racute Uacute -20
+KPX Racute Ucircumflex -20
+KPX Racute Udieresis -20
+KPX Racute Ugrave -20
+KPX Racute Uhungarumlaut -20
+KPX Racute Umacron -20
+KPX Racute Uogonek -20
+KPX Racute Uring -20
+KPX Racute V -50
+KPX Racute W -40
+KPX Racute Y -50
+KPX Racute Yacute -50
+KPX Racute Ydieresis -50
+KPX Rcaron O -20
+KPX Rcaron Oacute -20
+KPX Rcaron Ocircumflex -20
+KPX Rcaron Odieresis -20
+KPX Rcaron Ograve -20
+KPX Rcaron Ohungarumlaut -20
+KPX Rcaron Omacron -20
+KPX Rcaron Oslash -20
+KPX Rcaron Otilde -20
+KPX Rcaron T -20
+KPX Rcaron Tcaron -20
+KPX Rcaron Tcommaaccent -20
+KPX Rcaron U -20
+KPX Rcaron Uacute -20
+KPX Rcaron Ucircumflex -20
+KPX Rcaron Udieresis -20
+KPX Rcaron Ugrave -20
+KPX Rcaron Uhungarumlaut -20
+KPX Rcaron Umacron -20
+KPX Rcaron Uogonek -20
+KPX Rcaron Uring -20
+KPX Rcaron V -50
+KPX Rcaron W -40
+KPX Rcaron Y -50
+KPX Rcaron Yacute -50
+KPX Rcaron Ydieresis -50
+KPX Rcommaaccent O -20
+KPX Rcommaaccent Oacute -20
+KPX Rcommaaccent Ocircumflex -20
+KPX Rcommaaccent Odieresis -20
+KPX Rcommaaccent Ograve -20
+KPX Rcommaaccent Ohungarumlaut -20
+KPX Rcommaaccent Omacron -20
+KPX Rcommaaccent Oslash -20
+KPX Rcommaaccent Otilde -20
+KPX Rcommaaccent T -20
+KPX Rcommaaccent Tcaron -20
+KPX Rcommaaccent Tcommaaccent -20
+KPX Rcommaaccent U -20
+KPX Rcommaaccent Uacute -20
+KPX Rcommaaccent Ucircumflex -20
+KPX Rcommaaccent Udieresis -20
+KPX Rcommaaccent Ugrave -20
+KPX Rcommaaccent Uhungarumlaut -20
+KPX Rcommaaccent Umacron -20
+KPX Rcommaaccent Uogonek -20
+KPX Rcommaaccent Uring -20
+KPX Rcommaaccent V -50
+KPX Rcommaaccent W -40
+KPX Rcommaaccent Y -50
+KPX Rcommaaccent Yacute -50
+KPX Rcommaaccent Ydieresis -50
+KPX T A -90
+KPX T Aacute -90
+KPX T Abreve -90
+KPX T Acircumflex -90
+KPX T Adieresis -90
+KPX T Agrave -90
+KPX T Amacron -90
+KPX T Aogonek -90
+KPX T Aring -90
+KPX T Atilde -90
+KPX T O -40
+KPX T Oacute -40
+KPX T Ocircumflex -40
+KPX T Odieresis -40
+KPX T Ograve -40
+KPX T Ohungarumlaut -40
+KPX T Omacron -40
+KPX T Oslash -40
+KPX T Otilde -40
+KPX T a -80
+KPX T aacute -80
+KPX T abreve -80
+KPX T acircumflex -80
+KPX T adieresis -80
+KPX T agrave -80
+KPX T amacron -80
+KPX T aogonek -80
+KPX T aring -80
+KPX T atilde -80
+KPX T colon -40
+KPX T comma -80
+KPX T e -60
+KPX T eacute -60
+KPX T ecaron -60
+KPX T ecircumflex -60
+KPX T edieresis -60
+KPX T edotaccent -60
+KPX T egrave -60
+KPX T emacron -60
+KPX T eogonek -60
+KPX T hyphen -120
+KPX T o -80
+KPX T oacute -80
+KPX T ocircumflex -80
+KPX T odieresis -80
+KPX T ograve -80
+KPX T ohungarumlaut -80
+KPX T omacron -80
+KPX T oslash -80
+KPX T otilde -80
+KPX T period -80
+KPX T r -80
+KPX T racute -80
+KPX T rcommaaccent -80
+KPX T semicolon -40
+KPX T u -90
+KPX T uacute -90
+KPX T ucircumflex -90
+KPX T udieresis -90
+KPX T ugrave -90
+KPX T uhungarumlaut -90
+KPX T umacron -90
+KPX T uogonek -90
+KPX T uring -90
+KPX T w -60
+KPX T y -60
+KPX T yacute -60
+KPX T ydieresis -60
+KPX Tcaron A -90
+KPX Tcaron Aacute -90
+KPX Tcaron Abreve -90
+KPX Tcaron Acircumflex -90
+KPX Tcaron Adieresis -90
+KPX Tcaron Agrave -90
+KPX Tcaron Amacron -90
+KPX Tcaron Aogonek -90
+KPX Tcaron Aring -90
+KPX Tcaron Atilde -90
+KPX Tcaron O -40
+KPX Tcaron Oacute -40
+KPX Tcaron Ocircumflex -40
+KPX Tcaron Odieresis -40
+KPX Tcaron Ograve -40
+KPX Tcaron Ohungarumlaut -40
+KPX Tcaron Omacron -40
+KPX Tcaron Oslash -40
+KPX Tcaron Otilde -40
+KPX Tcaron a -80
+KPX Tcaron aacute -80
+KPX Tcaron abreve -80
+KPX Tcaron acircumflex -80
+KPX Tcaron adieresis -80
+KPX Tcaron agrave -80
+KPX Tcaron amacron -80
+KPX Tcaron aogonek -80
+KPX Tcaron aring -80
+KPX Tcaron atilde -80
+KPX Tcaron colon -40
+KPX Tcaron comma -80
+KPX Tcaron e -60
+KPX Tcaron eacute -60
+KPX Tcaron ecaron -60
+KPX Tcaron ecircumflex -60
+KPX Tcaron edieresis -60
+KPX Tcaron edotaccent -60
+KPX Tcaron egrave -60
+KPX Tcaron emacron -60
+KPX Tcaron eogonek -60
+KPX Tcaron hyphen -120
+KPX Tcaron o -80
+KPX Tcaron oacute -80
+KPX Tcaron ocircumflex -80
+KPX Tcaron odieresis -80
+KPX Tcaron ograve -80
+KPX Tcaron ohungarumlaut -80
+KPX Tcaron omacron -80
+KPX Tcaron oslash -80
+KPX Tcaron otilde -80
+KPX Tcaron period -80
+KPX Tcaron r -80
+KPX Tcaron racute -80
+KPX Tcaron rcommaaccent -80
+KPX Tcaron semicolon -40
+KPX Tcaron u -90
+KPX Tcaron uacute -90
+KPX Tcaron ucircumflex -90
+KPX Tcaron udieresis -90
+KPX Tcaron ugrave -90
+KPX Tcaron uhungarumlaut -90
+KPX Tcaron umacron -90
+KPX Tcaron uogonek -90
+KPX Tcaron uring -90
+KPX Tcaron w -60
+KPX Tcaron y -60
+KPX Tcaron yacute -60
+KPX Tcaron ydieresis -60
+KPX Tcommaaccent A -90
+KPX Tcommaaccent Aacute -90
+KPX Tcommaaccent Abreve -90
+KPX Tcommaaccent Acircumflex -90
+KPX Tcommaaccent Adieresis -90
+KPX Tcommaaccent Agrave -90
+KPX Tcommaaccent Amacron -90
+KPX Tcommaaccent Aogonek -90
+KPX Tcommaaccent Aring -90
+KPX Tcommaaccent Atilde -90
+KPX Tcommaaccent O -40
+KPX Tcommaaccent Oacute -40
+KPX Tcommaaccent Ocircumflex -40
+KPX Tcommaaccent Odieresis -40
+KPX Tcommaaccent Ograve -40
+KPX Tcommaaccent Ohungarumlaut -40
+KPX Tcommaaccent Omacron -40
+KPX Tcommaaccent Oslash -40
+KPX Tcommaaccent Otilde -40
+KPX Tcommaaccent a -80
+KPX Tcommaaccent aacute -80
+KPX Tcommaaccent abreve -80
+KPX Tcommaaccent acircumflex -80
+KPX Tcommaaccent adieresis -80
+KPX Tcommaaccent agrave -80
+KPX Tcommaaccent amacron -80
+KPX Tcommaaccent aogonek -80
+KPX Tcommaaccent aring -80
+KPX Tcommaaccent atilde -80
+KPX Tcommaaccent colon -40
+KPX Tcommaaccent comma -80
+KPX Tcommaaccent e -60
+KPX Tcommaaccent eacute -60
+KPX Tcommaaccent ecaron -60
+KPX Tcommaaccent ecircumflex -60
+KPX Tcommaaccent edieresis -60
+KPX Tcommaaccent edotaccent -60
+KPX Tcommaaccent egrave -60
+KPX Tcommaaccent emacron -60
+KPX Tcommaaccent eogonek -60
+KPX Tcommaaccent hyphen -120
+KPX Tcommaaccent o -80
+KPX Tcommaaccent oacute -80
+KPX Tcommaaccent ocircumflex -80
+KPX Tcommaaccent odieresis -80
+KPX Tcommaaccent ograve -80
+KPX Tcommaaccent ohungarumlaut -80
+KPX Tcommaaccent omacron -80
+KPX Tcommaaccent oslash -80
+KPX Tcommaaccent otilde -80
+KPX Tcommaaccent period -80
+KPX Tcommaaccent r -80
+KPX Tcommaaccent racute -80
+KPX Tcommaaccent rcommaaccent -80
+KPX Tcommaaccent semicolon -40
+KPX Tcommaaccent u -90
+KPX Tcommaaccent uacute -90
+KPX Tcommaaccent ucircumflex -90
+KPX Tcommaaccent udieresis -90
+KPX Tcommaaccent ugrave -90
+KPX Tcommaaccent uhungarumlaut -90
+KPX Tcommaaccent umacron -90
+KPX Tcommaaccent uogonek -90
+KPX Tcommaaccent uring -90
+KPX Tcommaaccent w -60
+KPX Tcommaaccent y -60
+KPX Tcommaaccent yacute -60
+KPX Tcommaaccent ydieresis -60
+KPX U A -50
+KPX U Aacute -50
+KPX U Abreve -50
+KPX U Acircumflex -50
+KPX U Adieresis -50
+KPX U Agrave -50
+KPX U Amacron -50
+KPX U Aogonek -50
+KPX U Aring -50
+KPX U Atilde -50
+KPX U comma -30
+KPX U period -30
+KPX Uacute A -50
+KPX Uacute Aacute -50
+KPX Uacute Abreve -50
+KPX Uacute Acircumflex -50
+KPX Uacute Adieresis -50
+KPX Uacute Agrave -50
+KPX Uacute Amacron -50
+KPX Uacute Aogonek -50
+KPX Uacute Aring -50
+KPX Uacute Atilde -50
+KPX Uacute comma -30
+KPX Uacute period -30
+KPX Ucircumflex A -50
+KPX Ucircumflex Aacute -50
+KPX Ucircumflex Abreve -50
+KPX Ucircumflex Acircumflex -50
+KPX Ucircumflex Adieresis -50
+KPX Ucircumflex Agrave -50
+KPX Ucircumflex Amacron -50
+KPX Ucircumflex Aogonek -50
+KPX Ucircumflex Aring -50
+KPX Ucircumflex Atilde -50
+KPX Ucircumflex comma -30
+KPX Ucircumflex period -30
+KPX Udieresis A -50
+KPX Udieresis Aacute -50
+KPX Udieresis Abreve -50
+KPX Udieresis Acircumflex -50
+KPX Udieresis Adieresis -50
+KPX Udieresis Agrave -50
+KPX Udieresis Amacron -50
+KPX Udieresis Aogonek -50
+KPX Udieresis Aring -50
+KPX Udieresis Atilde -50
+KPX Udieresis comma -30
+KPX Udieresis period -30
+KPX Ugrave A -50
+KPX Ugrave Aacute -50
+KPX Ugrave Abreve -50
+KPX Ugrave Acircumflex -50
+KPX Ugrave Adieresis -50
+KPX Ugrave Agrave -50
+KPX Ugrave Amacron -50
+KPX Ugrave Aogonek -50
+KPX Ugrave Aring -50
+KPX Ugrave Atilde -50
+KPX Ugrave comma -30
+KPX Ugrave period -30
+KPX Uhungarumlaut A -50
+KPX Uhungarumlaut Aacute -50
+KPX Uhungarumlaut Abreve -50
+KPX Uhungarumlaut Acircumflex -50
+KPX Uhungarumlaut Adieresis -50
+KPX Uhungarumlaut Agrave -50
+KPX Uhungarumlaut Amacron -50
+KPX Uhungarumlaut Aogonek -50
+KPX Uhungarumlaut Aring -50
+KPX Uhungarumlaut Atilde -50
+KPX Uhungarumlaut comma -30
+KPX Uhungarumlaut period -30
+KPX Umacron A -50
+KPX Umacron Aacute -50
+KPX Umacron Abreve -50
+KPX Umacron Acircumflex -50
+KPX Umacron Adieresis -50
+KPX Umacron Agrave -50
+KPX Umacron Amacron -50
+KPX Umacron Aogonek -50
+KPX Umacron Aring -50
+KPX Umacron Atilde -50
+KPX Umacron comma -30
+KPX Umacron period -30
+KPX Uogonek A -50
+KPX Uogonek Aacute -50
+KPX Uogonek Abreve -50
+KPX Uogonek Acircumflex -50
+KPX Uogonek Adieresis -50
+KPX Uogonek Agrave -50
+KPX Uogonek Amacron -50
+KPX Uogonek Aogonek -50
+KPX Uogonek Aring -50
+KPX Uogonek Atilde -50
+KPX Uogonek comma -30
+KPX Uogonek period -30
+KPX Uring A -50
+KPX Uring Aacute -50
+KPX Uring Abreve -50
+KPX Uring Acircumflex -50
+KPX Uring Adieresis -50
+KPX Uring Agrave -50
+KPX Uring Amacron -50
+KPX Uring Aogonek -50
+KPX Uring Aring -50
+KPX Uring Atilde -50
+KPX Uring comma -30
+KPX Uring period -30
+KPX V A -80
+KPX V Aacute -80
+KPX V Abreve -80
+KPX V Acircumflex -80
+KPX V Adieresis -80
+KPX V Agrave -80
+KPX V Amacron -80
+KPX V Aogonek -80
+KPX V Aring -80
+KPX V Atilde -80
+KPX V G -50
+KPX V Gbreve -50
+KPX V Gcommaaccent -50
+KPX V O -50
+KPX V Oacute -50
+KPX V Ocircumflex -50
+KPX V Odieresis -50
+KPX V Ograve -50
+KPX V Ohungarumlaut -50
+KPX V Omacron -50
+KPX V Oslash -50
+KPX V Otilde -50
+KPX V a -60
+KPX V aacute -60
+KPX V abreve -60
+KPX V acircumflex -60
+KPX V adieresis -60
+KPX V agrave -60
+KPX V amacron -60
+KPX V aogonek -60
+KPX V aring -60
+KPX V atilde -60
+KPX V colon -40
+KPX V comma -120
+KPX V e -50
+KPX V eacute -50
+KPX V ecaron -50
+KPX V ecircumflex -50
+KPX V edieresis -50
+KPX V edotaccent -50
+KPX V egrave -50
+KPX V emacron -50
+KPX V eogonek -50
+KPX V hyphen -80
+KPX V o -90
+KPX V oacute -90
+KPX V ocircumflex -90
+KPX V odieresis -90
+KPX V ograve -90
+KPX V ohungarumlaut -90
+KPX V omacron -90
+KPX V oslash -90
+KPX V otilde -90
+KPX V period -120
+KPX V semicolon -40
+KPX V u -60
+KPX V uacute -60
+KPX V ucircumflex -60
+KPX V udieresis -60
+KPX V ugrave -60
+KPX V uhungarumlaut -60
+KPX V umacron -60
+KPX V uogonek -60
+KPX V uring -60
+KPX W A -60
+KPX W Aacute -60
+KPX W Abreve -60
+KPX W Acircumflex -60
+KPX W Adieresis -60
+KPX W Agrave -60
+KPX W Amacron -60
+KPX W Aogonek -60
+KPX W Aring -60
+KPX W Atilde -60
+KPX W O -20
+KPX W Oacute -20
+KPX W Ocircumflex -20
+KPX W Odieresis -20
+KPX W Ograve -20
+KPX W Ohungarumlaut -20
+KPX W Omacron -20
+KPX W Oslash -20
+KPX W Otilde -20
+KPX W a -40
+KPX W aacute -40
+KPX W abreve -40
+KPX W acircumflex -40
+KPX W adieresis -40
+KPX W agrave -40
+KPX W amacron -40
+KPX W aogonek -40
+KPX W aring -40
+KPX W atilde -40
+KPX W colon -10
+KPX W comma -80
+KPX W e -35
+KPX W eacute -35
+KPX W ecaron -35
+KPX W ecircumflex -35
+KPX W edieresis -35
+KPX W edotaccent -35
+KPX W egrave -35
+KPX W emacron -35
+KPX W eogonek -35
+KPX W hyphen -40
+KPX W o -60
+KPX W oacute -60
+KPX W ocircumflex -60
+KPX W odieresis -60
+KPX W ograve -60
+KPX W ohungarumlaut -60
+KPX W omacron -60
+KPX W oslash -60
+KPX W otilde -60
+KPX W period -80
+KPX W semicolon -10
+KPX W u -45
+KPX W uacute -45
+KPX W ucircumflex -45
+KPX W udieresis -45
+KPX W ugrave -45
+KPX W uhungarumlaut -45
+KPX W umacron -45
+KPX W uogonek -45
+KPX W uring -45
+KPX W y -20
+KPX W yacute -20
+KPX W ydieresis -20
+KPX Y A -110
+KPX Y Aacute -110
+KPX Y Abreve -110
+KPX Y Acircumflex -110
+KPX Y Adieresis -110
+KPX Y Agrave -110
+KPX Y Amacron -110
+KPX Y Aogonek -110
+KPX Y Aring -110
+KPX Y Atilde -110
+KPX Y O -70
+KPX Y Oacute -70
+KPX Y Ocircumflex -70
+KPX Y Odieresis -70
+KPX Y Ograve -70
+KPX Y Ohungarumlaut -70
+KPX Y Omacron -70
+KPX Y Oslash -70
+KPX Y Otilde -70
+KPX Y a -90
+KPX Y aacute -90
+KPX Y abreve -90
+KPX Y acircumflex -90
+KPX Y adieresis -90
+KPX Y agrave -90
+KPX Y amacron -90
+KPX Y aogonek -90
+KPX Y aring -90
+KPX Y atilde -90
+KPX Y colon -50
+KPX Y comma -100
+KPX Y e -80
+KPX Y eacute -80
+KPX Y ecaron -80
+KPX Y ecircumflex -80
+KPX Y edieresis -80
+KPX Y edotaccent -80
+KPX Y egrave -80
+KPX Y emacron -80
+KPX Y eogonek -80
+KPX Y o -100
+KPX Y oacute -100
+KPX Y ocircumflex -100
+KPX Y odieresis -100
+KPX Y ograve -100
+KPX Y ohungarumlaut -100
+KPX Y omacron -100
+KPX Y oslash -100
+KPX Y otilde -100
+KPX Y period -100
+KPX Y semicolon -50
+KPX Y u -100
+KPX Y uacute -100
+KPX Y ucircumflex -100
+KPX Y udieresis -100
+KPX Y ugrave -100
+KPX Y uhungarumlaut -100
+KPX Y umacron -100
+KPX Y uogonek -100
+KPX Y uring -100
+KPX Yacute A -110
+KPX Yacute Aacute -110
+KPX Yacute Abreve -110
+KPX Yacute Acircumflex -110
+KPX Yacute Adieresis -110
+KPX Yacute Agrave -110
+KPX Yacute Amacron -110
+KPX Yacute Aogonek -110
+KPX Yacute Aring -110
+KPX Yacute Atilde -110
+KPX Yacute O -70
+KPX Yacute Oacute -70
+KPX Yacute Ocircumflex -70
+KPX Yacute Odieresis -70
+KPX Yacute Ograve -70
+KPX Yacute Ohungarumlaut -70
+KPX Yacute Omacron -70
+KPX Yacute Oslash -70
+KPX Yacute Otilde -70
+KPX Yacute a -90
+KPX Yacute aacute -90
+KPX Yacute abreve -90
+KPX Yacute acircumflex -90
+KPX Yacute adieresis -90
+KPX Yacute agrave -90
+KPX Yacute amacron -90
+KPX Yacute aogonek -90
+KPX Yacute aring -90
+KPX Yacute atilde -90
+KPX Yacute colon -50
+KPX Yacute comma -100
+KPX Yacute e -80
+KPX Yacute eacute -80
+KPX Yacute ecaron -80
+KPX Yacute ecircumflex -80
+KPX Yacute edieresis -80
+KPX Yacute edotaccent -80
+KPX Yacute egrave -80
+KPX Yacute emacron -80
+KPX Yacute eogonek -80
+KPX Yacute o -100
+KPX Yacute oacute -100
+KPX Yacute ocircumflex -100
+KPX Yacute odieresis -100
+KPX Yacute ograve -100
+KPX Yacute ohungarumlaut -100
+KPX Yacute omacron -100
+KPX Yacute oslash -100
+KPX Yacute otilde -100
+KPX Yacute period -100
+KPX Yacute semicolon -50
+KPX Yacute u -100
+KPX Yacute uacute -100
+KPX Yacute ucircumflex -100
+KPX Yacute udieresis -100
+KPX Yacute ugrave -100
+KPX Yacute uhungarumlaut -100
+KPX Yacute umacron -100
+KPX Yacute uogonek -100
+KPX Yacute uring -100
+KPX Ydieresis A -110
+KPX Ydieresis Aacute -110
+KPX Ydieresis Abreve -110
+KPX Ydieresis Acircumflex -110
+KPX Ydieresis Adieresis -110
+KPX Ydieresis Agrave -110
+KPX Ydieresis Amacron -110
+KPX Ydieresis Aogonek -110
+KPX Ydieresis Aring -110
+KPX Ydieresis Atilde -110
+KPX Ydieresis O -70
+KPX Ydieresis Oacute -70
+KPX Ydieresis Ocircumflex -70
+KPX Ydieresis Odieresis -70
+KPX Ydieresis Ograve -70
+KPX Ydieresis Ohungarumlaut -70
+KPX Ydieresis Omacron -70
+KPX Ydieresis Oslash -70
+KPX Ydieresis Otilde -70
+KPX Ydieresis a -90
+KPX Ydieresis aacute -90
+KPX Ydieresis abreve -90
+KPX Ydieresis acircumflex -90
+KPX Ydieresis adieresis -90
+KPX Ydieresis agrave -90
+KPX Ydieresis amacron -90
+KPX Ydieresis aogonek -90
+KPX Ydieresis aring -90
+KPX Ydieresis atilde -90
+KPX Ydieresis colon -50
+KPX Ydieresis comma -100
+KPX Ydieresis e -80
+KPX Ydieresis eacute -80
+KPX Ydieresis ecaron -80
+KPX Ydieresis ecircumflex -80
+KPX Ydieresis edieresis -80
+KPX Ydieresis edotaccent -80
+KPX Ydieresis egrave -80
+KPX Ydieresis emacron -80
+KPX Ydieresis eogonek -80
+KPX Ydieresis o -100
+KPX Ydieresis oacute -100
+KPX Ydieresis ocircumflex -100
+KPX Ydieresis odieresis -100
+KPX Ydieresis ograve -100
+KPX Ydieresis ohungarumlaut -100
+KPX Ydieresis omacron -100
+KPX Ydieresis oslash -100
+KPX Ydieresis otilde -100
+KPX Ydieresis period -100
+KPX Ydieresis semicolon -50
+KPX Ydieresis u -100
+KPX Ydieresis uacute -100
+KPX Ydieresis ucircumflex -100
+KPX Ydieresis udieresis -100
+KPX Ydieresis ugrave -100
+KPX Ydieresis uhungarumlaut -100
+KPX Ydieresis umacron -100
+KPX Ydieresis uogonek -100
+KPX Ydieresis uring -100
+KPX a g -10
+KPX a gbreve -10
+KPX a gcommaaccent -10
+KPX a v -15
+KPX a w -15
+KPX a y -20
+KPX a yacute -20
+KPX a ydieresis -20
+KPX aacute g -10
+KPX aacute gbreve -10
+KPX aacute gcommaaccent -10
+KPX aacute v -15
+KPX aacute w -15
+KPX aacute y -20
+KPX aacute yacute -20
+KPX aacute ydieresis -20
+KPX abreve g -10
+KPX abreve gbreve -10
+KPX abreve gcommaaccent -10
+KPX abreve v -15
+KPX abreve w -15
+KPX abreve y -20
+KPX abreve yacute -20
+KPX abreve ydieresis -20
+KPX acircumflex g -10
+KPX acircumflex gbreve -10
+KPX acircumflex gcommaaccent -10
+KPX acircumflex v -15
+KPX acircumflex w -15
+KPX acircumflex y -20
+KPX acircumflex yacute -20
+KPX acircumflex ydieresis -20
+KPX adieresis g -10
+KPX adieresis gbreve -10
+KPX adieresis gcommaaccent -10
+KPX adieresis v -15
+KPX adieresis w -15
+KPX adieresis y -20
+KPX adieresis yacute -20
+KPX adieresis ydieresis -20
+KPX agrave g -10
+KPX agrave gbreve -10
+KPX agrave gcommaaccent -10
+KPX agrave v -15
+KPX agrave w -15
+KPX agrave y -20
+KPX agrave yacute -20
+KPX agrave ydieresis -20
+KPX amacron g -10
+KPX amacron gbreve -10
+KPX amacron gcommaaccent -10
+KPX amacron v -15
+KPX amacron w -15
+KPX amacron y -20
+KPX amacron yacute -20
+KPX amacron ydieresis -20
+KPX aogonek g -10
+KPX aogonek gbreve -10
+KPX aogonek gcommaaccent -10
+KPX aogonek v -15
+KPX aogonek w -15
+KPX aogonek y -20
+KPX aogonek yacute -20
+KPX aogonek ydieresis -20
+KPX aring g -10
+KPX aring gbreve -10
+KPX aring gcommaaccent -10
+KPX aring v -15
+KPX aring w -15
+KPX aring y -20
+KPX aring yacute -20
+KPX aring ydieresis -20
+KPX atilde g -10
+KPX atilde gbreve -10
+KPX atilde gcommaaccent -10
+KPX atilde v -15
+KPX atilde w -15
+KPX atilde y -20
+KPX atilde yacute -20
+KPX atilde ydieresis -20
+KPX b l -10
+KPX b lacute -10
+KPX b lcommaaccent -10
+KPX b lslash -10
+KPX b u -20
+KPX b uacute -20
+KPX b ucircumflex -20
+KPX b udieresis -20
+KPX b ugrave -20
+KPX b uhungarumlaut -20
+KPX b umacron -20
+KPX b uogonek -20
+KPX b uring -20
+KPX b v -20
+KPX b y -20
+KPX b yacute -20
+KPX b ydieresis -20
+KPX c h -10
+KPX c k -20
+KPX c kcommaaccent -20
+KPX c l -20
+KPX c lacute -20
+KPX c lcommaaccent -20
+KPX c lslash -20
+KPX c y -10
+KPX c yacute -10
+KPX c ydieresis -10
+KPX cacute h -10
+KPX cacute k -20
+KPX cacute kcommaaccent -20
+KPX cacute l -20
+KPX cacute lacute -20
+KPX cacute lcommaaccent -20
+KPX cacute lslash -20
+KPX cacute y -10
+KPX cacute yacute -10
+KPX cacute ydieresis -10
+KPX ccaron h -10
+KPX ccaron k -20
+KPX ccaron kcommaaccent -20
+KPX ccaron l -20
+KPX ccaron lacute -20
+KPX ccaron lcommaaccent -20
+KPX ccaron lslash -20
+KPX ccaron y -10
+KPX ccaron yacute -10
+KPX ccaron ydieresis -10
+KPX ccedilla h -10
+KPX ccedilla k -20
+KPX ccedilla kcommaaccent -20
+KPX ccedilla l -20
+KPX ccedilla lacute -20
+KPX ccedilla lcommaaccent -20
+KPX ccedilla lslash -20
+KPX ccedilla y -10
+KPX ccedilla yacute -10
+KPX ccedilla ydieresis -10
+KPX colon space -40
+KPX comma quotedblright -120
+KPX comma quoteright -120
+KPX comma space -40
+KPX d d -10
+KPX d dcroat -10
+KPX d v -15
+KPX d w -15
+KPX d y -15
+KPX d yacute -15
+KPX d ydieresis -15
+KPX dcroat d -10
+KPX dcroat dcroat -10
+KPX dcroat v -15
+KPX dcroat w -15
+KPX dcroat y -15
+KPX dcroat yacute -15
+KPX dcroat ydieresis -15
+KPX e comma 10
+KPX e period 20
+KPX e v -15
+KPX e w -15
+KPX e x -15
+KPX e y -15
+KPX e yacute -15
+KPX e ydieresis -15
+KPX eacute comma 10
+KPX eacute period 20
+KPX eacute v -15
+KPX eacute w -15
+KPX eacute x -15
+KPX eacute y -15
+KPX eacute yacute -15
+KPX eacute ydieresis -15
+KPX ecaron comma 10
+KPX ecaron period 20
+KPX ecaron v -15
+KPX ecaron w -15
+KPX ecaron x -15
+KPX ecaron y -15
+KPX ecaron yacute -15
+KPX ecaron ydieresis -15
+KPX ecircumflex comma 10
+KPX ecircumflex period 20
+KPX ecircumflex v -15
+KPX ecircumflex w -15
+KPX ecircumflex x -15
+KPX ecircumflex y -15
+KPX ecircumflex yacute -15
+KPX ecircumflex ydieresis -15
+KPX edieresis comma 10
+KPX edieresis period 20
+KPX edieresis v -15
+KPX edieresis w -15
+KPX edieresis x -15
+KPX edieresis y -15
+KPX edieresis yacute -15
+KPX edieresis ydieresis -15
+KPX edotaccent comma 10
+KPX edotaccent period 20
+KPX edotaccent v -15
+KPX edotaccent w -15
+KPX edotaccent x -15
+KPX edotaccent y -15
+KPX edotaccent yacute -15
+KPX edotaccent ydieresis -15
+KPX egrave comma 10
+KPX egrave period 20
+KPX egrave v -15
+KPX egrave w -15
+KPX egrave x -15
+KPX egrave y -15
+KPX egrave yacute -15
+KPX egrave ydieresis -15
+KPX emacron comma 10
+KPX emacron period 20
+KPX emacron v -15
+KPX emacron w -15
+KPX emacron x -15
+KPX emacron y -15
+KPX emacron yacute -15
+KPX emacron ydieresis -15
+KPX eogonek comma 10
+KPX eogonek period 20
+KPX eogonek v -15
+KPX eogonek w -15
+KPX eogonek x -15
+KPX eogonek y -15
+KPX eogonek yacute -15
+KPX eogonek ydieresis -15
+KPX f comma -10
+KPX f e -10
+KPX f eacute -10
+KPX f ecaron -10
+KPX f ecircumflex -10
+KPX f edieresis -10
+KPX f edotaccent -10
+KPX f egrave -10
+KPX f emacron -10
+KPX f eogonek -10
+KPX f o -20
+KPX f oacute -20
+KPX f ocircumflex -20
+KPX f odieresis -20
+KPX f ograve -20
+KPX f ohungarumlaut -20
+KPX f omacron -20
+KPX f oslash -20
+KPX f otilde -20
+KPX f period -10
+KPX f quotedblright 30
+KPX f quoteright 30
+KPX g e 10
+KPX g eacute 10
+KPX g ecaron 10
+KPX g ecircumflex 10
+KPX g edieresis 10
+KPX g edotaccent 10
+KPX g egrave 10
+KPX g emacron 10
+KPX g eogonek 10
+KPX g g -10
+KPX g gbreve -10
+KPX g gcommaaccent -10
+KPX gbreve e 10
+KPX gbreve eacute 10
+KPX gbreve ecaron 10
+KPX gbreve ecircumflex 10
+KPX gbreve edieresis 10
+KPX gbreve edotaccent 10
+KPX gbreve egrave 10
+KPX gbreve emacron 10
+KPX gbreve eogonek 10
+KPX gbreve g -10
+KPX gbreve gbreve -10
+KPX gbreve gcommaaccent -10
+KPX gcommaaccent e 10
+KPX gcommaaccent eacute 10
+KPX gcommaaccent ecaron 10
+KPX gcommaaccent ecircumflex 10
+KPX gcommaaccent edieresis 10
+KPX gcommaaccent edotaccent 10
+KPX gcommaaccent egrave 10
+KPX gcommaaccent emacron 10
+KPX gcommaaccent eogonek 10
+KPX gcommaaccent g -10
+KPX gcommaaccent gbreve -10
+KPX gcommaaccent gcommaaccent -10
+KPX h y -20
+KPX h yacute -20
+KPX h ydieresis -20
+KPX k o -15
+KPX k oacute -15
+KPX k ocircumflex -15
+KPX k odieresis -15
+KPX k ograve -15
+KPX k ohungarumlaut -15
+KPX k omacron -15
+KPX k oslash -15
+KPX k otilde -15
+KPX kcommaaccent o -15
+KPX kcommaaccent oacute -15
+KPX kcommaaccent ocircumflex -15
+KPX kcommaaccent odieresis -15
+KPX kcommaaccent ograve -15
+KPX kcommaaccent ohungarumlaut -15
+KPX kcommaaccent omacron -15
+KPX kcommaaccent oslash -15
+KPX kcommaaccent otilde -15
+KPX l w -15
+KPX l y -15
+KPX l yacute -15
+KPX l ydieresis -15
+KPX lacute w -15
+KPX lacute y -15
+KPX lacute yacute -15
+KPX lacute ydieresis -15
+KPX lcommaaccent w -15
+KPX lcommaaccent y -15
+KPX lcommaaccent yacute -15
+KPX lcommaaccent ydieresis -15
+KPX lslash w -15
+KPX lslash y -15
+KPX lslash yacute -15
+KPX lslash ydieresis -15
+KPX m u -20
+KPX m uacute -20
+KPX m ucircumflex -20
+KPX m udieresis -20
+KPX m ugrave -20
+KPX m uhungarumlaut -20
+KPX m umacron -20
+KPX m uogonek -20
+KPX m uring -20
+KPX m y -30
+KPX m yacute -30
+KPX m ydieresis -30
+KPX n u -10
+KPX n uacute -10
+KPX n ucircumflex -10
+KPX n udieresis -10
+KPX n ugrave -10
+KPX n uhungarumlaut -10
+KPX n umacron -10
+KPX n uogonek -10
+KPX n uring -10
+KPX n v -40
+KPX n y -20
+KPX n yacute -20
+KPX n ydieresis -20
+KPX nacute u -10
+KPX nacute uacute -10
+KPX nacute ucircumflex -10
+KPX nacute udieresis -10
+KPX nacute ugrave -10
+KPX nacute uhungarumlaut -10
+KPX nacute umacron -10
+KPX nacute uogonek -10
+KPX nacute uring -10
+KPX nacute v -40
+KPX nacute y -20
+KPX nacute yacute -20
+KPX nacute ydieresis -20
+KPX ncaron u -10
+KPX ncaron uacute -10
+KPX ncaron ucircumflex -10
+KPX ncaron udieresis -10
+KPX ncaron ugrave -10
+KPX ncaron uhungarumlaut -10
+KPX ncaron umacron -10
+KPX ncaron uogonek -10
+KPX ncaron uring -10
+KPX ncaron v -40
+KPX ncaron y -20
+KPX ncaron yacute -20
+KPX ncaron ydieresis -20
+KPX ncommaaccent u -10
+KPX ncommaaccent uacute -10
+KPX ncommaaccent ucircumflex -10
+KPX ncommaaccent udieresis -10
+KPX ncommaaccent ugrave -10
+KPX ncommaaccent uhungarumlaut -10
+KPX ncommaaccent umacron -10
+KPX ncommaaccent uogonek -10
+KPX ncommaaccent uring -10
+KPX ncommaaccent v -40
+KPX ncommaaccent y -20
+KPX ncommaaccent yacute -20
+KPX ncommaaccent ydieresis -20
+KPX ntilde u -10
+KPX ntilde uacute -10
+KPX ntilde ucircumflex -10
+KPX ntilde udieresis -10
+KPX ntilde ugrave -10
+KPX ntilde uhungarumlaut -10
+KPX ntilde umacron -10
+KPX ntilde uogonek -10
+KPX ntilde uring -10
+KPX ntilde v -40
+KPX ntilde y -20
+KPX ntilde yacute -20
+KPX ntilde ydieresis -20
+KPX o v -20
+KPX o w -15
+KPX o x -30
+KPX o y -20
+KPX o yacute -20
+KPX o ydieresis -20
+KPX oacute v -20
+KPX oacute w -15
+KPX oacute x -30
+KPX oacute y -20
+KPX oacute yacute -20
+KPX oacute ydieresis -20
+KPX ocircumflex v -20
+KPX ocircumflex w -15
+KPX ocircumflex x -30
+KPX ocircumflex y -20
+KPX ocircumflex yacute -20
+KPX ocircumflex ydieresis -20
+KPX odieresis v -20
+KPX odieresis w -15
+KPX odieresis x -30
+KPX odieresis y -20
+KPX odieresis yacute -20
+KPX odieresis ydieresis -20
+KPX ograve v -20
+KPX ograve w -15
+KPX ograve x -30
+KPX ograve y -20
+KPX ograve yacute -20
+KPX ograve ydieresis -20
+KPX ohungarumlaut v -20
+KPX ohungarumlaut w -15
+KPX ohungarumlaut x -30
+KPX ohungarumlaut y -20
+KPX ohungarumlaut yacute -20
+KPX ohungarumlaut ydieresis -20
+KPX omacron v -20
+KPX omacron w -15
+KPX omacron x -30
+KPX omacron y -20
+KPX omacron yacute -20
+KPX omacron ydieresis -20
+KPX oslash v -20
+KPX oslash w -15
+KPX oslash x -30
+KPX oslash y -20
+KPX oslash yacute -20
+KPX oslash ydieresis -20
+KPX otilde v -20
+KPX otilde w -15
+KPX otilde x -30
+KPX otilde y -20
+KPX otilde yacute -20
+KPX otilde ydieresis -20
+KPX p y -15
+KPX p yacute -15
+KPX p ydieresis -15
+KPX period quotedblright -120
+KPX period quoteright -120
+KPX period space -40
+KPX quotedblright space -80
+KPX quoteleft quoteleft -46
+KPX quoteright d -80
+KPX quoteright dcroat -80
+KPX quoteright l -20
+KPX quoteright lacute -20
+KPX quoteright lcommaaccent -20
+KPX quoteright lslash -20
+KPX quoteright quoteright -46
+KPX quoteright r -40
+KPX quoteright racute -40
+KPX quoteright rcaron -40
+KPX quoteright rcommaaccent -40
+KPX quoteright s -60
+KPX quoteright sacute -60
+KPX quoteright scaron -60
+KPX quoteright scedilla -60
+KPX quoteright scommaaccent -60
+KPX quoteright space -80
+KPX quoteright v -20
+KPX r c -20
+KPX r cacute -20
+KPX r ccaron -20
+KPX r ccedilla -20
+KPX r comma -60
+KPX r d -20
+KPX r dcroat -20
+KPX r g -15
+KPX r gbreve -15
+KPX r gcommaaccent -15
+KPX r hyphen -20
+KPX r o -20
+KPX r oacute -20
+KPX r ocircumflex -20
+KPX r odieresis -20
+KPX r ograve -20
+KPX r ohungarumlaut -20
+KPX r omacron -20
+KPX r oslash -20
+KPX r otilde -20
+KPX r period -60
+KPX r q -20
+KPX r s -15
+KPX r sacute -15
+KPX r scaron -15
+KPX r scedilla -15
+KPX r scommaaccent -15
+KPX r t 20
+KPX r tcommaaccent 20
+KPX r v 10
+KPX r y 10
+KPX r yacute 10
+KPX r ydieresis 10
+KPX racute c -20
+KPX racute cacute -20
+KPX racute ccaron -20
+KPX racute ccedilla -20
+KPX racute comma -60
+KPX racute d -20
+KPX racute dcroat -20
+KPX racute g -15
+KPX racute gbreve -15
+KPX racute gcommaaccent -15
+KPX racute hyphen -20
+KPX racute o -20
+KPX racute oacute -20
+KPX racute ocircumflex -20
+KPX racute odieresis -20
+KPX racute ograve -20
+KPX racute ohungarumlaut -20
+KPX racute omacron -20
+KPX racute oslash -20
+KPX racute otilde -20
+KPX racute period -60
+KPX racute q -20
+KPX racute s -15
+KPX racute sacute -15
+KPX racute scaron -15
+KPX racute scedilla -15
+KPX racute scommaaccent -15
+KPX racute t 20
+KPX racute tcommaaccent 20
+KPX racute v 10
+KPX racute y 10
+KPX racute yacute 10
+KPX racute ydieresis 10
+KPX rcaron c -20
+KPX rcaron cacute -20
+KPX rcaron ccaron -20
+KPX rcaron ccedilla -20
+KPX rcaron comma -60
+KPX rcaron d -20
+KPX rcaron dcroat -20
+KPX rcaron g -15
+KPX rcaron gbreve -15
+KPX rcaron gcommaaccent -15
+KPX rcaron hyphen -20
+KPX rcaron o -20
+KPX rcaron oacute -20
+KPX rcaron ocircumflex -20
+KPX rcaron odieresis -20
+KPX rcaron ograve -20
+KPX rcaron ohungarumlaut -20
+KPX rcaron omacron -20
+KPX rcaron oslash -20
+KPX rcaron otilde -20
+KPX rcaron period -60
+KPX rcaron q -20
+KPX rcaron s -15
+KPX rcaron sacute -15
+KPX rcaron scaron -15
+KPX rcaron scedilla -15
+KPX rcaron scommaaccent -15
+KPX rcaron t 20
+KPX rcaron tcommaaccent 20
+KPX rcaron v 10
+KPX rcaron y 10
+KPX rcaron yacute 10
+KPX rcaron ydieresis 10
+KPX rcommaaccent c -20
+KPX rcommaaccent cacute -20
+KPX rcommaaccent ccaron -20
+KPX rcommaaccent ccedilla -20
+KPX rcommaaccent comma -60
+KPX rcommaaccent d -20
+KPX rcommaaccent dcroat -20
+KPX rcommaaccent g -15
+KPX rcommaaccent gbreve -15
+KPX rcommaaccent gcommaaccent -15
+KPX rcommaaccent hyphen -20
+KPX rcommaaccent o -20
+KPX rcommaaccent oacute -20
+KPX rcommaaccent ocircumflex -20
+KPX rcommaaccent odieresis -20
+KPX rcommaaccent ograve -20
+KPX rcommaaccent ohungarumlaut -20
+KPX rcommaaccent omacron -20
+KPX rcommaaccent oslash -20
+KPX rcommaaccent otilde -20
+KPX rcommaaccent period -60
+KPX rcommaaccent q -20
+KPX rcommaaccent s -15
+KPX rcommaaccent sacute -15
+KPX rcommaaccent scaron -15
+KPX rcommaaccent scedilla -15
+KPX rcommaaccent scommaaccent -15
+KPX rcommaaccent t 20
+KPX rcommaaccent tcommaaccent 20
+KPX rcommaaccent v 10
+KPX rcommaaccent y 10
+KPX rcommaaccent yacute 10
+KPX rcommaaccent ydieresis 10
+KPX s w -15
+KPX sacute w -15
+KPX scaron w -15
+KPX scedilla w -15
+KPX scommaaccent w -15
+KPX semicolon space -40
+KPX space T -100
+KPX space Tcaron -100
+KPX space Tcommaaccent -100
+KPX space V -80
+KPX space W -80
+KPX space Y -120
+KPX space Yacute -120
+KPX space Ydieresis -120
+KPX space quotedblleft -80
+KPX space quoteleft -60
+KPX v a -20
+KPX v aacute -20
+KPX v abreve -20
+KPX v acircumflex -20
+KPX v adieresis -20
+KPX v agrave -20
+KPX v amacron -20
+KPX v aogonek -20
+KPX v aring -20
+KPX v atilde -20
+KPX v comma -80
+KPX v o -30
+KPX v oacute -30
+KPX v ocircumflex -30
+KPX v odieresis -30
+KPX v ograve -30
+KPX v ohungarumlaut -30
+KPX v omacron -30
+KPX v oslash -30
+KPX v otilde -30
+KPX v period -80
+KPX w comma -40
+KPX w o -20
+KPX w oacute -20
+KPX w ocircumflex -20
+KPX w odieresis -20
+KPX w ograve -20
+KPX w ohungarumlaut -20
+KPX w omacron -20
+KPX w oslash -20
+KPX w otilde -20
+KPX w period -40
+KPX x e -10
+KPX x eacute -10
+KPX x ecaron -10
+KPX x ecircumflex -10
+KPX x edieresis -10
+KPX x edotaccent -10
+KPX x egrave -10
+KPX x emacron -10
+KPX x eogonek -10
+KPX y a -30
+KPX y aacute -30
+KPX y abreve -30
+KPX y acircumflex -30
+KPX y adieresis -30
+KPX y agrave -30
+KPX y amacron -30
+KPX y aogonek -30
+KPX y aring -30
+KPX y atilde -30
+KPX y comma -80
+KPX y e -10
+KPX y eacute -10
+KPX y ecaron -10
+KPX y ecircumflex -10
+KPX y edieresis -10
+KPX y edotaccent -10
+KPX y egrave -10
+KPX y emacron -10
+KPX y eogonek -10
+KPX y o -25
+KPX y oacute -25
+KPX y ocircumflex -25
+KPX y odieresis -25
+KPX y ograve -25
+KPX y ohungarumlaut -25
+KPX y omacron -25
+KPX y oslash -25
+KPX y otilde -25
+KPX y period -80
+KPX yacute a -30
+KPX yacute aacute -30
+KPX yacute abreve -30
+KPX yacute acircumflex -30
+KPX yacute adieresis -30
+KPX yacute agrave -30
+KPX yacute amacron -30
+KPX yacute aogonek -30
+KPX yacute aring -30
+KPX yacute atilde -30
+KPX yacute comma -80
+KPX yacute e -10
+KPX yacute eacute -10
+KPX yacute ecaron -10
+KPX yacute ecircumflex -10
+KPX yacute edieresis -10
+KPX yacute edotaccent -10
+KPX yacute egrave -10
+KPX yacute emacron -10
+KPX yacute eogonek -10
+KPX yacute o -25
+KPX yacute oacute -25
+KPX yacute ocircumflex -25
+KPX yacute odieresis -25
+KPX yacute ograve -25
+KPX yacute ohungarumlaut -25
+KPX yacute omacron -25
+KPX yacute oslash -25
+KPX yacute otilde -25
+KPX yacute period -80
+KPX ydieresis a -30
+KPX ydieresis aacute -30
+KPX ydieresis abreve -30
+KPX ydieresis acircumflex -30
+KPX ydieresis adieresis -30
+KPX ydieresis agrave -30
+KPX ydieresis amacron -30
+KPX ydieresis aogonek -30
+KPX ydieresis aring -30
+KPX ydieresis atilde -30
+KPX ydieresis comma -80
+KPX ydieresis e -10
+KPX ydieresis eacute -10
+KPX ydieresis ecaron -10
+KPX ydieresis ecircumflex -10
+KPX ydieresis edieresis -10
+KPX ydieresis edotaccent -10
+KPX ydieresis egrave -10
+KPX ydieresis emacron -10
+KPX ydieresis eogonek -10
+KPX ydieresis o -25
+KPX ydieresis oacute -25
+KPX ydieresis ocircumflex -25
+KPX ydieresis odieresis -25
+KPX ydieresis ograve -25
+KPX ydieresis ohungarumlaut -25
+KPX ydieresis omacron -25
+KPX ydieresis oslash -25
+KPX ydieresis otilde -25
+KPX ydieresis period -80
+KPX z e 10
+KPX z eacute 10
+KPX z ecaron 10
+KPX z ecircumflex 10
+KPX z edieresis 10
+KPX z edotaccent 10
+KPX z egrave 10
+KPX z emacron 10
+KPX z eogonek 10
+KPX zacute e 10
+KPX zacute eacute 10
+KPX zacute ecaron 10
+KPX zacute ecircumflex 10
+KPX zacute edieresis 10
+KPX zacute edotaccent 10
+KPX zacute egrave 10
+KPX zacute emacron 10
+KPX zacute eogonek 10
+KPX zcaron e 10
+KPX zcaron eacute 10
+KPX zcaron ecaron 10
+KPX zcaron ecircumflex 10
+KPX zcaron edieresis 10
+KPX zcaron edotaccent 10
+KPX zcaron egrave 10
+KPX zcaron emacron 10
+KPX zcaron eogonek 10
+KPX zdotaccent e 10
+KPX zdotaccent eacute 10
+KPX zdotaccent ecaron 10
+KPX zdotaccent ecircumflex 10
+KPX zdotaccent edieresis 10
+KPX zdotaccent edotaccent 10
+KPX zdotaccent egrave 10
+KPX zdotaccent emacron 10
+KPX zdotaccent eogonek 10
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/program/libraries/afm/Helvetica-Oblique.afm b/program/libraries/afm/Helvetica-Oblique.afm
new file mode 100644 (file)
index 0000000..7a7af00
--- /dev/null
@@ -0,0 +1,3051 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated.  All Rights Reserved.
+Comment Creation Date: Thu May  1 12:44:31 1997
+Comment UniqueID 43055
+Comment VMusage 14960 69346
+FontName Helvetica-Oblique
+FullName Helvetica Oblique
+FamilyName Helvetica
+Weight Medium
+ItalicAngle -12
+IsFixedPitch false
+CharacterSet ExtendedRoman
+FontBBox -170 -225 1116 931 
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated.  All Rights Reserved.Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries.
+EncodingScheme AdobeStandardEncoding
+CapHeight 718
+XHeight 523
+Ascender 718
+Descender -207
+StdHW 76
+StdVW 88
+StartCharMetrics 315
+C 32 ; WX 278 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 278 ; N exclam ; B 90 0 340 718 ;
+C 34 ; WX 355 ; N quotedbl ; B 168 463 438 718 ;
+C 35 ; WX 556 ; N numbersign ; B 73 0 631 688 ;
+C 36 ; WX 556 ; N dollar ; B 69 -115 617 775 ;
+C 37 ; WX 889 ; N percent ; B 147 -19 889 703 ;
+C 38 ; WX 667 ; N ampersand ; B 77 -15 647 718 ;
+C 39 ; WX 222 ; N quoteright ; B 151 463 310 718 ;
+C 40 ; WX 333 ; N parenleft ; B 108 -207 454 733 ;
+C 41 ; WX 333 ; N parenright ; B -9 -207 337 733 ;
+C 42 ; WX 389 ; N asterisk ; B 165 431 475 718 ;
+C 43 ; WX 584 ; N plus ; B 85 0 606 505 ;
+C 44 ; WX 278 ; N comma ; B 56 -147 214 106 ;
+C 45 ; WX 333 ; N hyphen ; B 93 232 357 322 ;
+C 46 ; WX 278 ; N period ; B 87 0 214 106 ;
+C 47 ; WX 278 ; N slash ; B -21 -19 452 737 ;
+C 48 ; WX 556 ; N zero ; B 93 -19 608 703 ;
+C 49 ; WX 556 ; N one ; B 207 0 508 703 ;
+C 50 ; WX 556 ; N two ; B 26 0 617 703 ;
+C 51 ; WX 556 ; N three ; B 75 -19 610 703 ;
+C 52 ; WX 556 ; N four ; B 61 0 576 703 ;
+C 53 ; WX 556 ; N five ; B 68 -19 621 688 ;
+C 54 ; WX 556 ; N six ; B 91 -19 615 703 ;
+C 55 ; WX 556 ; N seven ; B 137 0 669 688 ;
+C 56 ; WX 556 ; N eight ; B 74 -19 607 703 ;
+C 57 ; WX 556 ; N nine ; B 82 -19 609 703 ;
+C 58 ; WX 278 ; N colon ; B 87 0 301 516 ;
+C 59 ; WX 278 ; N semicolon ; B 56 -147 301 516 ;
+C 60 ; WX 584 ; N less ; B 94 11 641 495 ;
+C 61 ; WX 584 ; N equal ; B 63 115 628 390 ;
+C 62 ; WX 584 ; N greater ; B 50 11 597 495 ;
+C 63 ; WX 556 ; N question ; B 161 0 610 727 ;
+C 64 ; WX 1015 ; N at ; B 215 -19 965 737 ;
+C 65 ; WX 667 ; N A ; B 14 0 654 718 ;
+C 66 ; WX 667 ; N B ; B 74 0 712 718 ;
+C 67 ; WX 722 ; N C ; B 108 -19 782 737 ;
+C 68 ; WX 722 ; N D ; B 81 0 764 718 ;
+C 69 ; WX 667 ; N E ; B 86 0 762 718 ;
+C 70 ; WX 611 ; N F ; B 86 0 736 718 ;
+C 71 ; WX 778 ; N G ; B 111 -19 799 737 ;
+C 72 ; WX 722 ; N H ; B 77 0 799 718 ;
+C 73 ; WX 278 ; N I ; B 91 0 341 718 ;
+C 74 ; WX 500 ; N J ; B 47 -19 581 718 ;
+C 75 ; WX 667 ; N K ; B 76 0 808 718 ;
+C 76 ; WX 556 ; N L ; B 76 0 555 718 ;
+C 77 ; WX 833 ; N M ; B 73 0 914 718 ;
+C 78 ; WX 722 ; N N ; B 76 0 799 718 ;
+C 79 ; WX 778 ; N O ; B 105 -19 826 737 ;
+C 80 ; WX 667 ; N P ; B 86 0 737 718 ;
+C 81 ; WX 778 ; N Q ; B 105 -56 826 737 ;
+C 82 ; WX 722 ; N R ; B 88 0 773 718 ;
+C 83 ; WX 667 ; N S ; B 90 -19 713 737 ;
+C 84 ; WX 611 ; N T ; B 148 0 750 718 ;
+C 85 ; WX 722 ; N U ; B 123 -19 797 718 ;
+C 86 ; WX 667 ; N V ; B 173 0 800 718 ;
+C 87 ; WX 944 ; N W ; B 169 0 1081 718 ;
+C 88 ; WX 667 ; N X ; B 19 0 790 718 ;
+C 89 ; WX 667 ; N Y ; B 167 0 806 718 ;
+C 90 ; WX 611 ; N Z ; B 23 0 741 718 ;
+C 91 ; WX 278 ; N bracketleft ; B 21 -196 403 722 ;
+C 92 ; WX 278 ; N backslash ; B 140 -19 291 737 ;
+C 93 ; WX 278 ; N bracketright ; B -14 -196 368 722 ;
+C 94 ; WX 469 ; N asciicircum ; B 42 264 539 688 ;
+C 95 ; WX 556 ; N underscore ; B -27 -125 540 -75 ;
+C 96 ; WX 222 ; N quoteleft ; B 165 470 323 725 ;
+C 97 ; WX 556 ; N a ; B 61 -15 559 538 ;
+C 98 ; WX 556 ; N b ; B 58 -15 584 718 ;
+C 99 ; WX 500 ; N c ; B 74 -15 553 538 ;
+C 100 ; WX 556 ; N d ; B 84 -15 652 718 ;
+C 101 ; WX 556 ; N e ; B 84 -15 578 538 ;
+C 102 ; WX 278 ; N f ; B 86 0 416 728 ; L i fi ; L l fl ;
+C 103 ; WX 556 ; N g ; B 42 -220 610 538 ;
+C 104 ; WX 556 ; N h ; B 65 0 573 718 ;
+C 105 ; WX 222 ; N i ; B 67 0 308 718 ;
+C 106 ; WX 222 ; N j ; B -60 -210 308 718 ;
+C 107 ; WX 500 ; N k ; B 67 0 600 718 ;
+C 108 ; WX 222 ; N l ; B 67 0 308 718 ;
+C 109 ; WX 833 ; N m ; B 65 0 852 538 ;
+C 110 ; WX 556 ; N n ; B 65 0 573 538 ;
+C 111 ; WX 556 ; N o ; B 83 -14 585 538 ;
+C 112 ; WX 556 ; N p ; B 14 -207 584 538 ;
+C 113 ; WX 556 ; N q ; B 84 -207 605 538 ;
+C 114 ; WX 333 ; N r ; B 77 0 446 538 ;
+C 115 ; WX 500 ; N s ; B 63 -15 529 538 ;
+C 116 ; WX 278 ; N t ; B 102 -7 368 669 ;
+C 117 ; WX 556 ; N u ; B 94 -15 600 523 ;
+C 118 ; WX 500 ; N v ; B 119 0 603 523 ;
+C 119 ; WX 722 ; N w ; B 125 0 820 523 ;
+C 120 ; WX 500 ; N x ; B 11 0 594 523 ;
+C 121 ; WX 500 ; N y ; B 15 -214 600 523 ;
+C 122 ; WX 500 ; N z ; B 31 0 571 523 ;
+C 123 ; WX 334 ; N braceleft ; B 92 -196 445 722 ;
+C 124 ; WX 260 ; N bar ; B 46 -225 332 775 ;
+C 125 ; WX 334 ; N braceright ; B 0 -196 354 722 ;
+C 126 ; WX 584 ; N asciitilde ; B 111 180 580 326 ;
+C 161 ; WX 333 ; N exclamdown ; B 77 -195 326 523 ;
+C 162 ; WX 556 ; N cent ; B 95 -115 584 623 ;
+C 163 ; WX 556 ; N sterling ; B 49 -16 634 718 ;
+C 164 ; WX 167 ; N fraction ; B -170 -19 482 703 ;
+C 165 ; WX 556 ; N yen ; B 81 0 699 688 ;
+C 166 ; WX 556 ; N florin ; B -52 -207 654 737 ;
+C 167 ; WX 556 ; N section ; B 76 -191 584 737 ;
+C 168 ; WX 556 ; N currency ; B 60 99 646 603 ;
+C 169 ; WX 191 ; N quotesingle ; B 157 463 285 718 ;
+C 170 ; WX 333 ; N quotedblleft ; B 138 470 461 725 ;
+C 171 ; WX 556 ; N guillemotleft ; B 146 108 554 446 ;
+C 172 ; WX 333 ; N guilsinglleft ; B 137 108 340 446 ;
+C 173 ; WX 333 ; N guilsinglright ; B 111 108 314 446 ;
+C 174 ; WX 500 ; N fi ; B 86 0 587 728 ;
+C 175 ; WX 500 ; N fl ; B 86 0 585 728 ;
+C 177 ; WX 556 ; N endash ; B 51 240 623 313 ;
+C 178 ; WX 556 ; N dagger ; B 135 -159 622 718 ;
+C 179 ; WX 556 ; N daggerdbl ; B 52 -159 623 718 ;
+C 180 ; WX 278 ; N periodcentered ; B 129 190 257 315 ;
+C 182 ; WX 537 ; N paragraph ; B 126 -173 650 718 ;
+C 183 ; WX 350 ; N bullet ; B 91 202 413 517 ;
+C 184 ; WX 222 ; N quotesinglbase ; B 21 -149 180 106 ;
+C 185 ; WX 333 ; N quotedblbase ; B -6 -149 318 106 ;
+C 186 ; WX 333 ; N quotedblright ; B 124 463 448 718 ;
+C 187 ; WX 556 ; N guillemotright ; B 120 108 528 446 ;
+C 188 ; WX 1000 ; N ellipsis ; B 115 0 908 106 ;
+C 189 ; WX 1000 ; N perthousand ; B 88 -19 1029 703 ;
+C 191 ; WX 611 ; N questiondown ; B 85 -201 534 525 ;
+C 193 ; WX 333 ; N grave ; B 170 593 337 734 ;
+C 194 ; WX 333 ; N acute ; B 248 593 475 734 ;
+C 195 ; WX 333 ; N circumflex ; B 147 593 438 734 ;
+C 196 ; WX 333 ; N tilde ; B 125 606 490 722 ;
+C 197 ; WX 333 ; N macron ; B 143 627 468 684 ;
+C 198 ; WX 333 ; N breve ; B 167 595 476 731 ;
+C 199 ; WX 333 ; N dotaccent ; B 249 604 362 706 ;
+C 200 ; WX 333 ; N dieresis ; B 168 604 443 706 ;
+C 202 ; WX 333 ; N ring ; B 214 572 402 756 ;
+C 203 ; WX 333 ; N cedilla ; B 2 -225 232 0 ;
+C 205 ; WX 333 ; N hungarumlaut ; B 157 593 565 734 ;
+C 206 ; WX 333 ; N ogonek ; B 43 -225 249 0 ;
+C 207 ; WX 333 ; N caron ; B 177 593 468 734 ;
+C 208 ; WX 1000 ; N emdash ; B 51 240 1067 313 ;
+C 225 ; WX 1000 ; N AE ; B 8 0 1097 718 ;
+C 227 ; WX 370 ; N ordfeminine ; B 127 405 449 737 ;
+C 232 ; WX 556 ; N Lslash ; B 41 0 555 718 ;
+C 233 ; WX 778 ; N Oslash ; B 43 -19 890 737 ;
+C 234 ; WX 1000 ; N OE ; B 98 -19 1116 737 ;
+C 235 ; WX 365 ; N ordmasculine ; B 141 405 468 737 ;
+C 241 ; WX 889 ; N ae ; B 61 -15 909 538 ;
+C 245 ; WX 278 ; N dotlessi ; B 95 0 294 523 ;
+C 248 ; WX 222 ; N lslash ; B 41 0 347 718 ;
+C 249 ; WX 611 ; N oslash ; B 29 -22 647 545 ;
+C 250 ; WX 944 ; N oe ; B 83 -15 964 538 ;
+C 251 ; WX 611 ; N germandbls ; B 67 -15 658 728 ;
+C -1 ; WX 278 ; N Idieresis ; B 91 0 458 901 ;
+C -1 ; WX 556 ; N eacute ; B 84 -15 587 734 ;
+C -1 ; WX 556 ; N abreve ; B 61 -15 578 731 ;
+C -1 ; WX 556 ; N uhungarumlaut ; B 94 -15 677 734 ;
+C -1 ; WX 556 ; N ecaron ; B 84 -15 580 734 ;
+C -1 ; WX 667 ; N Ydieresis ; B 167 0 806 901 ;
+C -1 ; WX 584 ; N divide ; B 85 -19 606 524 ;
+C -1 ; WX 667 ; N Yacute ; B 167 0 806 929 ;
+C -1 ; WX 667 ; N Acircumflex ; B 14 0 654 929 ;
+C -1 ; WX 556 ; N aacute ; B 61 -15 587 734 ;
+C -1 ; WX 722 ; N Ucircumflex ; B 123 -19 797 929 ;
+C -1 ; WX 500 ; N yacute ; B 15 -214 600 734 ;
+C -1 ; WX 500 ; N scommaaccent ; B 63 -225 529 538 ;
+C -1 ; WX 556 ; N ecircumflex ; B 84 -15 578 734 ;
+C -1 ; WX 722 ; N Uring ; B 123 -19 797 931 ;
+C -1 ; WX 722 ; N Udieresis ; B 123 -19 797 901 ;
+C -1 ; WX 556 ; N aogonek ; B 61 -220 559 538 ;
+C -1 ; WX 722 ; N Uacute ; B 123 -19 797 929 ;
+C -1 ; WX 556 ; N uogonek ; B 94 -225 600 523 ;
+C -1 ; WX 667 ; N Edieresis ; B 86 0 762 901 ;
+C -1 ; WX 722 ; N Dcroat ; B 69 0 764 718 ;
+C -1 ; WX 250 ; N commaaccent ; B 39 -225 172 -40 ;
+C -1 ; WX 737 ; N copyright ; B 54 -19 837 737 ;
+C -1 ; WX 667 ; N Emacron ; B 86 0 762 879 ;
+C -1 ; WX 500 ; N ccaron ; B 74 -15 553 734 ;
+C -1 ; WX 556 ; N aring ; B 61 -15 559 756 ;
+C -1 ; WX 722 ; N Ncommaaccent ; B 76 -225 799 718 ;
+C -1 ; WX 222 ; N lacute ; B 67 0 461 929 ;
+C -1 ; WX 556 ; N agrave ; B 61 -15 559 734 ;
+C -1 ; WX 611 ; N Tcommaaccent ; B 148 -225 750 718 ;
+C -1 ; WX 722 ; N Cacute ; B 108 -19 782 929 ;
+C -1 ; WX 556 ; N atilde ; B 61 -15 592 722 ;
+C -1 ; WX 667 ; N Edotaccent ; B 86 0 762 901 ;
+C -1 ; WX 500 ; N scaron ; B 63 -15 552 734 ;
+C -1 ; WX 500 ; N scedilla ; B 63 -225 529 538 ;
+C -1 ; WX 278 ; N iacute ; B 95 0 448 734 ;
+C -1 ; WX 471 ; N lozenge ; B 88 0 540 728 ;
+C -1 ; WX 722 ; N Rcaron ; B 88 0 773 929 ;
+C -1 ; WX 778 ; N Gcommaaccent ; B 111 -225 799 737 ;
+C -1 ; WX 556 ; N ucircumflex ; B 94 -15 600 734 ;
+C -1 ; WX 556 ; N acircumflex ; B 61 -15 559 734 ;
+C -1 ; WX 667 ; N Amacron ; B 14 0 677 879 ;
+C -1 ; WX 333 ; N rcaron ; B 77 0 508 734 ;
+C -1 ; WX 500 ; N ccedilla ; B 74 -225 553 538 ;
+C -1 ; WX 611 ; N Zdotaccent ; B 23 0 741 901 ;
+C -1 ; WX 667 ; N Thorn ; B 86 0 712 718 ;
+C -1 ; WX 778 ; N Omacron ; B 105 -19 826 879 ;
+C -1 ; WX 722 ; N Racute ; B 88 0 773 929 ;
+C -1 ; WX 667 ; N Sacute ; B 90 -19 713 929 ;
+C -1 ; WX 643 ; N dcaron ; B 84 -15 808 718 ;
+C -1 ; WX 722 ; N Umacron ; B 123 -19 797 879 ;
+C -1 ; WX 556 ; N uring ; B 94 -15 600 756 ;
+C -1 ; WX 333 ; N threesuperior ; B 90 270 436 703 ;
+C -1 ; WX 778 ; N Ograve ; B 105 -19 826 929 ;
+C -1 ; WX 667 ; N Agrave ; B 14 0 654 929 ;
+C -1 ; WX 667 ; N Abreve ; B 14 0 685 926 ;
+C -1 ; WX 584 ; N multiply ; B 50 0 642 506 ;
+C -1 ; WX 556 ; N uacute ; B 94 -15 600 734 ;
+C -1 ; WX 611 ; N Tcaron ; B 148 0 750 929 ;
+C -1 ; WX 476 ; N partialdiff ; B 41 -38 550 714 ;
+C -1 ; WX 500 ; N ydieresis ; B 15 -214 600 706 ;
+C -1 ; WX 722 ; N Nacute ; B 76 0 799 929 ;
+C -1 ; WX 278 ; N icircumflex ; B 95 0 411 734 ;
+C -1 ; WX 667 ; N Ecircumflex ; B 86 0 762 929 ;
+C -1 ; WX 556 ; N adieresis ; B 61 -15 559 706 ;
+C -1 ; WX 556 ; N edieresis ; B 84 -15 578 706 ;
+C -1 ; WX 500 ; N cacute ; B 74 -15 559 734 ;
+C -1 ; WX 556 ; N nacute ; B 65 0 587 734 ;
+C -1 ; WX 556 ; N umacron ; B 94 -15 600 684 ;
+C -1 ; WX 722 ; N Ncaron ; B 76 0 799 929 ;
+C -1 ; WX 278 ; N Iacute ; B 91 0 489 929 ;
+C -1 ; WX 584 ; N plusminus ; B 39 0 618 506 ;
+C -1 ; WX 260 ; N brokenbar ; B 62 -150 316 700 ;
+C -1 ; WX 737 ; N registered ; B 54 -19 837 737 ;
+C -1 ; WX 778 ; N Gbreve ; B 111 -19 799 926 ;
+C -1 ; WX 278 ; N Idotaccent ; B 91 0 377 901 ;
+C -1 ; WX 600 ; N summation ; B 15 -10 671 706 ;
+C -1 ; WX 667 ; N Egrave ; B 86 0 762 929 ;
+C -1 ; WX 333 ; N racute ; B 77 0 475 734 ;
+C -1 ; WX 556 ; N omacron ; B 83 -14 585 684 ;
+C -1 ; WX 611 ; N Zacute ; B 23 0 741 929 ;
+C -1 ; WX 611 ; N Zcaron ; B 23 0 741 929 ;
+C -1 ; WX 549 ; N greaterequal ; B 26 0 620 674 ;
+C -1 ; WX 722 ; N Eth ; B 69 0 764 718 ;
+C -1 ; WX 722 ; N Ccedilla ; B 108 -225 782 737 ;
+C -1 ; WX 222 ; N lcommaaccent ; B 25 -225 308 718 ;
+C -1 ; WX 317 ; N tcaron ; B 102 -7 501 808 ;
+C -1 ; WX 556 ; N eogonek ; B 84 -225 578 538 ;
+C -1 ; WX 722 ; N Uogonek ; B 123 -225 797 718 ;
+C -1 ; WX 667 ; N Aacute ; B 14 0 683 929 ;
+C -1 ; WX 667 ; N Adieresis ; B 14 0 654 901 ;
+C -1 ; WX 556 ; N egrave ; B 84 -15 578 734 ;
+C -1 ; WX 500 ; N zacute ; B 31 0 571 734 ;
+C -1 ; WX 222 ; N iogonek ; B -61 -225 308 718 ;
+C -1 ; WX 778 ; N Oacute ; B 105 -19 826 929 ;
+C -1 ; WX 556 ; N oacute ; B 83 -14 587 734 ;
+C -1 ; WX 556 ; N amacron ; B 61 -15 580 684 ;
+C -1 ; WX 500 ; N sacute ; B 63 -15 559 734 ;
+C -1 ; WX 278 ; N idieresis ; B 95 0 416 706 ;
+C -1 ; WX 778 ; N Ocircumflex ; B 105 -19 826 929 ;
+C -1 ; WX 722 ; N Ugrave ; B 123 -19 797 929 ;
+C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C -1 ; WX 556 ; N thorn ; B 14 -207 584 718 ;
+C -1 ; WX 333 ; N twosuperior ; B 64 281 449 703 ;
+C -1 ; WX 778 ; N Odieresis ; B 105 -19 826 901 ;
+C -1 ; WX 556 ; N mu ; B 24 -207 600 523 ;
+C -1 ; WX 278 ; N igrave ; B 95 0 310 734 ;
+C -1 ; WX 556 ; N ohungarumlaut ; B 83 -14 677 734 ;
+C -1 ; WX 667 ; N Eogonek ; B 86 -220 762 718 ;
+C -1 ; WX 556 ; N dcroat ; B 84 -15 689 718 ;
+C -1 ; WX 834 ; N threequarters ; B 130 -19 861 703 ;
+C -1 ; WX 667 ; N Scedilla ; B 90 -225 713 737 ;
+C -1 ; WX 299 ; N lcaron ; B 67 0 464 718 ;
+C -1 ; WX 667 ; N Kcommaaccent ; B 76 -225 808 718 ;
+C -1 ; WX 556 ; N Lacute ; B 76 0 555 929 ;
+C -1 ; WX 1000 ; N trademark ; B 186 306 1056 718 ;
+C -1 ; WX 556 ; N edotaccent ; B 84 -15 578 706 ;
+C -1 ; WX 278 ; N Igrave ; B 91 0 351 929 ;
+C -1 ; WX 278 ; N Imacron ; B 91 0 483 879 ;
+C -1 ; WX 556 ; N Lcaron ; B 76 0 570 718 ;
+C -1 ; WX 834 ; N onehalf ; B 114 -19 839 703 ;
+C -1 ; WX 549 ; N lessequal ; B 26 0 666 674 ;
+C -1 ; WX 556 ; N ocircumflex ; B 83 -14 585 734 ;
+C -1 ; WX 556 ; N ntilde ; B 65 0 592 722 ;
+C -1 ; WX 722 ; N Uhungarumlaut ; B 123 -19 801 929 ;
+C -1 ; WX 667 ; N Eacute ; B 86 0 762 929 ;
+C -1 ; WX 556 ; N emacron ; B 84 -15 580 684 ;
+C -1 ; WX 556 ; N gbreve ; B 42 -220 610 731 ;
+C -1 ; WX 834 ; N onequarter ; B 150 -19 802 703 ;
+C -1 ; WX 667 ; N Scaron ; B 90 -19 713 929 ;
+C -1 ; WX 667 ; N Scommaaccent ; B 90 -225 713 737 ;
+C -1 ; WX 778 ; N Ohungarumlaut ; B 105 -19 829 929 ;
+C -1 ; WX 400 ; N degree ; B 169 411 468 703 ;
+C -1 ; WX 556 ; N ograve ; B 83 -14 585 734 ;
+C -1 ; WX 722 ; N Ccaron ; B 108 -19 782 929 ;
+C -1 ; WX 556 ; N ugrave ; B 94 -15 600 734 ;
+C -1 ; WX 453 ; N radical ; B 79 -80 617 762 ;
+C -1 ; WX 722 ; N Dcaron ; B 81 0 764 929 ;
+C -1 ; WX 333 ; N rcommaaccent ; B 30 -225 446 538 ;
+C -1 ; WX 722 ; N Ntilde ; B 76 0 799 917 ;
+C -1 ; WX 556 ; N otilde ; B 83 -14 602 722 ;
+C -1 ; WX 722 ; N Rcommaaccent ; B 88 -225 773 718 ;
+C -1 ; WX 556 ; N Lcommaaccent ; B 76 -225 555 718 ;
+C -1 ; WX 667 ; N Atilde ; B 14 0 699 917 ;
+C -1 ; WX 667 ; N Aogonek ; B 14 -225 654 718 ;
+C -1 ; WX 667 ; N Aring ; B 14 0 654 931 ;
+C -1 ; WX 778 ; N Otilde ; B 105 -19 826 917 ;
+C -1 ; WX 500 ; N zdotaccent ; B 31 0 571 706 ;
+C -1 ; WX 667 ; N Ecaron ; B 86 0 762 929 ;
+C -1 ; WX 278 ; N Iogonek ; B -33 -225 341 718 ;
+C -1 ; WX 500 ; N kcommaaccent ; B 67 -225 600 718 ;
+C -1 ; WX 584 ; N minus ; B 85 216 606 289 ;
+C -1 ; WX 278 ; N Icircumflex ; B 91 0 452 929 ;
+C -1 ; WX 556 ; N ncaron ; B 65 0 580 734 ;
+C -1 ; WX 278 ; N tcommaaccent ; B 63 -225 368 669 ;
+C -1 ; WX 584 ; N logicalnot ; B 106 108 628 390 ;
+C -1 ; WX 556 ; N odieresis ; B 83 -14 585 706 ;
+C -1 ; WX 556 ; N udieresis ; B 94 -15 600 706 ;
+C -1 ; WX 549 ; N notequal ; B 34 -35 623 551 ;
+C -1 ; WX 556 ; N gcommaaccent ; B 42 -220 610 822 ;
+C -1 ; WX 556 ; N eth ; B 81 -15 617 737 ;
+C -1 ; WX 500 ; N zcaron ; B 31 0 571 734 ;
+C -1 ; WX 556 ; N ncommaaccent ; B 65 -225 573 538 ;
+C -1 ; WX 333 ; N onesuperior ; B 166 281 371 703 ;
+C -1 ; WX 278 ; N imacron ; B 95 0 417 684 ;
+C -1 ; WX 556 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+StartKernData
+StartKernPairs 2705
+KPX A C -30
+KPX A Cacute -30
+KPX A Ccaron -30
+KPX A Ccedilla -30
+KPX A G -30
+KPX A Gbreve -30
+KPX A Gcommaaccent -30
+KPX A O -30
+KPX A Oacute -30
+KPX A Ocircumflex -30
+KPX A Odieresis -30
+KPX A Ograve -30
+KPX A Ohungarumlaut -30
+KPX A Omacron -30
+KPX A Oslash -30
+KPX A Otilde -30
+KPX A Q -30
+KPX A T -120
+KPX A Tcaron -120
+KPX A Tcommaaccent -120
+KPX A U -50
+KPX A Uacute -50
+KPX A Ucircumflex -50
+KPX A Udieresis -50
+KPX A Ugrave -50
+KPX A Uhungarumlaut -50
+KPX A Umacron -50
+KPX A Uogonek -50
+KPX A Uring -50
+KPX A V -70
+KPX A W -50
+KPX A Y -100
+KPX A Yacute -100
+KPX A Ydieresis -100
+KPX A u -30
+KPX A uacute -30
+KPX A ucircumflex -30
+KPX A udieresis -30
+KPX A ugrave -30
+KPX A uhungarumlaut -30
+KPX A umacron -30
+KPX A uogonek -30
+KPX A uring -30
+KPX A v -40
+KPX A w -40
+KPX A y -40
+KPX A yacute -40
+KPX A ydieresis -40
+KPX Aacute C -30
+KPX Aacute Cacute -30
+KPX Aacute Ccaron -30
+KPX Aacute Ccedilla -30
+KPX Aacute G -30
+KPX Aacute Gbreve -30
+KPX Aacute Gcommaaccent -30
+KPX Aacute O -30
+KPX Aacute Oacute -30
+KPX Aacute Ocircumflex -30
+KPX Aacute Odieresis -30
+KPX Aacute Ograve -30
+KPX Aacute Ohungarumlaut -30
+KPX Aacute Omacron -30
+KPX Aacute Oslash -30
+KPX Aacute Otilde -30
+KPX Aacute Q -30
+KPX Aacute T -120
+KPX Aacute Tcaron -120
+KPX Aacute Tcommaaccent -120
+KPX Aacute U -50
+KPX Aacute Uacute -50
+KPX Aacute Ucircumflex -50
+KPX Aacute Udieresis -50
+KPX Aacute Ugrave -50
+KPX Aacute Uhungarumlaut -50
+KPX Aacute Umacron -50
+KPX Aacute Uogonek -50
+KPX Aacute Uring -50
+KPX Aacute V -70
+KPX Aacute W -50
+KPX Aacute Y -100
+KPX Aacute Yacute -100
+KPX Aacute Ydieresis -100
+KPX Aacute u -30
+KPX Aacute uacute -30
+KPX Aacute ucircumflex -30
+KPX Aacute udieresis -30
+KPX Aacute ugrave -30
+KPX Aacute uhungarumlaut -30
+KPX Aacute umacron -30
+KPX Aacute uogonek -30
+KPX Aacute uring -30
+KPX Aacute v -40
+KPX Aacute w -40
+KPX Aacute y -40
+KPX Aacute yacute -40
+KPX Aacute ydieresis -40
+KPX Abreve C -30
+KPX Abreve Cacute -30
+KPX Abreve Ccaron -30
+KPX Abreve Ccedilla -30
+KPX Abreve G -30
+KPX Abreve Gbreve -30
+KPX Abreve Gcommaaccent -30
+KPX Abreve O -30
+KPX Abreve Oacute -30
+KPX Abreve Ocircumflex -30
+KPX Abreve Odieresis -30
+KPX Abreve Ograve -30
+KPX Abreve Ohungarumlaut -30
+KPX Abreve Omacron -30
+KPX Abreve Oslash -30
+KPX Abreve Otilde -30
+KPX Abreve Q -30
+KPX Abreve T -120
+KPX Abreve Tcaron -120
+KPX Abreve Tcommaaccent -120
+KPX Abreve U -50
+KPX Abreve Uacute -50
+KPX Abreve Ucircumflex -50
+KPX Abreve Udieresis -50
+KPX Abreve Ugrave -50
+KPX Abreve Uhungarumlaut -50
+KPX Abreve Umacron -50
+KPX Abreve Uogonek -50
+KPX Abreve Uring -50
+KPX Abreve V -70
+KPX Abreve W -50
+KPX Abreve Y -100
+KPX Abreve Yacute -100
+KPX Abreve Ydieresis -100
+KPX Abreve u -30
+KPX Abreve uacute -30
+KPX Abreve ucircumflex -30
+KPX Abreve udieresis -30
+KPX Abreve ugrave -30
+KPX Abreve uhungarumlaut -30
+KPX Abreve umacron -30
+KPX Abreve uogonek -30
+KPX Abreve uring -30
+KPX Abreve v -40
+KPX Abreve w -40
+KPX Abreve y -40
+KPX Abreve yacute -40
+KPX Abreve ydieresis -40
+KPX Acircumflex C -30
+KPX Acircumflex Cacute -30
+KPX Acircumflex Ccaron -30
+KPX Acircumflex Ccedilla -30
+KPX Acircumflex G -30
+KPX Acircumflex Gbreve -30
+KPX Acircumflex Gcommaaccent -30
+KPX Acircumflex O -30
+KPX Acircumflex Oacute -30
+KPX Acircumflex Ocircumflex -30
+KPX Acircumflex Odieresis -30
+KPX Acircumflex Ograve -30
+KPX Acircumflex Ohungarumlaut -30
+KPX Acircumflex Omacron -30
+KPX Acircumflex Oslash -30
+KPX Acircumflex Otilde -30
+KPX Acircumflex Q -30
+KPX Acircumflex T -120
+KPX Acircumflex Tcaron -120
+KPX Acircumflex Tcommaaccent -120
+KPX Acircumflex U -50
+KPX Acircumflex Uacute -50
+KPX Acircumflex Ucircumflex -50
+KPX Acircumflex Udieresis -50
+KPX Acircumflex Ugrave -50
+KPX Acircumflex Uhungarumlaut -50
+KPX Acircumflex Umacron -50
+KPX Acircumflex Uogonek -50
+KPX Acircumflex Uring -50
+KPX Acircumflex V -70
+KPX Acircumflex W -50
+KPX Acircumflex Y -100
+KPX Acircumflex Yacute -100
+KPX Acircumflex Ydieresis -100
+KPX Acircumflex u -30
+KPX Acircumflex uacute -30
+KPX Acircumflex ucircumflex -30
+KPX Acircumflex udieresis -30
+KPX Acircumflex ugrave -30
+KPX Acircumflex uhungarumlaut -30
+KPX Acircumflex umacron -30
+KPX Acircumflex uogonek -30
+KPX Acircumflex uring -30
+KPX Acircumflex v -40
+KPX Acircumflex w -40
+KPX Acircumflex y -40
+KPX Acircumflex yacute -40
+KPX Acircumflex ydieresis -40
+KPX Adieresis C -30
+KPX Adieresis Cacute -30
+KPX Adieresis Ccaron -30
+KPX Adieresis Ccedilla -30
+KPX Adieresis G -30
+KPX Adieresis Gbreve -30
+KPX Adieresis Gcommaaccent -30
+KPX Adieresis O -30
+KPX Adieresis Oacute -30
+KPX Adieresis Ocircumflex -30
+KPX Adieresis Odieresis -30
+KPX Adieresis Ograve -30
+KPX Adieresis Ohungarumlaut -30
+KPX Adieresis Omacron -30
+KPX Adieresis Oslash -30
+KPX Adieresis Otilde -30
+KPX Adieresis Q -30
+KPX Adieresis T -120
+KPX Adieresis Tcaron -120
+KPX Adieresis Tcommaaccent -120
+KPX Adieresis U -50
+KPX Adieresis Uacute -50
+KPX Adieresis Ucircumflex -50
+KPX Adieresis Udieresis -50
+KPX Adieresis Ugrave -50
+KPX Adieresis Uhungarumlaut -50
+KPX Adieresis Umacron -50
+KPX Adieresis Uogonek -50
+KPX Adieresis Uring -50
+KPX Adieresis V -70
+KPX Adieresis W -50
+KPX Adieresis Y -100
+KPX Adieresis Yacute -100
+KPX Adieresis Ydieresis -100
+KPX Adieresis u -30
+KPX Adieresis uacute -30
+KPX Adieresis ucircumflex -30
+KPX Adieresis udieresis -30
+KPX Adieresis ugrave -30
+KPX Adieresis uhungarumlaut -30
+KPX Adieresis umacron -30
+KPX Adieresis uogonek -30
+KPX Adieresis uring -30
+KPX Adieresis v -40
+KPX Adieresis w -40
+KPX Adieresis y -40
+KPX Adieresis yacute -40
+KPX Adieresis ydieresis -40
+KPX Agrave C -30
+KPX Agrave Cacute -30
+KPX Agrave Ccaron -30
+KPX Agrave Ccedilla -30
+KPX Agrave G -30
+KPX Agrave Gbreve -30
+KPX Agrave Gcommaaccent -30
+KPX Agrave O -30
+KPX Agrave Oacute -30
+KPX Agrave Ocircumflex -30
+KPX Agrave Odieresis -30
+KPX Agrave Ograve -30
+KPX Agrave Ohungarumlaut -30
+KPX Agrave Omacron -30
+KPX Agrave Oslash -30
+KPX Agrave Otilde -30
+KPX Agrave Q -30
+KPX Agrave T -120
+KPX Agrave Tcaron -120
+KPX Agrave Tcommaaccent -120
+KPX Agrave U -50
+KPX Agrave Uacute -50
+KPX Agrave Ucircumflex -50
+KPX Agrave Udieresis -50
+KPX Agrave Ugrave -50
+KPX Agrave Uhungarumlaut -50
+KPX Agrave Umacron -50
+KPX Agrave Uogonek -50
+KPX Agrave Uring -50
+KPX Agrave V -70
+KPX Agrave W -50
+KPX Agrave Y -100
+KPX Agrave Yacute -100
+KPX Agrave Ydieresis -100
+KPX Agrave u -30
+KPX Agrave uacute -30
+KPX Agrave ucircumflex -30
+KPX Agrave udieresis -30
+KPX Agrave ugrave -30
+KPX Agrave uhungarumlaut -30
+KPX Agrave umacron -30
+KPX Agrave uogonek -30
+KPX Agrave uring -30
+KPX Agrave v -40
+KPX Agrave w -40
+KPX Agrave y -40
+KPX Agrave yacute -40
+KPX Agrave ydieresis -40
+KPX Amacron C -30
+KPX Amacron Cacute -30
+KPX Amacron Ccaron -30
+KPX Amacron Ccedilla -30
+KPX Amacron G -30
+KPX Amacron Gbreve -30
+KPX Amacron Gcommaaccent -30
+KPX Amacron O -30
+KPX Amacron Oacute -30
+KPX Amacron Ocircumflex -30
+KPX Amacron Odieresis -30
+KPX Amacron Ograve -30
+KPX Amacron Ohungarumlaut -30
+KPX Amacron Omacron -30
+KPX Amacron Oslash -30
+KPX Amacron Otilde -30
+KPX Amacron Q -30
+KPX Amacron T -120
+KPX Amacron Tcaron -120
+KPX Amacron Tcommaaccent -120
+KPX Amacron U -50
+KPX Amacron Uacute -50
+KPX Amacron Ucircumflex -50
+KPX Amacron Udieresis -50
+KPX Amacron Ugrave -50
+KPX Amacron Uhungarumlaut -50
+KPX Amacron Umacron -50
+KPX Amacron Uogonek -50
+KPX Amacron Uring -50
+KPX Amacron V -70
+KPX Amacron W -50
+KPX Amacron Y -100
+KPX Amacron Yacute -100
+KPX Amacron Ydieresis -100
+KPX Amacron u -30
+KPX Amacron uacute -30
+KPX Amacron ucircumflex -30
+KPX Amacron udieresis -30
+KPX Amacron ugrave -30
+KPX Amacron uhungarumlaut -30
+KPX Amacron umacron -30
+KPX Amacron uogonek -30
+KPX Amacron uring -30
+KPX Amacron v -40
+KPX Amacron w -40
+KPX Amacron y -40
+KPX Amacron yacute -40
+KPX Amacron ydieresis -40
+KPX Aogonek C -30
+KPX Aogonek Cacute -30
+KPX Aogonek Ccaron -30
+KPX Aogonek Ccedilla -30
+KPX Aogonek G -30
+KPX Aogonek Gbreve -30
+KPX Aogonek Gcommaaccent -30
+KPX Aogonek O -30
+KPX Aogonek Oacute -30
+KPX Aogonek Ocircumflex -30
+KPX Aogonek Odieresis -30
+KPX Aogonek Ograve -30
+KPX Aogonek Ohungarumlaut -30
+KPX Aogonek Omacron -30
+KPX Aogonek Oslash -30
+KPX Aogonek Otilde -30
+KPX Aogonek Q -30
+KPX Aogonek T -120
+KPX Aogonek Tcaron -120
+KPX Aogonek Tcommaaccent -120
+KPX Aogonek U -50
+KPX Aogonek Uacute -50
+KPX Aogonek Ucircumflex -50
+KPX Aogonek Udieresis -50
+KPX Aogonek Ugrave -50
+KPX Aogonek Uhungarumlaut -50
+KPX Aogonek Umacron -50
+KPX Aogonek Uogonek -50
+KPX Aogonek Uring -50
+KPX Aogonek V -70
+KPX Aogonek W -50
+KPX Aogonek Y -100
+KPX Aogonek Yacute -100
+KPX Aogonek Ydieresis -100
+KPX Aogonek u -30
+KPX Aogonek uacute -30
+KPX Aogonek ucircumflex -30
+KPX Aogonek udieresis -30
+KPX Aogonek ugrave -30
+KPX Aogonek uhungarumlaut -30
+KPX Aogonek umacron -30
+KPX Aogonek uogonek -30
+KPX Aogonek uring -30
+KPX Aogonek v -40
+KPX Aogonek w -40
+KPX Aogonek y -40
+KPX Aogonek yacute -40
+KPX Aogonek ydieresis -40
+KPX Aring C -30
+KPX Aring Cacute -30
+KPX Aring Ccaron -30
+KPX Aring Ccedilla -30
+KPX Aring G -30
+KPX Aring Gbreve -30
+KPX Aring Gcommaaccent -30
+KPX Aring O -30
+KPX Aring Oacute -30
+KPX Aring Ocircumflex -30
+KPX Aring Odieresis -30
+KPX Aring Ograve -30
+KPX Aring Ohungarumlaut -30
+KPX Aring Omacron -30
+KPX Aring Oslash -30
+KPX Aring Otilde -30
+KPX Aring Q -30
+KPX Aring T -120
+KPX Aring Tcaron -120
+KPX Aring Tcommaaccent -120
+KPX Aring U -50
+KPX Aring Uacute -50
+KPX Aring Ucircumflex -50
+KPX Aring Udieresis -50
+KPX Aring Ugrave -50
+KPX Aring Uhungarumlaut -50
+KPX Aring Umacron -50
+KPX Aring Uogonek -50
+KPX Aring Uring -50
+KPX Aring V -70
+KPX Aring W -50
+KPX Aring Y -100
+KPX Aring Yacute -100
+KPX Aring Ydieresis -100
+KPX Aring u -30
+KPX Aring uacute -30
+KPX Aring ucircumflex -30
+KPX Aring udieresis -30
+KPX Aring ugrave -30
+KPX Aring uhungarumlaut -30
+KPX Aring umacron -30
+KPX Aring uogonek -30
+KPX Aring uring -30
+KPX Aring v -40
+KPX Aring w -40
+KPX Aring y -40
+KPX Aring yacute -40
+KPX Aring ydieresis -40
+KPX Atilde C -30
+KPX Atilde Cacute -30
+KPX Atilde Ccaron -30
+KPX Atilde Ccedilla -30
+KPX Atilde G -30
+KPX Atilde Gbreve -30
+KPX Atilde Gcommaaccent -30
+KPX Atilde O -30
+KPX Atilde Oacute -30
+KPX Atilde Ocircumflex -30
+KPX Atilde Odieresis -30
+KPX Atilde Ograve -30
+KPX Atilde Ohungarumlaut -30
+KPX Atilde Omacron -30
+KPX Atilde Oslash -30
+KPX Atilde Otilde -30
+KPX Atilde Q -30
+KPX Atilde T -120
+KPX Atilde Tcaron -120
+KPX Atilde Tcommaaccent -120
+KPX Atilde U -50
+KPX Atilde Uacute -50
+KPX Atilde Ucircumflex -50
+KPX Atilde Udieresis -50
+KPX Atilde Ugrave -50
+KPX Atilde Uhungarumlaut -50
+KPX Atilde Umacron -50
+KPX Atilde Uogonek -50
+KPX Atilde Uring -50
+KPX Atilde V -70
+KPX Atilde W -50
+KPX Atilde Y -100
+KPX Atilde Yacute -100
+KPX Atilde Ydieresis -100
+KPX Atilde u -30
+KPX Atilde uacute -30
+KPX Atilde ucircumflex -30
+KPX Atilde udieresis -30
+KPX Atilde ugrave -30
+KPX Atilde uhungarumlaut -30
+KPX Atilde umacron -30
+KPX Atilde uogonek -30
+KPX Atilde uring -30
+KPX Atilde v -40
+KPX Atilde w -40
+KPX Atilde y -40
+KPX Atilde yacute -40
+KPX Atilde ydieresis -40
+KPX B U -10
+KPX B Uacute -10
+KPX B Ucircumflex -10
+KPX B Udieresis -10
+KPX B Ugrave -10
+KPX B Uhungarumlaut -10
+KPX B Umacron -10
+KPX B Uogonek -10
+KPX B Uring -10
+KPX B comma -20
+KPX B period -20
+KPX C comma -30
+KPX C period -30
+KPX Cacute comma -30
+KPX Cacute period -30
+KPX Ccaron comma -30
+KPX Ccaron period -30
+KPX Ccedilla comma -30
+KPX Ccedilla period -30
+KPX D A -40
+KPX D Aacute -40
+KPX D Abreve -40
+KPX D Acircumflex -40
+KPX D Adieresis -40
+KPX D Agrave -40
+KPX D Amacron -40
+KPX D Aogonek -40
+KPX D Aring -40
+KPX D Atilde -40
+KPX D V -70
+KPX D W -40
+KPX D Y -90
+KPX D Yacute -90
+KPX D Ydieresis -90
+KPX D comma -70
+KPX D period -70
+KPX Dcaron A -40
+KPX Dcaron Aacute -40
+KPX Dcaron Abreve -40
+KPX Dcaron Acircumflex -40
+KPX Dcaron Adieresis -40
+KPX Dcaron Agrave -40
+KPX Dcaron Amacron -40
+KPX Dcaron Aogonek -40
+KPX Dcaron Aring -40
+KPX Dcaron Atilde -40
+KPX Dcaron V -70
+KPX Dcaron W -40
+KPX Dcaron Y -90
+KPX Dcaron Yacute -90
+KPX Dcaron Ydieresis -90
+KPX Dcaron comma -70
+KPX Dcaron period -70
+KPX Dcroat A -40
+KPX Dcroat Aacute -40
+KPX Dcroat Abreve -40
+KPX Dcroat Acircumflex -40
+KPX Dcroat Adieresis -40
+KPX Dcroat Agrave -40
+KPX Dcroat Amacron -40
+KPX Dcroat Aogonek -40
+KPX Dcroat Aring -40
+KPX Dcroat Atilde -40
+KPX Dcroat V -70
+KPX Dcroat W -40
+KPX Dcroat Y -90
+KPX Dcroat Yacute -90
+KPX Dcroat Ydieresis -90
+KPX Dcroat comma -70
+KPX Dcroat period -70
+KPX F A -80
+KPX F Aacute -80
+KPX F Abreve -80
+KPX F Acircumflex -80
+KPX F Adieresis -80
+KPX F Agrave -80
+KPX F Amacron -80
+KPX F Aogonek -80
+KPX F Aring -80
+KPX F Atilde -80
+KPX F a -50
+KPX F aacute -50
+KPX F abreve -50
+KPX F acircumflex -50
+KPX F adieresis -50
+KPX F agrave -50
+KPX F amacron -50
+KPX F aogonek -50
+KPX F aring -50
+KPX F atilde -50
+KPX F comma -150
+KPX F e -30
+KPX F eacute -30
+KPX F ecaron -30
+KPX F ecircumflex -30
+KPX F edieresis -30
+KPX F edotaccent -30
+KPX F egrave -30
+KPX F emacron -30
+KPX F eogonek -30
+KPX F o -30
+KPX F oacute -30
+KPX F ocircumflex -30
+KPX F odieresis -30
+KPX F ograve -30
+KPX F ohungarumlaut -30
+KPX F omacron -30
+KPX F oslash -30
+KPX F otilde -30
+KPX F period -150
+KPX F r -45
+KPX F racute -45
+KPX F rcaron -45
+KPX F rcommaaccent -45
+KPX J A -20
+KPX J Aacute -20
+KPX J Abreve -20
+KPX J Acircumflex -20
+KPX J Adieresis -20
+KPX J Agrave -20
+KPX J Amacron -20
+KPX J Aogonek -20
+KPX J Aring -20
+KPX J Atilde -20
+KPX J a -20
+KPX J aacute -20
+KPX J abreve -20
+KPX J acircumflex -20
+KPX J adieresis -20
+KPX J agrave -20
+KPX J amacron -20
+KPX J aogonek -20
+KPX J aring -20
+KPX J atilde -20
+KPX J comma -30
+KPX J period -30
+KPX J u -20
+KPX J uacute -20
+KPX J ucircumflex -20
+KPX J udieresis -20
+KPX J ugrave -20
+KPX J uhungarumlaut -20
+KPX J umacron -20
+KPX J uogonek -20
+KPX J uring -20
+KPX K O -50
+KPX K Oacute -50
+KPX K Ocircumflex -50
+KPX K Odieresis -50
+KPX K Ograve -50
+KPX K Ohungarumlaut -50
+KPX K Omacron -50
+KPX K Oslash -50
+KPX K Otilde -50
+KPX K e -40
+KPX K eacute -40
+KPX K ecaron -40
+KPX K ecircumflex -40
+KPX K edieresis -40
+KPX K edotaccent -40
+KPX K egrave -40
+KPX K emacron -40
+KPX K eogonek -40
+KPX K o -40
+KPX K oacute -40
+KPX K ocircumflex -40
+KPX K odieresis -40
+KPX K ograve -40
+KPX K ohungarumlaut -40
+KPX K omacron -40
+KPX K oslash -40
+KPX K otilde -40
+KPX K u -30
+KPX K uacute -30
+KPX K ucircumflex -30
+KPX K udieresis -30
+KPX K ugrave -30
+KPX K uhungarumlaut -30
+KPX K umacron -30
+KPX K uogonek -30
+KPX K uring -30
+KPX K y -50
+KPX K yacute -50
+KPX K ydieresis -50
+KPX Kcommaaccent O -50
+KPX Kcommaaccent Oacute -50
+KPX Kcommaaccent Ocircumflex -50
+KPX Kcommaaccent Odieresis -50
+KPX Kcommaaccent Ograve -50
+KPX Kcommaaccent Ohungarumlaut -50
+KPX Kcommaaccent Omacron -50
+KPX Kcommaaccent Oslash -50
+KPX Kcommaaccent Otilde -50
+KPX Kcommaaccent e -40
+KPX Kcommaaccent eacute -40
+KPX Kcommaaccent ecaron -40
+KPX Kcommaaccent ecircumflex -40
+KPX Kcommaaccent edieresis -40
+KPX Kcommaaccent edotaccent -40
+KPX Kcommaaccent egrave -40
+KPX Kcommaaccent emacron -40
+KPX Kcommaaccent eogonek -40
+KPX Kcommaaccent o -40
+KPX Kcommaaccent oacute -40
+KPX Kcommaaccent ocircumflex -40
+KPX Kcommaaccent odieresis -40
+KPX Kcommaaccent ograve -40
+KPX Kcommaaccent ohungarumlaut -40
+KPX Kcommaaccent omacron -40
+KPX Kcommaaccent oslash -40
+KPX Kcommaaccent otilde -40
+KPX Kcommaaccent u -30
+KPX Kcommaaccent uacute -30
+KPX Kcommaaccent ucircumflex -30
+KPX Kcommaaccent udieresis -30
+KPX Kcommaaccent ugrave -30
+KPX Kcommaaccent uhungarumlaut -30
+KPX Kcommaaccent umacron -30
+KPX Kcommaaccent uogonek -30
+KPX Kcommaaccent uring -30
+KPX Kcommaaccent y -50
+KPX Kcommaaccent yacute -50
+KPX Kcommaaccent ydieresis -50
+KPX L T -110
+KPX L Tcaron -110
+KPX L Tcommaaccent -110
+KPX L V -110
+KPX L W -70
+KPX L Y -140
+KPX L Yacute -140
+KPX L Ydieresis -140
+KPX L quotedblright -140
+KPX L quoteright -160
+KPX L y -30
+KPX L yacute -30
+KPX L ydieresis -30
+KPX Lacute T -110
+KPX Lacute Tcaron -110
+KPX Lacute Tcommaaccent -110
+KPX Lacute V -110
+KPX Lacute W -70
+KPX Lacute Y -140
+KPX Lacute Yacute -140
+KPX Lacute Ydieresis -140
+KPX Lacute quotedblright -140
+KPX Lacute quoteright -160
+KPX Lacute y -30
+KPX Lacute yacute -30
+KPX Lacute ydieresis -30
+KPX Lcaron T -110
+KPX Lcaron Tcaron -110
+KPX Lcaron Tcommaaccent -110
+KPX Lcaron V -110
+KPX Lcaron W -70
+KPX Lcaron Y -140
+KPX Lcaron Yacute -140
+KPX Lcaron Ydieresis -140
+KPX Lcaron quotedblright -140
+KPX Lcaron quoteright -160
+KPX Lcaron y -30
+KPX Lcaron yacute -30
+KPX Lcaron ydieresis -30
+KPX Lcommaaccent T -110
+KPX Lcommaaccent Tcaron -110
+KPX Lcommaaccent Tcommaaccent -110
+KPX Lcommaaccent V -110
+KPX Lcommaaccent W -70
+KPX Lcommaaccent Y -140
+KPX Lcommaaccent Yacute -140
+KPX Lcommaaccent Ydieresis -140
+KPX Lcommaaccent quotedblright -140
+KPX Lcommaaccent quoteright -160
+KPX Lcommaaccent y -30
+KPX Lcommaaccent yacute -30
+KPX Lcommaaccent ydieresis -30
+KPX Lslash T -110
+KPX Lslash Tcaron -110
+KPX Lslash Tcommaaccent -110
+KPX Lslash V -110
+KPX Lslash W -70
+KPX Lslash Y -140
+KPX Lslash Yacute -140
+KPX Lslash Ydieresis -140
+KPX Lslash quotedblright -140
+KPX Lslash quoteright -160
+KPX Lslash y -30
+KPX Lslash yacute -30
+KPX Lslash ydieresis -30
+KPX O A -20
+KPX O Aacute -20
+KPX O Abreve -20
+KPX O Acircumflex -20
+KPX O Adieresis -20
+KPX O Agrave -20
+KPX O Amacron -20
+KPX O Aogonek -20
+KPX O Aring -20
+KPX O Atilde -20
+KPX O T -40
+KPX O Tcaron -40
+KPX O Tcommaaccent -40
+KPX O V -50
+KPX O W -30
+KPX O X -60
+KPX O Y -70
+KPX O Yacute -70
+KPX O Ydieresis -70
+KPX O comma -40
+KPX O period -40
+KPX Oacute A -20
+KPX Oacute Aacute -20
+KPX Oacute Abreve -20
+KPX Oacute Acircumflex -20
+KPX Oacute Adieresis -20
+KPX Oacute Agrave -20
+KPX Oacute Amacron -20
+KPX Oacute Aogonek -20
+KPX Oacute Aring -20
+KPX Oacute Atilde -20
+KPX Oacute T -40
+KPX Oacute Tcaron -40
+KPX Oacute Tcommaaccent -40
+KPX Oacute V -50
+KPX Oacute W -30
+KPX Oacute X -60
+KPX Oacute Y -70
+KPX Oacute Yacute -70
+KPX Oacute Ydieresis -70
+KPX Oacute comma -40
+KPX Oacute period -40
+KPX Ocircumflex A -20
+KPX Ocircumflex Aacute -20
+KPX Ocircumflex Abreve -20
+KPX Ocircumflex Acircumflex -20
+KPX Ocircumflex Adieresis -20
+KPX Ocircumflex Agrave -20
+KPX Ocircumflex Amacron -20
+KPX Ocircumflex Aogonek -20
+KPX Ocircumflex Aring -20
+KPX Ocircumflex Atilde -20
+KPX Ocircumflex T -40
+KPX Ocircumflex Tcaron -40
+KPX Ocircumflex Tcommaaccent -40
+KPX Ocircumflex V -50
+KPX Ocircumflex W -30
+KPX Ocircumflex X -60
+KPX Ocircumflex Y -70
+KPX Ocircumflex Yacute -70
+KPX Ocircumflex Ydieresis -70
+KPX Ocircumflex comma -40
+KPX Ocircumflex period -40
+KPX Odieresis A -20
+KPX Odieresis Aacute -20
+KPX Odieresis Abreve -20
+KPX Odieresis Acircumflex -20
+KPX Odieresis Adieresis -20
+KPX Odieresis Agrave -20
+KPX Odieresis Amacron -20
+KPX Odieresis Aogonek -20
+KPX Odieresis Aring -20
+KPX Odieresis Atilde -20
+KPX Odieresis T -40
+KPX Odieresis Tcaron -40
+KPX Odieresis Tcommaaccent -40
+KPX Odieresis V -50
+KPX Odieresis W -30
+KPX Odieresis X -60
+KPX Odieresis Y -70
+KPX Odieresis Yacute -70
+KPX Odieresis Ydieresis -70
+KPX Odieresis comma -40
+KPX Odieresis period -40
+KPX Ograve A -20
+KPX Ograve Aacute -20
+KPX Ograve Abreve -20
+KPX Ograve Acircumflex -20
+KPX Ograve Adieresis -20
+KPX Ograve Agrave -20
+KPX Ograve Amacron -20
+KPX Ograve Aogonek -20
+KPX Ograve Aring -20
+KPX Ograve Atilde -20
+KPX Ograve T -40
+KPX Ograve Tcaron -40
+KPX Ograve Tcommaaccent -40
+KPX Ograve V -50
+KPX Ograve W -30
+KPX Ograve X -60
+KPX Ograve Y -70
+KPX Ograve Yacute -70
+KPX Ograve Ydieresis -70
+KPX Ograve comma -40
+KPX Ograve period -40
+KPX Ohungarumlaut A -20
+KPX Ohungarumlaut Aacute -20
+KPX Ohungarumlaut Abreve -20
+KPX Ohungarumlaut Acircumflex -20
+KPX Ohungarumlaut Adieresis -20
+KPX Ohungarumlaut Agrave -20
+KPX Ohungarumlaut Amacron -20
+KPX Ohungarumlaut Aogonek -20
+KPX Ohungarumlaut Aring -20
+KPX Ohungarumlaut Atilde -20
+KPX Ohungarumlaut T -40
+KPX Ohungarumlaut Tcaron -40
+KPX Ohungarumlaut Tcommaaccent -40
+KPX Ohungarumlaut V -50
+KPX Ohungarumlaut W -30
+KPX Ohungarumlaut X -60
+KPX Ohungarumlaut Y -70
+KPX Ohungarumlaut Yacute -70
+KPX Ohungarumlaut Ydieresis -70
+KPX Ohungarumlaut comma -40
+KPX Ohungarumlaut period -40
+KPX Omacron A -20
+KPX Omacron Aacute -20
+KPX Omacron Abreve -20
+KPX Omacron Acircumflex -20
+KPX Omacron Adieresis -20
+KPX Omacron Agrave -20
+KPX Omacron Amacron -20
+KPX Omacron Aogonek -20
+KPX Omacron Aring -20
+KPX Omacron Atilde -20
+KPX Omacron T -40
+KPX Omacron Tcaron -40
+KPX Omacron Tcommaaccent -40
+KPX Omacron V -50
+KPX Omacron W -30
+KPX Omacron X -60
+KPX Omacron Y -70
+KPX Omacron Yacute -70
+KPX Omacron Ydieresis -70
+KPX Omacron comma -40
+KPX Omacron period -40
+KPX Oslash A -20
+KPX Oslash Aacute -20
+KPX Oslash Abreve -20
+KPX Oslash Acircumflex -20
+KPX Oslash Adieresis -20
+KPX Oslash Agrave -20
+KPX Oslash Amacron -20
+KPX Oslash Aogonek -20
+KPX Oslash Aring -20
+KPX Oslash Atilde -20
+KPX Oslash T -40
+KPX Oslash Tcaron -40
+KPX Oslash Tcommaaccent -40
+KPX Oslash V -50
+KPX Oslash W -30
+KPX Oslash X -60
+KPX Oslash Y -70
+KPX Oslash Yacute -70
+KPX Oslash Ydieresis -70
+KPX Oslash comma -40
+KPX Oslash period -40
+KPX Otilde A -20
+KPX Otilde Aacute -20
+KPX Otilde Abreve -20
+KPX Otilde Acircumflex -20
+KPX Otilde Adieresis -20
+KPX Otilde Agrave -20
+KPX Otilde Amacron -20
+KPX Otilde Aogonek -20
+KPX Otilde Aring -20
+KPX Otilde Atilde -20
+KPX Otilde T -40
+KPX Otilde Tcaron -40
+KPX Otilde Tcommaaccent -40
+KPX Otilde V -50
+KPX Otilde W -30
+KPX Otilde X -60
+KPX Otilde Y -70
+KPX Otilde Yacute -70
+KPX Otilde Ydieresis -70
+KPX Otilde comma -40
+KPX Otilde period -40
+KPX P A -120
+KPX P Aacute -120
+KPX P Abreve -120
+KPX P Acircumflex -120
+KPX P Adieresis -120
+KPX P Agrave -120
+KPX P Amacron -120
+KPX P Aogonek -120
+KPX P Aring -120
+KPX P Atilde -120
+KPX P a -40
+KPX P aacute -40
+KPX P abreve -40
+KPX P acircumflex -40
+KPX P adieresis -40
+KPX P agrave -40
+KPX P amacron -40
+KPX P aogonek -40
+KPX P aring -40
+KPX P atilde -40
+KPX P comma -180
+KPX P e -50
+KPX P eacute -50
+KPX P ecaron -50
+KPX P ecircumflex -50
+KPX P edieresis -50
+KPX P edotaccent -50
+KPX P egrave -50
+KPX P emacron -50
+KPX P eogonek -50
+KPX P o -50
+KPX P oacute -50
+KPX P ocircumflex -50
+KPX P odieresis -50
+KPX P ograve -50
+KPX P ohungarumlaut -50
+KPX P omacron -50
+KPX P oslash -50
+KPX P otilde -50
+KPX P period -180
+KPX Q U -10
+KPX Q Uacute -10
+KPX Q Ucircumflex -10
+KPX Q Udieresis -10
+KPX Q Ugrave -10
+KPX Q Uhungarumlaut -10
+KPX Q Umacron -10
+KPX Q Uogonek -10
+KPX Q Uring -10
+KPX R O -20
+KPX R Oacute -20
+KPX R Ocircumflex -20
+KPX R Odieresis -20
+KPX R Ograve -20
+KPX R Ohungarumlaut -20
+KPX R Omacron -20
+KPX R Oslash -20
+KPX R Otilde -20
+KPX R T -30
+KPX R Tcaron -30
+KPX R Tcommaaccent -30
+KPX R U -40
+KPX R Uacute -40
+KPX R Ucircumflex -40
+KPX R Udieresis -40
+KPX R Ugrave -40
+KPX R Uhungarumlaut -40
+KPX R Umacron -40
+KPX R Uogonek -40
+KPX R Uring -40
+KPX R V -50
+KPX R W -30
+KPX R Y -50
+KPX R Yacute -50
+KPX R Ydieresis -50
+KPX Racute O -20
+KPX Racute Oacute -20
+KPX Racute Ocircumflex -20
+KPX Racute Odieresis -20
+KPX Racute Ograve -20
+KPX Racute Ohungarumlaut -20
+KPX Racute Omacron -20
+KPX Racute Oslash -20
+KPX Racute Otilde -20
+KPX Racute T -30
+KPX Racute Tcaron -30
+KPX Racute Tcommaaccent -30
+KPX Racute U -40
+KPX Racute Uacute -40
+KPX Racute Ucircumflex -40
+KPX Racute Udieresis -40
+KPX Racute Ugrave -40
+KPX Racute Uhungarumlaut -40
+KPX Racute Umacron -40
+KPX Racute Uogonek -40
+KPX Racute Uring -40
+KPX Racute V -50
+KPX Racute W -30
+KPX Racute Y -50
+KPX Racute Yacute -50
+KPX Racute Ydieresis -50
+KPX Rcaron O -20
+KPX Rcaron Oacute -20
+KPX Rcaron Ocircumflex -20
+KPX Rcaron Odieresis -20
+KPX Rcaron Ograve -20
+KPX Rcaron Ohungarumlaut -20
+KPX Rcaron Omacron -20
+KPX Rcaron Oslash -20
+KPX Rcaron Otilde -20
+KPX Rcaron T -30
+KPX Rcaron Tcaron -30
+KPX Rcaron Tcommaaccent -30
+KPX Rcaron U -40
+KPX Rcaron Uacute -40
+KPX Rcaron Ucircumflex -40
+KPX Rcaron Udieresis -40
+KPX Rcaron Ugrave -40
+KPX Rcaron Uhungarumlaut -40
+KPX Rcaron Umacron -40
+KPX Rcaron Uogonek -40
+KPX Rcaron Uring -40
+KPX Rcaron V -50
+KPX Rcaron W -30
+KPX Rcaron Y -50
+KPX Rcaron Yacute -50
+KPX Rcaron Ydieresis -50
+KPX Rcommaaccent O -20
+KPX Rcommaaccent Oacute -20
+KPX Rcommaaccent Ocircumflex -20
+KPX Rcommaaccent Odieresis -20
+KPX Rcommaaccent Ograve -20
+KPX Rcommaaccent Ohungarumlaut -20
+KPX Rcommaaccent Omacron -20
+KPX Rcommaaccent Oslash -20
+KPX Rcommaaccent Otilde -20
+KPX Rcommaaccent T -30
+KPX Rcommaaccent Tcaron -30
+KPX Rcommaaccent Tcommaaccent -30
+KPX Rcommaaccent U -40
+KPX Rcommaaccent Uacute -40
+KPX Rcommaaccent Ucircumflex -40
+KPX Rcommaaccent Udieresis -40
+KPX Rcommaaccent Ugrave -40
+KPX Rcommaaccent Uhungarumlaut -40
+KPX Rcommaaccent Umacron -40
+KPX Rcommaaccent Uogonek -40
+KPX Rcommaaccent Uring -40
+KPX Rcommaaccent V -50
+KPX Rcommaaccent W -30
+KPX Rcommaaccent Y -50
+KPX Rcommaaccent Yacute -50
+KPX Rcommaaccent Ydieresis -50
+KPX S comma -20
+KPX S period -20
+KPX Sacute comma -20
+KPX Sacute period -20
+KPX Scaron comma -20
+KPX Scaron period -20
+KPX Scedilla comma -20
+KPX Scedilla period -20
+KPX Scommaaccent comma -20
+KPX Scommaaccent period -20
+KPX T A -120
+KPX T Aacute -120
+KPX T Abreve -120
+KPX T Acircumflex -120
+KPX T Adieresis -120
+KPX T Agrave -120
+KPX T Amacron -120
+KPX T Aogonek -120
+KPX T Aring -120
+KPX T Atilde -120
+KPX T O -40
+KPX T Oacute -40
+KPX T Ocircumflex -40
+KPX T Odieresis -40
+KPX T Ograve -40
+KPX T Ohungarumlaut -40
+KPX T Omacron -40
+KPX T Oslash -40
+KPX T Otilde -40
+KPX T a -120
+KPX T aacute -120
+KPX T abreve -60
+KPX T acircumflex -120
+KPX T adieresis -120
+KPX T agrave -120
+KPX T amacron -60
+KPX T aogonek -120
+KPX T aring -120
+KPX T atilde -60
+KPX T colon -20
+KPX T comma -120
+KPX T e -120
+KPX T eacute -120
+KPX T ecaron -120
+KPX T ecircumflex -120
+KPX T edieresis -120
+KPX T edotaccent -120
+KPX T egrave -60
+KPX T emacron -60
+KPX T eogonek -120
+KPX T hyphen -140
+KPX T o -120
+KPX T oacute -120
+KPX T ocircumflex -120
+KPX T odieresis -120
+KPX T ograve -120
+KPX T ohungarumlaut -120
+KPX T omacron -60
+KPX T oslash -120
+KPX T otilde -60
+KPX T period -120
+KPX T r -120
+KPX T racute -120
+KPX T rcaron -120
+KPX T rcommaaccent -120
+KPX T semicolon -20
+KPX T u -120
+KPX T uacute -120
+KPX T ucircumflex -120
+KPX T udieresis -120
+KPX T ugrave -120
+KPX T uhungarumlaut -120
+KPX T umacron -60
+KPX T uogonek -120
+KPX T uring -120
+KPX T w -120
+KPX T y -120
+KPX T yacute -120
+KPX T ydieresis -60
+KPX Tcaron A -120
+KPX Tcaron Aacute -120
+KPX Tcaron Abreve -120
+KPX Tcaron Acircumflex -120
+KPX Tcaron Adieresis -120
+KPX Tcaron Agrave -120
+KPX Tcaron Amacron -120
+KPX Tcaron Aogonek -120
+KPX Tcaron Aring -120
+KPX Tcaron Atilde -120
+KPX Tcaron O -40
+KPX Tcaron Oacute -40
+KPX Tcaron Ocircumflex -40
+KPX Tcaron Odieresis -40
+KPX Tcaron Ograve -40
+KPX Tcaron Ohungarumlaut -40
+KPX Tcaron Omacron -40
+KPX Tcaron Oslash -40
+KPX Tcaron Otilde -40
+KPX Tcaron a -120
+KPX Tcaron aacute -120
+KPX Tcaron abreve -60
+KPX Tcaron acircumflex -120
+KPX Tcaron adieresis -120
+KPX Tcaron agrave -120
+KPX Tcaron amacron -60
+KPX Tcaron aogonek -120
+KPX Tcaron aring -120
+KPX Tcaron atilde -60
+KPX Tcaron colon -20
+KPX Tcaron comma -120
+KPX Tcaron e -120
+KPX Tcaron eacute -120
+KPX Tcaron ecaron -120
+KPX Tcaron ecircumflex -120
+KPX Tcaron edieresis -120
+KPX Tcaron edotaccent -120
+KPX Tcaron egrave -60
+KPX Tcaron emacron -60
+KPX Tcaron eogonek -120
+KPX Tcaron hyphen -140
+KPX Tcaron o -120
+KPX Tcaron oacute -120
+KPX Tcaron ocircumflex -120
+KPX Tcaron odieresis -120
+KPX Tcaron ograve -120
+KPX Tcaron ohungarumlaut -120
+KPX Tcaron omacron -60
+KPX Tcaron oslash -120
+KPX Tcaron otilde -60
+KPX Tcaron period -120
+KPX Tcaron r -120
+KPX Tcaron racute -120
+KPX Tcaron rcaron -120
+KPX Tcaron rcommaaccent -120
+KPX Tcaron semicolon -20
+KPX Tcaron u -120
+KPX Tcaron uacute -120
+KPX Tcaron ucircumflex -120
+KPX Tcaron udieresis -120
+KPX Tcaron ugrave -120
+KPX Tcaron uhungarumlaut -120
+KPX Tcaron umacron -60
+KPX Tcaron uogonek -120
+KPX Tcaron uring -120
+KPX Tcaron w -120
+KPX Tcaron y -120
+KPX Tcaron yacute -120
+KPX Tcaron ydieresis -60
+KPX Tcommaaccent A -120
+KPX Tcommaaccent Aacute -120
+KPX Tcommaaccent Abreve -120
+KPX Tcommaaccent Acircumflex -120
+KPX Tcommaaccent Adieresis -120
+KPX Tcommaaccent Agrave -120
+KPX Tcommaaccent Amacron -120
+KPX Tcommaaccent Aogonek -120
+KPX Tcommaaccent Aring -120
+KPX Tcommaaccent Atilde -120
+KPX Tcommaaccent O -40
+KPX Tcommaaccent Oacute -40
+KPX Tcommaaccent Ocircumflex -40
+KPX Tcommaaccent Odieresis -40
+KPX Tcommaaccent Ograve -40
+KPX Tcommaaccent Ohungarumlaut -40
+KPX Tcommaaccent Omacron -40
+KPX Tcommaaccent Oslash -40
+KPX Tcommaaccent Otilde -40
+KPX Tcommaaccent a -120
+KPX Tcommaaccent aacute -120
+KPX Tcommaaccent abreve -60
+KPX Tcommaaccent acircumflex -120
+KPX Tcommaaccent adieresis -120
+KPX Tcommaaccent agrave -120
+KPX Tcommaaccent amacron -60
+KPX Tcommaaccent aogonek -120
+KPX Tcommaaccent aring -120
+KPX Tcommaaccent atilde -60
+KPX Tcommaaccent colon -20
+KPX Tcommaaccent comma -120
+KPX Tcommaaccent e -120
+KPX Tcommaaccent eacute -120
+KPX Tcommaaccent ecaron -120
+KPX Tcommaaccent ecircumflex -120
+KPX Tcommaaccent edieresis -120
+KPX Tcommaaccent edotaccent -120
+KPX Tcommaaccent egrave -60
+KPX Tcommaaccent emacron -60
+KPX Tcommaaccent eogonek -120
+KPX Tcommaaccent hyphen -140
+KPX Tcommaaccent o -120
+KPX Tcommaaccent oacute -120
+KPX Tcommaaccent ocircumflex -120
+KPX Tcommaaccent odieresis -120
+KPX Tcommaaccent ograve -120
+KPX Tcommaaccent ohungarumlaut -120
+KPX Tcommaaccent omacron -60
+KPX Tcommaaccent oslash -120
+KPX Tcommaaccent otilde -60
+KPX Tcommaaccent period -120
+KPX Tcommaaccent r -120
+KPX Tcommaaccent racute -120
+KPX Tcommaaccent rcaron -120
+KPX Tcommaaccent rcommaaccent -120
+KPX Tcommaaccent semicolon -20
+KPX Tcommaaccent u -120
+KPX Tcommaaccent uacute -120
+KPX Tcommaaccent ucircumflex -120
+KPX Tcommaaccent udieresis -120
+KPX Tcommaaccent ugrave -120
+KPX Tcommaaccent uhungarumlaut -120
+KPX Tcommaaccent umacron -60
+KPX Tcommaaccent uogonek -120
+KPX Tcommaaccent uring -120
+KPX Tcommaaccent w -120
+KPX Tcommaaccent y -120
+KPX Tcommaaccent yacute -120
+KPX Tcommaaccent ydieresis -60
+KPX U A -40
+KPX U Aacute -40
+KPX U Abreve -40
+KPX U Acircumflex -40
+KPX U Adieresis -40
+KPX U Agrave -40
+KPX U Amacron -40
+KPX U Aogonek -40
+KPX U Aring -40
+KPX U Atilde -40
+KPX U comma -40
+KPX U period -40
+KPX Uacute A -40
+KPX Uacute Aacute -40
+KPX Uacute Abreve -40
+KPX Uacute Acircumflex -40
+KPX Uacute Adieresis -40
+KPX Uacute Agrave -40
+KPX Uacute Amacron -40
+KPX Uacute Aogonek -40
+KPX Uacute Aring -40
+KPX Uacute Atilde -40
+KPX Uacute comma -40
+KPX Uacute period -40
+KPX Ucircumflex A -40
+KPX Ucircumflex Aacute -40
+KPX Ucircumflex Abreve -40
+KPX Ucircumflex Acircumflex -40
+KPX Ucircumflex Adieresis -40
+KPX Ucircumflex Agrave -40
+KPX Ucircumflex Amacron -40
+KPX Ucircumflex Aogonek -40
+KPX Ucircumflex Aring -40
+KPX Ucircumflex Atilde -40
+KPX Ucircumflex comma -40
+KPX Ucircumflex period -40
+KPX Udieresis A -40
+KPX Udieresis Aacute -40
+KPX Udieresis Abreve -40
+KPX Udieresis Acircumflex -40
+KPX Udieresis Adieresis -40
+KPX Udieresis Agrave -40
+KPX Udieresis Amacron -40
+KPX Udieresis Aogonek -40
+KPX Udieresis Aring -40
+KPX Udieresis Atilde -40
+KPX Udieresis comma -40
+KPX Udieresis period -40
+KPX Ugrave A -40
+KPX Ugrave Aacute -40
+KPX Ugrave Abreve -40
+KPX Ugrave Acircumflex -40
+KPX Ugrave Adieresis -40
+KPX Ugrave Agrave -40
+KPX Ugrave Amacron -40
+KPX Ugrave Aogonek -40
+KPX Ugrave Aring -40
+KPX Ugrave Atilde -40
+KPX Ugrave comma -40
+KPX Ugrave period -40
+KPX Uhungarumlaut A -40
+KPX Uhungarumlaut Aacute -40
+KPX Uhungarumlaut Abreve -40
+KPX Uhungarumlaut Acircumflex -40
+KPX Uhungarumlaut Adieresis -40
+KPX Uhungarumlaut Agrave -40
+KPX Uhungarumlaut Amacron -40
+KPX Uhungarumlaut Aogonek -40
+KPX Uhungarumlaut Aring -40
+KPX Uhungarumlaut Atilde -40
+KPX Uhungarumlaut comma -40
+KPX Uhungarumlaut period -40
+KPX Umacron A -40
+KPX Umacron Aacute -40
+KPX Umacron Abreve -40
+KPX Umacron Acircumflex -40
+KPX Umacron Adieresis -40
+KPX Umacron Agrave -40
+KPX Umacron Amacron -40
+KPX Umacron Aogonek -40
+KPX Umacron Aring -40
+KPX Umacron Atilde -40
+KPX Umacron comma -40
+KPX Umacron period -40
+KPX Uogonek A -40
+KPX Uogonek Aacute -40
+KPX Uogonek Abreve -40
+KPX Uogonek Acircumflex -40
+KPX Uogonek Adieresis -40
+KPX Uogonek Agrave -40
+KPX Uogonek Amacron -40
+KPX Uogonek Aogonek -40
+KPX Uogonek Aring -40
+KPX Uogonek Atilde -40
+KPX Uogonek comma -40
+KPX Uogonek period -40
+KPX Uring A -40
+KPX Uring Aacute -40
+KPX Uring Abreve -40
+KPX Uring Acircumflex -40
+KPX Uring Adieresis -40
+KPX Uring Agrave -40
+KPX Uring Amacron -40
+KPX Uring Aogonek -40
+KPX Uring Aring -40
+KPX Uring Atilde -40
+KPX Uring comma -40
+KPX Uring period -40
+KPX V A -80
+KPX V Aacute -80
+KPX V Abreve -80
+KPX V Acircumflex -80
+KPX V Adieresis -80
+KPX V Agrave -80
+KPX V Amacron -80
+KPX V Aogonek -80
+KPX V Aring -80
+KPX V Atilde -80
+KPX V G -40
+KPX V Gbreve -40
+KPX V Gcommaaccent -40
+KPX V O -40
+KPX V Oacute -40
+KPX V Ocircumflex -40
+KPX V Odieresis -40
+KPX V Ograve -40
+KPX V Ohungarumlaut -40
+KPX V Omacron -40
+KPX V Oslash -40
+KPX V Otilde -40
+KPX V a -70
+KPX V aacute -70
+KPX V abreve -70
+KPX V acircumflex -70
+KPX V adieresis -70
+KPX V agrave -70
+KPX V amacron -70
+KPX V aogonek -70
+KPX V aring -70
+KPX V atilde -70
+KPX V colon -40
+KPX V comma -125
+KPX V e -80
+KPX V eacute -80
+KPX V ecaron -80
+KPX V ecircumflex -80
+KPX V edieresis -80
+KPX V edotaccent -80
+KPX V egrave -80
+KPX V emacron -80
+KPX V eogonek -80
+KPX V hyphen -80
+KPX V o -80
+KPX V oacute -80
+KPX V ocircumflex -80
+KPX V odieresis -80
+KPX V ograve -80
+KPX V ohungarumlaut -80
+KPX V omacron -80
+KPX V oslash -80
+KPX V otilde -80
+KPX V period -125
+KPX V semicolon -40
+KPX V u -70
+KPX V uacute -70
+KPX V ucircumflex -70
+KPX V udieresis -70
+KPX V ugrave -70
+KPX V uhungarumlaut -70
+KPX V umacron -70
+KPX V uogonek -70
+KPX V uring -70
+KPX W A -50
+KPX W Aacute -50
+KPX W Abreve -50
+KPX W Acircumflex -50
+KPX W Adieresis -50
+KPX W Agrave -50
+KPX W Amacron -50
+KPX W Aogonek -50
+KPX W Aring -50
+KPX W Atilde -50
+KPX W O -20
+KPX W Oacute -20
+KPX W Ocircumflex -20
+KPX W Odieresis -20
+KPX W Ograve -20
+KPX W Ohungarumlaut -20
+KPX W Omacron -20
+KPX W Oslash -20
+KPX W Otilde -20
+KPX W a -40
+KPX W aacute -40
+KPX W abreve -40
+KPX W acircumflex -40
+KPX W adieresis -40
+KPX W agrave -40
+KPX W amacron -40
+KPX W aogonek -40
+KPX W aring -40
+KPX W atilde -40
+KPX W comma -80
+KPX W e -30
+KPX W eacute -30
+KPX W ecaron -30
+KPX W ecircumflex -30
+KPX W edieresis -30
+KPX W edotaccent -30
+KPX W egrave -30
+KPX W emacron -30
+KPX W eogonek -30
+KPX W hyphen -40
+KPX W o -30
+KPX W oacute -30
+KPX W ocircumflex -30
+KPX W odieresis -30
+KPX W ograve -30
+KPX W ohungarumlaut -30
+KPX W omacron -30
+KPX W oslash -30
+KPX W otilde -30
+KPX W period -80
+KPX W u -30
+KPX W uacute -30
+KPX W ucircumflex -30
+KPX W udieresis -30
+KPX W ugrave -30
+KPX W uhungarumlaut -30
+KPX W umacron -30
+KPX W uogonek -30
+KPX W uring -30
+KPX W y -20
+KPX W yacute -20
+KPX W ydieresis -20
+KPX Y A -110
+KPX Y Aacute -110
+KPX Y Abreve -110
+KPX Y Acircumflex -110
+KPX Y Adieresis -110
+KPX Y Agrave -110
+KPX Y Amacron -110
+KPX Y Aogonek -110
+KPX Y Aring -110
+KPX Y Atilde -110
+KPX Y O -85
+KPX Y Oacute -85
+KPX Y Ocircumflex -85
+KPX Y Odieresis -85
+KPX Y Ograve -85
+KPX Y Ohungarumlaut -85
+KPX Y Omacron -85
+KPX Y Oslash -85
+KPX Y Otilde -85
+KPX Y a -140
+KPX Y aacute -140
+KPX Y abreve -70
+KPX Y acircumflex -140
+KPX Y adieresis -140
+KPX Y agrave -140
+KPX Y amacron -70
+KPX Y aogonek -140
+KPX Y aring -140
+KPX Y atilde -140
+KPX Y colon -60
+KPX Y comma -140
+KPX Y e -140
+KPX Y eacute -140
+KPX Y ecaron -140
+KPX Y ecircumflex -140
+KPX Y edieresis -140
+KPX Y edotaccent -140
+KPX Y egrave -140
+KPX Y emacron -70
+KPX Y eogonek -140
+KPX Y hyphen -140
+KPX Y i -20
+KPX Y iacute -20
+KPX Y iogonek -20
+KPX Y o -140
+KPX Y oacute -140
+KPX Y ocircumflex -140
+KPX Y odieresis -140
+KPX Y ograve -140
+KPX Y ohungarumlaut -140
+KPX Y omacron -140
+KPX Y oslash -140
+KPX Y otilde -140
+KPX Y period -140
+KPX Y semicolon -60
+KPX Y u -110
+KPX Y uacute -110
+KPX Y ucircumflex -110
+KPX Y udieresis -110
+KPX Y ugrave -110
+KPX Y uhungarumlaut -110
+KPX Y umacron -110
+KPX Y uogonek -110
+KPX Y uring -110
+KPX Yacute A -110
+KPX Yacute Aacute -110
+KPX Yacute Abreve -110
+KPX Yacute Acircumflex -110
+KPX Yacute Adieresis -110
+KPX Yacute Agrave -110
+KPX Yacute Amacron -110
+KPX Yacute Aogonek -110
+KPX Yacute Aring -110
+KPX Yacute Atilde -110
+KPX Yacute O -85
+KPX Yacute Oacute -85
+KPX Yacute Ocircumflex -85
+KPX Yacute Odieresis -85
+KPX Yacute Ograve -85
+KPX Yacute Ohungarumlaut -85
+KPX Yacute Omacron -85
+KPX Yacute Oslash -85
+KPX Yacute Otilde -85
+KPX Yacute a -140
+KPX Yacute aacute -140
+KPX Yacute abreve -70
+KPX Yacute acircumflex -140
+KPX Yacute adieresis -140
+KPX Yacute agrave -140
+KPX Yacute amacron -70
+KPX Yacute aogonek -140
+KPX Yacute aring -140
+KPX Yacute atilde -70
+KPX Yacute colon -60
+KPX Yacute comma -140
+KPX Yacute e -140
+KPX Yacute eacute -140
+KPX Yacute ecaron -140
+KPX Yacute ecircumflex -140
+KPX Yacute edieresis -140
+KPX Yacute edotaccent -140
+KPX Yacute egrave -140
+KPX Yacute emacron -70
+KPX Yacute eogonek -140
+KPX Yacute hyphen -140
+KPX Yacute i -20
+KPX Yacute iacute -20
+KPX Yacute iogonek -20
+KPX Yacute o -140
+KPX Yacute oacute -140
+KPX Yacute ocircumflex -140
+KPX Yacute odieresis -140
+KPX Yacute ograve -140
+KPX Yacute ohungarumlaut -140
+KPX Yacute omacron -70
+KPX Yacute oslash -140
+KPX Yacute otilde -140
+KPX Yacute period -140
+KPX Yacute semicolon -60
+KPX Yacute u -110
+KPX Yacute uacute -110
+KPX Yacute ucircumflex -110
+KPX Yacute udieresis -110
+KPX Yacute ugrave -110
+KPX Yacute uhungarumlaut -110
+KPX Yacute umacron -110
+KPX Yacute uogonek -110
+KPX Yacute uring -110
+KPX Ydieresis A -110
+KPX Ydieresis Aacute -110
+KPX Ydieresis Abreve -110
+KPX Ydieresis Acircumflex -110
+KPX Ydieresis Adieresis -110
+KPX Ydieresis Agrave -110
+KPX Ydieresis Amacron -110
+KPX Ydieresis Aogonek -110
+KPX Ydieresis Aring -110
+KPX Ydieresis Atilde -110
+KPX Ydieresis O -85
+KPX Ydieresis Oacute -85
+KPX Ydieresis Ocircumflex -85
+KPX Ydieresis Odieresis -85
+KPX Ydieresis Ograve -85
+KPX Ydieresis Ohungarumlaut -85
+KPX Ydieresis Omacron -85
+KPX Ydieresis Oslash -85
+KPX Ydieresis Otilde -85
+KPX Ydieresis a -140
+KPX Ydieresis aacute -140
+KPX Ydieresis abreve -70
+KPX Ydieresis acircumflex -140
+KPX Ydieresis adieresis -140
+KPX Ydieresis agrave -140
+KPX Ydieresis amacron -70
+KPX Ydieresis aogonek -140
+KPX Ydieresis aring -140
+KPX Ydieresis atilde -70
+KPX Ydieresis colon -60
+KPX Ydieresis comma -140
+KPX Ydieresis e -140
+KPX Ydieresis eacute -140
+KPX Ydieresis ecaron -140
+KPX Ydieresis ecircumflex -140
+KPX Ydieresis edieresis -140
+KPX Ydieresis edotaccent -140
+KPX Ydieresis egrave -140
+KPX Ydieresis emacron -70
+KPX Ydieresis eogonek -140
+KPX Ydieresis hyphen -140
+KPX Ydieresis i -20
+KPX Ydieresis iacute -20
+KPX Ydieresis iogonek -20
+KPX Ydieresis o -140
+KPX Ydieresis oacute -140
+KPX Ydieresis ocircumflex -140
+KPX Ydieresis odieresis -140
+KPX Ydieresis ograve -140
+KPX Ydieresis ohungarumlaut -140
+KPX Ydieresis omacron -140
+KPX Ydieresis oslash -140
+KPX Ydieresis otilde -140
+KPX Ydieresis period -140
+KPX Ydieresis semicolon -60
+KPX Ydieresis u -110
+KPX Ydieresis uacute -110
+KPX Ydieresis ucircumflex -110
+KPX Ydieresis udieresis -110
+KPX Ydieresis ugrave -110
+KPX Ydieresis uhungarumlaut -110
+KPX Ydieresis umacron -110
+KPX Ydieresis uogonek -110
+KPX Ydieresis uring -110
+KPX a v -20
+KPX a w -20
+KPX a y -30
+KPX a yacute -30
+KPX a ydieresis -30
+KPX aacute v -20
+KPX aacute w -20
+KPX aacute y -30
+KPX aacute yacute -30
+KPX aacute ydieresis -30
+KPX abreve v -20
+KPX abreve w -20
+KPX abreve y -30
+KPX abreve yacute -30
+KPX abreve ydieresis -30
+KPX acircumflex v -20
+KPX acircumflex w -20
+KPX acircumflex y -30
+KPX acircumflex yacute -30
+KPX acircumflex ydieresis -30
+KPX adieresis v -20
+KPX adieresis w -20
+KPX adieresis y -30
+KPX adieresis yacute -30
+KPX adieresis ydieresis -30
+KPX agrave v -20
+KPX agrave w -20
+KPX agrave y -30
+KPX agrave yacute -30
+KPX agrave ydieresis -30
+KPX amacron v -20
+KPX amacron w -20
+KPX amacron y -30
+KPX amacron yacute -30
+KPX amacron ydieresis -30
+KPX aogonek v -20
+KPX aogonek w -20
+KPX aogonek y -30
+KPX aogonek yacute -30
+KPX aogonek ydieresis -30
+KPX aring v -20
+KPX aring w -20
+KPX aring y -30
+KPX aring yacute -30
+KPX aring ydieresis -30
+KPX atilde v -20
+KPX atilde w -20
+KPX atilde y -30
+KPX atilde yacute -30
+KPX atilde ydieresis -30
+KPX b b -10
+KPX b comma -40
+KPX b l -20
+KPX b lacute -20
+KPX b lcommaaccent -20
+KPX b lslash -20
+KPX b period -40
+KPX b u -20
+KPX b uacute -20
+KPX b ucircumflex -20
+KPX b udieresis -20
+KPX b ugrave -20
+KPX b uhungarumlaut -20
+KPX b umacron -20
+KPX b uogonek -20
+KPX b uring -20
+KPX b v -20
+KPX b y -20
+KPX b yacute -20
+KPX b ydieresis -20
+KPX c comma -15
+KPX c k -20
+KPX c kcommaaccent -20
+KPX cacute comma -15
+KPX cacute k -20
+KPX cacute kcommaaccent -20
+KPX ccaron comma -15
+KPX ccaron k -20
+KPX ccaron kcommaaccent -20
+KPX ccedilla comma -15
+KPX ccedilla k -20
+KPX ccedilla kcommaaccent -20
+KPX colon space -50
+KPX comma quotedblright -100
+KPX comma quoteright -100
+KPX e comma -15
+KPX e period -15
+KPX e v -30
+KPX e w -20
+KPX e x -30
+KPX e y -20
+KPX e yacute -20
+KPX e ydieresis -20
+KPX eacute comma -15
+KPX eacute period -15
+KPX eacute v -30
+KPX eacute w -20
+KPX eacute x -30
+KPX eacute y -20
+KPX eacute yacute -20
+KPX eacute ydieresis -20
+KPX ecaron comma -15
+KPX ecaron period -15
+KPX ecaron v -30
+KPX ecaron w -20
+KPX ecaron x -30
+KPX ecaron y -20
+KPX ecaron yacute -20
+KPX ecaron ydieresis -20
+KPX ecircumflex comma -15
+KPX ecircumflex period -15
+KPX ecircumflex v -30
+KPX ecircumflex w -20
+KPX ecircumflex x -30
+KPX ecircumflex y -20
+KPX ecircumflex yacute -20
+KPX ecircumflex ydieresis -20
+KPX edieresis comma -15
+KPX edieresis period -15
+KPX edieresis v -30
+KPX edieresis w -20
+KPX edieresis x -30
+KPX edieresis y -20
+KPX edieresis yacute -20
+KPX edieresis ydieresis -20
+KPX edotaccent comma -15
+KPX edotaccent period -15
+KPX edotaccent v -30
+KPX edotaccent w -20
+KPX edotaccent x -30
+KPX edotaccent y -20
+KPX edotaccent yacute -20
+KPX edotaccent ydieresis -20
+KPX egrave comma -15
+KPX egrave period -15
+KPX egrave v -30
+KPX egrave w -20
+KPX egrave x -30
+KPX egrave y -20
+KPX egrave yacute -20
+KPX egrave ydieresis -20
+KPX emacron comma -15
+KPX emacron period -15
+KPX emacron v -30
+KPX emacron w -20
+KPX emacron x -30
+KPX emacron y -20
+KPX emacron yacute -20
+KPX emacron ydieresis -20
+KPX eogonek comma -15
+KPX eogonek period -15
+KPX eogonek v -30
+KPX eogonek w -20
+KPX eogonek x -30
+KPX eogonek y -20
+KPX eogonek yacute -20
+KPX eogonek ydieresis -20
+KPX f a -30
+KPX f aacute -30
+KPX f abreve -30
+KPX f acircumflex -30
+KPX f adieresis -30
+KPX f agrave -30
+KPX f amacron -30
+KPX f aogonek -30
+KPX f aring -30
+KPX f atilde -30
+KPX f comma -30
+KPX f dotlessi -28
+KPX f e -30
+KPX f eacute -30
+KPX f ecaron -30
+KPX f ecircumflex -30
+KPX f edieresis -30
+KPX f edotaccent -30
+KPX f egrave -30
+KPX f emacron -30
+KPX f eogonek -30
+KPX f o -30
+KPX f oacute -30
+KPX f ocircumflex -30
+KPX f odieresis -30
+KPX f ograve -30
+KPX f ohungarumlaut -30
+KPX f omacron -30
+KPX f oslash -30
+KPX f otilde -30
+KPX f period -30
+KPX f quotedblright 60
+KPX f quoteright 50
+KPX g r -10
+KPX g racute -10
+KPX g rcaron -10
+KPX g rcommaaccent -10
+KPX gbreve r -10
+KPX gbreve racute -10
+KPX gbreve rcaron -10
+KPX gbreve rcommaaccent -10
+KPX gcommaaccent r -10
+KPX gcommaaccent racute -10
+KPX gcommaaccent rcaron -10
+KPX gcommaaccent rcommaaccent -10
+KPX h y -30
+KPX h yacute -30
+KPX h ydieresis -30
+KPX k e -20
+KPX k eacute -20
+KPX k ecaron -20
+KPX k ecircumflex -20
+KPX k edieresis -20
+KPX k edotaccent -20
+KPX k egrave -20
+KPX k emacron -20
+KPX k eogonek -20
+KPX k o -20
+KPX k oacute -20
+KPX k ocircumflex -20
+KPX k odieresis -20
+KPX k ograve -20
+KPX k ohungarumlaut -20
+KPX k omacron -20
+KPX k oslash -20
+KPX k otilde -20
+KPX kcommaaccent e -20
+KPX kcommaaccent eacute -20
+KPX kcommaaccent ecaron -20
+KPX kcommaaccent ecircumflex -20
+KPX kcommaaccent edieresis -20
+KPX kcommaaccent edotaccent -20
+KPX kcommaaccent egrave -20
+KPX kcommaaccent emacron -20
+KPX kcommaaccent eogonek -20
+KPX kcommaaccent o -20
+KPX kcommaaccent oacute -20
+KPX kcommaaccent ocircumflex -20
+KPX kcommaaccent odieresis -20
+KPX kcommaaccent ograve -20
+KPX kcommaaccent ohungarumlaut -20
+KPX kcommaaccent omacron -20
+KPX kcommaaccent oslash -20
+KPX kcommaaccent otilde -20
+KPX m u -10
+KPX m uacute -10
+KPX m ucircumflex -10
+KPX m udieresis -10
+KPX m ugrave -10
+KPX m uhungarumlaut -10
+KPX m umacron -10
+KPX m uogonek -10
+KPX m uring -10
+KPX m y -15
+KPX m yacute -15
+KPX m ydieresis -15
+KPX n u -10
+KPX n uacute -10
+KPX n ucircumflex -10
+KPX n udieresis -10
+KPX n ugrave -10
+KPX n uhungarumlaut -10
+KPX n umacron -10
+KPX n uogonek -10
+KPX n uring -10
+KPX n v -20
+KPX n y -15
+KPX n yacute -15
+KPX n ydieresis -15
+KPX nacute u -10
+KPX nacute uacute -10
+KPX nacute ucircumflex -10
+KPX nacute udieresis -10
+KPX nacute ugrave -10
+KPX nacute uhungarumlaut -10
+KPX nacute umacron -10
+KPX nacute uogonek -10
+KPX nacute uring -10
+KPX nacute v -20
+KPX nacute y -15
+KPX nacute yacute -15
+KPX nacute ydieresis -15
+KPX ncaron u -10
+KPX ncaron uacute -10
+KPX ncaron ucircumflex -10
+KPX ncaron udieresis -10
+KPX ncaron ugrave -10
+KPX ncaron uhungarumlaut -10
+KPX ncaron umacron -10
+KPX ncaron uogonek -10
+KPX ncaron uring -10
+KPX ncaron v -20
+KPX ncaron y -15
+KPX ncaron yacute -15
+KPX ncaron ydieresis -15
+KPX ncommaaccent u -10
+KPX ncommaaccent uacute -10
+KPX ncommaaccent ucircumflex -10
+KPX ncommaaccent udieresis -10
+KPX ncommaaccent ugrave -10
+KPX ncommaaccent uhungarumlaut -10
+KPX ncommaaccent umacron -10
+KPX ncommaaccent uogonek -10
+KPX ncommaaccent uring -10
+KPX ncommaaccent v -20
+KPX ncommaaccent y -15
+KPX ncommaaccent yacute -15
+KPX ncommaaccent ydieresis -15
+KPX ntilde u -10
+KPX ntilde uacute -10
+KPX ntilde ucircumflex -10
+KPX ntilde udieresis -10
+KPX ntilde ugrave -10
+KPX ntilde uhungarumlaut -10
+KPX ntilde umacron -10
+KPX ntilde uogonek -10
+KPX ntilde uring -10
+KPX ntilde v -20
+KPX ntilde y -15
+KPX ntilde yacute -15
+KPX ntilde ydieresis -15
+KPX o comma -40
+KPX o period -40
+KPX o v -15
+KPX o w -15
+KPX o x -30
+KPX o y -30
+KPX o yacute -30
+KPX o ydieresis -30
+KPX oacute comma -40
+KPX oacute period -40
+KPX oacute v -15
+KPX oacute w -15
+KPX oacute x -30
+KPX oacute y -30
+KPX oacute yacute -30
+KPX oacute ydieresis -30
+KPX ocircumflex comma -40
+KPX ocircumflex period -40
+KPX ocircumflex v -15
+KPX ocircumflex w -15
+KPX ocircumflex x -30
+KPX ocircumflex y -30
+KPX ocircumflex yacute -30
+KPX ocircumflex ydieresis -30
+KPX odieresis comma -40
+KPX odieresis period -40
+KPX odieresis v -15
+KPX odieresis w -15
+KPX odieresis x -30
+KPX odieresis y -30
+KPX odieresis yacute -30
+KPX odieresis ydieresis -30
+KPX ograve comma -40
+KPX ograve period -40
+KPX ograve v -15
+KPX ograve w -15
+KPX ograve x -30
+KPX ograve y -30
+KPX ograve yacute -30
+KPX ograve ydieresis -30
+KPX ohungarumlaut comma -40
+KPX ohungarumlaut period -40
+KPX ohungarumlaut v -15
+KPX ohungarumlaut w -15
+KPX ohungarumlaut x -30
+KPX ohungarumlaut y -30
+KPX ohungarumlaut yacute -30
+KPX ohungarumlaut ydieresis -30
+KPX omacron comma -40
+KPX omacron period -40
+KPX omacron v -15
+KPX omacron w -15
+KPX omacron x -30
+KPX omacron y -30
+KPX omacron yacute -30
+KPX omacron ydieresis -30
+KPX oslash a -55
+KPX oslash aacute -55
+KPX oslash abreve -55
+KPX oslash acircumflex -55
+KPX oslash adieresis -55
+KPX oslash agrave -55
+KPX oslash amacron -55
+KPX oslash aogonek -55
+KPX oslash aring -55
+KPX oslash atilde -55
+KPX oslash b -55
+KPX oslash c -55
+KPX oslash cacute -55
+KPX oslash ccaron -55
+KPX oslash ccedilla -55
+KPX oslash comma -95
+KPX oslash d -55
+KPX oslash dcroat -55
+KPX oslash e -55
+KPX oslash eacute -55
+KPX oslash ecaron -55
+KPX oslash ecircumflex -55
+KPX oslash edieresis -55
+KPX oslash edotaccent -55
+KPX oslash egrave -55
+KPX oslash emacron -55
+KPX oslash eogonek -55
+KPX oslash f -55
+KPX oslash g -55
+KPX oslash gbreve -55
+KPX oslash gcommaaccent -55
+KPX oslash h -55
+KPX oslash i -55
+KPX oslash iacute -55
+KPX oslash icircumflex -55
+KPX oslash idieresis -55
+KPX oslash igrave -55
+KPX oslash imacron -55
+KPX oslash iogonek -55
+KPX oslash j -55
+KPX oslash k -55
+KPX oslash kcommaaccent -55
+KPX oslash l -55
+KPX oslash lacute -55
+KPX oslash lcommaaccent -55
+KPX oslash lslash -55
+KPX oslash m -55
+KPX oslash n -55
+KPX oslash nacute -55
+KPX oslash ncaron -55
+KPX oslash ncommaaccent -55
+KPX oslash ntilde -55
+KPX oslash o -55
+KPX oslash oacute -55
+KPX oslash ocircumflex -55
+KPX oslash odieresis -55
+KPX oslash ograve -55
+KPX oslash ohungarumlaut -55
+KPX oslash omacron -55
+KPX oslash oslash -55
+KPX oslash otilde -55
+KPX oslash p -55
+KPX oslash period -95
+KPX oslash q -55
+KPX oslash r -55
+KPX oslash racute -55
+KPX oslash rcaron -55
+KPX oslash rcommaaccent -55
+KPX oslash s -55
+KPX oslash sacute -55
+KPX oslash scaron -55
+KPX oslash scedilla -55
+KPX oslash scommaaccent -55
+KPX oslash t -55
+KPX oslash tcommaaccent -55
+KPX oslash u -55
+KPX oslash uacute -55
+KPX oslash ucircumflex -55
+KPX oslash udieresis -55
+KPX oslash ugrave -55
+KPX oslash uhungarumlaut -55
+KPX oslash umacron -55
+KPX oslash uogonek -55
+KPX oslash uring -55
+KPX oslash v -70
+KPX oslash w -70
+KPX oslash x -85
+KPX oslash y -70
+KPX oslash yacute -70
+KPX oslash ydieresis -70
+KPX oslash z -55
+KPX oslash zacute -55
+KPX oslash zcaron -55
+KPX oslash zdotaccent -55
+KPX otilde comma -40
+KPX otilde period -40
+KPX otilde v -15
+KPX otilde w -15
+KPX otilde x -30
+KPX otilde y -30
+KPX otilde yacute -30
+KPX otilde ydieresis -30
+KPX p comma -35
+KPX p period -35
+KPX p y -30
+KPX p yacute -30
+KPX p ydieresis -30
+KPX period quotedblright -100
+KPX period quoteright -100
+KPX period space -60
+KPX quotedblright space -40
+KPX quoteleft quoteleft -57
+KPX quoteright d -50
+KPX quoteright dcroat -50
+KPX quoteright quoteright -57
+KPX quoteright r -50
+KPX quoteright racute -50
+KPX quoteright rcaron -50
+KPX quoteright rcommaaccent -50
+KPX quoteright s -50
+KPX quoteright sacute -50
+KPX quoteright scaron -50
+KPX quoteright scedilla -50
+KPX quoteright scommaaccent -50
+KPX quoteright space -70
+KPX r a -10
+KPX r aacute -10
+KPX r abreve -10
+KPX r acircumflex -10
+KPX r adieresis -10
+KPX r agrave -10
+KPX r amacron -10
+KPX r aogonek -10
+KPX r aring -10
+KPX r atilde -10
+KPX r colon 30
+KPX r comma -50
+KPX r i 15
+KPX r iacute 15
+KPX r icircumflex 15
+KPX r idieresis 15
+KPX r igrave 15
+KPX r imacron 15
+KPX r iogonek 15
+KPX r k 15
+KPX r kcommaaccent 15
+KPX r l 15
+KPX r lacute 15
+KPX r lcommaaccent 15
+KPX r lslash 15
+KPX r m 25
+KPX r n 25
+KPX r nacute 25
+KPX r ncaron 25
+KPX r ncommaaccent 25
+KPX r ntilde 25
+KPX r p 30
+KPX r period -50
+KPX r semicolon 30
+KPX r t 40
+KPX r tcommaaccent 40
+KPX r u 15
+KPX r uacute 15
+KPX r ucircumflex 15
+KPX r udieresis 15
+KPX r ugrave 15
+KPX r uhungarumlaut 15
+KPX r umacron 15
+KPX r uogonek 15
+KPX r uring 15
+KPX r v 30
+KPX r y 30
+KPX r yacute 30
+KPX r ydieresis 30
+KPX racute a -10
+KPX racute aacute -10
+KPX racute abreve -10
+KPX racute acircumflex -10
+KPX racute adieresis -10
+KPX racute agrave -10
+KPX racute amacron -10
+KPX racute aogonek -10
+KPX racute aring -10
+KPX racute atilde -10
+KPX racute colon 30
+KPX racute comma -50
+KPX racute i 15
+KPX racute iacute 15
+KPX racute icircumflex 15
+KPX racute idieresis 15
+KPX racute igrave 15
+KPX racute imacron 15
+KPX racute iogonek 15
+KPX racute k 15
+KPX racute kcommaaccent 15
+KPX racute l 15
+KPX racute lacute 15
+KPX racute lcommaaccent 15
+KPX racute lslash 15
+KPX racute m 25
+KPX racute n 25
+KPX racute nacute 25
+KPX racute ncaron 25
+KPX racute ncommaaccent 25
+KPX racute ntilde 25
+KPX racute p 30
+KPX racute period -50
+KPX racute semicolon 30
+KPX racute t 40
+KPX racute tcommaaccent 40
+KPX racute u 15
+KPX racute uacute 15
+KPX racute ucircumflex 15
+KPX racute udieresis 15
+KPX racute ugrave 15
+KPX racute uhungarumlaut 15
+KPX racute umacron 15
+KPX racute uogonek 15
+KPX racute uring 15
+KPX racute v 30
+KPX racute y 30
+KPX racute yacute 30
+KPX racute ydieresis 30
+KPX rcaron a -10
+KPX rcaron aacute -10
+KPX rcaron abreve -10
+KPX rcaron acircumflex -10
+KPX rcaron adieresis -10
+KPX rcaron agrave -10
+KPX rcaron amacron -10
+KPX rcaron aogonek -10
+KPX rcaron aring -10
+KPX rcaron atilde -10
+KPX rcaron colon 30
+KPX rcaron comma -50
+KPX rcaron i 15
+KPX rcaron iacute 15
+KPX rcaron icircumflex 15
+KPX rcaron idieresis 15
+KPX rcaron igrave 15
+KPX rcaron imacron 15
+KPX rcaron iogonek 15
+KPX rcaron k 15
+KPX rcaron kcommaaccent 15
+KPX rcaron l 15
+KPX rcaron lacute 15
+KPX rcaron lcommaaccent 15
+KPX rcaron lslash 15
+KPX rcaron m 25
+KPX rcaron n 25
+KPX rcaron nacute 25
+KPX rcaron ncaron 25
+KPX rcaron ncommaaccent 25
+KPX rcaron ntilde 25
+KPX rcaron p 30
+KPX rcaron period -50
+KPX rcaron semicolon 30
+KPX rcaron t 40
+KPX rcaron tcommaaccent 40
+KPX rcaron u 15
+KPX rcaron uacute 15
+KPX rcaron ucircumflex 15
+KPX rcaron udieresis 15
+KPX rcaron ugrave 15
+KPX rcaron uhungarumlaut 15
+KPX rcaron umacron 15
+KPX rcaron uogonek 15
+KPX rcaron uring 15
+KPX rcaron v 30
+KPX rcaron y 30
+KPX rcaron yacute 30
+KPX rcaron ydieresis 30
+KPX rcommaaccent a -10
+KPX rcommaaccent aacute -10
+KPX rcommaaccent abreve -10
+KPX rcommaaccent acircumflex -10
+KPX rcommaaccent adieresis -10
+KPX rcommaaccent agrave -10
+KPX rcommaaccent amacron -10
+KPX rcommaaccent aogonek -10
+KPX rcommaaccent aring -10
+KPX rcommaaccent atilde -10
+KPX rcommaaccent colon 30
+KPX rcommaaccent comma -50
+KPX rcommaaccent i 15
+KPX rcommaaccent iacute 15
+KPX rcommaaccent icircumflex 15
+KPX rcommaaccent idieresis 15
+KPX rcommaaccent igrave 15
+KPX rcommaaccent imacron 15
+KPX rcommaaccent iogonek 15
+KPX rcommaaccent k 15
+KPX rcommaaccent kcommaaccent 15
+KPX rcommaaccent l 15
+KPX rcommaaccent lacute 15
+KPX rcommaaccent lcommaaccent 15
+KPX rcommaaccent lslash 15
+KPX rcommaaccent m 25
+KPX rcommaaccent n 25
+KPX rcommaaccent nacute 25
+KPX rcommaaccent ncaron 25
+KPX rcommaaccent ncommaaccent 25
+KPX rcommaaccent ntilde 25
+KPX rcommaaccent p 30
+KPX rcommaaccent period -50
+KPX rcommaaccent semicolon 30
+KPX rcommaaccent t 40
+KPX rcommaaccent tcommaaccent 40
+KPX rcommaaccent u 15
+KPX rcommaaccent uacute 15
+KPX rcommaaccent ucircumflex 15
+KPX rcommaaccent udieresis 15
+KPX rcommaaccent ugrave 15
+KPX rcommaaccent uhungarumlaut 15
+KPX rcommaaccent umacron 15
+KPX rcommaaccent uogonek 15
+KPX rcommaaccent uring 15
+KPX rcommaaccent v 30
+KPX rcommaaccent y 30
+KPX rcommaaccent yacute 30
+KPX rcommaaccent ydieresis 30
+KPX s comma -15
+KPX s period -15
+KPX s w -30
+KPX sacute comma -15
+KPX sacute period -15
+KPX sacute w -30
+KPX scaron comma -15
+KPX scaron period -15
+KPX scaron w -30
+KPX scedilla comma -15
+KPX scedilla period -15
+KPX scedilla w -30
+KPX scommaaccent comma -15
+KPX scommaaccent period -15
+KPX scommaaccent w -30
+KPX semicolon space -50
+KPX space T -50
+KPX space Tcaron -50
+KPX space Tcommaaccent -50
+KPX space V -50
+KPX space W -40
+KPX space Y -90
+KPX space Yacute -90
+KPX space Ydieresis -90
+KPX space quotedblleft -30
+KPX space quoteleft -60
+KPX v a -25
+KPX v aacute -25
+KPX v abreve -25
+KPX v acircumflex -25
+KPX v adieresis -25
+KPX v agrave -25
+KPX v amacron -25
+KPX v aogonek -25
+KPX v aring -25
+KPX v atilde -25
+KPX v comma -80
+KPX v e -25
+KPX v eacute -25
+KPX v ecaron -25
+KPX v ecircumflex -25
+KPX v edieresis -25
+KPX v edotaccent -25
+KPX v egrave -25
+KPX v emacron -25
+KPX v eogonek -25
+KPX v o -25
+KPX v oacute -25
+KPX v ocircumflex -25
+KPX v odieresis -25
+KPX v ograve -25
+KPX v ohungarumlaut -25
+KPX v omacron -25
+KPX v oslash -25
+KPX v otilde -25
+KPX v period -80
+KPX w a -15
+KPX w aacute -15
+KPX w abreve -15
+KPX w acircumflex -15
+KPX w adieresis -15
+KPX w agrave -15
+KPX w amacron -15
+KPX w aogonek -15
+KPX w aring -15
+KPX w atilde -15
+KPX w comma -60
+KPX w e -10
+KPX w eacute -10
+KPX w ecaron -10
+KPX w ecircumflex -10
+KPX w edieresis -10
+KPX w edotaccent -10
+KPX w egrave -10
+KPX w emacron -10
+KPX w eogonek -10
+KPX w o -10
+KPX w oacute -10
+KPX w ocircumflex -10
+KPX w odieresis -10
+KPX w ograve -10
+KPX w ohungarumlaut -10
+KPX w omacron -10
+KPX w oslash -10
+KPX w otilde -10
+KPX w period -60
+KPX x e -30
+KPX x eacute -30
+KPX x ecaron -30
+KPX x ecircumflex -30
+KPX x edieresis -30
+KPX x edotaccent -30
+KPX x egrave -30
+KPX x emacron -30
+KPX x eogonek -30
+KPX y a -20
+KPX y aacute -20
+KPX y abreve -20
+KPX y acircumflex -20
+KPX y adieresis -20
+KPX y agrave -20
+KPX y amacron -20
+KPX y aogonek -20
+KPX y aring -20
+KPX y atilde -20
+KPX y comma -100
+KPX y e -20
+KPX y eacute -20
+KPX y ecaron -20
+KPX y ecircumflex -20
+KPX y edieresis -20
+KPX y edotaccent -20
+KPX y egrave -20
+KPX y emacron -20
+KPX y eogonek -20
+KPX y o -20
+KPX y oacute -20
+KPX y ocircumflex -20
+KPX y odieresis -20
+KPX y ograve -20
+KPX y ohungarumlaut -20
+KPX y omacron -20
+KPX y oslash -20
+KPX y otilde -20
+KPX y period -100
+KPX yacute a -20
+KPX yacute aacute -20
+KPX yacute abreve -20
+KPX yacute acircumflex -20
+KPX yacute adieresis -20
+KPX yacute agrave -20
+KPX yacute amacron -20
+KPX yacute aogonek -20
+KPX yacute aring -20
+KPX yacute atilde -20
+KPX yacute comma -100
+KPX yacute e -20
+KPX yacute eacute -20
+KPX yacute ecaron -20
+KPX yacute ecircumflex -20
+KPX yacute edieresis -20
+KPX yacute edotaccent -20
+KPX yacute egrave -20
+KPX yacute emacron -20
+KPX yacute eogonek -20
+KPX yacute o -20
+KPX yacute oacute -20
+KPX yacute ocircumflex -20
+KPX yacute odieresis -20
+KPX yacute ograve -20
+KPX yacute ohungarumlaut -20
+KPX yacute omacron -20
+KPX yacute oslash -20
+KPX yacute otilde -20
+KPX yacute period -100
+KPX ydieresis a -20
+KPX ydieresis aacute -20
+KPX ydieresis abreve -20
+KPX ydieresis acircumflex -20
+KPX ydieresis adieresis -20
+KPX ydieresis agrave -20
+KPX ydieresis amacron -20
+KPX ydieresis aogonek -20
+KPX ydieresis aring -20
+KPX ydieresis atilde -20
+KPX ydieresis comma -100
+KPX ydieresis e -20
+KPX ydieresis eacute -20
+KPX ydieresis ecaron -20
+KPX ydieresis ecircumflex -20
+KPX ydieresis edieresis -20
+KPX ydieresis edotaccent -20
+KPX ydieresis egrave -20
+KPX ydieresis emacron -20
+KPX ydieresis eogonek -20
+KPX ydieresis o -20
+KPX ydieresis oacute -20
+KPX ydieresis ocircumflex -20
+KPX ydieresis odieresis -20
+KPX ydieresis ograve -20
+KPX ydieresis ohungarumlaut -20
+KPX ydieresis omacron -20
+KPX ydieresis oslash -20
+KPX ydieresis otilde -20
+KPX ydieresis period -100
+KPX z e -15
+KPX z eacute -15
+KPX z ecaron -15
+KPX z ecircumflex -15
+KPX z edieresis -15
+KPX z edotaccent -15
+KPX z egrave -15
+KPX z emacron -15
+KPX z eogonek -15
+KPX z o -15
+KPX z oacute -15
+KPX z ocircumflex -15
+KPX z odieresis -15
+KPX z ograve -15
+KPX z ohungarumlaut -15
+KPX z omacron -15
+KPX z oslash -15
+KPX z otilde -15
+KPX zacute e -15
+KPX zacute eacute -15
+KPX zacute ecaron -15
+KPX zacute ecircumflex -15
+KPX zacute edieresis -15
+KPX zacute edotaccent -15
+KPX zacute egrave -15
+KPX zacute emacron -15
+KPX zacute eogonek -15
+KPX zacute o -15
+KPX zacute oacute -15
+KPX zacute ocircumflex -15
+KPX zacute odieresis -15
+KPX zacute ograve -15
+KPX zacute ohungarumlaut -15
+KPX zacute omacron -15
+KPX zacute oslash -15
+KPX zacute otilde -15
+KPX zcaron e -15
+KPX zcaron eacute -15
+KPX zcaron ecaron -15
+KPX zcaron ecircumflex -15
+KPX zcaron edieresis -15
+KPX zcaron edotaccent -15
+KPX zcaron egrave -15
+KPX zcaron emacron -15
+KPX zcaron eogonek -15
+KPX zcaron o -15
+KPX zcaron oacute -15
+KPX zcaron ocircumflex -15
+KPX zcaron odieresis -15
+KPX zcaron ograve -15
+KPX zcaron ohungarumlaut -15
+KPX zcaron omacron -15
+KPX zcaron oslash -15
+KPX zcaron otilde -15
+KPX zdotaccent e -15
+KPX zdotaccent eacute -15
+KPX zdotaccent ecaron -15
+KPX zdotaccent ecircumflex -15
+KPX zdotaccent edieresis -15
+KPX zdotaccent edotaccent -15
+KPX zdotaccent egrave -15
+KPX zdotaccent emacron -15
+KPX zdotaccent eogonek -15
+KPX zdotaccent o -15
+KPX zdotaccent oacute -15
+KPX zdotaccent ocircumflex -15
+KPX zdotaccent odieresis -15
+KPX zdotaccent ograve -15
+KPX zdotaccent ohungarumlaut -15
+KPX zdotaccent omacron -15
+KPX zdotaccent oslash -15
+KPX zdotaccent otilde -15
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/program/libraries/afm/Helvetica.afm b/program/libraries/afm/Helvetica.afm
new file mode 100644 (file)
index 0000000..bd32af5
--- /dev/null
@@ -0,0 +1,3051 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated.  All Rights Reserved.
+Comment Creation Date: Thu May  1 12:38:23 1997
+Comment UniqueID 43054
+Comment VMusage 37069 48094
+FontName Helvetica
+FullName Helvetica
+FamilyName Helvetica
+Weight Medium
+ItalicAngle 0
+IsFixedPitch false
+CharacterSet ExtendedRoman
+FontBBox -166 -225 1000 931 
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated.  All Rights Reserved.Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries.
+EncodingScheme AdobeStandardEncoding
+CapHeight 718
+XHeight 523
+Ascender 718
+Descender -207
+StdHW 76
+StdVW 88
+StartCharMetrics 315
+C 32 ; WX 278 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 278 ; N exclam ; B 90 0 187 718 ;
+C 34 ; WX 355 ; N quotedbl ; B 70 463 285 718 ;
+C 35 ; WX 556 ; N numbersign ; B 28 0 529 688 ;
+C 36 ; WX 556 ; N dollar ; B 32 -115 520 775 ;
+C 37 ; WX 889 ; N percent ; B 39 -19 850 703 ;
+C 38 ; WX 667 ; N ampersand ; B 44 -15 645 718 ;
+C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
+C 40 ; WX 333 ; N parenleft ; B 68 -207 299 733 ;
+C 41 ; WX 333 ; N parenright ; B 34 -207 265 733 ;
+C 42 ; WX 389 ; N asterisk ; B 39 431 349 718 ;
+C 43 ; WX 584 ; N plus ; B 39 0 545 505 ;
+C 44 ; WX 278 ; N comma ; B 87 -147 191 106 ;
+C 45 ; WX 333 ; N hyphen ; B 44 232 289 322 ;
+C 46 ; WX 278 ; N period ; B 87 0 191 106 ;
+C 47 ; WX 278 ; N slash ; B -17 -19 295 737 ;
+C 48 ; WX 556 ; N zero ; B 37 -19 519 703 ;
+C 49 ; WX 556 ; N one ; B 101 0 359 703 ;
+C 50 ; WX 556 ; N two ; B 26 0 507 703 ;
+C 51 ; WX 556 ; N three ; B 34 -19 522 703 ;
+C 52 ; WX 556 ; N four ; B 25 0 523 703 ;
+C 53 ; WX 556 ; N five ; B 32 -19 514 688 ;
+C 54 ; WX 556 ; N six ; B 38 -19 518 703 ;
+C 55 ; WX 556 ; N seven ; B 37 0 523 688 ;
+C 56 ; WX 556 ; N eight ; B 38 -19 517 703 ;
+C 57 ; WX 556 ; N nine ; B 42 -19 514 703 ;
+C 58 ; WX 278 ; N colon ; B 87 0 191 516 ;
+C 59 ; WX 278 ; N semicolon ; B 87 -147 191 516 ;
+C 60 ; WX 584 ; N less ; B 48 11 536 495 ;
+C 61 ; WX 584 ; N equal ; B 39 115 545 390 ;
+C 62 ; WX 584 ; N greater ; B 48 11 536 495 ;
+C 63 ; WX 556 ; N question ; B 56 0 492 727 ;
+C 64 ; WX 1015 ; N at ; B 147 -19 868 737 ;
+C 65 ; WX 667 ; N A ; B 14 0 654 718 ;
+C 66 ; WX 667 ; N B ; B 74 0 627 718 ;
+C 67 ; WX 722 ; N C ; B 44 -19 681 737 ;
+C 68 ; WX 722 ; N D ; B 81 0 674 718 ;
+C 69 ; WX 667 ; N E ; B 86 0 616 718 ;
+C 70 ; WX 611 ; N F ; B 86 0 583 718 ;
+C 71 ; WX 778 ; N G ; B 48 -19 704 737 ;
+C 72 ; WX 722 ; N H ; B 77 0 646 718 ;
+C 73 ; WX 278 ; N I ; B 91 0 188 718 ;
+C 74 ; WX 500 ; N J ; B 17 -19 428 718 ;
+C 75 ; WX 667 ; N K ; B 76 0 663 718 ;
+C 76 ; WX 556 ; N L ; B 76 0 537 718 ;
+C 77 ; WX 833 ; N M ; B 73 0 761 718 ;
+C 78 ; WX 722 ; N N ; B 76 0 646 718 ;
+C 79 ; WX 778 ; N O ; B 39 -19 739 737 ;
+C 80 ; WX 667 ; N P ; B 86 0 622 718 ;
+C 81 ; WX 778 ; N Q ; B 39 -56 739 737 ;
+C 82 ; WX 722 ; N R ; B 88 0 684 718 ;
+C 83 ; WX 667 ; N S ; B 49 -19 620 737 ;
+C 84 ; WX 611 ; N T ; B 14 0 597 718 ;
+C 85 ; WX 722 ; N U ; B 79 -19 644 718 ;
+C 86 ; WX 667 ; N V ; B 20 0 647 718 ;
+C 87 ; WX 944 ; N W ; B 16 0 928 718 ;
+C 88 ; WX 667 ; N X ; B 19 0 648 718 ;
+C 89 ; WX 667 ; N Y ; B 14 0 653 718 ;
+C 90 ; WX 611 ; N Z ; B 23 0 588 718 ;
+C 91 ; WX 278 ; N bracketleft ; B 63 -196 250 722 ;
+C 92 ; WX 278 ; N backslash ; B -17 -19 295 737 ;
+C 93 ; WX 278 ; N bracketright ; B 28 -196 215 722 ;
+C 94 ; WX 469 ; N asciicircum ; B -14 264 483 688 ;
+C 95 ; WX 556 ; N underscore ; B 0 -125 556 -75 ;
+C 96 ; WX 222 ; N quoteleft ; B 65 470 169 725 ;
+C 97 ; WX 556 ; N a ; B 36 -15 530 538 ;
+C 98 ; WX 556 ; N b ; B 58 -15 517 718 ;
+C 99 ; WX 500 ; N c ; B 30 -15 477 538 ;
+C 100 ; WX 556 ; N d ; B 35 -15 499 718 ;
+C 101 ; WX 556 ; N e ; B 40 -15 516 538 ;
+C 102 ; WX 278 ; N f ; B 14 0 262 728 ; L i fi ; L l fl ;
+C 103 ; WX 556 ; N g ; B 40 -220 499 538 ;
+C 104 ; WX 556 ; N h ; B 65 0 491 718 ;
+C 105 ; WX 222 ; N i ; B 67 0 155 718 ;
+C 106 ; WX 222 ; N j ; B -16 -210 155 718 ;
+C 107 ; WX 500 ; N k ; B 67 0 501 718 ;
+C 108 ; WX 222 ; N l ; B 67 0 155 718 ;
+C 109 ; WX 833 ; N m ; B 65 0 769 538 ;
+C 110 ; WX 556 ; N n ; B 65 0 491 538 ;
+C 111 ; WX 556 ; N o ; B 35 -14 521 538 ;
+C 112 ; WX 556 ; N p ; B 58 -207 517 538 ;
+C 113 ; WX 556 ; N q ; B 35 -207 494 538 ;
+C 114 ; WX 333 ; N r ; B 77 0 332 538 ;
+C 115 ; WX 500 ; N s ; B 32 -15 464 538 ;
+C 116 ; WX 278 ; N t ; B 14 -7 257 669 ;
+C 117 ; WX 556 ; N u ; B 68 -15 489 523 ;
+C 118 ; WX 500 ; N v ; B 8 0 492 523 ;
+C 119 ; WX 722 ; N w ; B 14 0 709 523 ;
+C 120 ; WX 500 ; N x ; B 11 0 490 523 ;
+C 121 ; WX 500 ; N y ; B 11 -214 489 523 ;
+C 122 ; WX 500 ; N z ; B 31 0 469 523 ;
+C 123 ; WX 334 ; N braceleft ; B 42 -196 292 722 ;
+C 124 ; WX 260 ; N bar ; B 94 -225 167 775 ;
+C 125 ; WX 334 ; N braceright ; B 42 -196 292 722 ;
+C 126 ; WX 584 ; N asciitilde ; B 61 180 523 326 ;
+C 161 ; WX 333 ; N exclamdown ; B 118 -195 215 523 ;
+C 162 ; WX 556 ; N cent ; B 51 -115 513 623 ;
+C 163 ; WX 556 ; N sterling ; B 33 -16 539 718 ;
+C 164 ; WX 167 ; N fraction ; B -166 -19 333 703 ;
+C 165 ; WX 556 ; N yen ; B 3 0 553 688 ;
+C 166 ; WX 556 ; N florin ; B -11 -207 501 737 ;
+C 167 ; WX 556 ; N section ; B 43 -191 512 737 ;
+C 168 ; WX 556 ; N currency ; B 28 99 528 603 ;
+C 169 ; WX 191 ; N quotesingle ; B 59 463 132 718 ;
+C 170 ; WX 333 ; N quotedblleft ; B 38 470 307 725 ;
+C 171 ; WX 556 ; N guillemotleft ; B 97 108 459 446 ;
+C 172 ; WX 333 ; N guilsinglleft ; B 88 108 245 446 ;
+C 173 ; WX 333 ; N guilsinglright ; B 88 108 245 446 ;
+C 174 ; WX 500 ; N fi ; B 14 0 434 728 ;
+C 175 ; WX 500 ; N fl ; B 14 0 432 728 ;
+C 177 ; WX 556 ; N endash ; B 0 240 556 313 ;
+C 178 ; WX 556 ; N dagger ; B 43 -159 514 718 ;
+C 179 ; WX 556 ; N daggerdbl ; B 43 -159 514 718 ;
+C 180 ; WX 278 ; N periodcentered ; B 77 190 202 315 ;
+C 182 ; WX 537 ; N paragraph ; B 18 -173 497 718 ;
+C 183 ; WX 350 ; N bullet ; B 18 202 333 517 ;
+C 184 ; WX 222 ; N quotesinglbase ; B 53 -149 157 106 ;
+C 185 ; WX 333 ; N quotedblbase ; B 26 -149 295 106 ;
+C 186 ; WX 333 ; N quotedblright ; B 26 463 295 718 ;
+C 187 ; WX 556 ; N guillemotright ; B 97 108 459 446 ;
+C 188 ; WX 1000 ; N ellipsis ; B 115 0 885 106 ;
+C 189 ; WX 1000 ; N perthousand ; B 7 -19 994 703 ;
+C 191 ; WX 611 ; N questiondown ; B 91 -201 527 525 ;
+C 193 ; WX 333 ; N grave ; B 14 593 211 734 ;
+C 194 ; WX 333 ; N acute ; B 122 593 319 734 ;
+C 195 ; WX 333 ; N circumflex ; B 21 593 312 734 ;
+C 196 ; WX 333 ; N tilde ; B -4 606 337 722 ;
+C 197 ; WX 333 ; N macron ; B 10 627 323 684 ;
+C 198 ; WX 333 ; N breve ; B 13 595 321 731 ;
+C 199 ; WX 333 ; N dotaccent ; B 121 604 212 706 ;
+C 200 ; WX 333 ; N dieresis ; B 40 604 293 706 ;
+C 202 ; WX 333 ; N ring ; B 75 572 259 756 ;
+C 203 ; WX 333 ; N cedilla ; B 45 -225 259 0 ;
+C 205 ; WX 333 ; N hungarumlaut ; B 31 593 409 734 ;
+C 206 ; WX 333 ; N ogonek ; B 73 -225 287 0 ;
+C 207 ; WX 333 ; N caron ; B 21 593 312 734 ;
+C 208 ; WX 1000 ; N emdash ; B 0 240 1000 313 ;
+C 225 ; WX 1000 ; N AE ; B 8 0 951 718 ;
+C 227 ; WX 370 ; N ordfeminine ; B 24 405 346 737 ;
+C 232 ; WX 556 ; N Lslash ; B -20 0 537 718 ;
+C 233 ; WX 778 ; N Oslash ; B 39 -19 740 737 ;
+C 234 ; WX 1000 ; N OE ; B 36 -19 965 737 ;
+C 235 ; WX 365 ; N ordmasculine ; B 25 405 341 737 ;
+C 241 ; WX 889 ; N ae ; B 36 -15 847 538 ;
+C 245 ; WX 278 ; N dotlessi ; B 95 0 183 523 ;
+C 248 ; WX 222 ; N lslash ; B -20 0 242 718 ;
+C 249 ; WX 611 ; N oslash ; B 28 -22 537 545 ;
+C 250 ; WX 944 ; N oe ; B 35 -15 902 538 ;
+C 251 ; WX 611 ; N germandbls ; B 67 -15 571 728 ;
+C -1 ; WX 278 ; N Idieresis ; B 13 0 266 901 ;
+C -1 ; WX 556 ; N eacute ; B 40 -15 516 734 ;
+C -1 ; WX 556 ; N abreve ; B 36 -15 530 731 ;
+C -1 ; WX 556 ; N uhungarumlaut ; B 68 -15 521 734 ;
+C -1 ; WX 556 ; N ecaron ; B 40 -15 516 734 ;
+C -1 ; WX 667 ; N Ydieresis ; B 14 0 653 901 ;
+C -1 ; WX 584 ; N divide ; B 39 -19 545 524 ;
+C -1 ; WX 667 ; N Yacute ; B 14 0 653 929 ;
+C -1 ; WX 667 ; N Acircumflex ; B 14 0 654 929 ;
+C -1 ; WX 556 ; N aacute ; B 36 -15 530 734 ;
+C -1 ; WX 722 ; N Ucircumflex ; B 79 -19 644 929 ;
+C -1 ; WX 500 ; N yacute ; B 11 -214 489 734 ;
+C -1 ; WX 500 ; N scommaaccent ; B 32 -225 464 538 ;
+C -1 ; WX 556 ; N ecircumflex ; B 40 -15 516 734 ;
+C -1 ; WX 722 ; N Uring ; B 79 -19 644 931 ;
+C -1 ; WX 722 ; N Udieresis ; B 79 -19 644 901 ;
+C -1 ; WX 556 ; N aogonek ; B 36 -220 547 538 ;
+C -1 ; WX 722 ; N Uacute ; B 79 -19 644 929 ;
+C -1 ; WX 556 ; N uogonek ; B 68 -225 519 523 ;
+C -1 ; WX 667 ; N Edieresis ; B 86 0 616 901 ;
+C -1 ; WX 722 ; N Dcroat ; B 0 0 674 718 ;
+C -1 ; WX 250 ; N commaaccent ; B 87 -225 181 -40 ;
+C -1 ; WX 737 ; N copyright ; B -14 -19 752 737 ;
+C -1 ; WX 667 ; N Emacron ; B 86 0 616 879 ;
+C -1 ; WX 500 ; N ccaron ; B 30 -15 477 734 ;
+C -1 ; WX 556 ; N aring ; B 36 -15 530 756 ;
+C -1 ; WX 722 ; N Ncommaaccent ; B 76 -225 646 718 ;
+C -1 ; WX 222 ; N lacute ; B 67 0 264 929 ;
+C -1 ; WX 556 ; N agrave ; B 36 -15 530 734 ;
+C -1 ; WX 611 ; N Tcommaaccent ; B 14 -225 597 718 ;
+C -1 ; WX 722 ; N Cacute ; B 44 -19 681 929 ;
+C -1 ; WX 556 ; N atilde ; B 36 -15 530 722 ;
+C -1 ; WX 667 ; N Edotaccent ; B 86 0 616 901 ;
+C -1 ; WX 500 ; N scaron ; B 32 -15 464 734 ;
+C -1 ; WX 500 ; N scedilla ; B 32 -225 464 538 ;
+C -1 ; WX 278 ; N iacute ; B 95 0 292 734 ;
+C -1 ; WX 471 ; N lozenge ; B 10 0 462 728 ;
+C -1 ; WX 722 ; N Rcaron ; B 88 0 684 929 ;
+C -1 ; WX 778 ; N Gcommaaccent ; B 48 -225 704 737 ;
+C -1 ; WX 556 ; N ucircumflex ; B 68 -15 489 734 ;
+C -1 ; WX 556 ; N acircumflex ; B 36 -15 530 734 ;
+C -1 ; WX 667 ; N Amacron ; B 14 0 654 879 ;
+C -1 ; WX 333 ; N rcaron ; B 61 0 352 734 ;
+C -1 ; WX 500 ; N ccedilla ; B 30 -225 477 538 ;
+C -1 ; WX 611 ; N Zdotaccent ; B 23 0 588 901 ;
+C -1 ; WX 667 ; N Thorn ; B 86 0 622 718 ;
+C -1 ; WX 778 ; N Omacron ; B 39 -19 739 879 ;
+C -1 ; WX 722 ; N Racute ; B 88 0 684 929 ;
+C -1 ; WX 667 ; N Sacute ; B 49 -19 620 929 ;
+C -1 ; WX 643 ; N dcaron ; B 35 -15 655 718 ;
+C -1 ; WX 722 ; N Umacron ; B 79 -19 644 879 ;
+C -1 ; WX 556 ; N uring ; B 68 -15 489 756 ;
+C -1 ; WX 333 ; N threesuperior ; B 5 270 325 703 ;
+C -1 ; WX 778 ; N Ograve ; B 39 -19 739 929 ;
+C -1 ; WX 667 ; N Agrave ; B 14 0 654 929 ;
+C -1 ; WX 667 ; N Abreve ; B 14 0 654 926 ;
+C -1 ; WX 584 ; N multiply ; B 39 0 545 506 ;
+C -1 ; WX 556 ; N uacute ; B 68 -15 489 734 ;
+C -1 ; WX 611 ; N Tcaron ; B 14 0 597 929 ;
+C -1 ; WX 476 ; N partialdiff ; B 13 -38 463 714 ;
+C -1 ; WX 500 ; N ydieresis ; B 11 -214 489 706 ;
+C -1 ; WX 722 ; N Nacute ; B 76 0 646 929 ;
+C -1 ; WX 278 ; N icircumflex ; B -6 0 285 734 ;
+C -1 ; WX 667 ; N Ecircumflex ; B 86 0 616 929 ;
+C -1 ; WX 556 ; N adieresis ; B 36 -15 530 706 ;
+C -1 ; WX 556 ; N edieresis ; B 40 -15 516 706 ;
+C -1 ; WX 500 ; N cacute ; B 30 -15 477 734 ;
+C -1 ; WX 556 ; N nacute ; B 65 0 491 734 ;
+C -1 ; WX 556 ; N umacron ; B 68 -15 489 684 ;
+C -1 ; WX 722 ; N Ncaron ; B 76 0 646 929 ;
+C -1 ; WX 278 ; N Iacute ; B 91 0 292 929 ;
+C -1 ; WX 584 ; N plusminus ; B 39 0 545 506 ;
+C -1 ; WX 260 ; N brokenbar ; B 94 -150 167 700 ;
+C -1 ; WX 737 ; N registered ; B -14 -19 752 737 ;
+C -1 ; WX 778 ; N Gbreve ; B 48 -19 704 926 ;
+C -1 ; WX 278 ; N Idotaccent ; B 91 0 188 901 ;
+C -1 ; WX 600 ; N summation ; B 15 -10 586 706 ;
+C -1 ; WX 667 ; N Egrave ; B 86 0 616 929 ;
+C -1 ; WX 333 ; N racute ; B 77 0 332 734 ;
+C -1 ; WX 556 ; N omacron ; B 35 -14 521 684 ;
+C -1 ; WX 611 ; N Zacute ; B 23 0 588 929 ;
+C -1 ; WX 611 ; N Zcaron ; B 23 0 588 929 ;
+C -1 ; WX 549 ; N greaterequal ; B 26 0 523 674 ;
+C -1 ; WX 722 ; N Eth ; B 0 0 674 718 ;
+C -1 ; WX 722 ; N Ccedilla ; B 44 -225 681 737 ;
+C -1 ; WX 222 ; N lcommaaccent ; B 67 -225 167 718 ;
+C -1 ; WX 317 ; N tcaron ; B 14 -7 329 808 ;
+C -1 ; WX 556 ; N eogonek ; B 40 -225 516 538 ;
+C -1 ; WX 722 ; N Uogonek ; B 79 -225 644 718 ;
+C -1 ; WX 667 ; N Aacute ; B 14 0 654 929 ;
+C -1 ; WX 667 ; N Adieresis ; B 14 0 654 901 ;
+C -1 ; WX 556 ; N egrave ; B 40 -15 516 734 ;
+C -1 ; WX 500 ; N zacute ; B 31 0 469 734 ;
+C -1 ; WX 222 ; N iogonek ; B -31 -225 183 718 ;
+C -1 ; WX 778 ; N Oacute ; B 39 -19 739 929 ;
+C -1 ; WX 556 ; N oacute ; B 35 -14 521 734 ;
+C -1 ; WX 556 ; N amacron ; B 36 -15 530 684 ;
+C -1 ; WX 500 ; N sacute ; B 32 -15 464 734 ;
+C -1 ; WX 278 ; N idieresis ; B 13 0 266 706 ;
+C -1 ; WX 778 ; N Ocircumflex ; B 39 -19 739 929 ;
+C -1 ; WX 722 ; N Ugrave ; B 79 -19 644 929 ;
+C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C -1 ; WX 556 ; N thorn ; B 58 -207 517 718 ;
+C -1 ; WX 333 ; N twosuperior ; B 4 281 323 703 ;
+C -1 ; WX 778 ; N Odieresis ; B 39 -19 739 901 ;
+C -1 ; WX 556 ; N mu ; B 68 -207 489 523 ;
+C -1 ; WX 278 ; N igrave ; B -13 0 184 734 ;
+C -1 ; WX 556 ; N ohungarumlaut ; B 35 -14 521 734 ;
+C -1 ; WX 667 ; N Eogonek ; B 86 -220 633 718 ;
+C -1 ; WX 556 ; N dcroat ; B 35 -15 550 718 ;
+C -1 ; WX 834 ; N threequarters ; B 45 -19 810 703 ;
+C -1 ; WX 667 ; N Scedilla ; B 49 -225 620 737 ;
+C -1 ; WX 299 ; N lcaron ; B 67 0 311 718 ;
+C -1 ; WX 667 ; N Kcommaaccent ; B 76 -225 663 718 ;
+C -1 ; WX 556 ; N Lacute ; B 76 0 537 929 ;
+C -1 ; WX 1000 ; N trademark ; B 46 306 903 718 ;
+C -1 ; WX 556 ; N edotaccent ; B 40 -15 516 706 ;
+C -1 ; WX 278 ; N Igrave ; B -13 0 188 929 ;
+C -1 ; WX 278 ; N Imacron ; B -17 0 296 879 ;
+C -1 ; WX 556 ; N Lcaron ; B 76 0 537 718 ;
+C -1 ; WX 834 ; N onehalf ; B 43 -19 773 703 ;
+C -1 ; WX 549 ; N lessequal ; B 26 0 523 674 ;
+C -1 ; WX 556 ; N ocircumflex ; B 35 -14 521 734 ;
+C -1 ; WX 556 ; N ntilde ; B 65 0 491 722 ;
+C -1 ; WX 722 ; N Uhungarumlaut ; B 79 -19 644 929 ;
+C -1 ; WX 667 ; N Eacute ; B 86 0 616 929 ;
+C -1 ; WX 556 ; N emacron ; B 40 -15 516 684 ;
+C -1 ; WX 556 ; N gbreve ; B 40 -220 499 731 ;
+C -1 ; WX 834 ; N onequarter ; B 73 -19 756 703 ;
+C -1 ; WX 667 ; N Scaron ; B 49 -19 620 929 ;
+C -1 ; WX 667 ; N Scommaaccent ; B 49 -225 620 737 ;
+C -1 ; WX 778 ; N Ohungarumlaut ; B 39 -19 739 929 ;
+C -1 ; WX 400 ; N degree ; B 54 411 346 703 ;
+C -1 ; WX 556 ; N ograve ; B 35 -14 521 734 ;
+C -1 ; WX 722 ; N Ccaron ; B 44 -19 681 929 ;
+C -1 ; WX 556 ; N ugrave ; B 68 -15 489 734 ;
+C -1 ; WX 453 ; N radical ; B -4 -80 458 762 ;
+C -1 ; WX 722 ; N Dcaron ; B 81 0 674 929 ;
+C -1 ; WX 333 ; N rcommaaccent ; B 77 -225 332 538 ;
+C -1 ; WX 722 ; N Ntilde ; B 76 0 646 917 ;
+C -1 ; WX 556 ; N otilde ; B 35 -14 521 722 ;
+C -1 ; WX 722 ; N Rcommaaccent ; B 88 -225 684 718 ;
+C -1 ; WX 556 ; N Lcommaaccent ; B 76 -225 537 718 ;
+C -1 ; WX 667 ; N Atilde ; B 14 0 654 917 ;
+C -1 ; WX 667 ; N Aogonek ; B 14 -225 654 718 ;
+C -1 ; WX 667 ; N Aring ; B 14 0 654 931 ;
+C -1 ; WX 778 ; N Otilde ; B 39 -19 739 917 ;
+C -1 ; WX 500 ; N zdotaccent ; B 31 0 469 706 ;
+C -1 ; WX 667 ; N Ecaron ; B 86 0 616 929 ;
+C -1 ; WX 278 ; N Iogonek ; B -3 -225 211 718 ;
+C -1 ; WX 500 ; N kcommaaccent ; B 67 -225 501 718 ;
+C -1 ; WX 584 ; N minus ; B 39 216 545 289 ;
+C -1 ; WX 278 ; N Icircumflex ; B -6 0 285 929 ;
+C -1 ; WX 556 ; N ncaron ; B 65 0 491 734 ;
+C -1 ; WX 278 ; N tcommaaccent ; B 14 -225 257 669 ;
+C -1 ; WX 584 ; N logicalnot ; B 39 108 545 390 ;
+C -1 ; WX 556 ; N odieresis ; B 35 -14 521 706 ;
+C -1 ; WX 556 ; N udieresis ; B 68 -15 489 706 ;
+C -1 ; WX 549 ; N notequal ; B 12 -35 537 551 ;
+C -1 ; WX 556 ; N gcommaaccent ; B 40 -220 499 822 ;
+C -1 ; WX 556 ; N eth ; B 35 -15 522 737 ;
+C -1 ; WX 500 ; N zcaron ; B 31 0 469 734 ;
+C -1 ; WX 556 ; N ncommaaccent ; B 65 -225 491 538 ;
+C -1 ; WX 333 ; N onesuperior ; B 43 281 222 703 ;
+C -1 ; WX 278 ; N imacron ; B 5 0 272 684 ;
+C -1 ; WX 556 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+StartKernData
+StartKernPairs 2705
+KPX A C -30
+KPX A Cacute -30
+KPX A Ccaron -30
+KPX A Ccedilla -30
+KPX A G -30
+KPX A Gbreve -30
+KPX A Gcommaaccent -30
+KPX A O -30
+KPX A Oacute -30
+KPX A Ocircumflex -30
+KPX A Odieresis -30
+KPX A Ograve -30
+KPX A Ohungarumlaut -30
+KPX A Omacron -30
+KPX A Oslash -30
+KPX A Otilde -30
+KPX A Q -30
+KPX A T -120
+KPX A Tcaron -120
+KPX A Tcommaaccent -120
+KPX A U -50
+KPX A Uacute -50
+KPX A Ucircumflex -50
+KPX A Udieresis -50
+KPX A Ugrave -50
+KPX A Uhungarumlaut -50
+KPX A Umacron -50
+KPX A Uogonek -50
+KPX A Uring -50
+KPX A V -70
+KPX A W -50
+KPX A Y -100
+KPX A Yacute -100
+KPX A Ydieresis -100
+KPX A u -30
+KPX A uacute -30
+KPX A ucircumflex -30
+KPX A udieresis -30
+KPX A ugrave -30
+KPX A uhungarumlaut -30
+KPX A umacron -30
+KPX A uogonek -30
+KPX A uring -30
+KPX A v -40
+KPX A w -40
+KPX A y -40
+KPX A yacute -40
+KPX A ydieresis -40
+KPX Aacute C -30
+KPX Aacute Cacute -30
+KPX Aacute Ccaron -30
+KPX Aacute Ccedilla -30
+KPX Aacute G -30
+KPX Aacute Gbreve -30
+KPX Aacute Gcommaaccent -30
+KPX Aacute O -30
+KPX Aacute Oacute -30
+KPX Aacute Ocircumflex -30
+KPX Aacute Odieresis -30
+KPX Aacute Ograve -30
+KPX Aacute Ohungarumlaut -30
+KPX Aacute Omacron -30
+KPX Aacute Oslash -30
+KPX Aacute Otilde -30
+KPX Aacute Q -30
+KPX Aacute T -120
+KPX Aacute Tcaron -120
+KPX Aacute Tcommaaccent -120
+KPX Aacute U -50
+KPX Aacute Uacute -50
+KPX Aacute Ucircumflex -50
+KPX Aacute Udieresis -50
+KPX Aacute Ugrave -50
+KPX Aacute Uhungarumlaut -50
+KPX Aacute Umacron -50
+KPX Aacute Uogonek -50
+KPX Aacute Uring -50
+KPX Aacute V -70
+KPX Aacute W -50
+KPX Aacute Y -100
+KPX Aacute Yacute -100
+KPX Aacute Ydieresis -100
+KPX Aacute u -30
+KPX Aacute uacute -30
+KPX Aacute ucircumflex -30
+KPX Aacute udieresis -30
+KPX Aacute ugrave -30
+KPX Aacute uhungarumlaut -30
+KPX Aacute umacron -30
+KPX Aacute uogonek -30
+KPX Aacute uring -30
+KPX Aacute v -40
+KPX Aacute w -40
+KPX Aacute y -40
+KPX Aacute yacute -40
+KPX Aacute ydieresis -40
+KPX Abreve C -30
+KPX Abreve Cacute -30
+KPX Abreve Ccaron -30
+KPX Abreve Ccedilla -30
+KPX Abreve G -30
+KPX Abreve Gbreve -30
+KPX Abreve Gcommaaccent -30
+KPX Abreve O -30
+KPX Abreve Oacute -30
+KPX Abreve Ocircumflex -30
+KPX Abreve Odieresis -30
+KPX Abreve Ograve -30
+KPX Abreve Ohungarumlaut -30
+KPX Abreve Omacron -30
+KPX Abreve Oslash -30
+KPX Abreve Otilde -30
+KPX Abreve Q -30
+KPX Abreve T -120
+KPX Abreve Tcaron -120
+KPX Abreve Tcommaaccent -120
+KPX Abreve U -50
+KPX Abreve Uacute -50
+KPX Abreve Ucircumflex -50
+KPX Abreve Udieresis -50
+KPX Abreve Ugrave -50
+KPX Abreve Uhungarumlaut -50
+KPX Abreve Umacron -50
+KPX Abreve Uogonek -50
+KPX Abreve Uring -50
+KPX Abreve V -70
+KPX Abreve W -50
+KPX Abreve Y -100
+KPX Abreve Yacute -100
+KPX Abreve Ydieresis -100
+KPX Abreve u -30
+KPX Abreve uacute -30
+KPX Abreve ucircumflex -30
+KPX Abreve udieresis -30
+KPX Abreve ugrave -30
+KPX Abreve uhungarumlaut -30
+KPX Abreve umacron -30
+KPX Abreve uogonek -30
+KPX Abreve uring -30
+KPX Abreve v -40
+KPX Abreve w -40
+KPX Abreve y -40
+KPX Abreve yacute -40
+KPX Abreve ydieresis -40
+KPX Acircumflex C -30
+KPX Acircumflex Cacute -30
+KPX Acircumflex Ccaron -30
+KPX Acircumflex Ccedilla -30
+KPX Acircumflex G -30
+KPX Acircumflex Gbreve -30
+KPX Acircumflex Gcommaaccent -30
+KPX Acircumflex O -30
+KPX Acircumflex Oacute -30
+KPX Acircumflex Ocircumflex -30
+KPX Acircumflex Odieresis -30
+KPX Acircumflex Ograve -30
+KPX Acircumflex Ohungarumlaut -30
+KPX Acircumflex Omacron -30
+KPX Acircumflex Oslash -30
+KPX Acircumflex Otilde -30
+KPX Acircumflex Q -30
+KPX Acircumflex T -120
+KPX Acircumflex Tcaron -120
+KPX Acircumflex Tcommaaccent -120
+KPX Acircumflex U -50
+KPX Acircumflex Uacute -50
+KPX Acircumflex Ucircumflex -50
+KPX Acircumflex Udieresis -50
+KPX Acircumflex Ugrave -50
+KPX Acircumflex Uhungarumlaut -50
+KPX Acircumflex Umacron -50
+KPX Acircumflex Uogonek -50
+KPX Acircumflex Uring -50
+KPX Acircumflex V -70
+KPX Acircumflex W -50
+KPX Acircumflex Y -100
+KPX Acircumflex Yacute -100
+KPX Acircumflex Ydieresis -100
+KPX Acircumflex u -30
+KPX Acircumflex uacute -30
+KPX Acircumflex ucircumflex -30
+KPX Acircumflex udieresis -30
+KPX Acircumflex ugrave -30
+KPX Acircumflex uhungarumlaut -30
+KPX Acircumflex umacron -30
+KPX Acircumflex uogonek -30
+KPX Acircumflex uring -30
+KPX Acircumflex v -40
+KPX Acircumflex w -40
+KPX Acircumflex y -40
+KPX Acircumflex yacute -40
+KPX Acircumflex ydieresis -40
+KPX Adieresis C -30
+KPX Adieresis Cacute -30
+KPX Adieresis Ccaron -30
+KPX Adieresis Ccedilla -30
+KPX Adieresis G -30
+KPX Adieresis Gbreve -30
+KPX Adieresis Gcommaaccent -30
+KPX Adieresis O -30
+KPX Adieresis Oacute -30
+KPX Adieresis Ocircumflex -30
+KPX Adieresis Odieresis -30
+KPX Adieresis Ograve -30
+KPX Adieresis Ohungarumlaut -30
+KPX Adieresis Omacron -30
+KPX Adieresis Oslash -30
+KPX Adieresis Otilde -30
+KPX Adieresis Q -30
+KPX Adieresis T -120
+KPX Adieresis Tcaron -120
+KPX Adieresis Tcommaaccent -120
+KPX Adieresis U -50
+KPX Adieresis Uacute -50
+KPX Adieresis Ucircumflex -50
+KPX Adieresis Udieresis -50
+KPX Adieresis Ugrave -50
+KPX Adieresis Uhungarumlaut -50
+KPX Adieresis Umacron -50
+KPX Adieresis Uogonek -50
+KPX Adieresis Uring -50
+KPX Adieresis V -70
+KPX Adieresis W -50
+KPX Adieresis Y -100
+KPX Adieresis Yacute -100
+KPX Adieresis Ydieresis -100
+KPX Adieresis u -30
+KPX Adieresis uacute -30
+KPX Adieresis ucircumflex -30
+KPX Adieresis udieresis -30
+KPX Adieresis ugrave -30
+KPX Adieresis uhungarumlaut -30
+KPX Adieresis umacron -30
+KPX Adieresis uogonek -30
+KPX Adieresis uring -30
+KPX Adieresis v -40
+KPX Adieresis w -40
+KPX Adieresis y -40
+KPX Adieresis yacute -40
+KPX Adieresis ydieresis -40
+KPX Agrave C -30
+KPX Agrave Cacute -30
+KPX Agrave Ccaron -30
+KPX Agrave Ccedilla -30
+KPX Agrave G -30
+KPX Agrave Gbreve -30
+KPX Agrave Gcommaaccent -30
+KPX Agrave O -30
+KPX Agrave Oacute -30
+KPX Agrave Ocircumflex -30
+KPX Agrave Odieresis -30
+KPX Agrave Ograve -30
+KPX Agrave Ohungarumlaut -30
+KPX Agrave Omacron -30
+KPX Agrave Oslash -30
+KPX Agrave Otilde -30
+KPX Agrave Q -30
+KPX Agrave T -120
+KPX Agrave Tcaron -120
+KPX Agrave Tcommaaccent -120
+KPX Agrave U -50
+KPX Agrave Uacute -50
+KPX Agrave Ucircumflex -50
+KPX Agrave Udieresis -50
+KPX Agrave Ugrave -50
+KPX Agrave Uhungarumlaut -50
+KPX Agrave Umacron -50
+KPX Agrave Uogonek -50
+KPX Agrave Uring -50
+KPX Agrave V -70
+KPX Agrave W -50
+KPX Agrave Y -100
+KPX Agrave Yacute -100
+KPX Agrave Ydieresis -100
+KPX Agrave u -30
+KPX Agrave uacute -30
+KPX Agrave ucircumflex -30
+KPX Agrave udieresis -30
+KPX Agrave ugrave -30
+KPX Agrave uhungarumlaut -30
+KPX Agrave umacron -30
+KPX Agrave uogonek -30
+KPX Agrave uring -30
+KPX Agrave v -40
+KPX Agrave w -40
+KPX Agrave y -40
+KPX Agrave yacute -40
+KPX Agrave ydieresis -40
+KPX Amacron C -30
+KPX Amacron Cacute -30
+KPX Amacron Ccaron -30
+KPX Amacron Ccedilla -30
+KPX Amacron G -30
+KPX Amacron Gbreve -30
+KPX Amacron Gcommaaccent -30
+KPX Amacron O -30
+KPX Amacron Oacute -30
+KPX Amacron Ocircumflex -30
+KPX Amacron Odieresis -30
+KPX Amacron Ograve -30
+KPX Amacron Ohungarumlaut -30
+KPX Amacron Omacron -30
+KPX Amacron Oslash -30
+KPX Amacron Otilde -30
+KPX Amacron Q -30
+KPX Amacron T -120
+KPX Amacron Tcaron -120
+KPX Amacron Tcommaaccent -120
+KPX Amacron U -50
+KPX Amacron Uacute -50
+KPX Amacron Ucircumflex -50
+KPX Amacron Udieresis -50
+KPX Amacron Ugrave -50
+KPX Amacron Uhungarumlaut -50
+KPX Amacron Umacron -50
+KPX Amacron Uogonek -50
+KPX Amacron Uring -50
+KPX Amacron V -70
+KPX Amacron W -50
+KPX Amacron Y -100
+KPX Amacron Yacute -100
+KPX Amacron Ydieresis -100
+KPX Amacron u -30
+KPX Amacron uacute -30
+KPX Amacron ucircumflex -30
+KPX Amacron udieresis -30
+KPX Amacron ugrave -30
+KPX Amacron uhungarumlaut -30
+KPX Amacron umacron -30
+KPX Amacron uogonek -30
+KPX Amacron uring -30
+KPX Amacron v -40
+KPX Amacron w -40
+KPX Amacron y -40
+KPX Amacron yacute -40
+KPX Amacron ydieresis -40
+KPX Aogonek C -30
+KPX Aogonek Cacute -30
+KPX Aogonek Ccaron -30
+KPX Aogonek Ccedilla -30
+KPX Aogonek G -30
+KPX Aogonek Gbreve -30
+KPX Aogonek Gcommaaccent -30
+KPX Aogonek O -30
+KPX Aogonek Oacute -30
+KPX Aogonek Ocircumflex -30
+KPX Aogonek Odieresis -30
+KPX Aogonek Ograve -30
+KPX Aogonek Ohungarumlaut -30
+KPX Aogonek Omacron -30
+KPX Aogonek Oslash -30
+KPX Aogonek Otilde -30
+KPX Aogonek Q -30
+KPX Aogonek T -120
+KPX Aogonek Tcaron -120
+KPX Aogonek Tcommaaccent -120
+KPX Aogonek U -50
+KPX Aogonek Uacute -50
+KPX Aogonek Ucircumflex -50
+KPX Aogonek Udieresis -50
+KPX Aogonek Ugrave -50
+KPX Aogonek Uhungarumlaut -50
+KPX Aogonek Umacron -50
+KPX Aogonek Uogonek -50
+KPX Aogonek Uring -50
+KPX Aogonek V -70
+KPX Aogonek W -50
+KPX Aogonek Y -100
+KPX Aogonek Yacute -100
+KPX Aogonek Ydieresis -100
+KPX Aogonek u -30
+KPX Aogonek uacute -30
+KPX Aogonek ucircumflex -30
+KPX Aogonek udieresis -30
+KPX Aogonek ugrave -30
+KPX Aogonek uhungarumlaut -30
+KPX Aogonek umacron -30
+KPX Aogonek uogonek -30
+KPX Aogonek uring -30
+KPX Aogonek v -40
+KPX Aogonek w -40
+KPX Aogonek y -40
+KPX Aogonek yacute -40
+KPX Aogonek ydieresis -40
+KPX Aring C -30
+KPX Aring Cacute -30
+KPX Aring Ccaron -30
+KPX Aring Ccedilla -30
+KPX Aring G -30
+KPX Aring Gbreve -30
+KPX Aring Gcommaaccent -30
+KPX Aring O -30
+KPX Aring Oacute -30
+KPX Aring Ocircumflex -30
+KPX Aring Odieresis -30
+KPX Aring Ograve -30
+KPX Aring Ohungarumlaut -30
+KPX Aring Omacron -30
+KPX Aring Oslash -30
+KPX Aring Otilde -30
+KPX Aring Q -30
+KPX Aring T -120
+KPX Aring Tcaron -120
+KPX Aring Tcommaaccent -120
+KPX Aring U -50
+KPX Aring Uacute -50
+KPX Aring Ucircumflex -50
+KPX Aring Udieresis -50
+KPX Aring Ugrave -50
+KPX Aring Uhungarumlaut -50
+KPX Aring Umacron -50
+KPX Aring Uogonek -50
+KPX Aring Uring -50
+KPX Aring V -70
+KPX Aring W -50
+KPX Aring Y -100
+KPX Aring Yacute -100
+KPX Aring Ydieresis -100
+KPX Aring u -30
+KPX Aring uacute -30
+KPX Aring ucircumflex -30
+KPX Aring udieresis -30
+KPX Aring ugrave -30
+KPX Aring uhungarumlaut -30
+KPX Aring umacron -30
+KPX Aring uogonek -30
+KPX Aring uring -30
+KPX Aring v -40
+KPX Aring w -40
+KPX Aring y -40
+KPX Aring yacute -40
+KPX Aring ydieresis -40
+KPX Atilde C -30
+KPX Atilde Cacute -30
+KPX Atilde Ccaron -30
+KPX Atilde Ccedilla -30
+KPX Atilde G -30
+KPX Atilde Gbreve -30
+KPX Atilde Gcommaaccent -30
+KPX Atilde O -30
+KPX Atilde Oacute -30
+KPX Atilde Ocircumflex -30
+KPX Atilde Odieresis -30
+KPX Atilde Ograve -30
+KPX Atilde Ohungarumlaut -30
+KPX Atilde Omacron -30
+KPX Atilde Oslash -30
+KPX Atilde Otilde -30
+KPX Atilde Q -30
+KPX Atilde T -120
+KPX Atilde Tcaron -120
+KPX Atilde Tcommaaccent -120
+KPX Atilde U -50
+KPX Atilde Uacute -50
+KPX Atilde Ucircumflex -50
+KPX Atilde Udieresis -50
+KPX Atilde Ugrave -50
+KPX Atilde Uhungarumlaut -50
+KPX Atilde Umacron -50
+KPX Atilde Uogonek -50
+KPX Atilde Uring -50
+KPX Atilde V -70
+KPX Atilde W -50
+KPX Atilde Y -100
+KPX Atilde Yacute -100
+KPX Atilde Ydieresis -100
+KPX Atilde u -30
+KPX Atilde uacute -30
+KPX Atilde ucircumflex -30
+KPX Atilde udieresis -30
+KPX Atilde ugrave -30
+KPX Atilde uhungarumlaut -30
+KPX Atilde umacron -30
+KPX Atilde uogonek -30
+KPX Atilde uring -30
+KPX Atilde v -40
+KPX Atilde w -40
+KPX Atilde y -40
+KPX Atilde yacute -40
+KPX Atilde ydieresis -40
+KPX B U -10
+KPX B Uacute -10
+KPX B Ucircumflex -10
+KPX B Udieresis -10
+KPX B Ugrave -10
+KPX B Uhungarumlaut -10
+KPX B Umacron -10
+KPX B Uogonek -10
+KPX B Uring -10
+KPX B comma -20
+KPX B period -20
+KPX C comma -30
+KPX C period -30
+KPX Cacute comma -30
+KPX Cacute period -30
+KPX Ccaron comma -30
+KPX Ccaron period -30
+KPX Ccedilla comma -30
+KPX Ccedilla period -30
+KPX D A -40
+KPX D Aacute -40
+KPX D Abreve -40
+KPX D Acircumflex -40
+KPX D Adieresis -40
+KPX D Agrave -40
+KPX D Amacron -40
+KPX D Aogonek -40
+KPX D Aring -40
+KPX D Atilde -40
+KPX D V -70
+KPX D W -40
+KPX D Y -90
+KPX D Yacute -90
+KPX D Ydieresis -90
+KPX D comma -70
+KPX D period -70
+KPX Dcaron A -40
+KPX Dcaron Aacute -40
+KPX Dcaron Abreve -40
+KPX Dcaron Acircumflex -40
+KPX Dcaron Adieresis -40
+KPX Dcaron Agrave -40
+KPX Dcaron Amacron -40
+KPX Dcaron Aogonek -40
+KPX Dcaron Aring -40
+KPX Dcaron Atilde -40
+KPX Dcaron V -70
+KPX Dcaron W -40
+KPX Dcaron Y -90
+KPX Dcaron Yacute -90
+KPX Dcaron Ydieresis -90
+KPX Dcaron comma -70
+KPX Dcaron period -70
+KPX Dcroat A -40
+KPX Dcroat Aacute -40
+KPX Dcroat Abreve -40
+KPX Dcroat Acircumflex -40
+KPX Dcroat Adieresis -40
+KPX Dcroat Agrave -40
+KPX Dcroat Amacron -40
+KPX Dcroat Aogonek -40
+KPX Dcroat Aring -40
+KPX Dcroat Atilde -40
+KPX Dcroat V -70
+KPX Dcroat W -40
+KPX Dcroat Y -90
+KPX Dcroat Yacute -90
+KPX Dcroat Ydieresis -90
+KPX Dcroat comma -70
+KPX Dcroat period -70
+KPX F A -80
+KPX F Aacute -80
+KPX F Abreve -80
+KPX F Acircumflex -80
+KPX F Adieresis -80
+KPX F Agrave -80
+KPX F Amacron -80
+KPX F Aogonek -80
+KPX F Aring -80
+KPX F Atilde -80
+KPX F a -50
+KPX F aacute -50
+KPX F abreve -50
+KPX F acircumflex -50
+KPX F adieresis -50
+KPX F agrave -50
+KPX F amacron -50
+KPX F aogonek -50
+KPX F aring -50
+KPX F atilde -50
+KPX F comma -150
+KPX F e -30
+KPX F eacute -30
+KPX F ecaron -30
+KPX F ecircumflex -30
+KPX F edieresis -30
+KPX F edotaccent -30
+KPX F egrave -30
+KPX F emacron -30
+KPX F eogonek -30
+KPX F o -30
+KPX F oacute -30
+KPX F ocircumflex -30
+KPX F odieresis -30
+KPX F ograve -30
+KPX F ohungarumlaut -30
+KPX F omacron -30
+KPX F oslash -30
+KPX F otilde -30
+KPX F period -150
+KPX F r -45
+KPX F racute -45
+KPX F rcaron -45
+KPX F rcommaaccent -45
+KPX J A -20
+KPX J Aacute -20
+KPX J Abreve -20
+KPX J Acircumflex -20
+KPX J Adieresis -20
+KPX J Agrave -20
+KPX J Amacron -20
+KPX J Aogonek -20
+KPX J Aring -20
+KPX J Atilde -20
+KPX J a -20
+KPX J aacute -20
+KPX J abreve -20
+KPX J acircumflex -20
+KPX J adieresis -20
+KPX J agrave -20
+KPX J amacron -20
+KPX J aogonek -20
+KPX J aring -20
+KPX J atilde -20
+KPX J comma -30
+KPX J period -30
+KPX J u -20
+KPX J uacute -20
+KPX J ucircumflex -20
+KPX J udieresis -20
+KPX J ugrave -20
+KPX J uhungarumlaut -20
+KPX J umacron -20
+KPX J uogonek -20
+KPX J uring -20
+KPX K O -50
+KPX K Oacute -50
+KPX K Ocircumflex -50
+KPX K Odieresis -50
+KPX K Ograve -50
+KPX K Ohungarumlaut -50
+KPX K Omacron -50
+KPX K Oslash -50
+KPX K Otilde -50
+KPX K e -40
+KPX K eacute -40
+KPX K ecaron -40
+KPX K ecircumflex -40
+KPX K edieresis -40
+KPX K edotaccent -40
+KPX K egrave -40
+KPX K emacron -40
+KPX K eogonek -40
+KPX K o -40
+KPX K oacute -40
+KPX K ocircumflex -40
+KPX K odieresis -40
+KPX K ograve -40
+KPX K ohungarumlaut -40
+KPX K omacron -40
+KPX K oslash -40
+KPX K otilde -40
+KPX K u -30
+KPX K uacute -30
+KPX K ucircumflex -30
+KPX K udieresis -30
+KPX K ugrave -30
+KPX K uhungarumlaut -30
+KPX K umacron -30
+KPX K uogonek -30
+KPX K uring -30
+KPX K y -50
+KPX K yacute -50
+KPX K ydieresis -50
+KPX Kcommaaccent O -50
+KPX Kcommaaccent Oacute -50
+KPX Kcommaaccent Ocircumflex -50
+KPX Kcommaaccent Odieresis -50
+KPX Kcommaaccent Ograve -50
+KPX Kcommaaccent Ohungarumlaut -50
+KPX Kcommaaccent Omacron -50
+KPX Kcommaaccent Oslash -50
+KPX Kcommaaccent Otilde -50
+KPX Kcommaaccent e -40
+KPX Kcommaaccent eacute -40
+KPX Kcommaaccent ecaron -40
+KPX Kcommaaccent ecircumflex -40
+KPX Kcommaaccent edieresis -40
+KPX Kcommaaccent edotaccent -40
+KPX Kcommaaccent egrave -40
+KPX Kcommaaccent emacron -40
+KPX Kcommaaccent eogonek -40
+KPX Kcommaaccent o -40
+KPX Kcommaaccent oacute -40
+KPX Kcommaaccent ocircumflex -40
+KPX Kcommaaccent odieresis -40
+KPX Kcommaaccent ograve -40
+KPX Kcommaaccent ohungarumlaut -40
+KPX Kcommaaccent omacron -40
+KPX Kcommaaccent oslash -40
+KPX Kcommaaccent otilde -40
+KPX Kcommaaccent u -30
+KPX Kcommaaccent uacute -30
+KPX Kcommaaccent ucircumflex -30
+KPX Kcommaaccent udieresis -30
+KPX Kcommaaccent ugrave -30
+KPX Kcommaaccent uhungarumlaut -30
+KPX Kcommaaccent umacron -30
+KPX Kcommaaccent uogonek -30
+KPX Kcommaaccent uring -30
+KPX Kcommaaccent y -50
+KPX Kcommaaccent yacute -50
+KPX Kcommaaccent ydieresis -50
+KPX L T -110
+KPX L Tcaron -110
+KPX L Tcommaaccent -110
+KPX L V -110
+KPX L W -70
+KPX L Y -140
+KPX L Yacute -140
+KPX L Ydieresis -140
+KPX L quotedblright -140
+KPX L quoteright -160
+KPX L y -30
+KPX L yacute -30
+KPX L ydieresis -30
+KPX Lacute T -110
+KPX Lacute Tcaron -110
+KPX Lacute Tcommaaccent -110
+KPX Lacute V -110
+KPX Lacute W -70
+KPX Lacute Y -140
+KPX Lacute Yacute -140
+KPX Lacute Ydieresis -140
+KPX Lacute quotedblright -140
+KPX Lacute quoteright -160
+KPX Lacute y -30
+KPX Lacute yacute -30
+KPX Lacute ydieresis -30
+KPX Lcaron T -110
+KPX Lcaron Tcaron -110
+KPX Lcaron Tcommaaccent -110
+KPX Lcaron V -110
+KPX Lcaron W -70
+KPX Lcaron Y -140
+KPX Lcaron Yacute -140
+KPX Lcaron Ydieresis -140
+KPX Lcaron quotedblright -140
+KPX Lcaron quoteright -160
+KPX Lcaron y -30
+KPX Lcaron yacute -30
+KPX Lcaron ydieresis -30
+KPX Lcommaaccent T -110
+KPX Lcommaaccent Tcaron -110
+KPX Lcommaaccent Tcommaaccent -110
+KPX Lcommaaccent V -110
+KPX Lcommaaccent W -70
+KPX Lcommaaccent Y -140
+KPX Lcommaaccent Yacute -140
+KPX Lcommaaccent Ydieresis -140
+KPX Lcommaaccent quotedblright -140
+KPX Lcommaaccent quoteright -160
+KPX Lcommaaccent y -30
+KPX Lcommaaccent yacute -30
+KPX Lcommaaccent ydieresis -30
+KPX Lslash T -110
+KPX Lslash Tcaron -110
+KPX Lslash Tcommaaccent -110
+KPX Lslash V -110
+KPX Lslash W -70
+KPX Lslash Y -140
+KPX Lslash Yacute -140
+KPX Lslash Ydieresis -140
+KPX Lslash quotedblright -140
+KPX Lslash quoteright -160
+KPX Lslash y -30
+KPX Lslash yacute -30
+KPX Lslash ydieresis -30
+KPX O A -20
+KPX O Aacute -20
+KPX O Abreve -20
+KPX O Acircumflex -20
+KPX O Adieresis -20
+KPX O Agrave -20
+KPX O Amacron -20
+KPX O Aogonek -20
+KPX O Aring -20
+KPX O Atilde -20
+KPX O T -40
+KPX O Tcaron -40
+KPX O Tcommaaccent -40
+KPX O V -50
+KPX O W -30
+KPX O X -60
+KPX O Y -70
+KPX O Yacute -70
+KPX O Ydieresis -70
+KPX O comma -40
+KPX O period -40
+KPX Oacute A -20
+KPX Oacute Aacute -20
+KPX Oacute Abreve -20
+KPX Oacute Acircumflex -20
+KPX Oacute Adieresis -20
+KPX Oacute Agrave -20
+KPX Oacute Amacron -20
+KPX Oacute Aogonek -20
+KPX Oacute Aring -20
+KPX Oacute Atilde -20
+KPX Oacute T -40
+KPX Oacute Tcaron -40
+KPX Oacute Tcommaaccent -40
+KPX Oacute V -50
+KPX Oacute W -30
+KPX Oacute X -60
+KPX Oacute Y -70
+KPX Oacute Yacute -70
+KPX Oacute Ydieresis -70
+KPX Oacute comma -40
+KPX Oacute period -40
+KPX Ocircumflex A -20
+KPX Ocircumflex Aacute -20
+KPX Ocircumflex Abreve -20
+KPX Ocircumflex Acircumflex -20
+KPX Ocircumflex Adieresis -20
+KPX Ocircumflex Agrave -20
+KPX Ocircumflex Amacron -20
+KPX Ocircumflex Aogonek -20
+KPX Ocircumflex Aring -20
+KPX Ocircumflex Atilde -20
+KPX Ocircumflex T -40
+KPX Ocircumflex Tcaron -40
+KPX Ocircumflex Tcommaaccent -40
+KPX Ocircumflex V -50
+KPX Ocircumflex W -30
+KPX Ocircumflex X -60
+KPX Ocircumflex Y -70
+KPX Ocircumflex Yacute -70
+KPX Ocircumflex Ydieresis -70
+KPX Ocircumflex comma -40
+KPX Ocircumflex period -40
+KPX Odieresis A -20
+KPX Odieresis Aacute -20
+KPX Odieresis Abreve -20
+KPX Odieresis Acircumflex -20
+KPX Odieresis Adieresis -20
+KPX Odieresis Agrave -20
+KPX Odieresis Amacron -20
+KPX Odieresis Aogonek -20
+KPX Odieresis Aring -20
+KPX Odieresis Atilde -20
+KPX Odieresis T -40
+KPX Odieresis Tcaron -40
+KPX Odieresis Tcommaaccent -40
+KPX Odieresis V -50
+KPX Odieresis W -30
+KPX Odieresis X -60
+KPX Odieresis Y -70
+KPX Odieresis Yacute -70
+KPX Odieresis Ydieresis -70
+KPX Odieresis comma -40
+KPX Odieresis period -40
+KPX Ograve A -20
+KPX Ograve Aacute -20
+KPX Ograve Abreve -20
+KPX Ograve Acircumflex -20
+KPX Ograve Adieresis -20
+KPX Ograve Agrave -20
+KPX Ograve Amacron -20
+KPX Ograve Aogonek -20
+KPX Ograve Aring -20
+KPX Ograve Atilde -20
+KPX Ograve T -40
+KPX Ograve Tcaron -40
+KPX Ograve Tcommaaccent -40
+KPX Ograve V -50
+KPX Ograve W -30
+KPX Ograve X -60
+KPX Ograve Y -70
+KPX Ograve Yacute -70
+KPX Ograve Ydieresis -70
+KPX Ograve comma -40
+KPX Ograve period -40
+KPX Ohungarumlaut A -20
+KPX Ohungarumlaut Aacute -20
+KPX Ohungarumlaut Abreve -20
+KPX Ohungarumlaut Acircumflex -20
+KPX Ohungarumlaut Adieresis -20
+KPX Ohungarumlaut Agrave -20
+KPX Ohungarumlaut Amacron -20
+KPX Ohungarumlaut Aogonek -20
+KPX Ohungarumlaut Aring -20
+KPX Ohungarumlaut Atilde -20
+KPX Ohungarumlaut T -40
+KPX Ohungarumlaut Tcaron -40
+KPX Ohungarumlaut Tcommaaccent -40
+KPX Ohungarumlaut V -50
+KPX Ohungarumlaut W -30
+KPX Ohungarumlaut X -60
+KPX Ohungarumlaut Y -70
+KPX Ohungarumlaut Yacute -70
+KPX Ohungarumlaut Ydieresis -70
+KPX Ohungarumlaut comma -40
+KPX Ohungarumlaut period -40
+KPX Omacron A -20
+KPX Omacron Aacute -20
+KPX Omacron Abreve -20
+KPX Omacron Acircumflex -20
+KPX Omacron Adieresis -20
+KPX Omacron Agrave -20
+KPX Omacron Amacron -20
+KPX Omacron Aogonek -20
+KPX Omacron Aring -20
+KPX Omacron Atilde -20
+KPX Omacron T -40
+KPX Omacron Tcaron -40
+KPX Omacron Tcommaaccent -40
+KPX Omacron V -50
+KPX Omacron W -30
+KPX Omacron X -60
+KPX Omacron Y -70
+KPX Omacron Yacute -70
+KPX Omacron Ydieresis -70
+KPX Omacron comma -40
+KPX Omacron period -40
+KPX Oslash A -20
+KPX Oslash Aacute -20
+KPX Oslash Abreve -20
+KPX Oslash Acircumflex -20
+KPX Oslash Adieresis -20
+KPX Oslash Agrave -20
+KPX Oslash Amacron -20
+KPX Oslash Aogonek -20
+KPX Oslash Aring -20
+KPX Oslash Atilde -20
+KPX Oslash T -40
+KPX Oslash Tcaron -40
+KPX Oslash Tcommaaccent -40
+KPX Oslash V -50
+KPX Oslash W -30
+KPX Oslash X -60
+KPX Oslash Y -70
+KPX Oslash Yacute -70
+KPX Oslash Ydieresis -70
+KPX Oslash comma -40
+KPX Oslash period -40
+KPX Otilde A -20
+KPX Otilde Aacute -20
+KPX Otilde Abreve -20
+KPX Otilde Acircumflex -20
+KPX Otilde Adieresis -20
+KPX Otilde Agrave -20
+KPX Otilde Amacron -20
+KPX Otilde Aogonek -20
+KPX Otilde Aring -20
+KPX Otilde Atilde -20
+KPX Otilde T -40
+KPX Otilde Tcaron -40
+KPX Otilde Tcommaaccent -40
+KPX Otilde V -50
+KPX Otilde W -30
+KPX Otilde X -60
+KPX Otilde Y -70
+KPX Otilde Yacute -70
+KPX Otilde Ydieresis -70
+KPX Otilde comma -40
+KPX Otilde period -40
+KPX P A -120
+KPX P Aacute -120
+KPX P Abreve -120
+KPX P Acircumflex -120
+KPX P Adieresis -120
+KPX P Agrave -120
+KPX P Amacron -120
+KPX P Aogonek -120
+KPX P Aring -120
+KPX P Atilde -120
+KPX P a -40
+KPX P aacute -40
+KPX P abreve -40
+KPX P acircumflex -40
+KPX P adieresis -40
+KPX P agrave -40
+KPX P amacron -40
+KPX P aogonek -40
+KPX P aring -40
+KPX P atilde -40
+KPX P comma -180
+KPX P e -50
+KPX P eacute -50
+KPX P ecaron -50
+KPX P ecircumflex -50
+KPX P edieresis -50
+KPX P edotaccent -50
+KPX P egrave -50
+KPX P emacron -50
+KPX P eogonek -50
+KPX P o -50
+KPX P oacute -50
+KPX P ocircumflex -50
+KPX P odieresis -50
+KPX P ograve -50
+KPX P ohungarumlaut -50
+KPX P omacron -50
+KPX P oslash -50
+KPX P otilde -50
+KPX P period -180
+KPX Q U -10
+KPX Q Uacute -10
+KPX Q Ucircumflex -10
+KPX Q Udieresis -10
+KPX Q Ugrave -10
+KPX Q Uhungarumlaut -10
+KPX Q Umacron -10
+KPX Q Uogonek -10
+KPX Q Uring -10
+KPX R O -20
+KPX R Oacute -20
+KPX R Ocircumflex -20
+KPX R Odieresis -20
+KPX R Ograve -20
+KPX R Ohungarumlaut -20
+KPX R Omacron -20
+KPX R Oslash -20
+KPX R Otilde -20
+KPX R T -30
+KPX R Tcaron -30
+KPX R Tcommaaccent -30
+KPX R U -40
+KPX R Uacute -40
+KPX R Ucircumflex -40
+KPX R Udieresis -40
+KPX R Ugrave -40
+KPX R Uhungarumlaut -40
+KPX R Umacron -40
+KPX R Uogonek -40
+KPX R Uring -40
+KPX R V -50
+KPX R W -30
+KPX R Y -50
+KPX R Yacute -50
+KPX R Ydieresis -50
+KPX Racute O -20
+KPX Racute Oacute -20
+KPX Racute Ocircumflex -20
+KPX Racute Odieresis -20
+KPX Racute Ograve -20
+KPX Racute Ohungarumlaut -20
+KPX Racute Omacron -20
+KPX Racute Oslash -20
+KPX Racute Otilde -20
+KPX Racute T -30
+KPX Racute Tcaron -30
+KPX Racute Tcommaaccent -30
+KPX Racute U -40
+KPX Racute Uacute -40
+KPX Racute Ucircumflex -40
+KPX Racute Udieresis -40
+KPX Racute Ugrave -40
+KPX Racute Uhungarumlaut -40
+KPX Racute Umacron -40
+KPX Racute Uogonek -40
+KPX Racute Uring -40
+KPX Racute V -50
+KPX Racute W -30
+KPX Racute Y -50
+KPX Racute Yacute -50
+KPX Racute Ydieresis -50
+KPX Rcaron O -20
+KPX Rcaron Oacute -20
+KPX Rcaron Ocircumflex -20
+KPX Rcaron Odieresis -20
+KPX Rcaron Ograve -20
+KPX Rcaron Ohungarumlaut -20
+KPX Rcaron Omacron -20
+KPX Rcaron Oslash -20
+KPX Rcaron Otilde -20
+KPX Rcaron T -30
+KPX Rcaron Tcaron -30
+KPX Rcaron Tcommaaccent -30
+KPX Rcaron U -40
+KPX Rcaron Uacute -40
+KPX Rcaron Ucircumflex -40
+KPX Rcaron Udieresis -40
+KPX Rcaron Ugrave -40
+KPX Rcaron Uhungarumlaut -40
+KPX Rcaron Umacron -40
+KPX Rcaron Uogonek -40
+KPX Rcaron Uring -40
+KPX Rcaron V -50
+KPX Rcaron W -30
+KPX Rcaron Y -50
+KPX Rcaron Yacute -50
+KPX Rcaron Ydieresis -50
+KPX Rcommaaccent O -20
+KPX Rcommaaccent Oacute -20
+KPX Rcommaaccent Ocircumflex -20
+KPX Rcommaaccent Odieresis -20
+KPX Rcommaaccent Ograve -20
+KPX Rcommaaccent Ohungarumlaut -20
+KPX Rcommaaccent Omacron -20
+KPX Rcommaaccent Oslash -20
+KPX Rcommaaccent Otilde -20
+KPX Rcommaaccent T -30
+KPX Rcommaaccent Tcaron -30
+KPX Rcommaaccent Tcommaaccent -30
+KPX Rcommaaccent U -40
+KPX Rcommaaccent Uacute -40
+KPX Rcommaaccent Ucircumflex -40
+KPX Rcommaaccent Udieresis -40
+KPX Rcommaaccent Ugrave -40
+KPX Rcommaaccent Uhungarumlaut -40
+KPX Rcommaaccent Umacron -40
+KPX Rcommaaccent Uogonek -40
+KPX Rcommaaccent Uring -40
+KPX Rcommaaccent V -50
+KPX Rcommaaccent W -30
+KPX Rcommaaccent Y -50
+KPX Rcommaaccent Yacute -50
+KPX Rcommaaccent Ydieresis -50
+KPX S comma -20
+KPX S period -20
+KPX Sacute comma -20
+KPX Sacute period -20
+KPX Scaron comma -20
+KPX Scaron period -20
+KPX Scedilla comma -20
+KPX Scedilla period -20
+KPX Scommaaccent comma -20
+KPX Scommaaccent period -20
+KPX T A -120
+KPX T Aacute -120
+KPX T Abreve -120
+KPX T Acircumflex -120
+KPX T Adieresis -120
+KPX T Agrave -120
+KPX T Amacron -120
+KPX T Aogonek -120
+KPX T Aring -120
+KPX T Atilde -120
+KPX T O -40
+KPX T Oacute -40
+KPX T Ocircumflex -40
+KPX T Odieresis -40
+KPX T Ograve -40
+KPX T Ohungarumlaut -40
+KPX T Omacron -40
+KPX T Oslash -40
+KPX T Otilde -40
+KPX T a -120
+KPX T aacute -120
+KPX T abreve -60
+KPX T acircumflex -120
+KPX T adieresis -120
+KPX T agrave -120
+KPX T amacron -60
+KPX T aogonek -120
+KPX T aring -120
+KPX T atilde -60
+KPX T colon -20
+KPX T comma -120
+KPX T e -120
+KPX T eacute -120
+KPX T ecaron -120
+KPX T ecircumflex -120
+KPX T edieresis -120
+KPX T edotaccent -120
+KPX T egrave -60
+KPX T emacron -60
+KPX T eogonek -120
+KPX T hyphen -140
+KPX T o -120
+KPX T oacute -120
+KPX T ocircumflex -120
+KPX T odieresis -120
+KPX T ograve -120
+KPX T ohungarumlaut -120
+KPX T omacron -60
+KPX T oslash -120
+KPX T otilde -60
+KPX T period -120
+KPX T r -120
+KPX T racute -120
+KPX T rcaron -120
+KPX T rcommaaccent -120
+KPX T semicolon -20
+KPX T u -120
+KPX T uacute -120
+KPX T ucircumflex -120
+KPX T udieresis -120
+KPX T ugrave -120
+KPX T uhungarumlaut -120
+KPX T umacron -60
+KPX T uogonek -120
+KPX T uring -120
+KPX T w -120
+KPX T y -120
+KPX T yacute -120
+KPX T ydieresis -60
+KPX Tcaron A -120
+KPX Tcaron Aacute -120
+KPX Tcaron Abreve -120
+KPX Tcaron Acircumflex -120
+KPX Tcaron Adieresis -120
+KPX Tcaron Agrave -120
+KPX Tcaron Amacron -120
+KPX Tcaron Aogonek -120
+KPX Tcaron Aring -120
+KPX Tcaron Atilde -120
+KPX Tcaron O -40
+KPX Tcaron Oacute -40
+KPX Tcaron Ocircumflex -40
+KPX Tcaron Odieresis -40
+KPX Tcaron Ograve -40
+KPX Tcaron Ohungarumlaut -40
+KPX Tcaron Omacron -40
+KPX Tcaron Oslash -40
+KPX Tcaron Otilde -40
+KPX Tcaron a -120
+KPX Tcaron aacute -120
+KPX Tcaron abreve -60
+KPX Tcaron acircumflex -120
+KPX Tcaron adieresis -120
+KPX Tcaron agrave -120
+KPX Tcaron amacron -60
+KPX Tcaron aogonek -120
+KPX Tcaron aring -120
+KPX Tcaron atilde -60
+KPX Tcaron colon -20
+KPX Tcaron comma -120
+KPX Tcaron e -120
+KPX Tcaron eacute -120
+KPX Tcaron ecaron -120
+KPX Tcaron ecircumflex -120
+KPX Tcaron edieresis -120
+KPX Tcaron edotaccent -120
+KPX Tcaron egrave -60
+KPX Tcaron emacron -60
+KPX Tcaron eogonek -120
+KPX Tcaron hyphen -140
+KPX Tcaron o -120
+KPX Tcaron oacute -120
+KPX Tcaron ocircumflex -120
+KPX Tcaron odieresis -120
+KPX Tcaron ograve -120
+KPX Tcaron ohungarumlaut -120
+KPX Tcaron omacron -60
+KPX Tcaron oslash -120
+KPX Tcaron otilde -60
+KPX Tcaron period -120
+KPX Tcaron r -120
+KPX Tcaron racute -120
+KPX Tcaron rcaron -120
+KPX Tcaron rcommaaccent -120
+KPX Tcaron semicolon -20
+KPX Tcaron u -120
+KPX Tcaron uacute -120
+KPX Tcaron ucircumflex -120
+KPX Tcaron udieresis -120
+KPX Tcaron ugrave -120
+KPX Tcaron uhungarumlaut -120
+KPX Tcaron umacron -60
+KPX Tcaron uogonek -120
+KPX Tcaron uring -120
+KPX Tcaron w -120
+KPX Tcaron y -120
+KPX Tcaron yacute -120
+KPX Tcaron ydieresis -60
+KPX Tcommaaccent A -120
+KPX Tcommaaccent Aacute -120
+KPX Tcommaaccent Abreve -120
+KPX Tcommaaccent Acircumflex -120
+KPX Tcommaaccent Adieresis -120
+KPX Tcommaaccent Agrave -120
+KPX Tcommaaccent Amacron -120
+KPX Tcommaaccent Aogonek -120
+KPX Tcommaaccent Aring -120
+KPX Tcommaaccent Atilde -120
+KPX Tcommaaccent O -40
+KPX Tcommaaccent Oacute -40
+KPX Tcommaaccent Ocircumflex -40
+KPX Tcommaaccent Odieresis -40
+KPX Tcommaaccent Ograve -40
+KPX Tcommaaccent Ohungarumlaut -40
+KPX Tcommaaccent Omacron -40
+KPX Tcommaaccent Oslash -40
+KPX Tcommaaccent Otilde -40
+KPX Tcommaaccent a -120
+KPX Tcommaaccent aacute -120
+KPX Tcommaaccent abreve -60
+KPX Tcommaaccent acircumflex -120
+KPX Tcommaaccent adieresis -120
+KPX Tcommaaccent agrave -120
+KPX Tcommaaccent amacron -60
+KPX Tcommaaccent aogonek -120
+KPX Tcommaaccent aring -120
+KPX Tcommaaccent atilde -60
+KPX Tcommaaccent colon -20
+KPX Tcommaaccent comma -120
+KPX Tcommaaccent e -120
+KPX Tcommaaccent eacute -120
+KPX Tcommaaccent ecaron -120
+KPX Tcommaaccent ecircumflex -120
+KPX Tcommaaccent edieresis -120
+KPX Tcommaaccent edotaccent -120
+KPX Tcommaaccent egrave -60
+KPX Tcommaaccent emacron -60
+KPX Tcommaaccent eogonek -120
+KPX Tcommaaccent hyphen -140
+KPX Tcommaaccent o -120
+KPX Tcommaaccent oacute -120
+KPX Tcommaaccent ocircumflex -120
+KPX Tcommaaccent odieresis -120
+KPX Tcommaaccent ograve -120
+KPX Tcommaaccent ohungarumlaut -120
+KPX Tcommaaccent omacron -60
+KPX Tcommaaccent oslash -120
+KPX Tcommaaccent otilde -60
+KPX Tcommaaccent period -120
+KPX Tcommaaccent r -120
+KPX Tcommaaccent racute -120
+KPX Tcommaaccent rcaron -120
+KPX Tcommaaccent rcommaaccent -120
+KPX Tcommaaccent semicolon -20
+KPX Tcommaaccent u -120
+KPX Tcommaaccent uacute -120
+KPX Tcommaaccent ucircumflex -120
+KPX Tcommaaccent udieresis -120
+KPX Tcommaaccent ugrave -120
+KPX Tcommaaccent uhungarumlaut -120
+KPX Tcommaaccent umacron -60
+KPX Tcommaaccent uogonek -120
+KPX Tcommaaccent uring -120
+KPX Tcommaaccent w -120
+KPX Tcommaaccent y -120
+KPX Tcommaaccent yacute -120
+KPX Tcommaaccent ydieresis -60
+KPX U A -40
+KPX U Aacute -40
+KPX U Abreve -40
+KPX U Acircumflex -40
+KPX U Adieresis -40
+KPX U Agrave -40
+KPX U Amacron -40
+KPX U Aogonek -40
+KPX U Aring -40
+KPX U Atilde -40
+KPX U comma -40
+KPX U period -40
+KPX Uacute A -40
+KPX Uacute Aacute -40
+KPX Uacute Abreve -40
+KPX Uacute Acircumflex -40
+KPX Uacute Adieresis -40
+KPX Uacute Agrave -40
+KPX Uacute Amacron -40
+KPX Uacute Aogonek -40
+KPX Uacute Aring -40
+KPX Uacute Atilde -40
+KPX Uacute comma -40
+KPX Uacute period -40
+KPX Ucircumflex A -40
+KPX Ucircumflex Aacute -40
+KPX Ucircumflex Abreve -40
+KPX Ucircumflex Acircumflex -40
+KPX Ucircumflex Adieresis -40
+KPX Ucircumflex Agrave -40
+KPX Ucircumflex Amacron -40
+KPX Ucircumflex Aogonek -40
+KPX Ucircumflex Aring -40
+KPX Ucircumflex Atilde -40
+KPX Ucircumflex comma -40
+KPX Ucircumflex period -40
+KPX Udieresis A -40
+KPX Udieresis Aacute -40
+KPX Udieresis Abreve -40
+KPX Udieresis Acircumflex -40
+KPX Udieresis Adieresis -40
+KPX Udieresis Agrave -40
+KPX Udieresis Amacron -40
+KPX Udieresis Aogonek -40
+KPX Udieresis Aring -40
+KPX Udieresis Atilde -40
+KPX Udieresis comma -40
+KPX Udieresis period -40
+KPX Ugrave A -40
+KPX Ugrave Aacute -40
+KPX Ugrave Abreve -40
+KPX Ugrave Acircumflex -40
+KPX Ugrave Adieresis -40
+KPX Ugrave Agrave -40
+KPX Ugrave Amacron -40
+KPX Ugrave Aogonek -40
+KPX Ugrave Aring -40
+KPX Ugrave Atilde -40
+KPX Ugrave comma -40
+KPX Ugrave period -40
+KPX Uhungarumlaut A -40
+KPX Uhungarumlaut Aacute -40
+KPX Uhungarumlaut Abreve -40
+KPX Uhungarumlaut Acircumflex -40
+KPX Uhungarumlaut Adieresis -40
+KPX Uhungarumlaut Agrave -40
+KPX Uhungarumlaut Amacron -40
+KPX Uhungarumlaut Aogonek -40
+KPX Uhungarumlaut Aring -40
+KPX Uhungarumlaut Atilde -40
+KPX Uhungarumlaut comma -40
+KPX Uhungarumlaut period -40
+KPX Umacron A -40
+KPX Umacron Aacute -40
+KPX Umacron Abreve -40
+KPX Umacron Acircumflex -40
+KPX Umacron Adieresis -40
+KPX Umacron Agrave -40
+KPX Umacron Amacron -40
+KPX Umacron Aogonek -40
+KPX Umacron Aring -40
+KPX Umacron Atilde -40
+KPX Umacron comma -40
+KPX Umacron period -40
+KPX Uogonek A -40
+KPX Uogonek Aacute -40
+KPX Uogonek Abreve -40
+KPX Uogonek Acircumflex -40
+KPX Uogonek Adieresis -40
+KPX Uogonek Agrave -40
+KPX Uogonek Amacron -40
+KPX Uogonek Aogonek -40
+KPX Uogonek Aring -40
+KPX Uogonek Atilde -40
+KPX Uogonek comma -40
+KPX Uogonek period -40
+KPX Uring A -40
+KPX Uring Aacute -40
+KPX Uring Abreve -40
+KPX Uring Acircumflex -40
+KPX Uring Adieresis -40
+KPX Uring Agrave -40
+KPX Uring Amacron -40
+KPX Uring Aogonek -40
+KPX Uring Aring -40
+KPX Uring Atilde -40
+KPX Uring comma -40
+KPX Uring period -40
+KPX V A -80
+KPX V Aacute -80
+KPX V Abreve -80
+KPX V Acircumflex -80
+KPX V Adieresis -80
+KPX V Agrave -80
+KPX V Amacron -80
+KPX V Aogonek -80
+KPX V Aring -80
+KPX V Atilde -80
+KPX V G -40
+KPX V Gbreve -40
+KPX V Gcommaaccent -40
+KPX V O -40
+KPX V Oacute -40
+KPX V Ocircumflex -40
+KPX V Odieresis -40
+KPX V Ograve -40
+KPX V Ohungarumlaut -40
+KPX V Omacron -40
+KPX V Oslash -40
+KPX V Otilde -40
+KPX V a -70
+KPX V aacute -70
+KPX V abreve -70
+KPX V acircumflex -70
+KPX V adieresis -70
+KPX V agrave -70
+KPX V amacron -70
+KPX V aogonek -70
+KPX V aring -70
+KPX V atilde -70
+KPX V colon -40
+KPX V comma -125
+KPX V e -80
+KPX V eacute -80
+KPX V ecaron -80
+KPX V ecircumflex -80
+KPX V edieresis -80
+KPX V edotaccent -80
+KPX V egrave -80
+KPX V emacron -80
+KPX V eogonek -80
+KPX V hyphen -80
+KPX V o -80
+KPX V oacute -80
+KPX V ocircumflex -80
+KPX V odieresis -80
+KPX V ograve -80
+KPX V ohungarumlaut -80
+KPX V omacron -80
+KPX V oslash -80
+KPX V otilde -80
+KPX V period -125
+KPX V semicolon -40
+KPX V u -70
+KPX V uacute -70
+KPX V ucircumflex -70
+KPX V udieresis -70
+KPX V ugrave -70
+KPX V uhungarumlaut -70
+KPX V umacron -70
+KPX V uogonek -70
+KPX V uring -70
+KPX W A -50
+KPX W Aacute -50
+KPX W Abreve -50
+KPX W Acircumflex -50
+KPX W Adieresis -50
+KPX W Agrave -50
+KPX W Amacron -50
+KPX W Aogonek -50
+KPX W Aring -50
+KPX W Atilde -50
+KPX W O -20
+KPX W Oacute -20
+KPX W Ocircumflex -20
+KPX W Odieresis -20
+KPX W Ograve -20
+KPX W Ohungarumlaut -20
+KPX W Omacron -20
+KPX W Oslash -20
+KPX W Otilde -20
+KPX W a -40
+KPX W aacute -40
+KPX W abreve -40
+KPX W acircumflex -40
+KPX W adieresis -40
+KPX W agrave -40
+KPX W amacron -40
+KPX W aogonek -40
+KPX W aring -40
+KPX W atilde -40
+KPX W comma -80
+KPX W e -30
+KPX W eacute -30
+KPX W ecaron -30
+KPX W ecircumflex -30
+KPX W edieresis -30
+KPX W edotaccent -30
+KPX W egrave -30
+KPX W emacron -30
+KPX W eogonek -30
+KPX W hyphen -40
+KPX W o -30
+KPX W oacute -30
+KPX W ocircumflex -30
+KPX W odieresis -30
+KPX W ograve -30
+KPX W ohungarumlaut -30
+KPX W omacron -30
+KPX W oslash -30
+KPX W otilde -30
+KPX W period -80
+KPX W u -30
+KPX W uacute -30
+KPX W ucircumflex -30
+KPX W udieresis -30
+KPX W ugrave -30
+KPX W uhungarumlaut -30
+KPX W umacron -30
+KPX W uogonek -30
+KPX W uring -30
+KPX W y -20
+KPX W yacute -20
+KPX W ydieresis -20
+KPX Y A -110
+KPX Y Aacute -110
+KPX Y Abreve -110
+KPX Y Acircumflex -110
+KPX Y Adieresis -110
+KPX Y Agrave -110
+KPX Y Amacron -110
+KPX Y Aogonek -110
+KPX Y Aring -110
+KPX Y Atilde -110
+KPX Y O -85
+KPX Y Oacute -85
+KPX Y Ocircumflex -85
+KPX Y Odieresis -85
+KPX Y Ograve -85
+KPX Y Ohungarumlaut -85
+KPX Y Omacron -85
+KPX Y Oslash -85
+KPX Y Otilde -85
+KPX Y a -140
+KPX Y aacute -140
+KPX Y abreve -70
+KPX Y acircumflex -140
+KPX Y adieresis -140
+KPX Y agrave -140
+KPX Y amacron -70
+KPX Y aogonek -140
+KPX Y aring -140
+KPX Y atilde -140
+KPX Y colon -60
+KPX Y comma -140
+KPX Y e -140
+KPX Y eacute -140
+KPX Y ecaron -140
+KPX Y ecircumflex -140
+KPX Y edieresis -140
+KPX Y edotaccent -140
+KPX Y egrave -140
+KPX Y emacron -70
+KPX Y eogonek -140
+KPX Y hyphen -140
+KPX Y i -20
+KPX Y iacute -20
+KPX Y iogonek -20
+KPX Y o -140
+KPX Y oacute -140
+KPX Y ocircumflex -140
+KPX Y odieresis -140
+KPX Y ograve -140
+KPX Y ohungarumlaut -140
+KPX Y omacron -140
+KPX Y oslash -140
+KPX Y otilde -140
+KPX Y period -140
+KPX Y semicolon -60
+KPX Y u -110
+KPX Y uacute -110
+KPX Y ucircumflex -110
+KPX Y udieresis -110
+KPX Y ugrave -110
+KPX Y uhungarumlaut -110
+KPX Y umacron -110
+KPX Y uogonek -110
+KPX Y uring -110
+KPX Yacute A -110
+KPX Yacute Aacute -110
+KPX Yacute Abreve -110
+KPX Yacute Acircumflex -110
+KPX Yacute Adieresis -110
+KPX Yacute Agrave -110
+KPX Yacute Amacron -110
+KPX Yacute Aogonek -110
+KPX Yacute Aring -110
+KPX Yacute Atilde -110
+KPX Yacute O -85
+KPX Yacute Oacute -85
+KPX Yacute Ocircumflex -85
+KPX Yacute Odieresis -85
+KPX Yacute Ograve -85
+KPX Yacute Ohungarumlaut -85
+KPX Yacute Omacron -85
+KPX Yacute Oslash -85
+KPX Yacute Otilde -85
+KPX Yacute a -140
+KPX Yacute aacute -140
+KPX Yacute abreve -70
+KPX Yacute acircumflex -140
+KPX Yacute adieresis -140
+KPX Yacute agrave -140
+KPX Yacute amacron -70
+KPX Yacute aogonek -140
+KPX Yacute aring -140
+KPX Yacute atilde -70
+KPX Yacute colon -60
+KPX Yacute comma -140
+KPX Yacute e -140
+KPX Yacute eacute -140
+KPX Yacute ecaron -140
+KPX Yacute ecircumflex -140
+KPX Yacute edieresis -140
+KPX Yacute edotaccent -140
+KPX Yacute egrave -140
+KPX Yacute emacron -70
+KPX Yacute eogonek -140
+KPX Yacute hyphen -140
+KPX Yacute i -20
+KPX Yacute iacute -20
+KPX Yacute iogonek -20
+KPX Yacute o -140
+KPX Yacute oacute -140
+KPX Yacute ocircumflex -140
+KPX Yacute odieresis -140
+KPX Yacute ograve -140
+KPX Yacute ohungarumlaut -140
+KPX Yacute omacron -70
+KPX Yacute oslash -140
+KPX Yacute otilde -140
+KPX Yacute period -140
+KPX Yacute semicolon -60
+KPX Yacute u -110
+KPX Yacute uacute -110
+KPX Yacute ucircumflex -110
+KPX Yacute udieresis -110
+KPX Yacute ugrave -110
+KPX Yacute uhungarumlaut -110
+KPX Yacute umacron -110
+KPX Yacute uogonek -110
+KPX Yacute uring -110
+KPX Ydieresis A -110
+KPX Ydieresis Aacute -110
+KPX Ydieresis Abreve -110
+KPX Ydieresis Acircumflex -110
+KPX Ydieresis Adieresis -110
+KPX Ydieresis Agrave -110
+KPX Ydieresis Amacron -110
+KPX Ydieresis Aogonek -110
+KPX Ydieresis Aring -110
+KPX Ydieresis Atilde -110
+KPX Ydieresis O -85
+KPX Ydieresis Oacute -85
+KPX Ydieresis Ocircumflex -85
+KPX Ydieresis Odieresis -85
+KPX Ydieresis Ograve -85
+KPX Ydieresis Ohungarumlaut -85
+KPX Ydieresis Omacron -85
+KPX Ydieresis Oslash -85
+KPX Ydieresis Otilde -85
+KPX Ydieresis a -140
+KPX Ydieresis aacute -140
+KPX Ydieresis abreve -70
+KPX Ydieresis acircumflex -140
+KPX Ydieresis adieresis -140
+KPX Ydieresis agrave -140
+KPX Ydieresis amacron -70
+KPX Ydieresis aogonek -140
+KPX Ydieresis aring -140
+KPX Ydieresis atilde -70
+KPX Ydieresis colon -60
+KPX Ydieresis comma -140
+KPX Ydieresis e -140
+KPX Ydieresis eacute -140
+KPX Ydieresis ecaron -140
+KPX Ydieresis ecircumflex -140
+KPX Ydieresis edieresis -140
+KPX Ydieresis edotaccent -140
+KPX Ydieresis egrave -140
+KPX Ydieresis emacron -70
+KPX Ydieresis eogonek -140
+KPX Ydieresis hyphen -140
+KPX Ydieresis i -20
+KPX Ydieresis iacute -20
+KPX Ydieresis iogonek -20
+KPX Ydieresis o -140
+KPX Ydieresis oacute -140
+KPX Ydieresis ocircumflex -140
+KPX Ydieresis odieresis -140
+KPX Ydieresis ograve -140
+KPX Ydieresis ohungarumlaut -140
+KPX Ydieresis omacron -140
+KPX Ydieresis oslash -140
+KPX Ydieresis otilde -140
+KPX Ydieresis period -140
+KPX Ydieresis semicolon -60
+KPX Ydieresis u -110
+KPX Ydieresis uacute -110
+KPX Ydieresis ucircumflex -110
+KPX Ydieresis udieresis -110
+KPX Ydieresis ugrave -110
+KPX Ydieresis uhungarumlaut -110
+KPX Ydieresis umacron -110
+KPX Ydieresis uogonek -110
+KPX Ydieresis uring -110
+KPX a v -20
+KPX a w -20
+KPX a y -30
+KPX a yacute -30
+KPX a ydieresis -30
+KPX aacute v -20
+KPX aacute w -20
+KPX aacute y -30
+KPX aacute yacute -30
+KPX aacute ydieresis -30
+KPX abreve v -20
+KPX abreve w -20
+KPX abreve y -30
+KPX abreve yacute -30
+KPX abreve ydieresis -30
+KPX acircumflex v -20
+KPX acircumflex w -20
+KPX acircumflex y -30
+KPX acircumflex yacute -30
+KPX acircumflex ydieresis -30
+KPX adieresis v -20
+KPX adieresis w -20
+KPX adieresis y -30
+KPX adieresis yacute -30
+KPX adieresis ydieresis -30
+KPX agrave v -20
+KPX agrave w -20
+KPX agrave y -30
+KPX agrave yacute -30
+KPX agrave ydieresis -30
+KPX amacron v -20
+KPX amacron w -20
+KPX amacron y -30
+KPX amacron yacute -30
+KPX amacron ydieresis -30
+KPX aogonek v -20
+KPX aogonek w -20
+KPX aogonek y -30
+KPX aogonek yacute -30
+KPX aogonek ydieresis -30
+KPX aring v -20
+KPX aring w -20
+KPX aring y -30
+KPX aring yacute -30
+KPX aring ydieresis -30
+KPX atilde v -20
+KPX atilde w -20
+KPX atilde y -30
+KPX atilde yacute -30
+KPX atilde ydieresis -30
+KPX b b -10
+KPX b comma -40
+KPX b l -20
+KPX b lacute -20
+KPX b lcommaaccent -20
+KPX b lslash -20
+KPX b period -40
+KPX b u -20
+KPX b uacute -20
+KPX b ucircumflex -20
+KPX b udieresis -20
+KPX b ugrave -20
+KPX b uhungarumlaut -20
+KPX b umacron -20
+KPX b uogonek -20
+KPX b uring -20
+KPX b v -20
+KPX b y -20
+KPX b yacute -20
+KPX b ydieresis -20
+KPX c comma -15
+KPX c k -20
+KPX c kcommaaccent -20
+KPX cacute comma -15
+KPX cacute k -20
+KPX cacute kcommaaccent -20
+KPX ccaron comma -15
+KPX ccaron k -20
+KPX ccaron kcommaaccent -20
+KPX ccedilla comma -15
+KPX ccedilla k -20
+KPX ccedilla kcommaaccent -20
+KPX colon space -50
+KPX comma quotedblright -100
+KPX comma quoteright -100
+KPX e comma -15
+KPX e period -15
+KPX e v -30
+KPX e w -20
+KPX e x -30
+KPX e y -20
+KPX e yacute -20
+KPX e ydieresis -20
+KPX eacute comma -15
+KPX eacute period -15
+KPX eacute v -30
+KPX eacute w -20
+KPX eacute x -30
+KPX eacute y -20
+KPX eacute yacute -20
+KPX eacute ydieresis -20
+KPX ecaron comma -15
+KPX ecaron period -15
+KPX ecaron v -30
+KPX ecaron w -20
+KPX ecaron x -30
+KPX ecaron y -20
+KPX ecaron yacute -20
+KPX ecaron ydieresis -20
+KPX ecircumflex comma -15
+KPX ecircumflex period -15
+KPX ecircumflex v -30
+KPX ecircumflex w -20
+KPX ecircumflex x -30
+KPX ecircumflex y -20
+KPX ecircumflex yacute -20
+KPX ecircumflex ydieresis -20
+KPX edieresis comma -15
+KPX edieresis period -15
+KPX edieresis v -30
+KPX edieresis w -20
+KPX edieresis x -30
+KPX edieresis y -20
+KPX edieresis yacute -20
+KPX edieresis ydieresis -20
+KPX edotaccent comma -15
+KPX edotaccent period -15
+KPX edotaccent v -30
+KPX edotaccent w -20
+KPX edotaccent x -30
+KPX edotaccent y -20
+KPX edotaccent yacute -20
+KPX edotaccent ydieresis -20
+KPX egrave comma -15
+KPX egrave period -15
+KPX egrave v -30
+KPX egrave w -20
+KPX egrave x -30
+KPX egrave y -20
+KPX egrave yacute -20
+KPX egrave ydieresis -20
+KPX emacron comma -15
+KPX emacron period -15
+KPX emacron v -30
+KPX emacron w -20
+KPX emacron x -30
+KPX emacron y -20
+KPX emacron yacute -20
+KPX emacron ydieresis -20
+KPX eogonek comma -15
+KPX eogonek period -15
+KPX eogonek v -30
+KPX eogonek w -20
+KPX eogonek x -30
+KPX eogonek y -20
+KPX eogonek yacute -20
+KPX eogonek ydieresis -20
+KPX f a -30
+KPX f aacute -30
+KPX f abreve -30
+KPX f acircumflex -30
+KPX f adieresis -30
+KPX f agrave -30
+KPX f amacron -30
+KPX f aogonek -30
+KPX f aring -30
+KPX f atilde -30
+KPX f comma -30
+KPX f dotlessi -28
+KPX f e -30
+KPX f eacute -30
+KPX f ecaron -30
+KPX f ecircumflex -30
+KPX f edieresis -30
+KPX f edotaccent -30
+KPX f egrave -30
+KPX f emacron -30
+KPX f eogonek -30
+KPX f o -30
+KPX f oacute -30
+KPX f ocircumflex -30
+KPX f odieresis -30
+KPX f ograve -30
+KPX f ohungarumlaut -30
+KPX f omacron -30
+KPX f oslash -30
+KPX f otilde -30
+KPX f period -30
+KPX f quotedblright 60
+KPX f quoteright 50
+KPX g r -10
+KPX g racute -10
+KPX g rcaron -10
+KPX g rcommaaccent -10
+KPX gbreve r -10
+KPX gbreve racute -10
+KPX gbreve rcaron -10
+KPX gbreve rcommaaccent -10
+KPX gcommaaccent r -10
+KPX gcommaaccent racute -10
+KPX gcommaaccent rcaron -10
+KPX gcommaaccent rcommaaccent -10
+KPX h y -30
+KPX h yacute -30
+KPX h ydieresis -30
+KPX k e -20
+KPX k eacute -20
+KPX k ecaron -20
+KPX k ecircumflex -20
+KPX k edieresis -20
+KPX k edotaccent -20
+KPX k egrave -20
+KPX k emacron -20
+KPX k eogonek -20
+KPX k o -20
+KPX k oacute -20
+KPX k ocircumflex -20
+KPX k odieresis -20
+KPX k ograve -20
+KPX k ohungarumlaut -20
+KPX k omacron -20
+KPX k oslash -20
+KPX k otilde -20
+KPX kcommaaccent e -20
+KPX kcommaaccent eacute -20
+KPX kcommaaccent ecaron -20
+KPX kcommaaccent ecircumflex -20
+KPX kcommaaccent edieresis -20
+KPX kcommaaccent edotaccent -20
+KPX kcommaaccent egrave -20
+KPX kcommaaccent emacron -20
+KPX kcommaaccent eogonek -20
+KPX kcommaaccent o -20
+KPX kcommaaccent oacute -20
+KPX kcommaaccent ocircumflex -20
+KPX kcommaaccent odieresis -20
+KPX kcommaaccent ograve -20
+KPX kcommaaccent ohungarumlaut -20
+KPX kcommaaccent omacron -20
+KPX kcommaaccent oslash -20
+KPX kcommaaccent otilde -20
+KPX m u -10
+KPX m uacute -10
+KPX m ucircumflex -10
+KPX m udieresis -10
+KPX m ugrave -10
+KPX m uhungarumlaut -10
+KPX m umacron -10
+KPX m uogonek -10
+KPX m uring -10
+KPX m y -15
+KPX m yacute -15
+KPX m ydieresis -15
+KPX n u -10
+KPX n uacute -10
+KPX n ucircumflex -10
+KPX n udieresis -10
+KPX n ugrave -10
+KPX n uhungarumlaut -10
+KPX n umacron -10
+KPX n uogonek -10
+KPX n uring -10
+KPX n v -20
+KPX n y -15
+KPX n yacute -15
+KPX n ydieresis -15
+KPX nacute u -10
+KPX nacute uacute -10
+KPX nacute ucircumflex -10
+KPX nacute udieresis -10
+KPX nacute ugrave -10
+KPX nacute uhungarumlaut -10
+KPX nacute umacron -10
+KPX nacute uogonek -10
+KPX nacute uring -10
+KPX nacute v -20
+KPX nacute y -15
+KPX nacute yacute -15
+KPX nacute ydieresis -15
+KPX ncaron u -10
+KPX ncaron uacute -10
+KPX ncaron ucircumflex -10
+KPX ncaron udieresis -10
+KPX ncaron ugrave -10
+KPX ncaron uhungarumlaut -10
+KPX ncaron umacron -10
+KPX ncaron uogonek -10
+KPX ncaron uring -10
+KPX ncaron v -20
+KPX ncaron y -15
+KPX ncaron yacute -15
+KPX ncaron ydieresis -15
+KPX ncommaaccent u -10
+KPX ncommaaccent uacute -10
+KPX ncommaaccent ucircumflex -10
+KPX ncommaaccent udieresis -10
+KPX ncommaaccent ugrave -10
+KPX ncommaaccent uhungarumlaut -10
+KPX ncommaaccent umacron -10
+KPX ncommaaccent uogonek -10
+KPX ncommaaccent uring -10
+KPX ncommaaccent v -20
+KPX ncommaaccent y -15
+KPX ncommaaccent yacute -15
+KPX ncommaaccent ydieresis -15
+KPX ntilde u -10
+KPX ntilde uacute -10
+KPX ntilde ucircumflex -10
+KPX ntilde udieresis -10
+KPX ntilde ugrave -10
+KPX ntilde uhungarumlaut -10
+KPX ntilde umacron -10
+KPX ntilde uogonek -10
+KPX ntilde uring -10
+KPX ntilde v -20
+KPX ntilde y -15
+KPX ntilde yacute -15
+KPX ntilde ydieresis -15
+KPX o comma -40
+KPX o period -40
+KPX o v -15
+KPX o w -15
+KPX o x -30
+KPX o y -30
+KPX o yacute -30
+KPX o ydieresis -30
+KPX oacute comma -40
+KPX oacute period -40
+KPX oacute v -15
+KPX oacute w -15
+KPX oacute x -30
+KPX oacute y -30
+KPX oacute yacute -30
+KPX oacute ydieresis -30
+KPX ocircumflex comma -40
+KPX ocircumflex period -40
+KPX ocircumflex v -15
+KPX ocircumflex w -15
+KPX ocircumflex x -30
+KPX ocircumflex y -30
+KPX ocircumflex yacute -30
+KPX ocircumflex ydieresis -30
+KPX odieresis comma -40
+KPX odieresis period -40
+KPX odieresis v -15
+KPX odieresis w -15
+KPX odieresis x -30
+KPX odieresis y -30
+KPX odieresis yacute -30
+KPX odieresis ydieresis -30
+KPX ograve comma -40
+KPX ograve period -40
+KPX ograve v -15
+KPX ograve w -15
+KPX ograve x -30
+KPX ograve y -30
+KPX ograve yacute -30
+KPX ograve ydieresis -30
+KPX ohungarumlaut comma -40
+KPX ohungarumlaut period -40
+KPX ohungarumlaut v -15
+KPX ohungarumlaut w -15
+KPX ohungarumlaut x -30
+KPX ohungarumlaut y -30
+KPX ohungarumlaut yacute -30
+KPX ohungarumlaut ydieresis -30
+KPX omacron comma -40
+KPX omacron period -40
+KPX omacron v -15
+KPX omacron w -15
+KPX omacron x -30
+KPX omacron y -30
+KPX omacron yacute -30
+KPX omacron ydieresis -30
+KPX oslash a -55
+KPX oslash aacute -55
+KPX oslash abreve -55
+KPX oslash acircumflex -55
+KPX oslash adieresis -55
+KPX oslash agrave -55
+KPX oslash amacron -55
+KPX oslash aogonek -55
+KPX oslash aring -55
+KPX oslash atilde -55
+KPX oslash b -55
+KPX oslash c -55
+KPX oslash cacute -55
+KPX oslash ccaron -55
+KPX oslash ccedilla -55
+KPX oslash comma -95
+KPX oslash d -55
+KPX oslash dcroat -55
+KPX oslash e -55
+KPX oslash eacute -55
+KPX oslash ecaron -55
+KPX oslash ecircumflex -55
+KPX oslash edieresis -55
+KPX oslash edotaccent -55
+KPX oslash egrave -55
+KPX oslash emacron -55
+KPX oslash eogonek -55
+KPX oslash f -55
+KPX oslash g -55
+KPX oslash gbreve -55
+KPX oslash gcommaaccent -55
+KPX oslash h -55
+KPX oslash i -55
+KPX oslash iacute -55
+KPX oslash icircumflex -55
+KPX oslash idieresis -55
+KPX oslash igrave -55
+KPX oslash imacron -55
+KPX oslash iogonek -55
+KPX oslash j -55
+KPX oslash k -55
+KPX oslash kcommaaccent -55
+KPX oslash l -55
+KPX oslash lacute -55
+KPX oslash lcommaaccent -55
+KPX oslash lslash -55
+KPX oslash m -55
+KPX oslash n -55
+KPX oslash nacute -55
+KPX oslash ncaron -55
+KPX oslash ncommaaccent -55
+KPX oslash ntilde -55
+KPX oslash o -55
+KPX oslash oacute -55
+KPX oslash ocircumflex -55
+KPX oslash odieresis -55
+KPX oslash ograve -55
+KPX oslash ohungarumlaut -55
+KPX oslash omacron -55
+KPX oslash oslash -55
+KPX oslash otilde -55
+KPX oslash p -55
+KPX oslash period -95
+KPX oslash q -55
+KPX oslash r -55
+KPX oslash racute -55
+KPX oslash rcaron -55
+KPX oslash rcommaaccent -55
+KPX oslash s -55
+KPX oslash sacute -55
+KPX oslash scaron -55
+KPX oslash scedilla -55
+KPX oslash scommaaccent -55
+KPX oslash t -55
+KPX oslash tcommaaccent -55
+KPX oslash u -55
+KPX oslash uacute -55
+KPX oslash ucircumflex -55
+KPX oslash udieresis -55
+KPX oslash ugrave -55
+KPX oslash uhungarumlaut -55
+KPX oslash umacron -55
+KPX oslash uogonek -55
+KPX oslash uring -55
+KPX oslash v -70
+KPX oslash w -70
+KPX oslash x -85
+KPX oslash y -70
+KPX oslash yacute -70
+KPX oslash ydieresis -70
+KPX oslash z -55
+KPX oslash zacute -55
+KPX oslash zcaron -55
+KPX oslash zdotaccent -55
+KPX otilde comma -40
+KPX otilde period -40
+KPX otilde v -15
+KPX otilde w -15
+KPX otilde x -30
+KPX otilde y -30
+KPX otilde yacute -30
+KPX otilde ydieresis -30
+KPX p comma -35
+KPX p period -35
+KPX p y -30
+KPX p yacute -30
+KPX p ydieresis -30
+KPX period quotedblright -100
+KPX period quoteright -100
+KPX period space -60
+KPX quotedblright space -40
+KPX quoteleft quoteleft -57
+KPX quoteright d -50
+KPX quoteright dcroat -50
+KPX quoteright quoteright -57
+KPX quoteright r -50
+KPX quoteright racute -50
+KPX quoteright rcaron -50
+KPX quoteright rcommaaccent -50
+KPX quoteright s -50
+KPX quoteright sacute -50
+KPX quoteright scaron -50
+KPX quoteright scedilla -50
+KPX quoteright scommaaccent -50
+KPX quoteright space -70
+KPX r a -10
+KPX r aacute -10
+KPX r abreve -10
+KPX r acircumflex -10
+KPX r adieresis -10
+KPX r agrave -10
+KPX r amacron -10
+KPX r aogonek -10
+KPX r aring -10
+KPX r atilde -10
+KPX r colon 30
+KPX r comma -50
+KPX r i 15
+KPX r iacute 15
+KPX r icircumflex 15
+KPX r idieresis 15
+KPX r igrave 15
+KPX r imacron 15
+KPX r iogonek 15
+KPX r k 15
+KPX r kcommaaccent 15
+KPX r l 15
+KPX r lacute 15
+KPX r lcommaaccent 15
+KPX r lslash 15
+KPX r m 25
+KPX r n 25
+KPX r nacute 25
+KPX r ncaron 25
+KPX r ncommaaccent 25
+KPX r ntilde 25
+KPX r p 30
+KPX r period -50
+KPX r semicolon 30
+KPX r t 40
+KPX r tcommaaccent 40
+KPX r u 15
+KPX r uacute 15
+KPX r ucircumflex 15
+KPX r udieresis 15
+KPX r ugrave 15
+KPX r uhungarumlaut 15
+KPX r umacron 15
+KPX r uogonek 15
+KPX r uring 15
+KPX r v 30
+KPX r y 30
+KPX r yacute 30
+KPX r ydieresis 30
+KPX racute a -10
+KPX racute aacute -10
+KPX racute abreve -10
+KPX racute acircumflex -10
+KPX racute adieresis -10
+KPX racute agrave -10
+KPX racute amacron -10
+KPX racute aogonek -10
+KPX racute aring -10
+KPX racute atilde -10
+KPX racute colon 30
+KPX racute comma -50
+KPX racute i 15
+KPX racute iacute 15
+KPX racute icircumflex 15
+KPX racute idieresis 15
+KPX racute igrave 15
+KPX racute imacron 15
+KPX racute iogonek 15
+KPX racute k 15
+KPX racute kcommaaccent 15
+KPX racute l 15
+KPX racute lacute 15
+KPX racute lcommaaccent 15
+KPX racute lslash 15
+KPX racute m 25
+KPX racute n 25
+KPX racute nacute 25
+KPX racute ncaron 25
+KPX racute ncommaaccent 25
+KPX racute ntilde 25
+KPX racute p 30
+KPX racute period -50
+KPX racute semicolon 30
+KPX racute t 40
+KPX racute tcommaaccent 40
+KPX racute u 15
+KPX racute uacute 15
+KPX racute ucircumflex 15
+KPX racute udieresis 15
+KPX racute ugrave 15
+KPX racute uhungarumlaut 15
+KPX racute umacron 15
+KPX racute uogonek 15
+KPX racute uring 15
+KPX racute v 30
+KPX racute y 30
+KPX racute yacute 30
+KPX racute ydieresis 30
+KPX rcaron a -10
+KPX rcaron aacute -10
+KPX rcaron abreve -10
+KPX rcaron acircumflex -10
+KPX rcaron adieresis -10
+KPX rcaron agrave -10
+KPX rcaron amacron -10
+KPX rcaron aogonek -10
+KPX rcaron aring -10
+KPX rcaron atilde -10
+KPX rcaron colon 30
+KPX rcaron comma -50
+KPX rcaron i 15
+KPX rcaron iacute 15
+KPX rcaron icircumflex 15
+KPX rcaron idieresis 15
+KPX rcaron igrave 15
+KPX rcaron imacron 15
+KPX rcaron iogonek 15
+KPX rcaron k 15
+KPX rcaron kcommaaccent 15
+KPX rcaron l 15
+KPX rcaron lacute 15
+KPX rcaron lcommaaccent 15
+KPX rcaron lslash 15
+KPX rcaron m 25
+KPX rcaron n 25
+KPX rcaron nacute 25
+KPX rcaron ncaron 25
+KPX rcaron ncommaaccent 25
+KPX rcaron ntilde 25
+KPX rcaron p 30
+KPX rcaron period -50
+KPX rcaron semicolon 30
+KPX rcaron t 40
+KPX rcaron tcommaaccent 40
+KPX rcaron u 15
+KPX rcaron uacute 15
+KPX rcaron ucircumflex 15
+KPX rcaron udieresis 15
+KPX rcaron ugrave 15
+KPX rcaron uhungarumlaut 15
+KPX rcaron umacron 15
+KPX rcaron uogonek 15
+KPX rcaron uring 15
+KPX rcaron v 30
+KPX rcaron y 30
+KPX rcaron yacute 30
+KPX rcaron ydieresis 30
+KPX rcommaaccent a -10
+KPX rcommaaccent aacute -10
+KPX rcommaaccent abreve -10
+KPX rcommaaccent acircumflex -10
+KPX rcommaaccent adieresis -10
+KPX rcommaaccent agrave -10
+KPX rcommaaccent amacron -10
+KPX rcommaaccent aogonek -10
+KPX rcommaaccent aring -10
+KPX rcommaaccent atilde -10
+KPX rcommaaccent colon 30
+KPX rcommaaccent comma -50
+KPX rcommaaccent i 15
+KPX rcommaaccent iacute 15
+KPX rcommaaccent icircumflex 15
+KPX rcommaaccent idieresis 15
+KPX rcommaaccent igrave 15
+KPX rcommaaccent imacron 15
+KPX rcommaaccent iogonek 15
+KPX rcommaaccent k 15
+KPX rcommaaccent kcommaaccent 15
+KPX rcommaaccent l 15
+KPX rcommaaccent lacute 15
+KPX rcommaaccent lcommaaccent 15
+KPX rcommaaccent lslash 15
+KPX rcommaaccent m 25
+KPX rcommaaccent n 25
+KPX rcommaaccent nacute 25
+KPX rcommaaccent ncaron 25
+KPX rcommaaccent ncommaaccent 25
+KPX rcommaaccent ntilde 25
+KPX rcommaaccent p 30
+KPX rcommaaccent period -50
+KPX rcommaaccent semicolon 30
+KPX rcommaaccent t 40
+KPX rcommaaccent tcommaaccent 40
+KPX rcommaaccent u 15
+KPX rcommaaccent uacute 15
+KPX rcommaaccent ucircumflex 15
+KPX rcommaaccent udieresis 15
+KPX rcommaaccent ugrave 15
+KPX rcommaaccent uhungarumlaut 15
+KPX rcommaaccent umacron 15
+KPX rcommaaccent uogonek 15
+KPX rcommaaccent uring 15
+KPX rcommaaccent v 30
+KPX rcommaaccent y 30
+KPX rcommaaccent yacute 30
+KPX rcommaaccent ydieresis 30
+KPX s comma -15
+KPX s period -15
+KPX s w -30
+KPX sacute comma -15
+KPX sacute period -15
+KPX sacute w -30
+KPX scaron comma -15
+KPX scaron period -15
+KPX scaron w -30
+KPX scedilla comma -15
+KPX scedilla period -15
+KPX scedilla w -30
+KPX scommaaccent comma -15
+KPX scommaaccent period -15
+KPX scommaaccent w -30
+KPX semicolon space -50
+KPX space T -50
+KPX space Tcaron -50
+KPX space Tcommaaccent -50
+KPX space V -50
+KPX space W -40
+KPX space Y -90
+KPX space Yacute -90
+KPX space Ydieresis -90
+KPX space quotedblleft -30
+KPX space quoteleft -60
+KPX v a -25
+KPX v aacute -25
+KPX v abreve -25
+KPX v acircumflex -25
+KPX v adieresis -25
+KPX v agrave -25
+KPX v amacron -25
+KPX v aogonek -25
+KPX v aring -25
+KPX v atilde -25
+KPX v comma -80
+KPX v e -25
+KPX v eacute -25
+KPX v ecaron -25
+KPX v ecircumflex -25
+KPX v edieresis -25
+KPX v edotaccent -25
+KPX v egrave -25
+KPX v emacron -25
+KPX v eogonek -25
+KPX v o -25
+KPX v oacute -25
+KPX v ocircumflex -25
+KPX v odieresis -25
+KPX v ograve -25
+KPX v ohungarumlaut -25
+KPX v omacron -25
+KPX v oslash -25
+KPX v otilde -25
+KPX v period -80
+KPX w a -15
+KPX w aacute -15
+KPX w abreve -15
+KPX w acircumflex -15
+KPX w adieresis -15
+KPX w agrave -15
+KPX w amacron -15
+KPX w aogonek -15
+KPX w aring -15
+KPX w atilde -15
+KPX w comma -60
+KPX w e -10
+KPX w eacute -10
+KPX w ecaron -10
+KPX w ecircumflex -10
+KPX w edieresis -10
+KPX w edotaccent -10
+KPX w egrave -10
+KPX w emacron -10
+KPX w eogonek -10
+KPX w o -10
+KPX w oacute -10
+KPX w ocircumflex -10
+KPX w odieresis -10
+KPX w ograve -10
+KPX w ohungarumlaut -10
+KPX w omacron -10
+KPX w oslash -10
+KPX w otilde -10
+KPX w period -60
+KPX x e -30
+KPX x eacute -30
+KPX x ecaron -30
+KPX x ecircumflex -30
+KPX x edieresis -30
+KPX x edotaccent -30
+KPX x egrave -30
+KPX x emacron -30
+KPX x eogonek -30
+KPX y a -20
+KPX y aacute -20
+KPX y abreve -20
+KPX y acircumflex -20
+KPX y adieresis -20
+KPX y agrave -20
+KPX y amacron -20
+KPX y aogonek -20
+KPX y aring -20
+KPX y atilde -20
+KPX y comma -100
+KPX y e -20
+KPX y eacute -20
+KPX y ecaron -20
+KPX y ecircumflex -20
+KPX y edieresis -20
+KPX y edotaccent -20
+KPX y egrave -20
+KPX y emacron -20
+KPX y eogonek -20
+KPX y o -20
+KPX y oacute -20
+KPX y ocircumflex -20
+KPX y odieresis -20
+KPX y ograve -20
+KPX y ohungarumlaut -20
+KPX y omacron -20
+KPX y oslash -20
+KPX y otilde -20
+KPX y period -100
+KPX yacute a -20
+KPX yacute aacute -20
+KPX yacute abreve -20
+KPX yacute acircumflex -20
+KPX yacute adieresis -20
+KPX yacute agrave -20
+KPX yacute amacron -20
+KPX yacute aogonek -20
+KPX yacute aring -20
+KPX yacute atilde -20
+KPX yacute comma -100
+KPX yacute e -20
+KPX yacute eacute -20
+KPX yacute ecaron -20
+KPX yacute ecircumflex -20
+KPX yacute edieresis -20
+KPX yacute edotaccent -20
+KPX yacute egrave -20
+KPX yacute emacron -20
+KPX yacute eogonek -20
+KPX yacute o -20
+KPX yacute oacute -20
+KPX yacute ocircumflex -20
+KPX yacute odieresis -20
+KPX yacute ograve -20
+KPX yacute ohungarumlaut -20
+KPX yacute omacron -20
+KPX yacute oslash -20
+KPX yacute otilde -20
+KPX yacute period -100
+KPX ydieresis a -20
+KPX ydieresis aacute -20
+KPX ydieresis abreve -20
+KPX ydieresis acircumflex -20
+KPX ydieresis adieresis -20
+KPX ydieresis agrave -20
+KPX ydieresis amacron -20
+KPX ydieresis aogonek -20
+KPX ydieresis aring -20
+KPX ydieresis atilde -20
+KPX ydieresis comma -100
+KPX ydieresis e -20
+KPX ydieresis eacute -20
+KPX ydieresis ecaron -20
+KPX ydieresis ecircumflex -20
+KPX ydieresis edieresis -20
+KPX ydieresis edotaccent -20
+KPX ydieresis egrave -20
+KPX ydieresis emacron -20
+KPX ydieresis eogonek -20
+KPX ydieresis o -20
+KPX ydieresis oacute -20
+KPX ydieresis ocircumflex -20
+KPX ydieresis odieresis -20
+KPX ydieresis ograve -20
+KPX ydieresis ohungarumlaut -20
+KPX ydieresis omacron -20
+KPX ydieresis oslash -20
+KPX ydieresis otilde -20
+KPX ydieresis period -100
+KPX z e -15
+KPX z eacute -15
+KPX z ecaron -15
+KPX z ecircumflex -15
+KPX z edieresis -15
+KPX z edotaccent -15
+KPX z egrave -15
+KPX z emacron -15
+KPX z eogonek -15
+KPX z o -15
+KPX z oacute -15
+KPX z ocircumflex -15
+KPX z odieresis -15
+KPX z ograve -15
+KPX z ohungarumlaut -15
+KPX z omacron -15
+KPX z oslash -15
+KPX z otilde -15
+KPX zacute e -15
+KPX zacute eacute -15
+KPX zacute ecaron -15
+KPX zacute ecircumflex -15
+KPX zacute edieresis -15
+KPX zacute edotaccent -15
+KPX zacute egrave -15
+KPX zacute emacron -15
+KPX zacute eogonek -15
+KPX zacute o -15
+KPX zacute oacute -15
+KPX zacute ocircumflex -15
+KPX zacute odieresis -15
+KPX zacute ograve -15
+KPX zacute ohungarumlaut -15
+KPX zacute omacron -15
+KPX zacute oslash -15
+KPX zacute otilde -15
+KPX zcaron e -15
+KPX zcaron eacute -15
+KPX zcaron ecaron -15
+KPX zcaron ecircumflex -15
+KPX zcaron edieresis -15
+KPX zcaron edotaccent -15
+KPX zcaron egrave -15
+KPX zcaron emacron -15
+KPX zcaron eogonek -15
+KPX zcaron o -15
+KPX zcaron oacute -15
+KPX zcaron ocircumflex -15
+KPX zcaron odieresis -15
+KPX zcaron ograve -15
+KPX zcaron ohungarumlaut -15
+KPX zcaron omacron -15
+KPX zcaron oslash -15
+KPX zcaron otilde -15
+KPX zdotaccent e -15
+KPX zdotaccent eacute -15
+KPX zdotaccent ecaron -15
+KPX zdotaccent ecircumflex -15
+KPX zdotaccent edieresis -15
+KPX zdotaccent edotaccent -15
+KPX zdotaccent egrave -15
+KPX zdotaccent emacron -15
+KPX zdotaccent eogonek -15
+KPX zdotaccent o -15
+KPX zdotaccent oacute -15
+KPX zdotaccent ocircumflex -15
+KPX zdotaccent odieresis -15
+KPX zdotaccent ograve -15
+KPX zdotaccent ohungarumlaut -15
+KPX zdotaccent omacron -15
+KPX zdotaccent oslash -15
+KPX zdotaccent otilde -15
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/program/libraries/afm/Makefile.am b/program/libraries/afm/Makefile.am
new file mode 100644 (file)
index 0000000..fa7d009
--- /dev/null
@@ -0,0 +1,6 @@
+EXTRA_DIST= COPYRIGHT.txt Courier-Bold.afm Courier-BoldOblique.afm \
+       Courier-Oblique.afm Courier.afm Helvetica-Bold.afm \
+       Helvetica-BoldOblique.afm Helvetica-Oblique.afm \
+       Helvetica.afm Symbol.afm Times-Bold.afm Times-BoldItalic.afm \
+       Times-Italic.afm Times-Roman.afm ZapfDingbats.afm \
+       compile_afm.pl glyphlist.txt test-afm.c
diff --git a/program/libraries/afm/Symbol.afm b/program/libraries/afm/Symbol.afm
new file mode 100644 (file)
index 0000000..6a5386a
--- /dev/null
@@ -0,0 +1,213 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All rights reserved.
+Comment Creation Date: Thu May  1 15:12:25 1997
+Comment UniqueID 43064
+Comment VMusage 30820 39997
+FontName Symbol
+FullName Symbol
+FamilyName Symbol
+Weight Medium
+ItalicAngle 0
+IsFixedPitch false
+CharacterSet Special
+FontBBox -180 -293 1090 1010 
+UnderlinePosition -100
+UnderlineThickness 50
+Version 001.008
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All rights reserved.
+EncodingScheme FontSpecific
+StdHW 92
+StdVW 85
+StartCharMetrics 190
+C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 333 ; N exclam ; B 128 -17 240 672 ;
+C 34 ; WX 713 ; N universal ; B 31 0 681 705 ;
+C 35 ; WX 500 ; N numbersign ; B 20 -16 481 673 ;
+C 36 ; WX 549 ; N existential ; B 25 0 478 707 ;
+C 37 ; WX 833 ; N percent ; B 63 -36 771 655 ;
+C 38 ; WX 778 ; N ampersand ; B 41 -18 750 661 ;
+C 39 ; WX 439 ; N suchthat ; B 48 -17 414 500 ;
+C 40 ; WX 333 ; N parenleft ; B 53 -191 300 673 ;
+C 41 ; WX 333 ; N parenright ; B 30 -191 277 673 ;
+C 42 ; WX 500 ; N asteriskmath ; B 65 134 427 551 ;
+C 43 ; WX 549 ; N plus ; B 10 0 539 533 ;
+C 44 ; WX 250 ; N comma ; B 56 -152 194 104 ;
+C 45 ; WX 549 ; N minus ; B 11 233 535 288 ;
+C 46 ; WX 250 ; N period ; B 69 -17 181 95 ;
+C 47 ; WX 278 ; N slash ; B 0 -18 254 646 ;
+C 48 ; WX 500 ; N zero ; B 24 -14 476 685 ;
+C 49 ; WX 500 ; N one ; B 117 0 390 673 ;
+C 50 ; WX 500 ; N two ; B 25 0 475 685 ;
+C 51 ; WX 500 ; N three ; B 43 -14 435 685 ;
+C 52 ; WX 500 ; N four ; B 15 0 469 685 ;
+C 53 ; WX 500 ; N five ; B 32 -14 445 690 ;
+C 54 ; WX 500 ; N six ; B 34 -14 468 685 ;
+C 55 ; WX 500 ; N seven ; B 24 -16 448 673 ;
+C 56 ; WX 500 ; N eight ; B 56 -14 445 685 ;
+C 57 ; WX 500 ; N nine ; B 30 -18 459 685 ;
+C 58 ; WX 278 ; N colon ; B 81 -17 193 460 ;
+C 59 ; WX 278 ; N semicolon ; B 83 -152 221 460 ;
+C 60 ; WX 549 ; N less ; B 26 0 523 522 ;
+C 61 ; WX 549 ; N equal ; B 11 141 537 390 ;
+C 62 ; WX 549 ; N greater ; B 26 0 523 522 ;
+C 63 ; WX 444 ; N question ; B 70 -17 412 686 ;
+C 64 ; WX 549 ; N congruent ; B 11 0 537 475 ;
+C 65 ; WX 722 ; N Alpha ; B 4 0 684 673 ;
+C 66 ; WX 667 ; N Beta ; B 29 0 592 673 ;
+C 67 ; WX 722 ; N Chi ; B -9 0 704 673 ;
+C 68 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C 69 ; WX 611 ; N Epsilon ; B 32 0 617 673 ;
+C 70 ; WX 763 ; N Phi ; B 26 0 741 673 ;
+C 71 ; WX 603 ; N Gamma ; B 24 0 609 673 ;
+C 72 ; WX 722 ; N Eta ; B 39 0 729 673 ;
+C 73 ; WX 333 ; N Iota ; B 32 0 316 673 ;
+C 74 ; WX 631 ; N theta1 ; B 18 -18 623 689 ;
+C 75 ; WX 722 ; N Kappa ; B 35 0 722 673 ;
+C 76 ; WX 686 ; N Lambda ; B 6 0 680 688 ;
+C 77 ; WX 889 ; N Mu ; B 28 0 887 673 ;
+C 78 ; WX 722 ; N Nu ; B 29 -8 720 673 ;
+C 79 ; WX 722 ; N Omicron ; B 41 -17 715 685 ;
+C 80 ; WX 768 ; N Pi ; B 25 0 745 673 ;
+C 81 ; WX 741 ; N Theta ; B 41 -17 715 685 ;
+C 82 ; WX 556 ; N Rho ; B 28 0 563 673 ;
+C 83 ; WX 592 ; N Sigma ; B 5 0 589 673 ;
+C 84 ; WX 611 ; N Tau ; B 33 0 607 673 ;
+C 85 ; WX 690 ; N Upsilon ; B -8 0 694 673 ;
+C 86 ; WX 439 ; N sigma1 ; B 40 -233 436 500 ;
+C 87 ; WX 768 ; N Omega ; B 34 0 736 688 ;
+C 88 ; WX 645 ; N Xi ; B 40 0 599 673 ;
+C 89 ; WX 795 ; N Psi ; B 15 0 781 684 ;
+C 90 ; WX 611 ; N Zeta ; B 44 0 636 673 ;
+C 91 ; WX 333 ; N bracketleft ; B 86 -155 299 674 ;
+C 92 ; WX 863 ; N therefore ; B 163 0 701 487 ;
+C 93 ; WX 333 ; N bracketright ; B 33 -155 246 674 ;
+C 94 ; WX 658 ; N perpendicular ; B 15 0 652 674 ;
+C 95 ; WX 500 ; N underscore ; B -2 -125 502 -75 ;
+C 96 ; WX 500 ; N radicalex ; B 480 881 1090 917 ;
+C 97 ; WX 631 ; N alpha ; B 41 -18 622 500 ;
+C 98 ; WX 549 ; N beta ; B 61 -223 515 741 ;
+C 99 ; WX 549 ; N chi ; B 12 -231 522 499 ;
+C 100 ; WX 494 ; N delta ; B 40 -19 481 740 ;
+C 101 ; WX 439 ; N epsilon ; B 22 -19 427 502 ;
+C 102 ; WX 521 ; N phi ; B 28 -224 492 673 ;
+C 103 ; WX 411 ; N gamma ; B 5 -225 484 499 ;
+C 104 ; WX 603 ; N eta ; B 0 -202 527 514 ;
+C 105 ; WX 329 ; N iota ; B 0 -17 301 503 ;
+C 106 ; WX 603 ; N phi1 ; B 36 -224 587 499 ;
+C 107 ; WX 549 ; N kappa ; B 33 0 558 501 ;
+C 108 ; WX 549 ; N lambda ; B 24 -17 548 739 ;
+C 109 ; WX 576 ; N mu ; B 33 -223 567 500 ;
+C 110 ; WX 521 ; N nu ; B -9 -16 475 507 ;
+C 111 ; WX 549 ; N omicron ; B 35 -19 501 499 ;
+C 112 ; WX 549 ; N pi ; B 10 -19 530 487 ;
+C 113 ; WX 521 ; N theta ; B 43 -17 485 690 ;
+C 114 ; WX 549 ; N rho ; B 50 -230 490 499 ;
+C 115 ; WX 603 ; N sigma ; B 30 -21 588 500 ;
+C 116 ; WX 439 ; N tau ; B 10 -19 418 500 ;
+C 117 ; WX 576 ; N upsilon ; B 7 -18 535 507 ;
+C 118 ; WX 713 ; N omega1 ; B 12 -18 671 583 ;
+C 119 ; WX 686 ; N omega ; B 42 -17 684 500 ;
+C 120 ; WX 493 ; N xi ; B 27 -224 469 766 ;
+C 121 ; WX 686 ; N psi ; B 12 -228 701 500 ;
+C 122 ; WX 494 ; N zeta ; B 60 -225 467 756 ;
+C 123 ; WX 480 ; N braceleft ; B 58 -183 397 673 ;
+C 124 ; WX 200 ; N bar ; B 65 -293 135 707 ;
+C 125 ; WX 480 ; N braceright ; B 79 -183 418 673 ;
+C 126 ; WX 549 ; N similar ; B 17 203 529 307 ;
+C 160 ; WX 750 ; N Euro ; B 20 -12 714 685 ;
+C 161 ; WX 620 ; N Upsilon1 ; B -2 0 610 685 ;
+C 162 ; WX 247 ; N minute ; B 27 459 228 735 ;
+C 163 ; WX 549 ; N lessequal ; B 29 0 526 639 ;
+C 164 ; WX 167 ; N fraction ; B -180 -12 340 677 ;
+C 165 ; WX 713 ; N infinity ; B 26 124 688 404 ;
+C 166 ; WX 500 ; N florin ; B 2 -193 494 686 ;
+C 167 ; WX 753 ; N club ; B 86 -26 660 533 ;
+C 168 ; WX 753 ; N diamond ; B 142 -36 600 550 ;
+C 169 ; WX 753 ; N heart ; B 117 -33 631 532 ;
+C 170 ; WX 753 ; N spade ; B 113 -36 629 548 ;
+C 171 ; WX 1042 ; N arrowboth ; B 24 -15 1024 511 ;
+C 172 ; WX 987 ; N arrowleft ; B 32 -15 942 511 ;
+C 173 ; WX 603 ; N arrowup ; B 45 0 571 910 ;
+C 174 ; WX 987 ; N arrowright ; B 49 -15 959 511 ;
+C 175 ; WX 603 ; N arrowdown ; B 45 -22 571 888 ;
+C 176 ; WX 400 ; N degree ; B 50 385 350 685 ;
+C 177 ; WX 549 ; N plusminus ; B 10 0 539 645 ;
+C 178 ; WX 411 ; N second ; B 20 459 413 737 ;
+C 179 ; WX 549 ; N greaterequal ; B 29 0 526 639 ;
+C 180 ; WX 549 ; N multiply ; B 17 8 533 524 ;
+C 181 ; WX 713 ; N proportional ; B 27 123 639 404 ;
+C 182 ; WX 494 ; N partialdiff ; B 26 -20 462 746 ;
+C 183 ; WX 460 ; N bullet ; B 50 113 410 473 ;
+C 184 ; WX 549 ; N divide ; B 10 71 536 456 ;
+C 185 ; WX 549 ; N notequal ; B 15 -25 540 549 ;
+C 186 ; WX 549 ; N equivalence ; B 14 82 538 443 ;
+C 187 ; WX 549 ; N approxequal ; B 14 135 527 394 ;
+C 188 ; WX 1000 ; N ellipsis ; B 111 -17 889 95 ;
+C 189 ; WX 603 ; N arrowvertex ; B 280 -120 336 1010 ;
+C 190 ; WX 1000 ; N arrowhorizex ; B -60 220 1050 276 ;
+C 191 ; WX 658 ; N carriagereturn ; B 15 -16 602 629 ;
+C 192 ; WX 823 ; N aleph ; B 175 -18 661 658 ;
+C 193 ; WX 686 ; N Ifraktur ; B 10 -53 578 740 ;
+C 194 ; WX 795 ; N Rfraktur ; B 26 -15 759 734 ;
+C 195 ; WX 987 ; N weierstrass ; B 159 -211 870 573 ;
+C 196 ; WX 768 ; N circlemultiply ; B 43 -17 733 673 ;
+C 197 ; WX 768 ; N circleplus ; B 43 -15 733 675 ;
+C 198 ; WX 823 ; N emptyset ; B 39 -24 781 719 ;
+C 199 ; WX 768 ; N intersection ; B 40 0 732 509 ;
+C 200 ; WX 768 ; N union ; B 40 -17 732 492 ;
+C 201 ; WX 713 ; N propersuperset ; B 20 0 673 470 ;
+C 202 ; WX 713 ; N reflexsuperset ; B 20 -125 673 470 ;
+C 203 ; WX 713 ; N notsubset ; B 36 -70 690 540 ;
+C 204 ; WX 713 ; N propersubset ; B 37 0 690 470 ;
+C 205 ; WX 713 ; N reflexsubset ; B 37 -125 690 470 ;
+C 206 ; WX 713 ; N element ; B 45 0 505 468 ;
+C 207 ; WX 713 ; N notelement ; B 45 -58 505 555 ;
+C 208 ; WX 768 ; N angle ; B 26 0 738 673 ;
+C 209 ; WX 713 ; N gradient ; B 36 -19 681 718 ;
+C 210 ; WX 790 ; N registerserif ; B 50 -17 740 673 ;
+C 211 ; WX 790 ; N copyrightserif ; B 51 -15 741 675 ;
+C 212 ; WX 890 ; N trademarkserif ; B 18 293 855 673 ;
+C 213 ; WX 823 ; N product ; B 25 -101 803 751 ;
+C 214 ; WX 549 ; N radical ; B 10 -38 515 917 ;
+C 215 ; WX 250 ; N dotmath ; B 69 210 169 310 ;
+C 216 ; WX 713 ; N logicalnot ; B 15 0 680 288 ;
+C 217 ; WX 603 ; N logicaland ; B 23 0 583 454 ;
+C 218 ; WX 603 ; N logicalor ; B 30 0 578 477 ;
+C 219 ; WX 1042 ; N arrowdblboth ; B 27 -20 1023 510 ;
+C 220 ; WX 987 ; N arrowdblleft ; B 30 -15 939 513 ;
+C 221 ; WX 603 ; N arrowdblup ; B 39 2 567 911 ;
+C 222 ; WX 987 ; N arrowdblright ; B 45 -20 954 508 ;
+C 223 ; WX 603 ; N arrowdbldown ; B 44 -19 572 890 ;
+C 224 ; WX 494 ; N lozenge ; B 18 0 466 745 ;
+C 225 ; WX 329 ; N angleleft ; B 25 -198 306 746 ;
+C 226 ; WX 790 ; N registersans ; B 50 -20 740 670 ;
+C 227 ; WX 790 ; N copyrightsans ; B 49 -15 739 675 ;
+C 228 ; WX 786 ; N trademarksans ; B 5 293 725 673 ;
+C 229 ; WX 713 ; N summation ; B 14 -108 695 752 ;
+C 230 ; WX 384 ; N parenlefttp ; B 24 -293 436 926 ;
+C 231 ; WX 384 ; N parenleftex ; B 24 -85 108 925 ;
+C 232 ; WX 384 ; N parenleftbt ; B 24 -293 436 926 ;
+C 233 ; WX 384 ; N bracketlefttp ; B 0 -80 349 926 ;
+C 234 ; WX 384 ; N bracketleftex ; B 0 -79 77 925 ;
+C 235 ; WX 384 ; N bracketleftbt ; B 0 -80 349 926 ;
+C 236 ; WX 494 ; N bracelefttp ; B 209 -85 445 925 ;
+C 237 ; WX 494 ; N braceleftmid ; B 20 -85 284 935 ;
+C 238 ; WX 494 ; N braceleftbt ; B 209 -75 445 935 ;
+C 239 ; WX 494 ; N braceex ; B 209 -85 284 935 ;
+C 241 ; WX 329 ; N angleright ; B 21 -198 302 746 ;
+C 242 ; WX 274 ; N integral ; B 2 -107 291 916 ;
+C 243 ; WX 686 ; N integraltp ; B 308 -88 675 920 ;
+C 244 ; WX 686 ; N integralex ; B 308 -88 378 975 ;
+C 245 ; WX 686 ; N integralbt ; B 11 -87 378 921 ;
+C 246 ; WX 384 ; N parenrighttp ; B 54 -293 466 926 ;
+C 247 ; WX 384 ; N parenrightex ; B 382 -85 466 925 ;
+C 248 ; WX 384 ; N parenrightbt ; B 54 -293 466 926 ;
+C 249 ; WX 384 ; N bracketrighttp ; B 22 -80 371 926 ;
+C 250 ; WX 384 ; N bracketrightex ; B 294 -79 371 925 ;
+C 251 ; WX 384 ; N bracketrightbt ; B 22 -80 371 926 ;
+C 252 ; WX 494 ; N bracerighttp ; B 48 -85 284 925 ;
+C 253 ; WX 494 ; N bracerightmid ; B 209 -85 473 935 ;
+C 254 ; WX 494 ; N bracerightbt ; B 48 -75 284 935 ;
+C -1 ; WX 790 ; N apple ; B 56 -3 733 808 ;
+EndCharMetrics
+EndFontMetrics
diff --git a/program/libraries/afm/Times-Bold.afm b/program/libraries/afm/Times-Bold.afm
new file mode 100644 (file)
index 0000000..559ebae
--- /dev/null
@@ -0,0 +1,2588 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated.  All Rights Reserved.
+Comment Creation Date: Thu May  1 12:52:56 1997
+Comment UniqueID 43065
+Comment VMusage 41636 52661
+FontName Times-Bold
+FullName Times Bold
+FamilyName Times
+Weight Bold
+ItalicAngle 0
+IsFixedPitch false
+CharacterSet ExtendedRoman
+FontBBox -168 -218 1000 935 
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated.  All Rights Reserved.Times is a trademark of Linotype-Hell AG and/or its subsidiaries.
+EncodingScheme AdobeStandardEncoding
+CapHeight 676
+XHeight 461
+Ascender 683
+Descender -217
+StdHW 44
+StdVW 139
+StartCharMetrics 315
+C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 333 ; N exclam ; B 81 -13 251 691 ;
+C 34 ; WX 555 ; N quotedbl ; B 83 404 472 691 ;
+C 35 ; WX 500 ; N numbersign ; B 4 0 496 700 ;
+C 36 ; WX 500 ; N dollar ; B 29 -99 472 750 ;
+C 37 ; WX 1000 ; N percent ; B 124 -14 877 692 ;
+C 38 ; WX 833 ; N ampersand ; B 62 -16 787 691 ;
+C 39 ; WX 333 ; N quoteright ; B 79 356 263 691 ;
+C 40 ; WX 333 ; N parenleft ; B 46 -168 306 694 ;
+C 41 ; WX 333 ; N parenright ; B 27 -168 287 694 ;
+C 42 ; WX 500 ; N asterisk ; B 56 255 447 691 ;
+C 43 ; WX 570 ; N plus ; B 33 0 537 506 ;
+C 44 ; WX 250 ; N comma ; B 39 -180 223 155 ;
+C 45 ; WX 333 ; N hyphen ; B 44 171 287 287 ;
+C 46 ; WX 250 ; N period ; B 41 -13 210 156 ;
+C 47 ; WX 278 ; N slash ; B -24 -19 302 691 ;
+C 48 ; WX 500 ; N zero ; B 24 -13 476 688 ;
+C 49 ; WX 500 ; N one ; B 65 0 442 688 ;
+C 50 ; WX 500 ; N two ; B 17 0 478 688 ;
+C 51 ; WX 500 ; N three ; B 16 -14 468 688 ;
+C 52 ; WX 500 ; N four ; B 19 0 475 688 ;
+C 53 ; WX 500 ; N five ; B 22 -8 470 676 ;
+C 54 ; WX 500 ; N six ; B 28 -13 475 688 ;
+C 55 ; WX 500 ; N seven ; B 17 0 477 676 ;
+C 56 ; WX 500 ; N eight ; B 28 -13 472 688 ;
+C 57 ; WX 500 ; N nine ; B 26 -13 473 688 ;
+C 58 ; WX 333 ; N colon ; B 82 -13 251 472 ;
+C 59 ; WX 333 ; N semicolon ; B 82 -180 266 472 ;
+C 60 ; WX 570 ; N less ; B 31 -8 539 514 ;
+C 61 ; WX 570 ; N equal ; B 33 107 537 399 ;
+C 62 ; WX 570 ; N greater ; B 31 -8 539 514 ;
+C 63 ; WX 500 ; N question ; B 57 -13 445 689 ;
+C 64 ; WX 930 ; N at ; B 108 -19 822 691 ;
+C 65 ; WX 722 ; N A ; B 9 0 689 690 ;
+C 66 ; WX 667 ; N B ; B 16 0 619 676 ;
+C 67 ; WX 722 ; N C ; B 49 -19 687 691 ;
+C 68 ; WX 722 ; N D ; B 14 0 690 676 ;
+C 69 ; WX 667 ; N E ; B 16 0 641 676 ;
+C 70 ; WX 611 ; N F ; B 16 0 583 676 ;
+C 71 ; WX 778 ; N G ; B 37 -19 755 691 ;
+C 72 ; WX 778 ; N H ; B 21 0 759 676 ;
+C 73 ; WX 389 ; N I ; B 20 0 370 676 ;
+C 74 ; WX 500 ; N J ; B 3 -96 479 676 ;
+C 75 ; WX 778 ; N K ; B 30 0 769 676 ;
+C 76 ; WX 667 ; N L ; B 19 0 638 676 ;
+C 77 ; WX 944 ; N M ; B 14 0 921 676 ;
+C 78 ; WX 722 ; N N ; B 16 -18 701 676 ;
+C 79 ; WX 778 ; N O ; B 35 -19 743 691 ;
+C 80 ; WX 611 ; N P ; B 16 0 600 676 ;
+C 81 ; WX 778 ; N Q ; B 35 -176 743 691 ;
+C 82 ; WX 722 ; N R ; B 26 0 715 676 ;
+C 83 ; WX 556 ; N S ; B 35 -19 513 692 ;
+C 84 ; WX 667 ; N T ; B 31 0 636 676 ;
+C 85 ; WX 722 ; N U ; B 16 -19 701 676 ;
+C 86 ; WX 722 ; N V ; B 16 -18 701 676 ;
+C 87 ; WX 1000 ; N W ; B 19 -15 981 676 ;
+C 88 ; WX 722 ; N X ; B 16 0 699 676 ;
+C 89 ; WX 722 ; N Y ; B 15 0 699 676 ;
+C 90 ; WX 667 ; N Z ; B 28 0 634 676 ;
+C 91 ; WX 333 ; N bracketleft ; B 67 -149 301 678 ;
+C 92 ; WX 278 ; N backslash ; B -25 -19 303 691 ;
+C 93 ; WX 333 ; N bracketright ; B 32 -149 266 678 ;
+C 94 ; WX 581 ; N asciicircum ; B 73 311 509 676 ;
+C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ;
+C 96 ; WX 333 ; N quoteleft ; B 70 356 254 691 ;
+C 97 ; WX 500 ; N a ; B 25 -14 488 473 ;
+C 98 ; WX 556 ; N b ; B 17 -14 521 676 ;
+C 99 ; WX 444 ; N c ; B 25 -14 430 473 ;
+C 100 ; WX 556 ; N d ; B 25 -14 534 676 ;
+C 101 ; WX 444 ; N e ; B 25 -14 426 473 ;
+C 102 ; WX 333 ; N f ; B 14 0 389 691 ; L i fi ; L l fl ;
+C 103 ; WX 500 ; N g ; B 28 -206 483 473 ;
+C 104 ; WX 556 ; N h ; B 16 0 534 676 ;
+C 105 ; WX 278 ; N i ; B 16 0 255 691 ;
+C 106 ; WX 333 ; N j ; B -57 -203 263 691 ;
+C 107 ; WX 556 ; N k ; B 22 0 543 676 ;
+C 108 ; WX 278 ; N l ; B 16 0 255 676 ;
+C 109 ; WX 833 ; N m ; B 16 0 814 473 ;
+C 110 ; WX 556 ; N n ; B 21 0 539 473 ;
+C 111 ; WX 500 ; N o ; B 25 -14 476 473 ;
+C 112 ; WX 556 ; N p ; B 19 -205 524 473 ;
+C 113 ; WX 556 ; N q ; B 34 -205 536 473 ;
+C 114 ; WX 444 ; N r ; B 29 0 434 473 ;
+C 115 ; WX 389 ; N s ; B 25 -14 361 473 ;
+C 116 ; WX 333 ; N t ; B 20 -12 332 630 ;
+C 117 ; WX 556 ; N u ; B 16 -14 537 461 ;
+C 118 ; WX 500 ; N v ; B 21 -14 485 461 ;
+C 119 ; WX 722 ; N w ; B 23 -14 707 461 ;
+C 120 ; WX 500 ; N x ; B 12 0 484 461 ;
+C 121 ; WX 500 ; N y ; B 16 -205 480 461 ;
+C 122 ; WX 444 ; N z ; B 21 0 420 461 ;
+C 123 ; WX 394 ; N braceleft ; B 22 -175 340 698 ;
+C 124 ; WX 220 ; N bar ; B 66 -218 154 782 ;
+C 125 ; WX 394 ; N braceright ; B 54 -175 372 698 ;
+C 126 ; WX 520 ; N asciitilde ; B 29 173 491 333 ;
+C 161 ; WX 333 ; N exclamdown ; B 82 -203 252 501 ;
+C 162 ; WX 500 ; N cent ; B 53 -140 458 588 ;
+C 163 ; WX 500 ; N sterling ; B 21 -14 477 684 ;
+C 164 ; WX 167 ; N fraction ; B -168 -12 329 688 ;
+C 165 ; WX 500 ; N yen ; B -64 0 547 676 ;
+C 166 ; WX 500 ; N florin ; B 0 -155 498 706 ;
+C 167 ; WX 500 ; N section ; B 57 -132 443 691 ;
+C 168 ; WX 500 ; N currency ; B -26 61 526 613 ;
+C 169 ; WX 278 ; N quotesingle ; B 75 404 204 691 ;
+C 170 ; WX 500 ; N quotedblleft ; B 32 356 486 691 ;
+C 171 ; WX 500 ; N guillemotleft ; B 23 36 473 415 ;
+C 172 ; WX 333 ; N guilsinglleft ; B 51 36 305 415 ;
+C 173 ; WX 333 ; N guilsinglright ; B 28 36 282 415 ;
+C 174 ; WX 556 ; N fi ; B 14 0 536 691 ;
+C 175 ; WX 556 ; N fl ; B 14 0 536 691 ;
+C 177 ; WX 500 ; N endash ; B 0 181 500 271 ;
+C 178 ; WX 500 ; N dagger ; B 47 -134 453 691 ;
+C 179 ; WX 500 ; N daggerdbl ; B 45 -132 456 691 ;
+C 180 ; WX 250 ; N periodcentered ; B 41 248 210 417 ;
+C 182 ; WX 540 ; N paragraph ; B 0 -186 519 676 ;
+C 183 ; WX 350 ; N bullet ; B 35 198 315 478 ;
+C 184 ; WX 333 ; N quotesinglbase ; B 79 -180 263 155 ;
+C 185 ; WX 500 ; N quotedblbase ; B 14 -180 468 155 ;
+C 186 ; WX 500 ; N quotedblright ; B 14 356 468 691 ;
+C 187 ; WX 500 ; N guillemotright ; B 27 36 477 415 ;
+C 188 ; WX 1000 ; N ellipsis ; B 82 -13 917 156 ;
+C 189 ; WX 1000 ; N perthousand ; B 7 -29 995 706 ;
+C 191 ; WX 500 ; N questiondown ; B 55 -201 443 501 ;
+C 193 ; WX 333 ; N grave ; B 8 528 246 713 ;
+C 194 ; WX 333 ; N acute ; B 86 528 324 713 ;
+C 195 ; WX 333 ; N circumflex ; B -2 528 335 704 ;
+C 196 ; WX 333 ; N tilde ; B -16 547 349 674 ;
+C 197 ; WX 333 ; N macron ; B 1 565 331 637 ;
+C 198 ; WX 333 ; N breve ; B 15 528 318 691 ;
+C 199 ; WX 333 ; N dotaccent ; B 103 536 258 691 ;
+C 200 ; WX 333 ; N dieresis ; B -2 537 335 667 ;
+C 202 ; WX 333 ; N ring ; B 60 527 273 740 ;
+C 203 ; WX 333 ; N cedilla ; B 68 -218 294 0 ;
+C 205 ; WX 333 ; N hungarumlaut ; B -13 528 425 713 ;
+C 206 ; WX 333 ; N ogonek ; B 90 -193 319 24 ;
+C 207 ; WX 333 ; N caron ; B -2 528 335 704 ;
+C 208 ; WX 1000 ; N emdash ; B 0 181 1000 271 ;
+C 225 ; WX 1000 ; N AE ; B 4 0 951 676 ;
+C 227 ; WX 300 ; N ordfeminine ; B -1 397 301 688 ;
+C 232 ; WX 667 ; N Lslash ; B 19 0 638 676 ;
+C 233 ; WX 778 ; N Oslash ; B 35 -74 743 737 ;
+C 234 ; WX 1000 ; N OE ; B 22 -5 981 684 ;
+C 235 ; WX 330 ; N ordmasculine ; B 18 397 312 688 ;
+C 241 ; WX 722 ; N ae ; B 33 -14 693 473 ;
+C 245 ; WX 278 ; N dotlessi ; B 16 0 255 461 ;
+C 248 ; WX 278 ; N lslash ; B -22 0 303 676 ;
+C 249 ; WX 500 ; N oslash ; B 25 -92 476 549 ;
+C 250 ; WX 722 ; N oe ; B 22 -14 696 473 ;
+C 251 ; WX 556 ; N germandbls ; B 19 -12 517 691 ;
+C -1 ; WX 389 ; N Idieresis ; B 20 0 370 877 ;
+C -1 ; WX 444 ; N eacute ; B 25 -14 426 713 ;
+C -1 ; WX 500 ; N abreve ; B 25 -14 488 691 ;
+C -1 ; WX 556 ; N uhungarumlaut ; B 16 -14 557 713 ;
+C -1 ; WX 444 ; N ecaron ; B 25 -14 426 704 ;
+C -1 ; WX 722 ; N Ydieresis ; B 15 0 699 877 ;
+C -1 ; WX 570 ; N divide ; B 33 -31 537 537 ;
+C -1 ; WX 722 ; N Yacute ; B 15 0 699 923 ;
+C -1 ; WX 722 ; N Acircumflex ; B 9 0 689 914 ;
+C -1 ; WX 500 ; N aacute ; B 25 -14 488 713 ;
+C -1 ; WX 722 ; N Ucircumflex ; B 16 -19 701 914 ;
+C -1 ; WX 500 ; N yacute ; B 16 -205 480 713 ;
+C -1 ; WX 389 ; N scommaaccent ; B 25 -218 361 473 ;
+C -1 ; WX 444 ; N ecircumflex ; B 25 -14 426 704 ;
+C -1 ; WX 722 ; N Uring ; B 16 -19 701 935 ;
+C -1 ; WX 722 ; N Udieresis ; B 16 -19 701 877 ;
+C -1 ; WX 500 ; N aogonek ; B 25 -193 504 473 ;
+C -1 ; WX 722 ; N Uacute ; B 16 -19 701 923 ;
+C -1 ; WX 556 ; N uogonek ; B 16 -193 539 461 ;
+C -1 ; WX 667 ; N Edieresis ; B 16 0 641 877 ;
+C -1 ; WX 722 ; N Dcroat ; B 6 0 690 676 ;
+C -1 ; WX 250 ; N commaaccent ; B 47 -218 203 -50 ;
+C -1 ; WX 747 ; N copyright ; B 26 -19 721 691 ;
+C -1 ; WX 667 ; N Emacron ; B 16 0 641 847 ;
+C -1 ; WX 444 ; N ccaron ; B 25 -14 430 704 ;
+C -1 ; WX 500 ; N aring ; B 25 -14 488 740 ;
+C -1 ; WX 722 ; N Ncommaaccent ; B 16 -188 701 676 ;
+C -1 ; WX 278 ; N lacute ; B 16 0 297 923 ;
+C -1 ; WX 500 ; N agrave ; B 25 -14 488 713 ;
+C -1 ; WX 667 ; N Tcommaaccent ; B 31 -218 636 676 ;
+C -1 ; WX 722 ; N Cacute ; B 49 -19 687 923 ;
+C -1 ; WX 500 ; N atilde ; B 25 -14 488 674 ;
+C -1 ; WX 667 ; N Edotaccent ; B 16 0 641 901 ;
+C -1 ; WX 389 ; N scaron ; B 25 -14 363 704 ;
+C -1 ; WX 389 ; N scedilla ; B 25 -218 361 473 ;
+C -1 ; WX 278 ; N iacute ; B 16 0 289 713 ;
+C -1 ; WX 494 ; N lozenge ; B 10 0 484 745 ;
+C -1 ; WX 722 ; N Rcaron ; B 26 0 715 914 ;
+C -1 ; WX 778 ; N Gcommaaccent ; B 37 -218 755 691 ;
+C -1 ; WX 556 ; N ucircumflex ; B 16 -14 537 704 ;
+C -1 ; WX 500 ; N acircumflex ; B 25 -14 488 704 ;
+C -1 ; WX 722 ; N Amacron ; B 9 0 689 847 ;
+C -1 ; WX 444 ; N rcaron ; B 29 0 434 704 ;
+C -1 ; WX 444 ; N ccedilla ; B 25 -218 430 473 ;
+C -1 ; WX 667 ; N Zdotaccent ; B 28 0 634 901 ;
+C -1 ; WX 611 ; N Thorn ; B 16 0 600 676 ;
+C -1 ; WX 778 ; N Omacron ; B 35 -19 743 847 ;
+C -1 ; WX 722 ; N Racute ; B 26 0 715 923 ;
+C -1 ; WX 556 ; N Sacute ; B 35 -19 513 923 ;
+C -1 ; WX 672 ; N dcaron ; B 25 -14 681 682 ;
+C -1 ; WX 722 ; N Umacron ; B 16 -19 701 847 ;
+C -1 ; WX 556 ; N uring ; B 16 -14 537 740 ;
+C -1 ; WX 300 ; N threesuperior ; B 3 268 297 688 ;
+C -1 ; WX 778 ; N Ograve ; B 35 -19 743 923 ;
+C -1 ; WX 722 ; N Agrave ; B 9 0 689 923 ;
+C -1 ; WX 722 ; N Abreve ; B 9 0 689 901 ;
+C -1 ; WX 570 ; N multiply ; B 48 16 522 490 ;
+C -1 ; WX 556 ; N uacute ; B 16 -14 537 713 ;
+C -1 ; WX 667 ; N Tcaron ; B 31 0 636 914 ;
+C -1 ; WX 494 ; N partialdiff ; B 11 -21 494 750 ;
+C -1 ; WX 500 ; N ydieresis ; B 16 -205 480 667 ;
+C -1 ; WX 722 ; N Nacute ; B 16 -18 701 923 ;
+C -1 ; WX 278 ; N icircumflex ; B -37 0 300 704 ;
+C -1 ; WX 667 ; N Ecircumflex ; B 16 0 641 914 ;
+C -1 ; WX 500 ; N adieresis ; B 25 -14 488 667 ;
+C -1 ; WX 444 ; N edieresis ; B 25 -14 426 667 ;
+C -1 ; WX 444 ; N cacute ; B 25 -14 430 713 ;
+C -1 ; WX 556 ; N nacute ; B 21 0 539 713 ;
+C -1 ; WX 556 ; N umacron ; B 16 -14 537 637 ;
+C -1 ; WX 722 ; N Ncaron ; B 16 -18 701 914 ;
+C -1 ; WX 389 ; N Iacute ; B 20 0 370 923 ;
+C -1 ; WX 570 ; N plusminus ; B 33 0 537 506 ;
+C -1 ; WX 220 ; N brokenbar ; B 66 -143 154 707 ;
+C -1 ; WX 747 ; N registered ; B 26 -19 721 691 ;
+C -1 ; WX 778 ; N Gbreve ; B 37 -19 755 901 ;
+C -1 ; WX 389 ; N Idotaccent ; B 20 0 370 901 ;
+C -1 ; WX 600 ; N summation ; B 14 -10 585 706 ;
+C -1 ; WX 667 ; N Egrave ; B 16 0 641 923 ;
+C -1 ; WX 444 ; N racute ; B 29 0 434 713 ;
+C -1 ; WX 500 ; N omacron ; B 25 -14 476 637 ;
+C -1 ; WX 667 ; N Zacute ; B 28 0 634 923 ;
+C -1 ; WX 667 ; N Zcaron ; B 28 0 634 914 ;
+C -1 ; WX 549 ; N greaterequal ; B 26 0 523 704 ;
+C -1 ; WX 722 ; N Eth ; B 6 0 690 676 ;
+C -1 ; WX 722 ; N Ccedilla ; B 49 -218 687 691 ;
+C -1 ; WX 278 ; N lcommaaccent ; B 16 -218 255 676 ;
+C -1 ; WX 416 ; N tcaron ; B 20 -12 425 815 ;
+C -1 ; WX 444 ; N eogonek ; B 25 -193 426 473 ;
+C -1 ; WX 722 ; N Uogonek ; B 16 -193 701 676 ;
+C -1 ; WX 722 ; N Aacute ; B 9 0 689 923 ;
+C -1 ; WX 722 ; N Adieresis ; B 9 0 689 877 ;
+C -1 ; WX 444 ; N egrave ; B 25 -14 426 713 ;
+C -1 ; WX 444 ; N zacute ; B 21 0 420 713 ;
+C -1 ; WX 278 ; N iogonek ; B 16 -193 274 691 ;
+C -1 ; WX 778 ; N Oacute ; B 35 -19 743 923 ;
+C -1 ; WX 500 ; N oacute ; B 25 -14 476 713 ;
+C -1 ; WX 500 ; N amacron ; B 25 -14 488 637 ;
+C -1 ; WX 389 ; N sacute ; B 25 -14 361 713 ;
+C -1 ; WX 278 ; N idieresis ; B -37 0 300 667 ;
+C -1 ; WX 778 ; N Ocircumflex ; B 35 -19 743 914 ;
+C -1 ; WX 722 ; N Ugrave ; B 16 -19 701 923 ;
+C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C -1 ; WX 556 ; N thorn ; B 19 -205 524 676 ;
+C -1 ; WX 300 ; N twosuperior ; B 0 275 300 688 ;
+C -1 ; WX 778 ; N Odieresis ; B 35 -19 743 877 ;
+C -1 ; WX 556 ; N mu ; B 33 -206 536 461 ;
+C -1 ; WX 278 ; N igrave ; B -27 0 255 713 ;
+C -1 ; WX 500 ; N ohungarumlaut ; B 25 -14 529 713 ;
+C -1 ; WX 667 ; N Eogonek ; B 16 -193 644 676 ;
+C -1 ; WX 556 ; N dcroat ; B 25 -14 534 676 ;
+C -1 ; WX 750 ; N threequarters ; B 23 -12 733 688 ;
+C -1 ; WX 556 ; N Scedilla ; B 35 -218 513 692 ;
+C -1 ; WX 394 ; N lcaron ; B 16 0 412 682 ;
+C -1 ; WX 778 ; N Kcommaaccent ; B 30 -218 769 676 ;
+C -1 ; WX 667 ; N Lacute ; B 19 0 638 923 ;
+C -1 ; WX 1000 ; N trademark ; B 24 271 977 676 ;
+C -1 ; WX 444 ; N edotaccent ; B 25 -14 426 691 ;
+C -1 ; WX 389 ; N Igrave ; B 20 0 370 923 ;
+C -1 ; WX 389 ; N Imacron ; B 20 0 370 847 ;
+C -1 ; WX 667 ; N Lcaron ; B 19 0 652 682 ;
+C -1 ; WX 750 ; N onehalf ; B -7 -12 775 688 ;
+C -1 ; WX 549 ; N lessequal ; B 29 0 526 704 ;
+C -1 ; WX 500 ; N ocircumflex ; B 25 -14 476 704 ;
+C -1 ; WX 556 ; N ntilde ; B 21 0 539 674 ;
+C -1 ; WX 722 ; N Uhungarumlaut ; B 16 -19 701 923 ;
+C -1 ; WX 667 ; N Eacute ; B 16 0 641 923 ;
+C -1 ; WX 444 ; N emacron ; B 25 -14 426 637 ;
+C -1 ; WX 500 ; N gbreve ; B 28 -206 483 691 ;
+C -1 ; WX 750 ; N onequarter ; B 28 -12 743 688 ;
+C -1 ; WX 556 ; N Scaron ; B 35 -19 513 914 ;
+C -1 ; WX 556 ; N Scommaaccent ; B 35 -218 513 692 ;
+C -1 ; WX 778 ; N Ohungarumlaut ; B 35 -19 743 923 ;
+C -1 ; WX 400 ; N degree ; B 57 402 343 688 ;
+C -1 ; WX 500 ; N ograve ; B 25 -14 476 713 ;
+C -1 ; WX 722 ; N Ccaron ; B 49 -19 687 914 ;
+C -1 ; WX 556 ; N ugrave ; B 16 -14 537 713 ;
+C -1 ; WX 549 ; N radical ; B 10 -46 512 850 ;
+C -1 ; WX 722 ; N Dcaron ; B 14 0 690 914 ;
+C -1 ; WX 444 ; N rcommaaccent ; B 29 -218 434 473 ;
+C -1 ; WX 722 ; N Ntilde ; B 16 -18 701 884 ;
+C -1 ; WX 500 ; N otilde ; B 25 -14 476 674 ;
+C -1 ; WX 722 ; N Rcommaaccent ; B 26 -218 715 676 ;
+C -1 ; WX 667 ; N Lcommaaccent ; B 19 -218 638 676 ;
+C -1 ; WX 722 ; N Atilde ; B 9 0 689 884 ;
+C -1 ; WX 722 ; N Aogonek ; B 9 -193 699 690 ;
+C -1 ; WX 722 ; N Aring ; B 9 0 689 935 ;
+C -1 ; WX 778 ; N Otilde ; B 35 -19 743 884 ;
+C -1 ; WX 444 ; N zdotaccent ; B 21 0 420 691 ;
+C -1 ; WX 667 ; N Ecaron ; B 16 0 641 914 ;
+C -1 ; WX 389 ; N Iogonek ; B 20 -193 370 676 ;
+C -1 ; WX 556 ; N kcommaaccent ; B 22 -218 543 676 ;
+C -1 ; WX 570 ; N minus ; B 33 209 537 297 ;
+C -1 ; WX 389 ; N Icircumflex ; B 20 0 370 914 ;
+C -1 ; WX 556 ; N ncaron ; B 21 0 539 704 ;
+C -1 ; WX 333 ; N tcommaaccent ; B 20 -218 332 630 ;
+C -1 ; WX 570 ; N logicalnot ; B 33 108 537 399 ;
+C -1 ; WX 500 ; N odieresis ; B 25 -14 476 667 ;
+C -1 ; WX 556 ; N udieresis ; B 16 -14 537 667 ;
+C -1 ; WX 549 ; N notequal ; B 15 -49 540 570 ;
+C -1 ; WX 500 ; N gcommaaccent ; B 28 -206 483 829 ;
+C -1 ; WX 500 ; N eth ; B 25 -14 476 691 ;
+C -1 ; WX 444 ; N zcaron ; B 21 0 420 704 ;
+C -1 ; WX 556 ; N ncommaaccent ; B 21 -218 539 473 ;
+C -1 ; WX 300 ; N onesuperior ; B 28 275 273 688 ;
+C -1 ; WX 278 ; N imacron ; B -8 0 272 637 ;
+C -1 ; WX 500 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+StartKernData
+StartKernPairs 2242
+KPX A C -55
+KPX A Cacute -55
+KPX A Ccaron -55
+KPX A Ccedilla -55
+KPX A G -55
+KPX A Gbreve -55
+KPX A Gcommaaccent -55
+KPX A O -45
+KPX A Oacute -45
+KPX A Ocircumflex -45
+KPX A Odieresis -45
+KPX A Ograve -45
+KPX A Ohungarumlaut -45
+KPX A Omacron -45
+KPX A Oslash -45
+KPX A Otilde -45
+KPX A Q -45
+KPX A T -95
+KPX A Tcaron -95
+KPX A Tcommaaccent -95
+KPX A U -50
+KPX A Uacute -50
+KPX A Ucircumflex -50
+KPX A Udieresis -50
+KPX A Ugrave -50
+KPX A Uhungarumlaut -50
+KPX A Umacron -50
+KPX A Uogonek -50
+KPX A Uring -50
+KPX A V -145
+KPX A W -130
+KPX A Y -100
+KPX A Yacute -100
+KPX A Ydieresis -100
+KPX A p -25
+KPX A quoteright -74
+KPX A u -50
+KPX A uacute -50
+KPX A ucircumflex -50
+KPX A udieresis -50
+KPX A ugrave -50
+KPX A uhungarumlaut -50
+KPX A umacron -50
+KPX A uogonek -50
+KPX A uring -50
+KPX A v -100
+KPX A w -90
+KPX A y -74
+KPX A yacute -74
+KPX A ydieresis -74
+KPX Aacute C -55
+KPX Aacute Cacute -55
+KPX Aacute Ccaron -55
+KPX Aacute Ccedilla -55
+KPX Aacute G -55
+KPX Aacute Gbreve -55
+KPX Aacute Gcommaaccent -55
+KPX Aacute O -45
+KPX Aacute Oacute -45
+KPX Aacute Ocircumflex -45
+KPX Aacute Odieresis -45
+KPX Aacute Ograve -45
+KPX Aacute Ohungarumlaut -45
+KPX Aacute Omacron -45
+KPX Aacute Oslash -45
+KPX Aacute Otilde -45
+KPX Aacute Q -45
+KPX Aacute T -95
+KPX Aacute Tcaron -95
+KPX Aacute Tcommaaccent -95
+KPX Aacute U -50
+KPX Aacute Uacute -50
+KPX Aacute Ucircumflex -50
+KPX Aacute Udieresis -50
+KPX Aacute Ugrave -50
+KPX Aacute Uhungarumlaut -50
+KPX Aacute Umacron -50
+KPX Aacute Uogonek -50
+KPX Aacute Uring -50
+KPX Aacute V -145
+KPX Aacute W -130
+KPX Aacute Y -100
+KPX Aacute Yacute -100
+KPX Aacute Ydieresis -100
+KPX Aacute p -25
+KPX Aacute quoteright -74
+KPX Aacute u -50
+KPX Aacute uacute -50
+KPX Aacute ucircumflex -50
+KPX Aacute udieresis -50
+KPX Aacute ugrave -50
+KPX Aacute uhungarumlaut -50
+KPX Aacute umacron -50
+KPX Aacute uogonek -50
+KPX Aacute uring -50
+KPX Aacute v -100
+KPX Aacute w -90
+KPX Aacute y -74
+KPX Aacute yacute -74
+KPX Aacute ydieresis -74
+KPX Abreve C -55
+KPX Abreve Cacute -55
+KPX Abreve Ccaron -55
+KPX Abreve Ccedilla -55
+KPX Abreve G -55
+KPX Abreve Gbreve -55
+KPX Abreve Gcommaaccent -55
+KPX Abreve O -45
+KPX Abreve Oacute -45
+KPX Abreve Ocircumflex -45
+KPX Abreve Odieresis -45
+KPX Abreve Ograve -45
+KPX Abreve Ohungarumlaut -45
+KPX Abreve Omacron -45
+KPX Abreve Oslash -45
+KPX Abreve Otilde -45
+KPX Abreve Q -45
+KPX Abreve T -95
+KPX Abreve Tcaron -95
+KPX Abreve Tcommaaccent -95
+KPX Abreve U -50
+KPX Abreve Uacute -50
+KPX Abreve Ucircumflex -50
+KPX Abreve Udieresis -50
+KPX Abreve Ugrave -50
+KPX Abreve Uhungarumlaut -50
+KPX Abreve Umacron -50
+KPX Abreve Uogonek -50
+KPX Abreve Uring -50
+KPX Abreve V -145
+KPX Abreve W -130
+KPX Abreve Y -100
+KPX Abreve Yacute -100
+KPX Abreve Ydieresis -100
+KPX Abreve p -25
+KPX Abreve quoteright -74
+KPX Abreve u -50
+KPX Abreve uacute -50
+KPX Abreve ucircumflex -50
+KPX Abreve udieresis -50
+KPX Abreve ugrave -50
+KPX Abreve uhungarumlaut -50
+KPX Abreve umacron -50
+KPX Abreve uogonek -50
+KPX Abreve uring -50
+KPX Abreve v -100
+KPX Abreve w -90
+KPX Abreve y -74
+KPX Abreve yacute -74
+KPX Abreve ydieresis -74
+KPX Acircumflex C -55
+KPX Acircumflex Cacute -55
+KPX Acircumflex Ccaron -55
+KPX Acircumflex Ccedilla -55
+KPX Acircumflex G -55
+KPX Acircumflex Gbreve -55
+KPX Acircumflex Gcommaaccent -55
+KPX Acircumflex O -45
+KPX Acircumflex Oacute -45
+KPX Acircumflex Ocircumflex -45
+KPX Acircumflex Odieresis -45
+KPX Acircumflex Ograve -45
+KPX Acircumflex Ohungarumlaut -45
+KPX Acircumflex Omacron -45
+KPX Acircumflex Oslash -45
+KPX Acircumflex Otilde -45
+KPX Acircumflex Q -45
+KPX Acircumflex T -95
+KPX Acircumflex Tcaron -95
+KPX Acircumflex Tcommaaccent -95
+KPX Acircumflex U -50
+KPX Acircumflex Uacute -50
+KPX Acircumflex Ucircumflex -50
+KPX Acircumflex Udieresis -50
+KPX Acircumflex Ugrave -50
+KPX Acircumflex Uhungarumlaut -50
+KPX Acircumflex Umacron -50
+KPX Acircumflex Uogonek -50
+KPX Acircumflex Uring -50
+KPX Acircumflex V -145
+KPX Acircumflex W -130
+KPX Acircumflex Y -100
+KPX Acircumflex Yacute -100
+KPX Acircumflex Ydieresis -100
+KPX Acircumflex p -25
+KPX Acircumflex quoteright -74
+KPX Acircumflex u -50
+KPX Acircumflex uacute -50
+KPX Acircumflex ucircumflex -50
+KPX Acircumflex udieresis -50
+KPX Acircumflex ugrave -50
+KPX Acircumflex uhungarumlaut -50
+KPX Acircumflex umacron -50
+KPX Acircumflex uogonek -50
+KPX Acircumflex uring -50
+KPX Acircumflex v -100
+KPX Acircumflex w -90
+KPX Acircumflex y -74
+KPX Acircumflex yacute -74
+KPX Acircumflex ydieresis -74
+KPX Adieresis C -55
+KPX Adieresis Cacute -55
+KPX Adieresis Ccaron -55
+KPX Adieresis Ccedilla -55
+KPX Adieresis G -55
+KPX Adieresis Gbreve -55
+KPX Adieresis Gcommaaccent -55
+KPX Adieresis O -45
+KPX Adieresis Oacute -45
+KPX Adieresis Ocircumflex -45
+KPX Adieresis Odieresis -45
+KPX Adieresis Ograve -45
+KPX Adieresis Ohungarumlaut -45
+KPX Adieresis Omacron -45
+KPX Adieresis Oslash -45
+KPX Adieresis Otilde -45
+KPX Adieresis Q -45
+KPX Adieresis T -95
+KPX Adieresis Tcaron -95
+KPX Adieresis Tcommaaccent -95
+KPX Adieresis U -50
+KPX Adieresis Uacute -50
+KPX Adieresis Ucircumflex -50
+KPX Adieresis Udieresis -50
+KPX Adieresis Ugrave -50
+KPX Adieresis Uhungarumlaut -50
+KPX Adieresis Umacron -50
+KPX Adieresis Uogonek -50
+KPX Adieresis Uring -50
+KPX Adieresis V -145
+KPX Adieresis W -130
+KPX Adieresis Y -100
+KPX Adieresis Yacute -100
+KPX Adieresis Ydieresis -100
+KPX Adieresis p -25
+KPX Adieresis quoteright -74
+KPX Adieresis u -50
+KPX Adieresis uacute -50
+KPX Adieresis ucircumflex -50
+KPX Adieresis udieresis -50
+KPX Adieresis ugrave -50
+KPX Adieresis uhungarumlaut -50
+KPX Adieresis umacron -50
+KPX Adieresis uogonek -50
+KPX Adieresis uring -50
+KPX Adieresis v -100
+KPX Adieresis w -90
+KPX Adieresis y -74
+KPX Adieresis yacute -74
+KPX Adieresis ydieresis -74
+KPX Agrave C -55
+KPX Agrave Cacute -55
+KPX Agrave Ccaron -55
+KPX Agrave Ccedilla -55
+KPX Agrave G -55
+KPX Agrave Gbreve -55
+KPX Agrave Gcommaaccent -55
+KPX Agrave O -45
+KPX Agrave Oacute -45
+KPX Agrave Ocircumflex -45
+KPX Agrave Odieresis -45
+KPX Agrave Ograve -45
+KPX Agrave Ohungarumlaut -45
+KPX Agrave Omacron -45
+KPX Agrave Oslash -45
+KPX Agrave Otilde -45
+KPX Agrave Q -45
+KPX Agrave T -95
+KPX Agrave Tcaron -95
+KPX Agrave Tcommaaccent -95
+KPX Agrave U -50
+KPX Agrave Uacute -50
+KPX Agrave Ucircumflex -50
+KPX Agrave Udieresis -50
+KPX Agrave Ugrave -50
+KPX Agrave Uhungarumlaut -50
+KPX Agrave Umacron -50
+KPX Agrave Uogonek -50
+KPX Agrave Uring -50
+KPX Agrave V -145
+KPX Agrave W -130
+KPX Agrave Y -100
+KPX Agrave Yacute -100
+KPX Agrave Ydieresis -100
+KPX Agrave p -25
+KPX Agrave quoteright -74
+KPX Agrave u -50
+KPX Agrave uacute -50
+KPX Agrave ucircumflex -50
+KPX Agrave udieresis -50
+KPX Agrave ugrave -50
+KPX Agrave uhungarumlaut -50
+KPX Agrave umacron -50
+KPX Agrave uogonek -50
+KPX Agrave uring -50
+KPX Agrave v -100
+KPX Agrave w -90
+KPX Agrave y -74
+KPX Agrave yacute -74
+KPX Agrave ydieresis -74
+KPX Amacron C -55
+KPX Amacron Cacute -55
+KPX Amacron Ccaron -55
+KPX Amacron Ccedilla -55
+KPX Amacron G -55
+KPX Amacron Gbreve -55
+KPX Amacron Gcommaaccent -55
+KPX Amacron O -45
+KPX Amacron Oacute -45
+KPX Amacron Ocircumflex -45
+KPX Amacron Odieresis -45
+KPX Amacron Ograve -45
+KPX Amacron Ohungarumlaut -45
+KPX Amacron Omacron -45
+KPX Amacron Oslash -45
+KPX Amacron Otilde -45
+KPX Amacron Q -45
+KPX Amacron T -95
+KPX Amacron Tcaron -95
+KPX Amacron Tcommaaccent -95
+KPX Amacron U -50
+KPX Amacron Uacute -50
+KPX Amacron Ucircumflex -50
+KPX Amacron Udieresis -50
+KPX Amacron Ugrave -50
+KPX Amacron Uhungarumlaut -50
+KPX Amacron Umacron -50
+KPX Amacron Uogonek -50
+KPX Amacron Uring -50
+KPX Amacron V -145
+KPX Amacron W -130
+KPX Amacron Y -100
+KPX Amacron Yacute -100
+KPX Amacron Ydieresis -100
+KPX Amacron p -25
+KPX Amacron quoteright -74
+KPX Amacron u -50
+KPX Amacron uacute -50
+KPX Amacron ucircumflex -50
+KPX Amacron udieresis -50
+KPX Amacron ugrave -50
+KPX Amacron uhungarumlaut -50
+KPX Amacron umacron -50
+KPX Amacron uogonek -50
+KPX Amacron uring -50
+KPX Amacron v -100
+KPX Amacron w -90
+KPX Amacron y -74
+KPX Amacron yacute -74
+KPX Amacron ydieresis -74
+KPX Aogonek C -55
+KPX Aogonek Cacute -55
+KPX Aogonek Ccaron -55
+KPX Aogonek Ccedilla -55
+KPX Aogonek G -55
+KPX Aogonek Gbreve -55
+KPX Aogonek Gcommaaccent -55
+KPX Aogonek O -45
+KPX Aogonek Oacute -45
+KPX Aogonek Ocircumflex -45
+KPX Aogonek Odieresis -45
+KPX Aogonek Ograve -45
+KPX Aogonek Ohungarumlaut -45
+KPX Aogonek Omacron -45
+KPX Aogonek Oslash -45
+KPX Aogonek Otilde -45
+KPX Aogonek Q -45
+KPX Aogonek T -95
+KPX Aogonek Tcaron -95
+KPX Aogonek Tcommaaccent -95
+KPX Aogonek U -50
+KPX Aogonek Uacute -50
+KPX Aogonek Ucircumflex -50
+KPX Aogonek Udieresis -50
+KPX Aogonek Ugrave -50
+KPX Aogonek Uhungarumlaut -50
+KPX Aogonek Umacron -50
+KPX Aogonek Uogonek -50
+KPX Aogonek Uring -50
+KPX Aogonek V -145
+KPX Aogonek W -130
+KPX Aogonek Y -100
+KPX Aogonek Yacute -100
+KPX Aogonek Ydieresis -100
+KPX Aogonek p -25
+KPX Aogonek quoteright -74
+KPX Aogonek u -50
+KPX Aogonek uacute -50
+KPX Aogonek ucircumflex -50
+KPX Aogonek udieresis -50
+KPX Aogonek ugrave -50
+KPX Aogonek uhungarumlaut -50
+KPX Aogonek umacron -50
+KPX Aogonek uogonek -50
+KPX Aogonek uring -50
+KPX Aogonek v -100
+KPX Aogonek w -90
+KPX Aogonek y -34
+KPX Aogonek yacute -34
+KPX Aogonek ydieresis -34
+KPX Aring C -55
+KPX Aring Cacute -55
+KPX Aring Ccaron -55
+KPX Aring Ccedilla -55
+KPX Aring G -55
+KPX Aring Gbreve -55
+KPX Aring Gcommaaccent -55
+KPX Aring O -45
+KPX Aring Oacute -45
+KPX Aring Ocircumflex -45
+KPX Aring Odieresis -45
+KPX Aring Ograve -45
+KPX Aring Ohungarumlaut -45
+KPX Aring Omacron -45
+KPX Aring Oslash -45
+KPX Aring Otilde -45
+KPX Aring Q -45
+KPX Aring T -95
+KPX Aring Tcaron -95
+KPX Aring Tcommaaccent -95
+KPX Aring U -50
+KPX Aring Uacute -50
+KPX Aring Ucircumflex -50
+KPX Aring Udieresis -50
+KPX Aring Ugrave -50
+KPX Aring Uhungarumlaut -50
+KPX Aring Umacron -50
+KPX Aring Uogonek -50
+KPX Aring Uring -50
+KPX Aring V -145
+KPX Aring W -130
+KPX Aring Y -100
+KPX Aring Yacute -100
+KPX Aring Ydieresis -100
+KPX Aring p -25
+KPX Aring quoteright -74
+KPX Aring u -50
+KPX Aring uacute -50
+KPX Aring ucircumflex -50
+KPX Aring udieresis -50
+KPX Aring ugrave -50
+KPX Aring uhungarumlaut -50
+KPX Aring umacron -50
+KPX Aring uogonek -50
+KPX Aring uring -50
+KPX Aring v -100
+KPX Aring w -90
+KPX Aring y -74
+KPX Aring yacute -74
+KPX Aring ydieresis -74
+KPX Atilde C -55
+KPX Atilde Cacute -55
+KPX Atilde Ccaron -55
+KPX Atilde Ccedilla -55
+KPX Atilde G -55
+KPX Atilde Gbreve -55
+KPX Atilde Gcommaaccent -55
+KPX Atilde O -45
+KPX Atilde Oacute -45
+KPX Atilde Ocircumflex -45
+KPX Atilde Odieresis -45
+KPX Atilde Ograve -45
+KPX Atilde Ohungarumlaut -45
+KPX Atilde Omacron -45
+KPX Atilde Oslash -45
+KPX Atilde Otilde -45
+KPX Atilde Q -45
+KPX Atilde T -95
+KPX Atilde Tcaron -95
+KPX Atilde Tcommaaccent -95
+KPX Atilde U -50
+KPX Atilde Uacute -50
+KPX Atilde Ucircumflex -50
+KPX Atilde Udieresis -50
+KPX Atilde Ugrave -50
+KPX Atilde Uhungarumlaut -50
+KPX Atilde Umacron -50
+KPX Atilde Uogonek -50
+KPX Atilde Uring -50
+KPX Atilde V -145
+KPX Atilde W -130
+KPX Atilde Y -100
+KPX Atilde Yacute -100
+KPX Atilde Ydieresis -100
+KPX Atilde p -25
+KPX Atilde quoteright -74
+KPX Atilde u -50
+KPX Atilde uacute -50
+KPX Atilde ucircumflex -50
+KPX Atilde udieresis -50
+KPX Atilde ugrave -50
+KPX Atilde uhungarumlaut -50
+KPX Atilde umacron -50
+KPX Atilde uogonek -50
+KPX Atilde uring -50
+KPX Atilde v -100
+KPX Atilde w -90
+KPX Atilde y -74
+KPX Atilde yacute -74
+KPX Atilde ydieresis -74
+KPX B A -30
+KPX B Aacute -30
+KPX B Abreve -30
+KPX B Acircumflex -30
+KPX B Adieresis -30
+KPX B Agrave -30
+KPX B Amacron -30
+KPX B Aogonek -30
+KPX B Aring -30
+KPX B Atilde -30
+KPX B U -10
+KPX B Uacute -10
+KPX B Ucircumflex -10
+KPX B Udieresis -10
+KPX B Ugrave -10
+KPX B Uhungarumlaut -10
+KPX B Umacron -10
+KPX B Uogonek -10
+KPX B Uring -10
+KPX D A -35
+KPX D Aacute -35
+KPX D Abreve -35
+KPX D Acircumflex -35
+KPX D Adieresis -35
+KPX D Agrave -35
+KPX D Amacron -35
+KPX D Aogonek -35
+KPX D Aring -35
+KPX D Atilde -35
+KPX D V -40
+KPX D W -40
+KPX D Y -40
+KPX D Yacute -40
+KPX D Ydieresis -40
+KPX D period -20
+KPX Dcaron A -35
+KPX Dcaron Aacute -35
+KPX Dcaron Abreve -35
+KPX Dcaron Acircumflex -35
+KPX Dcaron Adieresis -35
+KPX Dcaron Agrave -35
+KPX Dcaron Amacron -35
+KPX Dcaron Aogonek -35
+KPX Dcaron Aring -35
+KPX Dcaron Atilde -35
+KPX Dcaron V -40
+KPX Dcaron W -40
+KPX Dcaron Y -40
+KPX Dcaron Yacute -40
+KPX Dcaron Ydieresis -40
+KPX Dcaron period -20
+KPX Dcroat A -35
+KPX Dcroat Aacute -35
+KPX Dcroat Abreve -35
+KPX Dcroat Acircumflex -35
+KPX Dcroat Adieresis -35
+KPX Dcroat Agrave -35
+KPX Dcroat Amacron -35
+KPX Dcroat Aogonek -35
+KPX Dcroat Aring -35
+KPX Dcroat Atilde -35
+KPX Dcroat V -40
+KPX Dcroat W -40
+KPX Dcroat Y -40
+KPX Dcroat Yacute -40
+KPX Dcroat Ydieresis -40
+KPX Dcroat period -20
+KPX F A -90
+KPX F Aacute -90
+KPX F Abreve -90
+KPX F Acircumflex -90
+KPX F Adieresis -90
+KPX F Agrave -90
+KPX F Amacron -90
+KPX F Aogonek -90
+KPX F Aring -90
+KPX F Atilde -90
+KPX F a -25
+KPX F aacute -25
+KPX F abreve -25
+KPX F acircumflex -25
+KPX F adieresis -25
+KPX F agrave -25
+KPX F amacron -25
+KPX F aogonek -25
+KPX F aring -25
+KPX F atilde -25
+KPX F comma -92
+KPX F e -25
+KPX F eacute -25
+KPX F ecaron -25
+KPX F ecircumflex -25
+KPX F edieresis -25
+KPX F edotaccent -25
+KPX F egrave -25
+KPX F emacron -25
+KPX F eogonek -25
+KPX F o -25
+KPX F oacute -25
+KPX F ocircumflex -25
+KPX F odieresis -25
+KPX F ograve -25
+KPX F ohungarumlaut -25
+KPX F omacron -25
+KPX F oslash -25
+KPX F otilde -25
+KPX F period -110
+KPX J A -30
+KPX J Aacute -30
+KPX J Abreve -30
+KPX J Acircumflex -30
+KPX J Adieresis -30
+KPX J Agrave -30
+KPX J Amacron -30
+KPX J Aogonek -30
+KPX J Aring -30
+KPX J Atilde -30
+KPX J a -15
+KPX J aacute -15
+KPX J abreve -15
+KPX J acircumflex -15
+KPX J adieresis -15
+KPX J agrave -15
+KPX J amacron -15
+KPX J aogonek -15
+KPX J aring -15
+KPX J atilde -15
+KPX J e -15
+KPX J eacute -15
+KPX J ecaron -15
+KPX J ecircumflex -15
+KPX J edieresis -15
+KPX J edotaccent -15
+KPX J egrave -15
+KPX J emacron -15
+KPX J eogonek -15
+KPX J o -15
+KPX J oacute -15
+KPX J ocircumflex -15
+KPX J odieresis -15
+KPX J ograve -15
+KPX J ohungarumlaut -15
+KPX J omacron -15
+KPX J oslash -15
+KPX J otilde -15
+KPX J period -20
+KPX J u -15
+KPX J uacute -15
+KPX J ucircumflex -15
+KPX J udieresis -15
+KPX J ugrave -15
+KPX J uhungarumlaut -15
+KPX J umacron -15
+KPX J uogonek -15
+KPX J uring -15
+KPX K O -30
+KPX K Oacute -30
+KPX K Ocircumflex -30
+KPX K Odieresis -30
+KPX K Ograve -30
+KPX K Ohungarumlaut -30
+KPX K Omacron -30
+KPX K Oslash -30
+KPX K Otilde -30
+KPX K e -25
+KPX K eacute -25
+KPX K ecaron -25
+KPX K ecircumflex -25
+KPX K edieresis -25
+KPX K edotaccent -25
+KPX K egrave -25
+KPX K emacron -25
+KPX K eogonek -25
+KPX K o -25
+KPX K oacute -25
+KPX K ocircumflex -25
+KPX K odieresis -25
+KPX K ograve -25
+KPX K ohungarumlaut -25
+KPX K omacron -25
+KPX K oslash -25
+KPX K otilde -25
+KPX K u -15
+KPX K uacute -15
+KPX K ucircumflex -15
+KPX K udieresis -15
+KPX K ugrave -15
+KPX K uhungarumlaut -15
+KPX K umacron -15
+KPX K uogonek -15
+KPX K uring -15
+KPX K y -45
+KPX K yacute -45
+KPX K ydieresis -45
+KPX Kcommaaccent O -30
+KPX Kcommaaccent Oacute -30
+KPX Kcommaaccent Ocircumflex -30
+KPX Kcommaaccent Odieresis -30
+KPX Kcommaaccent Ograve -30
+KPX Kcommaaccent Ohungarumlaut -30
+KPX Kcommaaccent Omacron -30
+KPX Kcommaaccent Oslash -30
+KPX Kcommaaccent Otilde -30
+KPX Kcommaaccent e -25
+KPX Kcommaaccent eacute -25
+KPX Kcommaaccent ecaron -25
+KPX Kcommaaccent ecircumflex -25
+KPX Kcommaaccent edieresis -25
+KPX Kcommaaccent edotaccent -25
+KPX Kcommaaccent egrave -25
+KPX Kcommaaccent emacron -25
+KPX Kcommaaccent eogonek -25
+KPX Kcommaaccent o -25
+KPX Kcommaaccent oacute -25
+KPX Kcommaaccent ocircumflex -25
+KPX Kcommaaccent odieresis -25
+KPX Kcommaaccent ograve -25
+KPX Kcommaaccent ohungarumlaut -25
+KPX Kcommaaccent omacron -25
+KPX Kcommaaccent oslash -25
+KPX Kcommaaccent otilde -25
+KPX Kcommaaccent u -15
+KPX Kcommaaccent uacute -15
+KPX Kcommaaccent ucircumflex -15
+KPX Kcommaaccent udieresis -15
+KPX Kcommaaccent ugrave -15
+KPX Kcommaaccent uhungarumlaut -15
+KPX Kcommaaccent umacron -15
+KPX Kcommaaccent uogonek -15
+KPX Kcommaaccent uring -15
+KPX Kcommaaccent y -45
+KPX Kcommaaccent yacute -45
+KPX Kcommaaccent ydieresis -45
+KPX L T -92
+KPX L Tcaron -92
+KPX L Tcommaaccent -92
+KPX L V -92
+KPX L W -92
+KPX L Y -92
+KPX L Yacute -92
+KPX L Ydieresis -92
+KPX L quotedblright -20
+KPX L quoteright -110
+KPX L y -55
+KPX L yacute -55
+KPX L ydieresis -55
+KPX Lacute T -92
+KPX Lacute Tcaron -92
+KPX Lacute Tcommaaccent -92
+KPX Lacute V -92
+KPX Lacute W -92
+KPX Lacute Y -92
+KPX Lacute Yacute -92
+KPX Lacute Ydieresis -92
+KPX Lacute quotedblright -20
+KPX Lacute quoteright -110
+KPX Lacute y -55
+KPX Lacute yacute -55
+KPX Lacute ydieresis -55
+KPX Lcommaaccent T -92
+KPX Lcommaaccent Tcaron -92
+KPX Lcommaaccent Tcommaaccent -92
+KPX Lcommaaccent V -92
+KPX Lcommaaccent W -92
+KPX Lcommaaccent Y -92
+KPX Lcommaaccent Yacute -92
+KPX Lcommaaccent Ydieresis -92
+KPX Lcommaaccent quotedblright -20
+KPX Lcommaaccent quoteright -110
+KPX Lcommaaccent y -55
+KPX Lcommaaccent yacute -55
+KPX Lcommaaccent ydieresis -55
+KPX Lslash T -92
+KPX Lslash Tcaron -92
+KPX Lslash Tcommaaccent -92
+KPX Lslash V -92
+KPX Lslash W -92
+KPX Lslash Y -92
+KPX Lslash Yacute -92
+KPX Lslash Ydieresis -92
+KPX Lslash quotedblright -20
+KPX Lslash quoteright -110
+KPX Lslash y -55
+KPX Lslash yacute -55
+KPX Lslash ydieresis -55
+KPX N A -20
+KPX N Aacute -20
+KPX N Abreve -20
+KPX N Acircumflex -20
+KPX N Adieresis -20
+KPX N Agrave -20
+KPX N Amacron -20
+KPX N Aogonek -20
+KPX N Aring -20
+KPX N Atilde -20
+KPX Nacute A -20
+KPX Nacute Aacute -20
+KPX Nacute Abreve -20
+KPX Nacute Acircumflex -20
+KPX Nacute Adieresis -20
+KPX Nacute Agrave -20
+KPX Nacute Amacron -20
+KPX Nacute Aogonek -20
+KPX Nacute Aring -20
+KPX Nacute Atilde -20
+KPX Ncaron A -20
+KPX Ncaron Aacute -20
+KPX Ncaron Abreve -20
+KPX Ncaron Acircumflex -20
+KPX Ncaron Adieresis -20
+KPX Ncaron Agrave -20
+KPX Ncaron Amacron -20
+KPX Ncaron Aogonek -20
+KPX Ncaron Aring -20
+KPX Ncaron Atilde -20
+KPX Ncommaaccent A -20
+KPX Ncommaaccent Aacute -20
+KPX Ncommaaccent Abreve -20
+KPX Ncommaaccent Acircumflex -20
+KPX Ncommaaccent Adieresis -20
+KPX Ncommaaccent Agrave -20
+KPX Ncommaaccent Amacron -20
+KPX Ncommaaccent Aogonek -20
+KPX Ncommaaccent Aring -20
+KPX Ncommaaccent Atilde -20
+KPX Ntilde A -20
+KPX Ntilde Aacute -20
+KPX Ntilde Abreve -20
+KPX Ntilde Acircumflex -20
+KPX Ntilde Adieresis -20
+KPX Ntilde Agrave -20
+KPX Ntilde Amacron -20
+KPX Ntilde Aogonek -20
+KPX Ntilde Aring -20
+KPX Ntilde Atilde -20
+KPX O A -40
+KPX O Aacute -40
+KPX O Abreve -40
+KPX O Acircumflex -40
+KPX O Adieresis -40
+KPX O Agrave -40
+KPX O Amacron -40
+KPX O Aogonek -40
+KPX O Aring -40
+KPX O Atilde -40
+KPX O T -40
+KPX O Tcaron -40
+KPX O Tcommaaccent -40
+KPX O V -50
+KPX O W -50
+KPX O X -40
+KPX O Y -50
+KPX O Yacute -50
+KPX O Ydieresis -50
+KPX Oacute A -40
+KPX Oacute Aacute -40
+KPX Oacute Abreve -40
+KPX Oacute Acircumflex -40
+KPX Oacute Adieresis -40
+KPX Oacute Agrave -40
+KPX Oacute Amacron -40
+KPX Oacute Aogonek -40
+KPX Oacute Aring -40
+KPX Oacute Atilde -40
+KPX Oacute T -40
+KPX Oacute Tcaron -40
+KPX Oacute Tcommaaccent -40
+KPX Oacute V -50
+KPX Oacute W -50
+KPX Oacute X -40
+KPX Oacute Y -50
+KPX Oacute Yacute -50
+KPX Oacute Ydieresis -50
+KPX Ocircumflex A -40
+KPX Ocircumflex Aacute -40
+KPX Ocircumflex Abreve -40
+KPX Ocircumflex Acircumflex -40
+KPX Ocircumflex Adieresis -40
+KPX Ocircumflex Agrave -40
+KPX Ocircumflex Amacron -40
+KPX Ocircumflex Aogonek -40
+KPX Ocircumflex Aring -40
+KPX Ocircumflex Atilde -40
+KPX Ocircumflex T -40
+KPX Ocircumflex Tcaron -40
+KPX Ocircumflex Tcommaaccent -40
+KPX Ocircumflex V -50
+KPX Ocircumflex W -50
+KPX Ocircumflex X -40
+KPX Ocircumflex Y -50
+KPX Ocircumflex Yacute -50
+KPX Ocircumflex Ydieresis -50
+KPX Odieresis A -40
+KPX Odieresis Aacute -40
+KPX Odieresis Abreve -40
+KPX Odieresis Acircumflex -40
+KPX Odieresis Adieresis -40
+KPX Odieresis Agrave -40
+KPX Odieresis Amacron -40
+KPX Odieresis Aogonek -40
+KPX Odieresis Aring -40
+KPX Odieresis Atilde -40
+KPX Odieresis T -40
+KPX Odieresis Tcaron -40
+KPX Odieresis Tcommaaccent -40
+KPX Odieresis V -50
+KPX Odieresis W -50
+KPX Odieresis X -40
+KPX Odieresis Y -50
+KPX Odieresis Yacute -50
+KPX Odieresis Ydieresis -50
+KPX Ograve A -40
+KPX Ograve Aacute -40
+KPX Ograve Abreve -40
+KPX Ograve Acircumflex -40
+KPX Ograve Adieresis -40
+KPX Ograve Agrave -40
+KPX Ograve Amacron -40
+KPX Ograve Aogonek -40
+KPX Ograve Aring -40
+KPX Ograve Atilde -40
+KPX Ograve T -40
+KPX Ograve Tcaron -40
+KPX Ograve Tcommaaccent -40
+KPX Ograve V -50
+KPX Ograve W -50
+KPX Ograve X -40
+KPX Ograve Y -50
+KPX Ograve Yacute -50
+KPX Ograve Ydieresis -50
+KPX Ohungarumlaut A -40
+KPX Ohungarumlaut Aacute -40
+KPX Ohungarumlaut Abreve -40
+KPX Ohungarumlaut Acircumflex -40
+KPX Ohungarumlaut Adieresis -40
+KPX Ohungarumlaut Agrave -40
+KPX Ohungarumlaut Amacron -40
+KPX Ohungarumlaut Aogonek -40
+KPX Ohungarumlaut Aring -40
+KPX Ohungarumlaut Atilde -40
+KPX Ohungarumlaut T -40
+KPX Ohungarumlaut Tcaron -40
+KPX Ohungarumlaut Tcommaaccent -40
+KPX Ohungarumlaut V -50
+KPX Ohungarumlaut W -50
+KPX Ohungarumlaut X -40
+KPX Ohungarumlaut Y -50
+KPX Ohungarumlaut Yacute -50
+KPX Ohungarumlaut Ydieresis -50
+KPX Omacron A -40
+KPX Omacron Aacute -40
+KPX Omacron Abreve -40
+KPX Omacron Acircumflex -40
+KPX Omacron Adieresis -40
+KPX Omacron Agrave -40
+KPX Omacron Amacron -40
+KPX Omacron Aogonek -40
+KPX Omacron Aring -40
+KPX Omacron Atilde -40
+KPX Omacron T -40
+KPX Omacron Tcaron -40
+KPX Omacron Tcommaaccent -40
+KPX Omacron V -50
+KPX Omacron W -50
+KPX Omacron X -40
+KPX Omacron Y -50
+KPX Omacron Yacute -50
+KPX Omacron Ydieresis -50
+KPX Oslash A -40
+KPX Oslash Aacute -40
+KPX Oslash Abreve -40
+KPX Oslash Acircumflex -40
+KPX Oslash Adieresis -40
+KPX Oslash Agrave -40
+KPX Oslash Amacron -40
+KPX Oslash Aogonek -40
+KPX Oslash Aring -40
+KPX Oslash Atilde -40
+KPX Oslash T -40
+KPX Oslash Tcaron -40
+KPX Oslash Tcommaaccent -40
+KPX Oslash V -50
+KPX Oslash W -50
+KPX Oslash X -40
+KPX Oslash Y -50
+KPX Oslash Yacute -50
+KPX Oslash Ydieresis -50
+KPX Otilde A -40
+KPX Otilde Aacute -40
+KPX Otilde Abreve -40
+KPX Otilde Acircumflex -40
+KPX Otilde Adieresis -40
+KPX Otilde Agrave -40
+KPX Otilde Amacron -40
+KPX Otilde Aogonek -40
+KPX Otilde Aring -40
+KPX Otilde Atilde -40
+KPX Otilde T -40
+KPX Otilde Tcaron -40
+KPX Otilde Tcommaaccent -40
+KPX Otilde V -50
+KPX Otilde W -50
+KPX Otilde X -40
+KPX Otilde Y -50
+KPX Otilde Yacute -50
+KPX Otilde Ydieresis -50
+KPX P A -74
+KPX P Aacute -74
+KPX P Abreve -74
+KPX P Acircumflex -74
+KPX P Adieresis -74
+KPX P Agrave -74
+KPX P Amacron -74
+KPX P Aogonek -74
+KPX P Aring -74
+KPX P Atilde -74
+KPX P a -10
+KPX P aacute -10
+KPX P abreve -10
+KPX P acircumflex -10
+KPX P adieresis -10
+KPX P agrave -10
+KPX P amacron -10
+KPX P aogonek -10
+KPX P aring -10
+KPX P atilde -10
+KPX P comma -92
+KPX P e -20
+KPX P eacute -20
+KPX P ecaron -20
+KPX P ecircumflex -20
+KPX P edieresis -20
+KPX P edotaccent -20
+KPX P egrave -20
+KPX P emacron -20
+KPX P eogonek -20
+KPX P o -20
+KPX P oacute -20
+KPX P ocircumflex -20
+KPX P odieresis -20
+KPX P ograve -20
+KPX P ohungarumlaut -20
+KPX P omacron -20
+KPX P oslash -20
+KPX P otilde -20
+KPX P period -110
+KPX Q U -10
+KPX Q Uacute -10
+KPX Q Ucircumflex -10
+KPX Q Udieresis -10
+KPX Q Ugrave -10
+KPX Q Uhungarumlaut -10
+KPX Q Umacron -10
+KPX Q Uogonek -10
+KPX Q Uring -10
+KPX Q period -20
+KPX R O -30
+KPX R Oacute -30
+KPX R Ocircumflex -30
+KPX R Odieresis -30
+KPX R Ograve -30
+KPX R Ohungarumlaut -30
+KPX R Omacron -30
+KPX R Oslash -30
+KPX R Otilde -30
+KPX R T -40
+KPX R Tcaron -40
+KPX R Tcommaaccent -40
+KPX R U -30
+KPX R Uacute -30
+KPX R Ucircumflex -30
+KPX R Udieresis -30
+KPX R Ugrave -30
+KPX R Uhungarumlaut -30
+KPX R Umacron -30
+KPX R Uogonek -30
+KPX R Uring -30
+KPX R V -55
+KPX R W -35
+KPX R Y -35
+KPX R Yacute -35
+KPX R Ydieresis -35
+KPX Racute O -30
+KPX Racute Oacute -30
+KPX Racute Ocircumflex -30
+KPX Racute Odieresis -30
+KPX Racute Ograve -30
+KPX Racute Ohungarumlaut -30
+KPX Racute Omacron -30
+KPX Racute Oslash -30
+KPX Racute Otilde -30
+KPX Racute T -40
+KPX Racute Tcaron -40
+KPX Racute Tcommaaccent -40
+KPX Racute U -30
+KPX Racute Uacute -30
+KPX Racute Ucircumflex -30
+KPX Racute Udieresis -30
+KPX Racute Ugrave -30
+KPX Racute Uhungarumlaut -30
+KPX Racute Umacron -30
+KPX Racute Uogonek -30
+KPX Racute Uring -30
+KPX Racute V -55
+KPX Racute W -35
+KPX Racute Y -35
+KPX Racute Yacute -35
+KPX Racute Ydieresis -35
+KPX Rcaron O -30
+KPX Rcaron Oacute -30
+KPX Rcaron Ocircumflex -30
+KPX Rcaron Odieresis -30
+KPX Rcaron Ograve -30
+KPX Rcaron Ohungarumlaut -30
+KPX Rcaron Omacron -30
+KPX Rcaron Oslash -30
+KPX Rcaron Otilde -30
+KPX Rcaron T -40
+KPX Rcaron Tcaron -40
+KPX Rcaron Tcommaaccent -40
+KPX Rcaron U -30
+KPX Rcaron Uacute -30
+KPX Rcaron Ucircumflex -30
+KPX Rcaron Udieresis -30
+KPX Rcaron Ugrave -30
+KPX Rcaron Uhungarumlaut -30
+KPX Rcaron Umacron -30
+KPX Rcaron Uogonek -30
+KPX Rcaron Uring -30
+KPX Rcaron V -55
+KPX Rcaron W -35
+KPX Rcaron Y -35
+KPX Rcaron Yacute -35
+KPX Rcaron Ydieresis -35
+KPX Rcommaaccent O -30
+KPX Rcommaaccent Oacute -30
+KPX Rcommaaccent Ocircumflex -30
+KPX Rcommaaccent Odieresis -30
+KPX Rcommaaccent Ograve -30
+KPX Rcommaaccent Ohungarumlaut -30
+KPX Rcommaaccent Omacron -30
+KPX Rcommaaccent Oslash -30
+KPX Rcommaaccent Otilde -30
+KPX Rcommaaccent T -40
+KPX Rcommaaccent Tcaron -40
+KPX Rcommaaccent Tcommaaccent -40
+KPX Rcommaaccent U -30
+KPX Rcommaaccent Uacute -30
+KPX Rcommaaccent Ucircumflex -30
+KPX Rcommaaccent Udieresis -30
+KPX Rcommaaccent Ugrave -30
+KPX Rcommaaccent Uhungarumlaut -30
+KPX Rcommaaccent Umacron -30
+KPX Rcommaaccent Uogonek -30
+KPX Rcommaaccent Uring -30
+KPX Rcommaaccent V -55
+KPX Rcommaaccent W -35
+KPX Rcommaaccent Y -35
+KPX Rcommaaccent Yacute -35
+KPX Rcommaaccent Ydieresis -35
+KPX T A -90
+KPX T Aacute -90
+KPX T Abreve -90
+KPX T Acircumflex -90
+KPX T Adieresis -90
+KPX T Agrave -90
+KPX T Amacron -90
+KPX T Aogonek -90
+KPX T Aring -90
+KPX T Atilde -90
+KPX T O -18
+KPX T Oacute -18
+KPX T Ocircumflex -18
+KPX T Odieresis -18
+KPX T Ograve -18
+KPX T Ohungarumlaut -18
+KPX T Omacron -18
+KPX T Oslash -18
+KPX T Otilde -18
+KPX T a -92
+KPX T aacute -92
+KPX T abreve -52
+KPX T acircumflex -52
+KPX T adieresis -52
+KPX T agrave -52
+KPX T amacron -52
+KPX T aogonek -92
+KPX T aring -92
+KPX T atilde -52
+KPX T colon -74
+KPX T comma -74
+KPX T e -92
+KPX T eacute -92
+KPX T ecaron -92
+KPX T ecircumflex -92
+KPX T edieresis -52
+KPX T edotaccent -92
+KPX T egrave -52
+KPX T emacron -52
+KPX T eogonek -92
+KPX T hyphen -92
+KPX T i -18
+KPX T iacute -18
+KPX T iogonek -18
+KPX T o -92
+KPX T oacute -92
+KPX T ocircumflex -92
+KPX T odieresis -92
+KPX T ograve -92
+KPX T ohungarumlaut -92
+KPX T omacron -92
+KPX T oslash -92
+KPX T otilde -92
+KPX T period -90
+KPX T r -74
+KPX T racute -74
+KPX T rcaron -74
+KPX T rcommaaccent -74
+KPX T semicolon -74
+KPX T u -92
+KPX T uacute -92
+KPX T ucircumflex -92
+KPX T udieresis -92
+KPX T ugrave -92
+KPX T uhungarumlaut -92
+KPX T umacron -92
+KPX T uogonek -92
+KPX T uring -92
+KPX T w -74
+KPX T y -34
+KPX T yacute -34
+KPX T ydieresis -34
+KPX Tcaron A -90
+KPX Tcaron Aacute -90
+KPX Tcaron Abreve -90
+KPX Tcaron Acircumflex -90
+KPX Tcaron Adieresis -90
+KPX Tcaron Agrave -90
+KPX Tcaron Amacron -90
+KPX Tcaron Aogonek -90
+KPX Tcaron Aring -90
+KPX Tcaron Atilde -90
+KPX Tcaron O -18
+KPX Tcaron Oacute -18
+KPX Tcaron Ocircumflex -18
+KPX Tcaron Odieresis -18
+KPX Tcaron Ograve -18
+KPX Tcaron Ohungarumlaut -18
+KPX Tcaron Omacron -18
+KPX Tcaron Oslash -18
+KPX Tcaron Otilde -18
+KPX Tcaron a -92
+KPX Tcaron aacute -92
+KPX Tcaron abreve -52
+KPX Tcaron acircumflex -52
+KPX Tcaron adieresis -52
+KPX Tcaron agrave -52
+KPX Tcaron amacron -52
+KPX Tcaron aogonek -92
+KPX Tcaron aring -92
+KPX Tcaron atilde -52
+KPX Tcaron colon -74
+KPX Tcaron comma -74
+KPX Tcaron e -92
+KPX Tcaron eacute -92
+KPX Tcaron ecaron -92
+KPX Tcaron ecircumflex -92
+KPX Tcaron edieresis -52
+KPX Tcaron edotaccent -92
+KPX Tcaron egrave -52
+KPX Tcaron emacron -52
+KPX Tcaron eogonek -92
+KPX Tcaron hyphen -92
+KPX Tcaron i -18
+KPX Tcaron iacute -18
+KPX Tcaron iogonek -18
+KPX Tcaron o -92
+KPX Tcaron oacute -92
+KPX Tcaron ocircumflex -92
+KPX Tcaron odieresis -92
+KPX Tcaron ograve -92
+KPX Tcaron ohungarumlaut -92
+KPX Tcaron omacron -92
+KPX Tcaron oslash -92
+KPX Tcaron otilde -92
+KPX Tcaron period -90
+KPX Tcaron r -74
+KPX Tcaron racute -74
+KPX Tcaron rcaron -74
+KPX Tcaron rcommaaccent -74
+KPX Tcaron semicolon -74
+KPX Tcaron u -92
+KPX Tcaron uacute -92
+KPX Tcaron ucircumflex -92
+KPX Tcaron udieresis -92
+KPX Tcaron ugrave -92
+KPX Tcaron uhungarumlaut -92
+KPX Tcaron umacron -92
+KPX Tcaron uogonek -92
+KPX Tcaron uring -92
+KPX Tcaron w -74
+KPX Tcaron y -34
+KPX Tcaron yacute -34
+KPX Tcaron ydieresis -34
+KPX Tcommaaccent A -90
+KPX Tcommaaccent Aacute -90
+KPX Tcommaaccent Abreve -90
+KPX Tcommaaccent Acircumflex -90
+KPX Tcommaaccent Adieresis -90
+KPX Tcommaaccent Agrave -90
+KPX Tcommaaccent Amacron -90
+KPX Tcommaaccent Aogonek -90
+KPX Tcommaaccent Aring -90
+KPX Tcommaaccent Atilde -90
+KPX Tcommaaccent O -18
+KPX Tcommaaccent Oacute -18
+KPX Tcommaaccent Ocircumflex -18
+KPX Tcommaaccent Odieresis -18
+KPX Tcommaaccent Ograve -18
+KPX Tcommaaccent Ohungarumlaut -18
+KPX Tcommaaccent Omacron -18
+KPX Tcommaaccent Oslash -18
+KPX Tcommaaccent Otilde -18
+KPX Tcommaaccent a -92
+KPX Tcommaaccent aacute -92
+KPX Tcommaaccent abreve -52
+KPX Tcommaaccent acircumflex -52
+KPX Tcommaaccent adieresis -52
+KPX Tcommaaccent agrave -52
+KPX Tcommaaccent amacron -52
+KPX Tcommaaccent aogonek -92
+KPX Tcommaaccent aring -92
+KPX Tcommaaccent atilde -52
+KPX Tcommaaccent colon -74
+KPX Tcommaaccent comma -74
+KPX Tcommaaccent e -92
+KPX Tcommaaccent eacute -92
+KPX Tcommaaccent ecaron -92
+KPX Tcommaaccent ecircumflex -92
+KPX Tcommaaccent edieresis -52
+KPX Tcommaaccent edotaccent -92
+KPX Tcommaaccent egrave -52
+KPX Tcommaaccent emacron -52
+KPX Tcommaaccent eogonek -92
+KPX Tcommaaccent hyphen -92
+KPX Tcommaaccent i -18
+KPX Tcommaaccent iacute -18
+KPX Tcommaaccent iogonek -18
+KPX Tcommaaccent o -92
+KPX Tcommaaccent oacute -92
+KPX Tcommaaccent ocircumflex -92
+KPX Tcommaaccent odieresis -92
+KPX Tcommaaccent ograve -92
+KPX Tcommaaccent ohungarumlaut -92
+KPX Tcommaaccent omacron -92
+KPX Tcommaaccent oslash -92
+KPX Tcommaaccent otilde -92
+KPX Tcommaaccent period -90
+KPX Tcommaaccent r -74
+KPX Tcommaaccent racute -74
+KPX Tcommaaccent rcaron -74
+KPX Tcommaaccent rcommaaccent -74
+KPX Tcommaaccent semicolon -74
+KPX Tcommaaccent u -92
+KPX Tcommaaccent uacute -92
+KPX Tcommaaccent ucircumflex -92
+KPX Tcommaaccent udieresis -92
+KPX Tcommaaccent ugrave -92
+KPX Tcommaaccent uhungarumlaut -92
+KPX Tcommaaccent umacron -92
+KPX Tcommaaccent uogonek -92
+KPX Tcommaaccent uring -92
+KPX Tcommaaccent w -74
+KPX Tcommaaccent y -34
+KPX Tcommaaccent yacute -34
+KPX Tcommaaccent ydieresis -34
+KPX U A -60
+KPX U Aacute -60
+KPX U Abreve -60
+KPX U Acircumflex -60
+KPX U Adieresis -60
+KPX U Agrave -60
+KPX U Amacron -60
+KPX U Aogonek -60
+KPX U Aring -60
+KPX U Atilde -60
+KPX U comma -50
+KPX U period -50
+KPX Uacute A -60
+KPX Uacute Aacute -60
+KPX Uacute Abreve -60
+KPX Uacute Acircumflex -60
+KPX Uacute Adieresis -60
+KPX Uacute Agrave -60
+KPX Uacute Amacron -60
+KPX Uacute Aogonek -60
+KPX Uacute Aring -60
+KPX Uacute Atilde -60
+KPX Uacute comma -50
+KPX Uacute period -50
+KPX Ucircumflex A -60
+KPX Ucircumflex Aacute -60
+KPX Ucircumflex Abreve -60
+KPX Ucircumflex Acircumflex -60
+KPX Ucircumflex Adieresis -60
+KPX Ucircumflex Agrave -60
+KPX Ucircumflex Amacron -60
+KPX Ucircumflex Aogonek -60
+KPX Ucircumflex Aring -60
+KPX Ucircumflex Atilde -60
+KPX Ucircumflex comma -50
+KPX Ucircumflex period -50
+KPX Udieresis A -60
+KPX Udieresis Aacute -60
+KPX Udieresis Abreve -60
+KPX Udieresis Acircumflex -60
+KPX Udieresis Adieresis -60
+KPX Udieresis Agrave -60
+KPX Udieresis Amacron -60
+KPX Udieresis Aogonek -60
+KPX Udieresis Aring -60
+KPX Udieresis Atilde -60
+KPX Udieresis comma -50
+KPX Udieresis period -50
+KPX Ugrave A -60
+KPX Ugrave Aacute -60
+KPX Ugrave Abreve -60
+KPX Ugrave Acircumflex -60
+KPX Ugrave Adieresis -60
+KPX Ugrave Agrave -60
+KPX Ugrave Amacron -60
+KPX Ugrave Aogonek -60
+KPX Ugrave Aring -60
+KPX Ugrave Atilde -60
+KPX Ugrave comma -50
+KPX Ugrave period -50
+KPX Uhungarumlaut A -60
+KPX Uhungarumlaut Aacute -60
+KPX Uhungarumlaut Abreve -60
+KPX Uhungarumlaut Acircumflex -60
+KPX Uhungarumlaut Adieresis -60
+KPX Uhungarumlaut Agrave -60
+KPX Uhungarumlaut Amacron -60
+KPX Uhungarumlaut Aogonek -60
+KPX Uhungarumlaut Aring -60
+KPX Uhungarumlaut Atilde -60
+KPX Uhungarumlaut comma -50
+KPX Uhungarumlaut period -50
+KPX Umacron A -60
+KPX Umacron Aacute -60
+KPX Umacron Abreve -60
+KPX Umacron Acircumflex -60
+KPX Umacron Adieresis -60
+KPX Umacron Agrave -60
+KPX Umacron Amacron -60
+KPX Umacron Aogonek -60
+KPX Umacron Aring -60
+KPX Umacron Atilde -60
+KPX Umacron comma -50
+KPX Umacron period -50
+KPX Uogonek A -60
+KPX Uogonek Aacute -60
+KPX Uogonek Abreve -60
+KPX Uogonek Acircumflex -60
+KPX Uogonek Adieresis -60
+KPX Uogonek Agrave -60
+KPX Uogonek Amacron -60
+KPX Uogonek Aogonek -60
+KPX Uogonek Aring -60
+KPX Uogonek Atilde -60
+KPX Uogonek comma -50
+KPX Uogonek period -50
+KPX Uring A -60
+KPX Uring Aacute -60
+KPX Uring Abreve -60
+KPX Uring Acircumflex -60
+KPX Uring Adieresis -60
+KPX Uring Agrave -60
+KPX Uring Amacron -60
+KPX Uring Aogonek -60
+KPX Uring Aring -60
+KPX Uring Atilde -60
+KPX Uring comma -50
+KPX Uring period -50
+KPX V A -135
+KPX V Aacute -135
+KPX V Abreve -135
+KPX V Acircumflex -135
+KPX V Adieresis -135
+KPX V Agrave -135
+KPX V Amacron -135
+KPX V Aogonek -135
+KPX V Aring -135
+KPX V Atilde -135
+KPX V G -30
+KPX V Gbreve -30
+KPX V Gcommaaccent -30
+KPX V O -45
+KPX V Oacute -45
+KPX V Ocircumflex -45
+KPX V Odieresis -45
+KPX V Ograve -45
+KPX V Ohungarumlaut -45
+KPX V Omacron -45
+KPX V Oslash -45
+KPX V Otilde -45
+KPX V a -92
+KPX V aacute -92
+KPX V abreve -92
+KPX V acircumflex -92
+KPX V adieresis -92
+KPX V agrave -92
+KPX V amacron -92
+KPX V aogonek -92
+KPX V aring -92
+KPX V atilde -92
+KPX V colon -92
+KPX V comma -129
+KPX V e -100
+KPX V eacute -100
+KPX V ecaron -100
+KPX V ecircumflex -100
+KPX V edieresis -100
+KPX V edotaccent -100
+KPX V egrave -100
+KPX V emacron -100
+KPX V eogonek -100
+KPX V hyphen -74
+KPX V i -37
+KPX V iacute -37
+KPX V icircumflex -37
+KPX V idieresis -37
+KPX V igrave -37
+KPX V imacron -37
+KPX V iogonek -37
+KPX V o -100
+KPX V oacute -100
+KPX V ocircumflex -100
+KPX V odieresis -100
+KPX V ograve -100
+KPX V ohungarumlaut -100
+KPX V omacron -100
+KPX V oslash -100
+KPX V otilde -100
+KPX V period -145
+KPX V semicolon -92
+KPX V u -92
+KPX V uacute -92
+KPX V ucircumflex -92
+KPX V udieresis -92
+KPX V ugrave -92
+KPX V uhungarumlaut -92
+KPX V umacron -92
+KPX V uogonek -92
+KPX V uring -92
+KPX W A -120
+KPX W Aacute -120
+KPX W Abreve -120
+KPX W Acircumflex -120
+KPX W Adieresis -120
+KPX W Agrave -120
+KPX W Amacron -120
+KPX W Aogonek -120
+KPX W Aring -120
+KPX W Atilde -120
+KPX W O -10
+KPX W Oacute -10
+KPX W Ocircumflex -10
+KPX W Odieresis -10
+KPX W Ograve -10
+KPX W Ohungarumlaut -10
+KPX W Omacron -10
+KPX W Oslash -10
+KPX W Otilde -10
+KPX W a -65
+KPX W aacute -65
+KPX W abreve -65
+KPX W acircumflex -65
+KPX W adieresis -65
+KPX W agrave -65
+KPX W amacron -65
+KPX W aogonek -65
+KPX W aring -65
+KPX W atilde -65
+KPX W colon -55
+KPX W comma -92
+KPX W e -65
+KPX W eacute -65
+KPX W ecaron -65
+KPX W ecircumflex -65
+KPX W edieresis -65
+KPX W edotaccent -65
+KPX W egrave -65
+KPX W emacron -65
+KPX W eogonek -65
+KPX W hyphen -37
+KPX W i -18
+KPX W iacute -18
+KPX W iogonek -18
+KPX W o -75
+KPX W oacute -75
+KPX W ocircumflex -75
+KPX W odieresis -75
+KPX W ograve -75
+KPX W ohungarumlaut -75
+KPX W omacron -75
+KPX W oslash -75
+KPX W otilde -75
+KPX W period -92
+KPX W semicolon -55
+KPX W u -50
+KPX W uacute -50
+KPX W ucircumflex -50
+KPX W udieresis -50
+KPX W ugrave -50
+KPX W uhungarumlaut -50
+KPX W umacron -50
+KPX W uogonek -50
+KPX W uring -50
+KPX W y -60
+KPX W yacute -60
+KPX W ydieresis -60
+KPX Y A -110
+KPX Y Aacute -110
+KPX Y Abreve -110
+KPX Y Acircumflex -110
+KPX Y Adieresis -110
+KPX Y Agrave -110
+KPX Y Amacron -110
+KPX Y Aogonek -110
+KPX Y Aring -110
+KPX Y Atilde -110
+KPX Y O -35
+KPX Y Oacute -35
+KPX Y Ocircumflex -35
+KPX Y Odieresis -35
+KPX Y Ograve -35
+KPX Y Ohungarumlaut -35
+KPX Y Omacron -35
+KPX Y Oslash -35
+KPX Y Otilde -35
+KPX Y a -85
+KPX Y aacute -85
+KPX Y abreve -85
+KPX Y acircumflex -85
+KPX Y adieresis -85
+KPX Y agrave -85
+KPX Y amacron -85
+KPX Y aogonek -85
+KPX Y aring -85
+KPX Y atilde -85
+KPX Y colon -92
+KPX Y comma -92
+KPX Y e -111
+KPX Y eacute -111
+KPX Y ecaron -111
+KPX Y ecircumflex -111
+KPX Y edieresis -71
+KPX Y edotaccent -111
+KPX Y egrave -71
+KPX Y emacron -71
+KPX Y eogonek -111
+KPX Y hyphen -92
+KPX Y i -37
+KPX Y iacute -37
+KPX Y iogonek -37
+KPX Y o -111
+KPX Y oacute -111
+KPX Y ocircumflex -111
+KPX Y odieresis -111
+KPX Y ograve -111
+KPX Y ohungarumlaut -111
+KPX Y omacron -111
+KPX Y oslash -111
+KPX Y otilde -111
+KPX Y period -92
+KPX Y semicolon -92
+KPX Y u -92
+KPX Y uacute -92
+KPX Y ucircumflex -92
+KPX Y udieresis -92
+KPX Y ugrave -92
+KPX Y uhungarumlaut -92
+KPX Y umacron -92
+KPX Y uogonek -92
+KPX Y uring -92
+KPX Yacute A -110
+KPX Yacute Aacute -110
+KPX Yacute Abreve -110
+KPX Yacute Acircumflex -110
+KPX Yacute Adieresis -110
+KPX Yacute Agrave -110
+KPX Yacute Amacron -110
+KPX Yacute Aogonek -110
+KPX Yacute Aring -110
+KPX Yacute Atilde -110
+KPX Yacute O -35
+KPX Yacute Oacute -35
+KPX Yacute Ocircumflex -35
+KPX Yacute Odieresis -35
+KPX Yacute Ograve -35
+KPX Yacute Ohungarumlaut -35
+KPX Yacute Omacron -35
+KPX Yacute Oslash -35
+KPX Yacute Otilde -35
+KPX Yacute a -85
+KPX Yacute aacute -85
+KPX Yacute abreve -85
+KPX Yacute acircumflex -85
+KPX Yacute adieresis -85
+KPX Yacute agrave -85
+KPX Yacute amacron -85
+KPX Yacute aogonek -85
+KPX Yacute aring -85
+KPX Yacute atilde -85
+KPX Yacute colon -92
+KPX Yacute comma -92
+KPX Yacute e -111
+KPX Yacute eacute -111
+KPX Yacute ecaron -111
+KPX Yacute ecircumflex -111
+KPX Yacute edieresis -71
+KPX Yacute edotaccent -111
+KPX Yacute egrave -71
+KPX Yacute emacron -71
+KPX Yacute eogonek -111
+KPX Yacute hyphen -92
+KPX Yacute i -37
+KPX Yacute iacute -37
+KPX Yacute iogonek -37
+KPX Yacute o -111
+KPX Yacute oacute -111
+KPX Yacute ocircumflex -111
+KPX Yacute odieresis -111
+KPX Yacute ograve -111
+KPX Yacute ohungarumlaut -111
+KPX Yacute omacron -111
+KPX Yacute oslash -111
+KPX Yacute otilde -111
+KPX Yacute period -92
+KPX Yacute semicolon -92
+KPX Yacute u -92
+KPX Yacute uacute -92
+KPX Yacute ucircumflex -92
+KPX Yacute udieresis -92
+KPX Yacute ugrave -92
+KPX Yacute uhungarumlaut -92
+KPX Yacute umacron -92
+KPX Yacute uogonek -92
+KPX Yacute uring -92
+KPX Ydieresis A -110
+KPX Ydieresis Aacute -110
+KPX Ydieresis Abreve -110
+KPX Ydieresis Acircumflex -110
+KPX Ydieresis Adieresis -110
+KPX Ydieresis Agrave -110
+KPX Ydieresis Amacron -110
+KPX Ydieresis Aogonek -110
+KPX Ydieresis Aring -110
+KPX Ydieresis Atilde -110
+KPX Ydieresis O -35
+KPX Ydieresis Oacute -35
+KPX Ydieresis Ocircumflex -35
+KPX Ydieresis Odieresis -35
+KPX Ydieresis Ograve -35
+KPX Ydieresis Ohungarumlaut -35
+KPX Ydieresis Omacron -35
+KPX Ydieresis Oslash -35
+KPX Ydieresis Otilde -35
+KPX Ydieresis a -85
+KPX Ydieresis aacute -85
+KPX Ydieresis abreve -85
+KPX Ydieresis acircumflex -85
+KPX Ydieresis adieresis -85
+KPX Ydieresis agrave -85
+KPX Ydieresis amacron -85
+KPX Ydieresis aogonek -85
+KPX Ydieresis aring -85
+KPX Ydieresis atilde -85
+KPX Ydieresis colon -92
+KPX Ydieresis comma -92
+KPX Ydieresis e -111
+KPX Ydieresis eacute -111
+KPX Ydieresis ecaron -111
+KPX Ydieresis ecircumflex -111
+KPX Ydieresis edieresis -71
+KPX Ydieresis edotaccent -111
+KPX Ydieresis egrave -71
+KPX Ydieresis emacron -71
+KPX Ydieresis eogonek -111
+KPX Ydieresis hyphen -92
+KPX Ydieresis i -37
+KPX Ydieresis iacute -37
+KPX Ydieresis iogonek -37
+KPX Ydieresis o -111
+KPX Ydieresis oacute -111
+KPX Ydieresis ocircumflex -111
+KPX Ydieresis odieresis -111
+KPX Ydieresis ograve -111
+KPX Ydieresis ohungarumlaut -111
+KPX Ydieresis omacron -111
+KPX Ydieresis oslash -111
+KPX Ydieresis otilde -111
+KPX Ydieresis period -92
+KPX Ydieresis semicolon -92
+KPX Ydieresis u -92
+KPX Ydieresis uacute -92
+KPX Ydieresis ucircumflex -92
+KPX Ydieresis udieresis -92
+KPX Ydieresis ugrave -92
+KPX Ydieresis uhungarumlaut -92
+KPX Ydieresis umacron -92
+KPX Ydieresis uogonek -92
+KPX Ydieresis uring -92
+KPX a v -25
+KPX aacute v -25
+KPX abreve v -25
+KPX acircumflex v -25
+KPX adieresis v -25
+KPX agrave v -25
+KPX amacron v -25
+KPX aogonek v -25
+KPX aring v -25
+KPX atilde v -25
+KPX b b -10
+KPX b period -40
+KPX b u -20
+KPX b uacute -20
+KPX b ucircumflex -20
+KPX b udieresis -20
+KPX b ugrave -20
+KPX b uhungarumlaut -20
+KPX b umacron -20
+KPX b uogonek -20
+KPX b uring -20
+KPX b v -15
+KPX comma quotedblright -45
+KPX comma quoteright -55
+KPX d w -15
+KPX dcroat w -15
+KPX e v -15
+KPX eacute v -15
+KPX ecaron v -15
+KPX ecircumflex v -15
+KPX edieresis v -15
+KPX edotaccent v -15
+KPX egrave v -15
+KPX emacron v -15
+KPX eogonek v -15
+KPX f comma -15
+KPX f dotlessi -35
+KPX f i -25
+KPX f o -25
+KPX f oacute -25
+KPX f ocircumflex -25
+KPX f odieresis -25
+KPX f ograve -25
+KPX f ohungarumlaut -25
+KPX f omacron -25
+KPX f oslash -25
+KPX f otilde -25
+KPX f period -15
+KPX f quotedblright 50
+KPX f quoteright 55
+KPX g period -15
+KPX gbreve period -15
+KPX gcommaaccent period -15
+KPX h y -15
+KPX h yacute -15
+KPX h ydieresis -15
+KPX i v -10
+KPX iacute v -10
+KPX icircumflex v -10
+KPX idieresis v -10
+KPX igrave v -10
+KPX imacron v -10
+KPX iogonek v -10
+KPX k e -10
+KPX k eacute -10
+KPX k ecaron -10
+KPX k ecircumflex -10
+KPX k edieresis -10
+KPX k edotaccent -10
+KPX k egrave -10
+KPX k emacron -10
+KPX k eogonek -10
+KPX k o -15
+KPX k oacute -15
+KPX k ocircumflex -15
+KPX k odieresis -15
+KPX k ograve -15
+KPX k ohungarumlaut -15
+KPX k omacron -15
+KPX k oslash -15
+KPX k otilde -15
+KPX k y -15
+KPX k yacute -15
+KPX k ydieresis -15
+KPX kcommaaccent e -10
+KPX kcommaaccent eacute -10
+KPX kcommaaccent ecaron -10
+KPX kcommaaccent ecircumflex -10
+KPX kcommaaccent edieresis -10
+KPX kcommaaccent edotaccent -10
+KPX kcommaaccent egrave -10
+KPX kcommaaccent emacron -10
+KPX kcommaaccent eogonek -10
+KPX kcommaaccent o -15
+KPX kcommaaccent oacute -15
+KPX kcommaaccent ocircumflex -15
+KPX kcommaaccent odieresis -15
+KPX kcommaaccent ograve -15
+KPX kcommaaccent ohungarumlaut -15
+KPX kcommaaccent omacron -15
+KPX kcommaaccent oslash -15
+KPX kcommaaccent otilde -15
+KPX kcommaaccent y -15
+KPX kcommaaccent yacute -15
+KPX kcommaaccent ydieresis -15
+KPX n v -40
+KPX nacute v -40
+KPX ncaron v -40
+KPX ncommaaccent v -40
+KPX ntilde v -40
+KPX o v -10
+KPX o w -10
+KPX oacute v -10
+KPX oacute w -10
+KPX ocircumflex v -10
+KPX ocircumflex w -10
+KPX odieresis v -10
+KPX odieresis w -10
+KPX ograve v -10
+KPX ograve w -10
+KPX ohungarumlaut v -10
+KPX ohungarumlaut w -10
+KPX omacron v -10
+KPX omacron w -10
+KPX oslash v -10
+KPX oslash w -10
+KPX otilde v -10
+KPX otilde w -10
+KPX period quotedblright -55
+KPX period quoteright -55
+KPX quotedblleft A -10
+KPX quotedblleft Aacute -10
+KPX quotedblleft Abreve -10
+KPX quotedblleft Acircumflex -10
+KPX quotedblleft Adieresis -10
+KPX quotedblleft Agrave -10
+KPX quotedblleft Amacron -10
+KPX quotedblleft Aogonek -10
+KPX quotedblleft Aring -10
+KPX quotedblleft Atilde -10
+KPX quoteleft A -10
+KPX quoteleft Aacute -10
+KPX quoteleft Abreve -10
+KPX quoteleft Acircumflex -10
+KPX quoteleft Adieresis -10
+KPX quoteleft Agrave -10
+KPX quoteleft Amacron -10
+KPX quoteleft Aogonek -10
+KPX quoteleft Aring -10
+KPX quoteleft Atilde -10
+KPX quoteleft quoteleft -63
+KPX quoteright d -20
+KPX quoteright dcroat -20
+KPX quoteright quoteright -63
+KPX quoteright r -20
+KPX quoteright racute -20
+KPX quoteright rcaron -20
+KPX quoteright rcommaaccent -20
+KPX quoteright s -37
+KPX quoteright sacute -37
+KPX quoteright scaron -37
+KPX quoteright scedilla -37
+KPX quoteright scommaaccent -37
+KPX quoteright space -74
+KPX quoteright v -20
+KPX r c -18
+KPX r cacute -18
+KPX r ccaron -18
+KPX r ccedilla -18
+KPX r comma -92
+KPX r e -18
+KPX r eacute -18
+KPX r ecaron -18
+KPX r ecircumflex -18
+KPX r edieresis -18
+KPX r edotaccent -18
+KPX r egrave -18
+KPX r emacron -18
+KPX r eogonek -18
+KPX r g -10
+KPX r gbreve -10
+KPX r gcommaaccent -10
+KPX r hyphen -37
+KPX r n -15
+KPX r nacute -15
+KPX r ncaron -15
+KPX r ncommaaccent -15
+KPX r ntilde -15
+KPX r o -18
+KPX r oacute -18
+KPX r ocircumflex -18
+KPX r odieresis -18
+KPX r ograve -18
+KPX r ohungarumlaut -18
+KPX r omacron -18
+KPX r oslash -18
+KPX r otilde -18
+KPX r p -10
+KPX r period -100
+KPX r q -18
+KPX r v -10
+KPX racute c -18
+KPX racute cacute -18
+KPX racute ccaron -18
+KPX racute ccedilla -18
+KPX racute comma -92
+KPX racute e -18
+KPX racute eacute -18
+KPX racute ecaron -18
+KPX racute ecircumflex -18
+KPX racute edieresis -18
+KPX racute edotaccent -18
+KPX racute egrave -18
+KPX racute emacron -18
+KPX racute eogonek -18
+KPX racute g -10
+KPX racute gbreve -10
+KPX racute gcommaaccent -10
+KPX racute hyphen -37
+KPX racute n -15
+KPX racute nacute -15
+KPX racute ncaron -15
+KPX racute ncommaaccent -15
+KPX racute ntilde -15
+KPX racute o -18
+KPX racute oacute -18
+KPX racute ocircumflex -18
+KPX racute odieresis -18
+KPX racute ograve -18
+KPX racute ohungarumlaut -18
+KPX racute omacron -18
+KPX racute oslash -18
+KPX racute otilde -18
+KPX racute p -10
+KPX racute period -100
+KPX racute q -18
+KPX racute v -10
+KPX rcaron c -18
+KPX rcaron cacute -18
+KPX rcaron ccaron -18
+KPX rcaron ccedilla -18
+KPX rcaron comma -92
+KPX rcaron e -18
+KPX rcaron eacute -18
+KPX rcaron ecaron -18
+KPX rcaron ecircumflex -18
+KPX rcaron edieresis -18
+KPX rcaron edotaccent -18
+KPX rcaron egrave -18
+KPX rcaron emacron -18
+KPX rcaron eogonek -18
+KPX rcaron g -10
+KPX rcaron gbreve -10
+KPX rcaron gcommaaccent -10
+KPX rcaron hyphen -37
+KPX rcaron n -15
+KPX rcaron nacute -15
+KPX rcaron ncaron -15
+KPX rcaron ncommaaccent -15
+KPX rcaron ntilde -15
+KPX rcaron o -18
+KPX rcaron oacute -18
+KPX rcaron ocircumflex -18
+KPX rcaron odieresis -18
+KPX rcaron ograve -18
+KPX rcaron ohungarumlaut -18
+KPX rcaron omacron -18
+KPX rcaron oslash -18
+KPX rcaron otilde -18
+KPX rcaron p -10
+KPX rcaron period -100
+KPX rcaron q -18
+KPX rcaron v -10
+KPX rcommaaccent c -18
+KPX rcommaaccent cacute -18
+KPX rcommaaccent ccaron -18
+KPX rcommaaccent ccedilla -18
+KPX rcommaaccent comma -92
+KPX rcommaaccent e -18
+KPX rcommaaccent eacute -18
+KPX rcommaaccent ecaron -18
+KPX rcommaaccent ecircumflex -18
+KPX rcommaaccent edieresis -18
+KPX rcommaaccent edotaccent -18
+KPX rcommaaccent egrave -18
+KPX rcommaaccent emacron -18
+KPX rcommaaccent eogonek -18
+KPX rcommaaccent g -10
+KPX rcommaaccent gbreve -10
+KPX rcommaaccent gcommaaccent -10
+KPX rcommaaccent hyphen -37
+KPX rcommaaccent n -15
+KPX rcommaaccent nacute -15
+KPX rcommaaccent ncaron -15
+KPX rcommaaccent ncommaaccent -15
+KPX rcommaaccent ntilde -15
+KPX rcommaaccent o -18
+KPX rcommaaccent oacute -18
+KPX rcommaaccent ocircumflex -18
+KPX rcommaaccent odieresis -18
+KPX rcommaaccent ograve -18
+KPX rcommaaccent ohungarumlaut -18
+KPX rcommaaccent omacron -18
+KPX rcommaaccent oslash -18
+KPX rcommaaccent otilde -18
+KPX rcommaaccent p -10
+KPX rcommaaccent period -100
+KPX rcommaaccent q -18
+KPX rcommaaccent v -10
+KPX space A -55
+KPX space Aacute -55
+KPX space Abreve -55
+KPX space Acircumflex -55
+KPX space Adieresis -55
+KPX space Agrave -55
+KPX space Amacron -55
+KPX space Aogonek -55
+KPX space Aring -55
+KPX space Atilde -55
+KPX space T -30
+KPX space Tcaron -30
+KPX space Tcommaaccent -30
+KPX space V -45
+KPX space W -30
+KPX space Y -55
+KPX space Yacute -55
+KPX space Ydieresis -55
+KPX v a -10
+KPX v aacute -10
+KPX v abreve -10
+KPX v acircumflex -10
+KPX v adieresis -10
+KPX v agrave -10
+KPX v amacron -10
+KPX v aogonek -10
+KPX v aring -10
+KPX v atilde -10
+KPX v comma -55
+KPX v e -10
+KPX v eacute -10
+KPX v ecaron -10
+KPX v ecircumflex -10
+KPX v edieresis -10
+KPX v edotaccent -10
+KPX v egrave -10
+KPX v emacron -10
+KPX v eogonek -10
+KPX v o -10
+KPX v oacute -10
+KPX v ocircumflex -10
+KPX v odieresis -10
+KPX v ograve -10
+KPX v ohungarumlaut -10
+KPX v omacron -10
+KPX v oslash -10
+KPX v otilde -10
+KPX v period -70
+KPX w comma -55
+KPX w o -10
+KPX w oacute -10
+KPX w ocircumflex -10
+KPX w odieresis -10
+KPX w ograve -10
+KPX w ohungarumlaut -10
+KPX w omacron -10
+KPX w oslash -10
+KPX w otilde -10
+KPX w period -70
+KPX y comma -55
+KPX y e -10
+KPX y eacute -10
+KPX y ecaron -10
+KPX y ecircumflex -10
+KPX y edieresis -10
+KPX y edotaccent -10
+KPX y egrave -10
+KPX y emacron -10
+KPX y eogonek -10
+KPX y o -25
+KPX y oacute -25
+KPX y ocircumflex -25
+KPX y odieresis -25
+KPX y ograve -25
+KPX y ohungarumlaut -25
+KPX y omacron -25
+KPX y oslash -25
+KPX y otilde -25
+KPX y period -70
+KPX yacute comma -55
+KPX yacute e -10
+KPX yacute eacute -10
+KPX yacute ecaron -10
+KPX yacute ecircumflex -10
+KPX yacute edieresis -10
+KPX yacute edotaccent -10
+KPX yacute egrave -10
+KPX yacute emacron -10
+KPX yacute eogonek -10
+KPX yacute o -25
+KPX yacute oacute -25
+KPX yacute ocircumflex -25
+KPX yacute odieresis -25
+KPX yacute ograve -25
+KPX yacute ohungarumlaut -25
+KPX yacute omacron -25
+KPX yacute oslash -25
+KPX yacute otilde -25
+KPX yacute period -70
+KPX ydieresis comma -55
+KPX ydieresis e -10
+KPX ydieresis eacute -10
+KPX ydieresis ecaron -10
+KPX ydieresis ecircumflex -10
+KPX ydieresis edieresis -10
+KPX ydieresis edotaccent -10
+KPX ydieresis egrave -10
+KPX ydieresis emacron -10
+KPX ydieresis eogonek -10
+KPX ydieresis o -25
+KPX ydieresis oacute -25
+KPX ydieresis ocircumflex -25
+KPX ydieresis odieresis -25
+KPX ydieresis ograve -25
+KPX ydieresis ohungarumlaut -25
+KPX ydieresis omacron -25
+KPX ydieresis oslash -25
+KPX ydieresis otilde -25
+KPX ydieresis period -70
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/program/libraries/afm/Times-BoldItalic.afm b/program/libraries/afm/Times-BoldItalic.afm
new file mode 100644 (file)
index 0000000..2301dfd
--- /dev/null
@@ -0,0 +1,2384 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated.  All Rights Reserved.
+Comment Creation Date: Thu May  1 13:04:06 1997
+Comment UniqueID 43066
+Comment VMusage 45874 56899
+FontName Times-BoldItalic
+FullName Times Bold Italic
+FamilyName Times
+Weight Bold
+ItalicAngle -15
+IsFixedPitch false
+CharacterSet ExtendedRoman
+FontBBox -200 -218 996 921 
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated.  All Rights Reserved.Times is a trademark of Linotype-Hell AG and/or its subsidiaries.
+EncodingScheme AdobeStandardEncoding
+CapHeight 669
+XHeight 462
+Ascender 683
+Descender -217
+StdHW 42
+StdVW 121
+StartCharMetrics 315
+C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 389 ; N exclam ; B 67 -13 370 684 ;
+C 34 ; WX 555 ; N quotedbl ; B 136 398 536 685 ;
+C 35 ; WX 500 ; N numbersign ; B -33 0 533 700 ;
+C 36 ; WX 500 ; N dollar ; B -20 -100 497 733 ;
+C 37 ; WX 833 ; N percent ; B 39 -10 793 692 ;
+C 38 ; WX 778 ; N ampersand ; B 5 -19 699 682 ;
+C 39 ; WX 333 ; N quoteright ; B 98 369 302 685 ;
+C 40 ; WX 333 ; N parenleft ; B 28 -179 344 685 ;
+C 41 ; WX 333 ; N parenright ; B -44 -179 271 685 ;
+C 42 ; WX 500 ; N asterisk ; B 65 249 456 685 ;
+C 43 ; WX 570 ; N plus ; B 33 0 537 506 ;
+C 44 ; WX 250 ; N comma ; B -60 -182 144 134 ;
+C 45 ; WX 333 ; N hyphen ; B 2 166 271 282 ;
+C 46 ; WX 250 ; N period ; B -9 -13 139 135 ;
+C 47 ; WX 278 ; N slash ; B -64 -18 342 685 ;
+C 48 ; WX 500 ; N zero ; B 17 -14 477 683 ;
+C 49 ; WX 500 ; N one ; B 5 0 419 683 ;
+C 50 ; WX 500 ; N two ; B -27 0 446 683 ;
+C 51 ; WX 500 ; N three ; B -15 -13 450 683 ;
+C 52 ; WX 500 ; N four ; B -15 0 503 683 ;
+C 53 ; WX 500 ; N five ; B -11 -13 487 669 ;
+C 54 ; WX 500 ; N six ; B 23 -15 509 679 ;
+C 55 ; WX 500 ; N seven ; B 52 0 525 669 ;
+C 56 ; WX 500 ; N eight ; B 3 -13 476 683 ;
+C 57 ; WX 500 ; N nine ; B -12 -10 475 683 ;
+C 58 ; WX 333 ; N colon ; B 23 -13 264 459 ;
+C 59 ; WX 333 ; N semicolon ; B -25 -183 264 459 ;
+C 60 ; WX 570 ; N less ; B 31 -8 539 514 ;
+C 61 ; WX 570 ; N equal ; B 33 107 537 399 ;
+C 62 ; WX 570 ; N greater ; B 31 -8 539 514 ;
+C 63 ; WX 500 ; N question ; B 79 -13 470 684 ;
+C 64 ; WX 832 ; N at ; B 63 -18 770 685 ;
+C 65 ; WX 667 ; N A ; B -67 0 593 683 ;
+C 66 ; WX 667 ; N B ; B -24 0 624 669 ;
+C 67 ; WX 667 ; N C ; B 32 -18 677 685 ;
+C 68 ; WX 722 ; N D ; B -46 0 685 669 ;
+C 69 ; WX 667 ; N E ; B -27 0 653 669 ;
+C 70 ; WX 667 ; N F ; B -13 0 660 669 ;
+C 71 ; WX 722 ; N G ; B 21 -18 706 685 ;
+C 72 ; WX 778 ; N H ; B -24 0 799 669 ;
+C 73 ; WX 389 ; N I ; B -32 0 406 669 ;
+C 74 ; WX 500 ; N J ; B -46 -99 524 669 ;
+C 75 ; WX 667 ; N K ; B -21 0 702 669 ;
+C 76 ; WX 611 ; N L ; B -22 0 590 669 ;
+C 77 ; WX 889 ; N M ; B -29 -12 917 669 ;
+C 78 ; WX 722 ; N N ; B -27 -15 748 669 ;
+C 79 ; WX 722 ; N O ; B 27 -18 691 685 ;
+C 80 ; WX 611 ; N P ; B -27 0 613 669 ;
+C 81 ; WX 722 ; N Q ; B 27 -208 691 685 ;
+C 82 ; WX 667 ; N R ; B -29 0 623 669 ;
+C 83 ; WX 556 ; N S ; B 2 -18 526 685 ;
+C 84 ; WX 611 ; N T ; B 50 0 650 669 ;
+C 85 ; WX 722 ; N U ; B 67 -18 744 669 ;
+C 86 ; WX 667 ; N V ; B 65 -18 715 669 ;
+C 87 ; WX 889 ; N W ; B 65 -18 940 669 ;
+C 88 ; WX 667 ; N X ; B -24 0 694 669 ;
+C 89 ; WX 611 ; N Y ; B 73 0 659 669 ;
+C 90 ; WX 611 ; N Z ; B -11 0 590 669 ;
+C 91 ; WX 333 ; N bracketleft ; B -37 -159 362 674 ;
+C 92 ; WX 278 ; N backslash ; B -1 -18 279 685 ;
+C 93 ; WX 333 ; N bracketright ; B -56 -157 343 674 ;
+C 94 ; WX 570 ; N asciicircum ; B 67 304 503 669 ;
+C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ;
+C 96 ; WX 333 ; N quoteleft ; B 128 369 332 685 ;
+C 97 ; WX 500 ; N a ; B -21 -14 455 462 ;
+C 98 ; WX 500 ; N b ; B -14 -13 444 699 ;
+C 99 ; WX 444 ; N c ; B -5 -13 392 462 ;
+C 100 ; WX 500 ; N d ; B -21 -13 517 699 ;
+C 101 ; WX 444 ; N e ; B 5 -13 398 462 ;
+C 102 ; WX 333 ; N f ; B -169 -205 446 698 ; L i fi ; L l fl ;
+C 103 ; WX 500 ; N g ; B -52 -203 478 462 ;
+C 104 ; WX 556 ; N h ; B -13 -9 498 699 ;
+C 105 ; WX 278 ; N i ; B 2 -9 263 684 ;
+C 106 ; WX 278 ; N j ; B -189 -207 279 684 ;
+C 107 ; WX 500 ; N k ; B -23 -8 483 699 ;
+C 108 ; WX 278 ; N l ; B 2 -9 290 699 ;
+C 109 ; WX 778 ; N m ; B -14 -9 722 462 ;
+C 110 ; WX 556 ; N n ; B -6 -9 493 462 ;
+C 111 ; WX 500 ; N o ; B -3 -13 441 462 ;
+C 112 ; WX 500 ; N p ; B -120 -205 446 462 ;
+C 113 ; WX 500 ; N q ; B 1 -205 471 462 ;
+C 114 ; WX 389 ; N r ; B -21 0 389 462 ;
+C 115 ; WX 389 ; N s ; B -19 -13 333 462 ;
+C 116 ; WX 278 ; N t ; B -11 -9 281 594 ;
+C 117 ; WX 556 ; N u ; B 15 -9 492 462 ;
+C 118 ; WX 444 ; N v ; B 16 -13 401 462 ;
+C 119 ; WX 667 ; N w ; B 16 -13 614 462 ;
+C 120 ; WX 500 ; N x ; B -46 -13 469 462 ;
+C 121 ; WX 444 ; N y ; B -94 -205 392 462 ;
+C 122 ; WX 389 ; N z ; B -43 -78 368 449 ;
+C 123 ; WX 348 ; N braceleft ; B 5 -187 436 686 ;
+C 124 ; WX 220 ; N bar ; B 66 -218 154 782 ;
+C 125 ; WX 348 ; N braceright ; B -129 -187 302 686 ;
+C 126 ; WX 570 ; N asciitilde ; B 54 173 516 333 ;
+C 161 ; WX 389 ; N exclamdown ; B 19 -205 322 492 ;
+C 162 ; WX 500 ; N cent ; B 42 -143 439 576 ;
+C 163 ; WX 500 ; N sterling ; B -32 -12 510 683 ;
+C 164 ; WX 167 ; N fraction ; B -169 -14 324 683 ;
+C 165 ; WX 500 ; N yen ; B 33 0 628 669 ;
+C 166 ; WX 500 ; N florin ; B -87 -156 537 707 ;
+C 167 ; WX 500 ; N section ; B 36 -143 459 685 ;
+C 168 ; WX 500 ; N currency ; B -26 34 526 586 ;
+C 169 ; WX 278 ; N quotesingle ; B 128 398 268 685 ;
+C 170 ; WX 500 ; N quotedblleft ; B 53 369 513 685 ;
+C 171 ; WX 500 ; N guillemotleft ; B 12 32 468 415 ;
+C 172 ; WX 333 ; N guilsinglleft ; B 32 32 303 415 ;
+C 173 ; WX 333 ; N guilsinglright ; B 10 32 281 415 ;
+C 174 ; WX 556 ; N fi ; B -188 -205 514 703 ;
+C 175 ; WX 556 ; N fl ; B -186 -205 553 704 ;
+C 177 ; WX 500 ; N endash ; B -40 178 477 269 ;
+C 178 ; WX 500 ; N dagger ; B 91 -145 494 685 ;
+C 179 ; WX 500 ; N daggerdbl ; B 10 -139 493 685 ;
+C 180 ; WX 250 ; N periodcentered ; B 51 257 199 405 ;
+C 182 ; WX 500 ; N paragraph ; B -57 -193 562 669 ;
+C 183 ; WX 350 ; N bullet ; B 0 175 350 525 ;
+C 184 ; WX 333 ; N quotesinglbase ; B -5 -182 199 134 ;
+C 185 ; WX 500 ; N quotedblbase ; B -57 -182 403 134 ;
+C 186 ; WX 500 ; N quotedblright ; B 53 369 513 685 ;
+C 187 ; WX 500 ; N guillemotright ; B 12 32 468 415 ;
+C 188 ; WX 1000 ; N ellipsis ; B 40 -13 852 135 ;
+C 189 ; WX 1000 ; N perthousand ; B 7 -29 996 706 ;
+C 191 ; WX 500 ; N questiondown ; B 30 -205 421 492 ;
+C 193 ; WX 333 ; N grave ; B 85 516 297 697 ;
+C 194 ; WX 333 ; N acute ; B 139 516 379 697 ;
+C 195 ; WX 333 ; N circumflex ; B 40 516 367 690 ;
+C 196 ; WX 333 ; N tilde ; B 48 536 407 655 ;
+C 197 ; WX 333 ; N macron ; B 51 553 393 623 ;
+C 198 ; WX 333 ; N breve ; B 71 516 387 678 ;
+C 199 ; WX 333 ; N dotaccent ; B 163 550 298 684 ;
+C 200 ; WX 333 ; N dieresis ; B 55 550 402 684 ;
+C 202 ; WX 333 ; N ring ; B 127 516 340 729 ;
+C 203 ; WX 333 ; N cedilla ; B -80 -218 156 5 ;
+C 205 ; WX 333 ; N hungarumlaut ; B 69 516 498 697 ;
+C 206 ; WX 333 ; N ogonek ; B 15 -183 244 34 ;
+C 207 ; WX 333 ; N caron ; B 79 516 411 690 ;
+C 208 ; WX 1000 ; N emdash ; B -40 178 977 269 ;
+C 225 ; WX 944 ; N AE ; B -64 0 918 669 ;
+C 227 ; WX 266 ; N ordfeminine ; B 16 399 330 685 ;
+C 232 ; WX 611 ; N Lslash ; B -22 0 590 669 ;
+C 233 ; WX 722 ; N Oslash ; B 27 -125 691 764 ;
+C 234 ; WX 944 ; N OE ; B 23 -8 946 677 ;
+C 235 ; WX 300 ; N ordmasculine ; B 56 400 347 685 ;
+C 241 ; WX 722 ; N ae ; B -5 -13 673 462 ;
+C 245 ; WX 278 ; N dotlessi ; B 2 -9 238 462 ;
+C 248 ; WX 278 ; N lslash ; B -7 -9 307 699 ;
+C 249 ; WX 500 ; N oslash ; B -3 -119 441 560 ;
+C 250 ; WX 722 ; N oe ; B 6 -13 674 462 ;
+C 251 ; WX 500 ; N germandbls ; B -200 -200 473 705 ;
+C -1 ; WX 389 ; N Idieresis ; B -32 0 450 862 ;
+C -1 ; WX 444 ; N eacute ; B 5 -13 435 697 ;
+C -1 ; WX 500 ; N abreve ; B -21 -14 471 678 ;
+C -1 ; WX 556 ; N uhungarumlaut ; B 15 -9 610 697 ;
+C -1 ; WX 444 ; N ecaron ; B 5 -13 467 690 ;
+C -1 ; WX 611 ; N Ydieresis ; B 73 0 659 862 ;
+C -1 ; WX 570 ; N divide ; B 33 -29 537 535 ;
+C -1 ; WX 611 ; N Yacute ; B 73 0 659 904 ;
+C -1 ; WX 667 ; N Acircumflex ; B -67 0 593 897 ;
+C -1 ; WX 500 ; N aacute ; B -21 -14 463 697 ;
+C -1 ; WX 722 ; N Ucircumflex ; B 67 -18 744 897 ;
+C -1 ; WX 444 ; N yacute ; B -94 -205 435 697 ;
+C -1 ; WX 389 ; N scommaaccent ; B -19 -218 333 462 ;
+C -1 ; WX 444 ; N ecircumflex ; B 5 -13 423 690 ;
+C -1 ; WX 722 ; N Uring ; B 67 -18 744 921 ;
+C -1 ; WX 722 ; N Udieresis ; B 67 -18 744 862 ;
+C -1 ; WX 500 ; N aogonek ; B -21 -183 455 462 ;
+C -1 ; WX 722 ; N Uacute ; B 67 -18 744 904 ;
+C -1 ; WX 556 ; N uogonek ; B 15 -183 492 462 ;
+C -1 ; WX 667 ; N Edieresis ; B -27 0 653 862 ;
+C -1 ; WX 722 ; N Dcroat ; B -31 0 700 669 ;
+C -1 ; WX 250 ; N commaaccent ; B -36 -218 131 -50 ;
+C -1 ; WX 747 ; N copyright ; B 30 -18 718 685 ;
+C -1 ; WX 667 ; N Emacron ; B -27 0 653 830 ;
+C -1 ; WX 444 ; N ccaron ; B -5 -13 467 690 ;
+C -1 ; WX 500 ; N aring ; B -21 -14 455 729 ;
+C -1 ; WX 722 ; N Ncommaaccent ; B -27 -218 748 669 ;
+C -1 ; WX 278 ; N lacute ; B 2 -9 392 904 ;
+C -1 ; WX 500 ; N agrave ; B -21 -14 455 697 ;
+C -1 ; WX 611 ; N Tcommaaccent ; B 50 -218 650 669 ;
+C -1 ; WX 667 ; N Cacute ; B 32 -18 677 904 ;
+C -1 ; WX 500 ; N atilde ; B -21 -14 491 655 ;
+C -1 ; WX 667 ; N Edotaccent ; B -27 0 653 862 ;
+C -1 ; WX 389 ; N scaron ; B -19 -13 424 690 ;
+C -1 ; WX 389 ; N scedilla ; B -19 -218 333 462 ;
+C -1 ; WX 278 ; N iacute ; B 2 -9 352 697 ;
+C -1 ; WX 494 ; N lozenge ; B 10 0 484 745 ;
+C -1 ; WX 667 ; N Rcaron ; B -29 0 623 897 ;
+C -1 ; WX 722 ; N Gcommaaccent ; B 21 -218 706 685 ;
+C -1 ; WX 556 ; N ucircumflex ; B 15 -9 492 690 ;
+C -1 ; WX 500 ; N acircumflex ; B -21 -14 455 690 ;
+C -1 ; WX 667 ; N Amacron ; B -67 0 593 830 ;
+C -1 ; WX 389 ; N rcaron ; B -21 0 424 690 ;
+C -1 ; WX 444 ; N ccedilla ; B -5 -218 392 462 ;
+C -1 ; WX 611 ; N Zdotaccent ; B -11 0 590 862 ;
+C -1 ; WX 611 ; N Thorn ; B -27 0 573 669 ;
+C -1 ; WX 722 ; N Omacron ; B 27 -18 691 830 ;
+C -1 ; WX 667 ; N Racute ; B -29 0 623 904 ;
+C -1 ; WX 556 ; N Sacute ; B 2 -18 531 904 ;
+C -1 ; WX 608 ; N dcaron ; B -21 -13 675 708 ;
+C -1 ; WX 722 ; N Umacron ; B 67 -18 744 830 ;
+C -1 ; WX 556 ; N uring ; B 15 -9 492 729 ;
+C -1 ; WX 300 ; N threesuperior ; B 17 265 321 683 ;
+C -1 ; WX 722 ; N Ograve ; B 27 -18 691 904 ;
+C -1 ; WX 667 ; N Agrave ; B -67 0 593 904 ;
+C -1 ; WX 667 ; N Abreve ; B -67 0 593 885 ;
+C -1 ; WX 570 ; N multiply ; B 48 16 522 490 ;
+C -1 ; WX 556 ; N uacute ; B 15 -9 492 697 ;
+C -1 ; WX 611 ; N Tcaron ; B 50 0 650 897 ;
+C -1 ; WX 494 ; N partialdiff ; B 11 -21 494 750 ;
+C -1 ; WX 444 ; N ydieresis ; B -94 -205 443 655 ;
+C -1 ; WX 722 ; N Nacute ; B -27 -15 748 904 ;
+C -1 ; WX 278 ; N icircumflex ; B -3 -9 324 690 ;
+C -1 ; WX 667 ; N Ecircumflex ; B -27 0 653 897 ;
+C -1 ; WX 500 ; N adieresis ; B -21 -14 476 655 ;
+C -1 ; WX 444 ; N edieresis ; B 5 -13 448 655 ;
+C -1 ; WX 444 ; N cacute ; B -5 -13 435 697 ;
+C -1 ; WX 556 ; N nacute ; B -6 -9 493 697 ;
+C -1 ; WX 556 ; N umacron ; B 15 -9 492 623 ;
+C -1 ; WX 722 ; N Ncaron ; B -27 -15 748 897 ;
+C -1 ; WX 389 ; N Iacute ; B -32 0 432 904 ;
+C -1 ; WX 570 ; N plusminus ; B 33 0 537 506 ;
+C -1 ; WX 220 ; N brokenbar ; B 66 -143 154 707 ;
+C -1 ; WX 747 ; N registered ; B 30 -18 718 685 ;
+C -1 ; WX 722 ; N Gbreve ; B 21 -18 706 885 ;
+C -1 ; WX 389 ; N Idotaccent ; B -32 0 406 862 ;
+C -1 ; WX 600 ; N summation ; B 14 -10 585 706 ;
+C -1 ; WX 667 ; N Egrave ; B -27 0 653 904 ;
+C -1 ; WX 389 ; N racute ; B -21 0 407 697 ;
+C -1 ; WX 500 ; N omacron ; B -3 -13 462 623 ;
+C -1 ; WX 611 ; N Zacute ; B -11 0 590 904 ;
+C -1 ; WX 611 ; N Zcaron ; B -11 0 590 897 ;
+C -1 ; WX 549 ; N greaterequal ; B 26 0 523 704 ;
+C -1 ; WX 722 ; N Eth ; B -31 0 700 669 ;
+C -1 ; WX 667 ; N Ccedilla ; B 32 -218 677 685 ;
+C -1 ; WX 278 ; N lcommaaccent ; B -42 -218 290 699 ;
+C -1 ; WX 366 ; N tcaron ; B -11 -9 434 754 ;
+C -1 ; WX 444 ; N eogonek ; B 5 -183 398 462 ;
+C -1 ; WX 722 ; N Uogonek ; B 67 -183 744 669 ;
+C -1 ; WX 667 ; N Aacute ; B -67 0 593 904 ;
+C -1 ; WX 667 ; N Adieresis ; B -67 0 593 862 ;
+C -1 ; WX 444 ; N egrave ; B 5 -13 398 697 ;
+C -1 ; WX 389 ; N zacute ; B -43 -78 407 697 ;
+C -1 ; WX 278 ; N iogonek ; B -20 -183 263 684 ;
+C -1 ; WX 722 ; N Oacute ; B 27 -18 691 904 ;
+C -1 ; WX 500 ; N oacute ; B -3 -13 463 697 ;
+C -1 ; WX 500 ; N amacron ; B -21 -14 467 623 ;
+C -1 ; WX 389 ; N sacute ; B -19 -13 407 697 ;
+C -1 ; WX 278 ; N idieresis ; B 2 -9 364 655 ;
+C -1 ; WX 722 ; N Ocircumflex ; B 27 -18 691 897 ;
+C -1 ; WX 722 ; N Ugrave ; B 67 -18 744 904 ;
+C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C -1 ; WX 500 ; N thorn ; B -120 -205 446 699 ;
+C -1 ; WX 300 ; N twosuperior ; B 2 274 313 683 ;
+C -1 ; WX 722 ; N Odieresis ; B 27 -18 691 862 ;
+C -1 ; WX 576 ; N mu ; B -60 -207 516 449 ;
+C -1 ; WX 278 ; N igrave ; B 2 -9 259 697 ;
+C -1 ; WX 500 ; N ohungarumlaut ; B -3 -13 582 697 ;
+C -1 ; WX 667 ; N Eogonek ; B -27 -183 653 669 ;
+C -1 ; WX 500 ; N dcroat ; B -21 -13 552 699 ;
+C -1 ; WX 750 ; N threequarters ; B 7 -14 726 683 ;
+C -1 ; WX 556 ; N Scedilla ; B 2 -218 526 685 ;
+C -1 ; WX 382 ; N lcaron ; B 2 -9 448 708 ;
+C -1 ; WX 667 ; N Kcommaaccent ; B -21 -218 702 669 ;
+C -1 ; WX 611 ; N Lacute ; B -22 0 590 904 ;
+C -1 ; WX 1000 ; N trademark ; B 32 263 968 669 ;
+C -1 ; WX 444 ; N edotaccent ; B 5 -13 398 655 ;
+C -1 ; WX 389 ; N Igrave ; B -32 0 406 904 ;
+C -1 ; WX 389 ; N Imacron ; B -32 0 461 830 ;
+C -1 ; WX 611 ; N Lcaron ; B -22 0 671 718 ;
+C -1 ; WX 750 ; N onehalf ; B -9 -14 723 683 ;
+C -1 ; WX 549 ; N lessequal ; B 29 0 526 704 ;
+C -1 ; WX 500 ; N ocircumflex ; B -3 -13 451 690 ;
+C -1 ; WX 556 ; N ntilde ; B -6 -9 504 655 ;
+C -1 ; WX 722 ; N Uhungarumlaut ; B 67 -18 744 904 ;
+C -1 ; WX 667 ; N Eacute ; B -27 0 653 904 ;
+C -1 ; WX 444 ; N emacron ; B 5 -13 439 623 ;
+C -1 ; WX 500 ; N gbreve ; B -52 -203 478 678 ;
+C -1 ; WX 750 ; N onequarter ; B 7 -14 721 683 ;
+C -1 ; WX 556 ; N Scaron ; B 2 -18 553 897 ;
+C -1 ; WX 556 ; N Scommaaccent ; B 2 -218 526 685 ;
+C -1 ; WX 722 ; N Ohungarumlaut ; B 27 -18 723 904 ;
+C -1 ; WX 400 ; N degree ; B 83 397 369 683 ;
+C -1 ; WX 500 ; N ograve ; B -3 -13 441 697 ;
+C -1 ; WX 667 ; N Ccaron ; B 32 -18 677 897 ;
+C -1 ; WX 556 ; N ugrave ; B 15 -9 492 697 ;
+C -1 ; WX 549 ; N radical ; B 10 -46 512 850 ;
+C -1 ; WX 722 ; N Dcaron ; B -46 0 685 897 ;
+C -1 ; WX 389 ; N rcommaaccent ; B -67 -218 389 462 ;
+C -1 ; WX 722 ; N Ntilde ; B -27 -15 748 862 ;
+C -1 ; WX 500 ; N otilde ; B -3 -13 491 655 ;
+C -1 ; WX 667 ; N Rcommaaccent ; B -29 -218 623 669 ;
+C -1 ; WX 611 ; N Lcommaaccent ; B -22 -218 590 669 ;
+C -1 ; WX 667 ; N Atilde ; B -67 0 593 862 ;
+C -1 ; WX 667 ; N Aogonek ; B -67 -183 604 683 ;
+C -1 ; WX 667 ; N Aring ; B -67 0 593 921 ;
+C -1 ; WX 722 ; N Otilde ; B 27 -18 691 862 ;
+C -1 ; WX 389 ; N zdotaccent ; B -43 -78 368 655 ;
+C -1 ; WX 667 ; N Ecaron ; B -27 0 653 897 ;
+C -1 ; WX 389 ; N Iogonek ; B -32 -183 406 669 ;
+C -1 ; WX 500 ; N kcommaaccent ; B -23 -218 483 699 ;
+C -1 ; WX 606 ; N minus ; B 51 209 555 297 ;
+C -1 ; WX 389 ; N Icircumflex ; B -32 0 450 897 ;
+C -1 ; WX 556 ; N ncaron ; B -6 -9 523 690 ;
+C -1 ; WX 278 ; N tcommaaccent ; B -62 -218 281 594 ;
+C -1 ; WX 606 ; N logicalnot ; B 51 108 555 399 ;
+C -1 ; WX 500 ; N odieresis ; B -3 -13 471 655 ;
+C -1 ; WX 556 ; N udieresis ; B 15 -9 499 655 ;
+C -1 ; WX 549 ; N notequal ; B 15 -49 540 570 ;
+C -1 ; WX 500 ; N gcommaaccent ; B -52 -203 478 767 ;
+C -1 ; WX 500 ; N eth ; B -3 -13 454 699 ;
+C -1 ; WX 389 ; N zcaron ; B -43 -78 424 690 ;
+C -1 ; WX 556 ; N ncommaaccent ; B -6 -218 493 462 ;
+C -1 ; WX 300 ; N onesuperior ; B 30 274 301 683 ;
+C -1 ; WX 278 ; N imacron ; B 2 -9 294 623 ;
+C -1 ; WX 500 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+StartKernData
+StartKernPairs 2038
+KPX A C -65
+KPX A Cacute -65
+KPX A Ccaron -65
+KPX A Ccedilla -65
+KPX A G -60
+KPX A Gbreve -60
+KPX A Gcommaaccent -60
+KPX A O -50
+KPX A Oacute -50
+KPX A Ocircumflex -50
+KPX A Odieresis -50
+KPX A Ograve -50
+KPX A Ohungarumlaut -50
+KPX A Omacron -50
+KPX A Oslash -50
+KPX A Otilde -50
+KPX A Q -55
+KPX A T -55
+KPX A Tcaron -55
+KPX A Tcommaaccent -55
+KPX A U -50
+KPX A Uacute -50
+KPX A Ucircumflex -50
+KPX A Udieresis -50
+KPX A Ugrave -50
+KPX A Uhungarumlaut -50
+KPX A Umacron -50
+KPX A Uogonek -50
+KPX A Uring -50
+KPX A V -95
+KPX A W -100
+KPX A Y -70
+KPX A Yacute -70
+KPX A Ydieresis -70
+KPX A quoteright -74
+KPX A u -30
+KPX A uacute -30
+KPX A ucircumflex -30
+KPX A udieresis -30
+KPX A ugrave -30
+KPX A uhungarumlaut -30
+KPX A umacron -30
+KPX A uogonek -30
+KPX A uring -30
+KPX A v -74
+KPX A w -74
+KPX A y -74
+KPX A yacute -74
+KPX A ydieresis -74
+KPX Aacute C -65
+KPX Aacute Cacute -65
+KPX Aacute Ccaron -65
+KPX Aacute Ccedilla -65
+KPX Aacute G -60
+KPX Aacute Gbreve -60
+KPX Aacute Gcommaaccent -60
+KPX Aacute O -50
+KPX Aacute Oacute -50
+KPX Aacute Ocircumflex -50
+KPX Aacute Odieresis -50
+KPX Aacute Ograve -50
+KPX Aacute Ohungarumlaut -50
+KPX Aacute Omacron -50
+KPX Aacute Oslash -50
+KPX Aacute Otilde -50
+KPX Aacute Q -55
+KPX Aacute T -55
+KPX Aacute Tcaron -55
+KPX Aacute Tcommaaccent -55
+KPX Aacute U -50
+KPX Aacute Uacute -50
+KPX Aacute Ucircumflex -50
+KPX Aacute Udieresis -50
+KPX Aacute Ugrave -50
+KPX Aacute Uhungarumlaut -50
+KPX Aacute Umacron -50
+KPX Aacute Uogonek -50
+KPX Aacute Uring -50
+KPX Aacute V -95
+KPX Aacute W -100
+KPX Aacute Y -70
+KPX Aacute Yacute -70
+KPX Aacute Ydieresis -70
+KPX Aacute quoteright -74
+KPX Aacute u -30
+KPX Aacute uacute -30
+KPX Aacute ucircumflex -30
+KPX Aacute udieresis -30
+KPX Aacute ugrave -30
+KPX Aacute uhungarumlaut -30
+KPX Aacute umacron -30
+KPX Aacute uogonek -30
+KPX Aacute uring -30
+KPX Aacute v -74
+KPX Aacute w -74
+KPX Aacute y -74
+KPX Aacute yacute -74
+KPX Aacute ydieresis -74
+KPX Abreve C -65
+KPX Abreve Cacute -65
+KPX Abreve Ccaron -65
+KPX Abreve Ccedilla -65
+KPX Abreve G -60
+KPX Abreve Gbreve -60
+KPX Abreve Gcommaaccent -60
+KPX Abreve O -50
+KPX Abreve Oacute -50
+KPX Abreve Ocircumflex -50
+KPX Abreve Odieresis -50
+KPX Abreve Ograve -50
+KPX Abreve Ohungarumlaut -50
+KPX Abreve Omacron -50
+KPX Abreve Oslash -50
+KPX Abreve Otilde -50
+KPX Abreve Q -55
+KPX Abreve T -55
+KPX Abreve Tcaron -55
+KPX Abreve Tcommaaccent -55
+KPX Abreve U -50
+KPX Abreve Uacute -50
+KPX Abreve Ucircumflex -50
+KPX Abreve Udieresis -50
+KPX Abreve Ugrave -50
+KPX Abreve Uhungarumlaut -50
+KPX Abreve Umacron -50
+KPX Abreve Uogonek -50
+KPX Abreve Uring -50
+KPX Abreve V -95
+KPX Abreve W -100
+KPX Abreve Y -70
+KPX Abreve Yacute -70
+KPX Abreve Ydieresis -70
+KPX Abreve quoteright -74
+KPX Abreve u -30
+KPX Abreve uacute -30
+KPX Abreve ucircumflex -30
+KPX Abreve udieresis -30
+KPX Abreve ugrave -30
+KPX Abreve uhungarumlaut -30
+KPX Abreve umacron -30
+KPX Abreve uogonek -30
+KPX Abreve uring -30
+KPX Abreve v -74
+KPX Abreve w -74
+KPX Abreve y -74
+KPX Abreve yacute -74
+KPX Abreve ydieresis -74
+KPX Acircumflex C -65
+KPX Acircumflex Cacute -65
+KPX Acircumflex Ccaron -65
+KPX Acircumflex Ccedilla -65
+KPX Acircumflex G -60
+KPX Acircumflex Gbreve -60
+KPX Acircumflex Gcommaaccent -60
+KPX Acircumflex O -50
+KPX Acircumflex Oacute -50
+KPX Acircumflex Ocircumflex -50
+KPX Acircumflex Odieresis -50
+KPX Acircumflex Ograve -50
+KPX Acircumflex Ohungarumlaut -50
+KPX Acircumflex Omacron -50
+KPX Acircumflex Oslash -50
+KPX Acircumflex Otilde -50
+KPX Acircumflex Q -55
+KPX Acircumflex T -55
+KPX Acircumflex Tcaron -55
+KPX Acircumflex Tcommaaccent -55
+KPX Acircumflex U -50
+KPX Acircumflex Uacute -50
+KPX Acircumflex Ucircumflex -50
+KPX Acircumflex Udieresis -50
+KPX Acircumflex Ugrave -50
+KPX Acircumflex Uhungarumlaut -50
+KPX Acircumflex Umacron -50
+KPX Acircumflex Uogonek -50
+KPX Acircumflex Uring -50
+KPX Acircumflex V -95
+KPX Acircumflex W -100
+KPX Acircumflex Y -70
+KPX Acircumflex Yacute -70
+KPX Acircumflex Ydieresis -70
+KPX Acircumflex quoteright -74
+KPX Acircumflex u -30
+KPX Acircumflex uacute -30
+KPX Acircumflex ucircumflex -30
+KPX Acircumflex udieresis -30
+KPX Acircumflex ugrave -30
+KPX Acircumflex uhungarumlaut -30
+KPX Acircumflex umacron -30
+KPX Acircumflex uogonek -30
+KPX Acircumflex uring -30
+KPX Acircumflex v -74
+KPX Acircumflex w -74
+KPX Acircumflex y -74
+KPX Acircumflex yacute -74
+KPX Acircumflex ydieresis -74
+KPX Adieresis C -65
+KPX Adieresis Cacute -65
+KPX Adieresis Ccaron -65
+KPX Adieresis Ccedilla -65
+KPX Adieresis G -60
+KPX Adieresis Gbreve -60
+KPX Adieresis Gcommaaccent -60
+KPX Adieresis O -50
+KPX Adieresis Oacute -50
+KPX Adieresis Ocircumflex -50
+KPX Adieresis Odieresis -50
+KPX Adieresis Ograve -50
+KPX Adieresis Ohungarumlaut -50
+KPX Adieresis Omacron -50
+KPX Adieresis Oslash -50
+KPX Adieresis Otilde -50
+KPX Adieresis Q -55
+KPX Adieresis T -55
+KPX Adieresis Tcaron -55
+KPX Adieresis Tcommaaccent -55
+KPX Adieresis U -50
+KPX Adieresis Uacute -50
+KPX Adieresis Ucircumflex -50
+KPX Adieresis Udieresis -50
+KPX Adieresis Ugrave -50
+KPX Adieresis Uhungarumlaut -50
+KPX Adieresis Umacron -50
+KPX Adieresis Uogonek -50
+KPX Adieresis Uring -50
+KPX Adieresis V -95
+KPX Adieresis W -100
+KPX Adieresis Y -70
+KPX Adieresis Yacute -70
+KPX Adieresis Ydieresis -70
+KPX Adieresis quoteright -74
+KPX Adieresis u -30
+KPX Adieresis uacute -30
+KPX Adieresis ucircumflex -30
+KPX Adieresis udieresis -30
+KPX Adieresis ugrave -30
+KPX Adieresis uhungarumlaut -30
+KPX Adieresis umacron -30
+KPX Adieresis uogonek -30
+KPX Adieresis uring -30
+KPX Adieresis v -74
+KPX Adieresis w -74
+KPX Adieresis y -74
+KPX Adieresis yacute -74
+KPX Adieresis ydieresis -74
+KPX Agrave C -65
+KPX Agrave Cacute -65
+KPX Agrave Ccaron -65
+KPX Agrave Ccedilla -65
+KPX Agrave G -60
+KPX Agrave Gbreve -60
+KPX Agrave Gcommaaccent -60
+KPX Agrave O -50
+KPX Agrave Oacute -50
+KPX Agrave Ocircumflex -50
+KPX Agrave Odieresis -50
+KPX Agrave Ograve -50
+KPX Agrave Ohungarumlaut -50
+KPX Agrave Omacron -50
+KPX Agrave Oslash -50
+KPX Agrave Otilde -50
+KPX Agrave Q -55
+KPX Agrave T -55
+KPX Agrave Tcaron -55
+KPX Agrave Tcommaaccent -55
+KPX Agrave U -50
+KPX Agrave Uacute -50
+KPX Agrave Ucircumflex -50
+KPX Agrave Udieresis -50
+KPX Agrave Ugrave -50
+KPX Agrave Uhungarumlaut -50
+KPX Agrave Umacron -50
+KPX Agrave Uogonek -50
+KPX Agrave Uring -50
+KPX Agrave V -95
+KPX Agrave W -100
+KPX Agrave Y -70
+KPX Agrave Yacute -70
+KPX Agrave Ydieresis -70
+KPX Agrave quoteright -74
+KPX Agrave u -30
+KPX Agrave uacute -30
+KPX Agrave ucircumflex -30
+KPX Agrave udieresis -30
+KPX Agrave ugrave -30
+KPX Agrave uhungarumlaut -30
+KPX Agrave umacron -30
+KPX Agrave uogonek -30
+KPX Agrave uring -30
+KPX Agrave v -74
+KPX Agrave w -74
+KPX Agrave y -74
+KPX Agrave yacute -74
+KPX Agrave ydieresis -74
+KPX Amacron C -65
+KPX Amacron Cacute -65
+KPX Amacron Ccaron -65
+KPX Amacron Ccedilla -65
+KPX Amacron G -60
+KPX Amacron Gbreve -60
+KPX Amacron Gcommaaccent -60
+KPX Amacron O -50
+KPX Amacron Oacute -50
+KPX Amacron Ocircumflex -50
+KPX Amacron Odieresis -50
+KPX Amacron Ograve -50
+KPX Amacron Ohungarumlaut -50
+KPX Amacron Omacron -50
+KPX Amacron Oslash -50
+KPX Amacron Otilde -50
+KPX Amacron Q -55
+KPX Amacron T -55
+KPX Amacron Tcaron -55
+KPX Amacron Tcommaaccent -55
+KPX Amacron U -50
+KPX Amacron Uacute -50
+KPX Amacron Ucircumflex -50
+KPX Amacron Udieresis -50
+KPX Amacron Ugrave -50
+KPX Amacron Uhungarumlaut -50
+KPX Amacron Umacron -50
+KPX Amacron Uogonek -50
+KPX Amacron Uring -50
+KPX Amacron V -95
+KPX Amacron W -100
+KPX Amacron Y -70
+KPX Amacron Yacute -70
+KPX Amacron Ydieresis -70
+KPX Amacron quoteright -74
+KPX Amacron u -30
+KPX Amacron uacute -30
+KPX Amacron ucircumflex -30
+KPX Amacron udieresis -30
+KPX Amacron ugrave -30
+KPX Amacron uhungarumlaut -30
+KPX Amacron umacron -30
+KPX Amacron uogonek -30
+KPX Amacron uring -30
+KPX Amacron v -74
+KPX Amacron w -74
+KPX Amacron y -74
+KPX Amacron yacute -74
+KPX Amacron ydieresis -74
+KPX Aogonek C -65
+KPX Aogonek Cacute -65
+KPX Aogonek Ccaron -65
+KPX Aogonek Ccedilla -65
+KPX Aogonek G -60
+KPX Aogonek Gbreve -60
+KPX Aogonek Gcommaaccent -60
+KPX Aogonek O -50
+KPX Aogonek Oacute -50
+KPX Aogonek Ocircumflex -50
+KPX Aogonek Odieresis -50
+KPX Aogonek Ograve -50
+KPX Aogonek Ohungarumlaut -50
+KPX Aogonek Omacron -50
+KPX Aogonek Oslash -50
+KPX Aogonek Otilde -50
+KPX Aogonek Q -55
+KPX Aogonek T -55
+KPX Aogonek Tcaron -55
+KPX Aogonek Tcommaaccent -55
+KPX Aogonek U -50
+KPX Aogonek Uacute -50
+KPX Aogonek Ucircumflex -50
+KPX Aogonek Udieresis -50
+KPX Aogonek Ugrave -50
+KPX Aogonek Uhungarumlaut -50
+KPX Aogonek Umacron -50
+KPX Aogonek Uogonek -50
+KPX Aogonek Uring -50
+KPX Aogonek V -95
+KPX Aogonek W -100
+KPX Aogonek Y -70
+KPX Aogonek Yacute -70
+KPX Aogonek Ydieresis -70
+KPX Aogonek quoteright -74
+KPX Aogonek u -30
+KPX Aogonek uacute -30
+KPX Aogonek ucircumflex -30
+KPX Aogonek udieresis -30
+KPX Aogonek ugrave -30
+KPX Aogonek uhungarumlaut -30
+KPX Aogonek umacron -30
+KPX Aogonek uogonek -30
+KPX Aogonek uring -30
+KPX Aogonek v -74
+KPX Aogonek w -74
+KPX Aogonek y -34
+KPX Aogonek yacute -34
+KPX Aogonek ydieresis -34
+KPX Aring C -65
+KPX Aring Cacute -65
+KPX Aring Ccaron -65
+KPX Aring Ccedilla -65
+KPX Aring G -60
+KPX Aring Gbreve -60
+KPX Aring Gcommaaccent -60
+KPX Aring O -50
+KPX Aring Oacute -50
+KPX Aring Ocircumflex -50
+KPX Aring Odieresis -50
+KPX Aring Ograve -50
+KPX Aring Ohungarumlaut -50
+KPX Aring Omacron -50
+KPX Aring Oslash -50
+KPX Aring Otilde -50
+KPX Aring Q -55
+KPX Aring T -55
+KPX Aring Tcaron -55
+KPX Aring Tcommaaccent -55
+KPX Aring U -50
+KPX Aring Uacute -50
+KPX Aring Ucircumflex -50
+KPX Aring Udieresis -50
+KPX Aring Ugrave -50
+KPX Aring Uhungarumlaut -50
+KPX Aring Umacron -50
+KPX Aring Uogonek -50
+KPX Aring Uring -50
+KPX Aring V -95
+KPX Aring W -100
+KPX Aring Y -70
+KPX Aring Yacute -70
+KPX Aring Ydieresis -70
+KPX Aring quoteright -74
+KPX Aring u -30
+KPX Aring uacute -30
+KPX Aring ucircumflex -30
+KPX Aring udieresis -30
+KPX Aring ugrave -30
+KPX Aring uhungarumlaut -30
+KPX Aring umacron -30
+KPX Aring uogonek -30
+KPX Aring uring -30
+KPX Aring v -74
+KPX Aring w -74
+KPX Aring y -74
+KPX Aring yacute -74
+KPX Aring ydieresis -74
+KPX Atilde C -65
+KPX Atilde Cacute -65
+KPX Atilde Ccaron -65
+KPX Atilde Ccedilla -65
+KPX Atilde G -60
+KPX Atilde Gbreve -60
+KPX Atilde Gcommaaccent -60
+KPX Atilde O -50
+KPX Atilde Oacute -50
+KPX Atilde Ocircumflex -50
+KPX Atilde Odieresis -50
+KPX Atilde Ograve -50
+KPX Atilde Ohungarumlaut -50
+KPX Atilde Omacron -50
+KPX Atilde Oslash -50
+KPX Atilde Otilde -50
+KPX Atilde Q -55
+KPX Atilde T -55
+KPX Atilde Tcaron -55
+KPX Atilde Tcommaaccent -55
+KPX Atilde U -50
+KPX Atilde Uacute -50
+KPX Atilde Ucircumflex -50
+KPX Atilde Udieresis -50
+KPX Atilde Ugrave -50
+KPX Atilde Uhungarumlaut -50
+KPX Atilde Umacron -50
+KPX Atilde Uogonek -50
+KPX Atilde Uring -50
+KPX Atilde V -95
+KPX Atilde W -100
+KPX Atilde Y -70
+KPX Atilde Yacute -70
+KPX Atilde Ydieresis -70
+KPX Atilde quoteright -74
+KPX Atilde u -30
+KPX Atilde uacute -30
+KPX Atilde ucircumflex -30
+KPX Atilde udieresis -30
+KPX Atilde ugrave -30
+KPX Atilde uhungarumlaut -30
+KPX Atilde umacron -30
+KPX Atilde uogonek -30
+KPX Atilde uring -30
+KPX Atilde v -74
+KPX Atilde w -74
+KPX Atilde y -74
+KPX Atilde yacute -74
+KPX Atilde ydieresis -74
+KPX B A -25
+KPX B Aacute -25
+KPX B Abreve -25
+KPX B Acircumflex -25
+KPX B Adieresis -25
+KPX B Agrave -25
+KPX B Amacron -25
+KPX B Aogonek -25
+KPX B Aring -25
+KPX B Atilde -25
+KPX B U -10
+KPX B Uacute -10
+KPX B Ucircumflex -10
+KPX B Udieresis -10
+KPX B Ugrave -10
+KPX B Uhungarumlaut -10
+KPX B Umacron -10
+KPX B Uogonek -10
+KPX B Uring -10
+KPX D A -25
+KPX D Aacute -25
+KPX D Abreve -25
+KPX D Acircumflex -25
+KPX D Adieresis -25
+KPX D Agrave -25
+KPX D Amacron -25
+KPX D Aogonek -25
+KPX D Aring -25
+KPX D Atilde -25
+KPX D V -50
+KPX D W -40
+KPX D Y -50
+KPX D Yacute -50
+KPX D Ydieresis -50
+KPX Dcaron A -25
+KPX Dcaron Aacute -25
+KPX Dcaron Abreve -25
+KPX Dcaron Acircumflex -25
+KPX Dcaron Adieresis -25
+KPX Dcaron Agrave -25
+KPX Dcaron Amacron -25
+KPX Dcaron Aogonek -25
+KPX Dcaron Aring -25
+KPX Dcaron Atilde -25
+KPX Dcaron V -50
+KPX Dcaron W -40
+KPX Dcaron Y -50
+KPX Dcaron Yacute -50
+KPX Dcaron Ydieresis -50
+KPX Dcroat A -25
+KPX Dcroat Aacute -25
+KPX Dcroat Abreve -25
+KPX Dcroat Acircumflex -25
+KPX Dcroat Adieresis -25
+KPX Dcroat Agrave -25
+KPX Dcroat Amacron -25
+KPX Dcroat Aogonek -25
+KPX Dcroat Aring -25
+KPX Dcroat Atilde -25
+KPX Dcroat V -50
+KPX Dcroat W -40
+KPX Dcroat Y -50
+KPX Dcroat Yacute -50
+KPX Dcroat Ydieresis -50
+KPX F A -100
+KPX F Aacute -100
+KPX F Abreve -100
+KPX F Acircumflex -100
+KPX F Adieresis -100
+KPX F Agrave -100
+KPX F Amacron -100
+KPX F Aogonek -100
+KPX F Aring -100
+KPX F Atilde -100
+KPX F a -95
+KPX F aacute -95
+KPX F abreve -95
+KPX F acircumflex -95
+KPX F adieresis -95
+KPX F agrave -95
+KPX F amacron -95
+KPX F aogonek -95
+KPX F aring -95
+KPX F atilde -95
+KPX F comma -129
+KPX F e -100
+KPX F eacute -100
+KPX F ecaron -100
+KPX F ecircumflex -100
+KPX F edieresis -100
+KPX F edotaccent -100
+KPX F egrave -100
+KPX F emacron -100
+KPX F eogonek -100
+KPX F i -40
+KPX F iacute -40
+KPX F icircumflex -40
+KPX F idieresis -40
+KPX F igrave -40
+KPX F imacron -40
+KPX F iogonek -40
+KPX F o -70
+KPX F oacute -70
+KPX F ocircumflex -70
+KPX F odieresis -70
+KPX F ograve -70
+KPX F ohungarumlaut -70
+KPX F omacron -70
+KPX F oslash -70
+KPX F otilde -70
+KPX F period -129
+KPX F r -50
+KPX F racute -50
+KPX F rcaron -50
+KPX F rcommaaccent -50
+KPX J A -25
+KPX J Aacute -25
+KPX J Abreve -25
+KPX J Acircumflex -25
+KPX J Adieresis -25
+KPX J Agrave -25
+KPX J Amacron -25
+KPX J Aogonek -25
+KPX J Aring -25
+KPX J Atilde -25
+KPX J a -40
+KPX J aacute -40
+KPX J abreve -40
+KPX J acircumflex -40
+KPX J adieresis -40
+KPX J agrave -40
+KPX J amacron -40
+KPX J aogonek -40
+KPX J aring -40
+KPX J atilde -40
+KPX J comma -10
+KPX J e -40
+KPX J eacute -40
+KPX J ecaron -40
+KPX J ecircumflex -40
+KPX J edieresis -40
+KPX J edotaccent -40
+KPX J egrave -40
+KPX J emacron -40
+KPX J eogonek -40
+KPX J o -40
+KPX J oacute -40
+KPX J ocircumflex -40
+KPX J odieresis -40
+KPX J ograve -40
+KPX J ohungarumlaut -40
+KPX J omacron -40
+KPX J oslash -40
+KPX J otilde -40
+KPX J period -10
+KPX J u -40
+KPX J uacute -40
+KPX J ucircumflex -40
+KPX J udieresis -40
+KPX J ugrave -40
+KPX J uhungarumlaut -40
+KPX J umacron -40
+KPX J uogonek -40
+KPX J uring -40
+KPX K O -30
+KPX K Oacute -30
+KPX K Ocircumflex -30
+KPX K Odieresis -30
+KPX K Ograve -30
+KPX K Ohungarumlaut -30
+KPX K Omacron -30
+KPX K Oslash -30
+KPX K Otilde -30
+KPX K e -25
+KPX K eacute -25
+KPX K ecaron -25
+KPX K ecircumflex -25
+KPX K edieresis -25
+KPX K edotaccent -25
+KPX K egrave -25
+KPX K emacron -25
+KPX K eogonek -25
+KPX K o -25
+KPX K oacute -25
+KPX K ocircumflex -25
+KPX K odieresis -25
+KPX K ograve -25
+KPX K ohungarumlaut -25
+KPX K omacron -25
+KPX K oslash -25
+KPX K otilde -25
+KPX K u -20
+KPX K uacute -20
+KPX K ucircumflex -20
+KPX K udieresis -20
+KPX K ugrave -20
+KPX K uhungarumlaut -20
+KPX K umacron -20
+KPX K uogonek -20
+KPX K uring -20
+KPX K y -20
+KPX K yacute -20
+KPX K ydieresis -20
+KPX Kcommaaccent O -30
+KPX Kcommaaccent Oacute -30
+KPX Kcommaaccent Ocircumflex -30
+KPX Kcommaaccent Odieresis -30
+KPX Kcommaaccent Ograve -30
+KPX Kcommaaccent Ohungarumlaut -30
+KPX Kcommaaccent Omacron -30
+KPX Kcommaaccent Oslash -30
+KPX Kcommaaccent Otilde -30
+KPX Kcommaaccent e -25
+KPX Kcommaaccent eacute -25
+KPX Kcommaaccent ecaron -25
+KPX Kcommaaccent ecircumflex -25
+KPX Kcommaaccent edieresis -25
+KPX Kcommaaccent edotaccent -25
+KPX Kcommaaccent egrave -25
+KPX Kcommaaccent emacron -25
+KPX Kcommaaccent eogonek -25
+KPX Kcommaaccent o -25
+KPX Kcommaaccent oacute -25
+KPX Kcommaaccent ocircumflex -25
+KPX Kcommaaccent odieresis -25
+KPX Kcommaaccent ograve -25
+KPX Kcommaaccent ohungarumlaut -25
+KPX Kcommaaccent omacron -25
+KPX Kcommaaccent oslash -25
+KPX Kcommaaccent otilde -25
+KPX Kcommaaccent u -20
+KPX Kcommaaccent uacute -20
+KPX Kcommaaccent ucircumflex -20
+KPX Kcommaaccent udieresis -20
+KPX Kcommaaccent ugrave -20
+KPX Kcommaaccent uhungarumlaut -20
+KPX Kcommaaccent umacron -20
+KPX Kcommaaccent uogonek -20
+KPX Kcommaaccent uring -20
+KPX Kcommaaccent y -20
+KPX Kcommaaccent yacute -20
+KPX Kcommaaccent ydieresis -20
+KPX L T -18
+KPX L Tcaron -18
+KPX L Tcommaaccent -18
+KPX L V -37
+KPX L W -37
+KPX L Y -37
+KPX L Yacute -37
+KPX L Ydieresis -37
+KPX L quoteright -55
+KPX L y -37
+KPX L yacute -37
+KPX L ydieresis -37
+KPX Lacute T -18
+KPX Lacute Tcaron -18
+KPX Lacute Tcommaaccent -18
+KPX Lacute V -37
+KPX Lacute W -37
+KPX Lacute Y -37
+KPX Lacute Yacute -37
+KPX Lacute Ydieresis -37
+KPX Lacute quoteright -55
+KPX Lacute y -37
+KPX Lacute yacute -37
+KPX Lacute ydieresis -37
+KPX Lcommaaccent T -18
+KPX Lcommaaccent Tcaron -18
+KPX Lcommaaccent Tcommaaccent -18
+KPX Lcommaaccent V -37
+KPX Lcommaaccent W -37
+KPX Lcommaaccent Y -37
+KPX Lcommaaccent Yacute -37
+KPX Lcommaaccent Ydieresis -37
+KPX Lcommaaccent quoteright -55
+KPX Lcommaaccent y -37
+KPX Lcommaaccent yacute -37
+KPX Lcommaaccent ydieresis -37
+KPX Lslash T -18
+KPX Lslash Tcaron -18
+KPX Lslash Tcommaaccent -18
+KPX Lslash V -37
+KPX Lslash W -37
+KPX Lslash Y -37
+KPX Lslash Yacute -37
+KPX Lslash Ydieresis -37
+KPX Lslash quoteright -55
+KPX Lslash y -37
+KPX Lslash yacute -37
+KPX Lslash ydieresis -37
+KPX N A -30
+KPX N Aacute -30
+KPX N Abreve -30
+KPX N Acircumflex -30
+KPX N Adieresis -30
+KPX N Agrave -30
+KPX N Amacron -30
+KPX N Aogonek -30
+KPX N Aring -30
+KPX N Atilde -30
+KPX Nacute A -30
+KPX Nacute Aacute -30
+KPX Nacute Abreve -30
+KPX Nacute Acircumflex -30
+KPX Nacute Adieresis -30
+KPX Nacute Agrave -30
+KPX Nacute Amacron -30
+KPX Nacute Aogonek -30
+KPX Nacute Aring -30
+KPX Nacute Atilde -30
+KPX Ncaron A -30
+KPX Ncaron Aacute -30
+KPX Ncaron Abreve -30
+KPX Ncaron Acircumflex -30
+KPX Ncaron Adieresis -30
+KPX Ncaron Agrave -30
+KPX Ncaron Amacron -30
+KPX Ncaron Aogonek -30
+KPX Ncaron Aring -30
+KPX Ncaron Atilde -30
+KPX Ncommaaccent A -30
+KPX Ncommaaccent Aacute -30
+KPX Ncommaaccent Abreve -30
+KPX Ncommaaccent Acircumflex -30
+KPX Ncommaaccent Adieresis -30
+KPX Ncommaaccent Agrave -30
+KPX Ncommaaccent Amacron -30
+KPX Ncommaaccent Aogonek -30
+KPX Ncommaaccent Aring -30
+KPX Ncommaaccent Atilde -30
+KPX Ntilde A -30
+KPX Ntilde Aacute -30
+KPX Ntilde Abreve -30
+KPX Ntilde Acircumflex -30
+KPX Ntilde Adieresis -30
+KPX Ntilde Agrave -30
+KPX Ntilde Amacron -30
+KPX Ntilde Aogonek -30
+KPX Ntilde Aring -30
+KPX Ntilde Atilde -30
+KPX O A -40
+KPX O Aacute -40
+KPX O Abreve -40
+KPX O Acircumflex -40
+KPX O Adieresis -40
+KPX O Agrave -40
+KPX O Amacron -40
+KPX O Aogonek -40
+KPX O Aring -40
+KPX O Atilde -40
+KPX O T -40
+KPX O Tcaron -40
+KPX O Tcommaaccent -40
+KPX O V -50
+KPX O W -50
+KPX O X -40
+KPX O Y -50
+KPX O Yacute -50
+KPX O Ydieresis -50
+KPX Oacute A -40
+KPX Oacute Aacute -40
+KPX Oacute Abreve -40
+KPX Oacute Acircumflex -40
+KPX Oacute Adieresis -40
+KPX Oacute Agrave -40
+KPX Oacute Amacron -40
+KPX Oacute Aogonek -40
+KPX Oacute Aring -40
+KPX Oacute Atilde -40
+KPX Oacute T -40
+KPX Oacute Tcaron -40
+KPX Oacute Tcommaaccent -40
+KPX Oacute V -50
+KPX Oacute W -50
+KPX Oacute X -40
+KPX Oacute Y -50
+KPX Oacute Yacute -50
+KPX Oacute Ydieresis -50
+KPX Ocircumflex A -40
+KPX Ocircumflex Aacute -40
+KPX Ocircumflex Abreve -40
+KPX Ocircumflex Acircumflex -40
+KPX Ocircumflex Adieresis -40
+KPX Ocircumflex Agrave -40
+KPX Ocircumflex Amacron -40
+KPX Ocircumflex Aogonek -40
+KPX Ocircumflex Aring -40
+KPX Ocircumflex Atilde -40
+KPX Ocircumflex T -40
+KPX Ocircumflex Tcaron -40
+KPX Ocircumflex Tcommaaccent -40
+KPX Ocircumflex V -50
+KPX Ocircumflex W -50
+KPX Ocircumflex X -40
+KPX Ocircumflex Y -50
+KPX Ocircumflex Yacute -50
+KPX Ocircumflex Ydieresis -50
+KPX Odieresis A -40
+KPX Odieresis Aacute -40
+KPX Odieresis Abreve -40
+KPX Odieresis Acircumflex -40
+KPX Odieresis Adieresis -40
+KPX Odieresis Agrave -40
+KPX Odieresis Amacron -40
+KPX Odieresis Aogonek -40
+KPX Odieresis Aring -40
+KPX Odieresis Atilde -40
+KPX Odieresis T -40
+KPX Odieresis Tcaron -40
+KPX Odieresis Tcommaaccent -40
+KPX Odieresis V -50
+KPX Odieresis W -50
+KPX Odieresis X -40
+KPX Odieresis Y -50
+KPX Odieresis Yacute -50
+KPX Odieresis Ydieresis -50
+KPX Ograve A -40
+KPX Ograve Aacute -40
+KPX Ograve Abreve -40
+KPX Ograve Acircumflex -40
+KPX Ograve Adieresis -40
+KPX Ograve Agrave -40
+KPX Ograve Amacron -40
+KPX Ograve Aogonek -40
+KPX Ograve Aring -40
+KPX Ograve Atilde -40
+KPX Ograve T -40
+KPX Ograve Tcaron -40
+KPX Ograve Tcommaaccent -40
+KPX Ograve V -50
+KPX Ograve W -50
+KPX Ograve X -40
+KPX Ograve Y -50
+KPX Ograve Yacute -50
+KPX Ograve Ydieresis -50
+KPX Ohungarumlaut A -40
+KPX Ohungarumlaut Aacute -40
+KPX Ohungarumlaut Abreve -40
+KPX Ohungarumlaut Acircumflex -40
+KPX Ohungarumlaut Adieresis -40
+KPX Ohungarumlaut Agrave -40
+KPX Ohungarumlaut Amacron -40
+KPX Ohungarumlaut Aogonek -40
+KPX Ohungarumlaut Aring -40
+KPX Ohungarumlaut Atilde -40
+KPX Ohungarumlaut T -40
+KPX Ohungarumlaut Tcaron -40
+KPX Ohungarumlaut Tcommaaccent -40
+KPX Ohungarumlaut V -50
+KPX Ohungarumlaut W -50
+KPX Ohungarumlaut X -40
+KPX Ohungarumlaut Y -50
+KPX Ohungarumlaut Yacute -50
+KPX Ohungarumlaut Ydieresis -50
+KPX Omacron A -40
+KPX Omacron Aacute -40
+KPX Omacron Abreve -40
+KPX Omacron Acircumflex -40
+KPX Omacron Adieresis -40
+KPX Omacron Agrave -40
+KPX Omacron Amacron -40
+KPX Omacron Aogonek -40
+KPX Omacron Aring -40
+KPX Omacron Atilde -40
+KPX Omacron T -40
+KPX Omacron Tcaron -40
+KPX Omacron Tcommaaccent -40
+KPX Omacron V -50
+KPX Omacron W -50
+KPX Omacron X -40
+KPX Omacron Y -50
+KPX Omacron Yacute -50
+KPX Omacron Ydieresis -50
+KPX Oslash A -40
+KPX Oslash Aacute -40
+KPX Oslash Abreve -40
+KPX Oslash Acircumflex -40
+KPX Oslash Adieresis -40
+KPX Oslash Agrave -40
+KPX Oslash Amacron -40
+KPX Oslash Aogonek -40
+KPX Oslash Aring -40
+KPX Oslash Atilde -40
+KPX Oslash T -40
+KPX Oslash Tcaron -40
+KPX Oslash Tcommaaccent -40
+KPX Oslash V -50
+KPX Oslash W -50
+KPX Oslash X -40
+KPX Oslash Y -50
+KPX Oslash Yacute -50
+KPX Oslash Ydieresis -50
+KPX Otilde A -40
+KPX Otilde Aacute -40
+KPX Otilde Abreve -40
+KPX Otilde Acircumflex -40
+KPX Otilde Adieresis -40
+KPX Otilde Agrave -40
+KPX Otilde Amacron -40
+KPX Otilde Aogonek -40
+KPX Otilde Aring -40
+KPX Otilde Atilde -40
+KPX Otilde T -40
+KPX Otilde Tcaron -40
+KPX Otilde Tcommaaccent -40
+KPX Otilde V -50
+KPX Otilde W -50
+KPX Otilde X -40
+KPX Otilde Y -50
+KPX Otilde Yacute -50
+KPX Otilde Ydieresis -50
+KPX P A -85
+KPX P Aacute -85
+KPX P Abreve -85
+KPX P Acircumflex -85
+KPX P Adieresis -85
+KPX P Agrave -85
+KPX P Amacron -85
+KPX P Aogonek -85
+KPX P Aring -85
+KPX P Atilde -85
+KPX P a -40
+KPX P aacute -40
+KPX P abreve -40
+KPX P acircumflex -40
+KPX P adieresis -40
+KPX P agrave -40
+KPX P amacron -40
+KPX P aogonek -40
+KPX P aring -40
+KPX P atilde -40
+KPX P comma -129
+KPX P e -50
+KPX P eacute -50
+KPX P ecaron -50
+KPX P ecircumflex -50
+KPX P edieresis -50
+KPX P edotaccent -50
+KPX P egrave -50
+KPX P emacron -50
+KPX P eogonek -50
+KPX P o -55
+KPX P oacute -55
+KPX P ocircumflex -55
+KPX P odieresis -55
+KPX P ograve -55
+KPX P ohungarumlaut -55
+KPX P omacron -55
+KPX P oslash -55
+KPX P otilde -55
+KPX P period -129
+KPX Q U -10
+KPX Q Uacute -10
+KPX Q Ucircumflex -10
+KPX Q Udieresis -10
+KPX Q Ugrave -10
+KPX Q Uhungarumlaut -10
+KPX Q Umacron -10
+KPX Q Uogonek -10
+KPX Q Uring -10
+KPX R O -40
+KPX R Oacute -40
+KPX R Ocircumflex -40
+KPX R Odieresis -40
+KPX R Ograve -40
+KPX R Ohungarumlaut -40
+KPX R Omacron -40
+KPX R Oslash -40
+KPX R Otilde -40
+KPX R T -30
+KPX R Tcaron -30
+KPX R Tcommaaccent -30
+KPX R U -40
+KPX R Uacute -40
+KPX R Ucircumflex -40
+KPX R Udieresis -40
+KPX R Ugrave -40
+KPX R Uhungarumlaut -40
+KPX R Umacron -40
+KPX R Uogonek -40
+KPX R Uring -40
+KPX R V -18
+KPX R W -18
+KPX R Y -18
+KPX R Yacute -18
+KPX R Ydieresis -18
+KPX Racute O -40
+KPX Racute Oacute -40
+KPX Racute Ocircumflex -40
+KPX Racute Odieresis -40
+KPX Racute Ograve -40
+KPX Racute Ohungarumlaut -40
+KPX Racute Omacron -40
+KPX Racute Oslash -40
+KPX Racute Otilde -40
+KPX Racute T -30
+KPX Racute Tcaron -30
+KPX Racute Tcommaaccent -30
+KPX Racute U -40
+KPX Racute Uacute -40
+KPX Racute Ucircumflex -40
+KPX Racute Udieresis -40
+KPX Racute Ugrave -40
+KPX Racute Uhungarumlaut -40
+KPX Racute Umacron -40
+KPX Racute Uogonek -40
+KPX Racute Uring -40
+KPX Racute V -18
+KPX Racute W -18
+KPX Racute Y -18
+KPX Racute Yacute -18
+KPX Racute Ydieresis -18
+KPX Rcaron O -40
+KPX Rcaron Oacute -40
+KPX Rcaron Ocircumflex -40
+KPX Rcaron Odieresis -40
+KPX Rcaron Ograve -40
+KPX Rcaron Ohungarumlaut -40
+KPX Rcaron Omacron -40
+KPX Rcaron Oslash -40
+KPX Rcaron Otilde -40
+KPX Rcaron T -30
+KPX Rcaron Tcaron -30
+KPX Rcaron Tcommaaccent -30
+KPX Rcaron U -40
+KPX Rcaron Uacute -40
+KPX Rcaron Ucircumflex -40
+KPX Rcaron Udieresis -40
+KPX Rcaron Ugrave -40
+KPX Rcaron Uhungarumlaut -40
+KPX Rcaron Umacron -40
+KPX Rcaron Uogonek -40
+KPX Rcaron Uring -40
+KPX Rcaron V -18
+KPX Rcaron W -18
+KPX Rcaron Y -18
+KPX Rcaron Yacute -18
+KPX Rcaron Ydieresis -18
+KPX Rcommaaccent O -40
+KPX Rcommaaccent Oacute -40
+KPX Rcommaaccent Ocircumflex -40
+KPX Rcommaaccent Odieresis -40
+KPX Rcommaaccent Ograve -40
+KPX Rcommaaccent Ohungarumlaut -40
+KPX Rcommaaccent Omacron -40
+KPX Rcommaaccent Oslash -40
+KPX Rcommaaccent Otilde -40
+KPX Rcommaaccent T -30
+KPX Rcommaaccent Tcaron -30
+KPX Rcommaaccent Tcommaaccent -30
+KPX Rcommaaccent U -40
+KPX Rcommaaccent Uacute -40
+KPX Rcommaaccent Ucircumflex -40
+KPX Rcommaaccent Udieresis -40
+KPX Rcommaaccent Ugrave -40
+KPX Rcommaaccent Uhungarumlaut -40
+KPX Rcommaaccent Umacron -40
+KPX Rcommaaccent Uogonek -40
+KPX Rcommaaccent Uring -40
+KPX Rcommaaccent V -18
+KPX Rcommaaccent W -18
+KPX Rcommaaccent Y -18
+KPX Rcommaaccent Yacute -18
+KPX Rcommaaccent Ydieresis -18
+KPX T A -55
+KPX T Aacute -55
+KPX T Abreve -55
+KPX T Acircumflex -55
+KPX T Adieresis -55
+KPX T Agrave -55
+KPX T Amacron -55
+KPX T Aogonek -55
+KPX T Aring -55
+KPX T Atilde -55
+KPX T O -18
+KPX T Oacute -18
+KPX T Ocircumflex -18
+KPX T Odieresis -18
+KPX T Ograve -18
+KPX T Ohungarumlaut -18
+KPX T Omacron -18
+KPX T Oslash -18
+KPX T Otilde -18
+KPX T a -92
+KPX T aacute -92
+KPX T abreve -92
+KPX T acircumflex -92
+KPX T adieresis -92
+KPX T agrave -92
+KPX T amacron -92
+KPX T aogonek -92
+KPX T aring -92
+KPX T atilde -92
+KPX T colon -74
+KPX T comma -92
+KPX T e -92
+KPX T eacute -92
+KPX T ecaron -92
+KPX T ecircumflex -92
+KPX T edieresis -52
+KPX T edotaccent -92
+KPX T egrave -52
+KPX T emacron -52
+KPX T eogonek -92
+KPX T hyphen -92
+KPX T i -37
+KPX T iacute -37
+KPX T iogonek -37
+KPX T o -95
+KPX T oacute -95
+KPX T ocircumflex -95
+KPX T odieresis -95
+KPX T ograve -95
+KPX T ohungarumlaut -95
+KPX T omacron -95
+KPX T oslash -95
+KPX T otilde -95
+KPX T period -92
+KPX T r -37
+KPX T racute -37
+KPX T rcaron -37
+KPX T rcommaaccent -37
+KPX T semicolon -74
+KPX T u -37
+KPX T uacute -37
+KPX T ucircumflex -37
+KPX T udieresis -37
+KPX T ugrave -37
+KPX T uhungarumlaut -37
+KPX T umacron -37
+KPX T uogonek -37
+KPX T uring -37
+KPX T w -37
+KPX T y -37
+KPX T yacute -37
+KPX T ydieresis -37
+KPX Tcaron A -55
+KPX Tcaron Aacute -55
+KPX Tcaron Abreve -55
+KPX Tcaron Acircumflex -55
+KPX Tcaron Adieresis -55
+KPX Tcaron Agrave -55
+KPX Tcaron Amacron -55
+KPX Tcaron Aogonek -55
+KPX Tcaron Aring -55
+KPX Tcaron Atilde -55
+KPX Tcaron O -18
+KPX Tcaron Oacute -18
+KPX Tcaron Ocircumflex -18
+KPX Tcaron Odieresis -18
+KPX Tcaron Ograve -18
+KPX Tcaron Ohungarumlaut -18
+KPX Tcaron Omacron -18
+KPX Tcaron Oslash -18
+KPX Tcaron Otilde -18
+KPX Tcaron a -92
+KPX Tcaron aacute -92
+KPX Tcaron abreve -92
+KPX Tcaron acircumflex -92
+KPX Tcaron adieresis -92
+KPX Tcaron agrave -92
+KPX Tcaron amacron -92
+KPX Tcaron aogonek -92
+KPX Tcaron aring -92
+KPX Tcaron atilde -92
+KPX Tcaron colon -74
+KPX Tcaron comma -92
+KPX Tcaron e -92
+KPX Tcaron eacute -92
+KPX Tcaron ecaron -92
+KPX Tcaron ecircumflex -92
+KPX Tcaron edieresis -52
+KPX Tcaron edotaccent -92
+KPX Tcaron egrave -52
+KPX Tcaron emacron -52
+KPX Tcaron eogonek -92
+KPX Tcaron hyphen -92
+KPX Tcaron i -37
+KPX Tcaron iacute -37
+KPX Tcaron iogonek -37
+KPX Tcaron o -95
+KPX Tcaron oacute -95
+KPX Tcaron ocircumflex -95
+KPX Tcaron odieresis -95
+KPX Tcaron ograve -95
+KPX Tcaron ohungarumlaut -95
+KPX Tcaron omacron -95
+KPX Tcaron oslash -95
+KPX Tcaron otilde -95
+KPX Tcaron period -92
+KPX Tcaron r -37
+KPX Tcaron racute -37
+KPX Tcaron rcaron -37
+KPX Tcaron rcommaaccent -37
+KPX Tcaron semicolon -74
+KPX Tcaron u -37
+KPX Tcaron uacute -37
+KPX Tcaron ucircumflex -37
+KPX Tcaron udieresis -37
+KPX Tcaron ugrave -37
+KPX Tcaron uhungarumlaut -37
+KPX Tcaron umacron -37
+KPX Tcaron uogonek -37
+KPX Tcaron uring -37
+KPX Tcaron w -37
+KPX Tcaron y -37
+KPX Tcaron yacute -37
+KPX Tcaron ydieresis -37
+KPX Tcommaaccent A -55
+KPX Tcommaaccent Aacute -55
+KPX Tcommaaccent Abreve -55
+KPX Tcommaaccent Acircumflex -55
+KPX Tcommaaccent Adieresis -55
+KPX Tcommaaccent Agrave -55
+KPX Tcommaaccent Amacron -55
+KPX Tcommaaccent Aogonek -55
+KPX Tcommaaccent Aring -55
+KPX Tcommaaccent Atilde -55
+KPX Tcommaaccent O -18
+KPX Tcommaaccent Oacute -18
+KPX Tcommaaccent Ocircumflex -18
+KPX Tcommaaccent Odieresis -18
+KPX Tcommaaccent Ograve -18
+KPX Tcommaaccent Ohungarumlaut -18
+KPX Tcommaaccent Omacron -18
+KPX Tcommaaccent Oslash -18
+KPX Tcommaaccent Otilde -18
+KPX Tcommaaccent a -92
+KPX Tcommaaccent aacute -92
+KPX Tcommaaccent abreve -92
+KPX Tcommaaccent acircumflex -92
+KPX Tcommaaccent adieresis -92
+KPX Tcommaaccent agrave -92
+KPX Tcommaaccent amacron -92
+KPX Tcommaaccent aogonek -92
+KPX Tcommaaccent aring -92
+KPX Tcommaaccent atilde -92
+KPX Tcommaaccent colon -74
+KPX Tcommaaccent comma -92
+KPX Tcommaaccent e -92
+KPX Tcommaaccent eacute -92
+KPX Tcommaaccent ecaron -92
+KPX Tcommaaccent ecircumflex -92
+KPX Tcommaaccent edieresis -52
+KPX Tcommaaccent edotaccent -92
+KPX Tcommaaccent egrave -52
+KPX Tcommaaccent emacron -52
+KPX Tcommaaccent eogonek -92
+KPX Tcommaaccent hyphen -92
+KPX Tcommaaccent i -37
+KPX Tcommaaccent iacute -37
+KPX Tcommaaccent iogonek -37
+KPX Tcommaaccent o -95
+KPX Tcommaaccent oacute -95
+KPX Tcommaaccent ocircumflex -95
+KPX Tcommaaccent odieresis -95
+KPX Tcommaaccent ograve -95
+KPX Tcommaaccent ohungarumlaut -95
+KPX Tcommaaccent omacron -95
+KPX Tcommaaccent oslash -95
+KPX Tcommaaccent otilde -95
+KPX Tcommaaccent period -92
+KPX Tcommaaccent r -37
+KPX Tcommaaccent racute -37
+KPX Tcommaaccent rcaron -37
+KPX Tcommaaccent rcommaaccent -37
+KPX Tcommaaccent semicolon -74
+KPX Tcommaaccent u -37
+KPX Tcommaaccent uacute -37
+KPX Tcommaaccent ucircumflex -37
+KPX Tcommaaccent udieresis -37
+KPX Tcommaaccent ugrave -37
+KPX Tcommaaccent uhungarumlaut -37
+KPX Tcommaaccent umacron -37
+KPX Tcommaaccent uogonek -37
+KPX Tcommaaccent uring -37
+KPX Tcommaaccent w -37
+KPX Tcommaaccent y -37
+KPX Tcommaaccent yacute -37
+KPX Tcommaaccent ydieresis -37
+KPX U A -45
+KPX U Aacute -45
+KPX U Abreve -45
+KPX U Acircumflex -45
+KPX U Adieresis -45
+KPX U Agrave -45
+KPX U Amacron -45
+KPX U Aogonek -45
+KPX U Aring -45
+KPX U Atilde -45
+KPX Uacute A -45
+KPX Uacute Aacute -45
+KPX Uacute Abreve -45
+KPX Uacute Acircumflex -45
+KPX Uacute Adieresis -45
+KPX Uacute Agrave -45
+KPX Uacute Amacron -45
+KPX Uacute Aogonek -45
+KPX Uacute Aring -45
+KPX Uacute Atilde -45
+KPX Ucircumflex A -45
+KPX Ucircumflex Aacute -45
+KPX Ucircumflex Abreve -45
+KPX Ucircumflex Acircumflex -45
+KPX Ucircumflex Adieresis -45
+KPX Ucircumflex Agrave -45
+KPX Ucircumflex Amacron -45
+KPX Ucircumflex Aogonek -45
+KPX Ucircumflex Aring -45
+KPX Ucircumflex Atilde -45
+KPX Udieresis A -45
+KPX Udieresis Aacute -45
+KPX Udieresis Abreve -45
+KPX Udieresis Acircumflex -45
+KPX Udieresis Adieresis -45
+KPX Udieresis Agrave -45
+KPX Udieresis Amacron -45
+KPX Udieresis Aogonek -45
+KPX Udieresis Aring -45
+KPX Udieresis Atilde -45
+KPX Ugrave A -45
+KPX Ugrave Aacute -45
+KPX Ugrave Abreve -45
+KPX Ugrave Acircumflex -45
+KPX Ugrave Adieresis -45
+KPX Ugrave Agrave -45
+KPX Ugrave Amacron -45
+KPX Ugrave Aogonek -45
+KPX Ugrave Aring -45
+KPX Ugrave Atilde -45
+KPX Uhungarumlaut A -45
+KPX Uhungarumlaut Aacute -45
+KPX Uhungarumlaut Abreve -45
+KPX Uhungarumlaut Acircumflex -45
+KPX Uhungarumlaut Adieresis -45
+KPX Uhungarumlaut Agrave -45
+KPX Uhungarumlaut Amacron -45
+KPX Uhungarumlaut Aogonek -45
+KPX Uhungarumlaut Aring -45
+KPX Uhungarumlaut Atilde -45
+KPX Umacron A -45
+KPX Umacron Aacute -45
+KPX Umacron Abreve -45
+KPX Umacron Acircumflex -45
+KPX Umacron Adieresis -45
+KPX Umacron Agrave -45
+KPX Umacron Amacron -45
+KPX Umacron Aogonek -45
+KPX Umacron Aring -45
+KPX Umacron Atilde -45
+KPX Uogonek A -45
+KPX Uogonek Aacute -45
+KPX Uogonek Abreve -45
+KPX Uogonek Acircumflex -45
+KPX Uogonek Adieresis -45
+KPX Uogonek Agrave -45
+KPX Uogonek Amacron -45
+KPX Uogonek Aogonek -45
+KPX Uogonek Aring -45
+KPX Uogonek Atilde -45
+KPX Uring A -45
+KPX Uring Aacute -45
+KPX Uring Abreve -45
+KPX Uring Acircumflex -45
+KPX Uring Adieresis -45
+KPX Uring Agrave -45
+KPX Uring Amacron -45
+KPX Uring Aogonek -45
+KPX Uring Aring -45
+KPX Uring Atilde -45
+KPX V A -85
+KPX V Aacute -85
+KPX V Abreve -85
+KPX V Acircumflex -85
+KPX V Adieresis -85
+KPX V Agrave -85
+KPX V Amacron -85
+KPX V Aogonek -85
+KPX V Aring -85
+KPX V Atilde -85
+KPX V G -10
+KPX V Gbreve -10
+KPX V Gcommaaccent -10
+KPX V O -30
+KPX V Oacute -30
+KPX V Ocircumflex -30
+KPX V Odieresis -30
+KPX V Ograve -30
+KPX V Ohungarumlaut -30
+KPX V Omacron -30
+KPX V Oslash -30
+KPX V Otilde -30
+KPX V a -111
+KPX V aacute -111
+KPX V abreve -111
+KPX V acircumflex -111
+KPX V adieresis -111
+KPX V agrave -111
+KPX V amacron -111
+KPX V aogonek -111
+KPX V aring -111
+KPX V atilde -111
+KPX V colon -74
+KPX V comma -129
+KPX V e -111
+KPX V eacute -111
+KPX V ecaron -111
+KPX V ecircumflex -111
+KPX V edieresis -71
+KPX V edotaccent -111
+KPX V egrave -71
+KPX V emacron -71
+KPX V eogonek -111
+KPX V hyphen -70
+KPX V i -55
+KPX V iacute -55
+KPX V iogonek -55
+KPX V o -111
+KPX V oacute -111
+KPX V ocircumflex -111
+KPX V odieresis -111
+KPX V ograve -111
+KPX V ohungarumlaut -111
+KPX V omacron -111
+KPX V oslash -111
+KPX V otilde -111
+KPX V period -129
+KPX V semicolon -74
+KPX V u -55
+KPX V uacute -55
+KPX V ucircumflex -55
+KPX V udieresis -55
+KPX V ugrave -55
+KPX V uhungarumlaut -55
+KPX V umacron -55
+KPX V uogonek -55
+KPX V uring -55
+KPX W A -74
+KPX W Aacute -74
+KPX W Abreve -74
+KPX W Acircumflex -74
+KPX W Adieresis -74
+KPX W Agrave -74
+KPX W Amacron -74
+KPX W Aogonek -74
+KPX W Aring -74
+KPX W Atilde -74
+KPX W O -15
+KPX W Oacute -15
+KPX W Ocircumflex -15
+KPX W Odieresis -15
+KPX W Ograve -15
+KPX W Ohungarumlaut -15
+KPX W Omacron -15
+KPX W Oslash -15
+KPX W Otilde -15
+KPX W a -85
+KPX W aacute -85
+KPX W abreve -85
+KPX W acircumflex -85
+KPX W adieresis -85
+KPX W agrave -85
+KPX W amacron -85
+KPX W aogonek -85
+KPX W aring -85
+KPX W atilde -85
+KPX W colon -55
+KPX W comma -74
+KPX W e -90
+KPX W eacute -90
+KPX W ecaron -90
+KPX W ecircumflex -90
+KPX W edieresis -50
+KPX W edotaccent -90
+KPX W egrave -50
+KPX W emacron -50
+KPX W eogonek -90
+KPX W hyphen -50
+KPX W i -37
+KPX W iacute -37
+KPX W iogonek -37
+KPX W o -80
+KPX W oacute -80
+KPX W ocircumflex -80
+KPX W odieresis -80
+KPX W ograve -80
+KPX W ohungarumlaut -80
+KPX W omacron -80
+KPX W oslash -80
+KPX W otilde -80
+KPX W period -74
+KPX W semicolon -55
+KPX W u -55
+KPX W uacute -55
+KPX W ucircumflex -55
+KPX W udieresis -55
+KPX W ugrave -55
+KPX W uhungarumlaut -55
+KPX W umacron -55
+KPX W uogonek -55
+KPX W uring -55
+KPX W y -55
+KPX W yacute -55
+KPX W ydieresis -55
+KPX Y A -74
+KPX Y Aacute -74
+KPX Y Abreve -74
+KPX Y Acircumflex -74
+KPX Y Adieresis -74
+KPX Y Agrave -74
+KPX Y Amacron -74
+KPX Y Aogonek -74
+KPX Y Aring -74
+KPX Y Atilde -74
+KPX Y O -25
+KPX Y Oacute -25
+KPX Y Ocircumflex -25
+KPX Y Odieresis -25
+KPX Y Ograve -25
+KPX Y Ohungarumlaut -25
+KPX Y Omacron -25
+KPX Y Oslash -25
+KPX Y Otilde -25
+KPX Y a -92
+KPX Y aacute -92
+KPX Y abreve -92
+KPX Y acircumflex -92
+KPX Y adieresis -92
+KPX Y agrave -92
+KPX Y amacron -92
+KPX Y aogonek -92
+KPX Y aring -92
+KPX Y atilde -92
+KPX Y colon -92
+KPX Y comma -92
+KPX Y e -111
+KPX Y eacute -111
+KPX Y ecaron -111
+KPX Y ecircumflex -71
+KPX Y edieresis -71
+KPX Y edotaccent -111
+KPX Y egrave -71
+KPX Y emacron -71
+KPX Y eogonek -111
+KPX Y hyphen -92
+KPX Y i -55
+KPX Y iacute -55
+KPX Y iogonek -55
+KPX Y o -111
+KPX Y oacute -111
+KPX Y ocircumflex -111
+KPX Y odieresis -111
+KPX Y ograve -111
+KPX Y ohungarumlaut -111
+KPX Y omacron -111
+KPX Y oslash -111
+KPX Y otilde -111
+KPX Y period -74
+KPX Y semicolon -92
+KPX Y u -92
+KPX Y uacute -92
+KPX Y ucircumflex -92
+KPX Y udieresis -92
+KPX Y ugrave -92
+KPX Y uhungarumlaut -92
+KPX Y umacron -92
+KPX Y uogonek -92
+KPX Y uring -92
+KPX Yacute A -74
+KPX Yacute Aacute -74
+KPX Yacute Abreve -74
+KPX Yacute Acircumflex -74
+KPX Yacute Adieresis -74
+KPX Yacute Agrave -74
+KPX Yacute Amacron -74
+KPX Yacute Aogonek -74
+KPX Yacute Aring -74
+KPX Yacute Atilde -74
+KPX Yacute O -25
+KPX Yacute Oacute -25
+KPX Yacute Ocircumflex -25
+KPX Yacute Odieresis -25
+KPX Yacute Ograve -25
+KPX Yacute Ohungarumlaut -25
+KPX Yacute Omacron -25
+KPX Yacute Oslash -25
+KPX Yacute Otilde -25
+KPX Yacute a -92
+KPX Yacute aacute -92
+KPX Yacute abreve -92
+KPX Yacute acircumflex -92
+KPX Yacute adieresis -92
+KPX Yacute agrave -92
+KPX Yacute amacron -92
+KPX Yacute aogonek -92
+KPX Yacute aring -92
+KPX Yacute atilde -92
+KPX Yacute colon -92
+KPX Yacute comma -92
+KPX Yacute e -111
+KPX Yacute eacute -111
+KPX Yacute ecaron -111
+KPX Yacute ecircumflex -71
+KPX Yacute edieresis -71
+KPX Yacute edotaccent -111
+KPX Yacute egrave -71
+KPX Yacute emacron -71
+KPX Yacute eogonek -111
+KPX Yacute hyphen -92
+KPX Yacute i -55
+KPX Yacute iacute -55
+KPX Yacute iogonek -55
+KPX Yacute o -111
+KPX Yacute oacute -111
+KPX Yacute ocircumflex -111
+KPX Yacute odieresis -111
+KPX Yacute ograve -111
+KPX Yacute ohungarumlaut -111
+KPX Yacute omacron -111
+KPX Yacute oslash -111
+KPX Yacute otilde -111
+KPX Yacute period -74
+KPX Yacute semicolon -92
+KPX Yacute u -92
+KPX Yacute uacute -92
+KPX Yacute ucircumflex -92
+KPX Yacute udieresis -92
+KPX Yacute ugrave -92
+KPX Yacute uhungarumlaut -92
+KPX Yacute umacron -92
+KPX Yacute uogonek -92
+KPX Yacute uring -92
+KPX Ydieresis A -74
+KPX Ydieresis Aacute -74
+KPX Ydieresis Abreve -74
+KPX Ydieresis Acircumflex -74
+KPX Ydieresis Adieresis -74
+KPX Ydieresis Agrave -74
+KPX Ydieresis Amacron -74
+KPX Ydieresis Aogonek -74
+KPX Ydieresis Aring -74
+KPX Ydieresis Atilde -74
+KPX Ydieresis O -25
+KPX Ydieresis Oacute -25
+KPX Ydieresis Ocircumflex -25
+KPX Ydieresis Odieresis -25
+KPX Ydieresis Ograve -25
+KPX Ydieresis Ohungarumlaut -25
+KPX Ydieresis Omacron -25
+KPX Ydieresis Oslash -25
+KPX Ydieresis Otilde -25
+KPX Ydieresis a -92
+KPX Ydieresis aacute -92
+KPX Ydieresis abreve -92
+KPX Ydieresis acircumflex -92
+KPX Ydieresis adieresis -92
+KPX Ydieresis agrave -92
+KPX Ydieresis amacron -92
+KPX Ydieresis aogonek -92
+KPX Ydieresis aring -92
+KPX Ydieresis atilde -92
+KPX Ydieresis colon -92
+KPX Ydieresis comma -92
+KPX Ydieresis e -111
+KPX Ydieresis eacute -111
+KPX Ydieresis ecaron -111
+KPX Ydieresis ecircumflex -71
+KPX Ydieresis edieresis -71
+KPX Ydieresis edotaccent -111
+KPX Ydieresis egrave -71
+KPX Ydieresis emacron -71
+KPX Ydieresis eogonek -111
+KPX Ydieresis hyphen -92
+KPX Ydieresis i -55
+KPX Ydieresis iacute -55
+KPX Ydieresis iogonek -55
+KPX Ydieresis o -111
+KPX Ydieresis oacute -111
+KPX Ydieresis ocircumflex -111
+KPX Ydieresis odieresis -111
+KPX Ydieresis ograve -111
+KPX Ydieresis ohungarumlaut -111
+KPX Ydieresis omacron -111
+KPX Ydieresis oslash -111
+KPX Ydieresis otilde -111
+KPX Ydieresis period -74
+KPX Ydieresis semicolon -92
+KPX Ydieresis u -92
+KPX Ydieresis uacute -92
+KPX Ydieresis ucircumflex -92
+KPX Ydieresis udieresis -92
+KPX Ydieresis ugrave -92
+KPX Ydieresis uhungarumlaut -92
+KPX Ydieresis umacron -92
+KPX Ydieresis uogonek -92
+KPX Ydieresis uring -92
+KPX b b -10
+KPX b period -40
+KPX b u -20
+KPX b uacute -20
+KPX b ucircumflex -20
+KPX b udieresis -20
+KPX b ugrave -20
+KPX b uhungarumlaut -20
+KPX b umacron -20
+KPX b uogonek -20
+KPX b uring -20
+KPX c h -10
+KPX c k -10
+KPX c kcommaaccent -10
+KPX cacute h -10
+KPX cacute k -10
+KPX cacute kcommaaccent -10
+KPX ccaron h -10
+KPX ccaron k -10
+KPX ccaron kcommaaccent -10
+KPX ccedilla h -10
+KPX ccedilla k -10
+KPX ccedilla kcommaaccent -10
+KPX comma quotedblright -95
+KPX comma quoteright -95
+KPX e b -10
+KPX eacute b -10
+KPX ecaron b -10
+KPX ecircumflex b -10
+KPX edieresis b -10
+KPX edotaccent b -10
+KPX egrave b -10
+KPX emacron b -10
+KPX eogonek b -10
+KPX f comma -10
+KPX f dotlessi -30
+KPX f e -10
+KPX f eacute -10
+KPX f edotaccent -10
+KPX f eogonek -10
+KPX f f -18
+KPX f o -10
+KPX f oacute -10
+KPX f ocircumflex -10
+KPX f ograve -10
+KPX f ohungarumlaut -10
+KPX f oslash -10
+KPX f otilde -10
+KPX f period -10
+KPX f quoteright 55
+KPX k e -30
+KPX k eacute -30
+KPX k ecaron -30
+KPX k ecircumflex -30
+KPX k edieresis -30
+KPX k edotaccent -30
+KPX k egrave -30
+KPX k emacron -30
+KPX k eogonek -30
+KPX k o -10
+KPX k oacute -10
+KPX k ocircumflex -10
+KPX k odieresis -10
+KPX k ograve -10
+KPX k ohungarumlaut -10
+KPX k omacron -10
+KPX k oslash -10
+KPX k otilde -10
+KPX kcommaaccent e -30
+KPX kcommaaccent eacute -30
+KPX kcommaaccent ecaron -30
+KPX kcommaaccent ecircumflex -30
+KPX kcommaaccent edieresis -30
+KPX kcommaaccent edotaccent -30
+KPX kcommaaccent egrave -30
+KPX kcommaaccent emacron -30
+KPX kcommaaccent eogonek -30
+KPX kcommaaccent o -10
+KPX kcommaaccent oacute -10
+KPX kcommaaccent ocircumflex -10
+KPX kcommaaccent odieresis -10
+KPX kcommaaccent ograve -10
+KPX kcommaaccent ohungarumlaut -10
+KPX kcommaaccent omacron -10
+KPX kcommaaccent oslash -10
+KPX kcommaaccent otilde -10
+KPX n v -40
+KPX nacute v -40
+KPX ncaron v -40
+KPX ncommaaccent v -40
+KPX ntilde v -40
+KPX o v -15
+KPX o w -25
+KPX o x -10
+KPX o y -10
+KPX o yacute -10
+KPX o ydieresis -10
+KPX oacute v -15
+KPX oacute w -25
+KPX oacute x -10
+KPX oacute y -10
+KPX oacute yacute -10
+KPX oacute ydieresis -10
+KPX ocircumflex v -15
+KPX ocircumflex w -25
+KPX ocircumflex x -10
+KPX ocircumflex y -10
+KPX ocircumflex yacute -10
+KPX ocircumflex ydieresis -10
+KPX odieresis v -15
+KPX odieresis w -25
+KPX odieresis x -10
+KPX odieresis y -10
+KPX odieresis yacute -10
+KPX odieresis ydieresis -10
+KPX ograve v -15
+KPX ograve w -25
+KPX ograve x -10
+KPX ograve y -10
+KPX ograve yacute -10
+KPX ograve ydieresis -10
+KPX ohungarumlaut v -15
+KPX ohungarumlaut w -25
+KPX ohungarumlaut x -10
+KPX ohungarumlaut y -10
+KPX ohungarumlaut yacute -10
+KPX ohungarumlaut ydieresis -10
+KPX omacron v -15
+KPX omacron w -25
+KPX omacron x -10
+KPX omacron y -10
+KPX omacron yacute -10
+KPX omacron ydieresis -10
+KPX oslash v -15
+KPX oslash w -25
+KPX oslash x -10
+KPX oslash y -10
+KPX oslash yacute -10
+KPX oslash ydieresis -10
+KPX otilde v -15
+KPX otilde w -25
+KPX otilde x -10
+KPX otilde y -10
+KPX otilde yacute -10
+KPX otilde ydieresis -10
+KPX period quotedblright -95
+KPX period quoteright -95
+KPX quoteleft quoteleft -74
+KPX quoteright d -15
+KPX quoteright dcroat -15
+KPX quoteright quoteright -74
+KPX quoteright r -15
+KPX quoteright racute -15
+KPX quoteright rcaron -15
+KPX quoteright rcommaaccent -15
+KPX quoteright s -74
+KPX quoteright sacute -74
+KPX quoteright scaron -74
+KPX quoteright scedilla -74
+KPX quoteright scommaaccent -74
+KPX quoteright space -74
+KPX quoteright t -37
+KPX quoteright tcommaaccent -37
+KPX quoteright v -15
+KPX r comma -65
+KPX r period -65
+KPX racute comma -65
+KPX racute period -65
+KPX rcaron comma -65
+KPX rcaron period -65
+KPX rcommaaccent comma -65
+KPX rcommaaccent period -65
+KPX space A -37
+KPX space Aacute -37
+KPX space Abreve -37
+KPX space Acircumflex -37
+KPX space Adieresis -37
+KPX space Agrave -37
+KPX space Amacron -37
+KPX space Aogonek -37
+KPX space Aring -37
+KPX space Atilde -37
+KPX space V -70
+KPX space W -70
+KPX space Y -70
+KPX space Yacute -70
+KPX space Ydieresis -70
+KPX v comma -37
+KPX v e -15
+KPX v eacute -15
+KPX v ecaron -15
+KPX v ecircumflex -15
+KPX v edieresis -15
+KPX v edotaccent -15
+KPX v egrave -15
+KPX v emacron -15
+KPX v eogonek -15
+KPX v o -15
+KPX v oacute -15
+KPX v ocircumflex -15
+KPX v odieresis -15
+KPX v ograve -15
+KPX v ohungarumlaut -15
+KPX v omacron -15
+KPX v oslash -15
+KPX v otilde -15
+KPX v period -37
+KPX w a -10
+KPX w aacute -10
+KPX w abreve -10
+KPX w acircumflex -10
+KPX w adieresis -10
+KPX w agrave -10
+KPX w amacron -10
+KPX w aogonek -10
+KPX w aring -10
+KPX w atilde -10
+KPX w comma -37
+KPX w e -10
+KPX w eacute -10
+KPX w ecaron -10
+KPX w ecircumflex -10
+KPX w edieresis -10
+KPX w edotaccent -10
+KPX w egrave -10
+KPX w emacron -10
+KPX w eogonek -10
+KPX w o -15
+KPX w oacute -15
+KPX w ocircumflex -15
+KPX w odieresis -15
+KPX w ograve -15
+KPX w ohungarumlaut -15
+KPX w omacron -15
+KPX w oslash -15
+KPX w otilde -15
+KPX w period -37
+KPX x e -10
+KPX x eacute -10
+KPX x ecaron -10
+KPX x ecircumflex -10
+KPX x edieresis -10
+KPX x edotaccent -10
+KPX x egrave -10
+KPX x emacron -10
+KPX x eogonek -10
+KPX y comma -37
+KPX y period -37
+KPX yacute comma -37
+KPX yacute period -37
+KPX ydieresis comma -37
+KPX ydieresis period -37
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/program/libraries/afm/Times-Italic.afm b/program/libraries/afm/Times-Italic.afm
new file mode 100644 (file)
index 0000000..b0eaee4
--- /dev/null
@@ -0,0 +1,2667 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated.  All Rights Reserved.
+Comment Creation Date: Thu May  1 12:56:55 1997
+Comment UniqueID 43067
+Comment VMusage 47727 58752
+FontName Times-Italic
+FullName Times Italic
+FamilyName Times
+Weight Medium
+ItalicAngle -15.5
+IsFixedPitch false
+CharacterSet ExtendedRoman
+FontBBox -169 -217 1010 883 
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated.  All Rights Reserved.Times is a trademark of Linotype-Hell AG and/or its subsidiaries.
+EncodingScheme AdobeStandardEncoding
+CapHeight 653
+XHeight 441
+Ascender 683
+Descender -217
+StdHW 32
+StdVW 76
+StartCharMetrics 315
+C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 333 ; N exclam ; B 39 -11 302 667 ;
+C 34 ; WX 420 ; N quotedbl ; B 144 421 432 666 ;
+C 35 ; WX 500 ; N numbersign ; B 2 0 540 676 ;
+C 36 ; WX 500 ; N dollar ; B 31 -89 497 731 ;
+C 37 ; WX 833 ; N percent ; B 79 -13 790 676 ;
+C 38 ; WX 778 ; N ampersand ; B 76 -18 723 666 ;
+C 39 ; WX 333 ; N quoteright ; B 151 436 290 666 ;
+C 40 ; WX 333 ; N parenleft ; B 42 -181 315 669 ;
+C 41 ; WX 333 ; N parenright ; B 16 -180 289 669 ;
+C 42 ; WX 500 ; N asterisk ; B 128 255 492 666 ;
+C 43 ; WX 675 ; N plus ; B 86 0 590 506 ;
+C 44 ; WX 250 ; N comma ; B -4 -129 135 101 ;
+C 45 ; WX 333 ; N hyphen ; B 49 192 282 255 ;
+C 46 ; WX 250 ; N period ; B 27 -11 138 100 ;
+C 47 ; WX 278 ; N slash ; B -65 -18 386 666 ;
+C 48 ; WX 500 ; N zero ; B 32 -7 497 676 ;
+C 49 ; WX 500 ; N one ; B 49 0 409 676 ;
+C 50 ; WX 500 ; N two ; B 12 0 452 676 ;
+C 51 ; WX 500 ; N three ; B 15 -7 465 676 ;
+C 52 ; WX 500 ; N four ; B 1 0 479 676 ;
+C 53 ; WX 500 ; N five ; B 15 -7 491 666 ;
+C 54 ; WX 500 ; N six ; B 30 -7 521 686 ;
+C 55 ; WX 500 ; N seven ; B 75 -8 537 666 ;
+C 56 ; WX 500 ; N eight ; B 30 -7 493 676 ;
+C 57 ; WX 500 ; N nine ; B 23 -17 492 676 ;
+C 58 ; WX 333 ; N colon ; B 50 -11 261 441 ;
+C 59 ; WX 333 ; N semicolon ; B 27 -129 261 441 ;
+C 60 ; WX 675 ; N less ; B 84 -8 592 514 ;
+C 61 ; WX 675 ; N equal ; B 86 120 590 386 ;
+C 62 ; WX 675 ; N greater ; B 84 -8 592 514 ;
+C 63 ; WX 500 ; N question ; B 132 -12 472 664 ;
+C 64 ; WX 920 ; N at ; B 118 -18 806 666 ;
+C 65 ; WX 611 ; N A ; B -51 0 564 668 ;
+C 66 ; WX 611 ; N B ; B -8 0 588 653 ;
+C 67 ; WX 667 ; N C ; B 66 -18 689 666 ;
+C 68 ; WX 722 ; N D ; B -8 0 700 653 ;
+C 69 ; WX 611 ; N E ; B -1 0 634 653 ;
+C 70 ; WX 611 ; N F ; B 8 0 645 653 ;
+C 71 ; WX 722 ; N G ; B 52 -18 722 666 ;
+C 72 ; WX 722 ; N H ; B -8 0 767 653 ;
+C 73 ; WX 333 ; N I ; B -8 0 384 653 ;
+C 74 ; WX 444 ; N J ; B -6 -18 491 653 ;
+C 75 ; WX 667 ; N K ; B 7 0 722 653 ;
+C 76 ; WX 556 ; N L ; B -8 0 559 653 ;
+C 77 ; WX 833 ; N M ; B -18 0 873 653 ;
+C 78 ; WX 667 ; N N ; B -20 -15 727 653 ;
+C 79 ; WX 722 ; N O ; B 60 -18 699 666 ;
+C 80 ; WX 611 ; N P ; B 0 0 605 653 ;
+C 81 ; WX 722 ; N Q ; B 59 -182 699 666 ;
+C 82 ; WX 611 ; N R ; B -13 0 588 653 ;
+C 83 ; WX 500 ; N S ; B 17 -18 508 667 ;
+C 84 ; WX 556 ; N T ; B 59 0 633 653 ;
+C 85 ; WX 722 ; N U ; B 102 -18 765 653 ;
+C 86 ; WX 611 ; N V ; B 76 -18 688 653 ;
+C 87 ; WX 833 ; N W ; B 71 -18 906 653 ;
+C 88 ; WX 611 ; N X ; B -29 0 655 653 ;
+C 89 ; WX 556 ; N Y ; B 78 0 633 653 ;
+C 90 ; WX 556 ; N Z ; B -6 0 606 653 ;
+C 91 ; WX 389 ; N bracketleft ; B 21 -153 391 663 ;
+C 92 ; WX 278 ; N backslash ; B -41 -18 319 666 ;
+C 93 ; WX 389 ; N bracketright ; B 12 -153 382 663 ;
+C 94 ; WX 422 ; N asciicircum ; B 0 301 422 666 ;
+C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ;
+C 96 ; WX 333 ; N quoteleft ; B 171 436 310 666 ;
+C 97 ; WX 500 ; N a ; B 17 -11 476 441 ;
+C 98 ; WX 500 ; N b ; B 23 -11 473 683 ;
+C 99 ; WX 444 ; N c ; B 30 -11 425 441 ;
+C 100 ; WX 500 ; N d ; B 15 -13 527 683 ;
+C 101 ; WX 444 ; N e ; B 31 -11 412 441 ;
+C 102 ; WX 278 ; N f ; B -147 -207 424 678 ; L i fi ; L l fl ;
+C 103 ; WX 500 ; N g ; B 8 -206 472 441 ;
+C 104 ; WX 500 ; N h ; B 19 -9 478 683 ;
+C 105 ; WX 278 ; N i ; B 49 -11 264 654 ;
+C 106 ; WX 278 ; N j ; B -124 -207 276 654 ;
+C 107 ; WX 444 ; N k ; B 14 -11 461 683 ;
+C 108 ; WX 278 ; N l ; B 41 -11 279 683 ;
+C 109 ; WX 722 ; N m ; B 12 -9 704 441 ;
+C 110 ; WX 500 ; N n ; B 14 -9 474 441 ;
+C 111 ; WX 500 ; N o ; B 27 -11 468 441 ;
+C 112 ; WX 500 ; N p ; B -75 -205 469 441 ;
+C 113 ; WX 500 ; N q ; B 25 -209 483 441 ;
+C 114 ; WX 389 ; N r ; B 45 0 412 441 ;
+C 115 ; WX 389 ; N s ; B 16 -13 366 442 ;
+C 116 ; WX 278 ; N t ; B 37 -11 296 546 ;
+C 117 ; WX 500 ; N u ; B 42 -11 475 441 ;
+C 118 ; WX 444 ; N v ; B 21 -18 426 441 ;
+C 119 ; WX 667 ; N w ; B 16 -18 648 441 ;
+C 120 ; WX 444 ; N x ; B -27 -11 447 441 ;
+C 121 ; WX 444 ; N y ; B -24 -206 426 441 ;
+C 122 ; WX 389 ; N z ; B -2 -81 380 428 ;
+C 123 ; WX 400 ; N braceleft ; B 51 -177 407 687 ;
+C 124 ; WX 275 ; N bar ; B 105 -217 171 783 ;
+C 125 ; WX 400 ; N braceright ; B -7 -177 349 687 ;
+C 126 ; WX 541 ; N asciitilde ; B 40 183 502 323 ;
+C 161 ; WX 389 ; N exclamdown ; B 59 -205 322 473 ;
+C 162 ; WX 500 ; N cent ; B 77 -143 472 560 ;
+C 163 ; WX 500 ; N sterling ; B 10 -6 517 670 ;
+C 164 ; WX 167 ; N fraction ; B -169 -10 337 676 ;
+C 165 ; WX 500 ; N yen ; B 27 0 603 653 ;
+C 166 ; WX 500 ; N florin ; B 25 -182 507 682 ;
+C 167 ; WX 500 ; N section ; B 53 -162 461 666 ;
+C 168 ; WX 500 ; N currency ; B -22 53 522 597 ;
+C 169 ; WX 214 ; N quotesingle ; B 132 421 241 666 ;
+C 170 ; WX 556 ; N quotedblleft ; B 166 436 514 666 ;
+C 171 ; WX 500 ; N guillemotleft ; B 53 37 445 403 ;
+C 172 ; WX 333 ; N guilsinglleft ; B 51 37 281 403 ;
+C 173 ; WX 333 ; N guilsinglright ; B 52 37 282 403 ;
+C 174 ; WX 500 ; N fi ; B -141 -207 481 681 ;
+C 175 ; WX 500 ; N fl ; B -141 -204 518 682 ;
+C 177 ; WX 500 ; N endash ; B -6 197 505 243 ;
+C 178 ; WX 500 ; N dagger ; B 101 -159 488 666 ;
+C 179 ; WX 500 ; N daggerdbl ; B 22 -143 491 666 ;
+C 180 ; WX 250 ; N periodcentered ; B 70 199 181 310 ;
+C 182 ; WX 523 ; N paragraph ; B 55 -123 616 653 ;
+C 183 ; WX 350 ; N bullet ; B 40 191 310 461 ;
+C 184 ; WX 333 ; N quotesinglbase ; B 44 -129 183 101 ;
+C 185 ; WX 556 ; N quotedblbase ; B 57 -129 405 101 ;
+C 186 ; WX 556 ; N quotedblright ; B 151 436 499 666 ;
+C 187 ; WX 500 ; N guillemotright ; B 55 37 447 403 ;
+C 188 ; WX 889 ; N ellipsis ; B 57 -11 762 100 ;
+C 189 ; WX 1000 ; N perthousand ; B 25 -19 1010 706 ;
+C 191 ; WX 500 ; N questiondown ; B 28 -205 368 471 ;
+C 193 ; WX 333 ; N grave ; B 121 492 311 664 ;
+C 194 ; WX 333 ; N acute ; B 180 494 403 664 ;
+C 195 ; WX 333 ; N circumflex ; B 91 492 385 661 ;
+C 196 ; WX 333 ; N tilde ; B 100 517 427 624 ;
+C 197 ; WX 333 ; N macron ; B 99 532 411 583 ;
+C 198 ; WX 333 ; N breve ; B 117 492 418 650 ;
+C 199 ; WX 333 ; N dotaccent ; B 207 548 305 646 ;
+C 200 ; WX 333 ; N dieresis ; B 107 548 405 646 ;
+C 202 ; WX 333 ; N ring ; B 155 492 355 691 ;
+C 203 ; WX 333 ; N cedilla ; B -30 -217 182 0 ;
+C 205 ; WX 333 ; N hungarumlaut ; B 93 494 486 664 ;
+C 206 ; WX 333 ; N ogonek ; B 20 -169 203 40 ;
+C 207 ; WX 333 ; N caron ; B 121 492 426 661 ;
+C 208 ; WX 889 ; N emdash ; B -6 197 894 243 ;
+C 225 ; WX 889 ; N AE ; B -27 0 911 653 ;
+C 227 ; WX 276 ; N ordfeminine ; B 42 406 352 676 ;
+C 232 ; WX 556 ; N Lslash ; B -8 0 559 653 ;
+C 233 ; WX 722 ; N Oslash ; B 60 -105 699 722 ;
+C 234 ; WX 944 ; N OE ; B 49 -8 964 666 ;
+C 235 ; WX 310 ; N ordmasculine ; B 67 406 362 676 ;
+C 241 ; WX 667 ; N ae ; B 23 -11 640 441 ;
+C 245 ; WX 278 ; N dotlessi ; B 49 -11 235 441 ;
+C 248 ; WX 278 ; N lslash ; B 41 -11 312 683 ;
+C 249 ; WX 500 ; N oslash ; B 28 -135 469 554 ;
+C 250 ; WX 667 ; N oe ; B 20 -12 646 441 ;
+C 251 ; WX 500 ; N germandbls ; B -168 -207 493 679 ;
+C -1 ; WX 333 ; N Idieresis ; B -8 0 435 818 ;
+C -1 ; WX 444 ; N eacute ; B 31 -11 459 664 ;
+C -1 ; WX 500 ; N abreve ; B 17 -11 502 650 ;
+C -1 ; WX 500 ; N uhungarumlaut ; B 42 -11 580 664 ;
+C -1 ; WX 444 ; N ecaron ; B 31 -11 482 661 ;
+C -1 ; WX 556 ; N Ydieresis ; B 78 0 633 818 ;
+C -1 ; WX 675 ; N divide ; B 86 -11 590 517 ;
+C -1 ; WX 556 ; N Yacute ; B 78 0 633 876 ;
+C -1 ; WX 611 ; N Acircumflex ; B -51 0 564 873 ;
+C -1 ; WX 500 ; N aacute ; B 17 -11 487 664 ;
+C -1 ; WX 722 ; N Ucircumflex ; B 102 -18 765 873 ;
+C -1 ; WX 444 ; N yacute ; B -24 -206 459 664 ;
+C -1 ; WX 389 ; N scommaaccent ; B 16 -217 366 442 ;
+C -1 ; WX 444 ; N ecircumflex ; B 31 -11 441 661 ;
+C -1 ; WX 722 ; N Uring ; B 102 -18 765 883 ;
+C -1 ; WX 722 ; N Udieresis ; B 102 -18 765 818 ;
+C -1 ; WX 500 ; N aogonek ; B 17 -169 476 441 ;
+C -1 ; WX 722 ; N Uacute ; B 102 -18 765 876 ;
+C -1 ; WX 500 ; N uogonek ; B 42 -169 477 441 ;
+C -1 ; WX 611 ; N Edieresis ; B -1 0 634 818 ;
+C -1 ; WX 722 ; N Dcroat ; B -8 0 700 653 ;
+C -1 ; WX 250 ; N commaaccent ; B 8 -217 133 -50 ;
+C -1 ; WX 760 ; N copyright ; B 41 -18 719 666 ;
+C -1 ; WX 611 ; N Emacron ; B -1 0 634 795 ;
+C -1 ; WX 444 ; N ccaron ; B 30 -11 482 661 ;
+C -1 ; WX 500 ; N aring ; B 17 -11 476 691 ;
+C -1 ; WX 667 ; N Ncommaaccent ; B -20 -187 727 653 ;
+C -1 ; WX 278 ; N lacute ; B 41 -11 395 876 ;
+C -1 ; WX 500 ; N agrave ; B 17 -11 476 664 ;
+C -1 ; WX 556 ; N Tcommaaccent ; B 59 -217 633 653 ;
+C -1 ; WX 667 ; N Cacute ; B 66 -18 690 876 ;
+C -1 ; WX 500 ; N atilde ; B 17 -11 511 624 ;
+C -1 ; WX 611 ; N Edotaccent ; B -1 0 634 818 ;
+C -1 ; WX 389 ; N scaron ; B 16 -13 454 661 ;
+C -1 ; WX 389 ; N scedilla ; B 16 -217 366 442 ;
+C -1 ; WX 278 ; N iacute ; B 49 -11 355 664 ;
+C -1 ; WX 471 ; N lozenge ; B 13 0 459 724 ;
+C -1 ; WX 611 ; N Rcaron ; B -13 0 588 873 ;
+C -1 ; WX 722 ; N Gcommaaccent ; B 52 -217 722 666 ;
+C -1 ; WX 500 ; N ucircumflex ; B 42 -11 475 661 ;
+C -1 ; WX 500 ; N acircumflex ; B 17 -11 476 661 ;
+C -1 ; WX 611 ; N Amacron ; B -51 0 564 795 ;
+C -1 ; WX 389 ; N rcaron ; B 45 0 434 661 ;
+C -1 ; WX 444 ; N ccedilla ; B 30 -217 425 441 ;
+C -1 ; WX 556 ; N Zdotaccent ; B -6 0 606 818 ;
+C -1 ; WX 611 ; N Thorn ; B 0 0 569 653 ;
+C -1 ; WX 722 ; N Omacron ; B 60 -18 699 795 ;
+C -1 ; WX 611 ; N Racute ; B -13 0 588 876 ;
+C -1 ; WX 500 ; N Sacute ; B 17 -18 508 876 ;
+C -1 ; WX 544 ; N dcaron ; B 15 -13 658 683 ;
+C -1 ; WX 722 ; N Umacron ; B 102 -18 765 795 ;
+C -1 ; WX 500 ; N uring ; B 42 -11 475 691 ;
+C -1 ; WX 300 ; N threesuperior ; B 43 268 339 676 ;
+C -1 ; WX 722 ; N Ograve ; B 60 -18 699 876 ;
+C -1 ; WX 611 ; N Agrave ; B -51 0 564 876 ;
+C -1 ; WX 611 ; N Abreve ; B -51 0 564 862 ;
+C -1 ; WX 675 ; N multiply ; B 93 8 582 497 ;
+C -1 ; WX 500 ; N uacute ; B 42 -11 477 664 ;
+C -1 ; WX 556 ; N Tcaron ; B 59 0 633 873 ;
+C -1 ; WX 476 ; N partialdiff ; B 17 -38 459 710 ;
+C -1 ; WX 444 ; N ydieresis ; B -24 -206 441 606 ;
+C -1 ; WX 667 ; N Nacute ; B -20 -15 727 876 ;
+C -1 ; WX 278 ; N icircumflex ; B 33 -11 327 661 ;
+C -1 ; WX 611 ; N Ecircumflex ; B -1 0 634 873 ;
+C -1 ; WX 500 ; N adieresis ; B 17 -11 489 606 ;
+C -1 ; WX 444 ; N edieresis ; B 31 -11 451 606 ;
+C -1 ; WX 444 ; N cacute ; B 30 -11 459 664 ;
+C -1 ; WX 500 ; N nacute ; B 14 -9 477 664 ;
+C -1 ; WX 500 ; N umacron ; B 42 -11 485 583 ;
+C -1 ; WX 667 ; N Ncaron ; B -20 -15 727 873 ;
+C -1 ; WX 333 ; N Iacute ; B -8 0 433 876 ;
+C -1 ; WX 675 ; N plusminus ; B 86 0 590 506 ;
+C -1 ; WX 275 ; N brokenbar ; B 105 -142 171 708 ;
+C -1 ; WX 760 ; N registered ; B 41 -18 719 666 ;
+C -1 ; WX 722 ; N Gbreve ; B 52 -18 722 862 ;
+C -1 ; WX 333 ; N Idotaccent ; B -8 0 384 818 ;
+C -1 ; WX 600 ; N summation ; B 15 -10 585 706 ;
+C -1 ; WX 611 ; N Egrave ; B -1 0 634 876 ;
+C -1 ; WX 389 ; N racute ; B 45 0 431 664 ;
+C -1 ; WX 500 ; N omacron ; B 27 -11 495 583 ;
+C -1 ; WX 556 ; N Zacute ; B -6 0 606 876 ;
+C -1 ; WX 556 ; N Zcaron ; B -6 0 606 873 ;
+C -1 ; WX 549 ; N greaterequal ; B 26 0 523 658 ;
+C -1 ; WX 722 ; N Eth ; B -8 0 700 653 ;
+C -1 ; WX 667 ; N Ccedilla ; B 66 -217 689 666 ;
+C -1 ; WX 278 ; N lcommaaccent ; B 22 -217 279 683 ;
+C -1 ; WX 300 ; N tcaron ; B 37 -11 407 681 ;
+C -1 ; WX 444 ; N eogonek ; B 31 -169 412 441 ;
+C -1 ; WX 722 ; N Uogonek ; B 102 -184 765 653 ;
+C -1 ; WX 611 ; N Aacute ; B -51 0 564 876 ;
+C -1 ; WX 611 ; N Adieresis ; B -51 0 564 818 ;
+C -1 ; WX 444 ; N egrave ; B 31 -11 412 664 ;
+C -1 ; WX 389 ; N zacute ; B -2 -81 431 664 ;
+C -1 ; WX 278 ; N iogonek ; B 49 -169 264 654 ;
+C -1 ; WX 722 ; N Oacute ; B 60 -18 699 876 ;
+C -1 ; WX 500 ; N oacute ; B 27 -11 487 664 ;
+C -1 ; WX 500 ; N amacron ; B 17 -11 495 583 ;
+C -1 ; WX 389 ; N sacute ; B 16 -13 431 664 ;
+C -1 ; WX 278 ; N idieresis ; B 49 -11 352 606 ;
+C -1 ; WX 722 ; N Ocircumflex ; B 60 -18 699 873 ;
+C -1 ; WX 722 ; N Ugrave ; B 102 -18 765 876 ;
+C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C -1 ; WX 500 ; N thorn ; B -75 -205 469 683 ;
+C -1 ; WX 300 ; N twosuperior ; B 33 271 324 676 ;
+C -1 ; WX 722 ; N Odieresis ; B 60 -18 699 818 ;
+C -1 ; WX 500 ; N mu ; B -30 -209 497 428 ;
+C -1 ; WX 278 ; N igrave ; B 49 -11 284 664 ;
+C -1 ; WX 500 ; N ohungarumlaut ; B 27 -11 590 664 ;
+C -1 ; WX 611 ; N Eogonek ; B -1 -169 634 653 ;
+C -1 ; WX 500 ; N dcroat ; B 15 -13 572 683 ;
+C -1 ; WX 750 ; N threequarters ; B 23 -10 736 676 ;
+C -1 ; WX 500 ; N Scedilla ; B 17 -217 508 667 ;
+C -1 ; WX 300 ; N lcaron ; B 41 -11 407 683 ;
+C -1 ; WX 667 ; N Kcommaaccent ; B 7 -217 722 653 ;
+C -1 ; WX 556 ; N Lacute ; B -8 0 559 876 ;
+C -1 ; WX 980 ; N trademark ; B 30 247 957 653 ;
+C -1 ; WX 444 ; N edotaccent ; B 31 -11 412 606 ;
+C -1 ; WX 333 ; N Igrave ; B -8 0 384 876 ;
+C -1 ; WX 333 ; N Imacron ; B -8 0 441 795 ;
+C -1 ; WX 611 ; N Lcaron ; B -8 0 586 653 ;
+C -1 ; WX 750 ; N onehalf ; B 34 -10 749 676 ;
+C -1 ; WX 549 ; N lessequal ; B 26 0 523 658 ;
+C -1 ; WX 500 ; N ocircumflex ; B 27 -11 468 661 ;
+C -1 ; WX 500 ; N ntilde ; B 14 -9 476 624 ;
+C -1 ; WX 722 ; N Uhungarumlaut ; B 102 -18 765 876 ;
+C -1 ; WX 611 ; N Eacute ; B -1 0 634 876 ;
+C -1 ; WX 444 ; N emacron ; B 31 -11 457 583 ;
+C -1 ; WX 500 ; N gbreve ; B 8 -206 487 650 ;
+C -1 ; WX 750 ; N onequarter ; B 33 -10 736 676 ;
+C -1 ; WX 500 ; N Scaron ; B 17 -18 520 873 ;
+C -1 ; WX 500 ; N Scommaaccent ; B 17 -217 508 667 ;
+C -1 ; WX 722 ; N Ohungarumlaut ; B 60 -18 699 876 ;
+C -1 ; WX 400 ; N degree ; B 101 390 387 676 ;
+C -1 ; WX 500 ; N ograve ; B 27 -11 468 664 ;
+C -1 ; WX 667 ; N Ccaron ; B 66 -18 689 873 ;
+C -1 ; WX 500 ; N ugrave ; B 42 -11 475 664 ;
+C -1 ; WX 453 ; N radical ; B 2 -60 452 768 ;
+C -1 ; WX 722 ; N Dcaron ; B -8 0 700 873 ;
+C -1 ; WX 389 ; N rcommaaccent ; B -3 -217 412 441 ;
+C -1 ; WX 667 ; N Ntilde ; B -20 -15 727 836 ;
+C -1 ; WX 500 ; N otilde ; B 27 -11 496 624 ;
+C -1 ; WX 611 ; N Rcommaaccent ; B -13 -187 588 653 ;
+C -1 ; WX 556 ; N Lcommaaccent ; B -8 -217 559 653 ;
+C -1 ; WX 611 ; N Atilde ; B -51 0 566 836 ;
+C -1 ; WX 611 ; N Aogonek ; B -51 -169 566 668 ;
+C -1 ; WX 611 ; N Aring ; B -51 0 564 883 ;
+C -1 ; WX 722 ; N Otilde ; B 60 -18 699 836 ;
+C -1 ; WX 389 ; N zdotaccent ; B -2 -81 380 606 ;
+C -1 ; WX 611 ; N Ecaron ; B -1 0 634 873 ;
+C -1 ; WX 333 ; N Iogonek ; B -8 -169 384 653 ;
+C -1 ; WX 444 ; N kcommaaccent ; B 14 -187 461 683 ;
+C -1 ; WX 675 ; N minus ; B 86 220 590 286 ;
+C -1 ; WX 333 ; N Icircumflex ; B -8 0 425 873 ;
+C -1 ; WX 500 ; N ncaron ; B 14 -9 510 661 ;
+C -1 ; WX 278 ; N tcommaaccent ; B 2 -217 296 546 ;
+C -1 ; WX 675 ; N logicalnot ; B 86 108 590 386 ;
+C -1 ; WX 500 ; N odieresis ; B 27 -11 489 606 ;
+C -1 ; WX 500 ; N udieresis ; B 42 -11 479 606 ;
+C -1 ; WX 549 ; N notequal ; B 12 -29 537 541 ;
+C -1 ; WX 500 ; N gcommaaccent ; B 8 -206 472 706 ;
+C -1 ; WX 500 ; N eth ; B 27 -11 482 683 ;
+C -1 ; WX 389 ; N zcaron ; B -2 -81 434 661 ;
+C -1 ; WX 500 ; N ncommaaccent ; B 14 -187 474 441 ;
+C -1 ; WX 300 ; N onesuperior ; B 43 271 284 676 ;
+C -1 ; WX 278 ; N imacron ; B 46 -11 311 583 ;
+C -1 ; WX 500 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+StartKernData
+StartKernPairs 2321
+KPX A C -30
+KPX A Cacute -30
+KPX A Ccaron -30
+KPX A Ccedilla -30
+KPX A G -35
+KPX A Gbreve -35
+KPX A Gcommaaccent -35
+KPX A O -40
+KPX A Oacute -40
+KPX A Ocircumflex -40
+KPX A Odieresis -40
+KPX A Ograve -40
+KPX A Ohungarumlaut -40
+KPX A Omacron -40
+KPX A Oslash -40
+KPX A Otilde -40
+KPX A Q -40
+KPX A T -37
+KPX A Tcaron -37
+KPX A Tcommaaccent -37
+KPX A U -50
+KPX A Uacute -50
+KPX A Ucircumflex -50
+KPX A Udieresis -50
+KPX A Ugrave -50
+KPX A Uhungarumlaut -50
+KPX A Umacron -50
+KPX A Uogonek -50
+KPX A Uring -50
+KPX A V -105
+KPX A W -95
+KPX A Y -55
+KPX A Yacute -55
+KPX A Ydieresis -55
+KPX A quoteright -37
+KPX A u -20
+KPX A uacute -20
+KPX A ucircumflex -20
+KPX A udieresis -20
+KPX A ugrave -20
+KPX A uhungarumlaut -20
+KPX A umacron -20
+KPX A uogonek -20
+KPX A uring -20
+KPX A v -55
+KPX A w -55
+KPX A y -55
+KPX A yacute -55
+KPX A ydieresis -55
+KPX Aacute C -30
+KPX Aacute Cacute -30
+KPX Aacute Ccaron -30
+KPX Aacute Ccedilla -30
+KPX Aacute G -35
+KPX Aacute Gbreve -35
+KPX Aacute Gcommaaccent -35
+KPX Aacute O -40
+KPX Aacute Oacute -40
+KPX Aacute Ocircumflex -40
+KPX Aacute Odieresis -40
+KPX Aacute Ograve -40
+KPX Aacute Ohungarumlaut -40
+KPX Aacute Omacron -40
+KPX Aacute Oslash -40
+KPX Aacute Otilde -40
+KPX Aacute Q -40
+KPX Aacute T -37
+KPX Aacute Tcaron -37
+KPX Aacute Tcommaaccent -37
+KPX Aacute U -50
+KPX Aacute Uacute -50
+KPX Aacute Ucircumflex -50
+KPX Aacute Udieresis -50
+KPX Aacute Ugrave -50
+KPX Aacute Uhungarumlaut -50
+KPX Aacute Umacron -50
+KPX Aacute Uogonek -50
+KPX Aacute Uring -50
+KPX Aacute V -105
+KPX Aacute W -95
+KPX Aacute Y -55
+KPX Aacute Yacute -55
+KPX Aacute Ydieresis -55
+KPX Aacute quoteright -37
+KPX Aacute u -20
+KPX Aacute uacute -20
+KPX Aacute ucircumflex -20
+KPX Aacute udieresis -20
+KPX Aacute ugrave -20
+KPX Aacute uhungarumlaut -20
+KPX Aacute umacron -20
+KPX Aacute uogonek -20
+KPX Aacute uring -20
+KPX Aacute v -55
+KPX Aacute w -55
+KPX Aacute y -55
+KPX Aacute yacute -55
+KPX Aacute ydieresis -55
+KPX Abreve C -30
+KPX Abreve Cacute -30
+KPX Abreve Ccaron -30
+KPX Abreve Ccedilla -30
+KPX Abreve G -35
+KPX Abreve Gbreve -35
+KPX Abreve Gcommaaccent -35
+KPX Abreve O -40
+KPX Abreve Oacute -40
+KPX Abreve Ocircumflex -40
+KPX Abreve Odieresis -40
+KPX Abreve Ograve -40
+KPX Abreve Ohungarumlaut -40
+KPX Abreve Omacron -40
+KPX Abreve Oslash -40
+KPX Abreve Otilde -40
+KPX Abreve Q -40
+KPX Abreve T -37
+KPX Abreve Tcaron -37
+KPX Abreve Tcommaaccent -37
+KPX Abreve U -50
+KPX Abreve Uacute -50
+KPX Abreve Ucircumflex -50
+KPX Abreve Udieresis -50
+KPX Abreve Ugrave -50
+KPX Abreve Uhungarumlaut -50
+KPX Abreve Umacron -50
+KPX Abreve Uogonek -50
+KPX Abreve Uring -50
+KPX Abreve V -105
+KPX Abreve W -95
+KPX Abreve Y -55
+KPX Abreve Yacute -55
+KPX Abreve Ydieresis -55
+KPX Abreve quoteright -37
+KPX Abreve u -20
+KPX Abreve uacute -20
+KPX Abreve ucircumflex -20
+KPX Abreve udieresis -20
+KPX Abreve ugrave -20
+KPX Abreve uhungarumlaut -20
+KPX Abreve umacron -20
+KPX Abreve uogonek -20
+KPX Abreve uring -20
+KPX Abreve v -55
+KPX Abreve w -55
+KPX Abreve y -55
+KPX Abreve yacute -55
+KPX Abreve ydieresis -55
+KPX Acircumflex C -30
+KPX Acircumflex Cacute -30
+KPX Acircumflex Ccaron -30
+KPX Acircumflex Ccedilla -30
+KPX Acircumflex G -35
+KPX Acircumflex Gbreve -35
+KPX Acircumflex Gcommaaccent -35
+KPX Acircumflex O -40
+KPX Acircumflex Oacute -40
+KPX Acircumflex Ocircumflex -40
+KPX Acircumflex Odieresis -40
+KPX Acircumflex Ograve -40
+KPX Acircumflex Ohungarumlaut -40
+KPX Acircumflex Omacron -40
+KPX Acircumflex Oslash -40
+KPX Acircumflex Otilde -40
+KPX Acircumflex Q -40
+KPX Acircumflex T -37
+KPX Acircumflex Tcaron -37
+KPX Acircumflex Tcommaaccent -37
+KPX Acircumflex U -50
+KPX Acircumflex Uacute -50
+KPX Acircumflex Ucircumflex -50
+KPX Acircumflex Udieresis -50
+KPX Acircumflex Ugrave -50
+KPX Acircumflex Uhungarumlaut -50
+KPX Acircumflex Umacron -50
+KPX Acircumflex Uogonek -50
+KPX Acircumflex Uring -50
+KPX Acircumflex V -105
+KPX Acircumflex W -95
+KPX Acircumflex Y -55
+KPX Acircumflex Yacute -55
+KPX Acircumflex Ydieresis -55
+KPX Acircumflex quoteright -37
+KPX Acircumflex u -20
+KPX Acircumflex uacute -20
+KPX Acircumflex ucircumflex -20
+KPX Acircumflex udieresis -20
+KPX Acircumflex ugrave -20
+KPX Acircumflex uhungarumlaut -20
+KPX Acircumflex umacron -20
+KPX Acircumflex uogonek -20
+KPX Acircumflex uring -20
+KPX Acircumflex v -55
+KPX Acircumflex w -55
+KPX Acircumflex y -55
+KPX Acircumflex yacute -55
+KPX Acircumflex ydieresis -55
+KPX Adieresis C -30
+KPX Adieresis Cacute -30
+KPX Adieresis Ccaron -30
+KPX Adieresis Ccedilla -30
+KPX Adieresis G -35
+KPX Adieresis Gbreve -35
+KPX Adieresis Gcommaaccent -35
+KPX Adieresis O -40
+KPX Adieresis Oacute -40
+KPX Adieresis Ocircumflex -40
+KPX Adieresis Odieresis -40
+KPX Adieresis Ograve -40
+KPX Adieresis Ohungarumlaut -40
+KPX Adieresis Omacron -40
+KPX Adieresis Oslash -40
+KPX Adieresis Otilde -40
+KPX Adieresis Q -40
+KPX Adieresis T -37
+KPX Adieresis Tcaron -37
+KPX Adieresis Tcommaaccent -37
+KPX Adieresis U -50
+KPX Adieresis Uacute -50
+KPX Adieresis Ucircumflex -50
+KPX Adieresis Udieresis -50
+KPX Adieresis Ugrave -50
+KPX Adieresis Uhungarumlaut -50
+KPX Adieresis Umacron -50
+KPX Adieresis Uogonek -50
+KPX Adieresis Uring -50
+KPX Adieresis V -105
+KPX Adieresis W -95
+KPX Adieresis Y -55
+KPX Adieresis Yacute -55
+KPX Adieresis Ydieresis -55
+KPX Adieresis quoteright -37
+KPX Adieresis u -20
+KPX Adieresis uacute -20
+KPX Adieresis ucircumflex -20
+KPX Adieresis udieresis -20
+KPX Adieresis ugrave -20
+KPX Adieresis uhungarumlaut -20
+KPX Adieresis umacron -20
+KPX Adieresis uogonek -20
+KPX Adieresis uring -20
+KPX Adieresis v -55
+KPX Adieresis w -55
+KPX Adieresis y -55
+KPX Adieresis yacute -55
+KPX Adieresis ydieresis -55
+KPX Agrave C -30
+KPX Agrave Cacute -30
+KPX Agrave Ccaron -30
+KPX Agrave Ccedilla -30
+KPX Agrave G -35
+KPX Agrave Gbreve -35
+KPX Agrave Gcommaaccent -35
+KPX Agrave O -40
+KPX Agrave Oacute -40
+KPX Agrave Ocircumflex -40
+KPX Agrave Odieresis -40
+KPX Agrave Ograve -40
+KPX Agrave Ohungarumlaut -40
+KPX Agrave Omacron -40
+KPX Agrave Oslash -40
+KPX Agrave Otilde -40
+KPX Agrave Q -40
+KPX Agrave T -37
+KPX Agrave Tcaron -37
+KPX Agrave Tcommaaccent -37
+KPX Agrave U -50
+KPX Agrave Uacute -50
+KPX Agrave Ucircumflex -50
+KPX Agrave Udieresis -50
+KPX Agrave Ugrave -50
+KPX Agrave Uhungarumlaut -50
+KPX Agrave Umacron -50
+KPX Agrave Uogonek -50
+KPX Agrave Uring -50
+KPX Agrave V -105
+KPX Agrave W -95
+KPX Agrave Y -55
+KPX Agrave Yacute -55
+KPX Agrave Ydieresis -55
+KPX Agrave quoteright -37
+KPX Agrave u -20
+KPX Agrave uacute -20
+KPX Agrave ucircumflex -20
+KPX Agrave udieresis -20
+KPX Agrave ugrave -20
+KPX Agrave uhungarumlaut -20
+KPX Agrave umacron -20
+KPX Agrave uogonek -20
+KPX Agrave uring -20
+KPX Agrave v -55
+KPX Agrave w -55
+KPX Agrave y -55
+KPX Agrave yacute -55
+KPX Agrave ydieresis -55
+KPX Amacron C -30
+KPX Amacron Cacute -30
+KPX Amacron Ccaron -30
+KPX Amacron Ccedilla -30
+KPX Amacron G -35
+KPX Amacron Gbreve -35
+KPX Amacron Gcommaaccent -35
+KPX Amacron O -40
+KPX Amacron Oacute -40
+KPX Amacron Ocircumflex -40
+KPX Amacron Odieresis -40
+KPX Amacron Ograve -40
+KPX Amacron Ohungarumlaut -40
+KPX Amacron Omacron -40
+KPX Amacron Oslash -40
+KPX Amacron Otilde -40
+KPX Amacron Q -40
+KPX Amacron T -37
+KPX Amacron Tcaron -37
+KPX Amacron Tcommaaccent -37
+KPX Amacron U -50
+KPX Amacron Uacute -50
+KPX Amacron Ucircumflex -50
+KPX Amacron Udieresis -50
+KPX Amacron Ugrave -50
+KPX Amacron Uhungarumlaut -50
+KPX Amacron Umacron -50
+KPX Amacron Uogonek -50
+KPX Amacron Uring -50
+KPX Amacron V -105
+KPX Amacron W -95
+KPX Amacron Y -55
+KPX Amacron Yacute -55
+KPX Amacron Ydieresis -55
+KPX Amacron quoteright -37
+KPX Amacron u -20
+KPX Amacron uacute -20
+KPX Amacron ucircumflex -20
+KPX Amacron udieresis -20
+KPX Amacron ugrave -20
+KPX Amacron uhungarumlaut -20
+KPX Amacron umacron -20
+KPX Amacron uogonek -20
+KPX Amacron uring -20
+KPX Amacron v -55
+KPX Amacron w -55
+KPX Amacron y -55
+KPX Amacron yacute -55
+KPX Amacron ydieresis -55
+KPX Aogonek C -30
+KPX Aogonek Cacute -30
+KPX Aogonek Ccaron -30
+KPX Aogonek Ccedilla -30
+KPX Aogonek G -35
+KPX Aogonek Gbreve -35
+KPX Aogonek Gcommaaccent -35
+KPX Aogonek O -40
+KPX Aogonek Oacute -40
+KPX Aogonek Ocircumflex -40
+KPX Aogonek Odieresis -40
+KPX Aogonek Ograve -40
+KPX Aogonek Ohungarumlaut -40
+KPX Aogonek Omacron -40
+KPX Aogonek Oslash -40
+KPX Aogonek Otilde -40
+KPX Aogonek Q -40
+KPX Aogonek T -37
+KPX Aogonek Tcaron -37
+KPX Aogonek Tcommaaccent -37
+KPX Aogonek U -50
+KPX Aogonek Uacute -50
+KPX Aogonek Ucircumflex -50
+KPX Aogonek Udieresis -50
+KPX Aogonek Ugrave -50
+KPX Aogonek Uhungarumlaut -50
+KPX Aogonek Umacron -50
+KPX Aogonek Uogonek -50
+KPX Aogonek Uring -50
+KPX Aogonek V -105
+KPX Aogonek W -95
+KPX Aogonek Y -55
+KPX Aogonek Yacute -55
+KPX Aogonek Ydieresis -55
+KPX Aogonek quoteright -37
+KPX Aogonek u -20
+KPX Aogonek uacute -20
+KPX Aogonek ucircumflex -20
+KPX Aogonek udieresis -20
+KPX Aogonek ugrave -20
+KPX Aogonek uhungarumlaut -20
+KPX Aogonek umacron -20
+KPX Aogonek uogonek -20
+KPX Aogonek uring -20
+KPX Aogonek v -55
+KPX Aogonek w -55
+KPX Aogonek y -55
+KPX Aogonek yacute -55
+KPX Aogonek ydieresis -55
+KPX Aring C -30
+KPX Aring Cacute -30
+KPX Aring Ccaron -30
+KPX Aring Ccedilla -30
+KPX Aring G -35
+KPX Aring Gbreve -35
+KPX Aring Gcommaaccent -35
+KPX Aring O -40
+KPX Aring Oacute -40
+KPX Aring Ocircumflex -40
+KPX Aring Odieresis -40
+KPX Aring Ograve -40
+KPX Aring Ohungarumlaut -40
+KPX Aring Omacron -40
+KPX Aring Oslash -40
+KPX Aring Otilde -40
+KPX Aring Q -40
+KPX Aring T -37
+KPX Aring Tcaron -37
+KPX Aring Tcommaaccent -37
+KPX Aring U -50
+KPX Aring Uacute -50
+KPX Aring Ucircumflex -50
+KPX Aring Udieresis -50
+KPX Aring Ugrave -50
+KPX Aring Uhungarumlaut -50
+KPX Aring Umacron -50
+KPX Aring Uogonek -50
+KPX Aring Uring -50
+KPX Aring V -105
+KPX Aring W -95
+KPX Aring Y -55
+KPX Aring Yacute -55
+KPX Aring Ydieresis -55
+KPX Aring quoteright -37
+KPX Aring u -20
+KPX Aring uacute -20
+KPX Aring ucircumflex -20
+KPX Aring udieresis -20
+KPX Aring ugrave -20
+KPX Aring uhungarumlaut -20
+KPX Aring umacron -20
+KPX Aring uogonek -20
+KPX Aring uring -20
+KPX Aring v -55
+KPX Aring w -55
+KPX Aring y -55
+KPX Aring yacute -55
+KPX Aring ydieresis -55
+KPX Atilde C -30
+KPX Atilde Cacute -30
+KPX Atilde Ccaron -30
+KPX Atilde Ccedilla -30
+KPX Atilde G -35
+KPX Atilde Gbreve -35
+KPX Atilde Gcommaaccent -35
+KPX Atilde O -40
+KPX Atilde Oacute -40
+KPX Atilde Ocircumflex -40
+KPX Atilde Odieresis -40
+KPX Atilde Ograve -40
+KPX Atilde Ohungarumlaut -40
+KPX Atilde Omacron -40
+KPX Atilde Oslash -40
+KPX Atilde Otilde -40
+KPX Atilde Q -40
+KPX Atilde T -37
+KPX Atilde Tcaron -37
+KPX Atilde Tcommaaccent -37
+KPX Atilde U -50
+KPX Atilde Uacute -50
+KPX Atilde Ucircumflex -50
+KPX Atilde Udieresis -50
+KPX Atilde Ugrave -50
+KPX Atilde Uhungarumlaut -50
+KPX Atilde Umacron -50
+KPX Atilde Uogonek -50
+KPX Atilde Uring -50
+KPX Atilde V -105
+KPX Atilde W -95
+KPX Atilde Y -55
+KPX Atilde Yacute -55
+KPX Atilde Ydieresis -55
+KPX Atilde quoteright -37
+KPX Atilde u -20
+KPX Atilde uacute -20
+KPX Atilde ucircumflex -20
+KPX Atilde udieresis -20
+KPX Atilde ugrave -20
+KPX Atilde uhungarumlaut -20
+KPX Atilde umacron -20
+KPX Atilde uogonek -20
+KPX Atilde uring -20
+KPX Atilde v -55
+KPX Atilde w -55
+KPX Atilde y -55
+KPX Atilde yacute -55
+KPX Atilde ydieresis -55
+KPX B A -25
+KPX B Aacute -25
+KPX B Abreve -25
+KPX B Acircumflex -25
+KPX B Adieresis -25
+KPX B Agrave -25
+KPX B Amacron -25
+KPX B Aogonek -25
+KPX B Aring -25
+KPX B Atilde -25
+KPX B U -10
+KPX B Uacute -10
+KPX B Ucircumflex -10
+KPX B Udieresis -10
+KPX B Ugrave -10
+KPX B Uhungarumlaut -10
+KPX B Umacron -10
+KPX B Uogonek -10
+KPX B Uring -10
+KPX D A -35
+KPX D Aacute -35
+KPX D Abreve -35
+KPX D Acircumflex -35
+KPX D Adieresis -35
+KPX D Agrave -35
+KPX D Amacron -35
+KPX D Aogonek -35
+KPX D Aring -35
+KPX D Atilde -35
+KPX D V -40
+KPX D W -40
+KPX D Y -40
+KPX D Yacute -40
+KPX D Ydieresis -40
+KPX Dcaron A -35
+KPX Dcaron Aacute -35
+KPX Dcaron Abreve -35
+KPX Dcaron Acircumflex -35
+KPX Dcaron Adieresis -35
+KPX Dcaron Agrave -35
+KPX Dcaron Amacron -35
+KPX Dcaron Aogonek -35
+KPX Dcaron Aring -35
+KPX Dcaron Atilde -35
+KPX Dcaron V -40
+KPX Dcaron W -40
+KPX Dcaron Y -40
+KPX Dcaron Yacute -40
+KPX Dcaron Ydieresis -40
+KPX Dcroat A -35
+KPX Dcroat Aacute -35
+KPX Dcroat Abreve -35
+KPX Dcroat Acircumflex -35
+KPX Dcroat Adieresis -35
+KPX Dcroat Agrave -35
+KPX Dcroat Amacron -35
+KPX Dcroat Aogonek -35
+KPX Dcroat Aring -35
+KPX Dcroat Atilde -35
+KPX Dcroat V -40
+KPX Dcroat W -40
+KPX Dcroat Y -40
+KPX Dcroat Yacute -40
+KPX Dcroat Ydieresis -40
+KPX F A -115
+KPX F Aacute -115
+KPX F Abreve -115
+KPX F Acircumflex -115
+KPX F Adieresis -115
+KPX F Agrave -115
+KPX F Amacron -115
+KPX F Aogonek -115
+KPX F Aring -115
+KPX F Atilde -115
+KPX F a -75
+KPX F aacute -75
+KPX F abreve -75
+KPX F acircumflex -75
+KPX F adieresis -75
+KPX F agrave -75
+KPX F amacron -75
+KPX F aogonek -75
+KPX F aring -75
+KPX F atilde -75
+KPX F comma -135
+KPX F e -75
+KPX F eacute -75
+KPX F ecaron -75
+KPX F ecircumflex -75
+KPX F edieresis -75
+KPX F edotaccent -75
+KPX F egrave -75
+KPX F emacron -75
+KPX F eogonek -75
+KPX F i -45
+KPX F iacute -45
+KPX F icircumflex -45
+KPX F idieresis -45
+KPX F igrave -45
+KPX F imacron -45
+KPX F iogonek -45
+KPX F o -105
+KPX F oacute -105
+KPX F ocircumflex -105
+KPX F odieresis -105
+KPX F ograve -105
+KPX F ohungarumlaut -105
+KPX F omacron -105
+KPX F oslash -105
+KPX F otilde -105
+KPX F period -135
+KPX F r -55
+KPX F racute -55
+KPX F rcaron -55
+KPX F rcommaaccent -55
+KPX J A -40
+KPX J Aacute -40
+KPX J Abreve -40
+KPX J Acircumflex -40
+KPX J Adieresis -40
+KPX J Agrave -40
+KPX J Amacron -40
+KPX J Aogonek -40
+KPX J Aring -40
+KPX J Atilde -40
+KPX J a -35
+KPX J aacute -35
+KPX J abreve -35
+KPX J acircumflex -35
+KPX J adieresis -35
+KPX J agrave -35
+KPX J amacron -35
+KPX J aogonek -35
+KPX J aring -35
+KPX J atilde -35
+KPX J comma -25
+KPX J e -25
+KPX J eacute -25
+KPX J ecaron -25
+KPX J ecircumflex -25
+KPX J edieresis -25
+KPX J edotaccent -25
+KPX J egrave -25
+KPX J emacron -25
+KPX J eogonek -25
+KPX J o -25
+KPX J oacute -25
+KPX J ocircumflex -25
+KPX J odieresis -25
+KPX J ograve -25
+KPX J ohungarumlaut -25
+KPX J omacron -25
+KPX J oslash -25
+KPX J otilde -25
+KPX J period -25
+KPX J u -35
+KPX J uacute -35
+KPX J ucircumflex -35
+KPX J udieresis -35
+KPX J ugrave -35
+KPX J uhungarumlaut -35
+KPX J umacron -35
+KPX J uogonek -35
+KPX J uring -35
+KPX K O -50
+KPX K Oacute -50
+KPX K Ocircumflex -50
+KPX K Odieresis -50
+KPX K Ograve -50
+KPX K Ohungarumlaut -50
+KPX K Omacron -50
+KPX K Oslash -50
+KPX K Otilde -50
+KPX K e -35
+KPX K eacute -35
+KPX K ecaron -35
+KPX K ecircumflex -35
+KPX K edieresis -35
+KPX K edotaccent -35
+KPX K egrave -35
+KPX K emacron -35
+KPX K eogonek -35
+KPX K o -40
+KPX K oacute -40
+KPX K ocircumflex -40
+KPX K odieresis -40
+KPX K ograve -40
+KPX K ohungarumlaut -40
+KPX K omacron -40
+KPX K oslash -40
+KPX K otilde -40
+KPX K u -40
+KPX K uacute -40
+KPX K ucircumflex -40
+KPX K udieresis -40
+KPX K ugrave -40
+KPX K uhungarumlaut -40
+KPX K umacron -40
+KPX K uogonek -40
+KPX K uring -40
+KPX K y -40
+KPX K yacute -40
+KPX K ydieresis -40
+KPX Kcommaaccent O -50
+KPX Kcommaaccent Oacute -50
+KPX Kcommaaccent Ocircumflex -50
+KPX Kcommaaccent Odieresis -50
+KPX Kcommaaccent Ograve -50
+KPX Kcommaaccent Ohungarumlaut -50
+KPX Kcommaaccent Omacron -50
+KPX Kcommaaccent Oslash -50
+KPX Kcommaaccent Otilde -50
+KPX Kcommaaccent e -35
+KPX Kcommaaccent eacute -35
+KPX Kcommaaccent ecaron -35
+KPX Kcommaaccent ecircumflex -35
+KPX Kcommaaccent edieresis -35
+KPX Kcommaaccent edotaccent -35
+KPX Kcommaaccent egrave -35
+KPX Kcommaaccent emacron -35
+KPX Kcommaaccent eogonek -35
+KPX Kcommaaccent o -40
+KPX Kcommaaccent oacute -40
+KPX Kcommaaccent ocircumflex -40
+KPX Kcommaaccent odieresis -40
+KPX Kcommaaccent ograve -40
+KPX Kcommaaccent ohungarumlaut -40
+KPX Kcommaaccent omacron -40
+KPX Kcommaaccent oslash -40
+KPX Kcommaaccent otilde -40
+KPX Kcommaaccent u -40
+KPX Kcommaaccent uacute -40
+KPX Kcommaaccent ucircumflex -40
+KPX Kcommaaccent udieresis -40
+KPX Kcommaaccent ugrave -40
+KPX Kcommaaccent uhungarumlaut -40
+KPX Kcommaaccent umacron -40
+KPX Kcommaaccent uogonek -40
+KPX Kcommaaccent uring -40
+KPX Kcommaaccent y -40
+KPX Kcommaaccent yacute -40
+KPX Kcommaaccent ydieresis -40
+KPX L T -20
+KPX L Tcaron -20
+KPX L Tcommaaccent -20
+KPX L V -55
+KPX L W -55
+KPX L Y -20
+KPX L Yacute -20
+KPX L Ydieresis -20
+KPX L quoteright -37
+KPX L y -30
+KPX L yacute -30
+KPX L ydieresis -30
+KPX Lacute T -20
+KPX Lacute Tcaron -20
+KPX Lacute Tcommaaccent -20
+KPX Lacute V -55
+KPX Lacute W -55
+KPX Lacute Y -20
+KPX Lacute Yacute -20
+KPX Lacute Ydieresis -20
+KPX Lacute quoteright -37
+KPX Lacute y -30
+KPX Lacute yacute -30
+KPX Lacute ydieresis -30
+KPX Lcommaaccent T -20
+KPX Lcommaaccent Tcaron -20
+KPX Lcommaaccent Tcommaaccent -20
+KPX Lcommaaccent V -55
+KPX Lcommaaccent W -55
+KPX Lcommaaccent Y -20
+KPX Lcommaaccent Yacute -20
+KPX Lcommaaccent Ydieresis -20
+KPX Lcommaaccent quoteright -37
+KPX Lcommaaccent y -30
+KPX Lcommaaccent yacute -30
+KPX Lcommaaccent ydieresis -30
+KPX Lslash T -20
+KPX Lslash Tcaron -20
+KPX Lslash Tcommaaccent -20
+KPX Lslash V -55
+KPX Lslash W -55
+KPX Lslash Y -20
+KPX Lslash Yacute -20
+KPX Lslash Ydieresis -20
+KPX Lslash quoteright -37
+KPX Lslash y -30
+KPX Lslash yacute -30
+KPX Lslash ydieresis -30
+KPX N A -27
+KPX N Aacute -27
+KPX N Abreve -27
+KPX N Acircumflex -27
+KPX N Adieresis -27
+KPX N Agrave -27
+KPX N Amacron -27
+KPX N Aogonek -27
+KPX N Aring -27
+KPX N Atilde -27
+KPX Nacute A -27
+KPX Nacute Aacute -27
+KPX Nacute Abreve -27
+KPX Nacute Acircumflex -27
+KPX Nacute Adieresis -27
+KPX Nacute Agrave -27
+KPX Nacute Amacron -27
+KPX Nacute Aogonek -27
+KPX Nacute Aring -27
+KPX Nacute Atilde -27
+KPX Ncaron A -27
+KPX Ncaron Aacute -27
+KPX Ncaron Abreve -27
+KPX Ncaron Acircumflex -27
+KPX Ncaron Adieresis -27
+KPX Ncaron Agrave -27
+KPX Ncaron Amacron -27
+KPX Ncaron Aogonek -27
+KPX Ncaron Aring -27
+KPX Ncaron Atilde -27
+KPX Ncommaaccent A -27
+KPX Ncommaaccent Aacute -27
+KPX Ncommaaccent Abreve -27
+KPX Ncommaaccent Acircumflex -27
+KPX Ncommaaccent Adieresis -27
+KPX Ncommaaccent Agrave -27
+KPX Ncommaaccent Amacron -27
+KPX Ncommaaccent Aogonek -27
+KPX Ncommaaccent Aring -27
+KPX Ncommaaccent Atilde -27
+KPX Ntilde A -27
+KPX Ntilde Aacute -27
+KPX Ntilde Abreve -27
+KPX Ntilde Acircumflex -27
+KPX Ntilde Adieresis -27
+KPX Ntilde Agrave -27
+KPX Ntilde Amacron -27
+KPX Ntilde Aogonek -27
+KPX Ntilde Aring -27
+KPX Ntilde Atilde -27
+KPX O A -55
+KPX O Aacute -55
+KPX O Abreve -55
+KPX O Acircumflex -55
+KPX O Adieresis -55
+KPX O Agrave -55
+KPX O Amacron -55
+KPX O Aogonek -55
+KPX O Aring -55
+KPX O Atilde -55
+KPX O T -40
+KPX O Tcaron -40
+KPX O Tcommaaccent -40
+KPX O V -50
+KPX O W -50
+KPX O X -40
+KPX O Y -50
+KPX O Yacute -50
+KPX O Ydieresis -50
+KPX Oacute A -55
+KPX Oacute Aacute -55
+KPX Oacute Abreve -55
+KPX Oacute Acircumflex -55
+KPX Oacute Adieresis -55
+KPX Oacute Agrave -55
+KPX Oacute Amacron -55
+KPX Oacute Aogonek -55
+KPX Oacute Aring -55
+KPX Oacute Atilde -55
+KPX Oacute T -40
+KPX Oacute Tcaron -40
+KPX Oacute Tcommaaccent -40
+KPX Oacute V -50
+KPX Oacute W -50
+KPX Oacute X -40
+KPX Oacute Y -50
+KPX Oacute Yacute -50
+KPX Oacute Ydieresis -50
+KPX Ocircumflex A -55
+KPX Ocircumflex Aacute -55
+KPX Ocircumflex Abreve -55
+KPX Ocircumflex Acircumflex -55
+KPX Ocircumflex Adieresis -55
+KPX Ocircumflex Agrave -55
+KPX Ocircumflex Amacron -55
+KPX Ocircumflex Aogonek -55
+KPX Ocircumflex Aring -55
+KPX Ocircumflex Atilde -55
+KPX Ocircumflex T -40
+KPX Ocircumflex Tcaron -40
+KPX Ocircumflex Tcommaaccent -40
+KPX Ocircumflex V -50
+KPX Ocircumflex W -50
+KPX Ocircumflex X -40
+KPX Ocircumflex Y -50
+KPX Ocircumflex Yacute -50
+KPX Ocircumflex Ydieresis -50
+KPX Odieresis A -55
+KPX Odieresis Aacute -55
+KPX Odieresis Abreve -55
+KPX Odieresis Acircumflex -55
+KPX Odieresis Adieresis -55
+KPX Odieresis Agrave -55
+KPX Odieresis Amacron -55
+KPX Odieresis Aogonek -55
+KPX Odieresis Aring -55
+KPX Odieresis Atilde -55
+KPX Odieresis T -40
+KPX Odieresis Tcaron -40
+KPX Odieresis Tcommaaccent -40
+KPX Odieresis V -50
+KPX Odieresis W -50
+KPX Odieresis X -40
+KPX Odieresis Y -50
+KPX Odieresis Yacute -50
+KPX Odieresis Ydieresis -50
+KPX Ograve A -55
+KPX Ograve Aacute -55
+KPX Ograve Abreve -55
+KPX Ograve Acircumflex -55
+KPX Ograve Adieresis -55
+KPX Ograve Agrave -55
+KPX Ograve Amacron -55
+KPX Ograve Aogonek -55
+KPX Ograve Aring -55
+KPX Ograve Atilde -55
+KPX Ograve T -40
+KPX Ograve Tcaron -40
+KPX Ograve Tcommaaccent -40
+KPX Ograve V -50
+KPX Ograve W -50
+KPX Ograve X -40
+KPX Ograve Y -50
+KPX Ograve Yacute -50
+KPX Ograve Ydieresis -50
+KPX Ohungarumlaut A -55
+KPX Ohungarumlaut Aacute -55
+KPX Ohungarumlaut Abreve -55
+KPX Ohungarumlaut Acircumflex -55
+KPX Ohungarumlaut Adieresis -55
+KPX Ohungarumlaut Agrave -55
+KPX Ohungarumlaut Amacron -55
+KPX Ohungarumlaut Aogonek -55
+KPX Ohungarumlaut Aring -55
+KPX Ohungarumlaut Atilde -55
+KPX Ohungarumlaut T -40
+KPX Ohungarumlaut Tcaron -40
+KPX Ohungarumlaut Tcommaaccent -40
+KPX Ohungarumlaut V -50
+KPX Ohungarumlaut W -50
+KPX Ohungarumlaut X -40
+KPX Ohungarumlaut Y -50
+KPX Ohungarumlaut Yacute -50
+KPX Ohungarumlaut Ydieresis -50
+KPX Omacron A -55
+KPX Omacron Aacute -55
+KPX Omacron Abreve -55
+KPX Omacron Acircumflex -55
+KPX Omacron Adieresis -55
+KPX Omacron Agrave -55
+KPX Omacron Amacron -55
+KPX Omacron Aogonek -55
+KPX Omacron Aring -55
+KPX Omacron Atilde -55
+KPX Omacron T -40
+KPX Omacron Tcaron -40
+KPX Omacron Tcommaaccent -40
+KPX Omacron V -50
+KPX Omacron W -50
+KPX Omacron X -40
+KPX Omacron Y -50
+KPX Omacron Yacute -50
+KPX Omacron Ydieresis -50
+KPX Oslash A -55
+KPX Oslash Aacute -55
+KPX Oslash Abreve -55
+KPX Oslash Acircumflex -55
+KPX Oslash Adieresis -55
+KPX Oslash Agrave -55
+KPX Oslash Amacron -55
+KPX Oslash Aogonek -55
+KPX Oslash Aring -55
+KPX Oslash Atilde -55
+KPX Oslash T -40
+KPX Oslash Tcaron -40
+KPX Oslash Tcommaaccent -40
+KPX Oslash V -50
+KPX Oslash W -50
+KPX Oslash X -40
+KPX Oslash Y -50
+KPX Oslash Yacute -50
+KPX Oslash Ydieresis -50
+KPX Otilde A -55
+KPX Otilde Aacute -55
+KPX Otilde Abreve -55
+KPX Otilde Acircumflex -55
+KPX Otilde Adieresis -55
+KPX Otilde Agrave -55
+KPX Otilde Amacron -55
+KPX Otilde Aogonek -55
+KPX Otilde Aring -55
+KPX Otilde Atilde -55
+KPX Otilde T -40
+KPX Otilde Tcaron -40
+KPX Otilde Tcommaaccent -40
+KPX Otilde V -50
+KPX Otilde W -50
+KPX Otilde X -40
+KPX Otilde Y -50
+KPX Otilde Yacute -50
+KPX Otilde Ydieresis -50
+KPX P A -90
+KPX P Aacute -90
+KPX P Abreve -90
+KPX P Acircumflex -90
+KPX P Adieresis -90
+KPX P Agrave -90
+KPX P Amacron -90
+KPX P Aogonek -90
+KPX P Aring -90
+KPX P Atilde -90
+KPX P a -80
+KPX P aacute -80
+KPX P abreve -80
+KPX P acircumflex -80
+KPX P adieresis -80
+KPX P agrave -80
+KPX P amacron -80
+KPX P aogonek -80
+KPX P aring -80
+KPX P atilde -80
+KPX P comma -135
+KPX P e -80
+KPX P eacute -80
+KPX P ecaron -80
+KPX P ecircumflex -80
+KPX P edieresis -80
+KPX P edotaccent -80
+KPX P egrave -80
+KPX P emacron -80
+KPX P eogonek -80
+KPX P o -80
+KPX P oacute -80
+KPX P ocircumflex -80
+KPX P odieresis -80
+KPX P ograve -80
+KPX P ohungarumlaut -80
+KPX P omacron -80
+KPX P oslash -80
+KPX P otilde -80
+KPX P period -135
+KPX Q U -10
+KPX Q Uacute -10
+KPX Q Ucircumflex -10
+KPX Q Udieresis -10
+KPX Q Ugrave -10
+KPX Q Uhungarumlaut -10
+KPX Q Umacron -10
+KPX Q Uogonek -10
+KPX Q Uring -10
+KPX R O -40
+KPX R Oacute -40
+KPX R Ocircumflex -40
+KPX R Odieresis -40
+KPX R Ograve -40
+KPX R Ohungarumlaut -40
+KPX R Omacron -40
+KPX R Oslash -40
+KPX R Otilde -40
+KPX R U -40
+KPX R Uacute -40
+KPX R Ucircumflex -40
+KPX R Udieresis -40
+KPX R Ugrave -40
+KPX R Uhungarumlaut -40
+KPX R Umacron -40
+KPX R Uogonek -40
+KPX R Uring -40
+KPX R V -18
+KPX R W -18
+KPX R Y -18
+KPX R Yacute -18
+KPX R Ydieresis -18
+KPX Racute O -40
+KPX Racute Oacute -40
+KPX Racute Ocircumflex -40
+KPX Racute Odieresis -40
+KPX Racute Ograve -40
+KPX Racute Ohungarumlaut -40
+KPX Racute Omacron -40
+KPX Racute Oslash -40
+KPX Racute Otilde -40
+KPX Racute U -40
+KPX Racute Uacute -40
+KPX Racute Ucircumflex -40
+KPX Racute Udieresis -40
+KPX Racute Ugrave -40
+KPX Racute Uhungarumlaut -40
+KPX Racute Umacron -40
+KPX Racute Uogonek -40
+KPX Racute Uring -40
+KPX Racute V -18
+KPX Racute W -18
+KPX Racute Y -18
+KPX Racute Yacute -18
+KPX Racute Ydieresis -18
+KPX Rcaron O -40
+KPX Rcaron Oacute -40
+KPX Rcaron Ocircumflex -40
+KPX Rcaron Odieresis -40
+KPX Rcaron Ograve -40
+KPX Rcaron Ohungarumlaut -40
+KPX Rcaron Omacron -40
+KPX Rcaron Oslash -40
+KPX Rcaron Otilde -40
+KPX Rcaron U -40
+KPX Rcaron Uacute -40
+KPX Rcaron Ucircumflex -40
+KPX Rcaron Udieresis -40
+KPX Rcaron Ugrave -40
+KPX Rcaron Uhungarumlaut -40
+KPX Rcaron Umacron -40
+KPX Rcaron Uogonek -40
+KPX Rcaron Uring -40
+KPX Rcaron V -18
+KPX Rcaron W -18
+KPX Rcaron Y -18
+KPX Rcaron Yacute -18
+KPX Rcaron Ydieresis -18
+KPX Rcommaaccent O -40
+KPX Rcommaaccent Oacute -40
+KPX Rcommaaccent Ocircumflex -40
+KPX Rcommaaccent Odieresis -40
+KPX Rcommaaccent Ograve -40
+KPX Rcommaaccent Ohungarumlaut -40
+KPX Rcommaaccent Omacron -40
+KPX Rcommaaccent Oslash -40
+KPX Rcommaaccent Otilde -40
+KPX Rcommaaccent U -40
+KPX Rcommaaccent Uacute -40
+KPX Rcommaaccent Ucircumflex -40
+KPX Rcommaaccent Udieresis -40
+KPX Rcommaaccent Ugrave -40
+KPX Rcommaaccent Uhungarumlaut -40
+KPX Rcommaaccent Umacron -40
+KPX Rcommaaccent Uogonek -40
+KPX Rcommaaccent Uring -40
+KPX Rcommaaccent V -18
+KPX Rcommaaccent W -18
+KPX Rcommaaccent Y -18
+KPX Rcommaaccent Yacute -18
+KPX Rcommaaccent Ydieresis -18
+KPX T A -50
+KPX T Aacute -50
+KPX T Abreve -50
+KPX T Acircumflex -50
+KPX T Adieresis -50
+KPX T Agrave -50
+KPX T Amacron -50
+KPX T Aogonek -50
+KPX T Aring -50
+KPX T Atilde -50
+KPX T O -18
+KPX T Oacute -18
+KPX T Ocircumflex -18
+KPX T Odieresis -18
+KPX T Ograve -18
+KPX T Ohungarumlaut -18
+KPX T Omacron -18
+KPX T Oslash -18
+KPX T Otilde -18
+KPX T a -92
+KPX T aacute -92
+KPX T abreve -92
+KPX T acircumflex -92
+KPX T adieresis -92
+KPX T agrave -92
+KPX T amacron -92
+KPX T aogonek -92
+KPX T aring -92
+KPX T atilde -92
+KPX T colon -55
+KPX T comma -74
+KPX T e -92
+KPX T eacute -92
+KPX T ecaron -92
+KPX T ecircumflex -52
+KPX T edieresis -52
+KPX T edotaccent -92
+KPX T egrave -52
+KPX T emacron -52
+KPX T eogonek -92
+KPX T hyphen -74
+KPX T i -55
+KPX T iacute -55
+KPX T iogonek -55
+KPX T o -92
+KPX T oacute -92
+KPX T ocircumflex -92
+KPX T odieresis -92
+KPX T ograve -92
+KPX T ohungarumlaut -92
+KPX T omacron -92
+KPX T oslash -92
+KPX T otilde -92
+KPX T period -74
+KPX T r -55
+KPX T racute -55
+KPX T rcaron -55
+KPX T rcommaaccent -55
+KPX T semicolon -65
+KPX T u -55
+KPX T uacute -55
+KPX T ucircumflex -55
+KPX T udieresis -55
+KPX T ugrave -55
+KPX T uhungarumlaut -55
+KPX T umacron -55
+KPX T uogonek -55
+KPX T uring -55
+KPX T w -74
+KPX T y -74
+KPX T yacute -74
+KPX T ydieresis -34
+KPX Tcaron A -50
+KPX Tcaron Aacute -50
+KPX Tcaron Abreve -50
+KPX Tcaron Acircumflex -50
+KPX Tcaron Adieresis -50
+KPX Tcaron Agrave -50
+KPX Tcaron Amacron -50
+KPX Tcaron Aogonek -50
+KPX Tcaron Aring -50
+KPX Tcaron Atilde -50
+KPX Tcaron O -18
+KPX Tcaron Oacute -18
+KPX Tcaron Ocircumflex -18
+KPX Tcaron Odieresis -18
+KPX Tcaron Ograve -18
+KPX Tcaron Ohungarumlaut -18
+KPX Tcaron Omacron -18
+KPX Tcaron Oslash -18
+KPX Tcaron Otilde -18
+KPX Tcaron a -92
+KPX Tcaron aacute -92
+KPX Tcaron abreve -92
+KPX Tcaron acircumflex -92
+KPX Tcaron adieresis -92
+KPX Tcaron agrave -92
+KPX Tcaron amacron -92
+KPX Tcaron aogonek -92
+KPX Tcaron aring -92
+KPX Tcaron atilde -92
+KPX Tcaron colon -55
+KPX Tcaron comma -74
+KPX Tcaron e -92
+KPX Tcaron eacute -92
+KPX Tcaron ecaron -92
+KPX Tcaron ecircumflex -52
+KPX Tcaron edieresis -52
+KPX Tcaron edotaccent -92
+KPX Tcaron egrave -52
+KPX Tcaron emacron -52
+KPX Tcaron eogonek -92
+KPX Tcaron hyphen -74
+KPX Tcaron i -55
+KPX Tcaron iacute -55
+KPX Tcaron iogonek -55
+KPX Tcaron o -92
+KPX Tcaron oacute -92
+KPX Tcaron ocircumflex -92
+KPX Tcaron odieresis -92
+KPX Tcaron ograve -92
+KPX Tcaron ohungarumlaut -92
+KPX Tcaron omacron -92
+KPX Tcaron oslash -92
+KPX Tcaron otilde -92
+KPX Tcaron period -74
+KPX Tcaron r -55
+KPX Tcaron racute -55
+KPX Tcaron rcaron -55
+KPX Tcaron rcommaaccent -55
+KPX Tcaron semicolon -65
+KPX Tcaron u -55
+KPX Tcaron uacute -55
+KPX Tcaron ucircumflex -55
+KPX Tcaron udieresis -55
+KPX Tcaron ugrave -55
+KPX Tcaron uhungarumlaut -55
+KPX Tcaron umacron -55
+KPX Tcaron uogonek -55
+KPX Tcaron uring -55
+KPX Tcaron w -74
+KPX Tcaron y -74
+KPX Tcaron yacute -74
+KPX Tcaron ydieresis -34
+KPX Tcommaaccent A -50
+KPX Tcommaaccent Aacute -50
+KPX Tcommaaccent Abreve -50
+KPX Tcommaaccent Acircumflex -50
+KPX Tcommaaccent Adieresis -50
+KPX Tcommaaccent Agrave -50
+KPX Tcommaaccent Amacron -50
+KPX Tcommaaccent Aogonek -50
+KPX Tcommaaccent Aring -50
+KPX Tcommaaccent Atilde -50
+KPX Tcommaaccent O -18
+KPX Tcommaaccent Oacute -18
+KPX Tcommaaccent Ocircumflex -18
+KPX Tcommaaccent Odieresis -18
+KPX Tcommaaccent Ograve -18
+KPX Tcommaaccent Ohungarumlaut -18
+KPX Tcommaaccent Omacron -18
+KPX Tcommaaccent Oslash -18
+KPX Tcommaaccent Otilde -18
+KPX Tcommaaccent a -92
+KPX Tcommaaccent aacute -92
+KPX Tcommaaccent abreve -92
+KPX Tcommaaccent acircumflex -92
+KPX Tcommaaccent adieresis -92
+KPX Tcommaaccent agrave -92
+KPX Tcommaaccent amacron -92
+KPX Tcommaaccent aogonek -92
+KPX Tcommaaccent aring -92
+KPX Tcommaaccent atilde -92
+KPX Tcommaaccent colon -55
+KPX Tcommaaccent comma -74
+KPX Tcommaaccent e -92
+KPX Tcommaaccent eacute -92
+KPX Tcommaaccent ecaron -92
+KPX Tcommaaccent ecircumflex -52
+KPX Tcommaaccent edieresis -52
+KPX Tcommaaccent edotaccent -92
+KPX Tcommaaccent egrave -52
+KPX Tcommaaccent emacron -52
+KPX Tcommaaccent eogonek -92
+KPX Tcommaaccent hyphen -74
+KPX Tcommaaccent i -55
+KPX Tcommaaccent iacute -55
+KPX Tcommaaccent iogonek -55
+KPX Tcommaaccent o -92
+KPX Tcommaaccent oacute -92
+KPX Tcommaaccent ocircumflex -92
+KPX Tcommaaccent odieresis -92
+KPX Tcommaaccent ograve -92
+KPX Tcommaaccent ohungarumlaut -92
+KPX Tcommaaccent omacron -92
+KPX Tcommaaccent oslash -92
+KPX Tcommaaccent otilde -92
+KPX Tcommaaccent period -74
+KPX Tcommaaccent r -55
+KPX Tcommaaccent racute -55
+KPX Tcommaaccent rcaron -55
+KPX Tcommaaccent rcommaaccent -55
+KPX Tcommaaccent semicolon -65
+KPX Tcommaaccent u -55
+KPX Tcommaaccent uacute -55
+KPX Tcommaaccent ucircumflex -55
+KPX Tcommaaccent udieresis -55
+KPX Tcommaaccent ugrave -55
+KPX Tcommaaccent uhungarumlaut -55
+KPX Tcommaaccent umacron -55
+KPX Tcommaaccent uogonek -55
+KPX Tcommaaccent uring -55
+KPX Tcommaaccent w -74
+KPX Tcommaaccent y -74
+KPX Tcommaaccent yacute -74
+KPX Tcommaaccent ydieresis -34
+KPX U A -40
+KPX U Aacute -40
+KPX U Abreve -40
+KPX U Acircumflex -40
+KPX U Adieresis -40
+KPX U Agrave -40
+KPX U Amacron -40
+KPX U Aogonek -40
+KPX U Aring -40
+KPX U Atilde -40
+KPX U comma -25
+KPX U period -25
+KPX Uacute A -40
+KPX Uacute Aacute -40
+KPX Uacute Abreve -40
+KPX Uacute Acircumflex -40
+KPX Uacute Adieresis -40
+KPX Uacute Agrave -40
+KPX Uacute Amacron -40
+KPX Uacute Aogonek -40
+KPX Uacute Aring -40
+KPX Uacute Atilde -40
+KPX Uacute comma -25
+KPX Uacute period -25
+KPX Ucircumflex A -40
+KPX Ucircumflex Aacute -40
+KPX Ucircumflex Abreve -40
+KPX Ucircumflex Acircumflex -40
+KPX Ucircumflex Adieresis -40
+KPX Ucircumflex Agrave -40
+KPX Ucircumflex Amacron -40
+KPX Ucircumflex Aogonek -40
+KPX Ucircumflex Aring -40
+KPX Ucircumflex Atilde -40
+KPX Ucircumflex comma -25
+KPX Ucircumflex period -25
+KPX Udieresis A -40
+KPX Udieresis Aacute -40
+KPX Udieresis Abreve -40
+KPX Udieresis Acircumflex -40
+KPX Udieresis Adieresis -40
+KPX Udieresis Agrave -40
+KPX Udieresis Amacron -40
+KPX Udieresis Aogonek -40
+KPX Udieresis Aring -40
+KPX Udieresis Atilde -40
+KPX Udieresis comma -25
+KPX Udieresis period -25
+KPX Ugrave A -40
+KPX Ugrave Aacute -40
+KPX Ugrave Abreve -40
+KPX Ugrave Acircumflex -40
+KPX Ugrave Adieresis -40
+KPX Ugrave Agrave -40
+KPX Ugrave Amacron -40
+KPX Ugrave Aogonek -40
+KPX Ugrave Aring -40
+KPX Ugrave Atilde -40
+KPX Ugrave comma -25
+KPX Ugrave period -25
+KPX Uhungarumlaut A -40
+KPX Uhungarumlaut Aacute -40
+KPX Uhungarumlaut Abreve -40
+KPX Uhungarumlaut Acircumflex -40
+KPX Uhungarumlaut Adieresis -40
+KPX Uhungarumlaut Agrave -40
+KPX Uhungarumlaut Amacron -40
+KPX Uhungarumlaut Aogonek -40
+KPX Uhungarumlaut Aring -40
+KPX Uhungarumlaut Atilde -40
+KPX Uhungarumlaut comma -25
+KPX Uhungarumlaut period -25
+KPX Umacron A -40
+KPX Umacron Aacute -40
+KPX Umacron Abreve -40
+KPX Umacron Acircumflex -40
+KPX Umacron Adieresis -40
+KPX Umacron Agrave -40
+KPX Umacron Amacron -40
+KPX Umacron Aogonek -40
+KPX Umacron Aring -40
+KPX Umacron Atilde -40
+KPX Umacron comma -25
+KPX Umacron period -25
+KPX Uogonek A -40
+KPX Uogonek Aacute -40
+KPX Uogonek Abreve -40
+KPX Uogonek Acircumflex -40
+KPX Uogonek Adieresis -40
+KPX Uogonek Agrave -40
+KPX Uogonek Amacron -40
+KPX Uogonek Aogonek -40
+KPX Uogonek Aring -40
+KPX Uogonek Atilde -40
+KPX Uogonek comma -25
+KPX Uogonek period -25
+KPX Uring A -40
+KPX Uring Aacute -40
+KPX Uring Abreve -40
+KPX Uring Acircumflex -40
+KPX Uring Adieresis -40
+KPX Uring Agrave -40
+KPX Uring Amacron -40
+KPX Uring Aogonek -40
+KPX Uring Aring -40
+KPX Uring Atilde -40
+KPX Uring comma -25
+KPX Uring period -25
+KPX V A -60
+KPX V Aacute -60
+KPX V Abreve -60
+KPX V Acircumflex -60
+KPX V Adieresis -60
+KPX V Agrave -60
+KPX V Amacron -60
+KPX V Aogonek -60
+KPX V Aring -60
+KPX V Atilde -60
+KPX V O -30
+KPX V Oacute -30
+KPX V Ocircumflex -30
+KPX V Odieresis -30
+KPX V Ograve -30
+KPX V Ohungarumlaut -30
+KPX V Omacron -30
+KPX V Oslash -30
+KPX V Otilde -30
+KPX V a -111
+KPX V aacute -111
+KPX V abreve -111
+KPX V acircumflex -111
+KPX V adieresis -111
+KPX V agrave -111
+KPX V amacron -111
+KPX V aogonek -111
+KPX V aring -111
+KPX V atilde -111
+KPX V colon -65
+KPX V comma -129
+KPX V e -111
+KPX V eacute -111
+KPX V ecaron -111
+KPX V ecircumflex -111
+KPX V edieresis -71
+KPX V edotaccent -111
+KPX V egrave -71
+KPX V emacron -71
+KPX V eogonek -111
+KPX V hyphen -55
+KPX V i -74
+KPX V iacute -74
+KPX V icircumflex -34
+KPX V idieresis -34
+KPX V igrave -34
+KPX V imacron -34
+KPX V iogonek -74
+KPX V o -111
+KPX V oacute -111
+KPX V ocircumflex -111
+KPX V odieresis -111
+KPX V ograve -111
+KPX V ohungarumlaut -111
+KPX V omacron -111
+KPX V oslash -111
+KPX V otilde -111
+KPX V period -129
+KPX V semicolon -74
+KPX V u -74
+KPX V uacute -74
+KPX V ucircumflex -74
+KPX V udieresis -74
+KPX V ugrave -74
+KPX V uhungarumlaut -74
+KPX V umacron -74
+KPX V uogonek -74
+KPX V uring -74
+KPX W A -60
+KPX W Aacute -60
+KPX W Abreve -60
+KPX W Acircumflex -60
+KPX W Adieresis -60
+KPX W Agrave -60
+KPX W Amacron -60
+KPX W Aogonek -60
+KPX W Aring -60
+KPX W Atilde -60
+KPX W O -25
+KPX W Oacute -25
+KPX W Ocircumflex -25
+KPX W Odieresis -25
+KPX W Ograve -25
+KPX W Ohungarumlaut -25
+KPX W Omacron -25
+KPX W Oslash -25
+KPX W Otilde -25
+KPX W a -92
+KPX W aacute -92
+KPX W abreve -92
+KPX W acircumflex -92
+KPX W adieresis -92
+KPX W agrave -92
+KPX W amacron -92
+KPX W aogonek -92
+KPX W aring -92
+KPX W atilde -92
+KPX W colon -65
+KPX W comma -92
+KPX W e -92
+KPX W eacute -92
+KPX W ecaron -92
+KPX W ecircumflex -92
+KPX W edieresis -52
+KPX W edotaccent -92
+KPX W egrave -52
+KPX W emacron -52
+KPX W eogonek -92
+KPX W hyphen -37
+KPX W i -55
+KPX W iacute -55
+KPX W iogonek -55
+KPX W o -92
+KPX W oacute -92
+KPX W ocircumflex -92
+KPX W odieresis -92
+KPX W ograve -92
+KPX W ohungarumlaut -92
+KPX W omacron -92
+KPX W oslash -92
+KPX W otilde -92
+KPX W period -92
+KPX W semicolon -65
+KPX W u -55
+KPX W uacute -55
+KPX W ucircumflex -55
+KPX W udieresis -55
+KPX W ugrave -55
+KPX W uhungarumlaut -55
+KPX W umacron -55
+KPX W uogonek -55
+KPX W uring -55
+KPX W y -70
+KPX W yacute -70
+KPX W ydieresis -70
+KPX Y A -50
+KPX Y Aacute -50
+KPX Y Abreve -50
+KPX Y Acircumflex -50
+KPX Y Adieresis -50
+KPX Y Agrave -50
+KPX Y Amacron -50
+KPX Y Aogonek -50
+KPX Y Aring -50
+KPX Y Atilde -50
+KPX Y O -15
+KPX Y Oacute -15
+KPX Y Ocircumflex -15
+KPX Y Odieresis -15
+KPX Y Ograve -15
+KPX Y Ohungarumlaut -15
+KPX Y Omacron -15
+KPX Y Oslash -15
+KPX Y Otilde -15
+KPX Y a -92
+KPX Y aacute -92
+KPX Y abreve -92
+KPX Y acircumflex -92
+KPX Y adieresis -92
+KPX Y agrave -92
+KPX Y amacron -92
+KPX Y aogonek -92
+KPX Y aring -92
+KPX Y atilde -92
+KPX Y colon -65
+KPX Y comma -92
+KPX Y e -92
+KPX Y eacute -92
+KPX Y ecaron -92
+KPX Y ecircumflex -92
+KPX Y edieresis -52
+KPX Y edotaccent -92
+KPX Y egrave -52
+KPX Y emacron -52
+KPX Y eogonek -92
+KPX Y hyphen -74
+KPX Y i -74
+KPX Y iacute -74
+KPX Y icircumflex -34
+KPX Y idieresis -34
+KPX Y igrave -34
+KPX Y imacron -34
+KPX Y iogonek -74
+KPX Y o -92
+KPX Y oacute -92
+KPX Y ocircumflex -92
+KPX Y odieresis -92
+KPX Y ograve -92
+KPX Y ohungarumlaut -92
+KPX Y omacron -92
+KPX Y oslash -92
+KPX Y otilde -92
+KPX Y period -92
+KPX Y semicolon -65
+KPX Y u -92
+KPX Y uacute -92
+KPX Y ucircumflex -92
+KPX Y udieresis -92
+KPX Y ugrave -92
+KPX Y uhungarumlaut -92
+KPX Y umacron -92
+KPX Y uogonek -92
+KPX Y uring -92
+KPX Yacute A -50
+KPX Yacute Aacute -50
+KPX Yacute Abreve -50
+KPX Yacute Acircumflex -50
+KPX Yacute Adieresis -50
+KPX Yacute Agrave -50
+KPX Yacute Amacron -50
+KPX Yacute Aogonek -50
+KPX Yacute Aring -50
+KPX Yacute Atilde -50
+KPX Yacute O -15
+KPX Yacute Oacute -15
+KPX Yacute Ocircumflex -15
+KPX Yacute Odieresis -15
+KPX Yacute Ograve -15
+KPX Yacute Ohungarumlaut -15
+KPX Yacute Omacron -15
+KPX Yacute Oslash -15
+KPX Yacute Otilde -15
+KPX Yacute a -92
+KPX Yacute aacute -92
+KPX Yacute abreve -92
+KPX Yacute acircumflex -92
+KPX Yacute adieresis -92
+KPX Yacute agrave -92
+KPX Yacute amacron -92
+KPX Yacute aogonek -92
+KPX Yacute aring -92
+KPX Yacute atilde -92
+KPX Yacute colon -65
+KPX Yacute comma -92
+KPX Yacute e -92
+KPX Yacute eacute -92
+KPX Yacute ecaron -92
+KPX Yacute ecircumflex -92
+KPX Yacute edieresis -52
+KPX Yacute edotaccent -92
+KPX Yacute egrave -52
+KPX Yacute emacron -52
+KPX Yacute eogonek -92
+KPX Yacute hyphen -74
+KPX Yacute i -74
+KPX Yacute iacute -74
+KPX Yacute icircumflex -34
+KPX Yacute idieresis -34
+KPX Yacute igrave -34
+KPX Yacute imacron -34
+KPX Yacute iogonek -74
+KPX Yacute o -92
+KPX Yacute oacute -92
+KPX Yacute ocircumflex -92
+KPX Yacute odieresis -92
+KPX Yacute ograve -92
+KPX Yacute ohungarumlaut -92
+KPX Yacute omacron -92
+KPX Yacute oslash -92
+KPX Yacute otilde -92
+KPX Yacute period -92
+KPX Yacute semicolon -65
+KPX Yacute u -92
+KPX Yacute uacute -92
+KPX Yacute ucircumflex -92
+KPX Yacute udieresis -92
+KPX Yacute ugrave -92
+KPX Yacute uhungarumlaut -92
+KPX Yacute umacron -92
+KPX Yacute uogonek -92
+KPX Yacute uring -92
+KPX Ydieresis A -50
+KPX Ydieresis Aacute -50
+KPX Ydieresis Abreve -50
+KPX Ydieresis Acircumflex -50
+KPX Ydieresis Adieresis -50
+KPX Ydieresis Agrave -50
+KPX Ydieresis Amacron -50
+KPX Ydieresis Aogonek -50
+KPX Ydieresis Aring -50
+KPX Ydieresis Atilde -50
+KPX Ydieresis O -15
+KPX Ydieresis Oacute -15
+KPX Ydieresis Ocircumflex -15
+KPX Ydieresis Odieresis -15
+KPX Ydieresis Ograve -15
+KPX Ydieresis Ohungarumlaut -15
+KPX Ydieresis Omacron -15
+KPX Ydieresis Oslash -15
+KPX Ydieresis Otilde -15
+KPX Ydieresis a -92
+KPX Ydieresis aacute -92
+KPX Ydieresis abreve -92
+KPX Ydieresis acircumflex -92
+KPX Ydieresis adieresis -92
+KPX Ydieresis agrave -92
+KPX Ydieresis amacron -92
+KPX Ydieresis aogonek -92
+KPX Ydieresis aring -92
+KPX Ydieresis atilde -92
+KPX Ydieresis colon -65
+KPX Ydieresis comma -92
+KPX Ydieresis e -92
+KPX Ydieresis eacute -92
+KPX Ydieresis ecaron -92
+KPX Ydieresis ecircumflex -92
+KPX Ydieresis edieresis -52
+KPX Ydieresis edotaccent -92
+KPX Ydieresis egrave -52
+KPX Ydieresis emacron -52
+KPX Ydieresis eogonek -92
+KPX Ydieresis hyphen -74
+KPX Ydieresis i -74
+KPX Ydieresis iacute -74
+KPX Ydieresis icircumflex -34
+KPX Ydieresis idieresis -34
+KPX Ydieresis igrave -34
+KPX Ydieresis imacron -34
+KPX Ydieresis iogonek -74
+KPX Ydieresis o -92
+KPX Ydieresis oacute -92
+KPX Ydieresis ocircumflex -92
+KPX Ydieresis odieresis -92
+KPX Ydieresis ograve -92
+KPX Ydieresis ohungarumlaut -92
+KPX Ydieresis omacron -92
+KPX Ydieresis oslash -92
+KPX Ydieresis otilde -92
+KPX Ydieresis period -92
+KPX Ydieresis semicolon -65
+KPX Ydieresis u -92
+KPX Ydieresis uacute -92
+KPX Ydieresis ucircumflex -92
+KPX Ydieresis udieresis -92
+KPX Ydieresis ugrave -92
+KPX Ydieresis uhungarumlaut -92
+KPX Ydieresis umacron -92
+KPX Ydieresis uogonek -92
+KPX Ydieresis uring -92
+KPX a g -10
+KPX a gbreve -10
+KPX a gcommaaccent -10
+KPX aacute g -10
+KPX aacute gbreve -10
+KPX aacute gcommaaccent -10
+KPX abreve g -10
+KPX abreve gbreve -10
+KPX abreve gcommaaccent -10
+KPX acircumflex g -10
+KPX acircumflex gbreve -10
+KPX acircumflex gcommaaccent -10
+KPX adieresis g -10
+KPX adieresis gbreve -10
+KPX adieresis gcommaaccent -10
+KPX agrave g -10
+KPX agrave gbreve -10
+KPX agrave gcommaaccent -10
+KPX amacron g -10
+KPX amacron gbreve -10
+KPX amacron gcommaaccent -10
+KPX aogonek g -10
+KPX aogonek gbreve -10
+KPX aogonek gcommaaccent -10
+KPX aring g -10
+KPX aring gbreve -10
+KPX aring gcommaaccent -10
+KPX atilde g -10
+KPX atilde gbreve -10
+KPX atilde gcommaaccent -10
+KPX b period -40
+KPX b u -20
+KPX b uacute -20
+KPX b ucircumflex -20
+KPX b udieresis -20
+KPX b ugrave -20
+KPX b uhungarumlaut -20
+KPX b umacron -20
+KPX b uogonek -20
+KPX b uring -20
+KPX c h -15
+KPX c k -20
+KPX c kcommaaccent -20
+KPX cacute h -15
+KPX cacute k -20
+KPX cacute kcommaaccent -20
+KPX ccaron h -15
+KPX ccaron k -20
+KPX ccaron kcommaaccent -20
+KPX ccedilla h -15
+KPX ccedilla k -20
+KPX ccedilla kcommaaccent -20
+KPX comma quotedblright -140
+KPX comma quoteright -140
+KPX e comma -10
+KPX e g -40
+KPX e gbreve -40
+KPX e gcommaaccent -40
+KPX e period -15
+KPX e v -15
+KPX e w -15
+KPX e x -20
+KPX e y -30
+KPX e yacute -30
+KPX e ydieresis -30
+KPX eacute comma -10
+KPX eacute g -40
+KPX eacute gbreve -40
+KPX eacute gcommaaccent -40
+KPX eacute period -15
+KPX eacute v -15
+KPX eacute w -15
+KPX eacute x -20
+KPX eacute y -30
+KPX eacute yacute -30
+KPX eacute ydieresis -30
+KPX ecaron comma -10
+KPX ecaron g -40
+KPX ecaron gbreve -40
+KPX ecaron gcommaaccent -40
+KPX ecaron period -15
+KPX ecaron v -15
+KPX ecaron w -15
+KPX ecaron x -20
+KPX ecaron y -30
+KPX ecaron yacute -30
+KPX ecaron ydieresis -30
+KPX ecircumflex comma -10
+KPX ecircumflex g -40
+KPX ecircumflex gbreve -40
+KPX ecircumflex gcommaaccent -40
+KPX ecircumflex period -15
+KPX ecircumflex v -15
+KPX ecircumflex w -15
+KPX ecircumflex x -20
+KPX ecircumflex y -30
+KPX ecircumflex yacute -30
+KPX ecircumflex ydieresis -30
+KPX edieresis comma -10
+KPX edieresis g -40
+KPX edieresis gbreve -40
+KPX edieresis gcommaaccent -40
+KPX edieresis period -15
+KPX edieresis v -15
+KPX edieresis w -15
+KPX edieresis x -20
+KPX edieresis y -30
+KPX edieresis yacute -30
+KPX edieresis ydieresis -30
+KPX edotaccent comma -10
+KPX edotaccent g -40
+KPX edotaccent gbreve -40
+KPX edotaccent gcommaaccent -40
+KPX edotaccent period -15
+KPX edotaccent v -15
+KPX edotaccent w -15
+KPX edotaccent x -20
+KPX edotaccent y -30
+KPX edotaccent yacute -30
+KPX edotaccent ydieresis -30
+KPX egrave comma -10
+KPX egrave g -40
+KPX egrave gbreve -40
+KPX egrave gcommaaccent -40
+KPX egrave period -15
+KPX egrave v -15
+KPX egrave w -15
+KPX egrave x -20
+KPX egrave y -30
+KPX egrave yacute -30
+KPX egrave ydieresis -30
+KPX emacron comma -10
+KPX emacron g -40
+KPX emacron gbreve -40
+KPX emacron gcommaaccent -40
+KPX emacron period -15
+KPX emacron v -15
+KPX emacron w -15
+KPX emacron x -20
+KPX emacron y -30
+KPX emacron yacute -30
+KPX emacron ydieresis -30
+KPX eogonek comma -10
+KPX eogonek g -40
+KPX eogonek gbreve -40
+KPX eogonek gcommaaccent -40
+KPX eogonek period -15
+KPX eogonek v -15
+KPX eogonek w -15
+KPX eogonek x -20
+KPX eogonek y -30
+KPX eogonek yacute -30
+KPX eogonek ydieresis -30
+KPX f comma -10
+KPX f dotlessi -60
+KPX f f -18
+KPX f i -20
+KPX f iogonek -20
+KPX f period -15
+KPX f quoteright 92
+KPX g comma -10
+KPX g e -10
+KPX g eacute -10
+KPX g ecaron -10
+KPX g ecircumflex -10
+KPX g edieresis -10
+KPX g edotaccent -10
+KPX g egrave -10
+KPX g emacron -10
+KPX g eogonek -10
+KPX g g -10
+KPX g gbreve -10
+KPX g gcommaaccent -10
+KPX g period -15
+KPX gbreve comma -10
+KPX gbreve e -10
+KPX gbreve eacute -10
+KPX gbreve ecaron -10
+KPX gbreve ecircumflex -10
+KPX gbreve edieresis -10
+KPX gbreve edotaccent -10
+KPX gbreve egrave -10
+KPX gbreve emacron -10
+KPX gbreve eogonek -10
+KPX gbreve g -10
+KPX gbreve gbreve -10
+KPX gbreve gcommaaccent -10
+KPX gbreve period -15
+KPX gcommaaccent comma -10
+KPX gcommaaccent e -10
+KPX gcommaaccent eacute -10
+KPX gcommaaccent ecaron -10
+KPX gcommaaccent ecircumflex -10
+KPX gcommaaccent edieresis -10
+KPX gcommaaccent edotaccent -10
+KPX gcommaaccent egrave -10
+KPX gcommaaccent emacron -10
+KPX gcommaaccent eogonek -10
+KPX gcommaaccent g -10
+KPX gcommaaccent gbreve -10
+KPX gcommaaccent gcommaaccent -10
+KPX gcommaaccent period -15
+KPX k e -10
+KPX k eacute -10
+KPX k ecaron -10
+KPX k ecircumflex -10
+KPX k edieresis -10
+KPX k edotaccent -10
+KPX k egrave -10
+KPX k emacron -10
+KPX k eogonek -10
+KPX k o -10
+KPX k oacute -10
+KPX k ocircumflex -10
+KPX k odieresis -10
+KPX k ograve -10
+KPX k ohungarumlaut -10
+KPX k omacron -10
+KPX k oslash -10
+KPX k otilde -10
+KPX k y -10
+KPX k yacute -10
+KPX k ydieresis -10
+KPX kcommaaccent e -10
+KPX kcommaaccent eacute -10
+KPX kcommaaccent ecaron -10
+KPX kcommaaccent ecircumflex -10
+KPX kcommaaccent edieresis -10
+KPX kcommaaccent edotaccent -10
+KPX kcommaaccent egrave -10
+KPX kcommaaccent emacron -10
+KPX kcommaaccent eogonek -10
+KPX kcommaaccent o -10
+KPX kcommaaccent oacute -10
+KPX kcommaaccent ocircumflex -10
+KPX kcommaaccent odieresis -10
+KPX kcommaaccent ograve -10
+KPX kcommaaccent ohungarumlaut -10
+KPX kcommaaccent omacron -10
+KPX kcommaaccent oslash -10
+KPX kcommaaccent otilde -10
+KPX kcommaaccent y -10
+KPX kcommaaccent yacute -10
+KPX kcommaaccent ydieresis -10
+KPX n v -40
+KPX nacute v -40
+KPX ncaron v -40
+KPX ncommaaccent v -40
+KPX ntilde v -40
+KPX o g -10
+KPX o gbreve -10
+KPX o gcommaaccent -10
+KPX o v -10
+KPX oacute g -10
+KPX oacute gbreve -10
+KPX oacute gcommaaccent -10
+KPX oacute v -10
+KPX ocircumflex g -10
+KPX ocircumflex gbreve -10
+KPX ocircumflex gcommaaccent -10
+KPX ocircumflex v -10
+KPX odieresis g -10
+KPX odieresis gbreve -10
+KPX odieresis gcommaaccent -10
+KPX odieresis v -10
+KPX ograve g -10
+KPX ograve gbreve -10
+KPX ograve gcommaaccent -10
+KPX ograve v -10
+KPX ohungarumlaut g -10
+KPX ohungarumlaut gbreve -10
+KPX ohungarumlaut gcommaaccent -10
+KPX ohungarumlaut v -10
+KPX omacron g -10
+KPX omacron gbreve -10
+KPX omacron gcommaaccent -10
+KPX omacron v -10
+KPX oslash g -10
+KPX oslash gbreve -10
+KPX oslash gcommaaccent -10
+KPX oslash v -10
+KPX otilde g -10
+KPX otilde gbreve -10
+KPX otilde gcommaaccent -10
+KPX otilde v -10
+KPX period quotedblright -140
+KPX period quoteright -140
+KPX quoteleft quoteleft -111
+KPX quoteright d -25
+KPX quoteright dcroat -25
+KPX quoteright quoteright -111
+KPX quoteright r -25
+KPX quoteright racute -25
+KPX quoteright rcaron -25
+KPX quoteright rcommaaccent -25
+KPX quoteright s -40
+KPX quoteright sacute -40
+KPX quoteright scaron -40
+KPX quoteright scedilla -40
+KPX quoteright scommaaccent -40
+KPX quoteright space -111
+KPX quoteright t -30
+KPX quoteright tcommaaccent -30
+KPX quoteright v -10
+KPX r a -15
+KPX r aacute -15
+KPX r abreve -15
+KPX r acircumflex -15
+KPX r adieresis -15
+KPX r agrave -15
+KPX r amacron -15
+KPX r aogonek -15
+KPX r aring -15
+KPX r atilde -15
+KPX r c -37
+KPX r cacute -37
+KPX r ccaron -37
+KPX r ccedilla -37
+KPX r comma -111
+KPX r d -37
+KPX r dcroat -37
+KPX r e -37
+KPX r eacute -37
+KPX r ecaron -37
+KPX r ecircumflex -37
+KPX r edieresis -37
+KPX r edotaccent -37
+KPX r egrave -37
+KPX r emacron -37
+KPX r eogonek -37
+KPX r g -37
+KPX r gbreve -37
+KPX r gcommaaccent -37
+KPX r hyphen -20
+KPX r o -45
+KPX r oacute -45
+KPX r ocircumflex -45
+KPX r odieresis -45
+KPX r ograve -45
+KPX r ohungarumlaut -45
+KPX r omacron -45
+KPX r oslash -45
+KPX r otilde -45
+KPX r period -111
+KPX r q -37
+KPX r s -10
+KPX r sacute -10
+KPX r scaron -10
+KPX r scedilla -10
+KPX r scommaaccent -10
+KPX racute a -15
+KPX racute aacute -15
+KPX racute abreve -15
+KPX racute acircumflex -15
+KPX racute adieresis -15
+KPX racute agrave -15
+KPX racute amacron -15
+KPX racute aogonek -15
+KPX racute aring -15
+KPX racute atilde -15
+KPX racute c -37
+KPX racute cacute -37
+KPX racute ccaron -37
+KPX racute ccedilla -37
+KPX racute comma -111
+KPX racute d -37
+KPX racute dcroat -37
+KPX racute e -37
+KPX racute eacute -37
+KPX racute ecaron -37
+KPX racute ecircumflex -37
+KPX racute edieresis -37
+KPX racute edotaccent -37
+KPX racute egrave -37
+KPX racute emacron -37
+KPX racute eogonek -37
+KPX racute g -37
+KPX racute gbreve -37
+KPX racute gcommaaccent -37
+KPX racute hyphen -20
+KPX racute o -45
+KPX racute oacute -45
+KPX racute ocircumflex -45
+KPX racute odieresis -45
+KPX racute ograve -45
+KPX racute ohungarumlaut -45
+KPX racute omacron -45
+KPX racute oslash -45
+KPX racute otilde -45
+KPX racute period -111
+KPX racute q -37
+KPX racute s -10
+KPX racute sacute -10
+KPX racute scaron -10
+KPX racute scedilla -10
+KPX racute scommaaccent -10
+KPX rcaron a -15
+KPX rcaron aacute -15
+KPX rcaron abreve -15
+KPX rcaron acircumflex -15
+KPX rcaron adieresis -15
+KPX rcaron agrave -15
+KPX rcaron amacron -15
+KPX rcaron aogonek -15
+KPX rcaron aring -15
+KPX rcaron atilde -15
+KPX rcaron c -37
+KPX rcaron cacute -37
+KPX rcaron ccaron -37
+KPX rcaron ccedilla -37
+KPX rcaron comma -111
+KPX rcaron d -37
+KPX rcaron dcroat -37
+KPX rcaron e -37
+KPX rcaron eacute -37
+KPX rcaron ecaron -37
+KPX rcaron ecircumflex -37
+KPX rcaron edieresis -37
+KPX rcaron edotaccent -37
+KPX rcaron egrave -37
+KPX rcaron emacron -37
+KPX rcaron eogonek -37
+KPX rcaron g -37
+KPX rcaron gbreve -37
+KPX rcaron gcommaaccent -37
+KPX rcaron hyphen -20
+KPX rcaron o -45
+KPX rcaron oacute -45
+KPX rcaron ocircumflex -45
+KPX rcaron odieresis -45
+KPX rcaron ograve -45
+KPX rcaron ohungarumlaut -45
+KPX rcaron omacron -45
+KPX rcaron oslash -45
+KPX rcaron otilde -45
+KPX rcaron period -111
+KPX rcaron q -37
+KPX rcaron s -10
+KPX rcaron sacute -10
+KPX rcaron scaron -10
+KPX rcaron scedilla -10
+KPX rcaron scommaaccent -10
+KPX rcommaaccent a -15
+KPX rcommaaccent aacute -15
+KPX rcommaaccent abreve -15
+KPX rcommaaccent acircumflex -15
+KPX rcommaaccent adieresis -15
+KPX rcommaaccent agrave -15
+KPX rcommaaccent amacron -15
+KPX rcommaaccent aogonek -15
+KPX rcommaaccent aring -15
+KPX rcommaaccent atilde -15
+KPX rcommaaccent c -37
+KPX rcommaaccent cacute -37
+KPX rcommaaccent ccaron -37
+KPX rcommaaccent ccedilla -37
+KPX rcommaaccent comma -111
+KPX rcommaaccent d -37
+KPX rcommaaccent dcroat -37
+KPX rcommaaccent e -37
+KPX rcommaaccent eacute -37
+KPX rcommaaccent ecaron -37
+KPX rcommaaccent ecircumflex -37
+KPX rcommaaccent edieresis -37
+KPX rcommaaccent edotaccent -37
+KPX rcommaaccent egrave -37
+KPX rcommaaccent emacron -37
+KPX rcommaaccent eogonek -37
+KPX rcommaaccent g -37
+KPX rcommaaccent gbreve -37
+KPX rcommaaccent gcommaaccent -37
+KPX rcommaaccent hyphen -20
+KPX rcommaaccent o -45
+KPX rcommaaccent oacute -45
+KPX rcommaaccent ocircumflex -45
+KPX rcommaaccent odieresis -45
+KPX rcommaaccent ograve -45
+KPX rcommaaccent ohungarumlaut -45
+KPX rcommaaccent omacron -45
+KPX rcommaaccent oslash -45
+KPX rcommaaccent otilde -45
+KPX rcommaaccent period -111
+KPX rcommaaccent q -37
+KPX rcommaaccent s -10
+KPX rcommaaccent sacute -10
+KPX rcommaaccent scaron -10
+KPX rcommaaccent scedilla -10
+KPX rcommaaccent scommaaccent -10
+KPX space A -18
+KPX space Aacute -18
+KPX space Abreve -18
+KPX space Acircumflex -18
+KPX space Adieresis -18
+KPX space Agrave -18
+KPX space Amacron -18
+KPX space Aogonek -18
+KPX space Aring -18
+KPX space Atilde -18
+KPX space T -18
+KPX space Tcaron -18
+KPX space Tcommaaccent -18
+KPX space V -35
+KPX space W -40
+KPX space Y -75
+KPX space Yacute -75
+KPX space Ydieresis -75
+KPX v comma -74
+KPX v period -74
+KPX w comma -74
+KPX w period -74
+KPX y comma -55
+KPX y period -55
+KPX yacute comma -55
+KPX yacute period -55
+KPX ydieresis comma -55
+KPX ydieresis period -55
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/program/libraries/afm/Times-Roman.afm b/program/libraries/afm/Times-Roman.afm
new file mode 100644 (file)
index 0000000..a0953f2
--- /dev/null
@@ -0,0 +1,2419 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated.  All Rights Reserved.
+Comment Creation Date: Thu May  1 12:49:17 1997
+Comment UniqueID 43068
+Comment VMusage 43909 54934
+FontName Times-Roman
+FullName Times Roman
+FamilyName Times
+Weight Roman
+ItalicAngle 0
+IsFixedPitch false
+CharacterSet ExtendedRoman
+FontBBox -168 -218 1000 898 
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated.  All Rights Reserved.Times is a trademark of Linotype-Hell AG and/or its subsidiaries.
+EncodingScheme AdobeStandardEncoding
+CapHeight 662
+XHeight 450
+Ascender 683
+Descender -217
+StdHW 28
+StdVW 84
+StartCharMetrics 315
+C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 333 ; N exclam ; B 130 -9 238 676 ;
+C 34 ; WX 408 ; N quotedbl ; B 77 431 331 676 ;
+C 35 ; WX 500 ; N numbersign ; B 5 0 496 662 ;
+C 36 ; WX 500 ; N dollar ; B 44 -87 457 727 ;
+C 37 ; WX 833 ; N percent ; B 61 -13 772 676 ;
+C 38 ; WX 778 ; N ampersand ; B 42 -13 750 676 ;
+C 39 ; WX 333 ; N quoteright ; B 79 433 218 676 ;
+C 40 ; WX 333 ; N parenleft ; B 48 -177 304 676 ;
+C 41 ; WX 333 ; N parenright ; B 29 -177 285 676 ;
+C 42 ; WX 500 ; N asterisk ; B 69 265 432 676 ;
+C 43 ; WX 564 ; N plus ; B 30 0 534 506 ;
+C 44 ; WX 250 ; N comma ; B 56 -141 195 102 ;
+C 45 ; WX 333 ; N hyphen ; B 39 194 285 257 ;
+C 46 ; WX 250 ; N period ; B 70 -11 181 100 ;
+C 47 ; WX 278 ; N slash ; B -9 -14 287 676 ;
+C 48 ; WX 500 ; N zero ; B 24 -14 476 676 ;
+C 49 ; WX 500 ; N one ; B 111 0 394 676 ;
+C 50 ; WX 500 ; N two ; B 30 0 475 676 ;
+C 51 ; WX 500 ; N three ; B 43 -14 431 676 ;
+C 52 ; WX 500 ; N four ; B 12 0 472 676 ;
+C 53 ; WX 500 ; N five ; B 32 -14 438 688 ;
+C 54 ; WX 500 ; N six ; B 34 -14 468 684 ;
+C 55 ; WX 500 ; N seven ; B 20 -8 449 662 ;
+C 56 ; WX 500 ; N eight ; B 56 -14 445 676 ;
+C 57 ; WX 500 ; N nine ; B 30 -22 459 676 ;
+C 58 ; WX 278 ; N colon ; B 81 -11 192 459 ;
+C 59 ; WX 278 ; N semicolon ; B 80 -141 219 459 ;
+C 60 ; WX 564 ; N less ; B 28 -8 536 514 ;
+C 61 ; WX 564 ; N equal ; B 30 120 534 386 ;
+C 62 ; WX 564 ; N greater ; B 28 -8 536 514 ;
+C 63 ; WX 444 ; N question ; B 68 -8 414 676 ;
+C 64 ; WX 921 ; N at ; B 116 -14 809 676 ;
+C 65 ; WX 722 ; N A ; B 15 0 706 674 ;
+C 66 ; WX 667 ; N B ; B 17 0 593 662 ;
+C 67 ; WX 667 ; N C ; B 28 -14 633 676 ;
+C 68 ; WX 722 ; N D ; B 16 0 685 662 ;
+C 69 ; WX 611 ; N E ; B 12 0 597 662 ;
+C 70 ; WX 556 ; N F ; B 12 0 546 662 ;
+C 71 ; WX 722 ; N G ; B 32 -14 709 676 ;
+C 72 ; WX 722 ; N H ; B 19 0 702 662 ;
+C 73 ; WX 333 ; N I ; B 18 0 315 662 ;
+C 74 ; WX 389 ; N J ; B 10 -14 370 662 ;
+C 75 ; WX 722 ; N K ; B 34 0 723 662 ;
+C 76 ; WX 611 ; N L ; B 12 0 598 662 ;
+C 77 ; WX 889 ; N M ; B 12 0 863 662 ;
+C 78 ; WX 722 ; N N ; B 12 -11 707 662 ;
+C 79 ; WX 722 ; N O ; B 34 -14 688 676 ;
+C 80 ; WX 556 ; N P ; B 16 0 542 662 ;
+C 81 ; WX 722 ; N Q ; B 34 -178 701 676 ;
+C 82 ; WX 667 ; N R ; B 17 0 659 662 ;
+C 83 ; WX 556 ; N S ; B 42 -14 491 676 ;
+C 84 ; WX 611 ; N T ; B 17 0 593 662 ;
+C 85 ; WX 722 ; N U ; B 14 -14 705 662 ;
+C 86 ; WX 722 ; N V ; B 16 -11 697 662 ;
+C 87 ; WX 944 ; N W ; B 5 -11 932 662 ;
+C 88 ; WX 722 ; N X ; B 10 0 704 662 ;
+C 89 ; WX 722 ; N Y ; B 22 0 703 662 ;
+C 90 ; WX 611 ; N Z ; B 9 0 597 662 ;
+C 91 ; WX 333 ; N bracketleft ; B 88 -156 299 662 ;
+C 92 ; WX 278 ; N backslash ; B -9 -14 287 676 ;
+C 93 ; WX 333 ; N bracketright ; B 34 -156 245 662 ;
+C 94 ; WX 469 ; N asciicircum ; B 24 297 446 662 ;
+C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ;
+C 96 ; WX 333 ; N quoteleft ; B 115 433 254 676 ;
+C 97 ; WX 444 ; N a ; B 37 -10 442 460 ;
+C 98 ; WX 500 ; N b ; B 3 -10 468 683 ;
+C 99 ; WX 444 ; N c ; B 25 -10 412 460 ;
+C 100 ; WX 500 ; N d ; B 27 -10 491 683 ;
+C 101 ; WX 444 ; N e ; B 25 -10 424 460 ;
+C 102 ; WX 333 ; N f ; B 20 0 383 683 ; L i fi ; L l fl ;
+C 103 ; WX 500 ; N g ; B 28 -218 470 460 ;
+C 104 ; WX 500 ; N h ; B 9 0 487 683 ;
+C 105 ; WX 278 ; N i ; B 16 0 253 683 ;
+C 106 ; WX 278 ; N j ; B -70 -218 194 683 ;
+C 107 ; WX 500 ; N k ; B 7 0 505 683 ;
+C 108 ; WX 278 ; N l ; B 19 0 257 683 ;
+C 109 ; WX 778 ; N m ; B 16 0 775 460 ;
+C 110 ; WX 500 ; N n ; B 16 0 485 460 ;
+C 111 ; WX 500 ; N o ; B 29 -10 470 460 ;
+C 112 ; WX 500 ; N p ; B 5 -217 470 460 ;
+C 113 ; WX 500 ; N q ; B 24 -217 488 460 ;
+C 114 ; WX 333 ; N r ; B 5 0 335 460 ;
+C 115 ; WX 389 ; N s ; B 51 -10 348 460 ;
+C 116 ; WX 278 ; N t ; B 13 -10 279 579 ;
+C 117 ; WX 500 ; N u ; B 9 -10 479 450 ;
+C 118 ; WX 500 ; N v ; B 19 -14 477 450 ;
+C 119 ; WX 722 ; N w ; B 21 -14 694 450 ;
+C 120 ; WX 500 ; N x ; B 17 0 479 450 ;
+C 121 ; WX 500 ; N y ; B 14 -218 475 450 ;
+C 122 ; WX 444 ; N z ; B 27 0 418 450 ;
+C 123 ; WX 480 ; N braceleft ; B 100 -181 350 680 ;
+C 124 ; WX 200 ; N bar ; B 67 -218 133 782 ;
+C 125 ; WX 480 ; N braceright ; B 130 -181 380 680 ;
+C 126 ; WX 541 ; N asciitilde ; B 40 183 502 323 ;
+C 161 ; WX 333 ; N exclamdown ; B 97 -218 205 467 ;
+C 162 ; WX 500 ; N cent ; B 53 -138 448 579 ;
+C 163 ; WX 500 ; N sterling ; B 12 -8 490 676 ;
+C 164 ; WX 167 ; N fraction ; B -168 -14 331 676 ;
+C 165 ; WX 500 ; N yen ; B -53 0 512 662 ;
+C 166 ; WX 500 ; N florin ; B 7 -189 490 676 ;
+C 167 ; WX 500 ; N section ; B 70 -148 426 676 ;
+C 168 ; WX 500 ; N currency ; B -22 58 522 602 ;
+C 169 ; WX 180 ; N quotesingle ; B 48 431 133 676 ;
+C 170 ; WX 444 ; N quotedblleft ; B 43 433 414 676 ;
+C 171 ; WX 500 ; N guillemotleft ; B 42 33 456 416 ;
+C 172 ; WX 333 ; N guilsinglleft ; B 63 33 285 416 ;
+C 173 ; WX 333 ; N guilsinglright ; B 48 33 270 416 ;
+C 174 ; WX 556 ; N fi ; B 31 0 521 683 ;
+C 175 ; WX 556 ; N fl ; B 32 0 521 683 ;
+C 177 ; WX 500 ; N endash ; B 0 201 500 250 ;
+C 178 ; WX 500 ; N dagger ; B 59 -149 442 676 ;
+C 179 ; WX 500 ; N daggerdbl ; B 58 -153 442 676 ;
+C 180 ; WX 250 ; N periodcentered ; B 70 199 181 310 ;
+C 182 ; WX 453 ; N paragraph ; B -22 -154 450 662 ;
+C 183 ; WX 350 ; N bullet ; B 40 196 310 466 ;
+C 184 ; WX 333 ; N quotesinglbase ; B 79 -141 218 102 ;
+C 185 ; WX 444 ; N quotedblbase ; B 45 -141 416 102 ;
+C 186 ; WX 444 ; N quotedblright ; B 30 433 401 676 ;
+C 187 ; WX 500 ; N guillemotright ; B 44 33 458 416 ;
+C 188 ; WX 1000 ; N ellipsis ; B 111 -11 888 100 ;
+C 189 ; WX 1000 ; N perthousand ; B 7 -19 994 706 ;
+C 191 ; WX 444 ; N questiondown ; B 30 -218 376 466 ;
+C 193 ; WX 333 ; N grave ; B 19 507 242 678 ;
+C 194 ; WX 333 ; N acute ; B 93 507 317 678 ;
+C 195 ; WX 333 ; N circumflex ; B 11 507 322 674 ;
+C 196 ; WX 333 ; N tilde ; B 1 532 331 638 ;
+C 197 ; WX 333 ; N macron ; B 11 547 322 601 ;
+C 198 ; WX 333 ; N breve ; B 26 507 307 664 ;
+C 199 ; WX 333 ; N dotaccent ; B 118 581 216 681 ;
+C 200 ; WX 333 ; N dieresis ; B 18 581 315 681 ;
+C 202 ; WX 333 ; N ring ; B 67 512 266 711 ;
+C 203 ; WX 333 ; N cedilla ; B 52 -215 261 0 ;
+C 205 ; WX 333 ; N hungarumlaut ; B -3 507 377 678 ;
+C 206 ; WX 333 ; N ogonek ; B 62 -165 243 0 ;
+C 207 ; WX 333 ; N caron ; B 11 507 322 674 ;
+C 208 ; WX 1000 ; N emdash ; B 0 201 1000 250 ;
+C 225 ; WX 889 ; N AE ; B 0 0 863 662 ;
+C 227 ; WX 276 ; N ordfeminine ; B 4 394 270 676 ;
+C 232 ; WX 611 ; N Lslash ; B 12 0 598 662 ;
+C 233 ; WX 722 ; N Oslash ; B 34 -80 688 734 ;
+C 234 ; WX 889 ; N OE ; B 30 -6 885 668 ;
+C 235 ; WX 310 ; N ordmasculine ; B 6 394 304 676 ;
+C 241 ; WX 667 ; N ae ; B 38 -10 632 460 ;
+C 245 ; WX 278 ; N dotlessi ; B 16 0 253 460 ;
+C 248 ; WX 278 ; N lslash ; B 19 0 259 683 ;
+C 249 ; WX 500 ; N oslash ; B 29 -112 470 551 ;
+C 250 ; WX 722 ; N oe ; B 30 -10 690 460 ;
+C 251 ; WX 500 ; N germandbls ; B 12 -9 468 683 ;
+C -1 ; WX 333 ; N Idieresis ; B 18 0 315 835 ;
+C -1 ; WX 444 ; N eacute ; B 25 -10 424 678 ;
+C -1 ; WX 444 ; N abreve ; B 37 -10 442 664 ;
+C -1 ; WX 500 ; N uhungarumlaut ; B 9 -10 501 678 ;
+C -1 ; WX 444 ; N ecaron ; B 25 -10 424 674 ;
+C -1 ; WX 722 ; N Ydieresis ; B 22 0 703 835 ;
+C -1 ; WX 564 ; N divide ; B 30 -10 534 516 ;
+C -1 ; WX 722 ; N Yacute ; B 22 0 703 890 ;
+C -1 ; WX 722 ; N Acircumflex ; B 15 0 706 886 ;
+C -1 ; WX 444 ; N aacute ; B 37 -10 442 678 ;
+C -1 ; WX 722 ; N Ucircumflex ; B 14 -14 705 886 ;
+C -1 ; WX 500 ; N yacute ; B 14 -218 475 678 ;
+C -1 ; WX 389 ; N scommaaccent ; B 51 -218 348 460 ;
+C -1 ; WX 444 ; N ecircumflex ; B 25 -10 424 674 ;
+C -1 ; WX 722 ; N Uring ; B 14 -14 705 898 ;
+C -1 ; WX 722 ; N Udieresis ; B 14 -14 705 835 ;
+C -1 ; WX 444 ; N aogonek ; B 37 -165 469 460 ;
+C -1 ; WX 722 ; N Uacute ; B 14 -14 705 890 ;
+C -1 ; WX 500 ; N uogonek ; B 9 -155 487 450 ;
+C -1 ; WX 611 ; N Edieresis ; B 12 0 597 835 ;
+C -1 ; WX 722 ; N Dcroat ; B 16 0 685 662 ;
+C -1 ; WX 250 ; N commaaccent ; B 59 -218 184 -50 ;
+C -1 ; WX 760 ; N copyright ; B 38 -14 722 676 ;
+C -1 ; WX 611 ; N Emacron ; B 12 0 597 813 ;
+C -1 ; WX 444 ; N ccaron ; B 25 -10 412 674 ;
+C -1 ; WX 444 ; N aring ; B 37 -10 442 711 ;
+C -1 ; WX 722 ; N Ncommaaccent ; B 12 -198 707 662 ;
+C -1 ; WX 278 ; N lacute ; B 19 0 290 890 ;
+C -1 ; WX 444 ; N agrave ; B 37 -10 442 678 ;
+C -1 ; WX 611 ; N Tcommaaccent ; B 17 -218 593 662 ;
+C -1 ; WX 667 ; N Cacute ; B 28 -14 633 890 ;
+C -1 ; WX 444 ; N atilde ; B 37 -10 442 638 ;
+C -1 ; WX 611 ; N Edotaccent ; B 12 0 597 835 ;
+C -1 ; WX 389 ; N scaron ; B 39 -10 350 674 ;
+C -1 ; WX 389 ; N scedilla ; B 51 -215 348 460 ;
+C -1 ; WX 278 ; N iacute ; B 16 0 290 678 ;
+C -1 ; WX 471 ; N lozenge ; B 13 0 459 724 ;
+C -1 ; WX 667 ; N Rcaron ; B 17 0 659 886 ;
+C -1 ; WX 722 ; N Gcommaaccent ; B 32 -218 709 676 ;
+C -1 ; WX 500 ; N ucircumflex ; B 9 -10 479 674 ;
+C -1 ; WX 444 ; N acircumflex ; B 37 -10 442 674 ;
+C -1 ; WX 722 ; N Amacron ; B 15 0 706 813 ;
+C -1 ; WX 333 ; N rcaron ; B 5 0 335 674 ;
+C -1 ; WX 444 ; N ccedilla ; B 25 -215 412 460 ;
+C -1 ; WX 611 ; N Zdotaccent ; B 9 0 597 835 ;
+C -1 ; WX 556 ; N Thorn ; B 16 0 542 662 ;
+C -1 ; WX 722 ; N Omacron ; B 34 -14 688 813 ;
+C -1 ; WX 667 ; N Racute ; B 17 0 659 890 ;
+C -1 ; WX 556 ; N Sacute ; B 42 -14 491 890 ;
+C -1 ; WX 588 ; N dcaron ; B 27 -10 589 695 ;
+C -1 ; WX 722 ; N Umacron ; B 14 -14 705 813 ;
+C -1 ; WX 500 ; N uring ; B 9 -10 479 711 ;
+C -1 ; WX 300 ; N threesuperior ; B 15 262 291 676 ;
+C -1 ; WX 722 ; N Ograve ; B 34 -14 688 890 ;
+C -1 ; WX 722 ; N Agrave ; B 15 0 706 890 ;
+C -1 ; WX 722 ; N Abreve ; B 15 0 706 876 ;
+C -1 ; WX 564 ; N multiply ; B 38 8 527 497 ;
+C -1 ; WX 500 ; N uacute ; B 9 -10 479 678 ;
+C -1 ; WX 611 ; N Tcaron ; B 17 0 593 886 ;
+C -1 ; WX 476 ; N partialdiff ; B 17 -38 459 710 ;
+C -1 ; WX 500 ; N ydieresis ; B 14 -218 475 623 ;
+C -1 ; WX 722 ; N Nacute ; B 12 -11 707 890 ;
+C -1 ; WX 278 ; N icircumflex ; B -16 0 295 674 ;
+C -1 ; WX 611 ; N Ecircumflex ; B 12 0 597 886 ;
+C -1 ; WX 444 ; N adieresis ; B 37 -10 442 623 ;
+C -1 ; WX 444 ; N edieresis ; B 25 -10 424 623 ;
+C -1 ; WX 444 ; N cacute ; B 25 -10 413 678 ;
+C -1 ; WX 500 ; N nacute ; B 16 0 485 678 ;
+C -1 ; WX 500 ; N umacron ; B 9 -10 479 601 ;
+C -1 ; WX 722 ; N Ncaron ; B 12 -11 707 886 ;
+C -1 ; WX 333 ; N Iacute ; B 18 0 317 890 ;
+C -1 ; WX 564 ; N plusminus ; B 30 0 534 506 ;
+C -1 ; WX 200 ; N brokenbar ; B 67 -143 133 707 ;
+C -1 ; WX 760 ; N registered ; B 38 -14 722 676 ;
+C -1 ; WX 722 ; N Gbreve ; B 32 -14 709 876 ;
+C -1 ; WX 333 ; N Idotaccent ; B 18 0 315 835 ;
+C -1 ; WX 600 ; N summation ; B 15 -10 585 706 ;
+C -1 ; WX 611 ; N Egrave ; B 12 0 597 890 ;
+C -1 ; WX 333 ; N racute ; B 5 0 335 678 ;
+C -1 ; WX 500 ; N omacron ; B 29 -10 470 601 ;
+C -1 ; WX 611 ; N Zacute ; B 9 0 597 890 ;
+C -1 ; WX 611 ; N Zcaron ; B 9 0 597 886 ;
+C -1 ; WX 549 ; N greaterequal ; B 26 0 523 666 ;
+C -1 ; WX 722 ; N Eth ; B 16 0 685 662 ;
+C -1 ; WX 667 ; N Ccedilla ; B 28 -215 633 676 ;
+C -1 ; WX 278 ; N lcommaaccent ; B 19 -218 257 683 ;
+C -1 ; WX 326 ; N tcaron ; B 13 -10 318 722 ;
+C -1 ; WX 444 ; N eogonek ; B 25 -165 424 460 ;
+C -1 ; WX 722 ; N Uogonek ; B 14 -165 705 662 ;
+C -1 ; WX 722 ; N Aacute ; B 15 0 706 890 ;
+C -1 ; WX 722 ; N Adieresis ; B 15 0 706 835 ;
+C -1 ; WX 444 ; N egrave ; B 25 -10 424 678 ;
+C -1 ; WX 444 ; N zacute ; B 27 0 418 678 ;
+C -1 ; WX 278 ; N iogonek ; B 16 -165 265 683 ;
+C -1 ; WX 722 ; N Oacute ; B 34 -14 688 890 ;
+C -1 ; WX 500 ; N oacute ; B 29 -10 470 678 ;
+C -1 ; WX 444 ; N amacron ; B 37 -10 442 601 ;
+C -1 ; WX 389 ; N sacute ; B 51 -10 348 678 ;
+C -1 ; WX 278 ; N idieresis ; B -9 0 288 623 ;
+C -1 ; WX 722 ; N Ocircumflex ; B 34 -14 688 886 ;
+C -1 ; WX 722 ; N Ugrave ; B 14 -14 705 890 ;
+C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C -1 ; WX 500 ; N thorn ; B 5 -217 470 683 ;
+C -1 ; WX 300 ; N twosuperior ; B 1 270 296 676 ;
+C -1 ; WX 722 ; N Odieresis ; B 34 -14 688 835 ;
+C -1 ; WX 500 ; N mu ; B 36 -218 512 450 ;
+C -1 ; WX 278 ; N igrave ; B -8 0 253 678 ;
+C -1 ; WX 500 ; N ohungarumlaut ; B 29 -10 491 678 ;
+C -1 ; WX 611 ; N Eogonek ; B 12 -165 597 662 ;
+C -1 ; WX 500 ; N dcroat ; B 27 -10 500 683 ;
+C -1 ; WX 750 ; N threequarters ; B 15 -14 718 676 ;
+C -1 ; WX 556 ; N Scedilla ; B 42 -215 491 676 ;
+C -1 ; WX 344 ; N lcaron ; B 19 0 347 695 ;
+C -1 ; WX 722 ; N Kcommaaccent ; B 34 -198 723 662 ;
+C -1 ; WX 611 ; N Lacute ; B 12 0 598 890 ;
+C -1 ; WX 980 ; N trademark ; B 30 256 957 662 ;
+C -1 ; WX 444 ; N edotaccent ; B 25 -10 424 623 ;
+C -1 ; WX 333 ; N Igrave ; B 18 0 315 890 ;
+C -1 ; WX 333 ; N Imacron ; B 11 0 322 813 ;
+C -1 ; WX 611 ; N Lcaron ; B 12 0 598 676 ;
+C -1 ; WX 750 ; N onehalf ; B 31 -14 746 676 ;
+C -1 ; WX 549 ; N lessequal ; B 26 0 523 666 ;
+C -1 ; WX 500 ; N ocircumflex ; B 29 -10 470 674 ;
+C -1 ; WX 500 ; N ntilde ; B 16 0 485 638 ;
+C -1 ; WX 722 ; N Uhungarumlaut ; B 14 -14 705 890 ;
+C -1 ; WX 611 ; N Eacute ; B 12 0 597 890 ;
+C -1 ; WX 444 ; N emacron ; B 25 -10 424 601 ;
+C -1 ; WX 500 ; N gbreve ; B 28 -218 470 664 ;
+C -1 ; WX 750 ; N onequarter ; B 37 -14 718 676 ;
+C -1 ; WX 556 ; N Scaron ; B 42 -14 491 886 ;
+C -1 ; WX 556 ; N Scommaaccent ; B 42 -218 491 676 ;
+C -1 ; WX 722 ; N Ohungarumlaut ; B 34 -14 688 890 ;
+C -1 ; WX 400 ; N degree ; B 57 390 343 676 ;
+C -1 ; WX 500 ; N ograve ; B 29 -10 470 678 ;
+C -1 ; WX 667 ; N Ccaron ; B 28 -14 633 886 ;
+C -1 ; WX 500 ; N ugrave ; B 9 -10 479 678 ;
+C -1 ; WX 453 ; N radical ; B 2 -60 452 768 ;
+C -1 ; WX 722 ; N Dcaron ; B 16 0 685 886 ;
+C -1 ; WX 333 ; N rcommaaccent ; B 5 -218 335 460 ;
+C -1 ; WX 722 ; N Ntilde ; B 12 -11 707 850 ;
+C -1 ; WX 500 ; N otilde ; B 29 -10 470 638 ;
+C -1 ; WX 667 ; N Rcommaaccent ; B 17 -198 659 662 ;
+C -1 ; WX 611 ; N Lcommaaccent ; B 12 -218 598 662 ;
+C -1 ; WX 722 ; N Atilde ; B 15 0 706 850 ;
+C -1 ; WX 722 ; N Aogonek ; B 15 -165 738 674 ;
+C -1 ; WX 722 ; N Aring ; B 15 0 706 898 ;
+C -1 ; WX 722 ; N Otilde ; B 34 -14 688 850 ;
+C -1 ; WX 444 ; N zdotaccent ; B 27 0 418 623 ;
+C -1 ; WX 611 ; N Ecaron ; B 12 0 597 886 ;
+C -1 ; WX 333 ; N Iogonek ; B 18 -165 315 662 ;
+C -1 ; WX 500 ; N kcommaaccent ; B 7 -218 505 683 ;
+C -1 ; WX 564 ; N minus ; B 30 220 534 286 ;
+C -1 ; WX 333 ; N Icircumflex ; B 11 0 322 886 ;
+C -1 ; WX 500 ; N ncaron ; B 16 0 485 674 ;
+C -1 ; WX 278 ; N tcommaaccent ; B 13 -218 279 579 ;
+C -1 ; WX 564 ; N logicalnot ; B 30 108 534 386 ;
+C -1 ; WX 500 ; N odieresis ; B 29 -10 470 623 ;
+C -1 ; WX 500 ; N udieresis ; B 9 -10 479 623 ;
+C -1 ; WX 549 ; N notequal ; B 12 -31 537 547 ;
+C -1 ; WX 500 ; N gcommaaccent ; B 28 -218 470 749 ;
+C -1 ; WX 500 ; N eth ; B 29 -10 471 686 ;
+C -1 ; WX 444 ; N zcaron ; B 27 0 418 674 ;
+C -1 ; WX 500 ; N ncommaaccent ; B 16 -218 485 460 ;
+C -1 ; WX 300 ; N onesuperior ; B 57 270 248 676 ;
+C -1 ; WX 278 ; N imacron ; B 6 0 271 601 ;
+C -1 ; WX 500 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+StartKernData
+StartKernPairs 2073
+KPX A C -40
+KPX A Cacute -40
+KPX A Ccaron -40
+KPX A Ccedilla -40
+KPX A G -40
+KPX A Gbreve -40
+KPX A Gcommaaccent -40
+KPX A O -55
+KPX A Oacute -55
+KPX A Ocircumflex -55
+KPX A Odieresis -55
+KPX A Ograve -55
+KPX A Ohungarumlaut -55
+KPX A Omacron -55
+KPX A Oslash -55
+KPX A Otilde -55
+KPX A Q -55
+KPX A T -111
+KPX A Tcaron -111
+KPX A Tcommaaccent -111
+KPX A U -55
+KPX A Uacute -55
+KPX A Ucircumflex -55
+KPX A Udieresis -55
+KPX A Ugrave -55
+KPX A Uhungarumlaut -55
+KPX A Umacron -55
+KPX A Uogonek -55
+KPX A Uring -55
+KPX A V -135
+KPX A W -90
+KPX A Y -105
+KPX A Yacute -105
+KPX A Ydieresis -105
+KPX A quoteright -111
+KPX A v -74
+KPX A w -92
+KPX A y -92
+KPX A yacute -92
+KPX A ydieresis -92
+KPX Aacute C -40
+KPX Aacute Cacute -40
+KPX Aacute Ccaron -40
+KPX Aacute Ccedilla -40
+KPX Aacute G -40
+KPX Aacute Gbreve -40
+KPX Aacute Gcommaaccent -40
+KPX Aacute O -55
+KPX Aacute Oacute -55
+KPX Aacute Ocircumflex -55
+KPX Aacute Odieresis -55
+KPX Aacute Ograve -55
+KPX Aacute Ohungarumlaut -55
+KPX Aacute Omacron -55
+KPX Aacute Oslash -55
+KPX Aacute Otilde -55
+KPX Aacute Q -55
+KPX Aacute T -111
+KPX Aacute Tcaron -111
+KPX Aacute Tcommaaccent -111
+KPX Aacute U -55
+KPX Aacute Uacute -55
+KPX Aacute Ucircumflex -55
+KPX Aacute Udieresis -55
+KPX Aacute Ugrave -55
+KPX Aacute Uhungarumlaut -55
+KPX Aacute Umacron -55
+KPX Aacute Uogonek -55
+KPX Aacute Uring -55
+KPX Aacute V -135
+KPX Aacute W -90
+KPX Aacute Y -105
+KPX Aacute Yacute -105
+KPX Aacute Ydieresis -105
+KPX Aacute quoteright -111
+KPX Aacute v -74
+KPX Aacute w -92
+KPX Aacute y -92
+KPX Aacute yacute -92
+KPX Aacute ydieresis -92
+KPX Abreve C -40
+KPX Abreve Cacute -40
+KPX Abreve Ccaron -40
+KPX Abreve Ccedilla -40
+KPX Abreve G -40
+KPX Abreve Gbreve -40
+KPX Abreve Gcommaaccent -40
+KPX Abreve O -55
+KPX Abreve Oacute -55
+KPX Abreve Ocircumflex -55
+KPX Abreve Odieresis -55
+KPX Abreve Ograve -55
+KPX Abreve Ohungarumlaut -55
+KPX Abreve Omacron -55
+KPX Abreve Oslash -55
+KPX Abreve Otilde -55
+KPX Abreve Q -55
+KPX Abreve T -111
+KPX Abreve Tcaron -111
+KPX Abreve Tcommaaccent -111
+KPX Abreve U -55
+KPX Abreve Uacute -55
+KPX Abreve Ucircumflex -55
+KPX Abreve Udieresis -55
+KPX Abreve Ugrave -55
+KPX Abreve Uhungarumlaut -55
+KPX Abreve Umacron -55
+KPX Abreve Uogonek -55
+KPX Abreve Uring -55
+KPX Abreve V -135
+KPX Abreve W -90
+KPX Abreve Y -105
+KPX Abreve Yacute -105
+KPX Abreve Ydieresis -105
+KPX Abreve quoteright -111
+KPX Abreve v -74
+KPX Abreve w -92
+KPX Abreve y -92
+KPX Abreve yacute -92
+KPX Abreve ydieresis -92
+KPX Acircumflex C -40
+KPX Acircumflex Cacute -40
+KPX Acircumflex Ccaron -40
+KPX Acircumflex Ccedilla -40
+KPX Acircumflex G -40
+KPX Acircumflex Gbreve -40
+KPX Acircumflex Gcommaaccent -40
+KPX Acircumflex O -55
+KPX Acircumflex Oacute -55
+KPX Acircumflex Ocircumflex -55
+KPX Acircumflex Odieresis -55
+KPX Acircumflex Ograve -55
+KPX Acircumflex Ohungarumlaut -55
+KPX Acircumflex Omacron -55
+KPX Acircumflex Oslash -55
+KPX Acircumflex Otilde -55
+KPX Acircumflex Q -55
+KPX Acircumflex T -111
+KPX Acircumflex Tcaron -111
+KPX Acircumflex Tcommaaccent -111
+KPX Acircumflex U -55
+KPX Acircumflex Uacute -55
+KPX Acircumflex Ucircumflex -55
+KPX Acircumflex Udieresis -55
+KPX Acircumflex Ugrave -55
+KPX Acircumflex Uhungarumlaut -55
+KPX Acircumflex Umacron -55
+KPX Acircumflex Uogonek -55
+KPX Acircumflex Uring -55
+KPX Acircumflex V -135
+KPX Acircumflex W -90
+KPX Acircumflex Y -105
+KPX Acircumflex Yacute -105
+KPX Acircumflex Ydieresis -105
+KPX Acircumflex quoteright -111
+KPX Acircumflex v -74
+KPX Acircumflex w -92
+KPX Acircumflex y -92
+KPX Acircumflex yacute -92
+KPX Acircumflex ydieresis -92
+KPX Adieresis C -40
+KPX Adieresis Cacute -40
+KPX Adieresis Ccaron -40
+KPX Adieresis Ccedilla -40
+KPX Adieresis G -40
+KPX Adieresis Gbreve -40
+KPX Adieresis Gcommaaccent -40
+KPX Adieresis O -55
+KPX Adieresis Oacute -55
+KPX Adieresis Ocircumflex -55
+KPX Adieresis Odieresis -55
+KPX Adieresis Ograve -55
+KPX Adieresis Ohungarumlaut -55
+KPX Adieresis Omacron -55
+KPX Adieresis Oslash -55
+KPX Adieresis Otilde -55
+KPX Adieresis Q -55
+KPX Adieresis T -111
+KPX Adieresis Tcaron -111
+KPX Adieresis Tcommaaccent -111
+KPX Adieresis U -55
+KPX Adieresis Uacute -55
+KPX Adieresis Ucircumflex -55
+KPX Adieresis Udieresis -55
+KPX Adieresis Ugrave -55
+KPX Adieresis Uhungarumlaut -55
+KPX Adieresis Umacron -55
+KPX Adieresis Uogonek -55
+KPX Adieresis Uring -55
+KPX Adieresis V -135
+KPX Adieresis W -90
+KPX Adieresis Y -105
+KPX Adieresis Yacute -105
+KPX Adieresis Ydieresis -105
+KPX Adieresis quoteright -111
+KPX Adieresis v -74
+KPX Adieresis w -92
+KPX Adieresis y -92
+KPX Adieresis yacute -92
+KPX Adieresis ydieresis -92
+KPX Agrave C -40
+KPX Agrave Cacute -40
+KPX Agrave Ccaron -40
+KPX Agrave Ccedilla -40
+KPX Agrave G -40
+KPX Agrave Gbreve -40
+KPX Agrave Gcommaaccent -40
+KPX Agrave O -55
+KPX Agrave Oacute -55
+KPX Agrave Ocircumflex -55
+KPX Agrave Odieresis -55
+KPX Agrave Ograve -55
+KPX Agrave Ohungarumlaut -55
+KPX Agrave Omacron -55
+KPX Agrave Oslash -55
+KPX Agrave Otilde -55
+KPX Agrave Q -55
+KPX Agrave T -111
+KPX Agrave Tcaron -111
+KPX Agrave Tcommaaccent -111
+KPX Agrave U -55
+KPX Agrave Uacute -55
+KPX Agrave Ucircumflex -55
+KPX Agrave Udieresis -55
+KPX Agrave Ugrave -55
+KPX Agrave Uhungarumlaut -55
+KPX Agrave Umacron -55
+KPX Agrave Uogonek -55
+KPX Agrave Uring -55
+KPX Agrave V -135
+KPX Agrave W -90
+KPX Agrave Y -105
+KPX Agrave Yacute -105
+KPX Agrave Ydieresis -105
+KPX Agrave quoteright -111
+KPX Agrave v -74
+KPX Agrave w -92
+KPX Agrave y -92
+KPX Agrave yacute -92
+KPX Agrave ydieresis -92
+KPX Amacron C -40
+KPX Amacron Cacute -40
+KPX Amacron Ccaron -40
+KPX Amacron Ccedilla -40
+KPX Amacron G -40
+KPX Amacron Gbreve -40
+KPX Amacron Gcommaaccent -40
+KPX Amacron O -55
+KPX Amacron Oacute -55
+KPX Amacron Ocircumflex -55
+KPX Amacron Odieresis -55
+KPX Amacron Ograve -55
+KPX Amacron Ohungarumlaut -55
+KPX Amacron Omacron -55
+KPX Amacron Oslash -55
+KPX Amacron Otilde -55
+KPX Amacron Q -55
+KPX Amacron T -111
+KPX Amacron Tcaron -111
+KPX Amacron Tcommaaccent -111
+KPX Amacron U -55
+KPX Amacron Uacute -55
+KPX Amacron Ucircumflex -55
+KPX Amacron Udieresis -55
+KPX Amacron Ugrave -55
+KPX Amacron Uhungarumlaut -55
+KPX Amacron Umacron -55
+KPX Amacron Uogonek -55
+KPX Amacron Uring -55
+KPX Amacron V -135
+KPX Amacron W -90
+KPX Amacron Y -105
+KPX Amacron Yacute -105
+KPX Amacron Ydieresis -105
+KPX Amacron quoteright -111
+KPX Amacron v -74
+KPX Amacron w -92
+KPX Amacron y -92
+KPX Amacron yacute -92
+KPX Amacron ydieresis -92
+KPX Aogonek C -40
+KPX Aogonek Cacute -40
+KPX Aogonek Ccaron -40
+KPX Aogonek Ccedilla -40
+KPX Aogonek G -40
+KPX Aogonek Gbreve -40
+KPX Aogonek Gcommaaccent -40
+KPX Aogonek O -55
+KPX Aogonek Oacute -55
+KPX Aogonek Ocircumflex -55
+KPX Aogonek Odieresis -55
+KPX Aogonek Ograve -55
+KPX Aogonek Ohungarumlaut -55
+KPX Aogonek Omacron -55
+KPX Aogonek Oslash -55
+KPX Aogonek Otilde -55
+KPX Aogonek Q -55
+KPX Aogonek T -111
+KPX Aogonek Tcaron -111
+KPX Aogonek Tcommaaccent -111
+KPX Aogonek U -55
+KPX Aogonek Uacute -55
+KPX Aogonek Ucircumflex -55
+KPX Aogonek Udieresis -55
+KPX Aogonek Ugrave -55
+KPX Aogonek Uhungarumlaut -55
+KPX Aogonek Umacron -55
+KPX Aogonek Uogonek -55
+KPX Aogonek Uring -55
+KPX Aogonek V -135
+KPX Aogonek W -90
+KPX Aogonek Y -105
+KPX Aogonek Yacute -105
+KPX Aogonek Ydieresis -105
+KPX Aogonek quoteright -111
+KPX Aogonek v -74
+KPX Aogonek w -52
+KPX Aogonek y -52
+KPX Aogonek yacute -52
+KPX Aogonek ydieresis -52
+KPX Aring C -40
+KPX Aring Cacute -40
+KPX Aring Ccaron -40
+KPX Aring Ccedilla -40
+KPX Aring G -40
+KPX Aring Gbreve -40
+KPX Aring Gcommaaccent -40
+KPX Aring O -55
+KPX Aring Oacute -55
+KPX Aring Ocircumflex -55
+KPX Aring Odieresis -55
+KPX Aring Ograve -55
+KPX Aring Ohungarumlaut -55
+KPX Aring Omacron -55
+KPX Aring Oslash -55
+KPX Aring Otilde -55
+KPX Aring Q -55
+KPX Aring T -111
+KPX Aring Tcaron -111
+KPX Aring Tcommaaccent -111
+KPX Aring U -55
+KPX Aring Uacute -55
+KPX Aring Ucircumflex -55
+KPX Aring Udieresis -55
+KPX Aring Ugrave -55
+KPX Aring Uhungarumlaut -55
+KPX Aring Umacron -55
+KPX Aring Uogonek -55
+KPX Aring Uring -55
+KPX Aring V -135
+KPX Aring W -90
+KPX Aring Y -105
+KPX Aring Yacute -105
+KPX Aring Ydieresis -105
+KPX Aring quoteright -111
+KPX Aring v -74
+KPX Aring w -92
+KPX Aring y -92
+KPX Aring yacute -92
+KPX Aring ydieresis -92
+KPX Atilde C -40
+KPX Atilde Cacute -40
+KPX Atilde Ccaron -40
+KPX Atilde Ccedilla -40
+KPX Atilde G -40
+KPX Atilde Gbreve -40
+KPX Atilde Gcommaaccent -40
+KPX Atilde O -55
+KPX Atilde Oacute -55
+KPX Atilde Ocircumflex -55
+KPX Atilde Odieresis -55
+KPX Atilde Ograve -55
+KPX Atilde Ohungarumlaut -55
+KPX Atilde Omacron -55
+KPX Atilde Oslash -55
+KPX Atilde Otilde -55
+KPX Atilde Q -55
+KPX Atilde T -111
+KPX Atilde Tcaron -111
+KPX Atilde Tcommaaccent -111
+KPX Atilde U -55
+KPX Atilde Uacute -55
+KPX Atilde Ucircumflex -55
+KPX Atilde Udieresis -55
+KPX Atilde Ugrave -55
+KPX Atilde Uhungarumlaut -55
+KPX Atilde Umacron -55
+KPX Atilde Uogonek -55
+KPX Atilde Uring -55
+KPX Atilde V -135
+KPX Atilde W -90
+KPX Atilde Y -105
+KPX Atilde Yacute -105
+KPX Atilde Ydieresis -105
+KPX Atilde quoteright -111
+KPX Atilde v -74
+KPX Atilde w -92
+KPX Atilde y -92
+KPX Atilde yacute -92
+KPX Atilde ydieresis -92
+KPX B A -35
+KPX B Aacute -35
+KPX B Abreve -35
+KPX B Acircumflex -35
+KPX B Adieresis -35
+KPX B Agrave -35
+KPX B Amacron -35
+KPX B Aogonek -35
+KPX B Aring -35
+KPX B Atilde -35
+KPX B U -10
+KPX B Uacute -10
+KPX B Ucircumflex -10
+KPX B Udieresis -10
+KPX B Ugrave -10
+KPX B Uhungarumlaut -10
+KPX B Umacron -10
+KPX B Uogonek -10
+KPX B Uring -10
+KPX D A -40
+KPX D Aacute -40
+KPX D Abreve -40
+KPX D Acircumflex -40
+KPX D Adieresis -40
+KPX D Agrave -40
+KPX D Amacron -40
+KPX D Aogonek -40
+KPX D Aring -40
+KPX D Atilde -40
+KPX D V -40
+KPX D W -30
+KPX D Y -55
+KPX D Yacute -55
+KPX D Ydieresis -55
+KPX Dcaron A -40
+KPX Dcaron Aacute -40
+KPX Dcaron Abreve -40
+KPX Dcaron Acircumflex -40
+KPX Dcaron Adieresis -40
+KPX Dcaron Agrave -40
+KPX Dcaron Amacron -40
+KPX Dcaron Aogonek -40
+KPX Dcaron Aring -40
+KPX Dcaron Atilde -40
+KPX Dcaron V -40
+KPX Dcaron W -30
+KPX Dcaron Y -55
+KPX Dcaron Yacute -55
+KPX Dcaron Ydieresis -55
+KPX Dcroat A -40
+KPX Dcroat Aacute -40
+KPX Dcroat Abreve -40
+KPX Dcroat Acircumflex -40
+KPX Dcroat Adieresis -40
+KPX Dcroat Agrave -40
+KPX Dcroat Amacron -40
+KPX Dcroat Aogonek -40
+KPX Dcroat Aring -40
+KPX Dcroat Atilde -40
+KPX Dcroat V -40
+KPX Dcroat W -30
+KPX Dcroat Y -55
+KPX Dcroat Yacute -55
+KPX Dcroat Ydieresis -55
+KPX F A -74
+KPX F Aacute -74
+KPX F Abreve -74
+KPX F Acircumflex -74
+KPX F Adieresis -74
+KPX F Agrave -74
+KPX F Amacron -74
+KPX F Aogonek -74
+KPX F Aring -74
+KPX F Atilde -74
+KPX F a -15
+KPX F aacute -15
+KPX F abreve -15
+KPX F acircumflex -15
+KPX F adieresis -15
+KPX F agrave -15
+KPX F amacron -15
+KPX F aogonek -15
+KPX F aring -15
+KPX F atilde -15
+KPX F comma -80
+KPX F o -15
+KPX F oacute -15
+KPX F ocircumflex -15
+KPX F odieresis -15
+KPX F ograve -15
+KPX F ohungarumlaut -15
+KPX F omacron -15
+KPX F oslash -15
+KPX F otilde -15
+KPX F period -80
+KPX J A -60
+KPX J Aacute -60
+KPX J Abreve -60
+KPX J Acircumflex -60
+KPX J Adieresis -60
+KPX J Agrave -60
+KPX J Amacron -60
+KPX J Aogonek -60
+KPX J Aring -60
+KPX J Atilde -60
+KPX K O -30
+KPX K Oacute -30
+KPX K Ocircumflex -30
+KPX K Odieresis -30
+KPX K Ograve -30
+KPX K Ohungarumlaut -30
+KPX K Omacron -30
+KPX K Oslash -30
+KPX K Otilde -30
+KPX K e -25
+KPX K eacute -25
+KPX K ecaron -25
+KPX K ecircumflex -25
+KPX K edieresis -25
+KPX K edotaccent -25
+KPX K egrave -25
+KPX K emacron -25
+KPX K eogonek -25
+KPX K o -35
+KPX K oacute -35
+KPX K ocircumflex -35
+KPX K odieresis -35
+KPX K ograve -35
+KPX K ohungarumlaut -35
+KPX K omacron -35
+KPX K oslash -35
+KPX K otilde -35
+KPX K u -15
+KPX K uacute -15
+KPX K ucircumflex -15
+KPX K udieresis -15
+KPX K ugrave -15
+KPX K uhungarumlaut -15
+KPX K umacron -15
+KPX K uogonek -15
+KPX K uring -15
+KPX K y -25
+KPX K yacute -25
+KPX K ydieresis -25
+KPX Kcommaaccent O -30
+KPX Kcommaaccent Oacute -30
+KPX Kcommaaccent Ocircumflex -30
+KPX Kcommaaccent Odieresis -30
+KPX Kcommaaccent Ograve -30
+KPX Kcommaaccent Ohungarumlaut -30
+KPX Kcommaaccent Omacron -30
+KPX Kcommaaccent Oslash -30
+KPX Kcommaaccent Otilde -30
+KPX Kcommaaccent e -25
+KPX Kcommaaccent eacute -25
+KPX Kcommaaccent ecaron -25
+KPX Kcommaaccent ecircumflex -25
+KPX Kcommaaccent edieresis -25
+KPX Kcommaaccent edotaccent -25
+KPX Kcommaaccent egrave -25
+KPX Kcommaaccent emacron -25
+KPX Kcommaaccent eogonek -25
+KPX Kcommaaccent o -35
+KPX Kcommaaccent oacute -35
+KPX Kcommaaccent ocircumflex -35
+KPX Kcommaaccent odieresis -35
+KPX Kcommaaccent ograve -35
+KPX Kcommaaccent ohungarumlaut -35
+KPX Kcommaaccent omacron -35
+KPX Kcommaaccent oslash -35
+KPX Kcommaaccent otilde -35
+KPX Kcommaaccent u -15
+KPX Kcommaaccent uacute -15
+KPX Kcommaaccent ucircumflex -15
+KPX Kcommaaccent udieresis -15
+KPX Kcommaaccent ugrave -15
+KPX Kcommaaccent uhungarumlaut -15
+KPX Kcommaaccent umacron -15
+KPX Kcommaaccent uogonek -15
+KPX Kcommaaccent uring -15
+KPX Kcommaaccent y -25
+KPX Kcommaaccent yacute -25
+KPX Kcommaaccent ydieresis -25
+KPX L T -92
+KPX L Tcaron -92
+KPX L Tcommaaccent -92
+KPX L V -100
+KPX L W -74
+KPX L Y -100
+KPX L Yacute -100
+KPX L Ydieresis -100
+KPX L quoteright -92
+KPX L y -55
+KPX L yacute -55
+KPX L ydieresis -55
+KPX Lacute T -92
+KPX Lacute Tcaron -92
+KPX Lacute Tcommaaccent -92
+KPX Lacute V -100
+KPX Lacute W -74
+KPX Lacute Y -100
+KPX Lacute Yacute -100
+KPX Lacute Ydieresis -100
+KPX Lacute quoteright -92
+KPX Lacute y -55
+KPX Lacute yacute -55
+KPX Lacute ydieresis -55
+KPX Lcaron quoteright -92
+KPX Lcaron y -55
+KPX Lcaron yacute -55
+KPX Lcaron ydieresis -55
+KPX Lcommaaccent T -92
+KPX Lcommaaccent Tcaron -92
+KPX Lcommaaccent Tcommaaccent -92
+KPX Lcommaaccent V -100
+KPX Lcommaaccent W -74
+KPX Lcommaaccent Y -100
+KPX Lcommaaccent Yacute -100
+KPX Lcommaaccent Ydieresis -100
+KPX Lcommaaccent quoteright -92
+KPX Lcommaaccent y -55
+KPX Lcommaaccent yacute -55
+KPX Lcommaaccent ydieresis -55
+KPX Lslash T -92
+KPX Lslash Tcaron -92
+KPX Lslash Tcommaaccent -92
+KPX Lslash V -100
+KPX Lslash W -74
+KPX Lslash Y -100
+KPX Lslash Yacute -100
+KPX Lslash Ydieresis -100
+KPX Lslash quoteright -92
+KPX Lslash y -55
+KPX Lslash yacute -55
+KPX Lslash ydieresis -55
+KPX N A -35
+KPX N Aacute -35
+KPX N Abreve -35
+KPX N Acircumflex -35
+KPX N Adieresis -35
+KPX N Agrave -35
+KPX N Amacron -35
+KPX N Aogonek -35
+KPX N Aring -35
+KPX N Atilde -35
+KPX Nacute A -35
+KPX Nacute Aacute -35
+KPX Nacute Abreve -35
+KPX Nacute Acircumflex -35
+KPX Nacute Adieresis -35
+KPX Nacute Agrave -35
+KPX Nacute Amacron -35
+KPX Nacute Aogonek -35
+KPX Nacute Aring -35
+KPX Nacute Atilde -35
+KPX Ncaron A -35
+KPX Ncaron Aacute -35
+KPX Ncaron Abreve -35
+KPX Ncaron Acircumflex -35
+KPX Ncaron Adieresis -35
+KPX Ncaron Agrave -35
+KPX Ncaron Amacron -35
+KPX Ncaron Aogonek -35
+KPX Ncaron Aring -35
+KPX Ncaron Atilde -35
+KPX Ncommaaccent A -35
+KPX Ncommaaccent Aacute -35
+KPX Ncommaaccent Abreve -35
+KPX Ncommaaccent Acircumflex -35
+KPX Ncommaaccent Adieresis -35
+KPX Ncommaaccent Agrave -35
+KPX Ncommaaccent Amacron -35
+KPX Ncommaaccent Aogonek -35
+KPX Ncommaaccent Aring -35
+KPX Ncommaaccent Atilde -35
+KPX Ntilde A -35
+KPX Ntilde Aacute -35
+KPX Ntilde Abreve -35
+KPX Ntilde Acircumflex -35
+KPX Ntilde Adieresis -35
+KPX Ntilde Agrave -35
+KPX Ntilde Amacron -35
+KPX Ntilde Aogonek -35
+KPX Ntilde Aring -35
+KPX Ntilde Atilde -35
+KPX O A -35
+KPX O Aacute -35
+KPX O Abreve -35
+KPX O Acircumflex -35
+KPX O Adieresis -35
+KPX O Agrave -35
+KPX O Amacron -35
+KPX O Aogonek -35
+KPX O Aring -35
+KPX O Atilde -35
+KPX O T -40
+KPX O Tcaron -40
+KPX O Tcommaaccent -40
+KPX O V -50
+KPX O W -35
+KPX O X -40
+KPX O Y -50
+KPX O Yacute -50
+KPX O Ydieresis -50
+KPX Oacute A -35
+KPX Oacute Aacute -35
+KPX Oacute Abreve -35
+KPX Oacute Acircumflex -35
+KPX Oacute Adieresis -35
+KPX Oacute Agrave -35
+KPX Oacute Amacron -35
+KPX Oacute Aogonek -35
+KPX Oacute Aring -35
+KPX Oacute Atilde -35
+KPX Oacute T -40
+KPX Oacute Tcaron -40
+KPX Oacute Tcommaaccent -40
+KPX Oacute V -50
+KPX Oacute W -35
+KPX Oacute X -40
+KPX Oacute Y -50
+KPX Oacute Yacute -50
+KPX Oacute Ydieresis -50
+KPX Ocircumflex A -35
+KPX Ocircumflex Aacute -35
+KPX Ocircumflex Abreve -35
+KPX Ocircumflex Acircumflex -35
+KPX Ocircumflex Adieresis -35
+KPX Ocircumflex Agrave -35
+KPX Ocircumflex Amacron -35
+KPX Ocircumflex Aogonek -35
+KPX Ocircumflex Aring -35
+KPX Ocircumflex Atilde -35
+KPX Ocircumflex T -40
+KPX Ocircumflex Tcaron -40
+KPX Ocircumflex Tcommaaccent -40
+KPX Ocircumflex V -50
+KPX Ocircumflex W -35
+KPX Ocircumflex X -40
+KPX Ocircumflex Y -50
+KPX Ocircumflex Yacute -50
+KPX Ocircumflex Ydieresis -50
+KPX Odieresis A -35
+KPX Odieresis Aacute -35
+KPX Odieresis Abreve -35
+KPX Odieresis Acircumflex -35
+KPX Odieresis Adieresis -35
+KPX Odieresis Agrave -35
+KPX Odieresis Amacron -35
+KPX Odieresis Aogonek -35
+KPX Odieresis Aring -35
+KPX Odieresis Atilde -35
+KPX Odieresis T -40
+KPX Odieresis Tcaron -40
+KPX Odieresis Tcommaaccent -40
+KPX Odieresis V -50
+KPX Odieresis W -35
+KPX Odieresis X -40
+KPX Odieresis Y -50
+KPX Odieresis Yacute -50
+KPX Odieresis Ydieresis -50
+KPX Ograve A -35
+KPX Ograve Aacute -35
+KPX Ograve Abreve -35
+KPX Ograve Acircumflex -35
+KPX Ograve Adieresis -35
+KPX Ograve Agrave -35
+KPX Ograve Amacron -35
+KPX Ograve Aogonek -35
+KPX Ograve Aring -35
+KPX Ograve Atilde -35
+KPX Ograve T -40
+KPX Ograve Tcaron -40
+KPX Ograve Tcommaaccent -40
+KPX Ograve V -50
+KPX Ograve W -35
+KPX Ograve X -40
+KPX Ograve Y -50
+KPX Ograve Yacute -50
+KPX Ograve Ydieresis -50
+KPX Ohungarumlaut A -35
+KPX Ohungarumlaut Aacute -35
+KPX Ohungarumlaut Abreve -35
+KPX Ohungarumlaut Acircumflex -35
+KPX Ohungarumlaut Adieresis -35
+KPX Ohungarumlaut Agrave -35
+KPX Ohungarumlaut Amacron -35
+KPX Ohungarumlaut Aogonek -35
+KPX Ohungarumlaut Aring -35
+KPX Ohungarumlaut Atilde -35
+KPX Ohungarumlaut T -40
+KPX Ohungarumlaut Tcaron -40
+KPX Ohungarumlaut Tcommaaccent -40
+KPX Ohungarumlaut V -50
+KPX Ohungarumlaut W -35
+KPX Ohungarumlaut X -40
+KPX Ohungarumlaut Y -50
+KPX Ohungarumlaut Yacute -50
+KPX Ohungarumlaut Ydieresis -50
+KPX Omacron A -35
+KPX Omacron Aacute -35
+KPX Omacron Abreve -35
+KPX Omacron Acircumflex -35
+KPX Omacron Adieresis -35
+KPX Omacron Agrave -35
+KPX Omacron Amacron -35
+KPX Omacron Aogonek -35
+KPX Omacron Aring -35
+KPX Omacron Atilde -35
+KPX Omacron T -40
+KPX Omacron Tcaron -40
+KPX Omacron Tcommaaccent -40
+KPX Omacron V -50
+KPX Omacron W -35
+KPX Omacron X -40
+KPX Omacron Y -50
+KPX Omacron Yacute -50
+KPX Omacron Ydieresis -50
+KPX Oslash A -35
+KPX Oslash Aacute -35
+KPX Oslash Abreve -35
+KPX Oslash Acircumflex -35
+KPX Oslash Adieresis -35
+KPX Oslash Agrave -35
+KPX Oslash Amacron -35
+KPX Oslash Aogonek -35
+KPX Oslash Aring -35
+KPX Oslash Atilde -35
+KPX Oslash T -40
+KPX Oslash Tcaron -40
+KPX Oslash Tcommaaccent -40
+KPX Oslash V -50
+KPX Oslash W -35
+KPX Oslash X -40
+KPX Oslash Y -50
+KPX Oslash Yacute -50
+KPX Oslash Ydieresis -50
+KPX Otilde A -35
+KPX Otilde Aacute -35
+KPX Otilde Abreve -35
+KPX Otilde Acircumflex -35
+KPX Otilde Adieresis -35
+KPX Otilde Agrave -35
+KPX Otilde Amacron -35
+KPX Otilde Aogonek -35
+KPX Otilde Aring -35
+KPX Otilde Atilde -35
+KPX Otilde T -40
+KPX Otilde Tcaron -40
+KPX Otilde Tcommaaccent -40
+KPX Otilde V -50
+KPX Otilde W -35
+KPX Otilde X -40
+KPX Otilde Y -50
+KPX Otilde Yacute -50
+KPX Otilde Ydieresis -50
+KPX P A -92
+KPX P Aacute -92
+KPX P Abreve -92
+KPX P Acircumflex -92
+KPX P Adieresis -92
+KPX P Agrave -92
+KPX P Amacron -92
+KPX P Aogonek -92
+KPX P Aring -92
+KPX P Atilde -92
+KPX P a -15
+KPX P aacute -15
+KPX P abreve -15
+KPX P acircumflex -15
+KPX P adieresis -15
+KPX P agrave -15
+KPX P amacron -15
+KPX P aogonek -15
+KPX P aring -15
+KPX P atilde -15
+KPX P comma -111
+KPX P period -111
+KPX Q U -10
+KPX Q Uacute -10
+KPX Q Ucircumflex -10
+KPX Q Udieresis -10
+KPX Q Ugrave -10
+KPX Q Uhungarumlaut -10
+KPX Q Umacron -10
+KPX Q Uogonek -10
+KPX Q Uring -10
+KPX R O -40
+KPX R Oacute -40
+KPX R Ocircumflex -40
+KPX R Odieresis -40
+KPX R Ograve -40
+KPX R Ohungarumlaut -40
+KPX R Omacron -40
+KPX R Oslash -40
+KPX R Otilde -40
+KPX R T -60
+KPX R Tcaron -60
+KPX R Tcommaaccent -60
+KPX R U -40
+KPX R Uacute -40
+KPX R Ucircumflex -40
+KPX R Udieresis -40
+KPX R Ugrave -40
+KPX R Uhungarumlaut -40
+KPX R Umacron -40
+KPX R Uogonek -40
+KPX R Uring -40
+KPX R V -80
+KPX R W -55
+KPX R Y -65
+KPX R Yacute -65
+KPX R Ydieresis -65
+KPX Racute O -40
+KPX Racute Oacute -40
+KPX Racute Ocircumflex -40
+KPX Racute Odieresis -40
+KPX Racute Ograve -40
+KPX Racute Ohungarumlaut -40
+KPX Racute Omacron -40
+KPX Racute Oslash -40
+KPX Racute Otilde -40
+KPX Racute T -60
+KPX Racute Tcaron -60
+KPX Racute Tcommaaccent -60
+KPX Racute U -40
+KPX Racute Uacute -40
+KPX Racute Ucircumflex -40
+KPX Racute Udieresis -40
+KPX Racute Ugrave -40
+KPX Racute Uhungarumlaut -40
+KPX Racute Umacron -40
+KPX Racute Uogonek -40
+KPX Racute Uring -40
+KPX Racute V -80
+KPX Racute W -55
+KPX Racute Y -65
+KPX Racute Yacute -65
+KPX Racute Ydieresis -65
+KPX Rcaron O -40
+KPX Rcaron Oacute -40
+KPX Rcaron Ocircumflex -40
+KPX Rcaron Odieresis -40
+KPX Rcaron Ograve -40
+KPX Rcaron Ohungarumlaut -40
+KPX Rcaron Omacron -40
+KPX Rcaron Oslash -40
+KPX Rcaron Otilde -40
+KPX Rcaron T -60
+KPX Rcaron Tcaron -60
+KPX Rcaron Tcommaaccent -60
+KPX Rcaron U -40
+KPX Rcaron Uacute -40
+KPX Rcaron Ucircumflex -40
+KPX Rcaron Udieresis -40
+KPX Rcaron Ugrave -40
+KPX Rcaron Uhungarumlaut -40
+KPX Rcaron Umacron -40
+KPX Rcaron Uogonek -40
+KPX Rcaron Uring -40
+KPX Rcaron V -80
+KPX Rcaron W -55
+KPX Rcaron Y -65
+KPX Rcaron Yacute -65
+KPX Rcaron Ydieresis -65
+KPX Rcommaaccent O -40
+KPX Rcommaaccent Oacute -40
+KPX Rcommaaccent Ocircumflex -40
+KPX Rcommaaccent Odieresis -40
+KPX Rcommaaccent Ograve -40
+KPX Rcommaaccent Ohungarumlaut -40
+KPX Rcommaaccent Omacron -40
+KPX Rcommaaccent Oslash -40
+KPX Rcommaaccent Otilde -40
+KPX Rcommaaccent T -60
+KPX Rcommaaccent Tcaron -60
+KPX Rcommaaccent Tcommaaccent -60
+KPX Rcommaaccent U -40
+KPX Rcommaaccent Uacute -40
+KPX Rcommaaccent Ucircumflex -40
+KPX Rcommaaccent Udieresis -40
+KPX Rcommaaccent Ugrave -40
+KPX Rcommaaccent Uhungarumlaut -40
+KPX Rcommaaccent Umacron -40
+KPX Rcommaaccent Uogonek -40
+KPX Rcommaaccent Uring -40
+KPX Rcommaaccent V -80
+KPX Rcommaaccent W -55
+KPX Rcommaaccent Y -65
+KPX Rcommaaccent Yacute -65
+KPX Rcommaaccent Ydieresis -65
+KPX T A -93
+KPX T Aacute -93
+KPX T Abreve -93
+KPX T Acircumflex -93
+KPX T Adieresis -93
+KPX T Agrave -93
+KPX T Amacron -93
+KPX T Aogonek -93
+KPX T Aring -93
+KPX T Atilde -93
+KPX T O -18
+KPX T Oacute -18
+KPX T Ocircumflex -18
+KPX T Odieresis -18
+KPX T Ograve -18
+KPX T Ohungarumlaut -18
+KPX T Omacron -18
+KPX T Oslash -18
+KPX T Otilde -18
+KPX T a -80
+KPX T aacute -80
+KPX T abreve -80
+KPX T acircumflex -80
+KPX T adieresis -40
+KPX T agrave -40
+KPX T amacron -40
+KPX T aogonek -80
+KPX T aring -80
+KPX T atilde -40
+KPX T colon -50
+KPX T comma -74
+KPX T e -70
+KPX T eacute -70
+KPX T ecaron -70
+KPX T ecircumflex -70
+KPX T edieresis -30
+KPX T edotaccent -70
+KPX T egrave -70
+KPX T emacron -30
+KPX T eogonek -70
+KPX T hyphen -92
+KPX T i -35
+KPX T iacute -35
+KPX T iogonek -35
+KPX T o -80
+KPX T oacute -80
+KPX T ocircumflex -80
+KPX T odieresis -80
+KPX T ograve -80
+KPX T ohungarumlaut -80
+KPX T omacron -80
+KPX T oslash -80
+KPX T otilde -80
+KPX T period -74
+KPX T r -35
+KPX T racute -35
+KPX T rcaron -35
+KPX T rcommaaccent -35
+KPX T semicolon -55
+KPX T u -45
+KPX T uacute -45
+KPX T ucircumflex -45
+KPX T udieresis -45
+KPX T ugrave -45
+KPX T uhungarumlaut -45
+KPX T umacron -45
+KPX T uogonek -45
+KPX T uring -45
+KPX T w -80
+KPX T y -80
+KPX T yacute -80
+KPX T ydieresis -80
+KPX Tcaron A -93
+KPX Tcaron Aacute -93
+KPX Tcaron Abreve -93
+KPX Tcaron Acircumflex -93
+KPX Tcaron Adieresis -93
+KPX Tcaron Agrave -93
+KPX Tcaron Amacron -93
+KPX Tcaron Aogonek -93
+KPX Tcaron Aring -93
+KPX Tcaron Atilde -93
+KPX Tcaron O -18
+KPX Tcaron Oacute -18
+KPX Tcaron Ocircumflex -18
+KPX Tcaron Odieresis -18
+KPX Tcaron Ograve -18
+KPX Tcaron Ohungarumlaut -18
+KPX Tcaron Omacron -18
+KPX Tcaron Oslash -18
+KPX Tcaron Otilde -18
+KPX Tcaron a -80
+KPX Tcaron aacute -80
+KPX Tcaron abreve -80
+KPX Tcaron acircumflex -80
+KPX Tcaron adieresis -40
+KPX Tcaron agrave -40
+KPX Tcaron amacron -40
+KPX Tcaron aogonek -80
+KPX Tcaron aring -80
+KPX Tcaron atilde -40
+KPX Tcaron colon -50
+KPX Tcaron comma -74
+KPX Tcaron e -70
+KPX Tcaron eacute -70
+KPX Tcaron ecaron -70
+KPX Tcaron ecircumflex -30
+KPX Tcaron edieresis -30
+KPX Tcaron edotaccent -70
+KPX Tcaron egrave -70
+KPX Tcaron emacron -30
+KPX Tcaron eogonek -70
+KPX Tcaron hyphen -92
+KPX Tcaron i -35
+KPX Tcaron iacute -35
+KPX Tcaron iogonek -35
+KPX Tcaron o -80
+KPX Tcaron oacute -80
+KPX Tcaron ocircumflex -80
+KPX Tcaron odieresis -80
+KPX Tcaron ograve -80
+KPX Tcaron ohungarumlaut -80
+KPX Tcaron omacron -80
+KPX Tcaron oslash -80
+KPX Tcaron otilde -80
+KPX Tcaron period -74
+KPX Tcaron r -35
+KPX Tcaron racute -35
+KPX Tcaron rcaron -35
+KPX Tcaron rcommaaccent -35
+KPX Tcaron semicolon -55
+KPX Tcaron u -45
+KPX Tcaron uacute -45
+KPX Tcaron ucircumflex -45
+KPX Tcaron udieresis -45
+KPX Tcaron ugrave -45
+KPX Tcaron uhungarumlaut -45
+KPX Tcaron umacron -45
+KPX Tcaron uogonek -45
+KPX Tcaron uring -45
+KPX Tcaron w -80
+KPX Tcaron y -80
+KPX Tcaron yacute -80
+KPX Tcaron ydieresis -80
+KPX Tcommaaccent A -93
+KPX Tcommaaccent Aacute -93
+KPX Tcommaaccent Abreve -93
+KPX Tcommaaccent Acircumflex -93
+KPX Tcommaaccent Adieresis -93
+KPX Tcommaaccent Agrave -93
+KPX Tcommaaccent Amacron -93
+KPX Tcommaaccent Aogonek -93
+KPX Tcommaaccent Aring -93
+KPX Tcommaaccent Atilde -93
+KPX Tcommaaccent O -18
+KPX Tcommaaccent Oacute -18
+KPX Tcommaaccent Ocircumflex -18
+KPX Tcommaaccent Odieresis -18
+KPX Tcommaaccent Ograve -18
+KPX Tcommaaccent Ohungarumlaut -18
+KPX Tcommaaccent Omacron -18
+KPX Tcommaaccent Oslash -18
+KPX Tcommaaccent Otilde -18
+KPX Tcommaaccent a -80
+KPX Tcommaaccent aacute -80
+KPX Tcommaaccent abreve -80
+KPX Tcommaaccent acircumflex -80
+KPX Tcommaaccent adieresis -40
+KPX Tcommaaccent agrave -40
+KPX Tcommaaccent amacron -40
+KPX Tcommaaccent aogonek -80
+KPX Tcommaaccent aring -80
+KPX Tcommaaccent atilde -40
+KPX Tcommaaccent colon -50
+KPX Tcommaaccent comma -74
+KPX Tcommaaccent e -70
+KPX Tcommaaccent eacute -70
+KPX Tcommaaccent ecaron -70
+KPX Tcommaaccent ecircumflex -30
+KPX Tcommaaccent edieresis -30
+KPX Tcommaaccent edotaccent -70
+KPX Tcommaaccent egrave -30
+KPX Tcommaaccent emacron -70
+KPX Tcommaaccent eogonek -70
+KPX Tcommaaccent hyphen -92
+KPX Tcommaaccent i -35
+KPX Tcommaaccent iacute -35
+KPX Tcommaaccent iogonek -35
+KPX Tcommaaccent o -80
+KPX Tcommaaccent oacute -80
+KPX Tcommaaccent ocircumflex -80
+KPX Tcommaaccent odieresis -80
+KPX Tcommaaccent ograve -80
+KPX Tcommaaccent ohungarumlaut -80
+KPX Tcommaaccent omacron -80
+KPX Tcommaaccent oslash -80
+KPX Tcommaaccent otilde -80
+KPX Tcommaaccent period -74
+KPX Tcommaaccent r -35
+KPX Tcommaaccent racute -35
+KPX Tcommaaccent rcaron -35
+KPX Tcommaaccent rcommaaccent -35
+KPX Tcommaaccent semicolon -55
+KPX Tcommaaccent u -45
+KPX Tcommaaccent uacute -45
+KPX Tcommaaccent ucircumflex -45
+KPX Tcommaaccent udieresis -45
+KPX Tcommaaccent ugrave -45
+KPX Tcommaaccent uhungarumlaut -45
+KPX Tcommaaccent umacron -45
+KPX Tcommaaccent uogonek -45
+KPX Tcommaaccent uring -45
+KPX Tcommaaccent w -80
+KPX Tcommaaccent y -80
+KPX Tcommaaccent yacute -80
+KPX Tcommaaccent ydieresis -80
+KPX U A -40
+KPX U Aacute -40
+KPX U Abreve -40
+KPX U Acircumflex -40
+KPX U Adieresis -40
+KPX U Agrave -40
+KPX U Amacron -40
+KPX U Aogonek -40
+KPX U Aring -40
+KPX U Atilde -40
+KPX Uacute A -40
+KPX Uacute Aacute -40
+KPX Uacute Abreve -40
+KPX Uacute Acircumflex -40
+KPX Uacute Adieresis -40
+KPX Uacute Agrave -40
+KPX Uacute Amacron -40
+KPX Uacute Aogonek -40
+KPX Uacute Aring -40
+KPX Uacute Atilde -40
+KPX Ucircumflex A -40
+KPX Ucircumflex Aacute -40
+KPX Ucircumflex Abreve -40
+KPX Ucircumflex Acircumflex -40
+KPX Ucircumflex Adieresis -40
+KPX Ucircumflex Agrave -40
+KPX Ucircumflex Amacron -40
+KPX Ucircumflex Aogonek -40
+KPX Ucircumflex Aring -40
+KPX Ucircumflex Atilde -40
+KPX Udieresis A -40
+KPX Udieresis Aacute -40
+KPX Udieresis Abreve -40
+KPX Udieresis Acircumflex -40
+KPX Udieresis Adieresis -40
+KPX Udieresis Agrave -40
+KPX Udieresis Amacron -40
+KPX Udieresis Aogonek -40
+KPX Udieresis Aring -40
+KPX Udieresis Atilde -40
+KPX Ugrave A -40
+KPX Ugrave Aacute -40
+KPX Ugrave Abreve -40
+KPX Ugrave Acircumflex -40
+KPX Ugrave Adieresis -40
+KPX Ugrave Agrave -40
+KPX Ugrave Amacron -40
+KPX Ugrave Aogonek -40
+KPX Ugrave Aring -40
+KPX Ugrave Atilde -40
+KPX Uhungarumlaut A -40
+KPX Uhungarumlaut Aacute -40
+KPX Uhungarumlaut Abreve -40
+KPX Uhungarumlaut Acircumflex -40
+KPX Uhungarumlaut Adieresis -40
+KPX Uhungarumlaut Agrave -40
+KPX Uhungarumlaut Amacron -40
+KPX Uhungarumlaut Aogonek -40
+KPX Uhungarumlaut Aring -40
+KPX Uhungarumlaut Atilde -40
+KPX Umacron A -40
+KPX Umacron Aacute -40
+KPX Umacron Abreve -40
+KPX Umacron Acircumflex -40
+KPX Umacron Adieresis -40
+KPX Umacron Agrave -40
+KPX Umacron Amacron -40
+KPX Umacron Aogonek -40
+KPX Umacron Aring -40
+KPX Umacron Atilde -40
+KPX Uogonek A -40
+KPX Uogonek Aacute -40
+KPX Uogonek Abreve -40
+KPX Uogonek Acircumflex -40
+KPX Uogonek Adieresis -40
+KPX Uogonek Agrave -40
+KPX Uogonek Amacron -40
+KPX Uogonek Aogonek -40
+KPX Uogonek Aring -40
+KPX Uogonek Atilde -40
+KPX Uring A -40
+KPX Uring Aacute -40
+KPX Uring Abreve -40
+KPX Uring Acircumflex -40
+KPX Uring Adieresis -40
+KPX Uring Agrave -40
+KPX Uring Amacron -40
+KPX Uring Aogonek -40
+KPX Uring Aring -40
+KPX Uring Atilde -40
+KPX V A -135
+KPX V Aacute -135
+KPX V Abreve -135
+KPX V Acircumflex -135
+KPX V Adieresis -135
+KPX V Agrave -135
+KPX V Amacron -135
+KPX V Aogonek -135
+KPX V Aring -135
+KPX V Atilde -135
+KPX V G -15
+KPX V Gbreve -15
+KPX V Gcommaaccent -15
+KPX V O -40
+KPX V Oacute -40
+KPX V Ocircumflex -40
+KPX V Odieresis -40
+KPX V Ograve -40
+KPX V Ohungarumlaut -40
+KPX V Omacron -40
+KPX V Oslash -40
+KPX V Otilde -40
+KPX V a -111
+KPX V aacute -111
+KPX V abreve -111
+KPX V acircumflex -71
+KPX V adieresis -71
+KPX V agrave -71
+KPX V amacron -71
+KPX V aogonek -111
+KPX V aring -111
+KPX V atilde -71
+KPX V colon -74
+KPX V comma -129
+KPX V e -111
+KPX V eacute -111
+KPX V ecaron -71
+KPX V ecircumflex -71
+KPX V edieresis -71
+KPX V edotaccent -111
+KPX V egrave -71
+KPX V emacron -71
+KPX V eogonek -111
+KPX V hyphen -100
+KPX V i -60
+KPX V iacute -60
+KPX V icircumflex -20
+KPX V idieresis -20
+KPX V igrave -20
+KPX V imacron -20
+KPX V iogonek -60
+KPX V o -129
+KPX V oacute -129
+KPX V ocircumflex -129
+KPX V odieresis -89
+KPX V ograve -89
+KPX V ohungarumlaut -129
+KPX V omacron -89
+KPX V oslash -129
+KPX V otilde -89
+KPX V period -129
+KPX V semicolon -74
+KPX V u -75
+KPX V uacute -75
+KPX V ucircumflex -75
+KPX V udieresis -75
+KPX V ugrave -75
+KPX V uhungarumlaut -75
+KPX V umacron -75
+KPX V uogonek -75
+KPX V uring -75
+KPX W A -120
+KPX W Aacute -120
+KPX W Abreve -120
+KPX W Acircumflex -120
+KPX W Adieresis -120
+KPX W Agrave -120
+KPX W Amacron -120
+KPX W Aogonek -120
+KPX W Aring -120
+KPX W Atilde -120
+KPX W O -10
+KPX W Oacute -10
+KPX W Ocircumflex -10
+KPX W Odieresis -10
+KPX W Ograve -10
+KPX W Ohungarumlaut -10
+KPX W Omacron -10
+KPX W Oslash -10
+KPX W Otilde -10
+KPX W a -80
+KPX W aacute -80
+KPX W abreve -80
+KPX W acircumflex -80
+KPX W adieresis -80
+KPX W agrave -80
+KPX W amacron -80
+KPX W aogonek -80
+KPX W aring -80
+KPX W atilde -80
+KPX W colon -37
+KPX W comma -92
+KPX W e -80
+KPX W eacute -80
+KPX W ecaron -80
+KPX W ecircumflex -80
+KPX W edieresis -40
+KPX W edotaccent -80
+KPX W egrave -40
+KPX W emacron -40
+KPX W eogonek -80
+KPX W hyphen -65
+KPX W i -40
+KPX W iacute -40
+KPX W iogonek -40
+KPX W o -80
+KPX W oacute -80
+KPX W ocircumflex -80
+KPX W odieresis -80
+KPX W ograve -80
+KPX W ohungarumlaut -80
+KPX W omacron -80
+KPX W oslash -80
+KPX W otilde -80
+KPX W period -92
+KPX W semicolon -37
+KPX W u -50
+KPX W uacute -50
+KPX W ucircumflex -50
+KPX W udieresis -50
+KPX W ugrave -50
+KPX W uhungarumlaut -50
+KPX W umacron -50
+KPX W uogonek -50
+KPX W uring -50
+KPX W y -73
+KPX W yacute -73
+KPX W ydieresis -73
+KPX Y A -120
+KPX Y Aacute -120
+KPX Y Abreve -120
+KPX Y Acircumflex -120
+KPX Y Adieresis -120
+KPX Y Agrave -120
+KPX Y Amacron -120
+KPX Y Aogonek -120
+KPX Y Aring -120
+KPX Y Atilde -120
+KPX Y O -30
+KPX Y Oacute -30
+KPX Y Ocircumflex -30
+KPX Y Odieresis -30
+KPX Y Ograve -30
+KPX Y Ohungarumlaut -30
+KPX Y Omacron -30
+KPX Y Oslash -30
+KPX Y Otilde -30
+KPX Y a -100
+KPX Y aacute -100
+KPX Y abreve -100
+KPX Y acircumflex -100
+KPX Y adieresis -60
+KPX Y agrave -60
+KPX Y amacron -60
+KPX Y aogonek -100
+KPX Y aring -100
+KPX Y atilde -60
+KPX Y colon -92
+KPX Y comma -129
+KPX Y e -100
+KPX Y eacute -100
+KPX Y ecaron -100
+KPX Y ecircumflex -100
+KPX Y edieresis -60
+KPX Y edotaccent -100
+KPX Y egrave -60
+KPX Y emacron -60
+KPX Y eogonek -100
+KPX Y hyphen -111
+KPX Y i -55
+KPX Y iacute -55
+KPX Y iogonek -55
+KPX Y o -110
+KPX Y oacute -110
+KPX Y ocircumflex -110
+KPX Y odieresis -70
+KPX Y ograve -70
+KPX Y ohungarumlaut -110
+KPX Y omacron -70
+KPX Y oslash -110
+KPX Y otilde -70
+KPX Y period -129
+KPX Y semicolon -92
+KPX Y u -111
+KPX Y uacute -111
+KPX Y ucircumflex -111
+KPX Y udieresis -71
+KPX Y ugrave -71
+KPX Y uhungarumlaut -111
+KPX Y umacron -71
+KPX Y uogonek -111
+KPX Y uring -111
+KPX Yacute A -120
+KPX Yacute Aacute -120
+KPX Yacute Abreve -120
+KPX Yacute Acircumflex -120
+KPX Yacute Adieresis -120
+KPX Yacute Agrave -120
+KPX Yacute Amacron -120
+KPX Yacute Aogonek -120
+KPX Yacute Aring -120
+KPX Yacute Atilde -120
+KPX Yacute O -30
+KPX Yacute Oacute -30
+KPX Yacute Ocircumflex -30
+KPX Yacute Odieresis -30
+KPX Yacute Ograve -30
+KPX Yacute Ohungarumlaut -30
+KPX Yacute Omacron -30
+KPX Yacute Oslash -30
+KPX Yacute Otilde -30
+KPX Yacute a -100
+KPX Yacute aacute -100
+KPX Yacute abreve -100
+KPX Yacute acircumflex -100
+KPX Yacute adieresis -60
+KPX Yacute agrave -60
+KPX Yacute amacron -60
+KPX Yacute aogonek -100
+KPX Yacute aring -100
+KPX Yacute atilde -60
+KPX Yacute colon -92
+KPX Yacute comma -129
+KPX Yacute e -100
+KPX Yacute eacute -100
+KPX Yacute ecaron -100
+KPX Yacute ecircumflex -100
+KPX Yacute edieresis -60
+KPX Yacute edotaccent -100
+KPX Yacute egrave -60
+KPX Yacute emacron -60
+KPX Yacute eogonek -100
+KPX Yacute hyphen -111
+KPX Yacute i -55
+KPX Yacute iacute -55
+KPX Yacute iogonek -55
+KPX Yacute o -110
+KPX Yacute oacute -110
+KPX Yacute ocircumflex -110
+KPX Yacute odieresis -70
+KPX Yacute ograve -70
+KPX Yacute ohungarumlaut -110
+KPX Yacute omacron -70
+KPX Yacute oslash -110
+KPX Yacute otilde -70
+KPX Yacute period -129
+KPX Yacute semicolon -92
+KPX Yacute u -111
+KPX Yacute uacute -111
+KPX Yacute ucircumflex -111
+KPX Yacute udieresis -71
+KPX Yacute ugrave -71
+KPX Yacute uhungarumlaut -111
+KPX Yacute umacron -71
+KPX Yacute uogonek -111
+KPX Yacute uring -111
+KPX Ydieresis A -120
+KPX Ydieresis Aacute -120
+KPX Ydieresis Abreve -120
+KPX Ydieresis Acircumflex -120
+KPX Ydieresis Adieresis -120
+KPX Ydieresis Agrave -120
+KPX Ydieresis Amacron -120
+KPX Ydieresis Aogonek -120
+KPX Ydieresis Aring -120
+KPX Ydieresis Atilde -120
+KPX Ydieresis O -30
+KPX Ydieresis Oacute -30
+KPX Ydieresis Ocircumflex -30
+KPX Ydieresis Odieresis -30
+KPX Ydieresis Ograve -30
+KPX Ydieresis Ohungarumlaut -30
+KPX Ydieresis Omacron -30
+KPX Ydieresis Oslash -30
+KPX Ydieresis Otilde -30
+KPX Ydieresis a -100
+KPX Ydieresis aacute -100
+KPX Ydieresis abreve -100
+KPX Ydieresis acircumflex -100
+KPX Ydieresis adieresis -60
+KPX Ydieresis agrave -60
+KPX Ydieresis amacron -60
+KPX Ydieresis aogonek -100
+KPX Ydieresis aring -100
+KPX Ydieresis atilde -100
+KPX Ydieresis colon -92
+KPX Ydieresis comma -129
+KPX Ydieresis e -100
+KPX Ydieresis eacute -100
+KPX Ydieresis ecaron -100
+KPX Ydieresis ecircumflex -100
+KPX Ydieresis edieresis -60
+KPX Ydieresis edotaccent -100
+KPX Ydieresis egrave -60
+KPX Ydieresis emacron -60
+KPX Ydieresis eogonek -100
+KPX Ydieresis hyphen -111
+KPX Ydieresis i -55
+KPX Ydieresis iacute -55
+KPX Ydieresis iogonek -55
+KPX Ydieresis o -110
+KPX Ydieresis oacute -110
+KPX Ydieresis ocircumflex -110
+KPX Ydieresis odieresis -70
+KPX Ydieresis ograve -70
+KPX Ydieresis ohungarumlaut -110
+KPX Ydieresis omacron -70
+KPX Ydieresis oslash -110
+KPX Ydieresis otilde -70
+KPX Ydieresis period -129
+KPX Ydieresis semicolon -92
+KPX Ydieresis u -111
+KPX Ydieresis uacute -111
+KPX Ydieresis ucircumflex -111
+KPX Ydieresis udieresis -71
+KPX Ydieresis ugrave -71
+KPX Ydieresis uhungarumlaut -111
+KPX Ydieresis umacron -71
+KPX Ydieresis uogonek -111
+KPX Ydieresis uring -111
+KPX a v -20
+KPX a w -15
+KPX aacute v -20
+KPX aacute w -15
+KPX abreve v -20
+KPX abreve w -15
+KPX acircumflex v -20
+KPX acircumflex w -15
+KPX adieresis v -20
+KPX adieresis w -15
+KPX agrave v -20
+KPX agrave w -15
+KPX amacron v -20
+KPX amacron w -15
+KPX aogonek v -20
+KPX aogonek w -15
+KPX aring v -20
+KPX aring w -15
+KPX atilde v -20
+KPX atilde w -15
+KPX b period -40
+KPX b u -20
+KPX b uacute -20
+KPX b ucircumflex -20
+KPX b udieresis -20
+KPX b ugrave -20
+KPX b uhungarumlaut -20
+KPX b umacron -20
+KPX b uogonek -20
+KPX b uring -20
+KPX b v -15
+KPX c y -15
+KPX c yacute -15
+KPX c ydieresis -15
+KPX cacute y -15
+KPX cacute yacute -15
+KPX cacute ydieresis -15
+KPX ccaron y -15
+KPX ccaron yacute -15
+KPX ccaron ydieresis -15
+KPX ccedilla y -15
+KPX ccedilla yacute -15
+KPX ccedilla ydieresis -15
+KPX comma quotedblright -70
+KPX comma quoteright -70
+KPX e g -15
+KPX e gbreve -15
+KPX e gcommaaccent -15
+KPX e v -25
+KPX e w -25
+KPX e x -15
+KPX e y -15
+KPX e yacute -15
+KPX e ydieresis -15
+KPX eacute g -15
+KPX eacute gbreve -15
+KPX eacute gcommaaccent -15
+KPX eacute v -25
+KPX eacute w -25
+KPX eacute x -15
+KPX eacute y -15
+KPX eacute yacute -15
+KPX eacute ydieresis -15
+KPX ecaron g -15
+KPX ecaron gbreve -15
+KPX ecaron gcommaaccent -15
+KPX ecaron v -25
+KPX ecaron w -25
+KPX ecaron x -15
+KPX ecaron y -15
+KPX ecaron yacute -15
+KPX ecaron ydieresis -15
+KPX ecircumflex g -15
+KPX ecircumflex gbreve -15
+KPX ecircumflex gcommaaccent -15
+KPX ecircumflex v -25
+KPX ecircumflex w -25
+KPX ecircumflex x -15
+KPX ecircumflex y -15
+KPX ecircumflex yacute -15
+KPX ecircumflex ydieresis -15
+KPX edieresis g -15
+KPX edieresis gbreve -15
+KPX edieresis gcommaaccent -15
+KPX edieresis v -25
+KPX edieresis w -25
+KPX edieresis x -15
+KPX edieresis y -15
+KPX edieresis yacute -15
+KPX edieresis ydieresis -15
+KPX edotaccent g -15
+KPX edotaccent gbreve -15
+KPX edotaccent gcommaaccent -15
+KPX edotaccent v -25
+KPX edotaccent w -25
+KPX edotaccent x -15
+KPX edotaccent y -15
+KPX edotaccent yacute -15
+KPX edotaccent ydieresis -15
+KPX egrave g -15
+KPX egrave gbreve -15
+KPX egrave gcommaaccent -15
+KPX egrave v -25
+KPX egrave w -25
+KPX egrave x -15
+KPX egrave y -15
+KPX egrave yacute -15
+KPX egrave ydieresis -15
+KPX emacron g -15
+KPX emacron gbreve -15
+KPX emacron gcommaaccent -15
+KPX emacron v -25
+KPX emacron w -25
+KPX emacron x -15
+KPX emacron y -15
+KPX emacron yacute -15
+KPX emacron ydieresis -15
+KPX eogonek g -15
+KPX eogonek gbreve -15
+KPX eogonek gcommaaccent -15
+KPX eogonek v -25
+KPX eogonek w -25
+KPX eogonek x -15
+KPX eogonek y -15
+KPX eogonek yacute -15
+KPX eogonek ydieresis -15
+KPX f a -10
+KPX f aacute -10
+KPX f abreve -10
+KPX f acircumflex -10
+KPX f adieresis -10
+KPX f agrave -10
+KPX f amacron -10
+KPX f aogonek -10
+KPX f aring -10
+KPX f atilde -10
+KPX f dotlessi -50
+KPX f f -25
+KPX f i -20
+KPX f iacute -20
+KPX f quoteright 55
+KPX g a -5
+KPX g aacute -5
+KPX g abreve -5
+KPX g acircumflex -5
+KPX g adieresis -5
+KPX g agrave -5
+KPX g amacron -5
+KPX g aogonek -5
+KPX g aring -5
+KPX g atilde -5
+KPX gbreve a -5
+KPX gbreve aacute -5
+KPX gbreve abreve -5
+KPX gbreve acircumflex -5
+KPX gbreve adieresis -5
+KPX gbreve agrave -5
+KPX gbreve amacron -5
+KPX gbreve aogonek -5
+KPX gbreve aring -5
+KPX gbreve atilde -5
+KPX gcommaaccent a -5
+KPX gcommaaccent aacute -5
+KPX gcommaaccent abreve -5
+KPX gcommaaccent acircumflex -5
+KPX gcommaaccent adieresis -5
+KPX gcommaaccent agrave -5
+KPX gcommaaccent amacron -5
+KPX gcommaaccent aogonek -5
+KPX gcommaaccent aring -5
+KPX gcommaaccent atilde -5
+KPX h y -5
+KPX h yacute -5
+KPX h ydieresis -5
+KPX i v -25
+KPX iacute v -25
+KPX icircumflex v -25
+KPX idieresis v -25
+KPX igrave v -25
+KPX imacron v -25
+KPX iogonek v -25
+KPX k e -10
+KPX k eacute -10
+KPX k ecaron -10
+KPX k ecircumflex -10
+KPX k edieresis -10
+KPX k edotaccent -10
+KPX k egrave -10
+KPX k emacron -10
+KPX k eogonek -10
+KPX k o -10
+KPX k oacute -10
+KPX k ocircumflex -10
+KPX k odieresis -10
+KPX k ograve -10
+KPX k ohungarumlaut -10
+KPX k omacron -10
+KPX k oslash -10
+KPX k otilde -10
+KPX k y -15
+KPX k yacute -15
+KPX k ydieresis -15
+KPX kcommaaccent e -10
+KPX kcommaaccent eacute -10
+KPX kcommaaccent ecaron -10
+KPX kcommaaccent ecircumflex -10
+KPX kcommaaccent edieresis -10
+KPX kcommaaccent edotaccent -10
+KPX kcommaaccent egrave -10
+KPX kcommaaccent emacron -10
+KPX kcommaaccent eogonek -10
+KPX kcommaaccent o -10
+KPX kcommaaccent oacute -10
+KPX kcommaaccent ocircumflex -10
+KPX kcommaaccent odieresis -10
+KPX kcommaaccent ograve -10
+KPX kcommaaccent ohungarumlaut -10
+KPX kcommaaccent omacron -10
+KPX kcommaaccent oslash -10
+KPX kcommaaccent otilde -10
+KPX kcommaaccent y -15
+KPX kcommaaccent yacute -15
+KPX kcommaaccent ydieresis -15
+KPX l w -10
+KPX lacute w -10
+KPX lcommaaccent w -10
+KPX lslash w -10
+KPX n v -40
+KPX n y -15
+KPX n yacute -15
+KPX n ydieresis -15
+KPX nacute v -40
+KPX nacute y -15
+KPX nacute yacute -15
+KPX nacute ydieresis -15
+KPX ncaron v -40
+KPX ncaron y -15
+KPX ncaron yacute -15
+KPX ncaron ydieresis -15
+KPX ncommaaccent v -40
+KPX ncommaaccent y -15
+KPX ncommaaccent yacute -15
+KPX ncommaaccent ydieresis -15
+KPX ntilde v -40
+KPX ntilde y -15
+KPX ntilde yacute -15
+KPX ntilde ydieresis -15
+KPX o v -15
+KPX o w -25
+KPX o y -10
+KPX o yacute -10
+KPX o ydieresis -10
+KPX oacute v -15
+KPX oacute w -25
+KPX oacute y -10
+KPX oacute yacute -10
+KPX oacute ydieresis -10
+KPX ocircumflex v -15
+KPX ocircumflex w -25
+KPX ocircumflex y -10
+KPX ocircumflex yacute -10
+KPX ocircumflex ydieresis -10
+KPX odieresis v -15
+KPX odieresis w -25
+KPX odieresis y -10
+KPX odieresis yacute -10
+KPX odieresis ydieresis -10
+KPX ograve v -15
+KPX ograve w -25
+KPX ograve y -10
+KPX ograve yacute -10
+KPX ograve ydieresis -10
+KPX ohungarumlaut v -15
+KPX ohungarumlaut w -25
+KPX ohungarumlaut y -10
+KPX ohungarumlaut yacute -10
+KPX ohungarumlaut ydieresis -10
+KPX omacron v -15
+KPX omacron w -25
+KPX omacron y -10
+KPX omacron yacute -10
+KPX omacron ydieresis -10
+KPX oslash v -15
+KPX oslash w -25
+KPX oslash y -10
+KPX oslash yacute -10
+KPX oslash ydieresis -10
+KPX otilde v -15
+KPX otilde w -25
+KPX otilde y -10
+KPX otilde yacute -10
+KPX otilde ydieresis -10
+KPX p y -10
+KPX p yacute -10
+KPX p ydieresis -10
+KPX period quotedblright -70
+KPX period quoteright -70
+KPX quotedblleft A -80
+KPX quotedblleft Aacute -80
+KPX quotedblleft Abreve -80
+KPX quotedblleft Acircumflex -80
+KPX quotedblleft Adieresis -80
+KPX quotedblleft Agrave -80
+KPX quotedblleft Amacron -80
+KPX quotedblleft Aogonek -80
+KPX quotedblleft Aring -80
+KPX quotedblleft Atilde -80
+KPX quoteleft A -80
+KPX quoteleft Aacute -80
+KPX quoteleft Abreve -80
+KPX quoteleft Acircumflex -80
+KPX quoteleft Adieresis -80
+KPX quoteleft Agrave -80
+KPX quoteleft Amacron -80
+KPX quoteleft Aogonek -80
+KPX quoteleft Aring -80
+KPX quoteleft Atilde -80
+KPX quoteleft quoteleft -74
+KPX quoteright d -50
+KPX quoteright dcroat -50
+KPX quoteright l -10
+KPX quoteright lacute -10
+KPX quoteright lcommaaccent -10
+KPX quoteright lslash -10
+KPX quoteright quoteright -74
+KPX quoteright r -50
+KPX quoteright racute -50
+KPX quoteright rcaron -50
+KPX quoteright rcommaaccent -50
+KPX quoteright s -55
+KPX quoteright sacute -55
+KPX quoteright scaron -55
+KPX quoteright scedilla -55
+KPX quoteright scommaaccent -55
+KPX quoteright space -74
+KPX quoteright t -18
+KPX quoteright tcommaaccent -18
+KPX quoteright v -50
+KPX r comma -40
+KPX r g -18
+KPX r gbreve -18
+KPX r gcommaaccent -18
+KPX r hyphen -20
+KPX r period -55
+KPX racute comma -40
+KPX racute g -18
+KPX racute gbreve -18
+KPX racute gcommaaccent -18
+KPX racute hyphen -20
+KPX racute period -55
+KPX rcaron comma -40
+KPX rcaron g -18
+KPX rcaron gbreve -18
+KPX rcaron gcommaaccent -18
+KPX rcaron hyphen -20
+KPX rcaron period -55
+KPX rcommaaccent comma -40
+KPX rcommaaccent g -18
+KPX rcommaaccent gbreve -18
+KPX rcommaaccent gcommaaccent -18
+KPX rcommaaccent hyphen -20
+KPX rcommaaccent period -55
+KPX space A -55
+KPX space Aacute -55
+KPX space Abreve -55
+KPX space Acircumflex -55
+KPX space Adieresis -55
+KPX space Agrave -55
+KPX space Amacron -55
+KPX space Aogonek -55
+KPX space Aring -55
+KPX space Atilde -55
+KPX space T -18
+KPX space Tcaron -18
+KPX space Tcommaaccent -18
+KPX space V -50
+KPX space W -30
+KPX space Y -90
+KPX space Yacute -90
+KPX space Ydieresis -90
+KPX v a -25
+KPX v aacute -25
+KPX v abreve -25
+KPX v acircumflex -25
+KPX v adieresis -25
+KPX v agrave -25
+KPX v amacron -25
+KPX v aogonek -25
+KPX v aring -25
+KPX v atilde -25
+KPX v comma -65
+KPX v e -15
+KPX v eacute -15
+KPX v ecaron -15
+KPX v ecircumflex -15
+KPX v edieresis -15
+KPX v edotaccent -15
+KPX v egrave -15
+KPX v emacron -15
+KPX v eogonek -15
+KPX v o -20
+KPX v oacute -20
+KPX v ocircumflex -20
+KPX v odieresis -20
+KPX v ograve -20
+KPX v ohungarumlaut -20
+KPX v omacron -20
+KPX v oslash -20
+KPX v otilde -20
+KPX v period -65
+KPX w a -10
+KPX w aacute -10
+KPX w abreve -10
+KPX w acircumflex -10
+KPX w adieresis -10
+KPX w agrave -10
+KPX w amacron -10
+KPX w aogonek -10
+KPX w aring -10
+KPX w atilde -10
+KPX w comma -65
+KPX w o -10
+KPX w oacute -10
+KPX w ocircumflex -10
+KPX w odieresis -10
+KPX w ograve -10
+KPX w ohungarumlaut -10
+KPX w omacron -10
+KPX w oslash -10
+KPX w otilde -10
+KPX w period -65
+KPX x e -15
+KPX x eacute -15
+KPX x ecaron -15
+KPX x ecircumflex -15
+KPX x edieresis -15
+KPX x edotaccent -15
+KPX x egrave -15
+KPX x emacron -15
+KPX x eogonek -15
+KPX y comma -65
+KPX y period -65
+KPX yacute comma -65
+KPX yacute period -65
+KPX ydieresis comma -65
+KPX ydieresis period -65
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/program/libraries/afm/ZapfDingbats.afm b/program/libraries/afm/ZapfDingbats.afm
new file mode 100644 (file)
index 0000000..b274505
--- /dev/null
@@ -0,0 +1,225 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1988, 1989, 1997 Adobe Systems Incorporated. All Rights Reserved.
+Comment Creation Date: Thu May  1 15:14:13 1997
+Comment UniqueID 43082
+Comment VMusage 45775 55535
+FontName ZapfDingbats
+FullName ITC Zapf Dingbats
+FamilyName ZapfDingbats
+Weight Medium
+ItalicAngle 0
+IsFixedPitch false
+CharacterSet Special
+FontBBox -1 -143 981 820 
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1988, 1989, 1997 Adobe Systems Incorporated. All Rights Reserved.ITC Zapf Dingbats is a registered trademark of International Typeface Corporation.
+EncodingScheme FontSpecific
+StdHW 28
+StdVW 90
+StartCharMetrics 202
+C 32 ; WX 278 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 974 ; N a1 ; B 35 72 939 621 ;
+C 34 ; WX 961 ; N a2 ; B 35 81 927 611 ;
+C 35 ; WX 974 ; N a202 ; B 35 72 939 621 ;
+C 36 ; WX 980 ; N a3 ; B 35 0 945 692 ;
+C 37 ; WX 719 ; N a4 ; B 34 139 685 566 ;
+C 38 ; WX 789 ; N a5 ; B 35 -14 755 705 ;
+C 39 ; WX 790 ; N a119 ; B 35 -14 755 705 ;
+C 40 ; WX 791 ; N a118 ; B 35 -13 761 705 ;
+C 41 ; WX 690 ; N a117 ; B 34 138 655 553 ;
+C 42 ; WX 960 ; N a11 ; B 35 123 925 568 ;
+C 43 ; WX 939 ; N a12 ; B 35 134 904 559 ;
+C 44 ; WX 549 ; N a13 ; B 29 -11 516 705 ;
+C 45 ; WX 855 ; N a14 ; B 34 59 820 632 ;
+C 46 ; WX 911 ; N a15 ; B 35 50 876 642 ;
+C 47 ; WX 933 ; N a16 ; B 35 139 899 550 ;
+C 48 ; WX 911 ; N a105 ; B 35 50 876 642 ;
+C 49 ; WX 945 ; N a17 ; B 35 139 909 553 ;
+C 50 ; WX 974 ; N a18 ; B 35 104 938 587 ;
+C 51 ; WX 755 ; N a19 ; B 34 -13 721 705 ;
+C 52 ; WX 846 ; N a20 ; B 36 -14 811 705 ;
+C 53 ; WX 762 ; N a21 ; B 35 0 727 692 ;
+C 54 ; WX 761 ; N a22 ; B 35 0 727 692 ;
+C 55 ; WX 571 ; N a23 ; B -1 -68 571 661 ;
+C 56 ; WX 677 ; N a24 ; B 36 -13 642 705 ;
+C 57 ; WX 763 ; N a25 ; B 35 0 728 692 ;
+C 58 ; WX 760 ; N a26 ; B 35 0 726 692 ;
+C 59 ; WX 759 ; N a27 ; B 35 0 725 692 ;
+C 60 ; WX 754 ; N a28 ; B 35 0 720 692 ;
+C 61 ; WX 494 ; N a6 ; B 35 0 460 692 ;
+C 62 ; WX 552 ; N a7 ; B 35 0 517 692 ;
+C 63 ; WX 537 ; N a8 ; B 35 0 503 692 ;
+C 64 ; WX 577 ; N a9 ; B 35 96 542 596 ;
+C 65 ; WX 692 ; N a10 ; B 35 -14 657 705 ;
+C 66 ; WX 786 ; N a29 ; B 35 -14 751 705 ;
+C 67 ; WX 788 ; N a30 ; B 35 -14 752 705 ;
+C 68 ; WX 788 ; N a31 ; B 35 -14 753 705 ;
+C 69 ; WX 790 ; N a32 ; B 35 -14 756 705 ;
+C 70 ; WX 793 ; N a33 ; B 35 -13 759 705 ;
+C 71 ; WX 794 ; N a34 ; B 35 -13 759 705 ;
+C 72 ; WX 816 ; N a35 ; B 35 -14 782 705 ;
+C 73 ; WX 823 ; N a36 ; B 35 -14 787 705 ;
+C 74 ; WX 789 ; N a37 ; B 35 -14 754 705 ;
+C 75 ; WX 841 ; N a38 ; B 35 -14 807 705 ;
+C 76 ; WX 823 ; N a39 ; B 35 -14 789 705 ;
+C 77 ; WX 833 ; N a40 ; B 35 -14 798 705 ;
+C 78 ; WX 816 ; N a41 ; B 35 -13 782 705 ;
+C 79 ; WX 831 ; N a42 ; B 35 -14 796 705 ;
+C 80 ; WX 923 ; N a43 ; B 35 -14 888 705 ;
+C 81 ; WX 744 ; N a44 ; B 35 0 710 692 ;
+C 82 ; WX 723 ; N a45 ; B 35 0 688 692 ;
+C 83 ; WX 749 ; N a46 ; B 35 0 714 692 ;
+C 84 ; WX 790 ; N a47 ; B 34 -14 756 705 ;
+C 85 ; WX 792 ; N a48 ; B 35 -14 758 705 ;
+C 86 ; WX 695 ; N a49 ; B 35 -14 661 706 ;
+C 87 ; WX 776 ; N a50 ; B 35 -6 741 699 ;
+C 88 ; WX 768 ; N a51 ; B 35 -7 734 699 ;
+C 89 ; WX 792 ; N a52 ; B 35 -14 757 705 ;
+C 90 ; WX 759 ; N a53 ; B 35 0 725 692 ;
+C 91 ; WX 707 ; N a54 ; B 35 -13 672 704 ;
+C 92 ; WX 708 ; N a55 ; B 35 -14 672 705 ;
+C 93 ; WX 682 ; N a56 ; B 35 -14 647 705 ;
+C 94 ; WX 701 ; N a57 ; B 35 -14 666 705 ;
+C 95 ; WX 826 ; N a58 ; B 35 -14 791 705 ;
+C 96 ; WX 815 ; N a59 ; B 35 -14 780 705 ;
+C 97 ; WX 789 ; N a60 ; B 35 -14 754 705 ;
+C 98 ; WX 789 ; N a61 ; B 35 -14 754 705 ;
+C 99 ; WX 707 ; N a62 ; B 34 -14 673 705 ;
+C 100 ; WX 687 ; N a63 ; B 36 0 651 692 ;
+C 101 ; WX 696 ; N a64 ; B 35 0 661 691 ;
+C 102 ; WX 689 ; N a65 ; B 35 0 655 692 ;
+C 103 ; WX 786 ; N a66 ; B 34 -14 751 705 ;
+C 104 ; WX 787 ; N a67 ; B 35 -14 752 705 ;
+C 105 ; WX 713 ; N a68 ; B 35 -14 678 705 ;
+C 106 ; WX 791 ; N a69 ; B 35 -14 756 705 ;
+C 107 ; WX 785 ; N a70 ; B 36 -14 751 705 ;
+C 108 ; WX 791 ; N a71 ; B 35 -14 757 705 ;
+C 109 ; WX 873 ; N a72 ; B 35 -14 838 705 ;
+C 110 ; WX 761 ; N a73 ; B 35 0 726 692 ;
+C 111 ; WX 762 ; N a74 ; B 35 0 727 692 ;
+C 112 ; WX 762 ; N a203 ; B 35 0 727 692 ;
+C 113 ; WX 759 ; N a75 ; B 35 0 725 692 ;
+C 114 ; WX 759 ; N a204 ; B 35 0 725 692 ;
+C 115 ; WX 892 ; N a76 ; B 35 0 858 705 ;
+C 116 ; WX 892 ; N a77 ; B 35 -14 858 692 ;
+C 117 ; WX 788 ; N a78 ; B 35 -14 754 705 ;
+C 118 ; WX 784 ; N a79 ; B 35 -14 749 705 ;
+C 119 ; WX 438 ; N a81 ; B 35 -14 403 705 ;
+C 120 ; WX 138 ; N a82 ; B 35 0 104 692 ;
+C 121 ; WX 277 ; N a83 ; B 35 0 242 692 ;
+C 122 ; WX 415 ; N a84 ; B 35 0 380 692 ;
+C 123 ; WX 392 ; N a97 ; B 35 263 357 705 ;
+C 124 ; WX 392 ; N a98 ; B 34 263 357 705 ;
+C 125 ; WX 668 ; N a99 ; B 35 263 633 705 ;
+C 126 ; WX 668 ; N a100 ; B 36 263 634 705 ;
+C 128 ; WX 390 ; N a89 ; B 35 -14 356 705 ;
+C 129 ; WX 390 ; N a90 ; B 35 -14 355 705 ;
+C 130 ; WX 317 ; N a93 ; B 35 0 283 692 ;
+C 131 ; WX 317 ; N a94 ; B 35 0 283 692 ;
+C 132 ; WX 276 ; N a91 ; B 35 0 242 692 ;
+C 133 ; WX 276 ; N a92 ; B 35 0 242 692 ;
+C 134 ; WX 509 ; N a205 ; B 35 0 475 692 ;
+C 135 ; WX 509 ; N a85 ; B 35 0 475 692 ;
+C 136 ; WX 410 ; N a206 ; B 35 0 375 692 ;
+C 137 ; WX 410 ; N a86 ; B 35 0 375 692 ;
+C 138 ; WX 234 ; N a87 ; B 35 -14 199 705 ;
+C 139 ; WX 234 ; N a88 ; B 35 -14 199 705 ;
+C 140 ; WX 334 ; N a95 ; B 35 0 299 692 ;
+C 141 ; WX 334 ; N a96 ; B 35 0 299 692 ;
+C 161 ; WX 732 ; N a101 ; B 35 -143 697 806 ;
+C 162 ; WX 544 ; N a102 ; B 56 -14 488 706 ;
+C 163 ; WX 544 ; N a103 ; B 34 -14 508 705 ;
+C 164 ; WX 910 ; N a104 ; B 35 40 875 651 ;
+C 165 ; WX 667 ; N a106 ; B 35 -14 633 705 ;
+C 166 ; WX 760 ; N a107 ; B 35 -14 726 705 ;
+C 167 ; WX 760 ; N a108 ; B 0 121 758 569 ;
+C 168 ; WX 776 ; N a112 ; B 35 0 741 705 ;
+C 169 ; WX 595 ; N a111 ; B 34 -14 560 705 ;
+C 170 ; WX 694 ; N a110 ; B 35 -14 659 705 ;
+C 171 ; WX 626 ; N a109 ; B 34 0 591 705 ;
+C 172 ; WX 788 ; N a120 ; B 35 -14 754 705 ;
+C 173 ; WX 788 ; N a121 ; B 35 -14 754 705 ;
+C 174 ; WX 788 ; N a122 ; B 35 -14 754 705 ;
+C 175 ; WX 788 ; N a123 ; B 35 -14 754 705 ;
+C 176 ; WX 788 ; N a124 ; B 35 -14 754 705 ;
+C 177 ; WX 788 ; N a125 ; B 35 -14 754 705 ;
+C 178 ; WX 788 ; N a126 ; B 35 -14 754 705 ;
+C 179 ; WX 788 ; N a127 ; B 35 -14 754 705 ;
+C 180 ; WX 788 ; N a128 ; B 35 -14 754 705 ;
+C 181 ; WX 788 ; N a129 ; B 35 -14 754 705 ;
+C 182 ; WX 788 ; N a130 ; B 35 -14 754 705 ;
+C 183 ; WX 788 ; N a131 ; B 35 -14 754 705 ;
+C 184 ; WX 788 ; N a132 ; B 35 -14 754 705 ;
+C 185 ; WX 788 ; N a133 ; B 35 -14 754 705 ;
+C 186 ; WX 788 ; N a134 ; B 35 -14 754 705 ;
+C 187 ; WX 788 ; N a135 ; B 35 -14 754 705 ;
+C 188 ; WX 788 ; N a136 ; B 35 -14 754 705 ;
+C 189 ; WX 788 ; N a137 ; B 35 -14 754 705 ;
+C 190 ; WX 788 ; N a138 ; B 35 -14 754 705 ;
+C 191 ; WX 788 ; N a139 ; B 35 -14 754 705 ;
+C 192 ; WX 788 ; N a140 ; B 35 -14 754 705 ;
+C 193 ; WX 788 ; N a141 ; B 35 -14 754 705 ;
+C 194 ; WX 788 ; N a142 ; B 35 -14 754 705 ;
+C 195 ; WX 788 ; N a143 ; B 35 -14 754 705 ;
+C 196 ; WX 788 ; N a144 ; B 35 -14 754 705 ;
+C 197 ; WX 788 ; N a145 ; B 35 -14 754 705 ;
+C 198 ; WX 788 ; N a146 ; B 35 -14 754 705 ;
+C 199 ; WX 788 ; N a147 ; B 35 -14 754 705 ;
+C 200 ; WX 788 ; N a148 ; B 35 -14 754 705 ;
+C 201 ; WX 788 ; N a149 ; B 35 -14 754 705 ;
+C 202 ; WX 788 ; N a150 ; B 35 -14 754 705 ;
+C 203 ; WX 788 ; N a151 ; B 35 -14 754 705 ;
+C 204 ; WX 788 ; N a152 ; B 35 -14 754 705 ;
+C 205 ; WX 788 ; N a153 ; B 35 -14 754 705 ;
+C 206 ; WX 788 ; N a154 ; B 35 -14 754 705 ;
+C 207 ; WX 788 ; N a155 ; B 35 -14 754 705 ;
+C 208 ; WX 788 ; N a156 ; B 35 -14 754 705 ;
+C 209 ; WX 788 ; N a157 ; B 35 -14 754 705 ;
+C 210 ; WX 788 ; N a158 ; B 35 -14 754 705 ;
+C 211 ; WX 788 ; N a159 ; B 35 -14 754 705 ;
+C 212 ; WX 894 ; N a160 ; B 35 58 860 634 ;
+C 213 ; WX 838 ; N a161 ; B 35 152 803 540 ;
+C 214 ; WX 1016 ; N a163 ; B 34 152 981 540 ;
+C 215 ; WX 458 ; N a164 ; B 35 -127 422 820 ;
+C 216 ; WX 748 ; N a196 ; B 35 94 698 597 ;
+C 217 ; WX 924 ; N a165 ; B 35 140 890 552 ;
+C 218 ; WX 748 ; N a192 ; B 35 94 698 597 ;
+C 219 ; WX 918 ; N a166 ; B 35 166 884 526 ;
+C 220 ; WX 927 ; N a167 ; B 35 32 892 660 ;
+C 221 ; WX 928 ; N a168 ; B 35 129 891 562 ;
+C 222 ; WX 928 ; N a169 ; B 35 128 893 563 ;
+C 223 ; WX 834 ; N a170 ; B 35 155 799 537 ;
+C 224 ; WX 873 ; N a171 ; B 35 93 838 599 ;
+C 225 ; WX 828 ; N a172 ; B 35 104 791 588 ;
+C 226 ; WX 924 ; N a173 ; B 35 98 889 594 ;
+C 227 ; WX 924 ; N a162 ; B 35 98 889 594 ;
+C 228 ; WX 917 ; N a174 ; B 35 0 882 692 ;
+C 229 ; WX 930 ; N a175 ; B 35 84 896 608 ;
+C 230 ; WX 931 ; N a176 ; B 35 84 896 608 ;
+C 231 ; WX 463 ; N a177 ; B 35 -99 429 791 ;
+C 232 ; WX 883 ; N a178 ; B 35 71 848 623 ;
+C 233 ; WX 836 ; N a179 ; B 35 44 802 648 ;
+C 234 ; WX 836 ; N a193 ; B 35 44 802 648 ;
+C 235 ; WX 867 ; N a180 ; B 35 101 832 591 ;
+C 236 ; WX 867 ; N a199 ; B 35 101 832 591 ;
+C 237 ; WX 696 ; N a181 ; B 35 44 661 648 ;
+C 238 ; WX 696 ; N a200 ; B 35 44 661 648 ;
+C 239 ; WX 874 ; N a182 ; B 35 77 840 619 ;
+C 241 ; WX 874 ; N a201 ; B 35 73 840 615 ;
+C 242 ; WX 760 ; N a183 ; B 35 0 725 692 ;
+C 243 ; WX 946 ; N a184 ; B 35 160 911 533 ;
+C 244 ; WX 771 ; N a197 ; B 34 37 736 655 ;
+C 245 ; WX 865 ; N a185 ; B 35 207 830 481 ;
+C 246 ; WX 771 ; N a194 ; B 34 37 736 655 ;
+C 247 ; WX 888 ; N a198 ; B 34 -19 853 712 ;
+C 248 ; WX 967 ; N a186 ; B 35 124 932 568 ;
+C 249 ; WX 888 ; N a195 ; B 34 -19 853 712 ;
+C 250 ; WX 831 ; N a187 ; B 35 113 796 579 ;
+C 251 ; WX 873 ; N a188 ; B 36 118 838 578 ;
+C 252 ; WX 927 ; N a189 ; B 35 150 891 542 ;
+C 253 ; WX 970 ; N a190 ; B 35 76 931 616 ;
+C 254 ; WX 918 ; N a191 ; B 34 99 884 593 ;
+EndCharMetrics
+EndFontMetrics
diff --git a/program/libraries/afm/compile_afm.pl b/program/libraries/afm/compile_afm.pl
new file mode 100644 (file)
index 0000000..efcf5dd
--- /dev/null
@@ -0,0 +1,486 @@
+#!/usr/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/.*\///;
+  $fi{Ascender} = 0;
+  $fi{Descender} = 0;
+  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*#/;
+    $fi{Ascender} = $1 if /^Ascender\s+(-?\d+)/;
+    $fi{Descender} = $1 if /^Descender\s+(-?\d+)/;
+    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}\", \"$$fiR{AfmFullName}\",\n";
+    $info_code .= $i2 . $$fiR{Ascender} . ", " . $$fiR{Descender} . ",\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 = ();
+      my $numKernings = 0;
+      foreach my $unicode2 (sort { $a <=> $b } keys %$kerningInfoR) {
+        my $delta = $$kerningInfoR{$unicode2};
+        $numKernings++;
+        append_escaped_16bit_int(\@kerns, $unicode2);
+        push(@kerns, $delta);
+        $had_kerning = 1;
+      }
+      $kerning_index = append_8bit_subarray($kerning_dataA, $numKernings, @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, $numItems, @newElements) = @_;
+  push(@$aR, 42) if !array_size($aR); # initial dummy value
+  die if $numItems > $#newElements + 1;
+  my $idx = $#{$aR} + 1;
+#print "append_8bit_subarray ", ($#newElements+1), " = (", join(", ", @newElements), ") -> $idx\n";
+  append_escaped_16bit_int($aR, $numItems);
+  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, 0, 0, 0, 0, 0, 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/libraries/afm/glyphlist.txt b/program/libraries/afm/glyphlist.txt
new file mode 100644 (file)
index 0000000..f282db5
--- /dev/null
@@ -0,0 +1,1151 @@
+#
+# Name:          Adobe Glyph List
+# Table version: 1.2
+# Date:          22 Oct 1998
+#
+# Description:
+#
+#   The Adobe Glyph List (AGL) list relates Unicode values (UVs) to glyph
+#   names, and should be used only as described in the document "Unicode and
+#   Glyph Names," at
+#   http://partners.adobe.com/asn/developer/typeforum/unicodegn.html .
+#
+#   The glyph name to UV relation is one to many. 12 glyph names are mapped to
+#   two UVs each; each UV has a separate entry. All other glyph names are
+#   mapped to one UV each.
+#
+#   The Unicode Standard version 2.1 is used for all UVs outside of the Private
+#   Use area, except for 4 entries (see Revision History for 1.2 below).
+#
+#   There are 1051 entries in this list, 171 of which are in the Corporate Use
+#   subarea (CUS). Refer to the document "Unicode Corporate Use Subarea as used
+#   by Adobe Systems," at
+#   http://partners.adobe.com/asn/developer/typeforum/corporateuse.txt
+#   for compatibility decompositions for these characters, and to the document
+#   "Unicode and Glyph Names" for more information the CUS.
+#
+# Format: Semicolon-delimited fields:
+#
+#   (1) Standard UV or CUS UV. (4 uppercase hexadecimal digits)
+#
+#   (2) Glyph name. (upper- and lowercase letters, digits)
+#
+#   (3) Character names: Unicode character names for standard UVs, and
+#       descriptive names for CUS UVs. (uppercase letters, hyphen, space)
+#
+#   (4) [optional] Comment. A comment of "Duplicate" indicates one of two
+#       UVs of a double-mapping. It is the UV that may be given a uni<CODE>
+#       override, or the UV that is in the CUS, as described in the document
+#       "Unicode and Glyph Names."
+#
+#   The entries are sorted by glyph name in increasing ASCII order; entries
+#   with the same glyph name are sorted in decreasing priority order.
+#
+#   Lines starting with "#" are comments; blank lines should be ignored.
+#
+# Revision History:
+#
+#   1.2  [22 Oct 1998]
+#
+#     Some Central European glyph names were remapped and the glyph "dotlessj"
+#     was added. Some entries in the table below have not changed but are
+#     included to provide a complete context for other glyphs that have been
+#     remapped or double-mapped. "-" means that the entry for that UV does not
+#     exist in the AGL.
+#
+#       -------- ---------------------- ---------------- --------------
+#       UV       Character name         AGL 1.1          AGL 1.2
+#                (shortened)            glyph name       glyph name
+#       -------- ---------------------- ---------------- --------------
+#       015E/F   S/s with cedilla       S/scommaaccent   S/scedilla
+#       0162/3   T/t with cedilla       T/tcommaaccent   T/tcommaaccent
+#       0218/9   S/s with comma below   -                S/scommaaccent
+#       021A/B   T/t with comma below   -                T/tcommaaccent
+#       1E9E/F   S/s with comma below   S/scedilla       -
+#       F6C1/2   S/s with cedilla       S/scedilla       S/scedilla
+#       F6BE     dotless j              -                dotlessj
+#       -------- ---------------------- ---------------- --------------
+#
+#     The characters at U+1E9E/F in AGL 1.1, LATIN CAPITAL/SMALL LETTER S WITH
+#     COMMA BELOW, which are proposed new characters (see (b) in the notes for
+#     AGL 1.1 below), have since been reassigned by the Unicode Standard to new
+#     proposed values of U+0218/9. These characters, as well as U+021A/B, LATIN
+#     CAPITAL/SMALL LETTER T WITH COMMA BELOW, are not in the Unicode Standard
+#     2.1.
+#
+#     Entries with the same glyph name are now sorted in decreasing priority
+#     order instead of in increasing UV order.
+#
+#   1.1  [24 Nov 1997]
+#
+#     a. The "Euro" glyph's UV assignment is changed from U+20A0 (EURO-CURRENCY
+#        SIGN) to U+20AC (EURO SIGN). While U+20AC is not defined in the
+#        Unicode Standard 2.0, it has been accepted by the Unicode Technical
+#        Committee for the next version of the Standard; it has not yet passed
+#        the ISO approval process as of 7 November '97.
+#
+#     b. Glyphs "Scedilla" and "scedilla", which were assigned in the Corporate
+#        Use Subarea in AGL 1.0, are now additionally mapped to U+1E9E and
+#        U+1E9F respectively. These two UVs share the same Unicode approval
+#        status as the Euro glyph (see a. above).
+#
+#     c. The "fraction" glyph is now additionally mapped to U+2215, to match
+#        Windows Glyph List 4.
+#
+#     d. The descriptive name for glyph "onefitted", in the Corporate Use
+#        subarea, is changed from "TABULAR DIGIT ONE" to "PROPORTIONAL DIGIT
+#        ONE".
+#
+#   1.0  [17 Jul 1997]  Original version
+#
+0041;A;LATIN CAPITAL LETTER A
+00C6;AE;LATIN CAPITAL LETTER AE
+01FC;AEacute;LATIN CAPITAL LETTER AE WITH ACUTE
+F7E6;AEsmall;LATIN SMALL CAPITAL LETTER AE
+00C1;Aacute;LATIN CAPITAL LETTER A WITH ACUTE
+F7E1;Aacutesmall;LATIN SMALL CAPITAL LETTER A WITH ACUTE
+0102;Abreve;LATIN CAPITAL LETTER A WITH BREVE
+00C2;Acircumflex;LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+F7E2;Acircumflexsmall;LATIN SMALL CAPITAL LETTER A WITH CIRCUMFLEX
+F6C9;Acute;CAPITAL ACUTE ACCENT
+F7B4;Acutesmall;SMALL CAPITAL ACUTE ACCENT
+00C4;Adieresis;LATIN CAPITAL LETTER A WITH DIAERESIS
+F7E4;Adieresissmall;LATIN SMALL CAPITAL LETTER A WITH DIAERESIS
+00C0;Agrave;LATIN CAPITAL LETTER A WITH GRAVE
+F7E0;Agravesmall;LATIN SMALL CAPITAL LETTER A WITH GRAVE
+0391;Alpha;GREEK CAPITAL LETTER ALPHA
+0386;Alphatonos;GREEK CAPITAL LETTER ALPHA WITH TONOS
+0100;Amacron;LATIN CAPITAL LETTER A WITH MACRON
+0104;Aogonek;LATIN CAPITAL LETTER A WITH OGONEK
+00C5;Aring;LATIN CAPITAL LETTER A WITH RING ABOVE
+01FA;Aringacute;LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
+F7E5;Aringsmall;LATIN SMALL CAPITAL LETTER A WITH RING ABOVE
+F761;Asmall;LATIN SMALL CAPITAL LETTER A
+00C3;Atilde;LATIN CAPITAL LETTER A WITH TILDE
+F7E3;Atildesmall;LATIN SMALL CAPITAL LETTER A WITH TILDE
+0042;B;LATIN CAPITAL LETTER B
+0392;Beta;GREEK CAPITAL LETTER BETA
+F6F4;Brevesmall;SMALL CAPITAL BREVE
+F762;Bsmall;LATIN SMALL CAPITAL LETTER B
+0043;C;LATIN CAPITAL LETTER C
+0106;Cacute;LATIN CAPITAL LETTER C WITH ACUTE
+F6CA;Caron;CAPITAL CARON
+F6F5;Caronsmall;SMALL CAPITAL CARON
+010C;Ccaron;LATIN CAPITAL LETTER C WITH CARON
+00C7;Ccedilla;LATIN CAPITAL LETTER C WITH CEDILLA
+F7E7;Ccedillasmall;LATIN SMALL CAPITAL LETTER C WITH CEDILLA
+0108;Ccircumflex;LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+010A;Cdotaccent;LATIN CAPITAL LETTER C WITH DOT ABOVE
+F7B8;Cedillasmall;SMALL CAPITAL CEDILLA
+03A7;Chi;GREEK CAPITAL LETTER CHI
+F6F6;Circumflexsmall;SMALL CAPITAL MODIFIER LETTER CIRCUMFLEX ACCENT
+F763;Csmall;LATIN SMALL CAPITAL LETTER C
+0044;D;LATIN CAPITAL LETTER D
+010E;Dcaron;LATIN CAPITAL LETTER D WITH CARON
+0110;Dcroat;LATIN CAPITAL LETTER D WITH STROKE
+2206;Delta;INCREMENT
+0394;Delta;GREEK CAPITAL LETTER DELTA;Duplicate
+F6CB;Dieresis;CAPITAL DIAERESIS
+F6CC;DieresisAcute;CAPITAL DIAERESIS ACUTE ACCENT
+F6CD;DieresisGrave;CAPITAL DIAERESIS GRAVE ACCENT
+F7A8;Dieresissmall;SMALL CAPITAL DIAERESIS
+F6F7;Dotaccentsmall;SMALL CAPITAL DOT ABOVE
+F764;Dsmall;LATIN SMALL CAPITAL LETTER D
+0045;E;LATIN CAPITAL LETTER E
+00C9;Eacute;LATIN CAPITAL LETTER E WITH ACUTE
+F7E9;Eacutesmall;LATIN SMALL CAPITAL LETTER E WITH ACUTE
+0114;Ebreve;LATIN CAPITAL LETTER E WITH BREVE
+011A;Ecaron;LATIN CAPITAL LETTER E WITH CARON
+00CA;Ecircumflex;LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+F7EA;Ecircumflexsmall;LATIN SMALL CAPITAL LETTER E WITH CIRCUMFLEX
+00CB;Edieresis;LATIN CAPITAL LETTER E WITH DIAERESIS
+F7EB;Edieresissmall;LATIN SMALL CAPITAL LETTER E WITH DIAERESIS
+0116;Edotaccent;LATIN CAPITAL LETTER E WITH DOT ABOVE
+00C8;Egrave;LATIN CAPITAL LETTER E WITH GRAVE
+F7E8;Egravesmall;LATIN SMALL CAPITAL LETTER E WITH GRAVE
+0112;Emacron;LATIN CAPITAL LETTER E WITH MACRON
+014A;Eng;LATIN CAPITAL LETTER ENG
+0118;Eogonek;LATIN CAPITAL LETTER E WITH OGONEK
+0395;Epsilon;GREEK CAPITAL LETTER EPSILON
+0388;Epsilontonos;GREEK CAPITAL LETTER EPSILON WITH TONOS
+F765;Esmall;LATIN SMALL CAPITAL LETTER E
+0397;Eta;GREEK CAPITAL LETTER ETA
+0389;Etatonos;GREEK CAPITAL LETTER ETA WITH TONOS
+00D0;Eth;LATIN CAPITAL LETTER ETH
+F7F0;Ethsmall;LATIN SMALL CAPITAL LETTER ETH
+20AC;Euro;EURO SIGN
+0046;F;LATIN CAPITAL LETTER F
+F766;Fsmall;LATIN SMALL CAPITAL LETTER F
+0047;G;LATIN CAPITAL LETTER G
+0393;Gamma;GREEK CAPITAL LETTER GAMMA
+011E;Gbreve;LATIN CAPITAL LETTER G WITH BREVE
+01E6;Gcaron;LATIN CAPITAL LETTER G WITH CARON
+011C;Gcircumflex;LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+0122;Gcommaaccent;LATIN CAPITAL LETTER G WITH CEDILLA
+0120;Gdotaccent;LATIN CAPITAL LETTER G WITH DOT ABOVE
+F6CE;Grave;CAPITAL GRAVE ACCENT
+F760;Gravesmall;SMALL CAPITAL GRAVE ACCENT
+F767;Gsmall;LATIN SMALL CAPITAL LETTER G
+0048;H;LATIN CAPITAL LETTER H
+25CF;H18533;BLACK CIRCLE
+25AA;H18543;BLACK SMALL SQUARE
+25AB;H18551;WHITE SMALL SQUARE
+25A1;H22073;WHITE SQUARE
+0126;Hbar;LATIN CAPITAL LETTER H WITH STROKE
+0124;Hcircumflex;LATIN CAPITAL LETTER H WITH CIRCUMFLEX
+F768;Hsmall;LATIN SMALL CAPITAL LETTER H
+F6CF;Hungarumlaut;CAPITAL DOUBLE ACUTE ACCENT
+F6F8;Hungarumlautsmall;SMALL CAPITAL DOUBLE ACUTE ACCENT
+0049;I;LATIN CAPITAL LETTER I
+0132;IJ;LATIN CAPITAL LIGATURE IJ
+00CD;Iacute;LATIN CAPITAL LETTER I WITH ACUTE
+F7ED;Iacutesmall;LATIN SMALL CAPITAL LETTER I WITH ACUTE
+012C;Ibreve;LATIN CAPITAL LETTER I WITH BREVE
+00CE;Icircumflex;LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+F7EE;Icircumflexsmall;LATIN SMALL CAPITAL LETTER I WITH CIRCUMFLEX
+00CF;Idieresis;LATIN CAPITAL LETTER I WITH DIAERESIS
+F7EF;Idieresissmall;LATIN SMALL CAPITAL LETTER I WITH DIAERESIS
+0130;Idotaccent;LATIN CAPITAL LETTER I WITH DOT ABOVE
+2111;Ifraktur;BLACK-LETTER CAPITAL I
+00CC;Igrave;LATIN CAPITAL LETTER I WITH GRAVE
+F7EC;Igravesmall;LATIN SMALL CAPITAL LETTER I WITH GRAVE
+012A;Imacron;LATIN CAPITAL LETTER I WITH MACRON
+012E;Iogonek;LATIN CAPITAL LETTER I WITH OGONEK
+0399;Iota;GREEK CAPITAL LETTER IOTA
+03AA;Iotadieresis;GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+038A;Iotatonos;GREEK CAPITAL LETTER IOTA WITH TONOS
+F769;Ismall;LATIN SMALL CAPITAL LETTER I
+0128;Itilde;LATIN CAPITAL LETTER I WITH TILDE
+004A;J;LATIN CAPITAL LETTER J
+0134;Jcircumflex;LATIN CAPITAL LETTER J WITH CIRCUMFLEX
+F76A;Jsmall;LATIN SMALL CAPITAL LETTER J
+004B;K;LATIN CAPITAL LETTER K
+039A;Kappa;GREEK CAPITAL LETTER KAPPA
+0136;Kcommaaccent;LATIN CAPITAL LETTER K WITH CEDILLA
+F76B;Ksmall;LATIN SMALL CAPITAL LETTER K
+004C;L;LATIN CAPITAL LETTER L
+F6BF;LL;LATIN CAPITAL LETTER LL
+0139;Lacute;LATIN CAPITAL LETTER L WITH ACUTE
+039B;Lambda;GREEK CAPITAL LETTER LAMDA
+013D;Lcaron;LATIN CAPITAL LETTER L WITH CARON
+013B;Lcommaaccent;LATIN CAPITAL LETTER L WITH CEDILLA
+013F;Ldot;LATIN CAPITAL LETTER L WITH MIDDLE DOT
+0141;Lslash;LATIN CAPITAL LETTER L WITH STROKE
+F6F9;Lslashsmall;LATIN SMALL CAPITAL LETTER L WITH STROKE
+F76C;Lsmall;LATIN SMALL CAPITAL LETTER L
+004D;M;LATIN CAPITAL LETTER M
+F6D0;Macron;CAPITAL MACRON
+F7AF;Macronsmall;SMALL CAPITAL MACRON
+F76D;Msmall;LATIN SMALL CAPITAL LETTER M
+039C;Mu;GREEK CAPITAL LETTER MU
+004E;N;LATIN CAPITAL LETTER N
+0143;Nacute;LATIN CAPITAL LETTER N WITH ACUTE
+0147;Ncaron;LATIN CAPITAL LETTER N WITH CARON
+0145;Ncommaaccent;LATIN CAPITAL LETTER N WITH CEDILLA
+F76E;Nsmall;LATIN SMALL CAPITAL LETTER N
+00D1;Ntilde;LATIN CAPITAL LETTER N WITH TILDE
+F7F1;Ntildesmall;LATIN SMALL CAPITAL LETTER N WITH TILDE
+039D;Nu;GREEK CAPITAL LETTER NU
+004F;O;LATIN CAPITAL LETTER O
+0152;OE;LATIN CAPITAL LIGATURE OE
+F6FA;OEsmall;LATIN SMALL CAPITAL LIGATURE OE
+00D3;Oacute;LATIN CAPITAL LETTER O WITH ACUTE
+F7F3;Oacutesmall;LATIN SMALL CAPITAL LETTER O WITH ACUTE
+014E;Obreve;LATIN CAPITAL LETTER O WITH BREVE
+00D4;Ocircumflex;LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+F7F4;Ocircumflexsmall;LATIN SMALL CAPITAL LETTER O WITH CIRCUMFLEX
+00D6;Odieresis;LATIN CAPITAL LETTER O WITH DIAERESIS
+F7F6;Odieresissmall;LATIN SMALL CAPITAL LETTER O WITH DIAERESIS
+F6FB;Ogoneksmall;SMALL CAPITAL OGONEK
+00D2;Ograve;LATIN CAPITAL LETTER O WITH GRAVE
+F7F2;Ogravesmall;LATIN SMALL CAPITAL LETTER O WITH GRAVE
+01A0;Ohorn;LATIN CAPITAL LETTER O WITH HORN
+0150;Ohungarumlaut;LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+014C;Omacron;LATIN CAPITAL LETTER O WITH MACRON
+2126;Omega;OHM SIGN
+03A9;Omega;GREEK CAPITAL LETTER OMEGA;Duplicate
+038F;Omegatonos;GREEK CAPITAL LETTER OMEGA WITH TONOS
+039F;Omicron;GREEK CAPITAL LETTER OMICRON
+038C;Omicrontonos;GREEK CAPITAL LETTER OMICRON WITH TONOS
+00D8;Oslash;LATIN CAPITAL LETTER O WITH STROKE
+01FE;Oslashacute;LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
+F7F8;Oslashsmall;LATIN SMALL CAPITAL LETTER O WITH STROKE
+F76F;Osmall;LATIN SMALL CAPITAL LETTER O
+00D5;Otilde;LATIN CAPITAL LETTER O WITH TILDE
+F7F5;Otildesmall;LATIN SMALL CAPITAL LETTER O WITH TILDE
+0050;P;LATIN CAPITAL LETTER P
+03A6;Phi;GREEK CAPITAL LETTER PHI
+03A0;Pi;GREEK CAPITAL LETTER PI
+03A8;Psi;GREEK CAPITAL LETTER PSI
+F770;Psmall;LATIN SMALL CAPITAL LETTER P
+0051;Q;LATIN CAPITAL LETTER Q
+F771;Qsmall;LATIN SMALL CAPITAL LETTER Q
+0052;R;LATIN CAPITAL LETTER R
+0154;Racute;LATIN CAPITAL LETTER R WITH ACUTE
+0158;Rcaron;LATIN CAPITAL LETTER R WITH CARON
+0156;Rcommaaccent;LATIN CAPITAL LETTER R WITH CEDILLA
+211C;Rfraktur;BLACK-LETTER CAPITAL R
+03A1;Rho;GREEK CAPITAL LETTER RHO
+F6FC;Ringsmall;SMALL CAPITAL RING ABOVE
+F772;Rsmall;LATIN SMALL CAPITAL LETTER R
+0053;S;LATIN CAPITAL LETTER S
+250C;SF010000;BOX DRAWINGS LIGHT DOWN AND RIGHT
+2514;SF020000;BOX DRAWINGS LIGHT UP AND RIGHT
+2510;SF030000;BOX DRAWINGS LIGHT DOWN AND LEFT
+2518;SF040000;BOX DRAWINGS LIGHT UP AND LEFT
+253C;SF050000;BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+252C;SF060000;BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+2534;SF070000;BOX DRAWINGS LIGHT UP AND HORIZONTAL
+251C;SF080000;BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+2524;SF090000;BOX DRAWINGS LIGHT VERTICAL AND LEFT
+2500;SF100000;BOX DRAWINGS LIGHT HORIZONTAL
+2502;SF110000;BOX DRAWINGS LIGHT VERTICAL
+2561;SF190000;BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+2562;SF200000;BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+2556;SF210000;BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+2555;SF220000;BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+2563;SF230000;BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+2551;SF240000;BOX DRAWINGS DOUBLE VERTICAL
+2557;SF250000;BOX DRAWINGS DOUBLE DOWN AND LEFT
+255D;SF260000;BOX DRAWINGS DOUBLE UP AND LEFT
+255C;SF270000;BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+255B;SF280000;BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+255E;SF360000;BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+255F;SF370000;BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+255A;SF380000;BOX DRAWINGS DOUBLE UP AND RIGHT
+2554;SF390000;BOX DRAWINGS DOUBLE DOWN AND RIGHT
+2569;SF400000;BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+2566;SF410000;BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+2560;SF420000;BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+2550;SF430000;BOX DRAWINGS DOUBLE HORIZONTAL
+256C;SF440000;BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+2567;SF450000;BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+2568;SF460000;BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+2564;SF470000;BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+2565;SF480000;BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+2559;SF490000;BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+2558;SF500000;BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+2552;SF510000;BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+2553;SF520000;BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+256B;SF530000;BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+256A;SF540000;BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+015A;Sacute;LATIN CAPITAL LETTER S WITH ACUTE
+0160;Scaron;LATIN CAPITAL LETTER S WITH CARON
+F6FD;Scaronsmall;LATIN SMALL CAPITAL LETTER S WITH CARON
+015E;Scedilla;LATIN CAPITAL LETTER S WITH CEDILLA
+F6C1;Scedilla;LATIN CAPITAL LETTER S WITH CEDILLA;Duplicate
+015C;Scircumflex;LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+0218;Scommaaccent;LATIN CAPITAL LETTER S WITH COMMA BELOW
+03A3;Sigma;GREEK CAPITAL LETTER SIGMA
+F773;Ssmall;LATIN SMALL CAPITAL LETTER S
+0054;T;LATIN CAPITAL LETTER T
+03A4;Tau;GREEK CAPITAL LETTER TAU
+0166;Tbar;LATIN CAPITAL LETTER T WITH STROKE
+0164;Tcaron;LATIN CAPITAL LETTER T WITH CARON
+0162;Tcommaaccent;LATIN CAPITAL LETTER T WITH CEDILLA
+021A;Tcommaaccent;LATIN CAPITAL LETTER T WITH COMMA BELOW;Duplicate
+0398;Theta;GREEK CAPITAL LETTER THETA
+00DE;Thorn;LATIN CAPITAL LETTER THORN
+F7FE;Thornsmall;LATIN SMALL CAPITAL LETTER THORN
+F6FE;Tildesmall;SMALL CAPITAL SMALL TILDE
+F774;Tsmall;LATIN SMALL CAPITAL LETTER T
+0055;U;LATIN CAPITAL LETTER U
+00DA;Uacute;LATIN CAPITAL LETTER U WITH ACUTE
+F7FA;Uacutesmall;LATIN SMALL CAPITAL LETTER U WITH ACUTE
+016C;Ubreve;LATIN CAPITAL LETTER U WITH BREVE
+00DB;Ucircumflex;LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+F7FB;Ucircumflexsmall;LATIN SMALL CAPITAL LETTER U WITH CIRCUMFLEX
+00DC;Udieresis;LATIN CAPITAL LETTER U WITH DIAERESIS
+F7FC;Udieresissmall;LATIN SMALL CAPITAL LETTER U WITH DIAERESIS
+00D9;Ugrave;LATIN CAPITAL LETTER U WITH GRAVE
+F7F9;Ugravesmall;LATIN SMALL CAPITAL LETTER U WITH GRAVE
+01AF;Uhorn;LATIN CAPITAL LETTER U WITH HORN
+0170;Uhungarumlaut;LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+016A;Umacron;LATIN CAPITAL LETTER U WITH MACRON
+0172;Uogonek;LATIN CAPITAL LETTER U WITH OGONEK
+03A5;Upsilon;GREEK CAPITAL LETTER UPSILON
+03D2;Upsilon1;GREEK UPSILON WITH HOOK SYMBOL
+03AB;Upsilondieresis;GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+038E;Upsilontonos;GREEK CAPITAL LETTER UPSILON WITH TONOS
+016E;Uring;LATIN CAPITAL LETTER U WITH RING ABOVE
+F775;Usmall;LATIN SMALL CAPITAL LETTER U
+0168;Utilde;LATIN CAPITAL LETTER U WITH TILDE
+0056;V;LATIN CAPITAL LETTER V
+F776;Vsmall;LATIN SMALL CAPITAL LETTER V
+0057;W;LATIN CAPITAL LETTER W
+1E82;Wacute;LATIN CAPITAL LETTER W WITH ACUTE
+0174;Wcircumflex;LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+1E84;Wdieresis;LATIN CAPITAL LETTER W WITH DIAERESIS
+1E80;Wgrave;LATIN CAPITAL LETTER W WITH GRAVE
+F777;Wsmall;LATIN SMALL CAPITAL LETTER W
+0058;X;LATIN CAPITAL LETTER X
+039E;Xi;GREEK CAPITAL LETTER XI
+F778;Xsmall;LATIN SMALL CAPITAL LETTER X
+0059;Y;LATIN CAPITAL LETTER Y
+00DD;Yacute;LATIN CAPITAL LETTER Y WITH ACUTE
+F7FD;Yacutesmall;LATIN SMALL CAPITAL LETTER Y WITH ACUTE
+0176;Ycircumflex;LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+0178;Ydieresis;LATIN CAPITAL LETTER Y WITH DIAERESIS
+F7FF;Ydieresissmall;LATIN SMALL CAPITAL LETTER Y WITH DIAERESIS
+1EF2;Ygrave;LATIN CAPITAL LETTER Y WITH GRAVE
+F779;Ysmall;LATIN SMALL CAPITAL LETTER Y
+005A;Z;LATIN CAPITAL LETTER Z
+0179;Zacute;LATIN CAPITAL LETTER Z WITH ACUTE
+017D;Zcaron;LATIN CAPITAL LETTER Z WITH CARON
+F6FF;Zcaronsmall;LATIN SMALL CAPITAL LETTER Z WITH CARON
+017B;Zdotaccent;LATIN CAPITAL LETTER Z WITH DOT ABOVE
+0396;Zeta;GREEK CAPITAL LETTER ZETA
+F77A;Zsmall;LATIN SMALL CAPITAL LETTER Z
+0061;a;LATIN SMALL LETTER A
+00E1;aacute;LATIN SMALL LETTER A WITH ACUTE
+0103;abreve;LATIN SMALL LETTER A WITH BREVE
+00E2;acircumflex;LATIN SMALL LETTER A WITH CIRCUMFLEX
+00B4;acute;ACUTE ACCENT
+0301;acutecomb;COMBINING ACUTE ACCENT
+00E4;adieresis;LATIN SMALL LETTER A WITH DIAERESIS
+00E6;ae;LATIN SMALL LETTER AE
+01FD;aeacute;LATIN SMALL LETTER AE WITH ACUTE
+2015;afii00208;HORIZONTAL BAR
+0410;afii10017;CYRILLIC CAPITAL LETTER A
+0411;afii10018;CYRILLIC CAPITAL LETTER BE
+0412;afii10019;CYRILLIC CAPITAL LETTER VE
+0413;afii10020;CYRILLIC CAPITAL LETTER GHE
+0414;afii10021;CYRILLIC CAPITAL LETTER DE
+0415;afii10022;CYRILLIC CAPITAL LETTER IE
+0401;afii10023;CYRILLIC CAPITAL LETTER IO
+0416;afii10024;CYRILLIC CAPITAL LETTER ZHE
+0417;afii10025;CYRILLIC CAPITAL LETTER ZE
+0418;afii10026;CYRILLIC CAPITAL LETTER I
+0419;afii10027;CYRILLIC CAPITAL LETTER SHORT I
+041A;afii10028;CYRILLIC CAPITAL LETTER KA
+041B;afii10029;CYRILLIC CAPITAL LETTER EL
+041C;afii10030;CYRILLIC CAPITAL LETTER EM
+041D;afii10031;CYRILLIC CAPITAL LETTER EN
+041E;afii10032;CYRILLIC CAPITAL LETTER O
+041F;afii10033;CYRILLIC CAPITAL LETTER PE
+0420;afii10034;CYRILLIC CAPITAL LETTER ER
+0421;afii10035;CYRILLIC CAPITAL LETTER ES
+0422;afii10036;CYRILLIC CAPITAL LETTER TE
+0423;afii10037;CYRILLIC CAPITAL LETTER U
+0424;afii10038;CYRILLIC CAPITAL LETTER EF
+0425;afii10039;CYRILLIC CAPITAL LETTER HA
+0426;afii10040;CYRILLIC CAPITAL LETTER TSE
+0427;afii10041;CYRILLIC CAPITAL LETTER CHE
+0428;afii10042;CYRILLIC CAPITAL LETTER SHA
+0429;afii10043;CYRILLIC CAPITAL LETTER SHCHA
+042A;afii10044;CYRILLIC CAPITAL LETTER HARD SIGN
+042B;afii10045;CYRILLIC CAPITAL LETTER YERU
+042C;afii10046;CYRILLIC CAPITAL LETTER SOFT SIGN
+042D;afii10047;CYRILLIC CAPITAL LETTER E
+042E;afii10048;CYRILLIC CAPITAL LETTER YU
+042F;afii10049;CYRILLIC CAPITAL LETTER YA
+0490;afii10050;CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+0402;afii10051;CYRILLIC CAPITAL LETTER DJE
+0403;afii10052;CYRILLIC CAPITAL LETTER GJE
+0404;afii10053;CYRILLIC CAPITAL LETTER UKRAINIAN IE
+0405;afii10054;CYRILLIC CAPITAL LETTER DZE
+0406;afii10055;CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+0407;afii10056;CYRILLIC CAPITAL LETTER YI
+0408;afii10057;CYRILLIC CAPITAL LETTER JE
+0409;afii10058;CYRILLIC CAPITAL LETTER LJE
+040A;afii10059;CYRILLIC CAPITAL LETTER NJE
+040B;afii10060;CYRILLIC CAPITAL LETTER TSHE
+040C;afii10061;CYRILLIC CAPITAL LETTER KJE
+040E;afii10062;CYRILLIC CAPITAL LETTER SHORT U
+F6C4;afii10063;CYRILLIC SMALL LETTER GHE VARIANT
+F6C5;afii10064;CYRILLIC SMALL LETTER BE VARIANT
+0430;afii10065;CYRILLIC SMALL LETTER A
+0431;afii10066;CYRILLIC SMALL LETTER BE
+0432;afii10067;CYRILLIC SMALL LETTER VE
+0433;afii10068;CYRILLIC SMALL LETTER GHE
+0434;afii10069;CYRILLIC SMALL LETTER DE
+0435;afii10070;CYRILLIC SMALL LETTER IE
+0451;afii10071;CYRILLIC SMALL LETTER IO
+0436;afii10072;CYRILLIC SMALL LETTER ZHE
+0437;afii10073;CYRILLIC SMALL LETTER ZE
+0438;afii10074;CYRILLIC SMALL LETTER I
+0439;afii10075;CYRILLIC SMALL LETTER SHORT I
+043A;afii10076;CYRILLIC SMALL LETTER KA
+043B;afii10077;CYRILLIC SMALL LETTER EL
+043C;afii10078;CYRILLIC SMALL LETTER EM
+043D;afii10079;CYRILLIC SMALL LETTER EN
+043E;afii10080;CYRILLIC SMALL LETTER O
+043F;afii10081;CYRILLIC SMALL LETTER PE
+0440;afii10082;CYRILLIC SMALL LETTER ER
+0441;afii10083;CYRILLIC SMALL LETTER ES
+0442;afii10084;CYRILLIC SMALL LETTER TE
+0443;afii10085;CYRILLIC SMALL LETTER U
+0444;afii10086;CYRILLIC SMALL LETTER EF
+0445;afii10087;CYRILLIC SMALL LETTER HA
+0446;afii10088;CYRILLIC SMALL LETTER TSE
+0447;afii10089;CYRILLIC SMALL LETTER CHE
+0448;afii10090;CYRILLIC SMALL LETTER SHA
+0449;afii10091;CYRILLIC SMALL LETTER SHCHA
+044A;afii10092;CYRILLIC SMALL LETTER HARD SIGN
+044B;afii10093;CYRILLIC SMALL LETTER YERU
+044C;afii10094;CYRILLIC SMALL LETTER SOFT SIGN
+044D;afii10095;CYRILLIC SMALL LETTER E
+044E;afii10096;CYRILLIC SMALL LETTER YU
+044F;afii10097;CYRILLIC SMALL LETTER YA
+0491;afii10098;CYRILLIC SMALL LETTER GHE WITH UPTURN
+0452;afii10099;CYRILLIC SMALL LETTER DJE
+0453;afii10100;CYRILLIC SMALL LETTER GJE
+0454;afii10101;CYRILLIC SMALL LETTER UKRAINIAN IE
+0455;afii10102;CYRILLIC SMALL LETTER DZE
+0456;afii10103;CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+0457;afii10104;CYRILLIC SMALL LETTER YI
+0458;afii10105;CYRILLIC SMALL LETTER JE
+0459;afii10106;CYRILLIC SMALL LETTER LJE
+045A;afii10107;CYRILLIC SMALL LETTER NJE
+045B;afii10108;CYRILLIC SMALL LETTER TSHE
+045C;afii10109;CYRILLIC SMALL LETTER KJE
+045E;afii10110;CYRILLIC SMALL LETTER SHORT U
+040F;afii10145;CYRILLIC CAPITAL LETTER DZHE
+0462;afii10146;CYRILLIC CAPITAL LETTER YAT
+0472;afii10147;CYRILLIC CAPITAL LETTER FITA
+0474;afii10148;CYRILLIC CAPITAL LETTER IZHITSA
+F6C6;afii10192;CYRILLIC SMALL LETTER DE VARIANT
+045F;afii10193;CYRILLIC SMALL LETTER DZHE
+0463;afii10194;CYRILLIC SMALL LETTER YAT
+0473;afii10195;CYRILLIC SMALL LETTER FITA
+0475;afii10196;CYRILLIC SMALL LETTER IZHITSA
+F6C7;afii10831;CYRILLIC SMALL LETTER PE VARIANT
+F6C8;afii10832;CYRILLIC SMALL LETTER TE VARIANT
+04D9;afii10846;CYRILLIC SMALL LETTER SCHWA
+200E;afii299;LEFT-TO-RIGHT MARK
+200F;afii300;RIGHT-TO-LEFT MARK
+200D;afii301;ZERO WIDTH JOINER
+066A;afii57381;ARABIC PERCENT SIGN
+060C;afii57388;ARABIC COMMA
+0660;afii57392;ARABIC-INDIC DIGIT ZERO
+0661;afii57393;ARABIC-INDIC DIGIT ONE
+0662;afii57394;ARABIC-INDIC DIGIT TWO
+0663;afii57395;ARABIC-INDIC DIGIT THREE
+0664;afii57396;ARABIC-INDIC DIGIT FOUR
+0665;afii57397;ARABIC-INDIC DIGIT FIVE
+0666;afii57398;ARABIC-INDIC DIGIT SIX
+0667;afii57399;ARABIC-INDIC DIGIT SEVEN
+0668;afii57400;ARABIC-INDIC DIGIT EIGHT
+0669;afii57401;ARABIC-INDIC DIGIT NINE
+061B;afii57403;ARABIC SEMICOLON
+061F;afii57407;ARABIC QUESTION MARK
+0621;afii57409;ARABIC LETTER HAMZA
+0622;afii57410;ARABIC LETTER ALEF WITH MADDA ABOVE
+0623;afii57411;ARABIC LETTER ALEF WITH HAMZA ABOVE
+0624;afii57412;ARABIC LETTER WAW WITH HAMZA ABOVE
+0625;afii57413;ARABIC LETTER ALEF WITH HAMZA BELOW
+0626;afii57414;ARABIC LETTER YEH WITH HAMZA ABOVE
+0627;afii57415;ARABIC LETTER ALEF
+0628;afii57416;ARABIC LETTER BEH
+0629;afii57417;ARABIC LETTER TEH MARBUTA
+062A;afii57418;ARABIC LETTER TEH
+062B;afii57419;ARABIC LETTER THEH
+062C;afii57420;ARABIC LETTER JEEM
+062D;afii57421;ARABIC LETTER HAH
+062E;afii57422;ARABIC LETTER KHAH
+062F;afii57423;ARABIC LETTER DAL
+0630;afii57424;ARABIC LETTER THAL
+0631;afii57425;ARABIC LETTER REH
+0632;afii57426;ARABIC LETTER ZAIN
+0633;afii57427;ARABIC LETTER SEEN
+0634;afii57428;ARABIC LETTER SHEEN
+0635;afii57429;ARABIC LETTER SAD
+0636;afii57430;ARABIC LETTER DAD
+0637;afii57431;ARABIC LETTER TAH
+0638;afii57432;ARABIC LETTER ZAH
+0639;afii57433;ARABIC LETTER AIN
+063A;afii57434;ARABIC LETTER GHAIN
+0640;afii57440;ARABIC TATWEEL
+0641;afii57441;ARABIC LETTER FEH
+0642;afii57442;ARABIC LETTER QAF
+0643;afii57443;ARABIC LETTER KAF
+0644;afii57444;ARABIC LETTER LAM
+0645;afii57445;ARABIC LETTER MEEM
+0646;afii57446;ARABIC LETTER NOON
+0648;afii57448;ARABIC LETTER WAW
+0649;afii57449;ARABIC LETTER ALEF MAKSURA
+064A;afii57450;ARABIC LETTER YEH
+064B;afii57451;ARABIC FATHATAN
+064C;afii57452;ARABIC DAMMATAN
+064D;afii57453;ARABIC KASRATAN
+064E;afii57454;ARABIC FATHA
+064F;afii57455;ARABIC DAMMA
+0650;afii57456;ARABIC KASRA
+0651;afii57457;ARABIC SHADDA
+0652;afii57458;ARABIC SUKUN
+0647;afii57470;ARABIC LETTER HEH
+06A4;afii57505;ARABIC LETTER VEH
+067E;afii57506;ARABIC LETTER PEH
+0686;afii57507;ARABIC LETTER TCHEH
+0698;afii57508;ARABIC LETTER JEH
+06AF;afii57509;ARABIC LETTER GAF
+0679;afii57511;ARABIC LETTER TTEH
+0688;afii57512;ARABIC LETTER DDAL
+0691;afii57513;ARABIC LETTER RREH
+06BA;afii57514;ARABIC LETTER NOON GHUNNA
+06D2;afii57519;ARABIC LETTER YEH BARREE
+06D5;afii57534;ARABIC LETTER AE
+20AA;afii57636;NEW SHEQEL SIGN
+05BE;afii57645;HEBREW PUNCTUATION MAQAF
+05C3;afii57658;HEBREW PUNCTUATION SOF PASUQ
+05D0;afii57664;HEBREW LETTER ALEF
+05D1;afii57665;HEBREW LETTER BET
+05D2;afii57666;HEBREW LETTER GIMEL
+05D3;afii57667;HEBREW LETTER DALET
+05D4;afii57668;HEBREW LETTER HE
+05D5;afii57669;HEBREW LETTER VAV
+05D6;afii57670;HEBREW LETTER ZAYIN
+05D7;afii57671;HEBREW LETTER HET
+05D8;afii57672;HEBREW LETTER TET
+05D9;afii57673;HEBREW LETTER YOD
+05DA;afii57674;HEBREW LETTER FINAL KAF
+05DB;afii57675;HEBREW LETTER KAF
+05DC;afii57676;HEBREW LETTER LAMED
+05DD;afii57677;HEBREW LETTER FINAL MEM
+05DE;afii57678;HEBREW LETTER MEM
+05DF;afii57679;HEBREW LETTER FINAL NUN
+05E0;afii57680;HEBREW LETTER NUN
+05E1;afii57681;HEBREW LETTER SAMEKH
+05E2;afii57682;HEBREW LETTER AYIN
+05E3;afii57683;HEBREW LETTER FINAL PE
+05E4;afii57684;HEBREW LETTER PE
+05E5;afii57685;HEBREW LETTER FINAL TSADI
+05E6;afii57686;HEBREW LETTER TSADI
+05E7;afii57687;HEBREW LETTER QOF
+05E8;afii57688;HEBREW LETTER RESH
+05E9;afii57689;HEBREW LETTER SHIN
+05EA;afii57690;HEBREW LETTER TAV
+FB2A;afii57694;HEBREW LETTER SHIN WITH SHIN DOT
+FB2B;afii57695;HEBREW LETTER SHIN WITH SIN DOT
+FB4B;afii57700;HEBREW LETTER VAV WITH HOLAM
+FB1F;afii57705;HEBREW LIGATURE YIDDISH YOD YOD PATAH
+05F0;afii57716;HEBREW LIGATURE YIDDISH DOUBLE VAV
+05F1;afii57717;HEBREW LIGATURE YIDDISH VAV YOD
+05F2;afii57718;HEBREW LIGATURE YIDDISH DOUBLE YOD
+FB35;afii57723;HEBREW LETTER VAV WITH DAGESH
+05B4;afii57793;HEBREW POINT HIRIQ
+05B5;afii57794;HEBREW POINT TSERE
+05B6;afii57795;HEBREW POINT SEGOL
+05BB;afii57796;HEBREW POINT QUBUTS
+05B8;afii57797;HEBREW POINT QAMATS
+05B7;afii57798;HEBREW POINT PATAH
+05B0;afii57799;HEBREW POINT SHEVA
+05B2;afii57800;HEBREW POINT HATAF PATAH
+05B1;afii57801;HEBREW POINT HATAF SEGOL
+05B3;afii57802;HEBREW POINT HATAF QAMATS
+05C2;afii57803;HEBREW POINT SIN DOT
+05C1;afii57804;HEBREW POINT SHIN DOT
+05B9;afii57806;HEBREW POINT HOLAM
+05BC;afii57807;HEBREW POINT DAGESH OR MAPIQ
+05BD;afii57839;HEBREW POINT METEG
+05BF;afii57841;HEBREW POINT RAFE
+05C0;afii57842;HEBREW PUNCTUATION PASEQ
+02BC;afii57929;MODIFIER LETTER APOSTROPHE
+2105;afii61248;CARE OF
+2113;afii61289;SCRIPT SMALL L
+2116;afii61352;NUMERO SIGN
+202C;afii61573;POP DIRECTIONAL FORMATTING
+202D;afii61574;LEFT-TO-RIGHT OVERRIDE
+202E;afii61575;RIGHT-TO-LEFT OVERRIDE
+200C;afii61664;ZERO WIDTH NON-JOINER
+066D;afii63167;ARABIC FIVE POINTED STAR
+02BD;afii64937;MODIFIER LETTER REVERSED COMMA
+00E0;agrave;LATIN SMALL LETTER A WITH GRAVE
+2135;aleph;ALEF SYMBOL
+03B1;alpha;GREEK SMALL LETTER ALPHA
+03AC;alphatonos;GREEK SMALL LETTER ALPHA WITH TONOS
+0101;amacron;LATIN SMALL LETTER A WITH MACRON
+0026;ampersand;AMPERSAND
+F726;ampersandsmall;SMALL CAPITAL AMPERSAND
+2220;angle;ANGLE
+2329;angleleft;LEFT-POINTING ANGLE BRACKET
+232A;angleright;RIGHT-POINTING ANGLE BRACKET
+0387;anoteleia;GREEK ANO TELEIA
+0105;aogonek;LATIN SMALL LETTER A WITH OGONEK
+2248;approxequal;ALMOST EQUAL TO
+00E5;aring;LATIN SMALL LETTER A WITH RING ABOVE
+01FB;aringacute;LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE
+2194;arrowboth;LEFT RIGHT ARROW
+21D4;arrowdblboth;LEFT RIGHT DOUBLE ARROW
+21D3;arrowdbldown;DOWNWARDS DOUBLE ARROW
+21D0;arrowdblleft;LEFTWARDS DOUBLE ARROW
+21D2;arrowdblright;RIGHTWARDS DOUBLE ARROW
+21D1;arrowdblup;UPWARDS DOUBLE ARROW
+2193;arrowdown;DOWNWARDS ARROW
+F8E7;arrowhorizex;HORIZONTAL ARROW EXTENDER
+2190;arrowleft;LEFTWARDS ARROW
+2192;arrowright;RIGHTWARDS ARROW
+2191;arrowup;UPWARDS ARROW
+2195;arrowupdn;UP DOWN ARROW
+21A8;arrowupdnbse;UP DOWN ARROW WITH BASE
+F8E6;arrowvertex;VERTICAL ARROW EXTENDER
+005E;asciicircum;CIRCUMFLEX ACCENT
+007E;asciitilde;TILDE
+002A;asterisk;ASTERISK
+2217;asteriskmath;ASTERISK OPERATOR
+F6E9;asuperior;SUPERSCRIPT LATIN SMALL LETTER A
+0040;at;COMMERCIAL AT
+00E3;atilde;LATIN SMALL LETTER A WITH TILDE
+0062;b;LATIN SMALL LETTER B
+005C;backslash;REVERSE SOLIDUS
+007C;bar;VERTICAL LINE
+03B2;beta;GREEK SMALL LETTER BETA
+2588;block;FULL BLOCK
+F8F4;braceex;CURLY BRACKET EXTENDER
+007B;braceleft;LEFT CURLY BRACKET
+F8F3;braceleftbt;LEFT CURLY BRACKET BOTTOM
+F8F2;braceleftmid;LEFT CURLY BRACKET MID
+F8F1;bracelefttp;LEFT CURLY BRACKET TOP
+007D;braceright;RIGHT CURLY BRACKET
+F8FE;bracerightbt;RIGHT CURLY BRACKET BOTTOM
+F8FD;bracerightmid;RIGHT CURLY BRACKET MID
+F8FC;bracerighttp;RIGHT CURLY BRACKET TOP
+005B;bracketleft;LEFT SQUARE BRACKET
+F8F0;bracketleftbt;LEFT SQUARE BRACKET BOTTOM
+F8EF;bracketleftex;LEFT SQUARE BRACKET EXTENDER
+F8EE;bracketlefttp;LEFT SQUARE BRACKET TOP
+005D;bracketright;RIGHT SQUARE BRACKET
+F8FB;bracketrightbt;RIGHT SQUARE BRACKET BOTTOM
+F8FA;bracketrightex;RIGHT SQUARE BRACKET EXTENDER
+F8F9;bracketrighttp;RIGHT SQUARE BRACKET TOP
+02D8;breve;BREVE
+00A6;brokenbar;BROKEN BAR
+F6EA;bsuperior;SUPERSCRIPT LATIN SMALL LETTER B
+2022;bullet;BULLET
+0063;c;LATIN SMALL LETTER C
+0107;cacute;LATIN SMALL LETTER C WITH ACUTE
+02C7;caron;CARON
+21B5;carriagereturn;DOWNWARDS ARROW WITH CORNER LEFTWARDS
+010D;ccaron;LATIN SMALL LETTER C WITH CARON
+00E7;ccedilla;LATIN SMALL LETTER C WITH CEDILLA
+0109;ccircumflex;LATIN SMALL LETTER C WITH CIRCUMFLEX
+010B;cdotaccent;LATIN SMALL LETTER C WITH DOT ABOVE
+00B8;cedilla;CEDILLA
+00A2;cent;CENT SIGN
+F6DF;centinferior;SUBSCRIPT CENT SIGN
+F7A2;centoldstyle;OLDSTYLE CENT SIGN
+F6E0;centsuperior;SUPERSCRIPT CENT SIGN
+03C7;chi;GREEK SMALL LETTER CHI
+25CB;circle;WHITE CIRCLE
+2297;circlemultiply;CIRCLED TIMES
+2295;circleplus;CIRCLED PLUS
+02C6;circumflex;MODIFIER LETTER CIRCUMFLEX ACCENT
+2663;club;BLACK CLUB SUIT
+003A;colon;COLON
+20A1;colonmonetary;COLON SIGN
+002C;comma;COMMA
+F6C3;commaaccent;COMMA BELOW
+F6E1;commainferior;SUBSCRIPT COMMA
+F6E2;commasuperior;SUPERSCRIPT COMMA
+2245;congruent;APPROXIMATELY EQUAL TO
+00A9;copyright;COPYRIGHT SIGN
+F8E9;copyrightsans;COPYRIGHT SIGN SANS SERIF
+F6D9;copyrightserif;COPYRIGHT SIGN SERIF
+00A4;currency;CURRENCY SIGN
+F6D1;cyrBreve;CAPITAL CYRILLIC BREVE
+F6D2;cyrFlex;CAPITAL CYRILLIC CIRCUMFLEX
+F6D4;cyrbreve;CYRILLIC BREVE
+F6D5;cyrflex;CYRILLIC CIRCUMFLEX
+0064;d;LATIN SMALL LETTER D
+2020;dagger;DAGGER
+2021;daggerdbl;DOUBLE DAGGER
+F6D3;dblGrave;CAPITAL DOUBLE GRAVE ACCENT
+F6D6;dblgrave;DOUBLE GRAVE ACCENT
+010F;dcaron;LATIN SMALL LETTER D WITH CARON
+0111;dcroat;LATIN SMALL LETTER D WITH STROKE
+00B0;degree;DEGREE SIGN
+03B4;delta;GREEK SMALL LETTER DELTA
+2666;diamond;BLACK DIAMOND SUIT
+00A8;dieresis;DIAERESIS
+F6D7;dieresisacute;DIAERESIS ACUTE ACCENT
+F6D8;dieresisgrave;DIAERESIS GRAVE ACCENT
+0385;dieresistonos;GREEK DIALYTIKA TONOS
+00F7;divide;DIVISION SIGN
+2593;dkshade;DARK SHADE
+2584;dnblock;LOWER HALF BLOCK
+0024;dollar;DOLLAR SIGN
+F6E3;dollarinferior;SUBSCRIPT DOLLAR SIGN
+F724;dollaroldstyle;OLDSTYLE DOLLAR SIGN
+F6E4;dollarsuperior;SUPERSCRIPT DOLLAR SIGN
+20AB;dong;DONG SIGN
+02D9;dotaccent;DOT ABOVE
+0323;dotbelowcomb;COMBINING DOT BELOW
+0131;dotlessi;LATIN SMALL LETTER DOTLESS I
+F6BE;dotlessj;LATIN SMALL LETTER DOTLESS J
+22C5;dotmath;DOT OPERATOR
+F6EB;dsuperior;SUPERSCRIPT LATIN SMALL LETTER D
+0065;e;LATIN SMALL LETTER E
+00E9;eacute;LATIN SMALL LETTER E WITH ACUTE
+0115;ebreve;LATIN SMALL LETTER E WITH BREVE
+011B;ecaron;LATIN SMALL LETTER E WITH CARON
+00EA;ecircumflex;LATIN SMALL LETTER E WITH CIRCUMFLEX
+00EB;edieresis;LATIN SMALL LETTER E WITH DIAERESIS
+0117;edotaccent;LATIN SMALL LETTER E WITH DOT ABOVE
+00E8;egrave;LATIN SMALL LETTER E WITH GRAVE
+0038;eight;DIGIT EIGHT
+2088;eightinferior;SUBSCRIPT EIGHT
+F738;eightoldstyle;OLDSTYLE DIGIT EIGHT
+2078;eightsuperior;SUPERSCRIPT EIGHT
+2208;element;ELEMENT OF
+2026;ellipsis;HORIZONTAL ELLIPSIS
+0113;emacron;LATIN SMALL LETTER E WITH MACRON
+2014;emdash;EM DASH
+2205;emptyset;EMPTY SET
+2013;endash;EN DASH
+014B;eng;LATIN SMALL LETTER ENG
+0119;eogonek;LATIN SMALL LETTER E WITH OGONEK
+03B5;epsilon;GREEK SMALL LETTER EPSILON
+03AD;epsilontonos;GREEK SMALL LETTER EPSILON WITH TONOS
+003D;equal;EQUALS SIGN
+2261;equivalence;IDENTICAL TO
+212E;estimated;ESTIMATED SYMBOL
+F6EC;esuperior;SUPERSCRIPT LATIN SMALL LETTER E
+03B7;eta;GREEK SMALL LETTER ETA
+03AE;etatonos;GREEK SMALL LETTER ETA WITH TONOS
+00F0;eth;LATIN SMALL LETTER ETH
+0021;exclam;EXCLAMATION MARK
+203C;exclamdbl;DOUBLE EXCLAMATION MARK
+00A1;exclamdown;INVERTED EXCLAMATION MARK
+F7A1;exclamdownsmall;SMALL CAPITAL INVERTED EXCLAMATION MARK
+F721;exclamsmall;SMALL CAPITAL EXCLAMATION MARK
+2203;existential;THERE EXISTS
+0066;f;LATIN SMALL LETTER F
+2640;female;FEMALE SIGN
+FB00;ff;LATIN SMALL LIGATURE FF
+FB03;ffi;LATIN SMALL LIGATURE FFI
+FB04;ffl;LATIN SMALL LIGATURE FFL
+FB01;fi;LATIN SMALL LIGATURE FI
+2012;figuredash;FIGURE DASH
+25A0;filledbox;BLACK SQUARE
+25AC;filledrect;BLACK RECTANGLE
+0035;five;DIGIT FIVE
+215D;fiveeighths;VULGAR FRACTION FIVE EIGHTHS
+2085;fiveinferior;SUBSCRIPT FIVE
+F735;fiveoldstyle;OLDSTYLE DIGIT FIVE
+2075;fivesuperior;SUPERSCRIPT FIVE
+FB02;fl;LATIN SMALL LIGATURE FL
+0192;florin;LATIN SMALL LETTER F WITH HOOK
+0034;four;DIGIT FOUR
+2084;fourinferior;SUBSCRIPT FOUR
+F734;fouroldstyle;OLDSTYLE DIGIT FOUR
+2074;foursuperior;SUPERSCRIPT FOUR
+2044;fraction;FRACTION SLASH
+2215;fraction;DIVISION SLASH;Duplicate
+20A3;franc;FRENCH FRANC SIGN
+0067;g;LATIN SMALL LETTER G
+03B3;gamma;GREEK SMALL LETTER GAMMA
+011F;gbreve;LATIN SMALL LETTER G WITH BREVE
+01E7;gcaron;LATIN SMALL LETTER G WITH CARON
+011D;gcircumflex;LATIN SMALL LETTER G WITH CIRCUMFLEX
+0123;gcommaaccent;LATIN SMALL LETTER G WITH CEDILLA
+0121;gdotaccent;LATIN SMALL LETTER G WITH DOT ABOVE
+00DF;germandbls;LATIN SMALL LETTER SHARP S
+2207;gradient;NABLA
+0060;grave;GRAVE ACCENT
+0300;gravecomb;COMBINING GRAVE ACCENT
+003E;greater;GREATER-THAN SIGN
+2265;greaterequal;GREATER-THAN OR EQUAL TO
+00AB;guillemotleft;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+00BB;guillemotright;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+2039;guilsinglleft;SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+203A;guilsinglright;SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0068;h;LATIN SMALL LETTER H
+0127;hbar;LATIN SMALL LETTER H WITH STROKE
+0125;hcircumflex;LATIN SMALL LETTER H WITH CIRCUMFLEX
+2665;heart;BLACK HEART SUIT
+0309;hookabovecomb;COMBINING HOOK ABOVE
+2302;house;HOUSE
+02DD;hungarumlaut;DOUBLE ACUTE ACCENT
+002D;hyphen;HYPHEN-MINUS
+00AD;hyphen;SOFT HYPHEN;Duplicate
+F6E5;hypheninferior;SUBSCRIPT HYPHEN-MINUS
+F6E6;hyphensuperior;SUPERSCRIPT HYPHEN-MINUS
+0069;i;LATIN SMALL LETTER I
+00ED;iacute;LATIN SMALL LETTER I WITH ACUTE
+012D;ibreve;LATIN SMALL LETTER I WITH BREVE
+00EE;icircumflex;LATIN SMALL LETTER I WITH CIRCUMFLEX
+00EF;idieresis;LATIN SMALL LETTER I WITH DIAERESIS
+00EC;igrave;LATIN SMALL LETTER I WITH GRAVE
+0133;ij;LATIN SMALL LIGATURE IJ
+012B;imacron;LATIN SMALL LETTER I WITH MACRON
+221E;infinity;INFINITY
+222B;integral;INTEGRAL
+2321;integralbt;BOTTOM HALF INTEGRAL
+F8F5;integralex;INTEGRAL EXTENDER
+2320;integraltp;TOP HALF INTEGRAL
+2229;intersection;INTERSECTION
+25D8;invbullet;INVERSE BULLET
+25D9;invcircle;INVERSE WHITE CIRCLE
+263B;invsmileface;BLACK SMILING FACE
+012F;iogonek;LATIN SMALL LETTER I WITH OGONEK
+03B9;iota;GREEK SMALL LETTER IOTA
+03CA;iotadieresis;GREEK SMALL LETTER IOTA WITH DIALYTIKA
+0390;iotadieresistonos;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+03AF;iotatonos;GREEK SMALL LETTER IOTA WITH TONOS
+F6ED;isuperior;SUPERSCRIPT LATIN SMALL LETTER I
+0129;itilde;LATIN SMALL LETTER I WITH TILDE
+006A;j;LATIN SMALL LETTER J
+0135;jcircumflex;LATIN SMALL LETTER J WITH CIRCUMFLEX
+006B;k;LATIN SMALL LETTER K
+03BA;kappa;GREEK SMALL LETTER KAPPA
+0137;kcommaaccent;LATIN SMALL LETTER K WITH CEDILLA
+0138;kgreenlandic;LATIN SMALL LETTER KRA
+006C;l;LATIN SMALL LETTER L
+013A;lacute;LATIN SMALL LETTER L WITH ACUTE
+03BB;lambda;GREEK SMALL LETTER LAMDA
+013E;lcaron;LATIN SMALL LETTER L WITH CARON
+013C;lcommaaccent;LATIN SMALL LETTER L WITH CEDILLA
+0140;ldot;LATIN SMALL LETTER L WITH MIDDLE DOT
+003C;less;LESS-THAN SIGN
+2264;lessequal;LESS-THAN OR EQUAL TO
+258C;lfblock;LEFT HALF BLOCK
+20A4;lira;LIRA SIGN
+F6C0;ll;LATIN SMALL LETTER LL
+2227;logicaland;LOGICAL AND
+00AC;logicalnot;NOT SIGN
+2228;logicalor;LOGICAL OR
+017F;longs;LATIN SMALL LETTER LONG S
+25CA;lozenge;LOZENGE
+0142;lslash;LATIN SMALL LETTER L WITH STROKE
+F6EE;lsuperior;SUPERSCRIPT LATIN SMALL LETTER L
+2591;ltshade;LIGHT SHADE
+006D;m;LATIN SMALL LETTER M
+00AF;macron;MACRON
+02C9;macron;MODIFIER LETTER MACRON;Duplicate
+2642;male;MALE SIGN
+2212;minus;MINUS SIGN
+2032;minute;PRIME
+F6EF;msuperior;SUPERSCRIPT LATIN SMALL LETTER M
+00B5;mu;MICRO SIGN
+03BC;mu;GREEK SMALL LETTER MU;Duplicate
+00D7;multiply;MULTIPLICATION SIGN
+266A;musicalnote;EIGHTH NOTE
+266B;musicalnotedbl;BEAMED EIGHTH NOTES
+006E;n;LATIN SMALL LETTER N
+0144;nacute;LATIN SMALL LETTER N WITH ACUTE
+0149;napostrophe;LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+0148;ncaron;LATIN SMALL LETTER N WITH CARON
+0146;ncommaaccent;LATIN SMALL LETTER N WITH CEDILLA
+0039;nine;DIGIT NINE
+2089;nineinferior;SUBSCRIPT NINE
+F739;nineoldstyle;OLDSTYLE DIGIT NINE
+2079;ninesuperior;SUPERSCRIPT NINE
+2209;notelement;NOT AN ELEMENT OF
+2260;notequal;NOT EQUAL TO
+2284;notsubset;NOT A SUBSET OF
+207F;nsuperior;SUPERSCRIPT LATIN SMALL LETTER N
+00F1;ntilde;LATIN SMALL LETTER N WITH TILDE
+03BD;nu;GREEK SMALL LETTER NU
+0023;numbersign;NUMBER SIGN
+006F;o;LATIN SMALL LETTER O
+00F3;oacute;LATIN SMALL LETTER O WITH ACUTE
+014F;obreve;LATIN SMALL LETTER O WITH BREVE
+00F4;ocircumflex;LATIN SMALL LETTER O WITH CIRCUMFLEX
+00F6;odieresis;LATIN SMALL LETTER O WITH DIAERESIS
+0153;oe;LATIN SMALL LIGATURE OE
+02DB;ogonek;OGONEK
+00F2;ograve;LATIN SMALL LETTER O WITH GRAVE
+01A1;ohorn;LATIN SMALL LETTER O WITH HORN
+0151;ohungarumlaut;LATIN SMALL LETTER O WITH DOUBLE ACUTE
+014D;omacron;LATIN SMALL LETTER O WITH MACRON
+03C9;omega;GREEK SMALL LETTER OMEGA
+03D6;omega1;GREEK PI SYMBOL
+03CE;omegatonos;GREEK SMALL LETTER OMEGA WITH TONOS
+03BF;omicron;GREEK SMALL LETTER OMICRON
+03CC;omicrontonos;GREEK SMALL LETTER OMICRON WITH TONOS
+0031;one;DIGIT ONE
+2024;onedotenleader;ONE DOT LEADER
+215B;oneeighth;VULGAR FRACTION ONE EIGHTH
+F6DC;onefitted;PROPORTIONAL DIGIT ONE
+00BD;onehalf;VULGAR FRACTION ONE HALF
+2081;oneinferior;SUBSCRIPT ONE
+F731;oneoldstyle;OLDSTYLE DIGIT ONE
+00BC;onequarter;VULGAR FRACTION ONE QUARTER
+00B9;onesuperior;SUPERSCRIPT ONE
+2153;onethird;VULGAR FRACTION ONE THIRD
+25E6;openbullet;WHITE BULLET
+00AA;ordfeminine;FEMININE ORDINAL INDICATOR
+00BA;ordmasculine;MASCULINE ORDINAL INDICATOR
+221F;orthogonal;RIGHT ANGLE
+00F8;oslash;LATIN SMALL LETTER O WITH STROKE
+01FF;oslashacute;LATIN SMALL LETTER O WITH STROKE AND ACUTE
+F6F0;osuperior;SUPERSCRIPT LATIN SMALL LETTER O
+00F5;otilde;LATIN SMALL LETTER O WITH TILDE
+0070;p;LATIN SMALL LETTER P
+00B6;paragraph;PILCROW SIGN
+0028;parenleft;LEFT PARENTHESIS
+F8ED;parenleftbt;LEFT PAREN BOTTOM
+F8EC;parenleftex;LEFT PAREN EXTENDER
+208D;parenleftinferior;SUBSCRIPT LEFT PARENTHESIS
+207D;parenleftsuperior;SUPERSCRIPT LEFT PARENTHESIS
+F8EB;parenlefttp;LEFT PAREN TOP
+0029;parenright;RIGHT PARENTHESIS
+F8F8;parenrightbt;RIGHT PAREN BOTTOM
+F8F7;parenrightex;RIGHT PAREN EXTENDER
+208E;parenrightinferior;SUBSCRIPT RIGHT PARENTHESIS
+207E;parenrightsuperior;SUPERSCRIPT RIGHT PARENTHESIS
+F8F6;parenrighttp;RIGHT PAREN TOP
+2202;partialdiff;PARTIAL DIFFERENTIAL
+0025;percent;PERCENT SIGN
+002E;period;FULL STOP
+00B7;periodcentered;MIDDLE DOT
+2219;periodcentered;BULLET OPERATOR;Duplicate
+F6E7;periodinferior;SUBSCRIPT FULL STOP
+F6E8;periodsuperior;SUPERSCRIPT FULL STOP
+22A5;perpendicular;UP TACK
+2030;perthousand;PER MILLE SIGN
+20A7;peseta;PESETA SIGN
+03C6;phi;GREEK SMALL LETTER PHI
+03D5;phi1;GREEK PHI SYMBOL
+03C0;pi;GREEK SMALL LETTER PI
+002B;plus;PLUS SIGN
+00B1;plusminus;PLUS-MINUS SIGN
+211E;prescription;PRESCRIPTION TAKE
+220F;product;N-ARY PRODUCT
+2282;propersubset;SUBSET OF
+2283;propersuperset;SUPERSET OF
+221D;proportional;PROPORTIONAL TO
+03C8;psi;GREEK SMALL LETTER PSI
+0071;q;LATIN SMALL LETTER Q
+003F;question;QUESTION MARK
+00BF;questiondown;INVERTED QUESTION MARK
+F7BF;questiondownsmall;SMALL CAPITAL INVERTED QUESTION MARK
+F73F;questionsmall;SMALL CAPITAL QUESTION MARK
+0022;quotedbl;QUOTATION MARK
+201E;quotedblbase;DOUBLE LOW-9 QUOTATION MARK
+201C;quotedblleft;LEFT DOUBLE QUOTATION MARK
+201D;quotedblright;RIGHT DOUBLE QUOTATION MARK
+2018;quoteleft;LEFT SINGLE QUOTATION MARK
+201B;quotereversed;SINGLE HIGH-REVERSED-9 QUOTATION MARK
+2019;quoteright;RIGHT SINGLE QUOTATION MARK
+201A;quotesinglbase;SINGLE LOW-9 QUOTATION MARK
+0027;quotesingle;APOSTROPHE
+0072;r;LATIN SMALL LETTER R
+0155;racute;LATIN SMALL LETTER R WITH ACUTE
+221A;radical;SQUARE ROOT
+F8E5;radicalex;RADICAL EXTENDER
+0159;rcaron;LATIN SMALL LETTER R WITH CARON
+0157;rcommaaccent;LATIN SMALL LETTER R WITH CEDILLA
+2286;reflexsubset;SUBSET OF OR EQUAL TO
+2287;reflexsuperset;SUPERSET OF OR EQUAL TO
+00AE;registered;REGISTERED SIGN
+F8E8;registersans;REGISTERED SIGN SANS SERIF
+F6DA;registerserif;REGISTERED SIGN SERIF
+2310;revlogicalnot;REVERSED NOT SIGN
+03C1;rho;GREEK SMALL LETTER RHO
+02DA;ring;RING ABOVE
+F6F1;rsuperior;SUPERSCRIPT LATIN SMALL LETTER R
+2590;rtblock;RIGHT HALF BLOCK
+F6DD;rupiah;RUPIAH SIGN
+0073;s;LATIN SMALL LETTER S
+015B;sacute;LATIN SMALL LETTER S WITH ACUTE
+0161;scaron;LATIN SMALL LETTER S WITH CARON
+015F;scedilla;LATIN SMALL LETTER S WITH CEDILLA
+F6C2;scedilla;LATIN SMALL LETTER S WITH CEDILLA;Duplicate
+015D;scircumflex;LATIN SMALL LETTER S WITH CIRCUMFLEX
+0219;scommaaccent;LATIN SMALL LETTER S WITH COMMA BELOW
+2033;second;DOUBLE PRIME
+00A7;section;SECTION SIGN
+003B;semicolon;SEMICOLON
+0037;seven;DIGIT SEVEN
+215E;seveneighths;VULGAR FRACTION SEVEN EIGHTHS
+2087;seveninferior;SUBSCRIPT SEVEN
+F737;sevenoldstyle;OLDSTYLE DIGIT SEVEN
+2077;sevensuperior;SUPERSCRIPT SEVEN
+2592;shade;MEDIUM SHADE
+03C3;sigma;GREEK SMALL LETTER SIGMA
+03C2;sigma1;GREEK SMALL LETTER FINAL SIGMA
+223C;similar;TILDE OPERATOR
+0036;six;DIGIT SIX
+2086;sixinferior;SUBSCRIPT SIX
+F736;sixoldstyle;OLDSTYLE DIGIT SIX
+2076;sixsuperior;SUPERSCRIPT SIX
+002F;slash;SOLIDUS
+263A;smileface;WHITE SMILING FACE
+0020;space;SPACE
+00A0;space;NO-BREAK SPACE;Duplicate
+2660;spade;BLACK SPADE SUIT
+F6F2;ssuperior;SUPERSCRIPT LATIN SMALL LETTER S
+00A3;sterling;POUND SIGN
+220B;suchthat;CONTAINS AS MEMBER
+2211;summation;N-ARY SUMMATION
+263C;sun;WHITE SUN WITH RAYS
+0074;t;LATIN SMALL LETTER T
+03C4;tau;GREEK SMALL LETTER TAU
+0167;tbar;LATIN SMALL LETTER T WITH STROKE
+0165;tcaron;LATIN SMALL LETTER T WITH CARON
+0163;tcommaaccent;LATIN SMALL LETTER T WITH CEDILLA
+021B;tcommaaccent;LATIN SMALL LETTER T WITH COMMA BELOW;Duplicate
+2234;therefore;THEREFORE
+03B8;theta;GREEK SMALL LETTER THETA
+03D1;theta1;GREEK THETA SYMBOL
+00FE;thorn;LATIN SMALL LETTER THORN
+0033;three;DIGIT THREE
+215C;threeeighths;VULGAR FRACTION THREE EIGHTHS
+2083;threeinferior;SUBSCRIPT THREE
+F733;threeoldstyle;OLDSTYLE DIGIT THREE
+00BE;threequarters;VULGAR FRACTION THREE QUARTERS
+F6DE;threequartersemdash;THREE QUARTERS EM DASH
+00B3;threesuperior;SUPERSCRIPT THREE
+02DC;tilde;SMALL TILDE
+0303;tildecomb;COMBINING TILDE
+0384;tonos;GREEK TONOS
+2122;trademark;TRADE MARK SIGN
+F8EA;trademarksans;TRADE MARK SIGN SANS SERIF
+F6DB;trademarkserif;TRADE MARK SIGN SERIF
+25BC;triagdn;BLACK DOWN-POINTING TRIANGLE
+25C4;triaglf;BLACK LEFT-POINTING POINTER
+25BA;triagrt;BLACK RIGHT-POINTING POINTER
+25B2;triagup;BLACK UP-POINTING TRIANGLE
+F6F3;tsuperior;SUPERSCRIPT LATIN SMALL LETTER T
+0032;two;DIGIT TWO
+2025;twodotenleader;TWO DOT LEADER
+2082;twoinferior;SUBSCRIPT TWO
+F732;twooldstyle;OLDSTYLE DIGIT TWO
+00B2;twosuperior;SUPERSCRIPT TWO
+2154;twothirds;VULGAR FRACTION TWO THIRDS
+0075;u;LATIN SMALL LETTER U
+00FA;uacute;LATIN SMALL LETTER U WITH ACUTE
+016D;ubreve;LATIN SMALL LETTER U WITH BREVE
+00FB;ucircumflex;LATIN SMALL LETTER U WITH CIRCUMFLEX
+00FC;udieresis;LATIN SMALL LETTER U WITH DIAERESIS
+00F9;ugrave;LATIN SMALL LETTER U WITH GRAVE
+01B0;uhorn;LATIN SMALL LETTER U WITH HORN
+0171;uhungarumlaut;LATIN SMALL LETTER U WITH DOUBLE ACUTE
+016B;umacron;LATIN SMALL LETTER U WITH MACRON
+005F;underscore;LOW LINE
+2017;underscoredbl;DOUBLE LOW LINE
+222A;union;UNION
+2200;universal;FOR ALL
+0173;uogonek;LATIN SMALL LETTER U WITH OGONEK
+2580;upblock;UPPER HALF BLOCK
+03C5;upsilon;GREEK SMALL LETTER UPSILON
+03CB;upsilondieresis;GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+03B0;upsilondieresistonos;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+03CD;upsilontonos;GREEK SMALL LETTER UPSILON WITH TONOS
+016F;uring;LATIN SMALL LETTER U WITH RING ABOVE
+0169;utilde;LATIN SMALL LETTER U WITH TILDE
+0076;v;LATIN SMALL LETTER V
+0077;w;LATIN SMALL LETTER W
+1E83;wacute;LATIN SMALL LETTER W WITH ACUTE
+0175;wcircumflex;LATIN SMALL LETTER W WITH CIRCUMFLEX
+1E85;wdieresis;LATIN SMALL LETTER W WITH DIAERESIS
+2118;weierstrass;SCRIPT CAPITAL P
+1E81;wgrave;LATIN SMALL LETTER W WITH GRAVE
+0078;x;LATIN SMALL LETTER X
+03BE;xi;GREEK SMALL LETTER XI
+0079;y;LATIN SMALL LETTER Y
+00FD;yacute;LATIN SMALL LETTER Y WITH ACUTE
+0177;ycircumflex;LATIN SMALL LETTER Y WITH CIRCUMFLEX
+00FF;ydieresis;LATIN SMALL LETTER Y WITH DIAERESIS
+00A5;yen;YEN SIGN
+1EF3;ygrave;LATIN SMALL LETTER Y WITH GRAVE
+007A;z;LATIN SMALL LETTER Z
+017A;zacute;LATIN SMALL LETTER Z WITH ACUTE
+017E;zcaron;LATIN SMALL LETTER Z WITH CARON
+017C;zdotaccent;LATIN SMALL LETTER Z WITH DOT ABOVE
+0030;zero;DIGIT ZERO
+2080;zeroinferior;SUBSCRIPT ZERO
+F730;zerooldstyle;OLDSTYLE DIGIT ZERO
+2070;zerosuperior;SUPERSCRIPT ZERO
+03B6;zeta;GREEK SMALL LETTER ZETA
diff --git a/program/libraries/afm/test-afm.c b/program/libraries/afm/test-afm.c
new file mode 100644 (file)
index 0000000..8347ad6
--- /dev/null
@@ -0,0 +1,108 @@
+#include "rrd_afm.h"
+#include <stdio.h>
+
+FILE *fp;
+
+static void make_tests(void);
+static void print(const char *s);
+double y = 0;
+//static const char *font = "Times-Roman";
+static const char *font = "Times Bold Italic";
+//static const char *font = "Courier";
+//static const char *font = "Courier Bold Oblique";
+
+void make_tests()
+{
+#ifdef __APPLE__
+#define charset_legend "Macintosh charset"
+#define AE "\xAE"
+#define ae "\xBE"
+#define oe "\xBF"
+#define aa "\x8C"
+#define NBSP "\x00CA"
+#else
+#define charset_legend "IsoLatin1 charset"
+#define AE "\xC6"
+#define ae "\xE6"
+#define oe "\xF8"
+#define aa "\xA5"
+#define NBSP "\x00A0"
+#endif
+  print(AE); /* very wide char */
+  print(AE AE AE AE AE AE AE AE AE AE AE AE AE AE AE);
+  print(charset_legend);
+  print("S,");
+  print("sfil");
+  print("Hello, world");
+  print("AVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAV");
+  print("AAAAAAAAAAAAAAAAAAVVVVVVVVVVVVVVVVVV");
+  print("fiffififfififfififfififfififfififfi");
+  print("fi");
+  print("fil");
+  print("fifififififififififififififififififififififififififi");
+  print(AE "bleskiver med gl"oe"gg. " NBSP NBSP NBSP NBSP NBSP NBSP NBSP
+      AE" Fywerhus: 'A "ae" u "aa" "ae" "oe" i "ae" fywer'.");
+  print("Ingef"ae"rp"ae"rer med karamelsauce. R"oe"dgr"oe"d med fl"oe"de.");
+  print("(Optional.) Ligature sequence where successor and ligature are both names.  The current character may join ...");
+}
+
+static void vline(double x, double y1, double y2)
+{
+  fprintf(fp, "<line x1=\"%.2f\" y1=\"%.2f\" x2=\"%.2f\" y2=\"%.2f\""
+      " stroke-width=\"1\" stroke=\"#000\"/>\n",
+      x, y1, x, y2);
+}
+
+static void print(const char *s)
+{
+  double size = 12;
+  double x = 10;
+  double width = afm_get_text_width(0, font, size, 4, s);
+  unsigned char *up = (unsigned char*)s;
+  fprintf(stderr, "Width = %f for '%s'\n", width, s);
+  y += 10;
+  vline(x, y, y + 5);
+  fprintf(fp, "<text x=\"%.2f\" y=\"%.2f\" font-size=\"%.2f\">", x, y, size);
+  while (*up) {
+    unsigned char ch = afm_host2unicode(*up);
+    if (ch < 127)
+      putc(ch, fp);
+    else
+      fprintf(fp, "&#%d;", ch);
+    up++;
+  }
+  fputs("</text>\n", fp);
+  vline(x + width, y, y + 5);
+  y += 1.1 * size;
+}
+
+static void header()
+{
+   fprintf(fp,
+ "<?xml version=\"1.0\" standalone=\"no\"?>\n"
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\"\n"
+ "   \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"
+ "<svg width=\"650\" height=\"400\" preserveAspectRatio=\"xMidYMid\"\n"
+ "    font-family=\"%s\">\n", font);
+ }
+
+
+static void footer()
+{
+   fputs("</svg>\n", fp);
+}
+
+int main()
+{
+  fp = fopen("test.svg", "w");
+  if (fp == NULL) {
+    fprintf(stderr, "Can't create output.\n");
+    exit(1);
+  }
+  header();
+  make_tests();
+  footer();
+  fclose(fp);
+  return 0;
+}
+
diff --git a/program/netware/Makefile b/program/netware/Makefile
new file mode 100644 (file)
index 0000000..c138dfa
--- /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)/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/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.spec b/program/rrdtool.spec
new file mode 100644 (file)
index 0000000..97871d1
--- /dev/null
@@ -0,0 +1,273 @@
+Summary: Round Robin Database Tool to store and display time-series data
+Name: rrdtool
+Version: 1.2.23
+Release: 3%{?dist}
+License: GPL
+Group: Applications/Databases
+URL: http://oss.oetiker.ch/%{name}/
+Source: http://oss.oetiker.ch/%{name}/pub/%{name}-%{version}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+BuildRequires: gcc-c++, openssl-devel
+BuildRequires: libpng-devel, zlib-devel, libart_lgpl-devel >= 2.0
+BuildRequires: freetype-devel, python-devel >= 2.3
+Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))
+
+%{!?python_sitearch: %define python_sitearch %(%{__python} -c 'from distutils import sysconfig; print sysconfig.get_python_lib(1)')}
+%{!?python_version: %define python_version %(%{__python} -c 'import sys; print sys.version.split(" ")[0]')}
+
+%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 -n perl-%{name}
+Summary: Perl RRDtool bindings
+Group: Development/Languages
+Requires: %{name} = %{version}-%{release}
+Obsoletes: %{name}-perl <= %{version}
+Provides: %{name}-perl = %{version}
+
+%description -n perl-%{name}
+The Perl RRDtool bindings
+
+%package -n python-%{name}
+Summary: Python RRDtool bindings
+Group: Development/Languages
+BuildRequires: python
+Requires: python >= %{python_version}
+Requires: %{name} = %{version}-%{release}
+
+%description -n python-%{name}
+Python RRDtool bindings.
+
+%prep
+%setup
+
+# 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
+
+# Shouldn't be necessary when using --libdir, but
+# introduces hardcoded rpaths where it shouldn't,
+# if not done...
+%{__perl} -pi.orig -e 's|/lib\b|/%{_lib}|g' \
+    configure Makefile.in
+
+%build
+%configure \
+    --program-prefix="%{?_program_prefix}" \
+    --libdir=%{_libdir} \
+    --disable-static \
+    --with-pic \
+    --with-perl-options='INSTALLDIRS="vendor"'
+
+# 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}
+
+# 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
+
+# Pesky RRDp.pm...
+%{__mv} $RPM_BUILD_ROOT%{perl_vendorarch}/../RRDp.pm $RPM_BUILD_ROOT%{perl_vendorarch}/
+
+# 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%{_datadir}/doc/%{name}-%{version}/{txt,html}/ \
+       $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}
+
+%clean
+%{__rm} -rf $RPM_BUILD_ROOT
+
+%post -p /sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+%files
+%{_bindir}/*
+%{_libdir}/*.so.*
+%{_libdir}/rrdtool/*
+%{_datadir}/%{name}/fonts/*
+%{_mandir}/man1/*
+
+%files devel
+%defattr(-,root,root,-)
+%{_includedir}/*.h
+%exclude %{_libdir}/*.la
+%{_libdir}/*.so
+
+%files doc
+%defattr(-,root,root,-)
+%doc CHANGES CONTRIBUTORS COPYING COPYRIGHT README TODO NEWS THREADS doc2/html doc2/txt
+%doc examples
+
+%files -n perl-%{name}
+%defattr(-,root,root,-)
+%doc doc3/html
+%{_mandir}/man3/*
+%{perl_vendorarch}/*.pm
+%attr(0755,root,root) %{perl_vendorarch}/auto/RRDs/*
+
+%files -n python-%{name}
+%defattr(-,root,root,-)
+%doc bindings/python/AUTHORS bindings/python/COPYING bindings/python/README
+%{python_sitearch}/rrdtoolmodule.so
+
+%changelog
+* 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/DejaVuSansMono-Roman.ttf b/program/src/DejaVuSansMono-Roman.ttf
new file mode 100644 (file)
index 0000000..691c8d6
Binary files /dev/null and b/program/src/DejaVuSansMono-Roman.ttf differ
diff --git a/program/src/Makefile.am b/program/src/Makefile.am
new file mode 100644 (file)
index 0000000..5ed0558
--- /dev/null
@@ -0,0 +1,134 @@
+## 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
+fontsdir =  $(datadir)/rrdtool/fonts
+fonts_DATA = DejaVuSansMono-Roman.ttf
+
+#INCLUDES = $(FREETYPE_INCLUDES) $(ART_INCLUDES) \
+#           $(PNG_INCLUDES) $(ZLIB_INCLUDES)
+RRD_DEFAULT_FONT=@RRD_DEFAULT_FONT@
+AM_CPPFLAGS = -DRRD_DEFAULT_FONT=\"$(RRD_DEFAULT_FONT)\" -DNUMVERS=@NUMVERS@
+
+UPD_C_FILES =          \
+       rrd_getopt.c    \
+       rrd_getopt1.c   \
+       parsetime.c     \
+       rrd_hw.c        \
+       rrd_diff.c      \
+       rrd_format.c    \
+       rrd_info.c      \
+       rrd_error.c     \
+       rrd_open.c      \
+       rrd_nan_inf.c   \
+       rrd_rpncalc.c   \
+       rrd_update.c
+
+RRD_C_FILES =          \
+       hash_32.c       \
+       pngsize.c       \
+       rrd_create.c    \
+       rrd_dump.c      \
+       rrd_fetch.c     \
+       rrd_graph.c     \
+       rrd_graph_helper.c      \
+       rrd_last.c      \
+       rrd_lastupdate.c        \
+       rrd_first.c     \
+       rrd_resize.c    \
+       rrd_restore.c   \
+       rrd_tune.c      \
+       rrd_version.c   \
+       rrd_xport.c     \
+        art_rgba_svp.c \
+       rrd_gfx.c \
+       rrd_afm.c rrd_afm_data.c \
+       rrd_tool.c
+
+noinst_HEADERS = \
+        art_rgba_svp.h \
+       unused.h \
+       rrd_gfx.h \
+       rrd_getopt.h parsetime.h \
+       rrd_format.h rrd_tool.h rrd_xport.h rrd.h rrd_hw.h rrd_rpncalc.h \
+       rrd_nan_inf.h fnv.h rrd_graph.h rrd_afm.h rrd_afm_data.h \
+       rrd_is_thread_safe.h
+
+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_LIBADD          = librrdupd.la $(ALL_LIBS)
+
+# This flag accepts an argument of the form current[:revision[:age]]. So,
+# passing -version-info 3:12:1 sets current to 3, revision to 12, and age to 1.
+#
+# If either revision or age are omitted, they default to 0. Also note that
+# age must be less than or equal to the current interface number.
+#
+# Here are a set of rules to help you update your library version information:
+#
+#   1. Start with version information of 0:0:0 for each libtool library.
+#
+#   2. Update the version information only immediately before a public
+#   release of your software. More frequent updates are unnecessary, and
+#   only guarantee that the current interface number gets larger faster.
+#
+#   3. If the library source code has changed at all since the last update,
+#   then increment revision (c:r:a becomes c:r+1:a).
+#
+#   4. If any interfaces have been added, removed, or changed since the last
+#   update, increment current, and set revision to 0.
+#
+#   5. If any interfaces have been added since the last public release, then
+#   increment age.
+#
+#   6. If any interfaces have been removed since the last public release,
+#   then set age to 0.
+#
+# Never try to set the interface numbers so that they correspond to the
+# release number of your package. This is an abuse that only fosters
+# misunderstanding of the purpose of library versions. Instead, use the
+# -release flag (see Release numbers), but be warned that every release of
+# your package will not be binary compatible with any other release.
+#
+# see http://www.gnu.org/software/libtool/manual.html#SEC32 for explanation
+librrd_la_LDFLAGS         = -version-info 2:11:0
+
+librrd_th_la_SOURCES         = $(UPD_C_FILES) $(RRD_C_FILES) rrd_thread_safe.c
+librrd_th_la_CFLAGS          = $(MULTITHREAD_CFLAGS)
+librrd_th_la_LDFLAGS         = $(MULTITHREAD_LDFLAGS) -version-info 2:11:0
+librrd_th_la_LIBADD          = $(ALL_LIBS)
+
+include_HEADERS        = rrd.h
+
+bin_PROGRAMS   = rrdtool rrdupdate
+
+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 = 
+rrdtool_DEPENDENCIES = rrd_tool.o librrd.la
+rrdtool_LDADD  = librrd.la
+
+# strftime is here because we do not usually need it. unices have propper
+# iso date support
+EXTRA_DIST= strftime.c strftime.h $(fonts_DATA) \
+       win32comp.c  rrd_thread_safe_nt.c get_ver.awk
+
diff --git a/program/src/VeraMono.ttf b/program/src/VeraMono.ttf
new file mode 100644 (file)
index 0000000..139f0b4
Binary files /dev/null and b/program/src/VeraMono.ttf differ
diff --git a/program/src/art_rgba_svp.c b/program/src/art_rgba_svp.c
new file mode 100644 (file)
index 0000000..25083d7
--- /dev/null
@@ -0,0 +1,333 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  art_rgba_svp.c: A slightly modified version of art_rgb_svp to render into rgba buffer
+ *
+ *  This program 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.
+ *
+ *  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 Library General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Authors:
+ *    Raph Levien <raph@acm.org>
+ *    Lauris Kaplinski <lauris@ariman.ee>
+ *
+ *  Copyright (C) 1998 Raph Levien
+ *
+ */
+
+#define SP_ART_RGBA_SVP_C
+
+/* Render a sorted vector path into an RGBA buffer. */
+
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_render_aa.h>
+#include <libart_lgpl/art_rgb.h>
+
+#include "art_rgba_svp.h"
+#include "unused.h"
+
+static void art_rgba_fill_run (art_u8 * linebuf, art_u8 r, art_u8 g, art_u8 b, int n);
+static void art_rgba_run_alpha (art_u8 * linebuf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n);
+
+typedef struct _ArtRgbaSVPAlphaData ArtRgbaSVPAlphaData;
+
+struct _ArtRgbaSVPAlphaData {
+  int alphatab[256];
+  art_u8 r, g, b, alpha;
+  art_u8 *buf;
+  int rowstride;
+  int libart_x0, libart_x1;
+};
+
+static void
+art_rgba_svp_alpha_callback (void *callback_data, int UNUSED(y),
+                           int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+  ArtRgbaSVPAlphaData *data = callback_data;
+  art_u8 *linebuf;
+  int run_x0, run_x1;
+  art_u32 running_sum = start;
+  int libart_x0, libart_x1;
+  int k;
+  art_u8 r, g, b;
+  int *alphatab;
+  int alpha;
+
+  linebuf = data->buf;
+  libart_x0 = data->libart_x0;
+  libart_x1 = data->libart_x1;
+
+  r = data->r;
+  g = data->g;
+  b = data->b;
+  alphatab = data->alphatab;
+
+  if (n_steps > 0)
+    {
+      run_x1 = steps[0].x;
+      if (run_x1 > libart_x0)
+       {
+         alpha = (running_sum >> 16) & 0xff;
+         if (alpha)
+           art_rgba_run_alpha (linebuf,
+                              r, g, b, alphatab[alpha],
+                              run_x1 - libart_x0);
+       }
+
+      /* render the steps into tmpbuf */
+      for (k = 0; k < n_steps - 1; k++)
+       {
+         running_sum += steps[k].delta;
+         run_x0 = run_x1;
+         run_x1 = steps[k + 1].x;
+         if (run_x1 > run_x0)
+           {
+             alpha = (running_sum >> 16) & 0xff;
+             if (alpha)
+               art_rgba_run_alpha (linebuf + (run_x0 - libart_x0) * 4,
+                                  r, g, b, alphatab[alpha],
+                                  run_x1 - run_x0);
+           }
+       }
+      running_sum += steps[k].delta;
+      if (libart_x1 > run_x1)
+       {
+         alpha = (running_sum >> 16) & 0xff;
+         if (alpha)
+           art_rgba_run_alpha (linebuf + (run_x1 - libart_x0) * 4,
+                              r, g, b, alphatab[alpha],
+                              libart_x1 - run_x1);
+       }
+    }
+  else
+    {
+      alpha = (running_sum >> 16) & 0xff;
+      if (alpha)
+       art_rgba_run_alpha (linebuf,
+                          r, g, b, alphatab[alpha],
+                          libart_x1 - libart_x0);
+    }
+
+  data->buf += data->rowstride;
+}
+
+static void
+art_rgba_svp_alpha_opaque_callback (void *callback_data, int UNUSED(y),
+                                  int start,
+                                  ArtSVPRenderAAStep *steps, int n_steps)
+{
+  ArtRgbaSVPAlphaData *data = callback_data;
+  art_u8 *linebuf;
+  int run_x0, run_x1;
+  art_u32 running_sum = start;
+  int libart_x0, libart_x1;
+  int k;
+  art_u8 r, g, b;
+  int *alphatab;
+  int alpha;
+
+  linebuf = data->buf;
+  libart_x0 = data->libart_x0;
+  libart_x1 = data->libart_x1;
+
+  r = data->r;
+  g = data->g;
+  b = data->b;
+  alphatab = data->alphatab;
+
+  if (n_steps > 0)
+    {
+      run_x1 = steps[0].x;
+      if (run_x1 > libart_x0)
+       {
+         alpha = running_sum >> 16;
+         if (alpha)
+           {
+             if (alpha >= 255)
+               art_rgba_fill_run (linebuf,
+                                 r, g, b,
+                                 run_x1 - libart_x0);
+             else
+               art_rgba_run_alpha (linebuf,
+                                  r, g, b, alphatab[alpha],
+                                  run_x1 - libart_x0);
+           }
+       }
+
+      /* render the steps into tmpbuf */
+      for (k = 0; k < n_steps - 1; k++)
+       {
+         running_sum += steps[k].delta;
+         run_x0 = run_x1;
+         run_x1 = steps[k + 1].x;
+         if (run_x1 > run_x0)
+           {
+             alpha = running_sum >> 16;
+             if (alpha)
+               {
+                 if (alpha >= 255)
+                   art_rgba_fill_run (linebuf + (run_x0 - libart_x0) * 4,
+                                     r, g, b,
+                                     run_x1 - run_x0);
+                 else
+                   art_rgba_run_alpha (linebuf + (run_x0 - libart_x0) * 4,
+                                      r, g, b, alphatab[alpha],
+                                      run_x1 - run_x0);
+               }
+           }
+       }
+      running_sum += steps[k].delta;
+      if (libart_x1 > run_x1)
+       {
+         alpha = running_sum >> 16;
+         if (alpha)
+           {
+             if (alpha >= 255)
+               art_rgba_fill_run (linebuf + (run_x1 - libart_x0) * 4,
+                                 r, g, b,
+                                 libart_x1 - run_x1);
+             else
+               art_rgba_run_alpha (linebuf + (run_x1 - libart_x0) * 4,
+                                  r, g, b, alphatab[alpha],
+                                  libart_x1 - run_x1);
+           }
+       }
+    }
+  else
+    {
+      alpha = running_sum >> 16;
+      if (alpha)
+       {
+         if (alpha >= 255)
+           art_rgba_fill_run (linebuf,
+                             r, g, b,
+                             libart_x1 - libart_x0);
+         else
+           art_rgba_run_alpha (linebuf,
+                              r, g, b, alphatab[alpha],
+                              libart_x1 - libart_x0);
+       }
+    }
+
+  data->buf += data->rowstride;
+}
+
+/**
+ * gnome_print_art_rgba_svp_alpha: Alpha-composite sorted vector path over RGBA buffer.
+ * @svp: The source sorted vector path.
+ * @libart_x0: Left coordinate of destination rectangle.
+ * @libart_y0: Top coordinate of destination rectangle.
+ * @libart_x1: Right coordinate of destination rectangle.
+ * @libart_y1: Bottom coordinate of destination rectangle.
+ * @rgba: Color in 0xRRGGBBAA format.
+ * @buf: Destination RGB buffer.
+ * @rowstride: Rowstride of @buf buffer.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
+ *
+ * Renders the shape specified with @svp over the @buf RGB buffer.
+ * @libart_x1 - @x0 specifies the width, and @libart_y1 - @libart_y0 specifies the height,
+ * of the rectangle rendered. The new pixels are stored starting at
+ * the first byte of @buf. Thus, the @x0 and @libart_y0 parameters specify
+ * an offset within @svp, and may be tweaked as a way of doing
+ * integer-pixel translations without fiddling with @svp itself.
+ *
+ * The @rgba argument specifies the color for the rendering. Pixels of
+ * entirely 0 winding number are left untouched. Pixels of entirely
+ * 1 winding number have the color @rgba composited over them (ie,
+ * are replaced by the red, green, blue components of @rgba if the alpha
+ * component is 0xff). Pixels of intermediate coverage are interpolated
+ * according to the rule in @alphagamma, or default to linear if
+ * @alphagamma is NULL.
+ **/
+void
+gnome_print_art_rgba_svp_alpha (const ArtSVP *svp,
+                               int libart_x0, int libart_y0, int libart_x1, int libart_y1,
+                               art_u32 rgba,
+                               art_u8 *buf, int rowstride,
+                               ArtAlphaGamma UNUSED(*alphagamma))
+{
+  ArtRgbaSVPAlphaData data;
+  int r, g, b, alpha;
+  int i;
+  int a, da;
+
+  r = rgba >> 24;
+  g = (rgba >> 16) & 0xff;
+  b = (rgba >> 8) & 0xff;
+  alpha = rgba & 0xff;
+
+  data.r = r;
+  data.g = g;
+  data.b = b;
+  data.alpha = alpha;
+
+  a = 0x8000;
+  da = (alpha * 66051 + 0x80) >> 8; /* 66051 equals 2 ^ 32 / (255 * 255) */
+
+  for (i = 0; i < 256; i++)
+    {
+      data.alphatab[i] = a >> 16;
+      a += da;
+    }
+
+  data.buf = buf;
+  data.rowstride = rowstride;
+  data.libart_x0 = libart_x0;
+  data.libart_x1 = libart_x1;
+  if (alpha == 255)
+    art_svp_render_aa (svp, libart_x0, libart_y0, libart_x1, libart_y1, art_rgba_svp_alpha_opaque_callback,
+                      &data);
+  else
+    art_svp_render_aa (svp, libart_x0, libart_y0, libart_x1, libart_y1, art_rgba_svp_alpha_callback, &data);
+}
+
+static void
+art_rgba_fill_run (art_u8 * buf, art_u8 r, art_u8 g, art_u8 b, int n)
+{
+       int i;
+
+       for (i = 0; i < n; i++) {
+               * buf++ = r;
+               * buf++ = g;
+               * buf++ = b;
+               * buf++ = 255;
+       }
+}
+
+/* fixme: this */
+
+static void
+art_rgba_run_alpha (art_u8 * buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n)
+{
+       int i;
+       int br, bg, bb, ba;
+       int cr, cg, cb;
+
+       for (i = 0; i < n; i++) {
+               br = * (buf + 0);
+               bg = * (buf + 1);
+               bb = * (buf + 2);
+               ba = * (buf + 3);
+
+               cr = (br * ba + 0x80) >> 8;
+               cg = (bg * ba + 0x80) >> 8;
+               cb = (bb * ba + 0x80) >> 8;
+
+               * buf++ = cr + (((r - cr) * alpha + 0x80) >> 8);
+               * buf++ = cg + (((g - cg) * alpha + 0x80) >> 8);
+               * buf++ = cb + (((b - cb) * alpha + 0x80) >> 8);
+               * buf++ = ba + (((255 - ba) * alpha + 0x80) >> 8);
+       }
+}
+
+
diff --git a/program/src/art_rgba_svp.h b/program/src/art_rgba_svp.h
new file mode 100644 (file)
index 0000000..4cac175
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef SP_ART_RGBA_SVP_H
+#define SP_ART_RGBA_SVP_H
+
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_alphagamma.h>
+#include <libart_lgpl/art_affine.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_uta.h>
+
+void
+gnome_print_art_rgba_svp_alpha (const ArtSVP *svp,
+                               int x0, int y0, int x1, int y1,
+                               art_u32 rgba,
+                               art_u8 *buf, int rowstride,
+                               ArtAlphaGamma *alphagamma);
+
+#endif /* SP_ART_RGBA_SVP_H */
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..e69f9ce
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * 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/gdpng.c b/program/src/gdpng.c
new file mode 100644 (file)
index 0000000..99e7db6
--- /dev/null
@@ -0,0 +1,71 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * gdpng.c  add PNG output routine to gd library
+ *****************************************************************************/
+
+#include <png.h>
+#include <gd.h>
+#include <stdlib.h>
+
+typedef struct _jmpbuf_wrapper {
+  jmp_buf jmpbuf;
+} jmpbuf_wrapper;
+
+static jmpbuf_wrapper gdPngJmpbufStruct;
+
+void gdImagePng(gdImagePtr im, FILE *out)
+{
+    int i;
+    png_colorp palette;
+    png_structp png_write_ptr = 
+       png_create_write_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_write_ptr);
+
+    if (setjmp(gdPngJmpbufStruct.jmpbuf)) {
+      png_destroy_write_struct(&png_write_ptr, &info_ptr);
+      return;
+    }
+
+    palette = (png_colorp)png_malloc (png_write_ptr,
+                                     im->colorsTotal*sizeof(png_color));
+    if (palette == NULL){
+      png_destroy_write_struct(&png_write_ptr, &info_ptr);
+      return;
+    }
+    
+    
+    png_init_io(png_write_ptr, out);
+    png_set_write_status_fn(png_write_ptr, NULL);
+    png_set_IHDR(png_write_ptr,info_ptr,
+                im->sx,im->sy,im->colorsTotal > 16 ? 8:4,
+                PNG_COLOR_TYPE_PALETTE,
+                im->interlace ? PNG_INTERLACE_ADAM7: PNG_INTERLACE_NONE ,
+                PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+    for(i=0;i<im->colorsTotal;i++){
+       palette[i].red = im->red[i];
+       palette[i].green = im->green[i];
+       palette[i].blue = im->blue[i];
+    }
+    png_set_PLTE(png_write_ptr, info_ptr, palette, im->colorsTotal);
+
+    /* choose between speed (1) and space (9) optimisation */
+    /* we want to be fast ... */
+    png_set_compression_level(png_write_ptr,1);
+    png_set_filter(png_write_ptr,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
+    /* store file info */
+    png_write_info(png_write_ptr, info_ptr);
+    png_set_packing(png_write_ptr);
+    png_write_image(png_write_ptr, im->pixels);
+    png_write_end(png_write_ptr, info_ptr);
+    png_free(png_write_ptr, palette);
+    png_destroy_write_struct(&png_write_ptr, &info_ptr);
+}
+
+
+
+
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/hash_32.c b/program/src/hash_32.c
new file mode 100644 (file)
index 0000000..d7edbee
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * 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/parsetime.c b/program/src/parsetime.c
new file mode 100644 (file)
index 0000000..919dd50
--- /dev/null
@@ -0,0 +1,981 @@
+/*  
+ *  parsetime.c - parse time for at(1)
+ *  Copyright (C) 1993, 1994  Thomas Koenig
+ *
+ *  modifications for English-language times
+ *  Copyright (C) 1993  David Parsons
+ *
+ *  A lot of modifications and extensions 
+ *  (including the new syntax being useful for RRDB)
+ *  Copyright (C) 1999  Oleg Cherevko (aka Olwi Deer)
+ *
+ *  severe structural damage inflicted by Tobi Oetiker in 1999
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author(s) may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* NOTE: nothing in here is thread-safe!!!! Not even the localtime
+   calls ... */
+
+/*
+ * The BNF-like specification of the time syntax parsed is below:
+ *                                                               
+ * As usual, [ X ] means that X is optional, { X } means that X may
+ * be either omitted or specified as many times as needed,
+ * alternatives are separated by |, brackets are used for grouping.
+ * (# marks the beginning of comment that extends to the end of line)
+ *
+ * TIME-SPECIFICATION ::= TIME-REFERENCE [ OFFSET-SPEC ] |
+ *                                        OFFSET-SPEC   |
+ *                        ( START | END ) OFFSET-SPEC 
+ *
+ * TIME-REFERENCE ::= NOW | TIME-OF-DAY-SPEC [ DAY-SPEC-1 ] |
+ *                        [ TIME-OF-DAY-SPEC ] DAY-SPEC-2
+ *
+ * TIME-OF-DAY-SPEC ::= NUMBER (':') NUMBER [am|pm] | # HH:MM
+ *                     'noon' | 'midnight' | 'teatime'
+ *
+ * DAY-SPEC-1 ::= NUMBER '/' NUMBER '/' NUMBER |  # MM/DD/[YY]YY
+ *                NUMBER '.' NUMBER '.' NUMBER |  # DD.MM.[YY]YY
+ *                NUMBER                          # Seconds since 1970
+ *                NUMBER                          # YYYYMMDD
+ *
+ * DAY-SPEC-2 ::= MONTH-NAME NUMBER [NUMBER] |    # Month DD [YY]YY
+ *                'yesterday' | 'today' | 'tomorrow' |
+ *                DAY-OF-WEEK
+ *
+ *
+ * OFFSET-SPEC ::= '+'|'-' NUMBER TIME-UNIT { ['+'|'-'] NUMBER TIME-UNIT }
+ *
+ * TIME-UNIT ::= SECONDS | MINUTES | HOURS |
+ *               DAYS | WEEKS | MONTHS | YEARS
+ *
+ * NOW ::= 'now' | 'n'
+ *
+ * START ::= 'start' | 's'
+ * END   ::= 'end' | 'e'
+ *
+ * SECONDS ::= 'seconds' | 'second' | 'sec' | 's'
+ * MINUTES ::= 'minutes' | 'minute' | 'min' | 'm'
+ * HOURS   ::= 'hours' | 'hour' | 'hr' | 'h'
+ * DAYS    ::= 'days' | 'day' | 'd'
+ * WEEKS   ::= 'weeks' | 'week' | 'wk' | 'w'
+ * MONTHS  ::= 'months' | 'month' | 'mon' | 'm'
+ * YEARS   ::= 'years' | 'year' | 'yr' | 'y'
+ *
+ * MONTH-NAME ::= 'jan' | 'january' | 'feb' | 'february' | 'mar' | 'march' |
+ *                'apr' | 'april' | 'may' | 'jun' | 'june' | 'jul' | 'july' |
+ *                'aug' | 'august' | 'sep' | 'september' | 'oct' | 'october' |
+ *               'nov' | 'november' | 'dec' | 'december'
+ *
+ * DAY-OF-WEEK ::= 'sunday' | 'sun' | 'monday' | 'mon' | 'tuesday' | 'tue' |
+ *                 'wednesday' | 'wed' | 'thursday' | 'thu' | 'friday' | 'fri' |
+ *                 'saturday' | 'sat'
+ *
+ *
+ * As you may note, there is an ambiguity with respect to
+ * the 'm' time unit (which can mean either minutes or months).
+ * To cope with this, code tries to read users mind :) by applying
+ * certain heuristics. There are two of them:
+ *
+ * 1. If 'm' is used in context of (i.e. right after the) years,
+ *    months, weeks, or days it is assumed to mean months, while
+ *    in the context of hours, minutes, and seconds it means minutes.
+ *    (e.g., in -1y6m or +3w1m 'm' means 'months', while in
+ *    -3h20m or +5s2m 'm' means 'minutes')
+ *
+ * 2. Out of context (i.e. right after the '+' or '-' sign) the
+ *    meaning of 'm' is guessed from the number it directly follows.
+ *    Currently, if the number absolute value is below 25 it is assumed
+ *    that 'm' means months, otherwise it is treated as minutes.
+ *    (e.g., -25m == -25 minutes, while +24m == +24 months)
+ *
+ */
+
+/* System Headers */
+
+/* Local headers */
+
+#include "rrd_tool.h"
+#include <stdarg.h>
+
+/* Structures and unions */
+
+enum { /* symbols */
+    MIDNIGHT, NOON, TEATIME,
+    PM, AM, YESTERDAY, TODAY, TOMORROW, NOW, START, END,
+    SECONDS, MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS,
+    MONTHS_MINUTES,
+    NUMBER, PLUS, MINUS, DOT, COLON, SLASH, ID, JUNK,
+    JAN, FEB, MAR, APR, MAY, JUN,
+    JUL, AUG, SEP, OCT, NOV, DEC,
+    SUN, MON, TUE, WED, THU, FRI, SAT
+    };
+
+/* the below is for plus_minus() */
+#define PREVIOUS_OP    (-1)
+
+/* parse translation table - table driven parsers can be your FRIEND!
+ */
+struct SpecialToken {
+    char *name;        /* token name */
+    int value; /* token id */
+};
+static 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 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 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 (s1, s2)
+     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()
+{
+    int idx;
+
+    while (1) {
+       memset(sc_token, '\0', sc_len);
+       sc_tokid = EOF;
+       idx = 0;
+
+       /* if we need to read another argument, walk along the argument list;
+        * when we fall off the arglist, we'll just return EOF forever
+        */
+       if (need) {
+           if (scc < 1)
+               return sc_tokid;
+           sct = *scp;
+           scp++;
+           scc--;
+           need = 0;
+       }
+       /* eat whitespace now - if we walk off the end of the argument,
+        * we'll continue, which puts us up at the top of the while loop
+        * to fetch the next argument in
+        */
+       while (isspace((unsigned char)*sct) || *sct == '_' || *sct == ',' )
+           ++sct;
+       if (!*sct) {
+           need = 1;
+           continue;
+       }
+
+       /* preserve the first character of the new token
+        */
+       sc_token[0] = *sct++;
+
+       /* then see what it is
+        */
+       if (isdigit((unsigned char)(sc_token[0]))) {
+           while (isdigit((unsigned char)(*sct)))
+               sc_token[++idx] = *sct++;
+           sc_token[++idx] = '\0';
+           return sc_tokid = NUMBER;
+       }
+       else if (isalpha((unsigned char)(sc_token[0]))) {
+           while (isalpha((unsigned char)(*sct)))
+               sc_token[++idx] = *sct++;
+           sc_token[++idx] = '\0';
+           return parse_token(sc_token);
+       }
+       else switch(sc_token[0]) {
+           case ':': return sc_tokid = COLON;
+           case '.': return sc_tokid = DOT;
+           case '+': return sc_tokid = PLUS;
+           case '-': return sc_tokid = MINUS;
+           case '/': return sc_tokid = SLASH;
+       default:
+        /*OK, we did not make it ... */
+           sct--;
+           return sc_tokid = EOF;
+       }
+    } /* while (1) */
+} /* token */
+
+
+/* 
+ * expect2() gets a token and complains if it's not the token we want
+ */
+static char *
+expect2(int desired, char *complain_fmt, ...)
+{
+    va_list ap;
+    va_start( ap, complain_fmt );
+    if (token() != desired) {
+       panic(ve( complain_fmt, ap ));
+    }
+    va_end( ap );
+    return TIME_OK;
+    
+} /* expect2 */
+
+
+/*
+ * plus_minus() is used to parse a single NUMBER TIME-UNIT pair
+ *              for the OFFSET-SPEC.
+ *              It also applies those m-guessing heuristics.
+ */
+static char *
+plus_minus(struct rrd_time_value *ptv, int doop)
+{
+    static int op = PLUS;
+    static int prev_multiplier = -1;
+    int delta;
+
+    if( doop >= 0 ) 
+      {
+      op = doop;
+      try(expect2(NUMBER,"There should be number after '%c'", op == PLUS ? '+' : '-'));
+      prev_multiplier = -1; /* reset months-minutes guessing mechanics */
+      }
+    /* if doop is < 0 then we repeat the previous op
+     * with the prefetched number */
+
+    delta = atoi(sc_token);
+
+    if( token() == MONTHS_MINUTES )
+      {
+      /* hard job to guess what does that -5m means: -5mon or -5min? */
+      switch(prev_multiplier)
+       {
+        case DAYS:
+        case WEEKS:
+        case MONTHS:
+        case YEARS:
+             sc_tokid = MONTHS;
+            break;
+
+        case SECONDS:
+        case MINUTES:
+        case HOURS:
+            sc_tokid = MINUTES;
+            break;
+
+        default:
+             if( delta < 6 ) /* it may be some other value but in the context
+                              * of RRD who needs less than 6 min deltas? */
+               sc_tokid = MONTHS;
+             else
+              sc_tokid = MINUTES;
+        }
+      }
+    prev_multiplier = sc_tokid;
+    switch (sc_tokid) {
+    case YEARS:
+           ptv->tm.tm_year += (op == PLUS) ? delta : -delta;
+           return TIME_OK;
+    case MONTHS:
+           ptv->tm.tm_mon += (op == PLUS) ? delta : -delta;
+           return TIME_OK;
+    case WEEKS:
+           delta *= 7;
+           /* FALLTHRU */
+    case DAYS:
+           ptv->tm.tm_mday += (op == PLUS) ? delta : -delta;
+           return TIME_OK;
+    case HOURS:
+           ptv->offset += (op == PLUS) ? delta*60*60 : -delta*60*60;
+           return TIME_OK;
+    case MINUTES:
+           ptv->offset += (op == PLUS) ? delta*60 : -delta*60;
+           return TIME_OK;
+    case SECONDS:
+           ptv->offset += (op == PLUS) ? delta : -delta;
+           return TIME_OK;
+    default: /*default unit is seconds */
+       ptv->offset += (op == PLUS) ? delta : -delta;
+       return TIME_OK;
+    }
+    panic(e("well-known time unit expected after %d", delta));
+    /* NORETURN */
+    return TIME_OK; /* to make compiler happy :) */
+} /* plus_minus */
+
+
+/*
+ * tod() computes the time of day (TIME-OF-DAY-SPEC)
+ */
+static char *
+tod(struct rrd_time_value *ptv)
+{
+    int hour, minute = 0;
+    int tlen;
+    /* save token status in  case we must abort */
+    int scc_sv = scc; 
+    const char *sct_sv = sct; 
+    int sc_tokid_sv = sc_tokid;
+
+    tlen = strlen(sc_token);
+    
+    /* first pick out the time of day - we assume a HH (COLON|DOT) MM time
+     */    
+    if (tlen > 2) {
+      return TIME_OK;
+    }
+    
+    hour = atoi(sc_token);
+
+    token();
+    if (sc_tokid == SLASH || sc_tokid == DOT) {
+      /* guess we are looking at a date */
+      scc = scc_sv;
+      sct = sct_sv;
+      sc_tokid = sc_tokid_sv;
+      sprintf (sc_token,"%d", hour);
+      return TIME_OK;
+    }
+    if (sc_tokid == COLON ) {
+       try(expect2(NUMBER,
+            "Parsing HH:MM syntax, expecting MM as number, got none"));
+       minute = atoi(sc_token);
+       if (minute > 59) {
+           panic(e("parsing HH:MM syntax, got MM = %d (>59!)", minute ));
+       }
+       token();
+    }
+
+    /* check if an AM or PM specifier was given
+     */
+    if (sc_tokid == AM || sc_tokid == PM) {
+       if (hour > 12) {
+           panic(e("there cannot be more than 12 AM or PM hours"));
+       }
+       if (sc_tokid == PM) {
+           if (hour != 12)     /* 12:xx PM is 12:xx, not 24:xx */
+                       hour += 12;
+       } else {
+           if (hour == 12)     /* 12:xx AM is 00:xx, not 12:xx */
+                       hour = 0;
+       }
+       token();
+    } 
+    else if (hour > 23) {
+      /* guess it was not a time then ... */
+      scc = scc_sv;
+      sct = sct_sv;
+      sc_tokid = sc_tokid_sv;
+      sprintf (sc_token,"%d", hour);
+      return TIME_OK;
+    }
+    ptv->tm.tm_hour = hour;
+    ptv->tm.tm_min = minute;
+    ptv->tm.tm_sec = 0;
+    if (ptv->tm.tm_hour == 24) {
+       ptv->tm.tm_hour = 0;
+       ptv->tm.tm_mday++;
+    }
+  return TIME_OK;
+} /* tod */
+
+
+/*
+ * assign_date() assigns a date, adjusting year as appropriate
+ */
+static char *
+assign_date(struct rrd_time_value *ptv, long mday, long mon, long year)
+{
+    if (year > 138) {
+       if (year > 1970)
+           year -= 1900;
+       else {
+           panic(e("invalid year %d (should be either 00-99 or >1900)",
+                    year));
+       }
+    } else if( year >= 0 && year < 38 ) {
+       year += 100;         /* Allow year 2000-2037 to be specified as   */
+    }                       /* 00-37 until the problem of 2038 year will */
+                            /* arise for unices with 32-bit time_t :)    */
+    if (year < 70) {
+      panic(e("won't handle dates before epoch (01/01/1970), sorry"));
+    }
+
+    ptv->tm.tm_mday = mday;
+    ptv->tm.tm_mon = mon;
+    ptv->tm.tm_year = year;
+  return TIME_OK;
+} /* assign_date */
+
+
+/* 
+ * day() picks apart DAY-SPEC-[12]
+ */
+static char *
+day(struct rrd_time_value *ptv)
+{
+    /* using time_t seems to help portability with 64bit oses */    
+    time_t mday=0, wday, mon, year = ptv->tm.tm_year;
+    int tlen;
+
+    switch (sc_tokid) {
+    case YESTERDAY:
+           ptv->tm.tm_mday--;
+           /* FALLTRHU */
+    case TODAY:        /* force ourselves to stay in today - no further processing */
+           token();
+           break;
+    case TOMORROW:
+           ptv->tm.tm_mday++;
+           token();
+           break;
+
+    case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
+    case JUL: case AUG: case SEP: case OCT: case NOV: case DEC:
+           /* do month mday [year]
+            */
+           mon = (sc_tokid-JAN);
+           try(expect2(NUMBER,
+               "the day of the month should follow month name"));
+           mday = atol(sc_token);
+           if (token() == NUMBER) {
+               year = atol(sc_token);
+               token();
+           }
+           else
+               year = ptv->tm.tm_year;
+           try(assign_date(ptv, mday, mon, year));
+           break;
+
+    case SUN: case MON: case TUE:
+    case WED: case THU: case FRI:
+    case SAT:
+           /* do a particular day of the week
+            */
+           wday = (sc_tokid-SUN);
+           ptv->tm.tm_mday += (wday - ptv->tm.tm_wday);
+            token();
+           break;
+           /*
+           mday = ptv->tm.tm_mday;
+           mday += (wday - ptv->tm.tm_wday);
+           ptv->tm.tm_wday = wday;
+
+           try(assign_date(ptv, mday, ptv->tm.tm_mon, ptv->tm.tm_year));
+           break;
+           */
+
+    case NUMBER:
+           /* get numeric <sec since 1970>, MM/DD/[YY]YY, or DD.MM.[YY]YY
+            */
+           tlen = strlen(sc_token);
+           mon = atol(sc_token);
+            if (mon > 10*365*24*60*60) {
+               ptv->tm=*localtime(&mon);
+               token();
+               break;
+           }
+
+           if (mon > 19700101 && mon < 24000101){ /*works between 1900 and 2400 */
+               char  cmon[3],cmday[3],cyear[5];
+               strncpy(cyear,sc_token,4);cyear[4]='\0';              
+               year = atol(cyear);           
+               strncpy(cmon,&(sc_token[4]),2);cmon[2]='\0';
+               mon = atol(cmon);
+               strncpy(cmday,&(sc_token[6]),2);cmday[2]='\0';
+               mday = atol(cmday);
+               token();
+           } else { 
+             token();
+             
+             if (mon <= 31 && (sc_tokid == SLASH || sc_tokid == DOT)) {
+               int sep;                    
+               sep = sc_tokid;
+               try(expect2(NUMBER,"there should be %s number after '%c'",
+                          sep == DOT ? "month" : "day", sep == DOT ? '.' : '/'));
+               mday = atol(sc_token);
+               if (token() == sep) {
+                 try(expect2(NUMBER,"there should be year number after '%c'",
+                            sep == DOT ? '.' : '/'));
+                 year = atol(sc_token);
+                 token();
+               }
+               
+               /* flip months and days for European timing
+                */
+               if (sep == DOT) {
+                 long x = mday;
+                 mday = mon;
+                 mon = x;
+               }
+             }
+           }
+
+           mon--;
+           if(mon < 0 || mon > 11 ) {
+               panic(e("did you really mean month %d?", mon+1));
+           }
+           if(mday < 1 || mday > 31) {
+               panic(e("I'm afraid that %d is not a valid day of the month",
+                       mday));
+           }      
+           try(assign_date(ptv, mday, mon, year));
+           break;
+    } /* case */
+    return TIME_OK;
+} /* month */
+
+
+/* Global functions */
+
+
+/*
+ * parsetime() is the external interface that takes tspec, parses
+ * it and puts the result in the rrd_time_value structure *ptv.
+ * It can return either absolute times (these are ensured to be
+ * correct) or relative time references that are expected to be
+ * added to some absolute time value and then normalized by
+ * mktime() The return value is either TIME_OK (aka NULL) or
+ * the pointer to the error message in the case of problems
+ */
+char *
+parsetime(const char *tspec, struct rrd_time_value *ptv)
+{
+    time_t now = time(NULL);
+    int hr = 0;
+    /* this MUST be initialized to zero for midnight/noon/teatime */
+
+    Specials = VariousWords; /* initialize special words context */
+
+    try(init_scanner( 1, &tspec ));
+
+    /* establish the default time reference */
+    ptv->type = ABSOLUTE_TIME;
+    ptv->offset = 0;
+    ptv->tm = *localtime(&now);
+    ptv->tm.tm_isdst = -1; /* mk time can figure this out for us ... */
+
+    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));
+    }
+
+    ptv->tm.tm_isdst = -1; /* for mktime to guess DST status */
+    if( ptv->type == ABSOLUTE_TIME )
+      if( mktime( &ptv->tm ) == -1 ) { /* normalize & check */
+        /* can happen for "nonexistent" times, e.g. around 3am */
+       /* when winter -> summer time correction eats a hour */
+        panic(e("the specified time is incorrect (out of range?)"));
+      }
+    EnsureMemFree();
+    return TIME_OK;
+} /* parsetime */
+
+
+int proc_start_end (struct rrd_time_value *start_tv, 
+                   struct rrd_time_value *end_tv, 
+                   time_t *start, 
+                   time_t *end){
+    if (start_tv->type == RELATIVE_TO_END_TIME  && /* same as the line above */
+       end_tv->type == RELATIVE_TO_START_TIME) {
+       rrd_set_error("the start and end times cannot be specified "
+                     "relative to each other");
+       return -1;
+    }
+
+    if (start_tv->type == RELATIVE_TO_START_TIME) {
+       rrd_set_error("the start time cannot be specified relative to itself");
+       return -1;
+    }
+
+    if (end_tv->type == RELATIVE_TO_END_TIME) {
+       rrd_set_error("the end time cannot be specified relative to itself");
+       return -1;
+    }
+
+    if( start_tv->type == RELATIVE_TO_END_TIME) {
+       struct tm tmtmp;
+       *end = mktime(&(end_tv->tm)) + end_tv->offset;    
+       tmtmp = *localtime(end); /* reinit end including offset */
+       tmtmp.tm_mday += start_tv->tm.tm_mday;
+       tmtmp.tm_mon += start_tv->tm.tm_mon;
+       tmtmp.tm_year += start_tv->tm.tm_year;  
+       *start = mktime(&tmtmp) + start_tv->offset;
+    } else {
+       *start = mktime(&(start_tv->tm)) + start_tv->offset;
+    }
+    if (end_tv->type == RELATIVE_TO_START_TIME) {
+       struct tm tmtmp;
+       *start = mktime(&(start_tv->tm)) + start_tv->offset;
+       tmtmp = *localtime(start);
+       tmtmp.tm_mday += end_tv->tm.tm_mday;
+       tmtmp.tm_mon += end_tv->tm.tm_mon;
+       tmtmp.tm_year += end_tv->tm.tm_year;    
+       *end = mktime(&tmtmp) + end_tv->offset;
+    } else {
+       *end = mktime(&(end_tv->tm)) + end_tv->offset;
+    }    
+    return 0;
+} /* proc_start_end */
+
+
+
+
+
+
+
diff --git a/program/src/parsetime.h b/program/src/parsetime.h
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/pngsize.c b/program/src/pngsize.c
new file mode 100644 (file)
index 0000000..78d0950
--- /dev/null
@@ -0,0 +1,50 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * 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 */
+
+#ifdef jmpbuf
+#undef jmpbuf
+#endif
+
+  if (setjmp(png_read_ptr->jmpbuf)){
+    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..3873033
--- /dev/null
@@ -0,0 +1,152 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * 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 <time.h>
+#include <stdio.h> /* for FILE */
+
+/* Transplanted from rrd_format.h */
+typedef double       rrd_value_t;         /* the data storage type is
+                                           * double */
+/* END rrd_format.h */
+
+/* main function blocks */
+int    rrd_create(int, char **);
+int    rrd_update(int, char **);
+int    rrd_graph(int, char **, char ***, int *, int *, FILE *, double *, double *);
+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 **);
+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 **);
+
+/* thread-safe (hopefully) */
+int    rrd_create_r(const char *filename,
+                   unsigned long pdp_step, time_t last_up,
+                   int argc, const char **argv);
+/* 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);
+time_t rrd_first_r(const char *filename, int rraindex);
+
+/* Transplanted from parsetime.h */
+typedef enum {
+        ABSOLUTE_TIME,
+        RELATIVE_TO_START_TIME, 
+        RELATIVE_TO_END_TIME
+} timetype;
+
+#define TIME_OK NULL
+
+struct rrd_time_value {
+  timetype type;
+  long offset;
+  struct tm tm;
+};
+
+char *parsetime(const char *spec, struct rrd_time_value *ptv);
+/* END parsetime.h */
+
+struct rrd_context {
+    int len;
+    int errlen;
+    char *lib_errstr;
+    char *rrd_error;
+};
+
+/* returns the current per-thread rrd_context */
+struct rrd_context *rrd_get_context(void);
+
+
+int proc_start_end (struct rrd_time_value *,  struct rrd_time_value *, 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);
+
+/** MULTITHREADED HELPER FUNCTIONS */
+struct rrd_context *rrd_new_context(void);
+void   rrd_free_context (struct rrd_context *buf);
+
+/* void   rrd_set_error_r  (struct rrd_context *, char *, ...); */
+/* void   rrd_clear_error_r(struct rrd_context *); */
+/* int    rrd_test_error_r (struct rrd_context *); */
+/* char  *rrd_get_error_r  (struct rrd_context *); */
+
+int  LockRRD(FILE *);
+
+#endif /* _RRDLIB_H */
+
+#ifdef  __cplusplus
+}
+#endif
diff --git a/program/src/rrd_afm.c b/program/src/rrd_afm.c
new file mode 100644 (file)
index 0000000..c42e6fe
--- /dev/null
@@ -0,0 +1,287 @@
+/****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd_afm.h  Parsing afm tables to find width of strings.
+ ****************************************************************************
+ * $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_afm.h"
+#include "rrd_afm_data.h"
+
+#include <stdio.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "unused.h"
+
+#if 0
+# define DEBUG 1
+# define DLOG(x) fprintf x
+#else
+# define DEBUG 0
+# define DLOG(x) 
+#endif
+
+/* Adobe SVG View and Batik 1.1.1 can't handle ligatures.
+   So disable it as we just waste speed.
+   Besides, it doesn't matter much in normal text.
+*/
+#define ENABLE_LIGATURES 0
+
+static const afm_fontinfo *afm_last_used_font = NULL;
+static const char *last_unknown_font = NULL;
+
+#define is_font(p, name) \
+  (!strcmp(p->postscript_name, name) || !strcmp(p->fullname, name))
+
+static const afm_fontinfo *afm_searchfont(const char *name)
+{
+  int i;
+  const afm_fontinfo *p = afm_last_used_font;
+  if (p && is_font(p, name))
+    return p;
+  p = afm_fontinfolist;
+  for (i = 0; i < afm_fontinfo_count; i++, p++) {
+    if (is_font(p, name)) {
+      afm_last_used_font = p;
+      return p;
+    }
+  }
+  return NULL;
+}
+
+
+/* returns always a font, never NULL.
+   The rest of the code depends on the result never being NULL.
+   See rrd_afm.h */
+static const afm_fontinfo *afm_findfont(const char *name)
+{
+  const afm_fontinfo *p = afm_searchfont(name);
+  if (p)
+    return p;
+  if (!last_unknown_font || strcmp(name, last_unknown_font)) {
+         fprintf(stderr, "Can't find font '%s'\n", name);
+         last_unknown_font = name;
+  }
+  p = afm_searchfont(RRD_AFM_DEFAULT_FONT);
+  if (p)
+    return p;
+  return afm_fontinfolist; /* anything, just anything. */
+}
+
+const char *afm_get_font_postscript_name(const char* font)
+{
+  const afm_fontinfo *p = afm_findfont(font);
+  return p->postscript_name;
+}
+
+const char *afm_get_font_name(const char* font)
+{
+  const afm_fontinfo *p = afm_findfont(font);
+  return p->fullname;
+}
+
+double afm_get_ascender(const char* font, double size)
+{
+  const afm_fontinfo *p = afm_findfont(font);
+  return size * p->ascender / 1000.0;
+}
+
+double afm_get_descender(const char* font, double size)
+{
+  const afm_fontinfo *p = afm_findfont(font);
+  return size * p->descender / 1000.0;
+}
+
+static int afm_find_char_index(const afm_fontinfo *fontinfo,
+    afm_cunicode ch1)
+{
+  int idx = ch1 - 32;
+  afm_cuint16 *indexP;
+  int numIndexChars, i;
+  if (idx <= 0)
+    return 0;
+  if (idx <= 126 - 32)
+    return idx;
+  indexP = fontinfo->highchars_index;
+  if (indexP == 0)
+    return 0;
+  numIndexChars = fontinfo->highchars_count;
+  DLOG((stderr, " find highbit, num = %d\n", numIndexChars));
+  if (ch1 >= 161 && ch1 <= 255) {
+    idx = ch1 - 161;
+    DLOG((stderr, "  161, idx = %d -> %d\n", idx, indexP[idx]));
+    if (idx < numIndexChars && indexP[idx] == ch1) {
+      idx += 127 - 32;
+      DLOG((stderr, "  161-guessed ok to %d\n", idx));
+      return idx;
+    }
+  }
+  for (i = 0; i < numIndexChars; i++) {
+    DLOG((stderr, "    compares to %d -> %d\n", indexP[i], i));
+    if (indexP[i] == ch1)
+      return i + 127 - 32;
+  }
+  DLOG((stderr, "Did not find %d in highchars_index ??\n", ch1));
+  return 0;
+}
+
+#if ENABLE_LIGATURES
+static afm_cunicode afm_find_combined_ligature(const afm_fontinfo *fontinfo,
+    afm_cunicode ch1, afm_cunicode ch2)
+{
+  afm_cunicode *p = fontinfo->ligatures;
+  int num = fontinfo->ligatures_count;
+  int i;
+  if (!num)
+    return 0;
+  DLOG((stderr, " find-lig, num = %d\n", num));
+  for (i = 0; i < num; i++, p += 3) {
+    DLOG((stderr, "    lig: %d + %d -> %d (%c %c %c)\n",
+        p[0], p[1], p[2], p[0], p[1], p[2]));
+    if (ch1 == *p && ch2 == p[1]) {
+      DLOG((stderr, "   matches.\n"));
+      return p[2];
+    }
+  }
+  return 0;
+}
+#endif
+
+#define READ_ESCAPED(p, val) \
+  if ((val = *p++) == 0) { \
+    val = 254 + *p++; \
+  } else if (!--val) { \
+    val = *p++ << 8; \
+    val |= *p++; \
+  }
+
+
+static long afm_find_kern(const afm_fontinfo *fontinfo,
+    int kern_idx, afm_cunicode ch2)
+{
+  afm_cuint8 *p8 = fontinfo->kerning_data + kern_idx;
+  int num;
+  READ_ESCAPED(p8, num);
+  DLOG((stderr, " find kern, num pairs = %d\n", num));
+  while (num > 0) {
+    afm_unicode ch;
+    READ_ESCAPED(p8, ch);
+    DLOG((stderr, "     pair-char = %d\n", ch));
+    if (ch == ch2) {
+      DLOG((stderr, " got kern = %d\n", *(afm_csint8*)p8));
+      return *(afm_csint8*)p8;
+    }
+    p8++;
+    num--;
+  }
+  return 0;
+}
+
+/* measure width of a text string */
+double afm_get_text_width( double start, const char* font, double size,
+          double tabwidth, const char* text)
+{
+#ifdef HAVE_MBSTOWCS     
+    size_t clen = strlen(text) + 1;
+    wchar_t *cstr = malloc(sizeof(wchar_t) * clen); /* yes we are allocating probably too much here, I know */
+    int text_count = mbstowcs(cstr, text, clen);
+    double w;
+    if (text_count == -1)
+           text_count = mbstowcs(cstr, "Enc-Err", 6);
+#ifdef __APPLE__
+       while (text_count > 0) {
+               text_count--;
+               cstr[text_count] = afm_fix_osx_charset(cstr[text_count]); /* unsafe macro */
+       }
+#endif
+    w = afm_get_text_width_wide(start, font, size, tabwidth, cstr);
+    free(cstr);
+    return w;
+#else
+    return afm_get_text_width_wide(start, font, size, tabwidth, text);
+#endif
+}
+
+double afm_get_text_width_wide( double UNUSED(start), const char* font, double size,
+          double UNUSED(tabwidth), const afm_char* text)
+{
+  const afm_fontinfo *fontinfo = afm_findfont(font);
+  long width = 0;
+  double widthf;
+  const afm_char *up = text;
+  DLOG((stderr, "================= %s\n", text));
+  if (fontinfo == NULL) {
+      while (*up)
+         up++;
+    return size * (up - text);
+  }
+  while (1) {
+    afm_unicode ch1, ch2;
+    int idx1, kern_idx;
+    if ((ch1 = *up) == 0)
+        break;
+    ch2 = *++up;
+    DLOG((stderr, "------------- Loop: %d + %d (%c%c)   at %d\n",
+          ch1, ch2, ch1, ch2 ? ch2 : ' ',
+         (up - (const unsigned char*)text) - 1));
+    idx1 = afm_find_char_index(fontinfo, ch1);
+    DLOG((stderr, "  idx1 = %d\n", idx1));
+#if ENABLE_LIGATURES
+    if (ch2) {
+      int ch1_new = afm_find_combined_ligature(fontinfo, ch1, ch2);
+      DLOG((stderr, "  lig-ch = %d\n", ch1_new));
+      if (ch1_new) {
+        ch1 = ch1_new;
+        idx1 = afm_find_char_index(fontinfo, ch1);
+        ch2 = *++up;
+        DLOG((stderr, "  -> idx1 = %d, ch2 = %d (%c)\n", 
+            idx1, ch2, ch2 ? ch2 : ' '));
+      }
+    }
+#endif
+    width += fontinfo->widths[idx1];
+    DLOG((stderr, "Plain width of %d = %d\n", ch1, fontinfo->widths[idx1]));
+    if (fontinfo->kerning_index && ch2) {
+      kern_idx = fontinfo->kerning_index[idx1];
+      DLOG((stderr, "    kern_idx = %d\n", kern_idx));
+      if (kern_idx > 0)
+        width += afm_find_kern(fontinfo, kern_idx, ch2);
+    }
+  }
+  widthf = (width * 6 / 1000.0) * size;
+  DLOG((stderr, "Returns %ld (%ld) -> %f\n", width, width * 6, widthf));
+  return widthf;
+}
+
+#ifdef __APPLE__
+const unsigned char afm_mac2iso[128] = {
+  '\xC4', '\xC5', '\xC7', '\xC9', '\xD1', '\xD6', '\xDC', '\xE1', /* 80 */
+  '\xE0', '\xE2', '\xE4', '\xE3', '\xE5', '\xE7', '\xE9', '\xE8', /* 88 */
+  '\xEA', '\xEB', '\xED', '\xEC', '\xEE', '\xEF', '\xF1', '\xF3', /* 90 */
+  '\xF2', '\xF4', '\xF6', '\xF5', '\xFA', '\xF9', '\xFB', '\xFC', /* 98 */
+  '\xDD', '\xB0', '\xA2', '\xA3', '\xA7', ' ',    '\xB6', '\xDF', /* A0 */
+  '\xAE', '\xA9', ' ',    '\xB4', '\xA8', ' ',    '\xC6', '\xD8', /* A8 */
+  ' ',    '\xB1', '\xBE', ' ',    '\xA5', '\xB5', ' ',    ' ',    /* B0 */
+  '\xBD', '\xBC', ' ',    '\xAA', '\xBA', ' ',    '\xE6', '\xF8', /* B8 */
+  '\xBF', '\xA1', '\xAC', ' ',    ' ',    ' ',    ' ',    '\xAB', /* C0 */
+  '\xBB', ' ',    '\xA0', '\xC0', '\xC3', '\xD5', ' ',    '\xA6', /* C8 */
+  '\xAD', ' ',    '"',    '"',    '\'',   '\'',   '\xF7', '\xD7', /* D0 */
+  '\xFF', ' ',    ' ',    '\xA4', '\xD0', '\xF0', '\xDE', '\xFE', /* D8 */
+  '\xFD', '\xB7', ' ',    ' ',    ' ',    '\xC2', '\xCA', '\xC1', /* E0 */
+  '\xCB', '\xC8', '\xCD', '\xCE', '\xCF', '\xCC', '\xD3', '\xD4', /* E8 */
+  ' ',    '\xD2', '\xDA', '\xDB', '\xD9', ' ',    ' ',    ' ',    /* F0 */
+  '\xAF', ' ',    ' ',    ' ',    '\xB8', ' ',    ' ',    ' ',    /* F8 */
+};
+#endif
diff --git a/program/src/rrd_afm.h b/program/src/rrd_afm.h
new file mode 100644 (file)
index 0000000..20350ff
--- /dev/null
@@ -0,0 +1,54 @@
+/****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd_afm.h  Parsing afm tables to find width of strings.
+ ****************************************************************************/
+
+#ifndef  RRD_AFM_H
+#define RRD_AFM_H
+
+#include <stdlib.h>
+#ifdef HAVE_MBSTOWCS
+#define afm_char wchar_t
+#else
+#define afm_char unsigned char
+#endif
+/*
+   If the font specified by the name parameter in the routes below
+   is not found
+   (because it is not compiled into rrd_afm_data.c by compile_afm.pl)
+   the font specified by RRD_AFM_DEFAULT_FONT will be used.
+   If it is not installed, it uses the first font compiled
+   into rrd_afm_data.c
+   So they will always use some font.
+*/
+
+#define RRD_AFM_DEFAULT_FONT "Courier"
+
+/* measure width of a text string */
+/* fontname can be full name or postscript name */
+double afm_get_text_width( double start, const char* font, double size,
+                           double tabwidth, const char* text);
+double afm_get_text_width_wide( double start, const char* font, double size,
+                           double tabwidth, const afm_char* text);
+
+double afm_get_ascender(const char* font, double size);
+double afm_get_descender(const char* font, double size);
+
+/* get postscript name from fullname or postscript name */
+const char *afm_get_font_postscript_name ( const char* font);
+const char *afm_get_font_name(const char* font);
+
+/* cc -E -dM /dev/null */
+#ifdef __APPLE__
+/* need charset conversion from macintosh to unicode. */
+extern const unsigned char afm_mac2iso[128];
+#define afm_fix_osx_charset(c) \
+       ( (c) >= 128 && (c) <= 255 ? afm_mac2iso[(c) - 128] : (c))
+#else
+/* UNSAFE macro */
+#define afm_fix_osx_charset(x) (x)
+#endif
+
+#endif
diff --git a/program/src/rrd_afm_data.c b/program/src/rrd_afm_data.c
new file mode 100644 (file)
index 0000000..fccf586
--- /dev/null
@@ -0,0 +1,3338 @@
+/****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd_afm_data.c  Encoded afm (Adobe Font Metrics) for selected fonts.
+ ****************************************************************************
+ *
+ * THIS FILE IS AUTOGENERATED BY PERL. DO NOT EDIT.
+ *
+ ****************************************************************************/
+
+#include "rrd_afm_data.h"
+#include <stdlib.h>
+
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Courier */
+/* FullName: Courier */
+/* FamilyName: Courier */
+static afm_cuint8 afm_Courier_widths[] = { /* 315 */
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100
+};
+static afm_cuint16 afm_Courier_highchars_index[] = { /* 220 */
+  161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+  178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+  194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+  210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+  226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+  242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+  258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+  280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+  313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+  336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+  354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+  380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+  8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+  8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+  64257,64258
+};
+static afm_cunicode afm_Courier_ligatures[] = { /* 3 */
+  102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Courier-Bold */
+/* FullName: Courier Bold */
+/* FamilyName: Courier */
+static afm_cuint8 afm_Courier_Bold_widths[] = { /* 315 */
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100
+};
+static afm_cuint16 afm_Courier_Bold_highchars_index[] = { /* 220 */
+  161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+  178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+  194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+  210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+  226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+  242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+  258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+  280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+  313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+  336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+  354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+  380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+  8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+  8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+  64257,64258
+};
+static afm_cunicode afm_Courier_Bold_ligatures[] = { /* 3 */
+  102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Courier-BoldOblique */
+/* FullName: Courier Bold Oblique */
+/* FamilyName: Courier */
+static afm_cuint8 afm_Courier_BoldOblique_widths[] = { /* 315 */
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100
+};
+static afm_cuint16 afm_Courier_BoldOblique_highchars_index[] = { /* 220 */
+  161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+  178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+  194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+  210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+  226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+  242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+  258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+  280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+  313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+  336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+  354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+  380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+  8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+  8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+  64257,64258
+};
+static afm_cunicode afm_Courier_BoldOblique_ligatures[] = { /* 3 */
+  102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Courier-Oblique */
+/* FullName: Courier Oblique */
+/* FamilyName: Courier */
+static afm_cuint8 afm_Courier_Oblique_widths[] = { /* 315 */
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+  100,100,100,100,100,100,100,100,100,100,100
+};
+static afm_cuint16 afm_Courier_Oblique_highchars_index[] = { /* 220 */
+  161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+  178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+  194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+  210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+  226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+  242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+  258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+  280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+  313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+  336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+  354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+  380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+  8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+  8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+  64257,64258
+};
+static afm_cunicode afm_Courier_Oblique_ligatures[] = { /* 3 */
+  102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Helvetica */
+/* FullName: Helvetica */
+/* FamilyName: Helvetica */
+static afm_cuint8 afm_Helvetica_widths[] = { /* 315 */
+  46,46,59,93,93,148,111,32,56,56,65,97,46,56,46,46,93,93,93,93,93,
+  93,93,93,93,93,46,46,97,97,97,93,169,111,111,120,120,111,102,130,
+  120,46,83,111,93,139,120,130,111,130,120,111,102,120,111,157,111,
+  111,102,46,46,46,78,93,56,93,93,83,93,93,46,93,93,37,37,83,37,139,
+  93,93,93,93,56,83,46,93,83,120,83,83,83,56,43,56,97,56,93,93,93,
+  93,43,93,56,123,62,93,97,123,56,67,97,56,56,56,93,90,46,56,56,61,
+  93,139,139,139,102,111,111,111,111,111,111,167,120,111,111,111,111,
+  46,46,46,46,120,120,130,130,130,130,130,97,130,120,120,120,120,111,
+  111,102,93,93,93,93,93,93,148,83,93,93,93,93,46,46,46,46,93,93,93,
+  93,93,93,93,97,102,93,93,93,93,83,93,83,111,93,111,93,111,93,120,
+  83,120,83,120,107,120,93,111,93,111,93,111,93,111,93,130,93,130,
+  93,46,46,46,37,46,46,111,83,93,37,93,37,93,50,93,37,120,93,120,93,
+  120,93,130,93,130,93,167,157,120,56,120,56,120,56,111,83,111,83,
+  111,83,102,46,102,53,120,93,120,93,120,93,120,93,111,102,83,102,
+  83,102,83,93,111,83,56,56,56,56,56,56,56,56,93,167,37,37,37,56,56,
+  56,93,93,58,167,167,56,56,28,93,167,79,102,100,97,76,92,92,92,79,
+  42,83,83
+};
+static afm_sint16 afm_Helvetica_kerning_index[] = { /* 315 */
+  1,0,0,0,0,0,0,0,0,0,0,0,29,0,38,0,0,0,0,0,0,0,0,0,0,0,49,52,0,0,
+  0,0,0,55,170,197,202,0,241,0,0,0,345,418,510,0,0,545,594,687,710,
+  772,777,938,966,1115,0,1259,0,0,0,0,0,0,0,1407,1419,1468,0,1476,
+  1494,1575,1587,0,0,1595,0,1638,1668,1700,1718,0,1730,1847,0,0,1854,
+  1924,1994,2017,2087,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,0,0,0,0,0,0,0,0,0,0,2130,2245,2360,2475,2590,2705,0,2820,0,0,
+  0,0,0,0,0,0,0,0,2825,2874,2923,2972,3021,0,3070,3119,3147,3175,3203,
+  3231,0,0,3379,3391,3403,3415,3427,3439,0,3451,3459,3477,3495,3513,
+  0,0,0,0,0,3531,3563,3581,3599,3617,3635,0,3653,0,0,0,0,3882,0,3952,
+  4022,4137,4149,4264,4276,4391,4403,4408,4416,4421,4429,0,4468,0,
+  0,4507,0,4525,0,4543,0,4561,0,4579,0,4591,0,0,0,0,0,0,4603,4695,
+  4738,0,4773,0,4808,0,4843,0,0,4878,0,4910,0,4942,4974,5023,5041,
+  5090,0,0,5108,5170,5287,5349,5466,5528,5645,5650,5657,5662,5669,
+  5674,5681,0,5842,0,6003,0,6031,0,6059,0,6087,0,6115,0,6263,0,6306,
+  0,6349,0,6392,6397,0,0,0,0,0,0,0,0,0,0,6404,6409,0,0,6447,0,0,0,
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+static afm_cuint8 afm_Helvetica_kerning_data[] = { /* 6450 */
+  42,11,85,249,87,249,88,250,90,242,222,242,0,100,249,0,102,249,0,
+  122,242,1,32,24,247,1,32,28,252,3,1,32,25,240,1,32,29,240,4,33,247,
+  1,32,25,240,1,32,29,240,2,33,249,2,33,249,49,68,252,72,252,80,252,
+  82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,250,120,250,
+  122,250,200,252,211,252,212,252,213,252,214,252,215,252,217,252,
+  218,249,219,249,220,249,221,249,222,240,250,252,251,252,252,252,
+  253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,0,78,
+  252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,249,0,
+  113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,12,45,
+  254,47,254,86,255,218,255,219,255,220,255,221,255,0,108,255,0,112,
+  255,0,114,255,0,116,255,3,45,252,47,252,18,45,245,47,245,66,250,
+  87,245,88,250,90,242,193,250,194,250,195,250,196,250,197,250,198,
+  250,222,242,0,2,250,0,4,250,0,6,250,0,122,242,45,45,232,47,232,66,
+  244,98,249,102,252,112,252,115,249,193,244,194,244,195,244,196,244,
+  197,244,198,244,225,249,226,249,227,249,228,249,229,249,230,249,
+  233,252,234,252,235,252,236,252,243,252,244,252,245,252,246,252,
+  247,252,249,252,0,2,244,0,3,249,0,4,244,0,5,249,0,6,244,0,7,249,
+  0,21,252,0,25,252,0,27,252,0,29,252,0,79,252,0,83,252,0,87,249,0,
+  89,249,0,91,249,32,45,252,47,252,66,254,98,254,118,254,193,254,194,
+  254,195,254,196,254,197,254,198,254,225,254,226,254,227,254,228,
+  254,229,254,230,254,250,254,251,254,252,254,253,254,0,2,254,0,3,
+  254,0,4,254,0,5,254,0,6,254,0,7,254,0,109,254,0,113,254,0,115,254,
+  0,117,254,40,80,249,102,250,112,250,118,252,122,249,211,249,212,
+  249,213,249,214,249,215,249,217,249,233,250,234,250,235,250,236,
+  250,243,250,244,250,245,250,246,250,247,250,249,250,250,252,251,
+  252,252,252,253,252,254,249,0,1,249,0,21,250,0,25,250,0,27,250,0,
+  29,250,0,78,249,0,79,250,0,82,249,0,83,250,0,109,252,0,113,252,0,
+  115,252,0,117,252,14,85,239,87,239,88,245,90,234,122,252,222,234,
+  254,252,0,1,252,0,100,239,0,102,239,0,122,234,1,32,25,230,1,32,29,
+  234,22,45,250,47,250,66,254,85,250,87,249,88,252,89,247,90,245,193,
+  254,194,254,195,254,196,254,197,254,198,254,222,245,0,2,254,0,4,
+  254,0,6,254,0,100,250,0,102,250,0,122,245,41,45,227,47,227,66,237,
+  98,250,102,249,112,249,193,237,194,237,195,237,196,237,197,237,198,
+  237,225,250,226,250,227,250,228,250,229,250,230,250,233,249,234,
+  249,235,249,236,249,243,249,244,249,245,249,246,249,247,249,249,
+  249,0,2,237,0,3,250,0,4,237,0,5,250,0,6,237,0,7,250,0,21,249,0,25,
+  249,0,27,249,0,29,249,0,79,249,0,83,249,10,86,255,218,255,219,255,
+  220,255,221,255,0,108,255,0,112,255,0,114,255,0,116,255,27,80,254,
+  85,252,86,250,87,249,88,252,90,249,211,254,212,254,213,254,214,254,
+  215,254,217,254,218,250,219,250,220,250,221,250,222,249,0,78,254,
+  0,82,254,0,100,252,0,102,252,0,108,250,0,112,250,0,114,250,0,116,
+  250,0,122,249,3,45,254,47,254,70,45,237,46,234,47,237,59,254,60,
+  254,66,237,80,250,98,237,102,237,112,237,115,237,118,237,120,237,
+  122,237,193,237,194,237,195,237,196,237,197,237,198,237,211,250,
+  212,250,213,250,214,250,215,250,217,250,225,237,226,237,227,237,
+  228,247,229,237,230,237,233,247,234,237,235,237,236,237,243,237,
+  244,237,245,237,246,247,247,237,249,237,250,237,251,237,252,237,
+  253,237,254,237,0,1,247,0,2,237,0,3,247,0,4,237,0,5,247,0,6,237,
+  0,7,237,0,21,247,0,25,237,0,27,237,0,29,237,0,78,250,0,79,247,0,
+  82,250,0,83,237,0,87,237,0,89,237,0,91,237,0,109,247,0,113,237,0,
+  115,237,0,117,237,13,45,250,47,250,66,250,193,250,194,250,195,250,
+  196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,65,45,236,46,244,
+  47,236,59,250,60,250,66,244,72,250,80,250,98,245,102,244,112,244,
+  118,245,193,244,194,244,195,244,196,244,197,244,198,244,211,250,
+  212,250,213,250,214,250,215,250,217,250,225,245,226,245,227,245,
+  228,245,229,245,230,245,233,244,234,244,235,244,236,244,243,244,
+  244,244,245,244,246,244,247,244,249,244,250,245,251,245,252,245,
+  253,245,0,2,244,0,3,245,0,4,244,0,5,245,0,6,244,0,7,245,0,21,244,
+  0,25,244,0,27,244,0,29,244,0,32,250,0,36,250,0,78,250,0,79,244,0,
+  82,250,0,83,244,0,109,245,0,113,245,0,115,245,0,117,245,63,45,244,
+  46,250,47,244,66,249,80,254,98,250,102,252,112,252,118,252,122,254,
+  193,249,194,249,195,249,196,249,197,249,198,249,211,254,212,254,
+  213,254,214,254,215,254,217,254,225,250,226,250,227,250,228,250,
+  229,250,230,250,233,252,234,252,235,252,236,252,243,252,244,252,
+  245,252,246,252,247,252,249,252,250,252,251,252,252,252,253,252,
+  254,254,0,1,254,0,2,249,0,3,250,0,4,249,0,5,250,0,6,249,0,7,250,
+  0,21,252,0,25,252,0,27,252,0,29,252,0,78,254,0,79,252,0,82,254,0,
+  83,252,0,109,252,0,113,252,0,115,252,0,117,252,65,45,234,46,234,
+  47,234,59,247,60,247,66,239,80,243,98,234,102,234,106,254,112,234,
+  118,239,193,239,194,239,195,239,196,239,197,239,198,239,211,243,
+  212,243,213,243,214,243,215,243,217,243,225,234,226,234,227,234,
+  228,234,229,234,230,234,233,234,234,234,235,234,236,234,238,254,
+  243,234,244,234,245,234,246,234,247,234,249,234,250,239,251,239,
+  252,239,253,239,0,2,239,0,3,245,0,4,239,0,5,245,0,6,239,0,7,234,
+  0,21,245,0,25,234,0,27,234,0,29,234,0,49,254,0,78,243,0,79,234,0,
+  82,243,0,83,234,0,109,239,0,113,239,0,115,239,0,117,239,6,119,254,
+  120,254,122,252,254,252,0,1,252,21,45,250,47,250,99,255,109,254,
+  118,254,119,254,122,254,250,254,251,254,252,254,253,254,254,254,
+  0,1,254,0,60,254,0,62,254,0,68,254,0,109,254,0,113,254,0,115,254,
+  0,117,254,4,45,254,108,254,0,57,254,9,45,254,47,254,119,252,120,
+  254,121,252,122,254,254,254,0,1,254,34,45,252,47,252,98,252,102,
+  252,112,252,225,252,226,252,227,252,228,252,229,252,230,252,233,
+  252,234,252,235,252,236,252,243,252,244,252,245,252,246,252,247,
+  252,249,252,0,3,252,0,5,252,0,7,252,0,21,252,0,25,252,0,27,252,0,
+  29,252,0,51,252,0,79,252,0,83,252,1,32,25,8,1,32,29,10,5,115,255,
+  0,87,255,0,89,255,0,91,255,4,122,252,254,252,0,1,252,19,102,254,
+  112,254,233,254,234,254,235,254,236,254,243,254,244,254,245,254,
+  246,254,247,254,249,254,0,21,254,0,25,254,0,27,254,0,29,254,0,79,
+  254,0,83,254,13,118,255,122,254,250,255,251,255,252,255,253,255,
+  254,254,0,1,254,0,109,255,0,113,255,0,115,255,0,117,255,14,118,255,
+  119,254,122,254,250,255,251,255,252,255,253,255,254,254,0,1,254,
+  0,109,255,0,113,255,0,115,255,0,117,255,9,45,250,47,250,119,254,
+  120,254,121,252,122,252,254,252,0,1,252,6,45,251,47,251,122,252,
+  254,252,0,1,252,50,45,249,47,249,59,5,60,5,98,255,106,3,108,3,109,
+  3,110,4,111,4,113,5,117,7,118,3,119,5,122,5,225,255,226,255,227,
+  255,228,255,229,255,230,255,237,3,238,3,239,3,240,3,242,4,250,3,
+  251,3,252,3,253,3,254,5,0,1,5,0,3,255,0,5,255,0,7,255,0,45,3,0,49,
+  3,0,57,3,0,60,3,0,62,3,0,68,3,0,70,4,0,72,4,0,74,4,0,101,7,0,109,
+  3,0,113,3,0,115,3,0,117,3,4,45,254,47,254,120,252,31,45,244,47,244,
+  98,253,102,253,112,253,225,253,226,253,227,253,228,253,229,253,230,
+  253,233,253,234,253,235,253,236,253,243,253,244,253,245,253,246,
+  253,247,253,249,253,0,3,253,0,5,253,0,7,253,0,21,253,0,25,253,0,
+  27,253,0,29,253,0,79,253,0,83,253,31,45,247,47,247,98,254,102,255,
+  112,255,225,254,226,254,227,254,228,254,229,254,230,254,233,255,
+  234,255,235,255,236,255,243,255,244,255,245,255,246,255,247,255,
+  249,255,0,3,254,0,5,254,0,7,254,0,21,255,0,25,255,0,27,255,0,29,
+  255,0,79,255,0,83,255,10,102,252,233,252,234,252,235,252,236,252,
+  0,21,252,0,25,252,0,27,252,0,29,252,31,45,240,47,240,98,254,102,
+  254,112,254,225,254,226,254,227,254,228,254,229,254,230,254,233,
+  254,234,254,235,254,236,254,243,254,244,254,245,254,246,254,247,
+  254,249,254,0,3,254,0,5,254,0,7,254,0,21,254,0,25,254,0,27,254,0,
+  29,254,0,79,254,0,83,254,19,102,254,112,254,233,254,234,254,235,
+  254,236,254,243,254,244,254,245,254,246,254,247,254,249,254,0,21,
+  254,0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,49,68,252,72,252,
+  80,252,82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,250,
+  120,250,122,250,200,252,211,252,212,252,213,252,214,252,215,252,
+  217,252,218,249,219,249,220,249,221,249,222,240,250,252,251,252,
+  252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,
+  0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,
+  249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,
+  49,68,252,72,252,80,252,82,252,85,237,86,249,87,245,88,249,90,240,
+  118,252,119,250,120,250,122,250,200,252,211,252,212,252,213,252,
+  214,252,215,252,217,252,218,249,219,249,220,249,221,249,222,240,
+  250,252,251,252,252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,
+  0,32,252,0,36,252,0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,
+  0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,
+  252,0,122,240,49,68,252,72,252,80,252,82,252,85,237,86,249,87,245,
+  88,249,90,240,118,252,119,250,120,250,122,250,200,252,211,252,212,
+  252,213,252,214,252,215,252,217,252,218,249,219,249,220,249,221,
+  249,222,240,250,252,251,252,252,252,253,252,254,250,0,1,250,0,8,
+  252,0,14,252,0,32,252,0,36,252,0,78,252,0,82,252,0,100,237,0,102,
+  237,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,
+  0,116,249,0,117,252,0,122,240,49,68,252,72,252,80,252,82,252,85,
+  237,86,249,87,245,88,249,90,240,118,252,119,250,120,250,122,250,
+  200,252,211,252,212,252,213,252,214,252,215,252,217,252,218,249,
+  219,249,220,249,221,249,222,240,250,252,251,252,252,252,253,252,
+  254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,0,78,252,0,82,
+  252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,249,0,113,252,
+  0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,49,68,252,72,252,
+  80,252,82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,250,
+  120,250,122,250,200,252,211,252,212,252,213,252,214,252,215,252,
+  217,252,218,249,219,249,220,249,221,249,222,240,250,252,251,252,
+  252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,
+  0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,
+  249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,
+  49,68,252,72,252,80,252,82,252,85,237,86,249,87,245,88,249,90,240,
+  118,252,119,250,120,250,122,250,200,252,211,252,212,252,213,252,
+  214,252,215,252,217,252,218,249,219,249,220,249,221,249,222,240,
+  250,252,251,252,252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,
+  0,32,252,0,36,252,0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,
+  0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,
+  252,0,122,240,3,45,252,47,252,22,45,250,47,250,66,254,85,250,87,
+  249,88,252,89,247,90,245,193,254,194,254,195,254,196,254,197,254,
+  198,254,222,245,0,2,254,0,4,254,0,6,254,0,100,250,0,102,250,0,122,
+  245,22,45,250,47,250,66,254,85,250,87,249,88,252,89,247,90,245,193,
+  254,194,254,195,254,196,254,197,254,198,254,222,245,0,2,254,0,4,
+  254,0,6,254,0,100,250,0,102,250,0,122,245,22,45,250,47,250,66,254,
+  85,250,87,249,88,252,89,247,90,245,193,254,194,254,195,254,196,254,
+  197,254,198,254,222,245,0,2,254,0,4,254,0,6,254,0,100,250,0,102,
+  250,0,122,245,22,45,250,47,250,66,254,85,250,87,249,88,252,89,247,
+  90,245,193,254,194,254,195,254,196,254,197,254,198,254,222,245,0,
+  2,254,0,4,254,0,6,254,0,100,250,0,102,250,0,122,245,22,45,250,47,
+  250,66,254,85,250,87,249,88,252,89,247,90,245,193,254,194,254,195,
+  254,196,254,197,254,198,254,222,245,0,2,254,0,4,254,0,6,254,0,100,
+  250,0,102,250,0,122,245,22,45,250,47,250,66,254,85,250,87,249,88,
+  252,89,247,90,245,193,254,194,254,195,254,196,254,197,254,198,254,
+  222,245,0,2,254,0,4,254,0,6,254,0,100,250,0,102,250,0,122,245,13,
+  45,250,47,250,66,250,193,250,194,250,195,250,196,250,197,250,198,
+  250,0,2,250,0,4,250,0,6,250,13,45,250,47,250,66,250,193,250,194,
+  250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,13,45,
+  250,47,250,66,250,193,250,194,250,195,250,196,250,197,250,198,250,
+  0,2,250,0,4,250,0,6,250,13,45,250,47,250,66,250,193,250,194,250,
+  195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,65,45,234,
+  46,234,47,234,59,247,60,247,66,239,80,243,98,234,102,234,106,254,
+  112,234,118,239,193,239,194,239,195,239,196,239,197,239,198,239,
+  211,243,212,243,213,243,214,243,215,243,217,243,225,234,226,234,
+  227,234,228,245,229,234,230,234,233,234,234,234,235,234,236,234,
+  238,254,243,234,244,234,245,234,246,234,247,234,249,234,250,239,
+  251,239,252,239,253,239,0,2,239,0,3,245,0,4,239,0,5,245,0,6,239,
+  0,7,234,0,21,245,0,25,234,0,27,234,0,29,234,0,49,254,0,78,243,0,
+  79,245,0,82,243,0,83,234,0,109,239,0,113,239,0,115,239,0,117,239,
+  6,119,254,120,254,122,252,254,252,0,1,252,6,119,254,120,254,122,
+  252,254,252,0,1,252,6,119,254,120,254,122,252,254,252,0,1,252,6,
+  119,254,120,254,122,252,254,252,0,1,252,6,119,254,120,254,122,252,
+  254,252,0,1,252,6,119,254,120,254,122,252,254,252,0,1,252,4,45,254,
+  108,254,0,57,254,9,45,254,47,254,119,252,120,254,121,252,122,254,
+  254,254,0,1,254,9,45,254,47,254,119,252,120,254,121,252,122,254,
+  254,254,0,1,254,9,45,254,47,254,119,252,120,254,121,252,122,254,
+  254,254,0,1,254,9,45,254,47,254,119,252,120,254,121,252,122,254,
+  254,254,0,1,254,14,118,255,119,254,122,254,250,255,251,255,252,255,
+  253,255,254,254,0,1,254,0,109,255,0,113,255,0,115,255,0,117,255,
+  9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+  9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+  9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+  9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+  9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+  95,45,241,47,241,98,248,99,248,100,248,101,248,102,248,103,248,104,
+  248,105,248,106,248,107,248,108,248,109,248,110,248,111,248,112,
+  248,113,248,114,248,115,248,116,248,117,248,118,248,119,245,120,
+  245,121,243,122,245,123,248,225,248,226,248,227,248,228,248,229,
+  248,230,248,232,248,233,248,234,248,235,248,236,248,237,248,238,
+  248,239,248,240,248,242,248,243,248,244,248,245,248,246,248,247,
+  248,249,248,250,248,251,248,252,248,253,248,254,245,0,1,245,0,3,
+  248,0,5,248,0,7,248,0,9,248,0,15,248,0,19,248,0,21,248,0,25,248,
+  0,27,248,0,29,248,0,33,248,0,37,248,0,45,248,0,49,248,0,57,248,0,
+  60,248,0,62,248,0,68,248,0,70,248,0,72,248,0,74,248,0,79,248,0,83,
+  248,0,87,248,0,89,248,0,91,248,0,93,248,0,97,248,0,99,248,0,101,
+  248,0,109,248,0,113,248,0,115,248,0,117,248,0,124,248,0,126,248,
+  0,128,248,1,2,25,248,31,45,240,47,240,98,254,102,254,112,254,225,
+  254,226,254,227,254,228,254,229,254,230,254,233,254,234,254,235,
+  254,236,254,243,254,244,254,245,254,246,254,247,254,249,254,0,3,
+  254,0,5,254,0,7,254,0,21,254,0,25,254,0,27,254,0,29,254,0,79,254,
+  0,83,254,31,45,240,47,240,98,254,102,254,112,254,225,254,226,254,
+  227,254,228,254,229,254,230,254,233,254,234,254,235,254,236,254,
+  243,254,244,254,245,254,246,254,247,254,249,254,0,3,254,0,5,254,
+  0,7,254,0,21,254,0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,49,
+  68,252,72,252,80,252,82,252,85,237,86,249,87,245,88,249,90,240,118,
+  252,119,250,120,250,122,250,200,252,211,252,212,252,213,252,214,
+  252,215,252,217,252,218,249,219,249,220,249,221,249,222,240,250,
+  252,251,252,252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,
+  252,0,36,252,0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,
+  252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,
+  0,122,240,6,119,254,120,254,122,252,254,252,0,1,252,49,68,252,72,
+  252,80,252,82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,
+  250,120,250,122,250,200,252,211,252,212,252,213,252,214,252,215,
+  252,217,252,218,249,219,249,220,249,221,249,222,240,250,252,251,
+  252,252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,
+  36,252,0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,
+  0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,
+  240,6,119,254,120,254,122,252,254,252,0,1,252,49,68,252,72,252,80,
+  252,82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,250,120,
+  250,122,250,200,252,211,252,212,252,213,252,214,252,215,252,217,
+  252,218,249,219,249,220,249,221,249,222,240,250,252,251,252,252,
+  252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,0,
+  78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,249,
+  0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,6,119,
+  254,120,254,122,252,254,252,0,1,252,3,45,252,47,252,4,45,254,108,
+  254,0,57,254,3,45,252,47,252,4,45,254,108,254,0,57,254,18,45,245,
+  47,245,66,250,87,245,88,250,90,242,193,250,194,250,195,250,196,250,
+  197,250,198,250,222,242,0,2,250,0,4,250,0,6,250,0,122,242,18,45,
+  245,47,245,66,250,87,245,88,250,90,242,193,250,194,250,195,250,196,
+  250,197,250,198,250,222,242,0,2,250,0,4,250,0,6,250,0,122,242,9,
+  45,254,47,254,119,252,120,254,121,252,122,254,254,254,0,1,254,9,
+  45,254,47,254,119,252,120,254,121,252,122,254,254,254,0,1,254,9,
+  45,254,47,254,119,252,120,254,121,252,122,254,254,254,0,1,254,9,
+  45,254,47,254,119,252,120,254,121,252,122,254,254,254,0,1,254,5,
+  115,255,0,87,255,0,89,255,0,91,255,5,115,255,0,87,255,0,89,255,0,
+  91,255,40,80,249,102,250,112,250,118,252,122,249,211,249,212,249,
+  213,249,214,249,215,249,217,249,233,250,234,250,235,250,236,250,
+  243,250,244,250,245,250,246,250,247,250,249,250,250,252,251,252,
+  252,252,253,252,254,249,0,1,249,0,21,250,0,25,250,0,27,250,0,29,
+  250,0,78,249,0,79,250,0,82,249,0,83,250,0,109,252,0,113,252,0,115,
+  252,0,117,252,19,102,254,112,254,233,254,234,254,235,254,236,254,
+  243,254,244,254,245,254,246,254,247,254,249,254,0,21,254,0,25,254,
+  0,27,254,0,29,254,0,79,254,0,83,254,14,85,239,87,239,88,245,90,234,
+  122,252,222,234,254,252,0,1,252,0,100,239,0,102,239,0,122,234,1,
+  32,25,230,1,32,29,234,14,85,239,87,239,88,245,90,234,122,252,222,
+  234,254,252,0,1,252,0,100,239,0,102,239,0,122,234,1,32,25,230,1,
+  32,29,234,14,85,239,87,239,88,245,90,234,122,252,222,234,254,252,
+  0,1,252,0,100,239,0,102,239,0,122,234,1,32,25,230,1,32,29,234,14,
+  85,239,87,239,88,245,90,234,122,252,222,234,254,252,0,1,252,0,100,
+  239,0,102,239,0,122,234,1,32,25,230,1,32,29,234,14,118,255,119,254,
+  122,254,250,255,251,255,252,255,253,255,254,254,0,1,254,0,109,255,
+  0,113,255,0,115,255,0,117,255,14,118,255,119,254,122,254,250,255,
+  251,255,252,255,253,255,254,254,0,1,254,0,109,255,0,113,255,0,115,
+  255,0,117,255,14,118,255,119,254,122,254,250,255,251,255,252,255,
+  253,255,254,254,0,1,254,0,109,255,0,113,255,0,115,255,0,117,255,
+  22,45,250,47,250,66,254,85,250,87,249,88,252,89,247,90,245,193,254,
+  194,254,195,254,196,254,197,254,198,254,222,245,0,2,254,0,4,254,
+  0,6,254,0,100,250,0,102,250,0,122,245,9,45,250,47,250,119,254,120,
+  254,121,252,122,252,254,252,0,1,252,22,45,250,47,250,66,254,85,250,
+  87,249,88,252,89,247,90,245,193,254,194,254,195,254,196,254,197,
+  254,198,254,222,245,0,2,254,0,4,254,0,6,254,0,100,250,0,102,250,
+  0,122,245,9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,
+  0,1,252,27,80,254,85,252,86,250,87,249,88,252,90,249,211,254,212,
+  254,213,254,214,254,215,254,217,254,218,250,219,250,220,250,221,
+  250,222,249,0,78,254,0,82,254,0,100,252,0,102,252,0,108,250,0,112,
+  250,0,114,250,0,116,250,0,122,249,50,45,249,47,249,59,5,60,5,98,
+  255,106,3,108,3,109,3,110,4,111,4,113,5,117,7,118,3,119,5,122,5,
+  225,255,226,255,227,255,228,255,229,255,230,255,237,3,238,3,239,
+  3,240,3,242,4,250,3,251,3,252,3,253,3,254,5,0,1,5,0,3,255,0,5,255,
+  0,7,255,0,45,3,0,49,3,0,57,3,0,60,3,0,62,3,0,68,3,0,70,4,0,72,4,
+  0,74,4,0,101,7,0,109,3,0,113,3,0,115,3,0,117,3,27,80,254,85,252,
+  86,250,87,249,88,252,90,249,211,254,212,254,213,254,214,254,215,
+  254,217,254,218,250,219,250,220,250,221,250,222,249,0,78,254,0,82,
+  254,0,100,252,0,102,252,0,108,250,0,112,250,0,114,250,0,116,250,
+  0,122,249,50,45,249,47,249,59,5,60,5,98,255,106,3,108,3,109,3,110,
+  4,111,4,113,5,117,7,118,3,119,5,122,5,225,255,226,255,227,255,228,
+  255,229,255,230,255,237,3,238,3,239,3,240,3,242,4,250,3,251,3,252,
+  3,253,3,254,5,0,1,5,0,3,255,0,5,255,0,7,255,0,45,3,0,49,3,0,57,3,
+  0,60,3,0,62,3,0,68,3,0,70,4,0,72,4,0,74,4,0,101,7,0,109,3,0,113,
+  3,0,115,3,0,117,3,27,80,254,85,252,86,250,87,249,88,252,90,249,211,
+  254,212,254,213,254,214,254,215,254,217,254,218,250,219,250,220,
+  250,221,250,222,249,0,78,254,0,82,254,0,100,252,0,102,252,0,108,
+  250,0,112,250,0,114,250,0,116,250,0,122,249,50,45,249,47,249,59,
+  5,60,5,98,255,106,3,108,3,109,3,110,4,111,4,113,5,117,7,118,3,119,
+  5,122,5,225,255,226,255,227,255,228,255,229,255,230,255,237,3,238,
+  3,239,3,240,3,242,4,250,3,251,3,252,3,253,3,254,5,0,1,5,0,3,255,
+  0,5,255,0,7,255,0,45,3,0,49,3,0,57,3,0,60,3,0,62,3,0,68,3,0,70,4,
+  0,72,4,0,74,4,0,101,7,0,109,3,0,113,3,0,115,3,0,117,3,3,45,254,47,
+  254,4,45,254,47,254,120,252,3,45,254,47,254,4,45,254,47,254,120,
+  252,3,45,254,47,254,4,45,254,47,254,120,252,70,45,237,46,234,47,
+  237,59,254,60,254,66,237,80,250,98,237,102,237,112,237,115,237,118,
+  237,120,237,122,237,193,237,194,237,195,237,196,237,197,237,198,
+  237,211,250,212,250,213,250,214,250,215,250,217,250,225,237,226,
+  237,227,237,228,247,229,237,230,237,233,247,234,237,235,237,236,
+  237,243,237,244,237,245,237,246,247,247,237,249,237,250,237,251,
+  237,252,237,253,237,254,237,0,1,247,0,2,237,0,3,247,0,4,237,0,5,
+  247,0,6,237,0,7,237,0,21,247,0,25,237,0,27,237,0,29,237,0,78,250,
+  0,79,247,0,82,250,0,83,237,0,87,237,0,89,237,0,91,237,0,109,247,
+  0,113,237,0,115,237,0,117,237,70,45,237,46,234,47,237,59,254,60,
+  254,66,237,80,250,98,237,102,237,112,237,115,237,118,237,120,237,
+  122,237,193,237,194,237,195,237,196,237,197,237,198,237,211,250,
+  212,250,213,250,214,250,215,250,217,250,225,237,226,237,227,237,
+  228,247,229,237,230,237,233,247,234,237,235,237,236,237,243,237,
+  244,237,245,237,246,247,247,237,249,237,250,237,251,237,252,237,
+  253,237,254,237,0,1,247,0,2,237,0,3,247,0,4,237,0,5,247,0,6,237,
+  0,7,237,0,21,247,0,25,237,0,27,237,0,29,237,0,78,250,0,79,247,0,
+  82,250,0,83,237,0,87,237,0,89,237,0,91,237,0,109,247,0,113,237,0,
+  115,237,0,117,237,13,45,250,47,250,66,250,193,250,194,250,195,250,
+  196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,13,45,250,47,250,
+  66,250,193,250,194,250,195,250,196,250,197,250,198,250,0,2,250,0,
+  4,250,0,6,250,13,45,250,47,250,66,250,193,250,194,250,195,250,196,
+  250,197,250,198,250,0,2,250,0,4,250,0,6,250,13,45,250,47,250,66,
+  250,193,250,194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,
+  250,0,6,250,65,45,234,46,234,47,234,59,247,60,247,66,239,80,243,
+  98,234,102,234,106,254,112,234,118,239,193,239,194,239,195,239,196,
+  239,197,239,198,239,211,243,212,243,213,243,214,243,215,243,217,
+  243,225,234,226,234,227,234,228,245,229,234,230,234,233,234,234,
+  234,235,234,236,234,238,254,243,234,244,234,245,234,246,234,247,
+  234,249,234,250,239,251,239,252,239,253,239,0,2,239,0,3,245,0,4,
+  239,0,5,245,0,6,239,0,7,234,0,21,245,0,25,234,0,27,234,0,29,234,
+  0,49,254,0,78,243,0,79,234,0,82,243,0,83,234,0,109,239,0,113,239,
+  0,115,239,0,117,239,19,102,254,112,254,233,254,234,254,235,254,236,
+  254,243,254,244,254,245,254,246,254,247,254,249,254,0,21,254,0,25,
+  254,0,27,254,0,29,254,0,79,254,0,83,254,19,102,254,112,254,233,254,
+  234,254,235,254,236,254,243,254,244,254,245,254,246,254,247,254,
+  249,254,0,21,254,0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,19,
+  102,254,112,254,233,254,234,254,235,254,236,254,243,254,244,254,
+  245,254,246,254,247,254,249,254,0,21,254,0,25,254,0,27,254,0,29,
+  254,0,79,254,0,83,254,3,45,254,47,254,4,45,254,47,254,120,252,2,
+  1,32,24,247,14,33,245,101,249,115,249,116,249,0,19,249,0,87,249,
+  0,89,249,0,91,249,0,93,249,0,97,249,0,99,249,1,2,25,249,1,32,25,
+  247,2,33,250
+};
+static afm_cuint16 afm_Helvetica_highchars_index[] = { /* 220 */
+  161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+  178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+  194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+  210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+  226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+  242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+  258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+  280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+  313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+  336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+  354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+  380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+  8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+  8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+  64257,64258
+};
+static afm_cunicode afm_Helvetica_ligatures[] = { /* 3 */
+  102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Helvetica-Bold */
+/* FullName: Helvetica Bold */
+/* FamilyName: Helvetica */
+static afm_cuint8 afm_Helvetica_Bold_widths[] = { /* 315 */
+  46,56,79,93,93,148,120,40,56,56,65,97,46,56,46,46,93,93,93,93,93,
+  93,93,93,93,93,56,56,97,97,97,102,163,120,120,120,120,111,102,130,
+  120,46,93,120,102,139,120,130,111,130,120,111,102,120,111,157,111,
+  111,102,56,46,56,97,93,56,93,102,93,102,93,56,102,102,46,46,93,46,
+  148,102,102,102,102,65,93,56,102,93,130,93,93,83,65,47,65,97,56,
+  93,93,93,93,47,93,56,123,62,93,97,123,56,67,97,56,56,56,102,93,46,
+  56,56,61,93,139,139,139,102,120,120,120,120,120,120,167,120,111,
+  111,111,111,46,46,46,46,120,120,130,130,130,130,130,97,130,120,120,
+  120,120,111,111,102,93,93,93,93,93,93,148,93,93,93,93,93,46,46,46,
+  46,102,102,102,102,102,102,102,97,102,102,102,102,102,93,102,93,
+  120,93,120,93,120,93,120,93,120,93,120,124,120,102,111,93,111,93,
+  111,93,111,93,130,102,130,102,46,46,46,46,46,46,120,93,102,46,102,
+  46,102,67,102,46,120,102,120,102,120,102,130,102,130,102,167,157,
+  120,65,120,65,120,65,111,93,111,93,111,93,102,56,102,65,120,102,
+  120,102,120,102,120,102,111,102,83,102,83,102,83,93,111,93,56,56,
+  56,56,56,56,56,56,93,167,46,46,46,83,83,83,93,93,58,167,167,56,56,
+  28,93,167,82,102,100,97,92,92,92,92,82,42,102,102
+};
+static afm_sint16 afm_Helvetica_Bold_kerning_index[] = { /* 315 */
+  1,0,0,0,0,0,0,0,0,0,0,0,29,0,40,0,0,0,0,0,0,0,0,0,0,0,51,54,0,0,
+  0,0,0,57,172,0,218,0,257,0,0,0,308,358,450,0,0,485,534,627,654,0,
+  716,874,902,1051,0,1199,0,0,0,0,0,0,0,1338,1358,1401,1427,1444,1462,
+  1517,1548,0,0,1556,1577,1587,1617,1649,1663,0,1671,1752,0,0,1755,
+  1803,1828,1851,1921,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,0,0,0,0,0,0,0,0,0,0,1944,2059,2174,2289,2404,2519,0,0,0,0,0,
+  0,0,0,0,0,0,0,2634,2683,2732,2781,2830,0,2879,2928,2956,2984,3012,
+  3040,0,0,3179,3199,3219,3239,3259,3279,0,3299,3325,3343,3361,3379,
+  0,0,0,0,0,3397,3429,3443,3457,3471,3485,0,3499,0,0,0,0,3513,0,3583,
+  3653,3768,3788,3903,3923,4038,0,4058,0,4084,4110,0,4149,4188,0,4205,
+  0,4223,0,4241,0,4259,0,4277,0,4308,0,0,0,0,0,0,4339,4431,4452,4487,
+  4497,4532,0,0,4542,4577,0,4587,0,4619,0,4651,4683,4732,4746,4795,
+  0,0,4809,4871,4952,5014,5095,5157,0,5238,0,5241,0,5244,5247,0,5405,
+  0,5563,0,5591,0,5619,0,5647,0,5675,0,5814,0,5837,0,5860,0,0,5883,
+  0,0,0,0,0,0,0,0,0,0,5886,5891,0,0,5942,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,0,0,0,0,0,0,0,0
+};
+static afm_cuint8 afm_Helvetica_Bold_kerning_data[] = { /* 5945 */
+  42,11,85,240,87,244,88,244,90,237,222,237,0,100,240,0,102,240,0,
+  122,237,1,32,24,247,1,32,28,244,4,33,250,1,32,25,237,1,32,29,237,
+  4,33,250,1,32,25,237,1,32,29,237,2,33,250,2,33,250,49,68,250,72,
+  249,80,250,82,250,85,242,86,249,87,244,88,247,90,239,118,252,119,
+  250,120,252,122,252,200,250,211,250,212,250,213,250,214,250,215,
+  250,217,250,218,249,219,249,220,249,221,249,222,239,250,252,251,
+  252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,0,32,249,0,
+  36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,0,109,252,
+  0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,
+  239,20,66,252,86,255,193,252,194,252,195,252,196,252,197,252,198,
+  252,218,255,219,255,220,255,221,255,0,2,252,0,4,252,0,6,252,0,108,
+  255,0,112,255,0,114,255,0,116,255,18,45,252,47,252,66,250,87,250,
+  88,250,90,245,193,250,194,250,195,250,196,250,197,250,198,250,222,
+  245,0,2,250,0,4,250,0,6,250,0,122,245,23,45,240,47,240,66,244,98,
+  254,193,244,194,244,195,244,196,244,197,244,198,244,225,254,226,
+  254,227,254,228,254,229,254,230,254,0,2,244,0,3,254,0,4,244,0,5,
+  254,0,6,244,0,7,254,22,45,254,47,254,66,254,118,254,193,254,194,
+  254,195,254,196,254,197,254,198,254,250,254,251,254,252,254,253,
+  254,0,2,254,0,4,254,0,6,254,0,109,254,0,113,254,0,115,254,0,117,
+  254,40,80,252,102,254,112,251,118,252,122,250,211,252,212,252,213,
+  252,214,252,215,252,217,252,233,254,234,254,235,254,236,254,243,
+  251,244,251,245,251,246,251,247,251,249,251,250,252,251,252,252,
+  252,253,252,254,250,0,1,250,0,21,254,0,25,254,0,27,254,0,29,254,
+  0,78,252,0,79,251,0,82,252,0,83,251,0,109,252,0,113,252,0,115,252,
+  0,117,252,14,85,242,87,239,88,244,90,237,122,252,222,237,254,252,
+  0,1,252,0,100,242,0,102,242,0,122,237,1,32,25,234,1,32,29,234,22,
+  45,250,47,250,66,249,85,250,87,249,88,249,89,249,90,245,193,249,
+  194,249,195,249,196,249,197,249,198,249,222,245,0,2,249,0,4,249,
+  0,6,249,0,100,250,0,102,250,0,122,245,41,45,237,47,237,66,240,98,
+  252,102,252,112,250,193,240,194,240,195,240,196,240,197,240,198,
+  240,225,252,226,252,227,252,228,252,229,252,230,252,233,252,234,
+  252,235,252,236,252,243,250,244,250,245,250,246,250,247,250,249,
+  250,0,2,240,0,3,252,0,4,240,0,5,252,0,6,240,0,7,252,0,21,252,0,25,
+  252,0,27,252,0,29,252,0,79,250,0,83,250,12,45,3,47,3,86,255,218,
+  255,219,255,220,255,221,255,0,108,255,0,112,255,0,114,255,0,116,
+  255,27,80,254,85,254,86,254,87,249,88,250,90,249,211,254,212,254,
+  213,254,214,254,215,254,217,254,218,254,219,254,220,254,221,254,
+  222,249,0,78,254,0,82,254,0,100,254,0,102,254,0,108,254,0,112,254,
+  0,114,254,0,116,254,0,122,249,69,45,244,46,237,47,244,59,250,60,
+  250,66,242,80,250,98,244,102,247,112,244,115,244,118,242,120,247,
+  122,247,193,242,194,242,195,242,196,242,197,242,198,242,211,250,
+  212,250,213,250,214,250,215,250,217,250,225,244,226,244,227,244,
+  228,244,229,244,230,244,233,247,234,247,235,247,236,247,243,244,
+  244,244,245,244,246,244,247,244,249,244,250,242,251,242,252,242,
+  253,242,254,247,0,1,247,0,2,242,0,3,244,0,4,242,0,5,244,0,6,242,
+  0,7,244,0,21,247,0,25,247,0,27,247,0,29,247,0,78,250,0,79,244,0,
+  82,250,0,83,244,0,87,244,0,89,244,0,109,242,0,113,242,0,115,242,
+  0,117,242,13,45,252,47,252,66,249,193,249,194,249,195,249,196,249,
+  197,249,198,249,0,2,249,0,4,249,0,6,249,65,45,237,46,244,47,237,
+  59,250,60,250,66,244,72,249,80,249,98,247,102,249,112,242,118,247,
+  193,244,194,244,195,244,196,244,197,244,198,244,211,249,212,249,
+  213,249,214,249,215,249,217,249,225,247,226,247,227,247,228,247,
+  229,247,230,247,233,249,234,249,235,249,236,249,243,242,244,242,
+  245,242,246,242,247,242,249,242,250,247,251,247,252,247,253,247,
+  0,2,244,0,3,247,0,4,244,0,5,247,0,6,244,0,7,247,0,21,249,0,25,249,
+  0,27,249,0,29,249,0,32,249,0,36,249,0,78,249,0,79,242,0,82,249,0,
+  83,242,0,109,247,0,113,247,0,115,247,0,117,247,65,45,244,46,250,
+  47,244,59,255,60,255,66,247,80,254,98,250,102,251,112,247,118,249,
+  122,254,193,247,194,247,195,247,196,247,197,247,198,247,211,254,
+  212,254,213,254,214,254,215,254,217,254,225,250,226,250,227,250,
+  228,250,229,250,230,250,233,251,234,251,235,251,236,251,243,247,
+  244,247,245,247,246,247,247,247,249,247,250,249,251,249,252,249,
+  253,249,254,254,0,1,254,0,2,247,0,3,250,0,4,247,0,5,250,0,6,247,
+  0,7,250,0,21,251,0,25,251,0,27,251,0,29,251,0,78,254,0,79,247,0,
+  82,254,0,83,247,0,109,249,0,113,249,0,115,249,0,117,249,61,45,240,
+  47,240,59,249,60,249,66,239,80,245,98,242,102,244,112,240,118,240,
+  193,239,194,239,195,239,196,239,197,239,198,239,211,245,212,245,
+  213,245,214,245,215,245,217,245,225,242,226,242,227,242,228,242,
+  229,242,230,242,233,244,234,244,235,244,236,244,243,240,244,240,
+  245,240,246,240,247,240,249,240,250,240,251,240,252,240,253,240,
+  0,2,239,0,3,242,0,4,239,0,5,242,0,6,239,0,7,242,0,21,244,0,25,244,
+  0,27,244,0,29,244,0,78,245,0,79,240,0,82,245,0,83,240,0,109,240,
+  0,113,240,0,115,240,0,117,240,9,104,255,119,254,120,254,122,254,
+  254,254,0,1,254,0,33,255,0,37,255,18,109,255,118,254,119,254,122,
+  254,250,254,251,254,252,254,253,254,254,254,0,1,254,0,60,255,0,62,
+  255,0,68,255,0,109,254,0,113,254,0,115,254,0,117,254,11,105,255,
+  108,254,109,254,122,255,254,255,0,1,255,0,57,254,0,60,254,0,62,254,
+  0,68,254,8,101,255,119,254,120,254,122,254,254,254,0,1,254,0,19,
+  255,9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,254,
+  23,45,255,47,255,102,255,112,254,233,255,234,255,235,255,236,255,
+  243,254,244,254,245,254,246,254,247,254,249,254,0,21,255,0,25,255,
+  0,27,255,0,29,255,0,79,254,0,83,254,1,32,25,5,1,32,29,5,13,102,2,
+  104,255,233,2,234,2,235,2,236,2,0,21,2,0,25,2,0,27,2,0,29,2,0,33,
+  255,0,37,255,4,122,254,254,254,0,1,254,10,112,254,243,254,244,254,
+  245,254,246,254,247,254,249,254,0,79,254,0,83,254,5,120,254,122,
+  254,254,254,0,1,254,13,118,254,122,252,250,254,251,254,252,254,253,
+  254,254,252,0,1,252,0,109,254,0,113,254,0,115,254,0,117,254,14,118,
+  255,119,250,122,254,250,255,251,255,252,255,253,255,254,254,0,1,
+  254,0,109,255,0,113,255,0,115,255,0,117,255,7,119,254,120,254,121,
+  252,122,254,254,254,0,1,254,4,122,254,254,254,0,1,254,34,45,247,
+  46,254,47,247,100,254,101,254,104,254,112,254,114,254,116,254,117,
+  3,119,2,122,2,232,254,243,254,244,254,245,254,246,254,247,254,249,
+  254,254,2,0,1,2,0,9,254,0,15,254,0,19,254,0,33,254,0,37,254,0,79,
+  254,0,83,254,0,93,254,0,97,254,0,99,254,0,101,3,1,2,25,254,2,120,
+  254,22,45,244,47,244,98,254,112,252,225,254,226,254,227,254,228,
+  254,229,254,230,254,243,252,244,252,245,252,246,252,247,252,249,
+  252,0,3,254,0,5,254,0,7,254,0,79,252,0,83,252,12,45,250,47,250,112,
+  254,243,254,244,254,245,254,246,254,247,254,249,254,0,79,254,0,83,
+  254,10,102,255,233,255,234,255,235,255,236,255,0,21,255,0,25,255,
+  0,27,255,0,29,255,31,45,244,47,244,98,252,102,255,112,253,225,252,
+  226,252,227,252,228,252,229,252,230,252,233,255,234,255,235,255,
+  236,255,243,253,244,253,245,253,246,253,247,253,249,253,0,3,252,
+  0,5,252,0,7,252,0,21,255,0,25,255,0,27,255,0,29,255,0,79,253,0,83,
+  253,10,102,2,233,2,234,2,235,2,236,2,0,21,2,0,25,2,0,27,2,0,29,2,
+  49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,88,247,90,239,
+  118,252,119,250,120,252,122,252,200,250,211,250,212,250,213,250,
+  214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,239,
+  250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,
+  0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,
+  0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,
+  252,0,122,239,49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,
+  88,247,90,239,118,252,119,250,120,252,122,252,200,250,211,250,212,
+  250,213,250,214,250,215,250,217,250,218,249,219,249,220,249,221,
+  249,222,239,250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,
+  250,0,14,250,0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,
+  242,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,
+  0,116,249,0,117,252,0,122,239,49,68,250,72,249,80,250,82,250,85,
+  242,86,249,87,244,88,247,90,239,118,252,119,250,120,252,122,252,
+  200,250,211,250,212,250,213,250,214,250,215,250,217,250,218,249,
+  219,249,220,249,221,249,222,239,250,252,251,252,252,252,253,252,
+  254,252,0,1,252,0,8,250,0,14,250,0,32,249,0,36,249,0,78,250,0,82,
+  250,0,100,242,0,102,242,0,108,249,0,109,252,0,112,249,0,113,252,
+  0,114,249,0,115,252,0,116,249,0,117,252,0,122,239,49,68,250,72,249,
+  80,250,82,250,85,242,86,249,87,244,88,247,90,239,118,252,119,250,
+  120,252,122,252,200,250,211,250,212,250,213,250,214,250,215,250,
+  217,250,218,249,219,249,220,249,221,249,222,239,250,252,251,252,
+  252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,0,32,249,0,36,249,
+  0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,0,109,252,0,112,
+  249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,239,
+  49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,88,247,90,239,
+  118,252,119,250,120,252,122,252,200,250,211,250,212,250,213,250,
+  214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,239,
+  250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,
+  0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,
+  0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,
+  252,0,122,239,49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,
+  88,247,90,239,118,252,119,250,120,252,122,252,200,250,211,250,212,
+  250,213,250,214,250,215,250,217,250,218,249,219,249,220,249,221,
+  249,222,239,250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,
+  250,0,14,250,0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,
+  242,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,
+  0,116,249,0,117,252,0,122,239,22,45,250,47,250,66,249,85,250,87,
+  249,88,249,89,249,90,245,193,249,194,249,195,249,196,249,197,249,
+  198,249,222,245,0,2,249,0,4,249,0,6,249,0,100,250,0,102,250,0,122,
+  245,22,45,250,47,250,66,249,85,250,87,249,88,249,89,249,90,245,193,
+  249,194,249,195,249,196,249,197,249,198,249,222,245,0,2,249,0,4,
+  249,0,6,249,0,100,250,0,102,250,0,122,245,22,45,250,47,250,66,249,
+  85,250,87,249,88,249,89,249,90,245,193,249,194,249,195,249,196,249,
+  197,249,198,249,222,245,0,2,249,0,4,249,0,6,249,0,100,250,0,102,
+  250,0,122,245,22,45,250,47,250,66,249,85,250,87,249,88,249,89,249,
+  90,245,193,249,194,249,195,249,196,249,197,249,198,249,222,245,0,
+  2,249,0,4,249,0,6,249,0,100,250,0,102,250,0,122,245,22,45,250,47,
+  250,66,249,85,250,87,249,88,249,89,249,90,245,193,249,194,249,195,
+  249,196,249,197,249,198,249,222,245,0,2,249,0,4,249,0,6,249,0,100,
+  250,0,102,250,0,122,245,22,45,250,47,250,66,249,85,250,87,249,88,
+  249,89,249,90,245,193,249,194,249,195,249,196,249,197,249,198,249,
+  222,245,0,2,249,0,4,249,0,6,249,0,100,250,0,102,250,0,122,245,13,
+  45,252,47,252,66,249,193,249,194,249,195,249,196,249,197,249,198,
+  249,0,2,249,0,4,249,0,6,249,13,45,252,47,252,66,249,193,249,194,
+  249,195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,13,45,
+  252,47,252,66,249,193,249,194,249,195,249,196,249,197,249,198,249,
+  0,2,249,0,4,249,0,6,249,13,45,252,47,252,66,249,193,249,194,249,
+  195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,61,45,240,
+  47,240,59,249,60,249,66,239,80,245,98,242,102,244,112,240,118,240,
+  193,239,194,239,195,239,196,239,197,239,198,239,211,245,212,245,
+  213,245,214,245,215,245,217,245,225,242,226,242,227,242,228,242,
+  229,242,230,242,233,244,234,244,235,244,236,244,243,240,244,240,
+  245,240,246,240,247,240,249,240,250,240,251,240,252,240,253,240,
+  0,2,239,0,3,242,0,4,239,0,5,242,0,6,239,0,7,242,0,21,244,0,25,244,
+  0,27,244,0,29,244,0,78,245,0,79,240,0,82,245,0,83,240,0,109,240,
+  0,113,240,0,115,240,0,117,240,9,104,255,119,254,120,254,122,254,
+  254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,254,120,254,122,
+  254,254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,254,120,254,
+  122,254,254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,254,120,
+  254,122,254,254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,254,
+  120,254,122,254,254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,
+  254,120,254,122,254,254,254,0,1,254,0,33,255,0,37,255,11,105,255,
+  108,254,109,254,122,255,254,255,0,1,255,0,57,254,0,60,254,0,62,254,
+  0,68,254,9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,
+  254,9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,254,
+  9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,254,9,45,
+  2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,254,14,118,255,
+  119,250,122,254,250,255,251,255,252,255,253,255,254,254,0,1,254,
+  0,109,255,0,113,255,0,115,255,0,117,255,7,119,254,120,254,121,252,
+  122,254,254,254,0,1,254,7,119,254,120,254,121,252,122,254,254,254,
+  0,1,254,7,119,254,120,254,121,252,122,254,254,254,0,1,254,7,119,
+  254,120,254,121,252,122,254,254,254,0,1,254,7,119,254,120,254,121,
+  252,122,254,254,254,0,1,254,7,119,254,120,254,121,252,122,254,254,
+  254,0,1,254,31,45,244,47,244,98,252,102,255,112,253,225,252,226,
+  252,227,252,228,252,229,252,230,252,233,255,234,255,235,255,236,
+  255,243,253,244,253,245,253,246,253,247,253,249,253,0,3,252,0,5,
+  252,0,7,252,0,21,255,0,25,255,0,27,255,0,29,255,0,79,253,0,83,253,
+  31,45,244,47,244,98,252,102,255,112,253,225,252,226,252,227,252,
+  228,252,229,252,230,252,233,255,234,255,235,255,236,255,243,253,
+  244,253,245,253,246,253,247,253,249,253,0,3,252,0,5,252,0,7,252,
+  0,21,255,0,25,255,0,27,255,0,29,255,0,79,253,0,83,253,49,68,250,
+  72,249,80,250,82,250,85,242,86,249,87,244,88,247,90,239,118,252,
+  119,250,120,252,122,252,200,250,211,250,212,250,213,250,214,250,
+  215,250,217,250,218,249,219,249,220,249,221,249,222,239,250,252,
+  251,252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,0,32,249,
+  0,36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,0,109,252,
+  0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,
+  239,9,104,255,119,254,120,254,122,254,254,254,0,1,254,0,33,255,0,
+  37,255,49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,88,247,
+  90,239,118,252,119,250,120,252,122,252,200,250,211,250,212,250,213,
+  250,214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,
+  239,250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,
+  250,0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,
+  249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,
+  0,117,252,0,122,239,9,104,255,119,254,120,254,122,254,254,254,0,
+  1,254,0,33,255,0,37,255,49,68,250,72,249,80,250,82,250,85,242,86,
+  249,87,244,88,247,90,239,118,252,119,250,120,252,122,252,200,250,
+  211,250,212,250,213,250,214,250,215,250,217,250,218,249,219,249,
+  220,249,221,249,222,239,250,252,251,252,252,252,253,252,254,252,
+  0,1,252,0,8,250,0,14,250,0,32,249,0,36,249,0,78,250,0,82,250,0,100,
+  242,0,102,242,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,
+  0,115,252,0,116,249,0,117,252,0,122,239,9,104,255,119,254,120,254,
+  122,254,254,254,0,1,254,0,33,255,0,37,255,11,105,255,108,254,109,
+  254,122,255,254,255,0,1,255,0,57,254,0,60,254,0,62,254,0,68,254,
+  11,105,255,108,254,109,254,122,255,254,255,0,1,255,0,57,254,0,60,
+  254,0,62,254,0,68,254,18,45,252,47,252,66,250,87,250,88,250,90,245,
+  193,250,194,250,195,250,196,250,197,250,198,250,222,245,0,2,250,
+  0,4,250,0,6,250,0,122,245,18,45,252,47,252,66,250,87,250,88,250,
+  90,245,193,250,194,250,195,250,196,250,197,250,198,250,222,245,0,
+  2,250,0,4,250,0,6,250,0,122,245,8,101,255,119,254,120,254,122,254,
+  254,254,0,1,254,0,19,255,9,45,2,47,3,119,254,120,254,121,254,122,
+  254,254,254,0,1,254,9,45,2,47,3,119,254,120,254,121,254,122,254,
+  254,254,0,1,254,9,45,2,47,3,119,254,120,254,121,254,122,254,254,
+  254,0,1,254,9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,
+  0,1,254,13,102,2,104,255,233,2,234,2,235,2,236,2,0,21,2,0,25,2,0,
+  27,2,0,29,2,0,33,255,0,37,255,13,102,2,104,255,233,2,234,2,235,2,
+  236,2,0,21,2,0,25,2,0,27,2,0,29,2,0,33,255,0,37,255,40,80,252,102,
+  254,112,251,118,252,122,250,211,252,212,252,213,252,214,252,215,
+  252,217,252,233,254,234,254,235,254,236,254,243,251,244,251,245,
+  251,246,251,247,251,249,251,250,252,251,252,252,252,253,252,254,
+  250,0,1,250,0,21,254,0,25,254,0,27,254,0,29,254,0,78,252,0,79,251,
+  0,82,252,0,83,251,0,109,252,0,113,252,0,115,252,0,117,252,10,112,
+  254,243,254,244,254,245,254,246,254,247,254,249,254,0,79,254,0,83,
+  254,14,85,242,87,239,88,244,90,237,122,252,222,237,254,252,0,1,252,
+  0,100,242,0,102,242,0,122,237,1,32,25,234,1,32,29,234,5,120,254,
+  122,254,254,254,0,1,254,14,85,242,87,239,88,244,90,237,122,252,222,
+  237,254,252,0,1,252,0,100,242,0,102,242,0,122,237,1,32,25,234,1,
+  32,29,234,5,120,254,122,254,254,254,0,1,254,14,85,242,87,239,88,
+  244,90,237,122,252,222,237,254,252,0,1,252,0,100,242,0,102,242,0,
+  122,237,1,32,25,234,1,32,29,234,5,120,254,122,254,254,254,0,1,254,
+  14,118,255,119,250,122,254,250,255,251,255,252,255,253,255,254,254,
+  0,1,254,0,109,255,0,113,255,0,115,255,0,117,255,14,118,255,119,250,
+  122,254,250,255,251,255,252,255,253,255,254,254,0,1,254,0,109,255,
+  0,113,255,0,115,255,0,117,255,14,118,255,119,250,122,254,250,255,
+  251,255,252,255,253,255,254,254,0,1,254,0,109,255,0,113,255,0,115,
+  255,0,117,255,22,45,250,47,250,66,249,85,250,87,249,88,249,89,249,
+  90,245,193,249,194,249,195,249,196,249,197,249,198,249,222,245,0,
+  2,249,0,4,249,0,6,249,0,100,250,0,102,250,0,122,245,7,119,254,120,
+  254,121,252,122,254,254,254,0,1,254,22,45,250,47,250,66,249,85,250,
+  87,249,88,249,89,249,90,245,193,249,194,249,195,249,196,249,197,
+  249,198,249,222,245,0,2,249,0,4,249,0,6,249,0,100,250,0,102,250,
+  0,122,245,7,119,254,120,254,121,252,122,254,254,254,0,1,254,27,80,
+  254,85,254,86,254,87,249,88,250,90,249,211,254,212,254,213,254,214,
+  254,215,254,217,254,218,254,219,254,220,254,221,254,222,249,0,78,
+  254,0,82,254,0,100,254,0,102,254,0,108,254,0,112,254,0,114,254,0,
+  116,254,0,122,249,34,45,247,46,254,47,247,100,254,101,254,104,254,
+  112,254,114,254,116,254,117,3,119,2,122,2,232,254,243,254,244,254,
+  245,254,246,254,247,254,249,254,254,2,0,1,2,0,9,254,0,15,254,0,19,
+  254,0,33,254,0,37,254,0,79,254,0,83,254,0,93,254,0,97,254,0,99,254,
+  0,101,3,1,2,25,254,27,80,254,85,254,86,254,87,249,88,250,90,249,
+  211,254,212,254,213,254,214,254,215,254,217,254,218,254,219,254,
+  220,254,221,254,222,249,0,78,254,0,82,254,0,100,254,0,102,254,0,
+  108,254,0,112,254,0,114,254,0,116,254,0,122,249,34,45,247,46,254,
+  47,247,100,254,101,254,104,254,112,254,114,254,116,254,117,3,119,
+  2,122,2,232,254,243,254,244,254,245,254,246,254,247,254,249,254,
+  254,2,0,1,2,0,9,254,0,15,254,0,19,254,0,33,254,0,37,254,0,79,254,
+  0,83,254,0,93,254,0,97,254,0,99,254,0,101,3,1,2,25,254,27,80,254,
+  85,254,86,254,87,249,88,250,90,249,211,254,212,254,213,254,214,254,
+  215,254,217,254,218,254,219,254,220,254,221,254,222,249,0,78,254,
+  0,82,254,0,100,254,0,102,254,0,108,254,0,112,254,0,114,254,0,116,
+  254,0,122,249,34,45,247,46,254,47,247,100,254,101,254,104,254,112,
+  254,114,254,116,254,117,3,119,2,122,2,232,254,243,254,244,254,245,
+  254,246,254,247,254,249,254,254,2,0,1,2,0,9,254,0,15,254,0,19,254,
+  0,33,254,0,37,254,0,79,254,0,83,254,0,93,254,0,97,254,0,99,254,0,
+  101,3,1,2,25,254,2,120,254,2,120,254,2,120,254,69,45,244,46,237,
+  47,244,59,250,60,250,66,242,80,250,98,244,102,247,112,244,115,244,
+  118,242,120,247,122,247,193,242,194,242,195,242,196,242,197,242,
+  198,242,211,250,212,250,213,250,214,250,215,250,217,250,225,244,
+  226,244,227,244,228,244,229,244,230,244,233,247,234,247,235,247,
+  236,247,243,244,244,244,245,244,246,244,247,244,249,244,250,242,
+  251,242,252,242,253,242,254,247,0,1,247,0,2,242,0,3,244,0,4,242,
+  0,5,244,0,6,242,0,7,244,0,21,247,0,25,247,0,27,247,0,29,247,0,78,
+  250,0,79,244,0,82,250,0,83,244,0,87,244,0,89,244,0,109,242,0,113,
+  242,0,115,242,0,117,242,69,45,244,46,237,47,244,59,250,60,250,66,
+  242,80,250,98,244,102,247,112,244,115,244,118,242,120,247,122,247,
+  193,242,194,242,195,242,196,242,197,242,198,242,211,250,212,250,
+  213,250,214,250,215,250,217,250,225,244,226,244,227,244,228,244,
+  229,244,230,244,233,247,234,247,235,247,236,247,243,244,244,244,
+  245,244,246,244,247,244,249,244,250,242,251,242,252,242,253,242,
+  254,247,0,1,247,0,2,242,0,3,244,0,4,242,0,5,244,0,6,242,0,7,244,
+  0,21,247,0,25,247,0,27,247,0,29,247,0,78,250,0,79,244,0,82,250,0,
+  83,244,0,87,244,0,89,244,0,109,242,0,113,242,0,115,242,0,117,242,
+  13,45,252,47,252,66,249,193,249,194,249,195,249,196,249,197,249,
+  198,249,0,2,249,0,4,249,0,6,249,13,45,252,47,252,66,249,193,249,
+  194,249,195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,
+  13,45,252,47,252,66,249,193,249,194,249,195,249,196,249,197,249,
+  198,249,0,2,249,0,4,249,0,6,249,13,45,252,47,252,66,249,193,249,
+  194,249,195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,
+  61,45,240,47,240,59,249,60,249,66,239,80,245,98,242,102,244,112,
+  240,118,240,193,239,194,239,195,239,196,239,197,239,198,239,211,
+  245,212,245,213,245,214,245,215,245,217,245,225,242,226,242,227,
+  242,228,242,229,242,230,242,233,244,234,244,235,244,236,244,243,
+  240,244,240,245,240,246,240,247,240,249,240,250,240,251,240,252,
+  240,253,240,0,2,239,0,3,242,0,4,239,0,5,242,0,6,239,0,7,242,0,21,
+  244,0,25,244,0,27,244,0,29,244,0,78,245,0,79,240,0,82,245,0,83,240,
+  0,109,240,0,113,240,0,115,240,0,117,240,10,102,2,233,2,234,2,235,
+  2,236,2,0,21,2,0,25,2,0,27,2,0,29,2,10,102,2,233,2,234,2,235,2,236,
+  2,0,21,2,0,25,2,0,27,2,0,29,2,10,102,2,233,2,234,2,235,2,236,2,0,
+  21,2,0,25,2,0,27,2,0,29,2,2,120,254,2,1,32,24,249,19,33,244,101,
+  244,109,254,115,250,116,247,119,254,0,19,244,0,60,254,0,62,254,0,
+  68,254,0,87,250,0,89,250,0,91,250,0,93,247,0,97,247,0,99,247,1,2,
+  25,247,1,32,25,249,2,33,244
+};
+static afm_cuint16 afm_Helvetica_Bold_highchars_index[] = { /* 220 */
+  161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+  178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+  194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+  210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+  226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+  242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+  258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+  280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+  313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+  336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+  354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+  380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+  8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+  8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+  64257,64258
+};
+static afm_cunicode afm_Helvetica_Bold_ligatures[] = { /* 3 */
+  102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Helvetica-BoldOblique */
+/* FullName: Helvetica Bold Oblique */
+/* FamilyName: Helvetica */
+static afm_cuint8 afm_Helvetica_BoldOblique_widths[] = { /* 315 */
+  46,56,79,93,93,148,120,40,56,56,65,97,46,56,46,46,93,93,93,93,93,
+  93,93,93,93,93,56,56,97,97,97,102,163,120,120,120,120,111,102,130,
+  120,46,93,120,102,139,120,130,111,130,120,111,102,120,111,157,111,
+  111,102,56,46,56,97,93,56,93,102,93,102,93,56,102,102,46,46,93,46,
+  148,102,102,102,102,65,93,56,102,93,130,93,93,83,65,47,65,97,56,
+  93,93,93,93,47,93,56,123,62,93,97,123,56,67,97,56,56,56,102,93,46,
+  56,56,61,93,139,139,139,102,120,120,120,120,120,120,167,120,111,
+  111,111,111,46,46,46,46,120,120,130,130,130,130,130,97,130,120,120,
+  120,120,111,111,102,93,93,93,93,93,93,148,93,93,93,93,93,46,46,46,
+  46,102,102,102,102,102,102,102,97,102,102,102,102,102,93,102,93,
+  120,93,120,93,120,93,120,93,120,93,120,124,120,102,111,93,111,93,
+  111,93,111,93,130,102,130,102,46,46,46,46,46,46,120,93,102,46,102,
+  46,102,67,102,46,120,102,120,102,120,102,130,102,130,102,167,157,
+  120,65,120,65,120,65,111,93,111,93,111,93,102,56,102,65,120,102,
+  120,102,120,102,120,102,111,102,83,102,83,102,83,93,111,93,56,56,
+  56,56,56,56,56,56,93,167,46,46,46,83,83,83,93,93,58,167,167,56,56,
+  28,93,167,82,102,100,97,92,92,92,92,82,42,102,102
+};
+static afm_sint16 afm_Helvetica_BoldOblique_kerning_index[] = { /* 315 */
+  1,0,0,0,0,0,0,0,0,0,0,0,29,0,40,0,0,0,0,0,0,0,0,0,0,0,51,54,0,0,
+  0,0,0,57,172,0,218,0,257,0,0,0,308,358,450,0,0,485,534,627,654,0,
+  716,874,902,1051,0,1199,0,0,0,0,0,0,0,1338,1358,1401,1427,1444,1462,
+  1517,1548,0,0,1556,1577,1587,1617,1649,1663,0,1671,1752,0,0,1755,
+  1803,1828,1851,1921,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,0,0,0,0,0,0,0,0,0,0,1944,2059,2174,2289,2404,2519,0,0,0,0,0,
+  0,0,0,0,0,0,0,2634,2683,2732,2781,2830,0,2879,2928,2956,2984,3012,
+  3040,0,0,3179,3199,3219,3239,3259,3279,0,3299,3325,3343,3361,3379,
+  0,0,0,0,0,3397,3429,3443,3457,3471,3485,0,3499,0,0,0,0,3513,0,3583,
+  3653,3768,3788,3903,3923,4038,0,4058,0,4084,4110,0,4149,4188,0,4205,
+  0,4223,0,4241,0,4259,0,4277,0,4308,0,0,0,0,0,0,4339,4431,4452,4487,
+  4497,4532,0,0,4542,4577,0,4587,0,4619,0,4651,4683,4732,4746,4795,
+  0,0,4809,4871,4952,5014,5095,5157,0,5238,0,5241,0,5244,5247,0,5405,
+  0,5563,0,5591,0,5619,0,5647,0,5675,0,5814,0,5837,0,5860,0,0,5883,
+  0,0,0,0,0,0,0,0,0,0,5886,5891,0,0,5942,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,0,0,0,0,0,0,0,0
+};
+static afm_cuint8 afm_Helvetica_BoldOblique_kerning_data[] = { /* 5945 */
+  42,11,85,240,87,244,88,244,90,237,222,237,0,100,240,0,102,240,0,
+  122,237,1,32,24,247,1,32,28,244,4,33,250,1,32,25,237,1,32,29,237,
+  4,33,250,1,32,25,237,1,32,29,237,2,33,250,2,33,250,49,68,250,72,
+  249,80,250,82,250,85,242,86,249,87,244,88,247,90,239,118,252,119,
+  250,120,252,122,252,200,250,211,250,212,250,213,250,214,250,215,
+  250,217,250,218,249,219,249,220,249,221,249,222,239,250,252,251,
+  252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,0,32,249,0,
+  36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,0,109,252,
+  0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,
+  239,20,66,252,86,255,193,252,194,252,195,252,196,252,197,252,198,
+  252,218,255,219,255,220,255,221,255,0,2,252,0,4,252,0,6,252,0,108,
+  255,0,112,255,0,114,255,0,116,255,18,45,252,47,252,66,250,87,250,
+  88,250,90,245,193,250,194,250,195,250,196,250,197,250,198,250,222,
+  245,0,2,250,0,4,250,0,6,250,0,122,245,23,45,240,47,240,66,244,98,
+  254,193,244,194,244,195,244,196,244,197,244,198,244,225,254,226,
+  254,227,254,228,254,229,254,230,254,0,2,244,0,3,254,0,4,244,0,5,
+  254,0,6,244,0,7,254,22,45,254,47,254,66,254,118,254,193,254,194,
+  254,195,254,196,254,197,254,198,254,250,254,251,254,252,254,253,
+  254,0,2,254,0,4,254,0,6,254,0,109,254,0,113,254,0,115,254,0,117,
+  254,40,80,252,102,254,112,251,118,252,122,250,211,252,212,252,213,
+  252,214,252,215,252,217,252,233,254,234,254,235,254,236,254,243,
+  251,244,251,245,251,246,251,247,251,249,251,250,252,251,252,252,
+  252,253,252,254,250,0,1,250,0,21,254,0,25,254,0,27,254,0,29,254,
+  0,78,252,0,79,251,0,82,252,0,83,251,0,109,252,0,113,252,0,115,252,
+  0,117,252,14,85,242,87,239,88,244,90,237,122,252,222,237,254,252,
+  0,1,252,0,100,242,0,102,242,0,122,237,1,32,25,234,1,32,29,234,22,
+  45,250,47,250,66,249,85,250,87,249,88,249,89,249,90,245,193,249,
+  194,249,195,249,196,249,197,249,198,249,222,245,0,2,249,0,4,249,
+  0,6,249,0,100,250,0,102,250,0,122,245,41,45,237,47,237,66,240,98,
+  252,102,252,112,250,193,240,194,240,195,240,196,240,197,240,198,
+  240,225,252,226,252,227,252,228,252,229,252,230,252,233,252,234,
+  252,235,252,236,252,243,250,244,250,245,250,246,250,247,250,249,
+  250,0,2,240,0,3,252,0,4,240,0,5,252,0,6,240,0,7,252,0,21,252,0,25,
+  252,0,27,252,0,29,252,0,79,250,0,83,250,12,45,3,47,3,86,255,218,
+  255,219,255,220,255,221,255,0,108,255,0,112,255,0,114,255,0,116,
+  255,27,80,254,85,254,86,254,87,249,88,250,90,249,211,254,212,254,
+  213,254,214,254,215,254,217,254,218,254,219,254,220,254,221,254,
+  222,249,0,78,254,0,82,254,0,100,254,0,102,254,0,108,254,0,112,254,
+  0,114,254,0,116,254,0,122,249,69,45,244,46,237,47,244,59,250,60,
+  250,66,242,80,250,98,244,102,247,112,244,115,244,118,242,120,247,
+  122,247,193,242,194,242,195,242,196,242,197,242,198,242,211,250,
+  212,250,213,250,214,250,215,250,217,250,225,244,226,244,227,244,
+  228,244,229,244,230,244,233,247,234,247,235,247,236,247,243,244,
+  244,244,245,244,246,244,247,244,249,244,250,242,251,242,252,242,
+  253,242,254,247,0,1,247,0,2,242,0,3,244,0,4,242,0,5,244,0,6,242,
+  0,7,244,0,21,247,0,25,247,0,27,247,0,29,247,0,78,250,0,79,244,0,
+  82,250,0,83,244,0,87,244,0,89,244,0,109,242,0,113,242,0,115,242,
+  0,117,242,13,45,252,47,252,66,249,193,249,194,249,195,249,196,249,
+  197,249,198,249,0,2,249,0,4,249,0,6,249,65,45,237,46,244,47,237,
+  59,250,60,250,66,244,72,249,80,249,98,247,102,249,112,242,118,247,
+  193,244,194,244,195,244,196,244,197,244,198,244,211,249,212,249,
+  213,249,214,249,215,249,217,249,225,247,226,247,227,247,228,247,
+  229,247,230,247,233,249,234,249,235,249,236,249,243,242,244,242,
+  245,242,246,242,247,242,249,242,250,247,251,247,252,247,253,247,
+  0,2,244,0,3,247,0,4,244,0,5,247,0,6,244,0,7,247,0,21,249,0,25,249,
+  0,27,249,0,29,249,0,32,249,0,36,249,0,78,249,0,79,242,0,82,249,0,
+  83,242,0,109,247,0,113,247,0,115,247,0,117,247,65,45,244,46,250,
+  47,244,59,255,60,255,66,247,80,254,98,250,102,251,112,247,118,249,
+  122,254,193,247,194,247,195,247,196,247,197,247,198,247,211,254,
+  212,254,213,254,214,254,215,254,217,254,225,250,226,250,227,250,
+  228,250,229,250,230,250,233,251,234,251,235,251,236,251,243,247,
+  244,247,245,247,246,247,247,247,249,247,250,249,251,249,252,249,
+  253,249,254,254,0,1,254,0,2,247,0,3,250,0,4,247,0,5,250,0,6,247,
+  0,7,250,0,21,251,0,25,251,0,27,251,0,29,251,0,78,254,0,79,247,0,
+  82,254,0,83,247,0,109,249,0,113,249,0,115,249,0,117,249,61,45,240,
+  47,240,59,249,60,249,66,239,80,245,98,242,102,244,112,240,118,240,
+  193,239,194,239,195,239,196,239,197,239,198,239,211,245,212,245,
+  213,245,214,245,215,245,217,245,225,242,226,242,227,242,228,242,
+  229,242,230,242,233,244,234,244,235,244,236,244,243,240,244,240,
+  245,240,246,240,247,240,249,240,250,240,251,240,252,240,253,240,
+  0,2,239,0,3,242,0,4,239,0,5,242,0,6,239,0,7,242,0,21,244,0,25,244,
+  0,27,244,0,29,244,0,78,245,0,79,240,0,82,245,0,83,240,0,109,240,
+  0,113,240,0,115,240,0,117,240,9,104,255,119,254,120,254,122,254,
+  254,254,0,1,254,0,33,255,0,37,255,18,109,255,118,254,119,254,122,
+  254,250,254,251,254,252,254,253,254,254,254,0,1,254,0,60,255,0,62,
+  255,0,68,255,0,109,254,0,113,254,0,115,254,0,117,254,11,105,255,
+  108,254,109,254,122,255,254,255,0,1,255,0,57,254,0,60,254,0,62,254,
+  0,68,254,8,101,255,119,254,120,254,122,254,254,254,0,1,254,0,19,
+  255,9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,254,
+  23,45,255,47,255,102,255,112,254,233,255,234,255,235,255,236,255,
+  243,254,244,254,245,254,246,254,247,254,249,254,0,21,255,0,25,255,
+  0,27,255,0,29,255,0,79,254,0,83,254,1,32,25,5,1,32,29,5,13,102,2,
+  104,255,233,2,234,2,235,2,236,2,0,21,2,0,25,2,0,27,2,0,29,2,0,33,
+  255,0,37,255,4,122,254,254,254,0,1,254,10,112,254,243,254,244,254,
+  245,254,246,254,247,254,249,254,0,79,254,0,83,254,5,120,254,122,
+  254,254,254,0,1,254,13,118,254,122,252,250,254,251,254,252,254,253,
+  254,254,252,0,1,252,0,109,254,0,113,254,0,115,254,0,117,254,14,118,
+  255,119,250,122,254,250,255,251,255,252,255,253,255,254,254,0,1,
+  254,0,109,255,0,113,255,0,115,255,0,117,255,7,119,254,120,254,121,
+  252,122,254,254,254,0,1,254,4,122,254,254,254,0,1,254,34,45,247,
+  46,254,47,247,100,254,101,254,104,254,112,254,114,254,116,254,117,
+  3,119,2,122,2,232,254,243,254,244,254,245,254,246,254,247,254,249,
+  254,254,2,0,1,2,0,9,254,0,15,254,0,19,254,0,33,254,0,37,254,0,79,
+  254,0,83,254,0,93,254,0,97,254,0,99,254,0,101,3,1,2,25,254,2,120,
+  254,22,45,244,47,244,98,254,112,252,225,254,226,254,227,254,228,
+  254,229,254,230,254,243,252,244,252,245,252,246,252,247,252,249,
+  252,0,3,254,0,5,254,0,7,254,0,79,252,0,83,252,12,45,250,47,250,112,
+  254,243,254,244,254,245,254,246,254,247,254,249,254,0,79,254,0,83,
+  254,10,102,255,233,255,234,255,235,255,236,255,0,21,255,0,25,255,
+  0,27,255,0,29,255,31,45,244,47,244,98,252,102,255,112,253,225,252,
+  226,252,227,252,228,252,229,252,230,252,233,255,234,255,235,255,
+  236,255,243,253,244,253,245,253,246,253,247,253,249,253,0,3,252,
+  0,5,252,0,7,252,0,21,255,0,25,255,0,27,255,0,29,255,0,79,253,0,83,
+  253,10,102,2,233,2,234,2,235,2,236,2,0,21,2,0,25,2,0,27,2,0,29,2,
+  49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,88,247,90,239,
+  118,252,119,250,120,252,122,252,200,250,211,250,212,250,213,250,
+  214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,239,
+  250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,
+  0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,
+  0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,
+  252,0,122,239,49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,
+  88,247,90,239,118,252,119,250,120,252,122,252,200,250,211,250,212,
+  250,213,250,214,250,215,250,217,250,218,249,219,249,220,249,221,
+  249,222,239,250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,
+  250,0,14,250,0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,
+  242,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,
+  0,116,249,0,117,252,0,122,239,49,68,250,72,249,80,250,82,250,85,
+  242,86,249,87,244,88,247,90,239,118,252,119,250,120,252,122,252,
+  200,250,211,250,212,250,213,250,214,250,215,250,217,250,218,249,
+  219,249,220,249,221,249,222,239,250,252,251,252,252,252,253,252,
+  254,252,0,1,252,0,8,250,0,14,250,0,32,249,0,36,249,0,78,250,0,82,
+  250,0,100,242,0,102,242,0,108,249,0,109,252,0,112,249,0,113,252,
+  0,114,249,0,115,252,0,116,249,0,117,252,0,122,239,49,68,250,72,249,
+  80,250,82,250,85,242,86,249,87,244,88,247,90,239,118,252,119,250,
+  120,252,122,252,200,250,211,250,212,250,213,250,214,250,215,250,
+  217,250,218,249,219,249,220,249,221,249,222,239,250,252,251,252,
+  252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,0,32,249,0,36,249,
+  0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,0,109,252,0,112,
+  249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,239,
+  49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,88,247,90,239,
+  118,252,119,250,120,252,122,252,200,250,211,250,212,250,213,250,
+  214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,239,
+  250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,
+  0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,
+  0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,
+  252,0,122,239,49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,
+  88,247,90,239,118,252,119,250,120,252,122,252,200,250,211,250,212,
+  250,213,250,214,250,215,250,217,250,218,249,219,249,220,249,221,
+  249,222,239,250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,
+  250,0,14,250,0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,
+  242,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,
+  0,116,249,0,117,252,0,122,239,22,45,250,47,250,66,249,85,250,87,
+  249,88,249,89,249,90,245,193,249,194,249,195,249,196,249,197,249,
+  198,249,222,245,0,2,249,0,4,249,0,6,249,0,100,250,0,102,250,0,122,
+  245,22,45,250,47,250,66,249,85,250,87,249,88,249,89,249,90,245,193,
+  249,194,249,195,249,196,249,197,249,198,249,222,245,0,2,249,0,4,
+  249,0,6,249,0,100,250,0,102,250,0,122,245,22,45,250,47,250,66,249,
+  85,250,87,249,88,249,89,249,90,245,193,249,194,249,195,249,196,249,
+  197,249,198,249,222,245,0,2,249,0,4,249,0,6,249,0,100,250,0,102,
+  250,0,122,245,22,45,250,47,250,66,249,85,250,87,249,88,249,89,249,
+  90,245,193,249,194,249,195,249,196,249,197,249,198,249,222,245,0,
+  2,249,0,4,249,0,6,249,0,100,250,0,102,250,0,122,245,22,45,250,47,
+  250,66,249,85,250,87,249,88,249,89,249,90,245,193,249,194,249,195,
+  249,196,249,197,249,198,249,222,245,0,2,249,0,4,249,0,6,249,0,100,
+  250,0,102,250,0,122,245,22,45,250,47,250,66,249,85,250,87,249,88,
+  249,89,249,90,245,193,249,194,249,195,249,196,249,197,249,198,249,
+  222,245,0,2,249,0,4,249,0,6,249,0,100,250,0,102,250,0,122,245,13,
+  45,252,47,252,66,249,193,249,194,249,195,249,196,249,197,249,198,
+  249,0,2,249,0,4,249,0,6,249,13,45,252,47,252,66,249,193,249,194,
+  249,195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,13,45,
+  252,47,252,66,249,193,249,194,249,195,249,196,249,197,249,198,249,
+  0,2,249,0,4,249,0,6,249,13,45,252,47,252,66,249,193,249,194,249,
+  195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,61,45,240,
+  47,240,59,249,60,249,66,239,80,245,98,242,102,244,112,240,118,240,
+  193,239,194,239,195,239,196,239,197,239,198,239,211,245,212,245,
+  213,245,214,245,215,245,217,245,225,242,226,242,227,242,228,242,
+  229,242,230,242,233,244,234,244,235,244,236,244,243,240,244,240,
+  245,240,246,240,247,240,249,240,250,240,251,240,252,240,253,240,
+  0,2,239,0,3,242,0,4,239,0,5,242,0,6,239,0,7,242,0,21,244,0,25,244,
+  0,27,244,0,29,244,0,78,245,0,79,240,0,82,245,0,83,240,0,109,240,
+  0,113,240,0,115,240,0,117,240,9,104,255,119,254,120,254,122,254,
+  254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,254,120,254,122,
+  254,254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,254,120,254,
+  122,254,254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,254,120,
+  254,122,254,254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,254,
+  120,254,122,254,254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,
+  254,120,254,122,254,254,254,0,1,254,0,33,255,0,37,255,11,105,255,
+  108,254,109,254,122,255,254,255,0,1,255,0,57,254,0,60,254,0,62,254,
+  0,68,254,9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,
+  254,9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,254,
+  9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,254,9,45,
+  2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,254,14,118,255,
+  119,250,122,254,250,255,251,255,252,255,253,255,254,254,0,1,254,
+  0,109,255,0,113,255,0,115,255,0,117,255,7,119,254,120,254,121,252,
+  122,254,254,254,0,1,254,7,119,254,120,254,121,252,122,254,254,254,
+  0,1,254,7,119,254,120,254,121,252,122,254,254,254,0,1,254,7,119,
+  254,120,254,121,252,122,254,254,254,0,1,254,7,119,254,120,254,121,
+  252,122,254,254,254,0,1,254,7,119,254,120,254,121,252,122,254,254,
+  254,0,1,254,31,45,244,47,244,98,252,102,255,112,253,225,252,226,
+  252,227,252,228,252,229,252,230,252,233,255,234,255,235,255,236,
+  255,243,253,244,253,245,253,246,253,247,253,249,253,0,3,252,0,5,
+  252,0,7,252,0,21,255,0,25,255,0,27,255,0,29,255,0,79,253,0,83,253,
+  31,45,244,47,244,98,252,102,255,112,253,225,252,226,252,227,252,
+  228,252,229,252,230,252,233,255,234,255,235,255,236,255,243,253,
+  244,253,245,253,246,253,247,253,249,253,0,3,252,0,5,252,0,7,252,
+  0,21,255,0,25,255,0,27,255,0,29,255,0,79,253,0,83,253,49,68,250,
+  72,249,80,250,82,250,85,242,86,249,87,244,88,247,90,239,118,252,
+  119,250,120,252,122,252,200,250,211,250,212,250,213,250,214,250,
+  215,250,217,250,218,249,219,249,220,249,221,249,222,239,250,252,
+  251,252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,0,32,249,
+  0,36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,0,109,252,
+  0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,
+  239,9,104,255,119,254,120,254,122,254,254,254,0,1,254,0,33,255,0,
+  37,255,49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,88,247,
+  90,239,118,252,119,250,120,252,122,252,200,250,211,250,212,250,213,
+  250,214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,
+  239,250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,
+  250,0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,
+  249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,
+  0,117,252,0,122,239,9,104,255,119,254,120,254,122,254,254,254,0,
+  1,254,0,33,255,0,37,255,49,68,250,72,249,80,250,82,250,85,242,86,
+  249,87,244,88,247,90,239,118,252,119,250,120,252,122,252,200,250,
+  211,250,212,250,213,250,214,250,215,250,217,250,218,249,219,249,
+  220,249,221,249,222,239,250,252,251,252,252,252,253,252,254,252,
+  0,1,252,0,8,250,0,14,250,0,32,249,0,36,249,0,78,250,0,82,250,0,100,
+  242,0,102,242,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,
+  0,115,252,0,116,249,0,117,252,0,122,239,9,104,255,119,254,120,254,
+  122,254,254,254,0,1,254,0,33,255,0,37,255,11,105,255,108,254,109,
+  254,122,255,254,255,0,1,255,0,57,254,0,60,254,0,62,254,0,68,254,
+  11,105,255,108,254,109,254,122,255,254,255,0,1,255,0,57,254,0,60,
+  254,0,62,254,0,68,254,18,45,252,47,252,66,250,87,250,88,250,90,245,
+  193,250,194,250,195,250,196,250,197,250,198,250,222,245,0,2,250,
+  0,4,250,0,6,250,0,122,245,18,45,252,47,252,66,250,87,250,88,250,
+  90,245,193,250,194,250,195,250,196,250,197,250,198,250,222,245,0,
+  2,250,0,4,250,0,6,250,0,122,245,8,101,255,119,254,120,254,122,254,
+  254,254,0,1,254,0,19,255,9,45,2,47,3,119,254,120,254,121,254,122,
+  254,254,254,0,1,254,9,45,2,47,3,119,254,120,254,121,254,122,254,
+  254,254,0,1,254,9,45,2,47,3,119,254,120,254,121,254,122,254,254,
+  254,0,1,254,9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,
+  0,1,254,13,102,2,104,255,233,2,234,2,235,2,236,2,0,21,2,0,25,2,0,
+  27,2,0,29,2,0,33,255,0,37,255,13,102,2,104,255,233,2,234,2,235,2,
+  236,2,0,21,2,0,25,2,0,27,2,0,29,2,0,33,255,0,37,255,40,80,252,102,
+  254,112,251,118,252,122,250,211,252,212,252,213,252,214,252,215,
+  252,217,252,233,254,234,254,235,254,236,254,243,251,244,251,245,
+  251,246,251,247,251,249,251,250,252,251,252,252,252,253,252,254,
+  250,0,1,250,0,21,254,0,25,254,0,27,254,0,29,254,0,78,252,0,79,251,
+  0,82,252,0,83,251,0,109,252,0,113,252,0,115,252,0,117,252,10,112,
+  254,243,254,244,254,245,254,246,254,247,254,249,254,0,79,254,0,83,
+  254,14,85,242,87,239,88,244,90,237,122,252,222,237,254,252,0,1,252,
+  0,100,242,0,102,242,0,122,237,1,32,25,234,1,32,29,234,5,120,254,
+  122,254,254,254,0,1,254,14,85,242,87,239,88,244,90,237,122,252,222,
+  237,254,252,0,1,252,0,100,242,0,102,242,0,122,237,1,32,25,234,1,
+  32,29,234,5,120,254,122,254,254,254,0,1,254,14,85,242,87,239,88,
+  244,90,237,122,252,222,237,254,252,0,1,252,0,100,242,0,102,242,0,
+  122,237,1,32,25,234,1,32,29,234,5,120,254,122,254,254,254,0,1,254,
+  14,118,255,119,250,122,254,250,255,251,255,252,255,253,255,254,254,
+  0,1,254,0,109,255,0,113,255,0,115,255,0,117,255,14,118,255,119,250,
+  122,254,250,255,251,255,252,255,253,255,254,254,0,1,254,0,109,255,
+  0,113,255,0,115,255,0,117,255,14,118,255,119,250,122,254,250,255,
+  251,255,252,255,253,255,254,254,0,1,254,0,109,255,0,113,255,0,115,
+  255,0,117,255,22,45,250,47,250,66,249,85,250,87,249,88,249,89,249,
+  90,245,193,249,194,249,195,249,196,249,197,249,198,249,222,245,0,
+  2,249,0,4,249,0,6,249,0,100,250,0,102,250,0,122,245,7,119,254,120,
+  254,121,252,122,254,254,254,0,1,254,22,45,250,47,250,66,249,85,250,
+  87,249,88,249,89,249,90,245,193,249,194,249,195,249,196,249,197,
+  249,198,249,222,245,0,2,249,0,4,249,0,6,249,0,100,250,0,102,250,
+  0,122,245,7,119,254,120,254,121,252,122,254,254,254,0,1,254,27,80,
+  254,85,254,86,254,87,249,88,250,90,249,211,254,212,254,213,254,214,
+  254,215,254,217,254,218,254,219,254,220,254,221,254,222,249,0,78,
+  254,0,82,254,0,100,254,0,102,254,0,108,254,0,112,254,0,114,254,0,
+  116,254,0,122,249,34,45,247,46,254,47,247,100,254,101,254,104,254,
+  112,254,114,254,116,254,117,3,119,2,122,2,232,254,243,254,244,254,
+  245,254,246,254,247,254,249,254,254,2,0,1,2,0,9,254,0,15,254,0,19,
+  254,0,33,254,0,37,254,0,79,254,0,83,254,0,93,254,0,97,254,0,99,254,
+  0,101,3,1,2,25,254,27,80,254,85,254,86,254,87,249,88,250,90,249,
+  211,254,212,254,213,254,214,254,215,254,217,254,218,254,219,254,
+  220,254,221,254,222,249,0,78,254,0,82,254,0,100,254,0,102,254,0,
+  108,254,0,112,254,0,114,254,0,116,254,0,122,249,34,45,247,46,254,
+  47,247,100,254,101,254,104,254,112,254,114,254,116,254,117,3,119,
+  2,122,2,232,254,243,254,244,254,245,254,246,254,247,254,249,254,
+  254,2,0,1,2,0,9,254,0,15,254,0,19,254,0,33,254,0,37,254,0,79,254,
+  0,83,254,0,93,254,0,97,254,0,99,254,0,101,3,1,2,25,254,27,80,254,
+  85,254,86,254,87,249,88,250,90,249,211,254,212,254,213,254,214,254,
+  215,254,217,254,218,254,219,254,220,254,221,254,222,249,0,78,254,
+  0,82,254,0,100,254,0,102,254,0,108,254,0,112,254,0,114,254,0,116,
+  254,0,122,249,34,45,247,46,254,47,247,100,254,101,254,104,254,112,
+  254,114,254,116,254,117,3,119,2,122,2,232,254,243,254,244,254,245,
+  254,246,254,247,254,249,254,254,2,0,1,2,0,9,254,0,15,254,0,19,254,
+  0,33,254,0,37,254,0,79,254,0,83,254,0,93,254,0,97,254,0,99,254,0,
+  101,3,1,2,25,254,2,120,254,2,120,254,2,120,254,69,45,244,46,237,
+  47,244,59,250,60,250,66,242,80,250,98,244,102,247,112,244,115,244,
+  118,242,120,247,122,247,193,242,194,242,195,242,196,242,197,242,
+  198,242,211,250,212,250,213,250,214,250,215,250,217,250,225,244,
+  226,244,227,244,228,244,229,244,230,244,233,247,234,247,235,247,
+  236,247,243,244,244,244,245,244,246,244,247,244,249,244,250,242,
+  251,242,252,242,253,242,254,247,0,1,247,0,2,242,0,3,244,0,4,242,
+  0,5,244,0,6,242,0,7,244,0,21,247,0,25,247,0,27,247,0,29,247,0,78,
+  250,0,79,244,0,82,250,0,83,244,0,87,244,0,89,244,0,109,242,0,113,
+  242,0,115,242,0,117,242,69,45,244,46,237,47,244,59,250,60,250,66,
+  242,80,250,98,244,102,247,112,244,115,244,118,242,120,247,122,247,
+  193,242,194,242,195,242,196,242,197,242,198,242,211,250,212,250,
+  213,250,214,250,215,250,217,250,225,244,226,244,227,244,228,244,
+  229,244,230,244,233,247,234,247,235,247,236,247,243,244,244,244,
+  245,244,246,244,247,244,249,244,250,242,251,242,252,242,253,242,
+  254,247,0,1,247,0,2,242,0,3,244,0,4,242,0,5,244,0,6,242,0,7,244,
+  0,21,247,0,25,247,0,27,247,0,29,247,0,78,250,0,79,244,0,82,250,0,
+  83,244,0,87,244,0,89,244,0,109,242,0,113,242,0,115,242,0,117,242,
+  13,45,252,47,252,66,249,193,249,194,249,195,249,196,249,197,249,
+  198,249,0,2,249,0,4,249,0,6,249,13,45,252,47,252,66,249,193,249,
+  194,249,195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,
+  13,45,252,47,252,66,249,193,249,194,249,195,249,196,249,197,249,
+  198,249,0,2,249,0,4,249,0,6,249,13,45,252,47,252,66,249,193,249,
+  194,249,195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,
+  61,45,240,47,240,59,249,60,249,66,239,80,245,98,242,102,244,112,
+  240,118,240,193,239,194,239,195,239,196,239,197,239,198,239,211,
+  245,212,245,213,245,214,245,215,245,217,245,225,242,226,242,227,
+  242,228,242,229,242,230,242,233,244,234,244,235,244,236,244,243,
+  240,244,240,245,240,246,240,247,240,249,240,250,240,251,240,252,
+  240,253,240,0,2,239,0,3,242,0,4,239,0,5,242,0,6,239,0,7,242,0,21,
+  244,0,25,244,0,27,244,0,29,244,0,78,245,0,79,240,0,82,245,0,83,240,
+  0,109,240,0,113,240,0,115,240,0,117,240,10,102,2,233,2,234,2,235,
+  2,236,2,0,21,2,0,25,2,0,27,2,0,29,2,10,102,2,233,2,234,2,235,2,236,
+  2,0,21,2,0,25,2,0,27,2,0,29,2,10,102,2,233,2,234,2,235,2,236,2,0,
+  21,2,0,25,2,0,27,2,0,29,2,2,120,254,2,1,32,24,249,19,33,244,101,
+  244,109,254,115,250,116,247,119,254,0,19,244,0,60,254,0,62,254,0,
+  68,254,0,87,250,0,89,250,0,91,250,0,93,247,0,97,247,0,99,247,1,2,
+  25,247,1,32,25,249,2,33,244
+};
+static afm_cuint16 afm_Helvetica_BoldOblique_highchars_index[] = { /* 220 */
+  161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+  178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+  194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+  210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+  226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+  242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+  258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+  280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+  313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+  336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+  354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+  380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+  8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+  8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+  64257,64258
+};
+static afm_cunicode afm_Helvetica_BoldOblique_ligatures[] = { /* 3 */
+  102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Helvetica-Oblique */
+/* FullName: Helvetica Oblique */
+/* FamilyName: Helvetica */
+static afm_cuint8 afm_Helvetica_Oblique_widths[] = { /* 315 */
+  46,46,59,93,93,148,111,32,56,56,65,97,46,56,46,46,93,93,93,93,93,
+  93,93,93,93,93,46,46,97,97,97,93,169,111,111,120,120,111,102,130,
+  120,46,83,111,93,139,120,130,111,130,120,111,102,120,111,157,111,
+  111,102,46,46,46,78,93,56,93,93,83,93,93,46,93,93,37,37,83,37,139,
+  93,93,93,93,56,83,46,93,83,120,83,83,83,56,43,56,97,56,93,93,93,
+  93,43,93,56,123,62,93,97,123,56,67,97,56,56,56,93,90,46,56,56,61,
+  93,139,139,139,102,111,111,111,111,111,111,167,120,111,111,111,111,
+  46,46,46,46,120,120,130,130,130,130,130,97,130,120,120,120,120,111,
+  111,102,93,93,93,93,93,93,148,83,93,93,93,93,46,46,46,46,93,93,93,
+  93,93,93,93,97,102,93,93,93,93,83,93,83,111,93,111,93,111,93,120,
+  83,120,83,120,107,120,93,111,93,111,93,111,93,111,93,130,93,130,
+  93,46,46,46,37,46,46,111,83,93,37,93,37,93,50,93,37,120,93,120,93,
+  120,93,130,93,130,93,167,157,120,56,120,56,120,56,111,83,111,83,
+  111,83,102,46,102,53,120,93,120,93,120,93,120,93,111,102,83,102,
+  83,102,83,93,111,83,56,56,56,56,56,56,56,56,93,167,37,37,37,56,56,
+  56,93,93,58,167,167,56,56,28,93,167,79,102,100,97,76,92,92,92,79,
+  42,83,83
+};
+static afm_sint16 afm_Helvetica_Oblique_kerning_index[] = { /* 315 */
+  1,0,0,0,0,0,0,0,0,0,0,0,29,0,38,0,0,0,0,0,0,0,0,0,0,0,49,52,0,0,
+  0,0,0,55,170,197,202,0,241,0,0,0,345,418,510,0,0,545,594,687,710,
+  772,777,938,966,1115,0,1259,0,0,0,0,0,0,0,1407,1419,1468,0,1476,
+  1494,1575,1587,0,0,1595,0,1638,1668,1700,1718,0,1730,1847,0,0,1854,
+  1924,1994,2017,2087,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,0,0,0,0,0,0,0,0,0,0,2130,2245,2360,2475,2590,2705,0,2820,0,0,
+  0,0,0,0,0,0,0,0,2825,2874,2923,2972,3021,0,3070,3119,3147,3175,3203,
+  3231,0,0,3379,3391,3403,3415,3427,3439,0,3451,3459,3477,3495,3513,
+  0,0,0,0,0,3531,3563,3581,3599,3617,3635,0,3653,0,0,0,0,3882,0,3952,
+  4022,4137,4149,4264,4276,4391,4403,4408,4416,4421,4429,0,4468,0,
+  0,4507,0,4525,0,4543,0,4561,0,4579,0,4591,0,0,0,0,0,0,4603,4695,
+  4738,0,4773,0,4808,0,4843,0,0,4878,0,4910,0,4942,4974,5023,5041,
+  5090,0,0,5108,5170,5287,5349,5466,5528,5645,5650,5657,5662,5669,
+  5674,5681,0,5842,0,6003,0,6031,0,6059,0,6087,0,6115,0,6263,0,6306,
+  0,6349,0,6392,6397,0,0,0,0,0,0,0,0,0,0,6404,6409,0,0,6447,0,0,0,
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+static afm_cuint8 afm_Helvetica_Oblique_kerning_data[] = { /* 6450 */
+  42,11,85,249,87,249,88,250,90,242,222,242,0,100,249,0,102,249,0,
+  122,242,1,32,24,247,1,32,28,252,3,1,32,25,240,1,32,29,240,4,33,247,
+  1,32,25,240,1,32,29,240,2,33,249,2,33,249,49,68,252,72,252,80,252,
+  82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,250,120,250,
+  122,250,200,252,211,252,212,252,213,252,214,252,215,252,217,252,
+  218,249,219,249,220,249,221,249,222,240,250,252,251,252,252,252,
+  253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,0,78,
+  252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,249,0,
+  113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,12,45,
+  254,47,254,86,255,218,255,219,255,220,255,221,255,0,108,255,0,112,
+  255,0,114,255,0,116,255,3,45,252,47,252,18,45,245,47,245,66,250,
+  87,245,88,250,90,242,193,250,194,250,195,250,196,250,197,250,198,
+  250,222,242,0,2,250,0,4,250,0,6,250,0,122,242,45,45,232,47,232,66,
+  244,98,249,102,252,112,252,115,249,193,244,194,244,195,244,196,244,
+  197,244,198,244,225,249,226,249,227,249,228,249,229,249,230,249,
+  233,252,234,252,235,252,236,252,243,252,244,252,245,252,246,252,
+  247,252,249,252,0,2,244,0,3,249,0,4,244,0,5,249,0,6,244,0,7,249,
+  0,21,252,0,25,252,0,27,252,0,29,252,0,79,252,0,83,252,0,87,249,0,
+  89,249,0,91,249,32,45,252,47,252,66,254,98,254,118,254,193,254,194,
+  254,195,254,196,254,197,254,198,254,225,254,226,254,227,254,228,
+  254,229,254,230,254,250,254,251,254,252,254,253,254,0,2,254,0,3,
+  254,0,4,254,0,5,254,0,6,254,0,7,254,0,109,254,0,113,254,0,115,254,
+  0,117,254,40,80,249,102,250,112,250,118,252,122,249,211,249,212,
+  249,213,249,214,249,215,249,217,249,233,250,234,250,235,250,236,
+  250,243,250,244,250,245,250,246,250,247,250,249,250,250,252,251,
+  252,252,252,253,252,254,249,0,1,249,0,21,250,0,25,250,0,27,250,0,
+  29,250,0,78,249,0,79,250,0,82,249,0,83,250,0,109,252,0,113,252,0,
+  115,252,0,117,252,14,85,239,87,239,88,245,90,234,122,252,222,234,
+  254,252,0,1,252,0,100,239,0,102,239,0,122,234,1,32,25,230,1,32,29,
+  234,22,45,250,47,250,66,254,85,250,87,249,88,252,89,247,90,245,193,
+  254,194,254,195,254,196,254,197,254,198,254,222,245,0,2,254,0,4,
+  254,0,6,254,0,100,250,0,102,250,0,122,245,41,45,227,47,227,66,237,
+  98,250,102,249,112,249,193,237,194,237,195,237,196,237,197,237,198,
+  237,225,250,226,250,227,250,228,250,229,250,230,250,233,249,234,
+  249,235,249,236,249,243,249,244,249,245,249,246,249,247,249,249,
+  249,0,2,237,0,3,250,0,4,237,0,5,250,0,6,237,0,7,250,0,21,249,0,25,
+  249,0,27,249,0,29,249,0,79,249,0,83,249,10,86,255,218,255,219,255,
+  220,255,221,255,0,108,255,0,112,255,0,114,255,0,116,255,27,80,254,
+  85,252,86,250,87,249,88,252,90,249,211,254,212,254,213,254,214,254,
+  215,254,217,254,218,250,219,250,220,250,221,250,222,249,0,78,254,
+  0,82,254,0,100,252,0,102,252,0,108,250,0,112,250,0,114,250,0,116,
+  250,0,122,249,3,45,254,47,254,70,45,237,46,234,47,237,59,254,60,
+  254,66,237,80,250,98,237,102,237,112,237,115,237,118,237,120,237,
+  122,237,193,237,194,237,195,237,196,237,197,237,198,237,211,250,
+  212,250,213,250,214,250,215,250,217,250,225,237,226,237,227,237,
+  228,247,229,237,230,237,233,247,234,237,235,237,236,237,243,237,
+  244,237,245,237,246,247,247,237,249,237,250,237,251,237,252,237,
+  253,237,254,237,0,1,247,0,2,237,0,3,247,0,4,237,0,5,247,0,6,237,
+  0,7,237,0,21,247,0,25,237,0,27,237,0,29,237,0,78,250,0,79,247,0,
+  82,250,0,83,237,0,87,237,0,89,237,0,91,237,0,109,247,0,113,237,0,
+  115,237,0,117,237,13,45,250,47,250,66,250,193,250,194,250,195,250,
+  196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,65,45,236,46,244,
+  47,236,59,250,60,250,66,244,72,250,80,250,98,245,102,244,112,244,
+  118,245,193,244,194,244,195,244,196,244,197,244,198,244,211,250,
+  212,250,213,250,214,250,215,250,217,250,225,245,226,245,227,245,
+  228,245,229,245,230,245,233,244,234,244,235,244,236,244,243,244,
+  244,244,245,244,246,244,247,244,249,244,250,245,251,245,252,245,
+  253,245,0,2,244,0,3,245,0,4,244,0,5,245,0,6,244,0,7,245,0,21,244,
+  0,25,244,0,27,244,0,29,244,0,32,250,0,36,250,0,78,250,0,79,244,0,
+  82,250,0,83,244,0,109,245,0,113,245,0,115,245,0,117,245,63,45,244,
+  46,250,47,244,66,249,80,254,98,250,102,252,112,252,118,252,122,254,
+  193,249,194,249,195,249,196,249,197,249,198,249,211,254,212,254,
+  213,254,214,254,215,254,217,254,225,250,226,250,227,250,228,250,
+  229,250,230,250,233,252,234,252,235,252,236,252,243,252,244,252,
+  245,252,246,252,247,252,249,252,250,252,251,252,252,252,253,252,
+  254,254,0,1,254,0,2,249,0,3,250,0,4,249,0,5,250,0,6,249,0,7,250,
+  0,21,252,0,25,252,0,27,252,0,29,252,0,78,254,0,79,252,0,82,254,0,
+  83,252,0,109,252,0,113,252,0,115,252,0,117,252,65,45,234,46,234,
+  47,234,59,247,60,247,66,239,80,243,98,234,102,234,106,254,112,234,
+  118,239,193,239,194,239,195,239,196,239,197,239,198,239,211,243,
+  212,243,213,243,214,243,215,243,217,243,225,234,226,234,227,234,
+  228,234,229,234,230,234,233,234,234,234,235,234,236,234,238,254,
+  243,234,244,234,245,234,246,234,247,234,249,234,250,239,251,239,
+  252,239,253,239,0,2,239,0,3,245,0,4,239,0,5,245,0,6,239,0,7,234,
+  0,21,245,0,25,234,0,27,234,0,29,234,0,49,254,0,78,243,0,79,234,0,
+  82,243,0,83,234,0,109,239,0,113,239,0,115,239,0,117,239,6,119,254,
+  120,254,122,252,254,252,0,1,252,21,45,250,47,250,99,255,109,254,
+  118,254,119,254,122,254,250,254,251,254,252,254,253,254,254,254,
+  0,1,254,0,60,254,0,62,254,0,68,254,0,109,254,0,113,254,0,115,254,
+  0,117,254,4,45,254,108,254,0,57,254,9,45,254,47,254,119,252,120,
+  254,121,252,122,254,254,254,0,1,254,34,45,252,47,252,98,252,102,
+  252,112,252,225,252,226,252,227,252,228,252,229,252,230,252,233,
+  252,234,252,235,252,236,252,243,252,244,252,245,252,246,252,247,
+  252,249,252,0,3,252,0,5,252,0,7,252,0,21,252,0,25,252,0,27,252,0,
+  29,252,0,51,252,0,79,252,0,83,252,1,32,25,8,1,32,29,10,5,115,255,
+  0,87,255,0,89,255,0,91,255,4,122,252,254,252,0,1,252,19,102,254,
+  112,254,233,254,234,254,235,254,236,254,243,254,244,254,245,254,
+  246,254,247,254,249,254,0,21,254,0,25,254,0,27,254,0,29,254,0,79,
+  254,0,83,254,13,118,255,122,254,250,255,251,255,252,255,253,255,
+  254,254,0,1,254,0,109,255,0,113,255,0,115,255,0,117,255,14,118,255,
+  119,254,122,254,250,255,251,255,252,255,253,255,254,254,0,1,254,
+  0,109,255,0,113,255,0,115,255,0,117,255,9,45,250,47,250,119,254,
+  120,254,121,252,122,252,254,252,0,1,252,6,45,251,47,251,122,252,
+  254,252,0,1,252,50,45,249,47,249,59,5,60,5,98,255,106,3,108,3,109,
+  3,110,4,111,4,113,5,117,7,118,3,119,5,122,5,225,255,226,255,227,
+  255,228,255,229,255,230,255,237,3,238,3,239,3,240,3,242,4,250,3,
+  251,3,252,3,253,3,254,5,0,1,5,0,3,255,0,5,255,0,7,255,0,45,3,0,49,
+  3,0,57,3,0,60,3,0,62,3,0,68,3,0,70,4,0,72,4,0,74,4,0,101,7,0,109,
+  3,0,113,3,0,115,3,0,117,3,4,45,254,47,254,120,252,31,45,244,47,244,
+  98,253,102,253,112,253,225,253,226,253,227,253,228,253,229,253,230,
+  253,233,253,234,253,235,253,236,253,243,253,244,253,245,253,246,
+  253,247,253,249,253,0,3,253,0,5,253,0,7,253,0,21,253,0,25,253,0,
+  27,253,0,29,253,0,79,253,0,83,253,31,45,247,47,247,98,254,102,255,
+  112,255,225,254,226,254,227,254,228,254,229,254,230,254,233,255,
+  234,255,235,255,236,255,243,255,244,255,245,255,246,255,247,255,
+  249,255,0,3,254,0,5,254,0,7,254,0,21,255,0,25,255,0,27,255,0,29,
+  255,0,79,255,0,83,255,10,102,252,233,252,234,252,235,252,236,252,
+  0,21,252,0,25,252,0,27,252,0,29,252,31,45,240,47,240,98,254,102,
+  254,112,254,225,254,226,254,227,254,228,254,229,254,230,254,233,
+  254,234,254,235,254,236,254,243,254,244,254,245,254,246,254,247,
+  254,249,254,0,3,254,0,5,254,0,7,254,0,21,254,0,25,254,0,27,254,0,
+  29,254,0,79,254,0,83,254,19,102,254,112,254,233,254,234,254,235,
+  254,236,254,243,254,244,254,245,254,246,254,247,254,249,254,0,21,
+  254,0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,49,68,252,72,252,
+  80,252,82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,250,
+  120,250,122,250,200,252,211,252,212,252,213,252,214,252,215,252,
+  217,252,218,249,219,249,220,249,221,249,222,240,250,252,251,252,
+  252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,
+  0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,
+  249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,
+  49,68,252,72,252,80,252,82,252,85,237,86,249,87,245,88,249,90,240,
+  118,252,119,250,120,250,122,250,200,252,211,252,212,252,213,252,
+  214,252,215,252,217,252,218,249,219,249,220,249,221,249,222,240,
+  250,252,251,252,252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,
+  0,32,252,0,36,252,0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,
+  0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,
+  252,0,122,240,49,68,252,72,252,80,252,82,252,85,237,86,249,87,245,
+  88,249,90,240,118,252,119,250,120,250,122,250,200,252,211,252,212,
+  252,213,252,214,252,215,252,217,252,218,249,219,249,220,249,221,
+  249,222,240,250,252,251,252,252,252,253,252,254,250,0,1,250,0,8,
+  252,0,14,252,0,32,252,0,36,252,0,78,252,0,82,252,0,100,237,0,102,
+  237,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,
+  0,116,249,0,117,252,0,122,240,49,68,252,72,252,80,252,82,252,85,
+  237,86,249,87,245,88,249,90,240,118,252,119,250,120,250,122,250,
+  200,252,211,252,212,252,213,252,214,252,215,252,217,252,218,249,
+  219,249,220,249,221,249,222,240,250,252,251,252,252,252,253,252,
+  254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,0,78,252,0,82,
+  252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,249,0,113,252,
+  0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,49,68,252,72,252,
+  80,252,82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,250,
+  120,250,122,250,200,252,211,252,212,252,213,252,214,252,215,252,
+  217,252,218,249,219,249,220,249,221,249,222,240,250,252,251,252,
+  252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,
+  0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,
+  249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,
+  49,68,252,72,252,80,252,82,252,85,237,86,249,87,245,88,249,90,240,
+  118,252,119,250,120,250,122,250,200,252,211,252,212,252,213,252,
+  214,252,215,252,217,252,218,249,219,249,220,249,221,249,222,240,
+  250,252,251,252,252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,
+  0,32,252,0,36,252,0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,
+  0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,
+  252,0,122,240,3,45,252,47,252,22,45,250,47,250,66,254,85,250,87,
+  249,88,252,89,247,90,245,193,254,194,254,195,254,196,254,197,254,
+  198,254,222,245,0,2,254,0,4,254,0,6,254,0,100,250,0,102,250,0,122,
+  245,22,45,250,47,250,66,254,85,250,87,249,88,252,89,247,90,245,193,
+  254,194,254,195,254,196,254,197,254,198,254,222,245,0,2,254,0,4,
+  254,0,6,254,0,100,250,0,102,250,0,122,245,22,45,250,47,250,66,254,
+  85,250,87,249,88,252,89,247,90,245,193,254,194,254,195,254,196,254,
+  197,254,198,254,222,245,0,2,254,0,4,254,0,6,254,0,100,250,0,102,
+  250,0,122,245,22,45,250,47,250,66,254,85,250,87,249,88,252,89,247,
+  90,245,193,254,194,254,195,254,196,254,197,254,198,254,222,245,0,
+  2,254,0,4,254,0,6,254,0,100,250,0,102,250,0,122,245,22,45,250,47,
+  250,66,254,85,250,87,249,88,252,89,247,90,245,193,254,194,254,195,
+  254,196,254,197,254,198,254,222,245,0,2,254,0,4,254,0,6,254,0,100,
+  250,0,102,250,0,122,245,22,45,250,47,250,66,254,85,250,87,249,88,
+  252,89,247,90,245,193,254,194,254,195,254,196,254,197,254,198,254,
+  222,245,0,2,254,0,4,254,0,6,254,0,100,250,0,102,250,0,122,245,13,
+  45,250,47,250,66,250,193,250,194,250,195,250,196,250,197,250,198,
+  250,0,2,250,0,4,250,0,6,250,13,45,250,47,250,66,250,193,250,194,
+  250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,13,45,
+  250,47,250,66,250,193,250,194,250,195,250,196,250,197,250,198,250,
+  0,2,250,0,4,250,0,6,250,13,45,250,47,250,66,250,193,250,194,250,
+  195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,65,45,234,
+  46,234,47,234,59,247,60,247,66,239,80,243,98,234,102,234,106,254,
+  112,234,118,239,193,239,194,239,195,239,196,239,197,239,198,239,
+  211,243,212,243,213,243,214,243,215,243,217,243,225,234,226,234,
+  227,234,228,245,229,234,230,234,233,234,234,234,235,234,236,234,
+  238,254,243,234,244,234,245,234,246,234,247,234,249,234,250,239,
+  251,239,252,239,253,239,0,2,239,0,3,245,0,4,239,0,5,245,0,6,239,
+  0,7,234,0,21,245,0,25,234,0,27,234,0,29,234,0,49,254,0,78,243,0,
+  79,245,0,82,243,0,83,234,0,109,239,0,113,239,0,115,239,0,117,239,
+  6,119,254,120,254,122,252,254,252,0,1,252,6,119,254,120,254,122,
+  252,254,252,0,1,252,6,119,254,120,254,122,252,254,252,0,1,252,6,
+  119,254,120,254,122,252,254,252,0,1,252,6,119,254,120,254,122,252,
+  254,252,0,1,252,6,119,254,120,254,122,252,254,252,0,1,252,4,45,254,
+  108,254,0,57,254,9,45,254,47,254,119,252,120,254,121,252,122,254,
+  254,254,0,1,254,9,45,254,47,254,119,252,120,254,121,252,122,254,
+  254,254,0,1,254,9,45,254,47,254,119,252,120,254,121,252,122,254,
+  254,254,0,1,254,9,45,254,47,254,119,252,120,254,121,252,122,254,
+  254,254,0,1,254,14,118,255,119,254,122,254,250,255,251,255,252,255,
+  253,255,254,254,0,1,254,0,109,255,0,113,255,0,115,255,0,117,255,
+  9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+  9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+  9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+  9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+  9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+  95,45,241,47,241,98,248,99,248,100,248,101,248,102,248,103,248,104,
+  248,105,248,106,248,107,248,108,248,109,248,110,248,111,248,112,
+  248,113,248,114,248,115,248,116,248,117,248,118,248,119,245,120,
+  245,121,243,122,245,123,248,225,248,226,248,227,248,228,248,229,
+  248,230,248,232,248,233,248,234,248,235,248,236,248,237,248,238,
+  248,239,248,240,248,242,248,243,248,244,248,245,248,246,248,247,
+  248,249,248,250,248,251,248,252,248,253,248,254,245,0,1,245,0,3,
+  248,0,5,248,0,7,248,0,9,248,0,15,248,0,19,248,0,21,248,0,25,248,
+  0,27,248,0,29,248,0,33,248,0,37,248,0,45,248,0,49,248,0,57,248,0,
+  60,248,0,62,248,0,68,248,0,70,248,0,72,248,0,74,248,0,79,248,0,83,
+  248,0,87,248,0,89,248,0,91,248,0,93,248,0,97,248,0,99,248,0,101,
+  248,0,109,248,0,113,248,0,115,248,0,117,248,0,124,248,0,126,248,
+  0,128,248,1,2,25,248,31,45,240,47,240,98,254,102,254,112,254,225,
+  254,226,254,227,254,228,254,229,254,230,254,233,254,234,254,235,
+  254,236,254,243,254,244,254,245,254,246,254,247,254,249,254,0,3,
+  254,0,5,254,0,7,254,0,21,254,0,25,254,0,27,254,0,29,254,0,79,254,
+  0,83,254,31,45,240,47,240,98,254,102,254,112,254,225,254,226,254,
+  227,254,228,254,229,254,230,254,233,254,234,254,235,254,236,254,
+  243,254,244,254,245,254,246,254,247,254,249,254,0,3,254,0,5,254,
+  0,7,254,0,21,254,0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,49,
+  68,252,72,252,80,252,82,252,85,237,86,249,87,245,88,249,90,240,118,
+  252,119,250,120,250,122,250,200,252,211,252,212,252,213,252,214,
+  252,215,252,217,252,218,249,219,249,220,249,221,249,222,240,250,
+  252,251,252,252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,
+  252,0,36,252,0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,
+  252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,
+  0,122,240,6,119,254,120,254,122,252,254,252,0,1,252,49,68,252,72,
+  252,80,252,82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,
+  250,120,250,122,250,200,252,211,252,212,252,213,252,214,252,215,
+  252,217,252,218,249,219,249,220,249,221,249,222,240,250,252,251,
+  252,252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,
+  36,252,0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,
+  0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,
+  240,6,119,254,120,254,122,252,254,252,0,1,252,49,68,252,72,252,80,
+  252,82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,250,120,
+  250,122,250,200,252,211,252,212,252,213,252,214,252,215,252,217,
+  252,218,249,219,249,220,249,221,249,222,240,250,252,251,252,252,
+  252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,0,
+  78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,249,
+  0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,6,119,
+  254,120,254,122,252,254,252,0,1,252,3,45,252,47,252,4,45,254,108,
+  254,0,57,254,3,45,252,47,252,4,45,254,108,254,0,57,254,18,45,245,
+  47,245,66,250,87,245,88,250,90,242,193,250,194,250,195,250,196,250,
+  197,250,198,250,222,242,0,2,250,0,4,250,0,6,250,0,122,242,18,45,
+  245,47,245,66,250,87,245,88,250,90,242,193,250,194,250,195,250,196,
+  250,197,250,198,250,222,242,0,2,250,0,4,250,0,6,250,0,122,242,9,
+  45,254,47,254,119,252,120,254,121,252,122,254,254,254,0,1,254,9,
+  45,254,47,254,119,252,120,254,121,252,122,254,254,254,0,1,254,9,
+  45,254,47,254,119,252,120,254,121,252,122,254,254,254,0,1,254,9,
+  45,254,47,254,119,252,120,254,121,252,122,254,254,254,0,1,254,5,
+  115,255,0,87,255,0,89,255,0,91,255,5,115,255,0,87,255,0,89,255,0,
+  91,255,40,80,249,102,250,112,250,118,252,122,249,211,249,212,249,
+  213,249,214,249,215,249,217,249,233,250,234,250,235,250,236,250,
+  243,250,244,250,245,250,246,250,247,250,249,250,250,252,251,252,
+  252,252,253,252,254,249,0,1,249,0,21,250,0,25,250,0,27,250,0,29,
+  250,0,78,249,0,79,250,0,82,249,0,83,250,0,109,252,0,113,252,0,115,
+  252,0,117,252,19,102,254,112,254,233,254,234,254,235,254,236,254,
+  243,254,244,254,245,254,246,254,247,254,249,254,0,21,254,0,25,254,
+  0,27,254,0,29,254,0,79,254,0,83,254,14,85,239,87,239,88,245,90,234,
+  122,252,222,234,254,252,0,1,252,0,100,239,0,102,239,0,122,234,1,
+  32,25,230,1,32,29,234,14,85,239,87,239,88,245,90,234,122,252,222,
+  234,254,252,0,1,252,0,100,239,0,102,239,0,122,234,1,32,25,230,1,
+  32,29,234,14,85,239,87,239,88,245,90,234,122,252,222,234,254,252,
+  0,1,252,0,100,239,0,102,239,0,122,234,1,32,25,230,1,32,29,234,14,
+  85,239,87,239,88,245,90,234,122,252,222,234,254,252,0,1,252,0,100,
+  239,0,102,239,0,122,234,1,32,25,230,1,32,29,234,14,118,255,119,254,
+  122,254,250,255,251,255,252,255,253,255,254,254,0,1,254,0,109,255,
+  0,113,255,0,115,255,0,117,255,14,118,255,119,254,122,254,250,255,
+  251,255,252,255,253,255,254,254,0,1,254,0,109,255,0,113,255,0,115,
+  255,0,117,255,14,118,255,119,254,122,254,250,255,251,255,252,255,
+  253,255,254,254,0,1,254,0,109,255,0,113,255,0,115,255,0,117,255,
+  22,45,250,47,250,66,254,85,250,87,249,88,252,89,247,90,245,193,254,
+  194,254,195,254,196,254,197,254,198,254,222,245,0,2,254,0,4,254,
+  0,6,254,0,100,250,0,102,250,0,122,245,9,45,250,47,250,119,254,120,
+  254,121,252,122,252,254,252,0,1,252,22,45,250,47,250,66,254,85,250,
+  87,249,88,252,89,247,90,245,193,254,194,254,195,254,196,254,197,
+  254,198,254,222,245,0,2,254,0,4,254,0,6,254,0,100,250,0,102,250,
+  0,122,245,9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,
+  0,1,252,27,80,254,85,252,86,250,87,249,88,252,90,249,211,254,212,
+  254,213,254,214,254,215,254,217,254,218,250,219,250,220,250,221,
+  250,222,249,0,78,254,0,82,254,0,100,252,0,102,252,0,108,250,0,112,
+  250,0,114,250,0,116,250,0,122,249,50,45,249,47,249,59,5,60,5,98,
+  255,106,3,108,3,109,3,110,4,111,4,113,5,117,7,118,3,119,5,122,5,
+  225,255,226,255,227,255,228,255,229,255,230,255,237,3,238,3,239,
+  3,240,3,242,4,250,3,251,3,252,3,253,3,254,5,0,1,5,0,3,255,0,5,255,
+  0,7,255,0,45,3,0,49,3,0,57,3,0,60,3,0,62,3,0,68,3,0,70,4,0,72,4,
+  0,74,4,0,101,7,0,109,3,0,113,3,0,115,3,0,117,3,27,80,254,85,252,
+  86,250,87,249,88,252,90,249,211,254,212,254,213,254,214,254,215,
+  254,217,254,218,250,219,250,220,250,221,250,222,249,0,78,254,0,82,
+  254,0,100,252,0,102,252,0,108,250,0,112,250,0,114,250,0,116,250,
+  0,122,249,50,45,249,47,249,59,5,60,5,98,255,106,3,108,3,109,3,110,
+  4,111,4,113,5,117,7,118,3,119,5,122,5,225,255,226,255,227,255,228,
+  255,229,255,230,255,237,3,238,3,239,3,240,3,242,4,250,3,251,3,252,
+  3,253,3,254,5,0,1,5,0,3,255,0,5,255,0,7,255,0,45,3,0,49,3,0,57,3,
+  0,60,3,0,62,3,0,68,3,0,70,4,0,72,4,0,74,4,0,101,7,0,109,3,0,113,
+  3,0,115,3,0,117,3,27,80,254,85,252,86,250,87,249,88,252,90,249,211,
+  254,212,254,213,254,214,254,215,254,217,254,218,250,219,250,220,
+  250,221,250,222,249,0,78,254,0,82,254,0,100,252,0,102,252,0,108,
+  250,0,112,250,0,114,250,0,116,250,0,122,249,50,45,249,47,249,59,
+  5,60,5,98,255,106,3,108,3,109,3,110,4,111,4,113,5,117,7,118,3,119,
+  5,122,5,225,255,226,255,227,255,228,255,229,255,230,255,237,3,238,
+  3,239,3,240,3,242,4,250,3,251,3,252,3,253,3,254,5,0,1,5,0,3,255,
+  0,5,255,0,7,255,0,45,3,0,49,3,0,57,3,0,60,3,0,62,3,0,68,3,0,70,4,
+  0,72,4,0,74,4,0,101,7,0,109,3,0,113,3,0,115,3,0,117,3,3,45,254,47,
+  254,4,45,254,47,254,120,252,3,45,254,47,254,4,45,254,47,254,120,
+  252,3,45,254,47,254,4,45,254,47,254,120,252,70,45,237,46,234,47,
+  237,59,254,60,254,66,237,80,250,98,237,102,237,112,237,115,237,118,
+  237,120,237,122,237,193,237,194,237,195,237,196,237,197,237,198,
+  237,211,250,212,250,213,250,214,250,215,250,217,250,225,237,226,
+  237,227,237,228,247,229,237,230,237,233,247,234,237,235,237,236,
+  237,243,237,244,237,245,237,246,247,247,237,249,237,250,237,251,
+  237,252,237,253,237,254,237,0,1,247,0,2,237,0,3,247,0,4,237,0,5,
+  247,0,6,237,0,7,237,0,21,247,0,25,237,0,27,237,0,29,237,0,78,250,
+  0,79,247,0,82,250,0,83,237,0,87,237,0,89,237,0,91,237,0,109,247,
+  0,113,237,0,115,237,0,117,237,70,45,237,46,234,47,237,59,254,60,
+  254,66,237,80,250,98,237,102,237,112,237,115,237,118,237,120,237,
+  122,237,193,237,194,237,195,237,196,237,197,237,198,237,211,250,
+  212,250,213,250,214,250,215,250,217,250,225,237,226,237,227,237,
+  228,247,229,237,230,237,233,247,234,237,235,237,236,237,243,237,
+  244,237,245,237,246,247,247,237,249,237,250,237,251,237,252,237,
+  253,237,254,237,0,1,247,0,2,237,0,3,247,0,4,237,0,5,247,0,6,237,
+  0,7,237,0,21,247,0,25,237,0,27,237,0,29,237,0,78,250,0,79,247,0,
+  82,250,0,83,237,0,87,237,0,89,237,0,91,237,0,109,247,0,113,237,0,
+  115,237,0,117,237,13,45,250,47,250,66,250,193,250,194,250,195,250,
+  196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,13,45,250,47,250,
+  66,250,193,250,194,250,195,250,196,250,197,250,198,250,0,2,250,0,
+  4,250,0,6,250,13,45,250,47,250,66,250,193,250,194,250,195,250,196,
+  250,197,250,198,250,0,2,250,0,4,250,0,6,250,13,45,250,47,250,66,
+  250,193,250,194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,
+  250,0,6,250,65,45,234,46,234,47,234,59,247,60,247,66,239,80,243,
+  98,234,102,234,106,254,112,234,118,239,193,239,194,239,195,239,196,
+  239,197,239,198,239,211,243,212,243,213,243,214,243,215,243,217,
+  243,225,234,226,234,227,234,228,245,229,234,230,234,233,234,234,
+  234,235,234,236,234,238,254,243,234,244,234,245,234,246,234,247,
+  234,249,234,250,239,251,239,252,239,253,239,0,2,239,0,3,245,0,4,
+  239,0,5,245,0,6,239,0,7,234,0,21,245,0,25,234,0,27,234,0,29,234,
+  0,49,254,0,78,243,0,79,234,0,82,243,0,83,234,0,109,239,0,113,239,
+  0,115,239,0,117,239,19,102,254,112,254,233,254,234,254,235,254,236,
+  254,243,254,244,254,245,254,246,254,247,254,249,254,0,21,254,0,25,
+  254,0,27,254,0,29,254,0,79,254,0,83,254,19,102,254,112,254,233,254,
+  234,254,235,254,236,254,243,254,244,254,245,254,246,254,247,254,
+  249,254,0,21,254,0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,19,
+  102,254,112,254,233,254,234,254,235,254,236,254,243,254,244,254,
+  245,254,246,254,247,254,249,254,0,21,254,0,25,254,0,27,254,0,29,
+  254,0,79,254,0,83,254,3,45,254,47,254,4,45,254,47,254,120,252,2,
+  1,32,24,247,14,33,245,101,249,115,249,116,249,0,19,249,0,87,249,
+  0,89,249,0,91,249,0,93,249,0,97,249,0,99,249,1,2,25,249,1,32,25,
+  247,2,33,250
+};
+static afm_cuint16 afm_Helvetica_Oblique_highchars_index[] = { /* 220 */
+  161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+  178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+  194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+  210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+  226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+  242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+  258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+  280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+  313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+  336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+  354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+  380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+  8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+  8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+  64257,64258
+};
+static afm_cunicode afm_Helvetica_Oblique_ligatures[] = { /* 3 */
+  102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: ZapfDingbats */
+/* FullName: ITC Zapf Dingbats */
+/* FamilyName: ZapfDingbats */
+static afm_cuint8 afm_ZapfDingbats_widths[] = { /* 202 */
+  46,162,160,162,163,120,132,132,132,115,160,157,92,143,152,156,152,
+  158,162,126,141,127,127,95,113,127,127,127,126,82,92,90,96,115,131,
+  131,131,132,132,132,136,137,132,140,137,139,136,139,154,124,121,
+  125,132,132,116,129,128,132,127,118,118,114,117,138,136,132,132,
+  118,115,116,115,131,131,119,132,131,132,146,127,127,127,127,127,
+  149,149,131,131,73,23,46,69,65,65,111,111,65,65,53,53,46,46,85,85,
+  68,68,39,39,56,56,122,91,91,152,111,127,127,129,99,116,104,131,131,
+  131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,
+  131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,
+  131,131,131,131,131,131,149,140,169,76,125,154,125,153,155,155,155,
+  139,146,138,154,154,153,155,155,77,147,139,139,145,145,116,116,146,
+  146,127,158,129,144,129,148,161,148,139,146,155,162,153
+};
+static afm_cuint16 afm_ZapfDingbats_highchars_index[] = { /* 107 */
+  128,129,130,131,132,133,134,135,136,137,138,139,140,141,161,162,
+  163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,
+  179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,
+  195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,
+  211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,
+  227,228,229,230,231,232,233,234,235,236,237,238,239,241,242,243,
+  244,245,246,247,248,249,250,251,252,253,254
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Symbol */
+/* FullName: Symbol */
+/* FamilyName: Symbol */
+static afm_cuint8 afm_Symbol_widths[] = { /* 251 */
+  42,56,0,83,0,139,130,0,56,56,0,92,42,0,42,46,83,83,83,83,83,83,83,
+  83,83,83,46,46,92,92,92,74,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,0,0,0,0,0,0,56,0,56,0,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,0,0,0,0,0,0,0,80,33,80,0,119,67,92,96,92,92,83,120,111,101,102,
+  102,120,124,56,120,114,148,120,108,120,128,93,99,102,115,127,120,
+  133,105,92,69,82,73,82,101,87,55,92,92,87,82,92,92,92,73,101,73,
+  96,87,92,114,114,105,103,101,119,77,167,41,69,28,125,114,165,133,
+  128,137,165,101,165,101,174,110,165,101,165,101,174,119,82,92,137,
+  102,119,119,119,73,137,119,92,83,92,119,119,128,101,101,128,128,
+  46,144,92,92,92,92,92,92,92,119,119,119,119,119,128,128,110,42,114,
+  114,55,55,82,126,126,126,126,132,132,148,83,101,167,132,132,131,
+  64,64,64,64,64,64,82,82,82,82,114,64,64,64,64,64,64,82,82,82
+};
+static afm_cuint16 afm_Symbol_highchars_index[] = { /* 156 */
+  172,176,177,181,215,247,402,913,914,915,917,918,919,920,921,922,
+  923,924,925,926,927,928,929,931,932,933,934,935,936,945,946,947,
+  948,949,950,951,952,953,954,955,957,958,959,960,961,962,963,964,
+  965,966,967,968,969,977,978,981,982,8226,8230,8242,8243,8260,8364,
+  8465,8472,8476,8486,8501,8592,8593,8594,8595,8596,8629,8656,8657,
+  8658,8659,8660,8704,8706,8707,8709,8710,8711,8712,8713,8715,8719,
+  8721,8722,8727,8730,8733,8734,8736,8743,8744,8745,8746,8747,8756,
+  8764,8773,8776,8800,8801,8804,8805,8834,8835,8836,8838,8839,8853,
+  8855,8869,8901,8992,8993,9001,9002,9674,9824,9827,9829,9830,63193,
+  63194,63195,63717,63718,63719,63720,63721,63722,63723,63724,63725,
+  63726,63727,63728,63729,63730,63731,63732,63733,63734,63735,63736,
+  63737,63738,63739,63740,63741,63742
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Times-Bold */
+/* FullName: Times Bold */
+/* FamilyName: Times */
+static afm_cuint8 afm_Times_Bold_widths[] = { /* 315 */
+  42,56,93,83,83,167,139,46,56,56,83,95,42,56,42,46,83,83,83,83,83,
+  83,83,83,83,83,56,56,95,95,95,83,155,120,111,120,120,111,102,130,
+  130,65,83,130,111,157,120,130,102,130,120,93,111,120,120,167,120,
+  120,111,56,46,56,97,83,56,83,93,74,93,74,56,83,93,46,56,93,46,139,
+  93,83,93,93,74,65,56,93,83,120,83,83,74,66,37,66,87,56,83,83,83,
+  83,37,83,56,125,50,83,95,125,56,67,95,50,50,56,93,90,42,56,50,55,
+  83,125,125,125,83,120,120,120,120,120,120,167,120,111,111,111,111,
+  65,65,65,65,120,120,130,130,130,130,130,95,130,120,120,120,120,120,
+  102,93,83,83,83,83,83,83,120,74,74,74,74,74,46,46,46,46,83,93,83,
+  83,83,83,83,95,83,93,93,93,93,83,93,83,120,83,120,83,120,83,120,
+  74,120,74,120,112,120,93,111,74,111,74,111,74,111,74,130,83,130,
+  83,65,46,65,46,65,46,130,93,111,46,111,46,111,66,111,46,120,93,120,
+  93,120,93,130,83,130,83,167,120,120,74,120,74,120,74,93,65,93,65,
+  93,65,111,56,111,69,120,93,120,93,120,93,120,93,120,111,74,111,74,
+  111,74,83,93,65,56,56,56,56,56,56,56,56,83,167,56,56,56,83,83,83,
+  83,83,58,167,167,56,56,28,83,167,82,102,100,95,92,92,92,92,82,42,
+  93,93
+};
+static afm_sint16 afm_Times_Bold_kerning_index[] = { /* 315 */
+  1,0,0,0,0,0,0,0,0,0,0,0,44,0,53,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,62,183,0,229,0,266,0,0,0,359,472,564,0,599,623,668,761,786,0,
+  848,1016,1044,1209,0,1364,0,0,0,0,0,0,0,1512,1515,0,1544,1547,1550,
+  1588,1591,1599,0,1602,0,0,1652,1655,0,0,1660,0,0,0,1746,1816,0,1841,
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,0,1888,2009,2130,2251,2372,2493,0,0,0,0,0,0,0,0,0,0,0,2614,2638,
+  2683,2728,2773,2818,0,2863,2908,2936,2964,2992,3020,0,0,3168,3171,
+  3174,3177,3180,3183,0,0,3186,3189,3192,3195,3198,3201,3204,3207,
+  0,3210,3213,3218,3223,3228,3233,0,3238,0,0,0,0,3243,0,3290,3337,
+  3458,3461,3582,3585,3706,0,0,0,0,3709,0,3746,3783,0,3786,0,3789,
+  0,3792,0,3795,0,3798,0,3801,0,3804,0,3807,0,0,3810,3902,3952,0,3987,
+  0,0,0,4022,0,4057,4081,4084,4108,4111,4135,4138,4183,4188,4233,0,
+  0,4238,4300,4386,4448,4534,4596,0,0,0,0,0,0,4682,0,4850,0,5018,0,
+  5046,0,5074,0,5102,0,5130,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  5278,5306,0,5346,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+static afm_cuint8 afm_Times_Bold_kerning_data[] = { /* 5370 */
+  42,19,66,248,85,252,87,249,88,252,90,248,193,248,194,248,195,248,
+  196,248,197,248,198,248,222,248,0,2,248,0,4,248,0,6,248,0,100,252,
+  0,102,252,0,122,248,3,1,32,25,248,1,32,29,249,3,1,32,25,248,1,32,
+  29,248,51,68,248,72,248,80,249,82,249,85,241,86,249,87,233,88,235,
+  90,240,113,253,118,249,119,240,120,242,122,245,200,248,211,249,212,
+  249,213,249,214,249,215,249,217,249,218,249,219,249,220,249,221,
+  249,222,240,250,249,251,249,252,249,253,249,254,245,0,1,245,0,8,
+  248,0,14,248,0,32,248,0,36,248,0,78,249,0,82,249,0,100,241,0,102,
+  241,0,108,249,0,109,249,0,112,249,0,113,249,0,114,249,0,115,249,
+  0,116,249,0,117,249,0,122,240,1,32,25,245,20,66,252,86,255,193,252,
+  194,252,195,252,196,252,197,252,198,252,218,255,219,255,220,255,
+  221,255,0,2,252,0,4,252,0,6,252,0,108,255,0,112,255,0,114,255,0,
+  116,255,17,47,254,66,251,87,250,88,250,90,250,193,251,194,251,195,
+  251,196,251,197,251,198,251,222,250,0,2,251,0,4,251,0,6,251,0,122,
+  250,41,45,242,47,239,66,242,98,253,102,253,112,253,193,242,194,242,
+  195,242,196,242,197,242,198,242,225,253,226,253,227,253,228,253,
+  229,253,230,253,233,253,234,253,235,253,236,253,243,253,244,253,
+  245,253,246,253,247,253,249,253,0,2,242,0,3,253,0,4,242,0,5,253,
+  0,6,242,0,7,253,0,21,253,0,25,253,0,27,253,0,29,253,0,79,253,0,83,
+  253,49,47,254,66,252,98,254,102,254,112,254,118,254,193,252,194,
+  252,195,252,196,252,197,252,198,252,225,254,226,254,227,254,228,
+  254,229,254,230,254,233,254,234,254,235,254,236,254,243,254,244,
+  254,245,254,246,254,247,254,249,254,250,254,251,254,252,254,253,
+  254,0,2,252,0,3,254,0,4,252,0,5,254,0,6,252,0,7,254,0,21,254,0,25,
+  254,0,27,254,0,29,254,0,79,254,0,83,254,0,109,254,0,113,254,0,115,
+  254,0,117,254,40,80,252,102,253,112,253,118,254,122,249,211,252,
+  212,252,213,252,214,252,215,252,217,252,233,253,234,253,235,253,
+  236,253,243,253,244,253,245,253,246,253,247,253,249,253,250,254,
+  251,254,252,254,253,254,254,249,0,1,249,0,21,253,0,25,253,0,27,253,
+  0,29,253,0,78,252,0,79,253,0,82,252,0,83,253,0,109,254,0,113,254,
+  0,115,254,0,117,254,14,85,242,87,242,88,242,90,242,122,248,222,242,
+  254,248,0,1,248,0,100,242,0,102,242,0,122,242,1,32,25,239,1,32,29,
+  254,11,66,254,193,254,194,254,195,254,196,254,197,254,198,254,0,
+  2,254,0,4,254,0,6,254,20,66,250,85,250,87,249,88,249,89,250,90,249,
+  193,250,194,250,195,250,196,250,197,250,198,250,222,249,0,2,250,
+  0,4,250,0,6,250,0,100,250,0,102,250,0,122,249,41,45,242,47,239,66,
+  245,98,255,102,254,112,254,193,245,194,245,195,245,196,245,197,245,
+  198,245,225,255,226,255,227,255,228,255,229,255,230,255,233,254,
+  234,254,235,254,236,254,243,254,244,254,245,254,246,254,247,254,
+  249,254,0,2,245,0,3,255,0,4,245,0,5,255,0,6,245,0,7,255,0,21,254,
+  0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,11,47,254,86,255,218,
+  255,219,255,220,255,221,255,0,108,255,0,112,255,0,114,255,0,116,
+  255,27,80,252,85,250,86,252,87,248,88,251,90,251,211,252,212,252,
+  213,252,214,252,215,252,217,252,218,252,219,252,220,252,221,252,
+  222,251,0,78,252,0,82,252,0,100,250,0,102,250,0,108,252,0,112,252,
+  0,114,252,0,116,252,0,122,251,73,45,245,46,242,47,242,59,245,60,
+  245,66,242,80,254,98,242,102,242,106,254,112,242,115,245,118,242,
+  120,245,122,251,193,242,194,242,195,242,196,242,197,242,198,242,
+  211,254,212,254,213,254,214,254,215,254,217,254,225,248,226,242,
+  227,248,228,248,229,248,230,242,233,248,234,242,235,242,236,248,
+  238,254,243,242,244,242,245,242,246,242,247,242,249,242,250,242,
+  251,242,252,242,253,242,254,251,0,1,251,0,2,242,0,3,248,0,4,242,
+  0,5,248,0,6,242,0,7,242,0,21,248,0,25,242,0,27,242,0,29,242,0,49,
+  254,0,78,254,0,79,242,0,82,254,0,83,242,0,87,245,0,89,245,0,91,245,
+  0,109,242,0,113,242,0,115,242,0,117,242,13,45,249,47,249,66,247,
+  193,247,194,247,195,247,196,247,197,247,198,247,0,2,247,0,4,247,
+  0,6,247,72,45,235,46,245,47,233,59,242,60,242,66,234,72,252,80,249,
+  98,242,102,240,106,251,112,240,118,242,193,234,194,234,195,234,196,
+  234,197,234,198,234,211,249,212,249,213,249,214,249,215,249,217,
+  249,225,242,226,242,227,242,228,242,229,242,230,242,233,240,234,
+  240,235,240,236,240,237,251,238,251,239,251,240,251,243,240,244,
+  240,245,240,246,240,247,240,249,240,250,242,251,242,252,242,253,
+  242,0,2,234,0,3,242,0,4,234,0,5,242,0,6,234,0,7,242,0,21,240,0,25,
+  240,0,27,240,0,29,240,0,32,252,0,36,252,0,45,251,0,49,251,0,78,249,
+  0,79,240,0,82,249,0,83,240,0,109,242,0,113,242,0,115,242,0,117,242,
+  68,45,242,46,251,47,242,59,248,60,248,66,237,80,255,98,246,102,246,
+  106,254,112,244,118,249,122,247,193,237,194,237,195,237,196,237,
+  197,237,198,237,211,255,212,255,213,255,214,255,215,255,217,255,
+  225,246,226,246,227,246,228,246,229,246,230,246,233,246,234,246,
+  235,246,236,246,238,254,243,244,244,244,245,244,246,244,247,244,
+  249,244,250,249,251,249,252,249,253,249,254,247,0,1,247,0,2,237,
+  0,3,246,0,4,237,0,5,246,0,6,237,0,7,246,0,21,246,0,25,246,0,27,246,
+  0,29,246,0,49,254,0,78,255,0,79,244,0,82,255,0,83,244,0,109,249,
+  0,113,249,0,115,249,0,117,249,65,45,242,46,242,47,242,59,242,60,
+  242,66,239,80,251,98,243,102,238,106,251,112,238,118,242,193,239,
+  194,239,195,239,196,239,197,239,198,239,211,251,212,251,213,251,
+  214,251,215,251,217,251,225,243,226,243,227,243,228,243,229,243,
+  230,243,233,245,234,238,235,238,236,245,238,251,243,238,244,238,
+  245,238,246,238,247,238,249,238,250,242,251,242,252,242,253,242,
+  0,2,239,0,3,243,0,4,239,0,5,243,0,6,239,0,7,243,0,21,245,0,25,238,
+  0,27,238,0,29,238,0,49,251,0,78,251,0,79,238,0,82,251,0,83,238,0,
+  109,242,0,113,242,0,115,242,0,117,242,2,119,253,13,47,250,99,255,
+  118,254,119,254,250,254,251,254,252,254,253,254,0,109,254,0,113,
+  254,0,115,254,0,117,254,2,120,254,2,119,254,16,45,254,47,254,106,
+  253,112,253,243,253,244,253,245,253,246,253,247,253,249,253,0,51,
+  251,0,79,253,0,83,253,1,32,25,9,1,32,29,8,2,47,254,4,122,254,254,
+  254,0,1,254,2,119,255,22,102,255,112,254,122,254,233,255,234,255,
+  235,255,236,255,243,254,244,254,245,254,246,254,247,254,249,254,
+  254,254,0,1,254,0,21,255,0,25,255,0,27,255,0,29,255,0,79,254,0,83,
+  254,2,119,250,3,119,255,120,255,37,45,242,46,251,47,240,100,254,
+  102,254,104,255,111,254,112,254,113,255,114,254,119,255,232,254,
+  233,254,234,254,235,254,236,254,242,254,243,254,244,254,245,254,
+  246,254,247,254,249,254,0,9,254,0,15,254,0,21,254,0,25,254,0,27,
+  254,0,29,254,0,33,255,0,37,255,0,70,254,0,72,254,0,74,254,0,79,254,
+  0,83,254,31,45,248,47,245,98,255,102,255,112,255,225,255,226,255,
+  227,255,228,255,229,255,230,255,233,255,234,255,235,255,236,255,
+  243,255,244,255,245,255,246,255,247,255,249,255,0,3,255,0,5,255,
+  0,7,255,0,21,255,0,25,255,0,27,255,0,29,255,0,79,255,0,83,255,12,
+  45,248,47,245,112,255,243,255,244,255,245,255,246,255,247,255,249,
+  255,0,79,255,0,83,255,21,45,248,47,245,102,255,112,253,233,255,234,
+  255,235,255,236,255,243,253,244,253,245,253,246,253,247,253,249,
+  253,0,21,255,0,25,255,0,27,255,0,29,255,0,79,253,0,83,253,51,68,
+  248,72,248,80,249,82,249,85,241,86,249,87,233,88,235,90,240,113,
+  253,118,249,119,240,120,242,122,245,200,248,211,249,212,249,213,
+  249,214,249,215,249,217,249,218,249,219,249,220,249,221,249,222,
+  240,250,249,251,249,252,249,253,249,254,245,0,1,245,0,8,248,0,14,
+  248,0,32,248,0,36,248,0,78,249,0,82,249,0,100,241,0,102,241,0,108,
+  249,0,109,249,0,112,249,0,113,249,0,114,249,0,115,249,0,116,249,
+  0,117,249,0,122,240,1,32,25,245,51,68,248,72,248,80,249,82,249,85,
+  241,86,249,87,233,88,235,90,240,113,253,118,249,119,240,120,242,
+  122,245,200,248,211,249,212,249,213,249,214,249,215,249,217,249,
+  218,249,219,249,220,249,221,249,222,240,250,249,251,249,252,249,
+  253,249,254,245,0,1,245,0,8,248,0,14,248,0,32,248,0,36,248,0,78,
+  249,0,82,249,0,100,241,0,102,241,0,108,249,0,109,249,0,112,249,0,
+  113,249,0,114,249,0,115,249,0,116,249,0,117,249,0,122,240,1,32,25,
+  245,51,68,248,72,248,80,249,82,249,85,241,86,249,87,233,88,235,90,
+  240,113,253,118,249,119,240,120,242,122,245,200,248,211,249,212,
+  249,213,249,214,249,215,249,217,249,218,249,219,249,220,249,221,
+  249,222,240,250,249,251,249,252,249,253,249,254,245,0,1,245,0,8,
+  248,0,14,248,0,32,248,0,36,248,0,78,249,0,82,249,0,100,241,0,102,
+  241,0,108,249,0,109,249,0,112,249,0,113,249,0,114,249,0,115,249,
+  0,116,249,0,117,249,0,122,240,1,32,25,245,51,68,248,72,248,80,249,
+  82,249,85,241,86,249,87,233,88,235,90,240,113,253,118,249,119,240,
+  120,242,122,245,200,248,211,249,212,249,213,249,214,249,215,249,
+  217,249,218,249,219,249,220,249,221,249,222,240,250,249,251,249,
+  252,249,253,249,254,245,0,1,245,0,8,248,0,14,248,0,32,248,0,36,248,
+  0,78,249,0,82,249,0,100,241,0,102,241,0,108,249,0,109,249,0,112,
+  249,0,113,249,0,114,249,0,115,249,0,116,249,0,117,249,0,122,240,
+  1,32,25,245,51,68,248,72,248,80,249,82,249,85,241,86,249,87,233,
+  88,235,90,240,113,253,118,249,119,240,120,242,122,245,200,248,211,
+  249,212,249,213,249,214,249,215,249,217,249,218,249,219,249,220,
+  249,221,249,222,240,250,249,251,249,252,249,253,249,254,245,0,1,
+  245,0,8,248,0,14,248,0,32,248,0,36,248,0,78,249,0,82,249,0,100,241,
+  0,102,241,0,108,249,0,109,249,0,112,249,0,113,249,0,114,249,0,115,
+  249,0,116,249,0,117,249,0,122,240,1,32,25,245,51,68,248,72,248,80,
+  249,82,249,85,241,86,249,87,233,88,235,90,240,113,253,118,249,119,
+  240,120,242,122,245,200,248,211,249,212,249,213,249,214,249,215,
+  249,217,249,218,249,219,249,220,249,221,249,222,240,250,249,251,
+  249,252,249,253,249,254,245,0,1,245,0,8,248,0,14,248,0,32,248,0,
+  36,248,0,78,249,0,82,249,0,100,241,0,102,241,0,108,249,0,109,249,
+  0,112,249,0,113,249,0,114,249,0,115,249,0,116,249,0,117,249,0,122,
+  240,1,32,25,245,11,66,254,193,254,194,254,195,254,196,254,197,254,
+  198,254,0,2,254,0,4,254,0,6,254,20,66,250,85,250,87,249,88,249,89,
+  250,90,249,193,250,194,250,195,250,196,250,197,250,198,250,222,249,
+  0,2,250,0,4,250,0,6,250,0,100,250,0,102,250,0,122,249,20,66,250,
+  85,250,87,249,88,249,89,250,90,249,193,250,194,250,195,250,196,250,
+  197,250,198,250,222,249,0,2,250,0,4,250,0,6,250,0,100,250,0,102,
+  250,0,122,249,20,66,250,85,250,87,249,88,249,89,250,90,249,193,250,
+  194,250,195,250,196,250,197,250,198,250,222,249,0,2,250,0,4,250,
+  0,6,250,0,100,250,0,102,250,0,122,249,20,66,250,85,250,87,249,88,
+  249,89,250,90,249,193,250,194,250,195,250,196,250,197,250,198,250,
+  222,249,0,2,250,0,4,250,0,6,250,0,100,250,0,102,250,0,122,249,20,
+  66,250,85,250,87,249,88,249,89,250,90,249,193,250,194,250,195,250,
+  196,250,197,250,198,250,222,249,0,2,250,0,4,250,0,6,250,0,100,250,
+  0,102,250,0,122,249,20,66,250,85,250,87,249,88,249,89,250,90,249,
+  193,250,194,250,195,250,196,250,197,250,198,250,222,249,0,2,250,
+  0,4,250,0,6,250,0,100,250,0,102,250,0,122,249,13,45,249,47,249,66,
+  247,193,247,194,247,195,247,196,247,197,247,198,247,0,2,247,0,4,
+  247,0,6,247,13,45,249,47,249,66,247,193,247,194,247,195,247,196,
+  247,197,247,198,247,0,2,247,0,4,247,0,6,247,13,45,249,47,249,66,
+  247,193,247,194,247,195,247,196,247,197,247,198,247,0,2,247,0,4,
+  247,0,6,247,13,45,249,47,249,66,247,193,247,194,247,195,247,196,
+  247,197,247,198,247,0,2,247,0,4,247,0,6,247,65,45,242,46,242,47,
+  242,59,242,60,242,66,239,80,251,98,243,102,238,106,251,112,238,118,
+  242,193,239,194,239,195,239,196,239,197,239,198,239,211,251,212,
+  251,213,251,214,251,215,251,217,251,225,243,226,243,227,243,228,
+  243,229,243,230,243,233,245,234,238,235,238,236,245,238,251,243,
+  238,244,238,245,238,246,238,247,238,249,238,250,242,251,242,252,
+  242,253,242,0,2,239,0,3,243,0,4,239,0,5,243,0,6,239,0,7,243,0,21,
+  245,0,25,238,0,27,238,0,29,238,0,49,251,0,78,251,0,79,238,0,82,251,
+  0,83,238,0,109,242,0,113,242,0,115,242,0,117,242,2,119,253,2,119,
+  253,2,119,253,2,119,253,2,119,253,2,119,253,2,119,254,2,119,254,
+  2,119,254,2,119,254,2,119,255,2,119,255,2,119,255,2,119,255,2,119,
+  250,3,119,255,120,255,3,119,255,120,255,3,119,255,120,255,3,119,
+  255,120,255,3,119,255,120,255,3,119,255,120,255,21,45,248,47,245,
+  102,255,112,253,233,255,234,255,235,255,236,255,243,253,244,253,
+  245,253,246,253,247,253,249,253,0,21,255,0,25,255,0,27,255,0,29,
+  255,0,79,253,0,83,253,21,45,248,47,245,102,255,112,253,233,255,234,
+  255,235,255,236,255,243,253,244,253,245,253,246,253,247,253,249,
+  253,0,21,255,0,25,255,0,27,255,0,29,255,0,79,253,0,83,253,51,68,
+  248,72,248,80,249,82,249,85,241,86,249,87,233,88,235,90,240,113,
+  253,118,249,119,240,120,242,122,245,200,248,211,249,212,249,213,
+  249,214,249,215,249,217,249,218,249,219,249,220,249,221,249,222,
+  240,250,249,251,249,252,249,253,249,254,245,0,1,245,0,8,248,0,14,
+  248,0,32,248,0,36,248,0,78,249,0,82,249,0,100,241,0,102,241,0,108,
+  249,0,109,249,0,112,249,0,113,249,0,114,249,0,115,249,0,116,249,
+  0,117,249,0,122,240,1,32,25,245,2,119,253,51,68,248,72,248,80,249,
+  82,249,85,241,86,249,87,233,88,235,90,240,113,253,118,249,119,240,
+  120,242,122,245,200,248,211,249,212,249,213,249,214,249,215,249,
+  217,249,218,249,219,249,220,249,221,249,222,240,250,249,251,249,
+  252,249,253,249,254,245,0,1,245,0,8,248,0,14,248,0,32,248,0,36,248,
+  0,78,249,0,82,249,0,100,241,0,102,241,0,108,249,0,109,249,0,112,
+  249,0,113,249,0,114,249,0,115,249,0,116,249,0,117,249,0,122,240,
+  1,32,25,245,2,119,253,51,68,248,72,248,80,249,82,249,85,241,86,249,
+  87,233,88,235,90,240,113,253,118,249,119,240,120,242,122,251,200,
+  248,211,249,212,249,213,249,214,249,215,249,217,249,218,249,219,
+  249,220,249,221,249,222,240,250,249,251,249,252,249,253,249,254,
+  251,0,1,251,0,8,248,0,14,248,0,32,248,0,36,248,0,78,249,0,82,249,
+  0,100,241,0,102,241,0,108,249,0,109,249,0,112,249,0,113,249,0,114,
+  249,0,115,249,0,116,249,0,117,249,0,122,240,1,32,25,245,2,119,253,
+  17,47,254,66,251,87,250,88,250,90,250,193,251,194,251,195,251,196,
+  251,197,251,198,251,222,250,0,2,251,0,4,251,0,6,251,0,122,250,17,
+  47,254,66,251,87,250,88,250,90,250,193,251,194,251,195,251,196,251,
+  197,251,198,251,222,250,0,2,251,0,4,251,0,6,251,0,122,250,2,120,
+  254,2,119,254,2,119,254,2,119,254,2,119,254,2,47,254,2,47,254,2,
+  119,255,2,119,255,40,80,252,102,253,112,253,118,254,122,249,211,
+  252,212,252,213,252,214,252,215,252,217,252,233,253,234,253,235,
+  253,236,253,243,253,244,253,245,253,246,253,247,253,249,253,250,
+  254,251,254,252,254,253,254,254,249,0,1,249,0,21,253,0,25,253,0,
+  27,253,0,29,253,0,78,252,0,79,253,0,82,252,0,83,253,0,109,254,0,
+  113,254,0,115,254,0,117,254,22,102,255,112,254,122,254,233,255,234,
+  255,235,255,236,255,243,254,244,254,245,254,246,254,247,254,249,
+  254,254,254,0,1,254,0,21,255,0,25,255,0,27,255,0,29,255,0,79,254,
+  0,83,254,14,85,242,87,242,88,242,90,242,122,248,222,242,254,248,
+  0,1,248,0,100,242,0,102,242,0,122,242,1,32,25,239,1,32,29,254,14,
+  85,242,87,242,88,242,90,242,122,248,222,242,254,248,0,1,248,0,100,
+  242,0,102,242,0,122,242,1,32,25,239,1,32,29,254,14,85,242,87,242,
+  88,242,90,242,122,248,222,242,254,248,0,1,248,0,100,242,0,102,242,
+  0,122,242,1,32,25,239,1,32,29,254,11,66,254,193,254,194,254,195,
+  254,196,254,197,254,198,254,0,2,254,0,4,254,0,6,254,2,119,250,11,
+  66,254,193,254,194,254,195,254,196,254,197,254,198,254,0,2,254,0,
+  4,254,0,6,254,2,119,250,11,66,254,193,254,194,254,195,254,196,254,
+  197,254,198,254,0,2,254,0,4,254,0,6,254,2,119,250,20,66,250,85,250,
+  87,249,88,249,89,250,90,249,193,250,194,250,195,250,196,250,197,
+  250,198,250,222,249,0,2,250,0,4,250,0,6,250,0,100,250,0,102,250,
+  0,122,249,3,119,255,120,255,20,66,250,85,250,87,249,88,249,89,250,
+  90,249,193,250,194,250,195,250,196,250,197,250,198,250,222,249,0,
+  2,250,0,4,250,0,6,250,0,100,250,0,102,250,0,122,249,3,119,255,120,
+  255,27,80,252,85,250,86,252,87,248,88,251,90,251,211,252,212,252,
+  213,252,214,252,215,252,217,252,218,252,219,252,220,252,221,252,
+  222,251,0,78,252,0,82,252,0,100,250,0,102,250,0,108,252,0,112,252,
+  0,114,252,0,116,252,0,122,251,37,45,242,46,251,47,240,100,254,102,
+  254,104,255,111,254,112,254,113,255,114,254,119,255,232,254,233,
+  254,234,254,235,254,236,254,242,254,243,254,244,254,245,254,246,
+  254,247,254,249,254,0,9,254,0,15,254,0,21,254,0,25,254,0,27,254,
+  0,29,254,0,33,255,0,37,255,0,70,254,0,72,254,0,74,254,0,79,254,0,
+  83,254,27,80,252,85,250,86,252,87,248,88,251,90,251,211,252,212,
+  252,213,252,214,252,215,252,217,252,218,252,219,252,220,252,221,
+  252,222,251,0,78,252,0,82,252,0,100,250,0,102,250,0,108,252,0,112,
+  252,0,114,252,0,116,252,0,122,251,37,45,242,46,251,47,240,100,254,
+  102,254,104,255,111,254,112,254,113,255,114,254,119,255,232,254,
+  233,254,234,254,235,254,236,254,242,254,243,254,244,254,245,254,
+  246,254,247,254,249,254,0,9,254,0,15,254,0,21,254,0,25,254,0,27,
+  254,0,29,254,0,33,255,0,37,255,0,70,254,0,72,254,0,74,254,0,79,254,
+  0,83,254,27,80,252,85,250,86,252,87,248,88,251,90,251,211,252,212,
+  252,213,252,214,252,215,252,217,252,218,252,219,252,220,252,221,
+  252,222,251,0,78,252,0,82,252,0,100,250,0,102,250,0,108,252,0,112,
+  252,0,114,252,0,116,252,0,122,251,37,45,242,46,251,47,240,100,254,
+  102,254,104,255,111,254,112,254,113,255,114,254,119,255,232,254,
+  233,254,234,254,235,254,236,254,242,254,243,254,244,254,245,254,
+  246,254,247,254,249,254,0,9,254,0,15,254,0,21,254,0,25,254,0,27,
+  254,0,29,254,0,33,255,0,37,255,0,70,254,0,72,254,0,74,254,0,79,254,
+  0,83,254,73,45,245,46,242,47,242,59,245,60,245,66,242,80,254,98,
+  242,102,242,106,254,112,242,115,245,118,242,120,245,122,251,193,
+  242,194,242,195,242,196,242,197,242,198,242,211,254,212,254,213,
+  254,214,254,215,254,217,254,225,248,226,242,227,248,228,248,229,
+  248,230,242,233,248,234,242,235,242,236,248,238,254,243,242,244,
+  242,245,242,246,242,247,242,249,242,250,242,251,242,252,242,253,
+  242,254,251,0,1,251,0,2,242,0,3,248,0,4,242,0,5,248,0,6,242,0,7,
+  242,0,21,248,0,25,242,0,27,242,0,29,242,0,49,254,0,78,254,0,79,242,
+  0,82,254,0,83,242,0,87,245,0,89,245,0,91,245,0,109,242,0,113,242,
+  0,115,242,0,117,242,73,45,245,46,242,47,242,59,245,60,245,66,242,
+  80,254,98,242,102,242,106,254,112,242,115,245,118,242,120,245,122,
+  251,193,242,194,242,195,242,196,242,197,242,198,242,211,254,212,
+  254,213,254,214,254,215,254,217,254,225,248,226,242,227,248,228,
+  248,229,248,230,242,233,248,234,242,235,242,236,248,238,254,243,
+  242,244,242,245,242,246,242,247,242,249,242,250,242,251,242,252,
+  242,253,242,254,251,0,1,251,0,2,242,0,3,248,0,4,242,0,5,248,0,6,
+  242,0,7,242,0,21,248,0,25,242,0,27,242,0,29,242,0,49,254,0,78,254,
+  0,79,242,0,82,254,0,83,242,0,87,245,0,89,245,0,91,245,0,109,242,
+  0,113,242,0,115,242,0,117,242,13,45,249,47,249,66,247,193,247,194,
+  247,195,247,196,247,197,247,198,247,0,2,247,0,4,247,0,6,247,13,45,
+  249,47,249,66,247,193,247,194,247,195,247,196,247,197,247,198,247,
+  0,2,247,0,4,247,0,6,247,13,45,249,47,249,66,247,193,247,194,247,
+  195,247,196,247,197,247,198,247,0,2,247,0,4,247,0,6,247,13,45,249,
+  47,249,66,247,193,247,194,247,195,247,196,247,197,247,198,247,0,
+  2,247,0,4,247,0,6,247,65,45,242,46,242,47,242,59,242,60,242,66,239,
+  80,251,98,243,102,238,106,251,112,238,118,242,193,239,194,239,195,
+  239,196,239,197,239,198,239,211,251,212,251,213,251,214,251,215,
+  251,217,251,225,243,226,243,227,243,228,243,229,243,230,243,233,
+  245,234,238,235,238,236,245,238,251,243,238,244,238,245,238,246,
+  238,247,238,249,238,250,242,251,242,252,242,253,242,0,2,239,0,3,
+  243,0,4,239,0,5,243,0,6,239,0,7,243,0,21,245,0,25,238,0,27,238,0,
+  29,238,0,49,251,0,78,251,0,79,238,0,82,251,0,83,238,0,109,242,0,
+  113,242,0,115,242,0,117,242,12,66,255,193,255,194,255,195,255,196,
+  255,197,255,198,255,0,2,255,0,4,255,0,6,255,1,32,24,246,15,33,245,
+  101,254,115,254,116,251,119,254,0,19,254,0,87,254,0,89,254,0,91,
+  254,0,93,251,0,97,251,0,99,251,1,2,25,251,1,32,25,246,11,66,255,
+  193,255,194,255,195,255,196,255,197,255,198,255,0,2,255,0,4,255,
+  0,6,255
+};
+static afm_cuint16 afm_Times_Bold_highchars_index[] = { /* 220 */
+  161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+  178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+  194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+  210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+  226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+  242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+  258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+  280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+  313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+  336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+  354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+  380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+  8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+  8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+  64257,64258
+};
+static afm_cunicode afm_Times_Bold_ligatures[] = { /* 3 */
+  102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Times-BoldItalic */
+/* FullName: Times Bold Italic */
+/* FamilyName: Times */
+static afm_cuint8 afm_Times_BoldItalic_widths[] = { /* 315 */
+  42,65,93,83,83,139,130,46,56,56,83,95,42,56,42,46,83,83,83,83,83,
+  83,83,83,83,83,56,56,95,95,95,83,139,111,111,111,120,111,111,120,
+  130,65,83,111,102,148,120,120,102,120,111,93,102,120,111,148,111,
+  102,102,56,46,56,95,83,56,83,83,74,83,74,56,83,93,46,46,83,46,130,
+  93,83,83,83,65,65,46,93,74,111,83,74,65,58,37,58,95,65,83,83,83,
+  83,37,83,56,125,44,83,101,125,56,67,95,50,50,56,96,83,42,56,50,50,
+  83,125,125,125,83,111,111,111,111,111,111,157,111,111,111,111,111,
+  65,65,65,65,120,120,120,120,120,120,120,95,120,120,120,120,120,102,
+  102,83,83,83,83,83,83,83,120,74,74,74,74,74,46,46,46,46,83,93,83,
+  83,83,83,83,95,83,93,93,93,93,74,83,74,111,83,111,83,111,83,111,
+  74,111,74,120,101,120,83,111,74,111,74,111,74,111,74,120,83,120,
+  83,65,46,65,46,65,46,111,83,102,46,102,46,102,64,102,46,120,93,120,
+  93,120,93,120,83,120,83,157,120,111,65,111,65,111,65,93,65,93,65,
+  93,65,102,46,102,61,120,93,120,93,120,93,120,93,102,102,65,102,65,
+  102,65,83,93,65,56,56,56,56,56,56,56,56,83,167,56,56,56,83,83,83,
+  83,83,58,167,167,56,56,28,83,167,82,102,100,101,92,92,92,92,82,42,
+  93,93
+};
+static afm_sint16 afm_Times_BoldItalic_kerning_index[] = { /* 315 */
+  1,0,0,0,0,0,0,0,0,0,0,0,36,0,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,54,173,0,219,0,254,0,0,0,374,489,581,0,612,636,681,774,797,0,
+  859,1027,1051,1207,0,1362,0,0,0,0,0,0,0,0,1510,1537,0,1545,1548,
+  0,0,0,0,1587,0,0,1630,1633,0,0,1647,0,0,0,1652,1699,1769,1792,0,
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,1797,1916,2035,2154,2273,2392,0,0,0,0,0,0,0,0,0,0,0,2511,2535,
+  2580,2625,2670,2715,0,2760,2805,2829,2853,2877,2901,0,0,0,0,0,0,
+  0,0,0,3049,3057,3060,3063,3066,0,0,0,0,0,3069,3072,3086,3100,3114,
+  3128,0,3142,0,0,0,0,3156,0,3161,3166,0,3285,0,3404,0,0,3523,0,3531,
+  3539,0,3574,0,0,3609,0,3612,0,3615,0,3618,0,0,0,0,0,0,0,0,0,0,3621,
+  3713,3756,0,3787,0,0,0,3818,0,3849,3873,3876,3900,3903,3927,3930,
+  3975,3989,4034,0,0,4048,4110,4115,4177,4182,4244,0,0,0,0,0,0,4249,
+  0,4417,0,4585,0,4609,0,4633,0,4657,0,4681,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,0,0,0,0,0,0,4829,4834,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,0,0,0,0,0
+};
+static afm_cuint8 afm_Times_BoldItalic_kerning_data[] = { /* 4879 */
+  42,16,66,251,87,245,88,245,90,245,193,251,194,251,195,251,196,251,
+  197,251,198,251,222,245,0,2,251,0,4,251,0,6,251,0,122,245,3,1,32,
+  25,241,1,32,29,241,3,1,32,25,241,1,32,29,241,50,68,246,72,247,80,
+  249,82,248,85,248,86,249,87,241,88,240,90,245,118,252,119,245,120,
+  245,122,245,200,246,211,249,212,249,213,249,214,249,215,249,217,
+  249,218,249,219,249,220,249,221,249,222,245,250,252,251,252,252,
+  252,253,252,254,245,0,1,245,0,8,246,0,14,246,0,32,247,0,36,247,0,
+  78,249,0,82,249,0,100,248,0,102,248,0,108,249,0,109,252,0,112,249,
+  0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,245,1,32,
+  25,245,20,66,253,86,255,193,253,194,253,195,253,196,253,197,253,
+  198,253,218,255,219,255,220,255,221,255,0,2,253,0,4,253,0,6,253,
+  0,108,255,0,112,255,0,114,255,0,116,255,16,66,253,87,249,88,250,
+  90,249,193,253,194,253,195,253,196,253,197,253,198,253,222,249,0,
+  2,253,0,4,253,0,6,253,0,122,249,52,45,235,47,235,66,240,98,241,102,
+  240,106,250,112,245,115,249,193,240,194,240,195,240,196,240,197,
+  240,198,240,225,241,226,241,227,241,228,241,229,241,230,241,233,
+  240,234,240,235,240,236,240,237,250,238,250,239,250,240,250,243,
+  245,244,245,245,245,246,245,247,245,249,245,0,2,240,0,3,241,0,4,
+  240,0,5,241,0,6,240,0,7,241,0,21,240,0,25,240,0,27,240,0,29,240,
+  0,45,250,0,49,250,0,79,245,0,83,245,0,87,249,0,89,249,0,91,249,50,
+  45,255,47,255,66,253,98,250,102,250,112,250,118,250,193,253,194,
+  253,195,253,196,253,197,253,198,253,225,250,226,250,227,250,228,
+  250,229,250,230,250,233,250,234,250,235,250,236,250,243,250,244,
+  250,245,250,246,250,247,250,249,250,250,250,251,250,252,250,253,
+  250,0,2,253,0,3,250,0,4,253,0,5,250,0,6,253,0,7,250,0,21,250,0,25,
+  250,0,27,250,0,29,250,0,79,250,0,83,250,0,109,250,0,113,250,0,115,
+  250,0,117,250,40,80,252,102,253,112,253,118,254,122,254,211,252,
+  212,252,213,252,214,252,215,252,217,252,233,253,234,253,235,253,
+  236,253,243,253,244,253,245,253,246,253,247,253,249,253,250,254,
+  251,254,252,254,253,254,254,254,0,1,254,0,21,253,0,25,253,0,27,253,
+  0,29,253,0,78,252,0,79,253,0,82,252,0,83,253,0,109,254,0,113,254,
+  0,115,254,0,117,254,13,85,254,87,251,88,251,90,251,122,251,222,251,
+  254,251,0,1,251,0,100,254,0,102,254,0,122,251,1,32,25,248,11,66,
+  252,193,252,194,252,195,252,196,252,197,252,198,252,0,2,252,0,4,
+  252,0,6,252,20,66,250,85,250,87,249,88,249,89,250,90,249,193,250,
+  194,250,195,250,196,250,197,250,198,250,222,249,0,2,250,0,4,250,
+  0,6,250,0,100,250,0,102,250,0,122,249,41,45,235,47,235,66,243,98,
+  250,102,249,112,248,193,243,194,243,195,243,196,243,197,243,198,
+  243,225,250,226,250,227,250,228,250,229,250,230,250,233,249,234,
+  249,235,249,236,249,243,248,244,248,245,248,246,248,247,248,249,
+  248,0,2,243,0,3,250,0,4,243,0,5,250,0,6,243,0,7,250,0,21,249,0,25,
+  249,0,27,249,0,29,249,0,79,248,0,83,248,10,86,255,218,255,219,255,
+  220,255,221,255,0,108,255,0,112,255,0,114,255,0,116,255,27,80,250,
+  85,252,86,250,87,254,88,254,90,254,211,250,212,250,213,250,214,250,
+  215,250,217,250,218,250,219,250,220,250,221,250,222,254,0,78,250,
+  0,82,250,0,100,252,0,102,252,0,108,250,0,112,250,0,114,250,0,116,
+  250,0,122,254,73,45,242,46,242,47,242,59,245,60,245,66,248,80,254,
+  98,242,102,242,106,251,112,241,115,251,118,251,120,251,122,251,193,
+  248,194,248,195,248,196,248,197,248,198,248,211,254,212,254,213,
+  254,214,254,215,254,217,254,225,242,226,242,227,242,228,242,229,
+  242,230,242,233,248,234,242,235,242,236,248,238,251,243,241,244,
+  241,245,241,246,241,247,241,249,241,250,251,251,251,252,251,253,
+  251,254,251,0,1,251,0,2,248,0,3,242,0,4,248,0,5,242,0,6,248,0,7,
+  242,0,21,248,0,25,242,0,27,242,0,29,242,0,49,251,0,78,254,0,79,241,
+  0,82,254,0,83,241,0,87,251,0,89,251,0,91,251,0,109,251,0,113,251,
+  0,115,251,0,117,251,11,66,249,193,249,194,249,195,249,196,249,197,
+  249,198,249,0,2,249,0,4,249,0,6,249,68,45,235,46,245,47,235,59,245,
+  60,245,66,243,72,255,80,252,98,238,102,238,106,248,112,238,118,248,
+  193,243,194,243,195,243,196,243,197,243,198,243,211,252,212,252,
+  213,252,214,252,215,252,217,252,225,238,226,238,227,238,228,238,
+  229,238,230,238,233,245,234,238,235,238,236,245,238,248,243,238,
+  244,238,245,238,246,238,247,238,249,238,250,248,251,248,252,248,
+  253,248,0,2,243,0,3,238,0,4,243,0,5,238,0,6,243,0,7,238,0,21,245,
+  0,25,238,0,27,238,0,29,238,0,32,255,0,36,255,0,49,248,0,78,252,0,
+  79,238,0,82,252,0,83,238,0,109,248,0,113,248,0,115,248,0,117,248,
+  68,45,245,46,249,47,245,59,248,60,248,66,245,80,254,98,243,102,242,
+  106,251,112,244,118,248,122,248,193,245,194,245,195,245,196,245,
+  197,245,198,245,211,254,212,254,213,254,214,254,215,254,217,254,
+  225,243,226,243,227,243,228,243,229,243,230,243,233,249,234,242,
+  235,242,236,249,238,251,243,244,244,244,245,244,246,244,247,244,
+  249,244,250,248,251,248,252,248,253,248,254,248,0,1,248,0,2,245,
+  0,3,243,0,4,245,0,5,243,0,6,245,0,7,243,0,21,249,0,25,242,0,27,242,
+  0,29,242,0,49,251,0,78,254,0,79,244,0,82,254,0,83,244,0,109,248,
+  0,113,248,0,115,248,0,117,248,65,45,242,46,242,47,245,59,242,60,
+  242,66,245,80,253,98,242,102,238,106,248,112,238,118,242,193,245,
+  194,245,195,245,196,245,197,245,198,245,211,253,212,253,213,253,
+  214,253,215,253,217,253,225,242,226,242,227,242,228,242,229,242,
+  230,242,233,245,234,238,235,245,236,245,238,248,243,238,244,238,
+  245,238,246,238,247,238,249,238,250,242,251,242,252,242,253,242,
+  0,2,245,0,3,242,0,4,245,0,5,242,0,6,245,0,7,242,0,21,245,0,25,238,
+  0,27,238,0,29,238,0,49,248,0,78,253,0,79,238,0,82,253,0,83,238,0,
+  109,242,0,113,242,0,115,242,0,117,242,12,47,250,99,255,118,254,250,
+  254,251,254,252,254,253,254,0,109,254,0,113,254,0,115,254,0,117,
+  254,4,105,255,108,255,0,57,255,2,99,255,17,45,255,47,255,102,255,
+  103,254,112,255,234,255,243,255,244,255,245,255,246,255,249,255,
+  0,25,255,0,27,255,0,51,252,0,83,255,1,32,25,9,19,102,252,112,255,
+  233,252,234,252,235,252,236,252,243,255,244,255,245,255,246,255,
+  247,255,249,255,0,21,252,0,25,252,0,27,252,0,29,252,0,79,255,0,83,
+  255,2,119,250,7,119,254,120,253,121,255,122,255,254,255,0,1,255,
+  3,45,246,47,246,21,45,251,47,251,102,254,112,254,233,254,234,254,
+  235,254,236,254,243,254,244,254,245,254,246,254,247,254,249,254,
+  0,21,254,0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,31,45,251,
+  47,251,98,255,102,255,112,254,225,255,226,255,227,255,228,255,229,
+  255,230,255,233,255,234,255,235,255,236,255,243,254,244,254,245,
+  254,246,254,247,254,249,254,0,3,255,0,5,255,0,7,255,0,21,255,0,25,
+  255,0,27,255,0,29,255,0,79,254,0,83,254,10,102,255,233,255,234,255,
+  235,255,236,255,0,21,255,0,25,255,0,27,255,0,29,255,3,45,251,47,
+  251,50,68,246,72,247,80,249,82,248,85,248,86,249,87,241,88,240,90,
+  245,118,252,119,245,120,245,122,245,200,246,211,249,212,249,213,
+  249,214,249,215,249,217,249,218,249,219,249,220,249,221,249,222,
+  245,250,252,251,252,252,252,253,252,254,245,0,1,245,0,8,246,0,14,
+  246,0,32,247,0,36,247,0,78,249,0,82,249,0,100,248,0,102,248,0,108,
+  249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,
+  0,117,252,0,122,245,1,32,25,245,50,68,246,72,247,80,249,82,248,85,
+  248,86,249,87,241,88,240,90,245,118,252,119,245,120,245,122,245,
+  200,246,211,249,212,249,213,249,214,249,215,249,217,249,218,249,
+  219,249,220,249,221,249,222,245,250,252,251,252,252,252,253,252,
+  254,245,0,1,245,0,8,246,0,14,246,0,32,247,0,36,247,0,78,249,0,82,
+  249,0,100,248,0,102,248,0,108,249,0,109,252,0,112,249,0,113,252,
+  0,114,249,0,115,252,0,116,249,0,117,252,0,122,245,1,32,25,245,50,
+  68,246,72,247,80,249,82,248,85,248,86,249,87,241,88,240,90,245,118,
+  252,119,245,120,245,122,245,200,246,211,249,212,249,213,249,214,
+  249,215,249,217,249,218,249,219,249,220,249,221,249,222,245,250,
+  252,251,252,252,252,253,252,254,245,0,1,245,0,8,246,0,14,246,0,32,
+  247,0,36,247,0,78,249,0,82,249,0,100,248,0,102,248,0,108,249,0,109,
+  252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,
+  0,122,245,1,32,25,245,50,68,246,72,247,80,249,82,248,85,248,86,249,
+  87,241,88,240,90,245,118,252,119,245,120,245,122,245,200,246,211,
+  249,212,249,213,249,214,249,215,249,217,249,218,249,219,249,220,
+  249,221,249,222,245,250,252,251,252,252,252,253,252,254,245,0,1,
+  245,0,8,246,0,14,246,0,32,247,0,36,247,0,78,249,0,82,249,0,100,248,
+  0,102,248,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,
+  252,0,116,249,0,117,252,0,122,245,1,32,25,245,50,68,246,72,247,80,
+  249,82,248,85,248,86,249,87,241,88,240,90,245,118,252,119,245,120,
+  245,122,245,200,246,211,249,212,249,213,249,214,249,215,249,217,
+  249,218,249,219,249,220,249,221,249,222,245,250,252,251,252,252,
+  252,253,252,254,245,0,1,245,0,8,246,0,14,246,0,32,247,0,36,247,0,
+  78,249,0,82,249,0,100,248,0,102,248,0,108,249,0,109,252,0,112,249,
+  0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,245,1,32,
+  25,245,50,68,246,72,247,80,249,82,248,85,248,86,249,87,241,88,240,
+  90,245,118,252,119,245,120,245,122,245,200,246,211,249,212,249,213,
+  249,214,249,215,249,217,249,218,249,219,249,220,249,221,249,222,
+  245,250,252,251,252,252,252,253,252,254,245,0,1,245,0,8,246,0,14,
+  246,0,32,247,0,36,247,0,78,249,0,82,249,0,100,248,0,102,248,0,108,
+  249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,
+  0,117,252,0,122,245,1,32,25,245,11,66,252,193,252,194,252,195,252,
+  196,252,197,252,198,252,0,2,252,0,4,252,0,6,252,20,66,250,85,250,
+  87,249,88,249,89,250,90,249,193,250,194,250,195,250,196,250,197,
+  250,198,250,222,249,0,2,250,0,4,250,0,6,250,0,100,250,0,102,250,
+  0,122,249,20,66,250,85,250,87,249,88,249,89,250,90,249,193,250,194,
+  250,195,250,196,250,197,250,198,250,222,249,0,2,250,0,4,250,0,6,
+  250,0,100,250,0,102,250,0,122,249,20,66,250,85,250,87,249,88,249,
+  89,250,90,249,193,250,194,250,195,250,196,250,197,250,198,250,222,
+  249,0,2,250,0,4,250,0,6,250,0,100,250,0,102,250,0,122,249,20,66,
+  250,85,250,87,249,88,249,89,250,90,249,193,250,194,250,195,250,196,
+  250,197,250,198,250,222,249,0,2,250,0,4,250,0,6,250,0,100,250,0,
+  102,250,0,122,249,20,66,250,85,250,87,249,88,249,89,250,90,249,193,
+  250,194,250,195,250,196,250,197,250,198,250,222,249,0,2,250,0,4,
+  250,0,6,250,0,100,250,0,102,250,0,122,249,20,66,250,85,250,87,249,
+  88,249,89,250,90,249,193,250,194,250,195,250,196,250,197,250,198,
+  250,222,249,0,2,250,0,4,250,0,6,250,0,100,250,0,102,250,0,122,249,
+  11,66,249,193,249,194,249,195,249,196,249,197,249,198,249,0,2,249,
+  0,4,249,0,6,249,11,66,249,193,249,194,249,195,249,196,249,197,249,
+  198,249,0,2,249,0,4,249,0,6,249,11,66,249,193,249,194,249,195,249,
+  196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,11,66,249,193,249,
+  194,249,195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,
+  65,45,242,46,242,47,245,59,242,60,242,66,245,80,253,98,242,102,238,
+  106,248,112,238,118,242,193,245,194,245,195,245,196,245,197,245,
+  198,245,211,253,212,253,213,253,214,253,215,253,217,253,225,242,
+  226,242,227,242,228,242,229,242,230,242,233,245,234,238,235,245,
+  236,245,238,248,243,238,244,238,245,238,246,238,247,238,249,238,
+  250,242,251,242,252,242,253,242,0,2,245,0,3,242,0,4,245,0,5,242,
+  0,6,245,0,7,242,0,21,245,0,25,238,0,27,238,0,29,238,0,49,248,0,78,
+  253,0,79,238,0,82,253,0,83,238,0,109,242,0,113,242,0,115,242,0,117,
+  242,4,105,255,108,255,0,57,255,2,99,255,2,99,255,2,99,255,2,99,255,
+  2,119,250,7,119,254,120,253,121,255,122,255,254,255,0,1,255,7,119,
+  254,120,253,121,255,122,255,254,255,0,1,255,7,119,254,120,253,121,
+  255,122,255,254,255,0,1,255,7,119,254,120,253,121,255,122,255,254,
+  255,0,1,255,7,119,254,120,253,121,255,122,255,254,255,0,1,255,7,
+  119,254,120,253,121,255,122,255,254,255,0,1,255,3,45,251,47,251,
+  3,45,251,47,251,50,68,246,72,247,80,249,82,248,85,248,86,249,87,
+  241,88,240,90,245,118,252,119,245,120,245,122,245,200,246,211,249,
+  212,249,213,249,214,249,215,249,217,249,218,249,219,249,220,249,
+  221,249,222,245,250,252,251,252,252,252,253,252,254,245,0,1,245,
+  0,8,246,0,14,246,0,32,247,0,36,247,0,78,249,0,82,249,0,100,248,0,
+  102,248,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,
+  252,0,116,249,0,117,252,0,122,245,1,32,25,245,50,68,246,72,247,80,
+  249,82,248,85,248,86,249,87,241,88,240,90,245,118,252,119,245,120,
+  245,122,245,200,246,211,249,212,249,213,249,214,249,215,249,217,
+  249,218,249,219,249,220,249,221,249,222,245,250,252,251,252,252,
+  252,253,252,254,245,0,1,245,0,8,246,0,14,246,0,32,247,0,36,247,0,
+  78,249,0,82,249,0,100,248,0,102,248,0,108,249,0,109,252,0,112,249,
+  0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,245,1,32,
+  25,245,50,68,246,72,247,80,249,82,248,85,248,86,249,87,241,88,240,
+  90,245,118,252,119,245,120,245,122,251,200,246,211,249,212,249,213,
+  249,214,249,215,249,217,249,218,249,219,249,220,249,221,249,222,
+  245,250,252,251,252,252,252,253,252,254,251,0,1,251,0,8,246,0,14,
+  246,0,32,247,0,36,247,0,78,249,0,82,249,0,100,248,0,102,248,0,108,
+  249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,
+  0,117,252,0,122,245,1,32,25,245,4,105,255,108,255,0,57,255,4,105,
+  255,108,255,0,57,255,16,66,253,87,249,88,250,90,249,193,253,194,
+  253,195,253,196,253,197,253,198,253,222,249,0,2,253,0,4,253,0,6,
+  253,0,122,249,16,66,253,87,249,88,250,90,249,193,253,194,253,195,
+  253,196,253,197,253,198,253,222,249,0,2,253,0,4,253,0,6,253,0,122,
+  249,2,99,255,2,99,255,2,99,255,2,99,255,40,80,252,102,253,112,253,
+  118,254,122,254,211,252,212,252,213,252,214,252,215,252,217,252,
+  233,253,234,253,235,253,236,253,243,253,244,253,245,253,246,253,
+  247,253,249,253,250,254,251,254,252,254,253,254,254,254,0,1,254,
+  0,21,253,0,25,253,0,27,253,0,29,253,0,78,252,0,79,253,0,82,252,0,
+  83,253,0,109,254,0,113,254,0,115,254,0,117,254,19,102,252,112,255,
+  233,252,234,252,235,252,236,252,243,255,244,255,245,255,246,255,
+  247,255,249,255,0,21,252,0,25,252,0,27,252,0,29,252,0,79,255,0,83,
+  255,13,85,254,87,251,88,251,90,251,122,251,222,251,254,251,0,1,251,
+  0,100,254,0,102,254,0,122,251,1,32,25,248,13,85,254,87,251,88,251,
+  90,251,122,251,222,251,254,251,0,1,251,0,100,254,0,102,254,0,122,
+  251,1,32,25,248,13,85,254,87,251,88,251,90,251,122,251,222,251,254,
+  251,0,1,251,0,100,254,0,102,254,0,122,251,1,32,25,248,11,66,252,
+  193,252,194,252,195,252,196,252,197,252,198,252,0,2,252,0,4,252,
+  0,6,252,2,119,250,11,66,252,193,252,194,252,195,252,196,252,197,
+  252,198,252,0,2,252,0,4,252,0,6,252,2,119,250,11,66,252,193,252,
+  194,252,195,252,196,252,197,252,198,252,0,2,252,0,4,252,0,6,252,
+  2,119,250,20,66,250,85,250,87,249,88,249,89,250,90,249,193,250,194,
+  250,195,250,196,250,197,250,198,250,222,249,0,2,250,0,4,250,0,6,
+  250,0,100,250,0,102,250,0,122,249,7,119,254,120,253,121,255,122,
+  255,254,255,0,1,255,20,66,250,85,250,87,249,88,249,89,250,90,249,
+  193,250,194,250,195,250,196,250,197,250,198,250,222,249,0,2,250,
+  0,4,250,0,6,250,0,100,250,0,102,250,0,122,249,7,119,254,120,253,
+  121,255,122,255,254,255,0,1,255,27,80,250,85,252,86,250,87,254,88,
+  254,90,254,211,250,212,250,213,250,214,250,215,250,217,250,218,250,
+  219,250,220,250,221,250,222,254,0,78,250,0,82,250,0,100,252,0,102,
+  252,0,108,250,0,112,250,0,114,250,0,116,250,0,122,254,3,45,246,47,
+  246,27,80,250,85,252,86,250,87,254,88,254,90,254,211,250,212,250,
+  213,250,214,250,215,250,217,250,218,250,219,250,220,250,221,250,
+  222,254,0,78,250,0,82,250,0,100,252,0,102,252,0,108,250,0,112,250,
+  0,114,250,0,116,250,0,122,254,3,45,246,47,246,27,80,250,85,252,86,
+  250,87,254,88,254,90,254,211,250,212,250,213,250,214,250,215,250,
+  217,250,218,250,219,250,220,250,221,250,222,254,0,78,250,0,82,250,
+  0,100,252,0,102,252,0,108,250,0,112,250,0,114,250,0,116,250,0,122,
+  254,3,45,246,47,246,73,45,242,46,242,47,242,59,245,60,245,66,248,
+  80,254,98,242,102,242,106,251,112,241,115,251,118,251,120,251,122,
+  251,193,248,194,248,195,248,196,248,197,248,198,248,211,254,212,
+  254,213,254,214,254,215,254,217,254,225,242,226,242,227,242,228,
+  242,229,242,230,242,233,248,234,242,235,242,236,248,238,251,243,
+  241,244,241,245,241,246,241,247,241,249,241,250,251,251,251,252,
+  251,253,251,254,251,0,1,251,0,2,248,0,3,242,0,4,248,0,5,242,0,6,
+  248,0,7,242,0,21,248,0,25,242,0,27,242,0,29,242,0,49,251,0,78,254,
+  0,79,241,0,82,254,0,83,241,0,87,251,0,89,251,0,91,251,0,109,251,
+  0,113,251,0,115,251,0,117,251,73,45,242,46,242,47,242,59,245,60,
+  245,66,248,80,254,98,242,102,242,106,251,112,241,115,251,118,251,
+  120,251,122,251,193,248,194,248,195,248,196,248,197,248,198,248,
+  211,254,212,254,213,254,214,254,215,254,217,254,225,242,226,242,
+  227,242,228,242,229,242,230,242,233,248,234,242,235,242,236,248,
+  238,251,243,241,244,241,245,241,246,241,247,241,249,241,250,251,
+  251,251,252,251,253,251,254,251,0,1,251,0,2,248,0,3,242,0,4,248,
+  0,5,242,0,6,248,0,7,242,0,21,248,0,25,242,0,27,242,0,29,242,0,49,
+  251,0,78,254,0,79,241,0,82,254,0,83,241,0,87,251,0,89,251,0,91,251,
+  0,109,251,0,113,251,0,115,251,0,117,251,11,66,249,193,249,194,249,
+  195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,11,66,249,
+  193,249,194,249,195,249,196,249,197,249,198,249,0,2,249,0,4,249,
+  0,6,249,11,66,249,193,249,194,249,195,249,196,249,197,249,198,249,
+  0,2,249,0,4,249,0,6,249,11,66,249,193,249,194,249,195,249,196,249,
+  197,249,198,249,0,2,249,0,4,249,0,6,249,65,45,242,46,242,47,245,
+  59,242,60,242,66,245,80,253,98,242,102,238,106,248,112,238,118,242,
+  193,245,194,245,195,245,196,245,197,245,198,245,211,253,212,253,
+  213,253,214,253,215,253,217,253,225,242,226,242,227,242,228,242,
+  229,242,230,242,233,245,234,238,235,245,236,245,238,248,243,238,
+  244,238,245,238,246,238,247,238,249,238,250,242,251,242,252,242,
+  253,242,0,2,245,0,3,242,0,4,245,0,5,242,0,6,245,0,7,242,0,21,245,
+  0,25,238,0,27,238,0,29,238,0,49,248,0,78,253,0,79,238,0,82,253,0,
+  83,238,0,109,242,0,113,242,0,115,242,0,117,242,2,1,32,24,245,17,
+  33,245,101,254,115,254,116,245,117,251,119,254,0,19,254,0,87,254,
+  0,89,254,0,91,254,0,93,245,0,97,245,0,99,245,0,101,251,1,2,25,245,
+  1,32,25,245
+};
+static afm_cuint16 afm_Times_BoldItalic_highchars_index[] = { /* 220 */
+  161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+  178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+  194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+  210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+  226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+  242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+  258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+  280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+  313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+  336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+  354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+  380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+  8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+  8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+  64257,64258
+};
+static afm_cunicode afm_Times_BoldItalic_ligatures[] = { /* 3 */
+  102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Times-Italic */
+/* FullName: Times Italic */
+/* FamilyName: Times */
+static afm_cuint8 afm_Times_Italic_widths[] = { /* 315 */
+  42,56,70,83,83,139,130,36,56,56,83,113,42,56,42,46,83,83,83,83,83,
+  83,83,83,83,83,56,56,113,113,113,83,153,102,102,111,120,102,102,
+  120,120,56,74,111,93,139,111,120,102,120,102,83,93,120,102,139,102,
+  93,93,65,46,65,70,83,56,83,83,74,83,74,46,83,83,46,46,74,46,120,
+  83,83,83,83,65,65,46,83,74,111,74,74,65,67,46,67,90,65,83,83,83,
+  83,46,83,56,127,46,83,113,127,56,67,113,50,50,56,83,87,42,56,50,
+  52,83,125,125,125,83,102,102,102,102,102,102,148,111,102,102,102,
+  102,56,56,56,56,120,111,120,120,120,120,120,113,120,120,120,120,
+  120,93,102,83,83,83,83,83,83,83,111,74,74,74,74,74,46,46,46,46,83,
+  83,83,83,83,83,83,113,83,83,83,83,83,74,83,74,102,83,102,83,102,
+  83,111,74,111,74,120,91,120,83,102,74,102,74,102,74,102,74,120,83,
+  120,83,56,46,56,46,56,46,111,74,93,46,93,46,102,50,93,46,111,83,
+  111,83,111,83,120,83,120,83,157,111,102,65,102,65,102,65,83,65,83,
+  65,83,65,93,46,93,50,120,83,120,83,120,83,120,83,93,93,65,93,65,
+  93,65,83,83,65,56,56,56,56,56,56,56,56,83,148,56,56,56,93,93,93,
+  83,83,58,148,167,56,56,28,83,163,79,102,100,113,76,92,92,92,79,42,
+  83,83
+};
+static afm_sint16 afm_Times_Italic_kerning_index[] = { /* 315 */
+  1,0,0,0,0,0,0,0,0,0,0,0,44,0,53,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,62,181,0,227,0,262,0,0,0,382,497,589,0,620,644,689,782,805,0,
+  859,1027,1055,1212,0,1367,0,0,0,0,0,0,0,1524,1533,1558,0,1566,1592,
+  1611,0,0,0,1646,0,0,1696,1699,0,0,1710,0,0,0,1822,1827,0,1832,0,
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,1837,1956,2075,2194,2313,2432,0,0,0,0,0,0,0,0,0,0,0,2551,2575,
+  2620,2665,2710,2755,0,2800,2845,2873,2901,2929,2957,0,0,3114,3123,
+  3132,3141,3150,3159,0,3168,3176,3202,3228,3254,0,0,0,0,0,3280,3283,
+  3294,3305,3316,3327,0,3338,0,0,0,0,3349,0,3354,3359,3478,3487,3606,
+  3615,3734,0,3743,0,3751,3759,0,3794,0,0,3829,0,3855,0,3881,0,3907,
+  0,3933,0,3968,0,0,0,0,0,0,4003,4095,4145,0,4176,0,0,0,4207,0,4238,
+  4262,4265,4289,4292,4316,4319,4364,4375,4420,0,0,4431,4485,4597,
+  4651,4763,4817,0,0,0,0,0,0,4929,0,5097,0,5265,0,5293,0,5321,0,5349,
+  0,5377,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5534,5539,0,0,0,0,0,
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+static afm_cuint8 afm_Times_Italic_kerning_data[] = { /* 5584 */
+  42,19,66,254,85,254,87,251,88,250,90,244,193,254,194,254,195,254,
+  196,254,197,254,198,254,222,244,0,2,254,0,4,254,0,6,254,0,100,254,
+  0,102,254,0,122,244,3,1,32,25,234,1,32,29,234,3,1,32,25,234,1,32,
+  29,234,50,68,252,72,251,80,250,82,250,85,251,86,249,87,239,88,241,
+  90,248,118,254,119,248,120,248,122,248,200,252,211,250,212,250,213,
+  250,214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,
+  248,250,254,251,254,252,254,253,254,254,248,0,1,248,0,8,252,0,14,
+  252,0,32,251,0,36,251,0,78,250,0,82,250,0,100,251,0,102,251,0,108,
+  249,0,109,254,0,112,249,0,113,254,0,114,249,0,115,254,0,116,249,
+  0,117,254,0,122,248,1,32,25,251,20,66,253,86,255,193,253,194,253,
+  195,253,196,253,197,253,198,253,218,255,219,255,220,255,221,255,
+  0,2,253,0,4,253,0,6,253,0,108,255,0,112,255,0,114,255,0,116,255,
+  16,66,251,87,250,88,250,90,250,193,251,194,251,195,251,196,251,197,
+  251,198,251,222,250,0,2,251,0,4,251,0,6,251,0,122,250,52,45,234,
+  47,234,66,238,98,244,102,244,106,249,112,239,115,248,193,238,194,
+  238,195,238,196,238,197,238,198,238,225,244,226,244,227,244,228,
+  244,229,244,230,244,233,244,234,244,235,244,236,244,237,249,238,
+  249,239,249,240,249,243,239,244,239,245,239,246,239,247,239,249,
+  239,0,2,238,0,3,244,0,4,238,0,5,244,0,6,238,0,7,244,0,21,244,0,25,
+  244,0,27,244,0,29,244,0,45,249,0,49,249,0,79,239,0,83,239,0,87,248,
+  0,89,248,0,91,248,50,45,253,47,253,66,250,98,251,102,253,112,253,
+  118,251,193,250,194,250,195,250,196,250,197,250,198,250,225,251,
+  226,251,227,251,228,251,229,251,230,251,233,253,234,253,235,253,
+  236,253,243,253,244,253,245,253,246,253,247,253,249,253,250,251,
+  251,251,252,251,253,251,0,2,250,0,3,251,0,4,250,0,5,251,0,6,250,
+  0,7,251,0,21,253,0,25,253,0,27,253,0,29,253,0,79,253,0,83,253,0,
+  109,251,0,113,251,0,115,251,0,117,251,40,80,249,102,251,112,250,
+  118,250,122,250,211,249,212,249,213,249,214,249,215,249,217,249,
+  233,251,234,251,235,251,236,251,243,250,244,250,245,250,246,250,
+  247,250,249,250,250,250,251,250,252,250,253,250,254,250,0,1,250,
+  0,21,251,0,25,251,0,27,251,0,29,251,0,78,249,0,79,250,0,82,249,0,
+  83,250,0,109,250,0,113,250,0,115,250,0,117,250,13,85,254,87,248,
+  88,248,90,254,122,252,222,254,254,252,0,1,252,0,100,254,0,102,254,
+  0,122,254,1,32,25,251,11,66,252,193,252,194,252,195,252,196,252,
+  197,252,198,252,0,2,252,0,4,252,0,6,252,20,66,248,85,250,87,249,
+  88,249,89,250,90,249,193,248,194,248,195,248,196,248,197,248,198,
+  248,222,249,0,2,248,0,4,248,0,6,248,0,100,250,0,102,250,0,122,249,
+  41,45,234,47,234,66,242,98,244,102,244,112,244,193,242,194,242,195,
+  242,196,242,197,242,198,242,225,244,226,244,227,244,228,244,229,
+  244,230,244,233,244,234,244,235,244,236,244,243,244,244,244,245,
+  244,246,244,247,244,249,244,0,2,242,0,3,244,0,4,242,0,5,244,0,6,
+  242,0,7,244,0,21,244,0,25,244,0,27,244,0,29,244,0,79,244,0,83,244,
+  10,86,255,218,255,219,255,220,255,221,255,0,108,255,0,112,255,0,
+  114,255,0,116,255,24,80,250,86,250,87,254,88,254,90,254,211,250,
+  212,250,213,250,214,250,215,250,217,250,218,250,219,250,220,250,
+  221,250,222,254,0,78,250,0,82,250,0,108,250,0,112,250,0,114,250,
+  0,116,250,0,122,254,73,45,245,46,245,47,245,59,248,60,246,66,249,
+  80,254,98,242,102,242,106,248,112,242,115,248,118,248,120,245,122,
+  245,193,249,194,249,195,249,196,249,197,249,198,249,211,254,212,
+  254,213,254,214,254,215,254,217,254,225,242,226,242,227,242,228,
+  242,229,242,230,242,233,248,234,242,235,248,236,248,238,248,243,
+  242,244,242,245,242,246,242,247,242,249,242,250,248,251,248,252,
+  248,253,248,254,245,0,1,251,0,2,249,0,3,242,0,4,249,0,5,242,0,6,
+  249,0,7,242,0,21,248,0,25,242,0,27,242,0,29,242,0,49,248,0,78,254,
+  0,79,242,0,82,254,0,83,242,0,87,248,0,89,248,0,91,248,0,109,248,
+  0,113,248,0,115,248,0,117,248,13,45,253,47,253,66,250,193,250,194,
+  250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,69,45,
+  235,46,248,47,235,59,246,60,245,66,247,80,252,98,238,102,238,106,
+  245,112,238,118,245,193,247,194,247,195,247,196,247,197,247,198,
+  247,211,252,212,252,213,252,214,252,215,252,217,252,225,238,226,
+  238,227,238,228,238,229,238,230,238,233,245,234,238,235,238,236,
+  245,237,251,238,245,239,251,240,251,243,238,244,238,245,238,246,
+  238,247,238,249,238,250,245,251,245,252,245,253,245,0,2,247,0,3,
+  238,0,4,247,0,5,238,0,6,247,0,7,238,0,21,245,0,25,238,0,27,238,0,
+  29,238,0,45,251,0,49,245,0,78,252,0,79,238,0,82,252,0,83,238,0,109,
+  245,0,113,245,0,115,245,0,117,245,68,45,242,46,251,47,242,59,246,
+  60,246,66,247,80,253,98,242,102,242,106,248,112,242,118,248,122,
+  245,193,247,194,247,195,247,196,247,197,247,198,247,211,253,212,
+  253,213,253,214,253,215,253,217,253,225,242,226,242,227,242,228,
+  242,229,242,230,242,233,248,234,242,235,242,236,248,238,248,243,
+  242,244,242,245,242,246,242,247,242,249,242,250,248,251,248,252,
+  248,253,248,254,245,0,1,245,0,2,247,0,3,242,0,4,247,0,5,242,0,6,
+  247,0,7,242,0,21,248,0,25,242,0,27,242,0,29,242,0,49,248,0,78,253,
+  0,79,242,0,82,253,0,83,242,0,109,248,0,113,248,0,115,248,0,117,248,
+  69,45,242,46,245,47,242,59,246,60,246,66,249,80,254,98,242,102,242,
+  106,245,112,242,118,242,193,249,194,249,195,249,196,249,197,249,
+  198,249,211,254,212,254,213,254,214,254,215,254,217,254,225,242,
+  226,242,227,242,228,242,229,242,230,242,233,248,234,242,235,242,
+  236,248,237,251,238,245,239,251,240,251,243,242,244,242,245,242,
+  246,242,247,242,249,242,250,242,251,242,252,242,253,242,0,2,249,
+  0,3,242,0,4,249,0,5,242,0,6,249,0,7,242,0,21,248,0,25,242,0,27,242,
+  0,29,242,0,45,251,0,49,245,0,78,254,0,79,242,0,82,254,0,83,242,0,
+  109,242,0,113,242,0,115,242,0,117,242,4,104,255,0,33,255,0,37,255,
+  11,47,250,118,254,250,254,251,254,252,254,253,254,0,109,254,0,113,
+  254,0,115,254,0,117,254,4,105,254,108,254,0,57,254,12,45,255,47,
+  254,104,250,119,254,120,254,121,254,122,252,254,252,0,1,252,0,33,
+  250,0,37,250,8,45,255,47,254,103,254,106,254,0,49,254,0,51,247,1,
+  32,25,15,15,45,255,47,254,102,255,104,255,233,255,234,255,235,255,
+  236,255,0,21,255,0,25,255,0,27,255,0,29,255,0,33,255,0,37,255,22,
+  102,255,112,255,122,255,233,255,234,255,235,255,236,255,243,255,
+  244,255,245,255,246,255,247,255,249,255,254,255,0,1,255,0,21,255,
+  0,25,255,0,27,255,0,29,255,0,79,255,0,83,255,2,119,250,5,104,255,
+  119,255,0,33,255,0,37,255,47,45,238,46,254,47,238,98,254,100,251,
+  101,251,102,251,104,251,112,249,114,251,116,255,225,254,226,254,
+  227,254,228,254,229,254,230,254,232,251,233,251,234,251,235,251,
+  236,251,243,249,244,249,245,249,246,249,247,249,249,249,0,3,254,
+  0,5,254,0,7,254,0,9,251,0,15,251,0,19,251,0,21,251,0,25,251,0,27,
+  251,0,29,251,0,33,251,0,37,251,0,79,249,0,83,249,0,93,255,0,97,255,
+  0,99,255,1,2,25,255,3,45,245,47,245,3,45,245,47,245,3,45,248,47,
+  248,50,68,252,72,251,80,250,82,250,85,251,86,249,87,239,88,241,90,
+  248,118,254,119,248,120,248,122,248,200,252,211,250,212,250,213,
+  250,214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,
+  248,250,254,251,254,252,254,253,254,254,248,0,1,248,0,8,252,0,14,
+  252,0,32,251,0,36,251,0,78,250,0,82,250,0,100,251,0,102,251,0,108,
+  249,0,109,254,0,112,249,0,113,254,0,114,249,0,115,254,0,116,249,
+  0,117,254,0,122,248,1,32,25,251,50,68,252,72,251,80,250,82,250,85,
+  251,86,249,87,239,88,241,90,248,118,254,119,248,120,248,122,248,
+  200,252,211,250,212,250,213,250,214,250,215,250,217,250,218,249,
+  219,249,220,249,221,249,222,248,250,254,251,254,252,254,253,254,
+  254,248,0,1,248,0,8,252,0,14,252,0,32,251,0,36,251,0,78,250,0,82,
+  250,0,100,251,0,102,251,0,108,249,0,109,254,0,112,249,0,113,254,
+  0,114,249,0,115,254,0,116,249,0,117,254,0,122,248,1,32,25,251,50,
+  68,252,72,251,80,250,82,250,85,251,86,249,87,239,88,241,90,248,118,
+  254,119,248,120,248,122,248,200,252,211,250,212,250,213,250,214,
+  250,215,250,217,250,218,249,219,249,220,249,221,249,222,248,250,
+  254,251,254,252,254,253,254,254,248,0,1,248,0,8,252,0,14,252,0,32,
+  251,0,36,251,0,78,250,0,82,250,0,100,251,0,102,251,0,108,249,0,109,
+  254,0,112,249,0,113,254,0,114,249,0,115,254,0,116,249,0,117,254,
+  0,122,248,1,32,25,251,50,68,252,72,251,80,250,82,250,85,251,86,249,
+  87,239,88,241,90,248,118,254,119,248,120,248,122,248,200,252,211,
+  250,212,250,213,250,214,250,215,250,217,250,218,249,219,249,220,
+  249,221,249,222,248,250,254,251,254,252,254,253,254,254,248,0,1,
+  248,0,8,252,0,14,252,0,32,251,0,36,251,0,78,250,0,82,250,0,100,251,
+  0,102,251,0,108,249,0,109,254,0,112,249,0,113,254,0,114,249,0,115,
+  254,0,116,249,0,117,254,0,122,248,1,32,25,251,50,68,252,72,251,80,
+  250,82,250,85,251,86,249,87,239,88,241,90,248,118,254,119,248,120,
+  248,122,248,200,252,211,250,212,250,213,250,214,250,215,250,217,
+  250,218,249,219,249,220,249,221,249,222,248,250,254,251,254,252,
+  254,253,254,254,248,0,1,248,0,8,252,0,14,252,0,32,251,0,36,251,0,
+  78,250,0,82,250,0,100,251,0,102,251,0,108,249,0,109,254,0,112,249,
+  0,113,254,0,114,249,0,115,254,0,116,249,0,117,254,0,122,248,1,32,
+  25,251,50,68,252,72,251,80,250,82,250,85,251,86,249,87,239,88,241,
+  90,248,118,254,119,248,120,248,122,248,200,252,211,250,212,250,213,
+  250,214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,
+  248,250,254,251,254,252,254,253,254,254,248,0,1,248,0,8,252,0,14,
+  252,0,32,251,0,36,251,0,78,250,0,82,250,0,100,251,0,102,251,0,108,
+  249,0,109,254,0,112,249,0,113,254,0,114,249,0,115,254,0,116,249,
+  0,117,254,0,122,248,1,32,25,251,11,66,252,193,252,194,252,195,252,
+  196,252,197,252,198,252,0,2,252,0,4,252,0,6,252,20,66,248,85,250,
+  87,249,88,249,89,250,90,249,193,248,194,248,195,248,196,248,197,
+  248,198,248,222,249,0,2,248,0,4,248,0,6,248,0,100,250,0,102,250,
+  0,122,249,20,66,248,85,250,87,249,88,249,89,250,90,249,193,248,194,
+  248,195,248,196,248,197,248,198,248,222,249,0,2,248,0,4,248,0,6,
+  248,0,100,250,0,102,250,0,122,249,20,66,248,85,250,87,249,88,249,
+  89,250,90,249,193,248,194,248,195,248,196,248,197,248,198,248,222,
+  249,0,2,248,0,4,248,0,6,248,0,100,250,0,102,250,0,122,249,20,66,
+  248,85,250,87,249,88,249,89,250,90,249,193,248,194,248,195,248,196,
+  248,197,248,198,248,222,249,0,2,248,0,4,248,0,6,248,0,100,250,0,
+  102,250,0,122,249,20,66,248,85,250,87,249,88,249,89,250,90,249,193,
+  248,194,248,195,248,196,248,197,248,198,248,222,249,0,2,248,0,4,
+  248,0,6,248,0,100,250,0,102,250,0,122,249,20,66,248,85,250,87,249,
+  88,249,89,250,90,249,193,248,194,248,195,248,196,248,197,248,198,
+  248,222,249,0,2,248,0,4,248,0,6,248,0,100,250,0,102,250,0,122,249,
+  13,45,253,47,253,66,250,193,250,194,250,195,250,196,250,197,250,
+  198,250,0,2,250,0,4,250,0,6,250,13,45,253,47,253,66,250,193,250,
+  194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,
+  13,45,253,47,253,66,250,193,250,194,250,195,250,196,250,197,250,
+  198,250,0,2,250,0,4,250,0,6,250,13,45,253,47,253,66,250,193,250,
+  194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,
+  69,45,242,46,245,47,242,59,246,60,246,66,249,80,254,98,242,102,242,
+  106,245,112,242,118,242,193,249,194,249,195,249,196,249,197,249,
+  198,249,211,254,212,254,213,254,214,254,215,254,217,254,225,242,
+  226,242,227,242,228,242,229,242,230,242,233,248,234,242,235,242,
+  236,248,237,251,238,245,239,251,240,251,243,242,244,242,245,242,
+  246,242,247,242,249,242,250,242,251,242,252,242,253,242,0,2,249,
+  0,3,242,0,4,249,0,5,242,0,6,249,0,7,242,0,21,248,0,25,242,0,27,242,
+  0,29,242,0,45,251,0,49,245,0,78,254,0,79,242,0,82,254,0,83,242,0,
+  109,242,0,113,242,0,115,242,0,117,242,4,104,255,0,33,255,0,37,255,
+  4,104,255,0,33,255,0,37,255,4,104,255,0,33,255,0,37,255,4,104,255,
+  0,33,255,0,37,255,4,104,255,0,33,255,0,37,255,4,104,255,0,33,255,
+  0,37,255,4,105,254,108,254,0,57,254,12,45,255,47,254,104,250,119,
+  254,120,254,121,254,122,252,254,252,0,1,252,0,33,250,0,37,250,12,
+  45,255,47,254,104,250,119,254,120,254,121,254,122,252,254,252,0,
+  1,252,0,33,250,0,37,250,12,45,255,47,254,104,250,119,254,120,254,
+  121,254,122,252,254,252,0,1,252,0,33,250,0,37,250,12,45,255,47,254,
+  104,250,119,254,120,254,121,254,122,252,254,252,0,1,252,0,33,250,
+  0,37,250,2,119,250,5,104,255,119,255,0,33,255,0,37,255,5,104,255,
+  119,255,0,33,255,0,37,255,5,104,255,119,255,0,33,255,0,37,255,5,
+  104,255,119,255,0,33,255,0,37,255,5,104,255,119,255,0,33,255,0,37,
+  255,5,104,255,119,255,0,33,255,0,37,255,3,45,248,47,248,3,45,248,
+  47,248,50,68,252,72,251,80,250,82,250,85,251,86,249,87,239,88,241,
+  90,248,118,254,119,248,120,248,122,248,200,252,211,250,212,250,213,
+  250,214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,
+  248,250,254,251,254,252,254,253,254,254,248,0,1,248,0,8,252,0,14,
+  252,0,32,251,0,36,251,0,78,250,0,82,250,0,100,251,0,102,251,0,108,
+  249,0,109,254,0,112,249,0,113,254,0,114,249,0,115,254,0,116,249,
+  0,117,254,0,122,248,1,32,25,251,4,104,255,0,33,255,0,37,255,50,68,
+  252,72,251,80,250,82,250,85,251,86,249,87,239,88,241,90,248,118,
+  254,119,248,120,248,122,248,200,252,211,250,212,250,213,250,214,
+  250,215,250,217,250,218,249,219,249,220,249,221,249,222,248,250,
+  254,251,254,252,254,253,254,254,248,0,1,248,0,8,252,0,14,252,0,32,
+  251,0,36,251,0,78,250,0,82,250,0,100,251,0,102,251,0,108,249,0,109,
+  254,0,112,249,0,113,254,0,114,249,0,115,254,0,116,249,0,117,254,
+  0,122,248,1,32,25,251,4,104,255,0,33,255,0,37,255,50,68,252,72,251,
+  80,250,82,250,85,251,86,249,87,239,88,241,90,248,118,254,119,248,
+  120,248,122,248,200,252,211,250,212,250,213,250,214,250,215,250,
+  217,250,218,249,219,249,220,249,221,249,222,248,250,254,251,254,
+  252,254,253,254,254,248,0,1,248,0,8,252,0,14,252,0,32,251,0,36,251,
+  0,78,250,0,82,250,0,100,251,0,102,251,0,108,249,0,109,254,0,112,
+  249,0,113,254,0,114,249,0,115,254,0,116,249,0,117,254,0,122,248,
+  1,32,25,251,4,104,255,0,33,255,0,37,255,4,105,254,108,254,0,57,254,
+  4,105,254,108,254,0,57,254,16,66,251,87,250,88,250,90,250,193,251,
+  194,251,195,251,196,251,197,251,198,251,222,250,0,2,251,0,4,251,
+  0,6,251,0,122,250,16,66,251,87,250,88,250,90,250,193,251,194,251,
+  195,251,196,251,197,251,198,251,222,250,0,2,251,0,4,251,0,6,251,
+  0,122,250,12,45,255,47,254,104,250,119,254,120,254,121,254,122,252,
+  254,252,0,1,252,0,33,250,0,37,250,12,45,255,47,254,104,250,119,254,
+  120,254,121,254,122,252,254,252,0,1,252,0,33,250,0,37,250,12,45,
+  255,47,254,104,250,119,254,120,254,121,254,122,252,254,252,0,1,252,
+  0,33,250,0,37,250,12,45,255,47,254,104,250,119,254,120,254,121,254,
+  122,252,254,252,0,1,252,0,33,250,0,37,250,15,45,255,47,254,102,255,
+  104,255,233,255,234,255,235,255,236,255,0,21,255,0,25,255,0,27,255,
+  0,29,255,0,33,255,0,37,255,15,45,255,47,254,102,255,104,255,233,
+  255,234,255,235,255,236,255,0,21,255,0,25,255,0,27,255,0,29,255,
+  0,33,255,0,37,255,40,80,249,102,251,112,250,118,250,122,250,211,
+  249,212,249,213,249,214,249,215,249,217,249,233,251,234,251,235,
+  251,236,251,243,250,244,250,245,250,246,250,247,250,249,250,250,
+  250,251,250,252,250,253,250,254,250,0,1,250,0,21,251,0,25,251,0,
+  27,251,0,29,251,0,78,249,0,79,250,0,82,249,0,83,250,0,109,250,0,
+  113,250,0,115,250,0,117,250,22,102,255,112,255,122,255,233,255,234,
+  255,235,255,236,255,243,255,244,255,245,255,246,255,247,255,249,
+  255,254,255,0,1,255,0,21,255,0,25,255,0,27,255,0,29,255,0,79,255,
+  0,83,255,13,85,254,87,248,88,248,90,254,122,252,222,254,254,252,
+  0,1,252,0,100,254,0,102,254,0,122,254,1,32,25,251,13,85,254,87,248,
+  88,248,90,254,122,252,222,254,254,252,0,1,252,0,100,254,0,102,254,
+  0,122,254,1,32,25,251,13,85,254,87,248,88,248,90,254,122,252,222,
+  254,254,252,0,1,252,0,100,254,0,102,254,0,122,254,1,32,25,251,11,
+  66,252,193,252,194,252,195,252,196,252,197,252,198,252,0,2,252,0,
+  4,252,0,6,252,2,119,250,11,66,252,193,252,194,252,195,252,196,252,
+  197,252,198,252,0,2,252,0,4,252,0,6,252,2,119,250,11,66,252,193,
+  252,194,252,195,252,196,252,197,252,198,252,0,2,252,0,4,252,0,6,
+  252,2,119,250,20,66,248,85,250,87,249,88,249,89,250,90,249,193,248,
+  194,248,195,248,196,248,197,248,198,248,222,249,0,2,248,0,4,248,
+  0,6,248,0,100,250,0,102,250,0,122,249,5,104,255,119,255,0,33,255,
+  0,37,255,20,66,248,85,250,87,249,88,249,89,250,90,249,193,248,194,
+  248,195,248,196,248,197,248,198,248,222,249,0,2,248,0,4,248,0,6,
+  248,0,100,250,0,102,250,0,122,249,5,104,255,119,255,0,33,255,0,37,
+  255,24,80,250,86,250,87,254,88,254,90,254,211,250,212,250,213,250,
+  214,250,215,250,217,250,218,250,219,250,220,250,221,250,222,254,
+  0,78,250,0,82,250,0,108,250,0,112,250,0,114,250,0,116,250,0,122,
+  254,47,45,238,46,254,47,238,98,254,100,251,101,251,102,251,104,251,
+  112,249,114,251,116,255,225,254,226,254,227,254,228,254,229,254,
+  230,254,232,251,233,251,234,251,235,251,236,251,243,249,244,249,
+  245,249,246,249,247,249,249,249,0,3,254,0,5,254,0,7,254,0,9,251,
+  0,15,251,0,19,251,0,21,251,0,25,251,0,27,251,0,29,251,0,33,251,0,
+  37,251,0,79,249,0,83,249,0,93,255,0,97,255,0,99,255,1,2,25,255,24,
+  80,250,86,250,87,254,88,254,90,254,211,250,212,250,213,250,214,250,
+  215,250,217,250,218,250,219,250,220,250,221,250,222,254,0,78,250,
+  0,82,250,0,108,250,0,112,250,0,114,250,0,116,250,0,122,254,47,45,
+  238,46,254,47,238,98,254,100,251,101,251,102,251,104,251,112,249,
+  114,251,116,255,225,254,226,254,227,254,228,254,229,254,230,254,
+  232,251,233,251,234,251,235,251,236,251,243,249,244,249,245,249,
+  246,249,247,249,249,249,0,3,254,0,5,254,0,7,254,0,9,251,0,15,251,
+  0,19,251,0,21,251,0,25,251,0,27,251,0,29,251,0,33,251,0,37,251,0,
+  79,249,0,83,249,0,93,255,0,97,255,0,99,255,1,2,25,255,24,80,250,
+  86,250,87,254,88,254,90,254,211,250,212,250,213,250,214,250,215,
+  250,217,250,218,250,219,250,220,250,221,250,222,254,0,78,250,0,82,
+  250,0,108,250,0,112,250,0,114,250,0,116,250,0,122,254,47,45,238,
+  46,254,47,238,98,254,100,251,101,251,102,251,104,251,112,249,114,
+  251,116,255,225,254,226,254,227,254,228,254,229,254,230,254,232,
+  251,233,251,234,251,235,251,236,251,243,249,244,249,245,249,246,
+  249,247,249,249,249,0,3,254,0,5,254,0,7,254,0,9,251,0,15,251,0,19,
+  251,0,21,251,0,25,251,0,27,251,0,29,251,0,33,251,0,37,251,0,79,249,
+  0,83,249,0,93,255,0,97,255,0,99,255,1,2,25,255,73,45,245,46,245,
+  47,245,59,248,60,246,66,249,80,254,98,242,102,242,106,248,112,242,
+  115,248,118,248,120,245,122,245,193,249,194,249,195,249,196,249,
+  197,249,198,249,211,254,212,254,213,254,214,254,215,254,217,254,
+  225,242,226,242,227,242,228,242,229,242,230,242,233,248,234,242,
+  235,248,236,248,238,248,243,242,244,242,245,242,246,242,247,242,
+  249,242,250,248,251,248,252,248,253,248,254,245,0,1,251,0,2,249,
+  0,3,242,0,4,249,0,5,242,0,6,249,0,7,242,0,21,248,0,25,242,0,27,242,
+  0,29,242,0,49,248,0,78,254,0,79,242,0,82,254,0,83,242,0,87,248,0,
+  89,248,0,91,248,0,109,248,0,113,248,0,115,248,0,117,248,73,45,245,
+  46,245,47,245,59,248,60,246,66,249,80,254,98,242,102,242,106,248,
+  112,242,115,248,118,248,120,245,122,245,193,249,194,249,195,249,
+  196,249,197,249,198,249,211,254,212,254,213,254,214,254,215,254,
+  217,254,225,242,226,242,227,242,228,242,229,242,230,242,233,248,
+  234,242,235,248,236,248,238,248,243,242,244,242,245,242,246,242,
+  247,242,249,242,250,248,251,248,252,248,253,248,254,245,0,1,251,
+  0,2,249,0,3,242,0,4,249,0,5,242,0,6,249,0,7,242,0,21,248,0,25,242,
+  0,27,242,0,29,242,0,49,248,0,78,254,0,79,242,0,82,254,0,83,242,0,
+  87,248,0,89,248,0,91,248,0,109,248,0,113,248,0,115,248,0,117,248,
+  13,45,253,47,253,66,250,193,250,194,250,195,250,196,250,197,250,
+  198,250,0,2,250,0,4,250,0,6,250,13,45,253,47,253,66,250,193,250,
+  194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,
+  13,45,253,47,253,66,250,193,250,194,250,195,250,196,250,197,250,
+  198,250,0,2,250,0,4,250,0,6,250,13,45,253,47,253,66,250,193,250,
+  194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,
+  69,45,242,46,245,47,242,59,246,60,246,66,249,80,254,98,242,102,242,
+  106,245,112,242,118,242,193,249,194,249,195,249,196,249,197,249,
+  198,249,211,254,212,254,213,254,214,254,215,254,217,254,225,242,
+  226,242,227,242,228,242,229,242,230,242,233,248,234,242,235,242,
+  236,248,237,251,238,245,239,251,240,251,243,242,244,242,245,242,
+  246,242,247,242,249,242,250,242,251,242,252,242,253,242,0,2,249,
+  0,3,242,0,4,249,0,5,242,0,6,249,0,7,242,0,21,248,0,25,242,0,27,242,
+  0,29,242,0,45,251,0,49,245,0,78,254,0,79,242,0,82,254,0,83,242,0,
+  109,242,0,113,242,0,115,242,0,117,242,2,1,32,24,238,17,33,238,101,
+  253,115,253,116,250,117,252,119,255,0,19,253,0,87,253,0,89,253,0,
+  91,253,0,93,250,0,97,250,0,99,250,0,101,252,1,2,25,250,1,32,25,238
+};
+static afm_cuint16 afm_Times_Italic_highchars_index[] = { /* 220 */
+  161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+  178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+  194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+  210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+  226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+  242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+  258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+  280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+  313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+  336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+  354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+  380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+  8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+  8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+  64257,64258
+};
+static afm_cunicode afm_Times_Italic_ligatures[] = { /* 3 */
+  102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Times-Roman */
+/* FullName: Times Roman */
+/* FamilyName: Times */
+static afm_cuint8 afm_Times_Roman_widths[] = { /* 315 */
+  42,56,68,83,83,139,130,30,56,56,83,94,42,56,42,46,83,83,83,83,83,
+  83,83,83,83,83,46,46,94,94,94,74,154,120,111,111,120,102,93,120,
+  120,56,65,120,102,148,120,120,93,120,111,93,102,120,120,157,120,
+  120,102,56,46,56,78,83,56,74,83,74,83,74,56,83,83,46,46,83,46,130,
+  83,83,83,83,56,65,46,83,83,120,83,83,74,80,33,80,90,56,83,83,83,
+  83,33,83,56,127,46,83,94,127,56,67,94,50,50,56,83,76,42,56,50,52,
+  83,125,125,125,74,120,120,120,120,120,120,148,111,102,102,102,102,
+  56,56,56,56,120,120,120,120,120,120,120,94,120,120,120,120,120,120,
+  93,83,74,74,74,74,74,74,111,74,74,74,74,74,46,46,46,46,83,83,83,
+  83,83,83,83,94,83,83,83,83,83,83,83,83,120,74,120,74,120,74,111,
+  74,111,74,120,98,120,83,102,74,102,74,102,74,102,74,120,83,120,83,
+  56,46,56,46,56,46,120,83,102,46,102,46,102,57,102,46,120,83,120,
+  83,120,83,120,83,120,83,148,120,111,56,111,56,111,56,93,65,93,65,
+  93,65,102,46,102,54,120,83,120,83,120,83,120,83,120,102,74,102,74,
+  102,74,83,93,65,56,56,56,56,56,56,56,56,83,167,56,56,56,74,74,74,
+  83,83,58,167,167,56,56,28,83,163,79,102,100,94,76,92,92,92,79,42,
+  93,93
+};
+static afm_sint16 afm_Times_Roman_kerning_index[] = { /* 315 */
+  1,0,0,0,0,0,0,0,0,0,0,0,44,0,53,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,62,159,0,205,0,240,0,0,0,311,335,427,0,458,482,527,578,601,0,
+  663,831,855,1020,0,1175,0,0,0,0,0,0,0,1323,1328,1355,0,1363,1385,
+  0,0,1422,0,1425,1475,0,1478,1488,1500,0,1508,0,0,0,1523,1593,1641,
+  1664,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,0,0,0,1669,1766,1863,1960,2057,2154,0,0,0,0,0,0,0,0,0,0,0,2251,
+  2275,2320,2365,2410,2455,0,2500,2545,2569,2593,2617,2641,0,0,2789,
+  2794,2799,2804,2809,2814,0,2819,2827,2849,2871,2893,2915,2918,2921,
+  2924,0,2927,2937,2949,2961,2973,2985,0,2997,0,0,0,0,3009,0,3014,
+  3019,3116,3121,3218,3223,3320,0,3325,0,3333,3341,0,3376,0,0,3411,
+  0,3433,0,3455,0,3477,0,0,0,0,0,3499,0,3502,0,0,3505,3597,3647,3678,
+  3681,3712,3715,0,3727,3758,3761,3785,3795,3819,3829,3853,3863,3908,
+  3920,3965,0,0,3977,4039,4054,4116,4131,4193,0,0,0,0,0,0,4208,0,4376,
+  0,4544,0,4568,0,4592,0,4616,0,4640,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,0,0,4788,4816,0,4872,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,0,0
+};
+static afm_cuint8 afm_Times_Roman_kerning_data[] = { /* 4896 */
+  42,19,66,248,85,254,87,249,88,252,90,242,193,248,194,248,195,248,
+  196,248,197,248,198,248,222,242,0,2,248,0,4,248,0,6,248,0,100,254,
+  0,102,254,0,122,242,3,1,32,25,245,1,32,29,245,3,1,32,25,245,1,32,
+  29,245,41,68,250,72,250,80,248,82,248,85,238,86,248,87,234,88,242,
+  90,239,119,245,120,242,122,242,200,250,211,248,212,248,213,248,214,
+  248,215,248,217,248,218,248,219,248,220,248,221,248,222,239,254,
+  242,0,1,242,0,8,250,0,14,250,0,32,250,0,36,250,0,78,248,0,82,248,
+  0,100,238,0,102,238,0,108,248,0,112,248,0,114,248,0,116,248,0,122,
+  239,1,32,25,238,20,66,251,86,255,193,251,194,251,195,251,196,251,
+  197,251,198,251,218,255,219,255,220,255,221,255,0,2,251,0,4,251,
+  0,6,251,0,108,255,0,112,255,0,114,255,0,116,255,16,66,250,87,250,
+  88,252,90,248,193,250,194,250,195,250,196,250,197,250,198,250,222,
+  248,0,2,250,0,4,250,0,6,250,0,122,248,32,45,244,47,244,66,245,98,
+  254,112,254,193,245,194,245,195,245,196,245,197,245,198,245,225,
+  254,226,254,227,254,228,254,229,254,230,254,243,254,244,254,245,
+  254,246,254,247,254,249,254,0,2,245,0,3,254,0,4,245,0,5,254,0,6,
+  245,0,7,254,0,79,254,0,83,254,11,66,247,193,247,194,247,195,247,
+  196,247,197,247,198,247,0,2,247,0,4,247,0,6,247,40,80,252,102,253,
+  112,251,118,254,122,253,211,252,212,252,213,252,214,252,215,252,
+  217,252,233,253,234,253,235,253,236,253,243,251,244,251,245,251,
+  246,251,247,251,249,251,250,254,251,254,252,254,253,254,254,253,
+  0,1,253,0,21,253,0,25,253,0,27,253,0,29,253,0,78,252,0,79,251,0,
+  82,252,0,83,251,0,109,254,0,113,254,0,115,254,0,117,254,13,85,242,
+  87,240,88,245,90,240,122,248,222,240,254,248,0,1,248,0,100,242,0,
+  102,242,0,122,240,1,32,25,242,11,66,251,193,251,194,251,195,251,
+  196,251,197,251,198,251,0,2,251,0,4,251,0,6,251,20,66,251,85,250,
+  87,249,88,251,89,250,90,249,193,251,194,251,195,251,196,251,197,
+  251,198,251,222,249,0,2,251,0,4,251,0,6,251,0,100,250,0,102,250,
+  0,122,249,23,45,238,47,238,66,242,98,254,193,242,194,242,195,242,
+  196,242,197,242,198,242,225,254,226,254,227,254,228,254,229,254,
+  230,254,0,2,242,0,3,254,0,4,242,0,5,254,0,6,242,0,7,254,10,86,255,
+  218,255,219,255,220,255,221,255,0,108,255,0,112,255,0,114,255,0,
+  116,255,27,80,250,85,247,86,250,87,244,88,248,90,246,211,250,212,
+  250,213,250,214,250,215,250,217,250,218,250,219,250,220,250,221,
+  250,222,246,0,78,250,0,82,250,0,100,247,0,102,247,0,108,250,0,112,
+  250,0,114,250,0,116,250,0,122,246,73,45,245,46,242,47,245,59,249,
+  60,248,66,241,80,254,98,244,102,245,106,251,112,244,115,251,118,
+  249,120,244,122,244,193,241,194,241,195,241,196,241,197,241,198,
+  241,211,254,212,254,213,254,214,254,215,254,217,254,225,250,226,
+  244,227,244,228,250,229,250,230,244,233,245,234,245,235,245,236,
+  252,238,251,243,244,244,244,245,244,246,244,247,244,249,244,250,
+  249,251,249,252,249,253,249,254,244,0,1,244,0,2,241,0,3,250,0,4,
+  241,0,5,244,0,6,241,0,7,244,0,21,252,0,25,245,0,27,245,0,29,245,
+  0,49,251,0,78,254,0,79,244,0,82,254,0,83,244,0,87,251,0,89,251,0,
+  91,251,0,109,249,0,113,249,0,115,249,0,117,249,11,66,250,193,250,
+  194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,
+  72,45,235,46,240,47,235,59,245,60,245,66,234,72,254,80,250,98,238,
+  102,238,106,247,112,235,118,244,193,234,194,234,195,234,196,234,
+  197,234,198,234,211,250,212,250,213,250,214,250,215,250,217,250,
+  225,245,226,238,227,245,228,245,229,245,230,238,233,245,234,238,
+  235,245,236,245,237,254,238,247,239,254,240,254,243,242,244,235,
+  245,235,246,242,247,242,249,235,250,244,251,244,252,244,253,244,
+  0,2,234,0,3,245,0,4,234,0,5,238,0,6,234,0,7,238,0,21,245,0,25,238,
+  0,27,238,0,29,245,0,32,254,0,36,254,0,45,254,0,49,247,0,78,250,0,
+  79,242,0,82,250,0,83,235,0,109,244,0,113,244,0,115,244,0,117,244,
+  68,45,242,46,246,47,242,59,251,60,251,66,237,80,255,98,244,102,244,
+  106,250,112,244,118,249,122,245,193,237,194,237,195,237,196,237,
+  197,237,198,237,211,255,212,255,213,255,214,255,215,255,217,255,
+  225,244,226,244,227,244,228,244,229,244,230,244,233,250,234,244,
+  235,244,236,250,238,250,243,244,244,244,245,244,246,244,247,244,
+  249,244,250,249,251,249,252,249,253,249,254,245,0,1,245,0,2,237,
+  0,3,244,0,4,237,0,5,244,0,6,237,0,7,244,0,21,250,0,25,244,0,27,244,
+  0,29,244,0,49,250,0,78,255,0,79,244,0,82,255,0,83,244,0,109,249,
+  0,113,249,0,115,249,0,117,249,65,45,235,46,238,47,235,59,242,60,
+  242,66,237,80,252,98,240,102,240,106,248,112,239,118,238,193,237,
+  194,237,195,237,196,237,197,237,198,237,211,252,212,252,213,252,
+  214,252,215,252,217,252,225,247,226,240,227,240,228,247,229,247,
+  230,240,233,247,234,240,235,240,236,247,238,248,243,245,244,239,
+  245,239,246,245,247,245,249,239,250,245,251,238,252,238,253,245,
+  0,2,237,0,3,247,0,4,237,0,5,240,0,6,237,0,7,240,0,21,247,0,25,240,
+  0,27,240,0,29,240,0,49,248,0,78,252,0,79,245,0,82,252,0,83,239,0,
+  109,245,0,113,238,0,115,238,0,117,238,3,119,254,120,254,12,47,250,
+  118,254,119,254,250,254,251,254,252,254,253,254,0,109,254,0,113,
+  254,0,115,254,0,117,254,4,122,254,254,254,0,1,254,10,104,254,119,
+  253,120,253,121,254,122,254,254,254,0,1,254,0,33,254,0,37,254,16,
+  98,255,103,253,106,254,225,255,226,255,227,255,228,255,229,255,230,
+  255,238,254,0,3,255,0,5,255,0,7,255,0,51,249,1,32,25,9,2,119,253,
+  22,102,255,112,255,122,254,233,255,234,255,235,255,236,255,243,255,
+  244,255,245,255,246,255,247,255,249,255,254,254,0,1,254,0,21,255,
+  0,25,255,0,27,255,0,29,255,0,79,255,0,83,255,2,120,255,5,119,250,
+  122,254,254,254,0,1,254,6,119,254,120,253,122,255,254,255,0,1,255,
+  4,122,255,254,255,0,1,255,7,45,250,46,254,47,248,104,254,0,33,254,
+  0,37,254,31,45,246,47,246,98,253,102,254,112,254,225,253,226,253,
+  227,253,228,253,229,253,230,253,233,254,234,254,235,254,236,254,
+  243,254,244,254,245,254,246,254,247,254,249,254,0,3,253,0,5,253,
+  0,7,253,0,21,254,0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,22,
+  45,246,47,246,98,255,112,255,225,255,226,255,227,255,228,255,229,
+  255,230,255,243,255,244,255,245,255,246,255,247,255,249,255,0,3,
+  255,0,5,255,0,7,255,0,79,255,0,83,255,10,102,254,233,254,234,254,
+  235,254,236,254,0,21,254,0,25,254,0,27,254,0,29,254,3,45,246,47,
+  246,41,68,250,72,250,80,248,82,248,85,238,86,248,87,234,88,242,90,
+  239,119,245,120,242,122,242,200,250,211,248,212,248,213,248,214,
+  248,215,248,217,248,218,248,219,248,220,248,221,248,222,239,254,
+  242,0,1,242,0,8,250,0,14,250,0,32,250,0,36,250,0,78,248,0,82,248,
+  0,100,238,0,102,238,0,108,248,0,112,248,0,114,248,0,116,248,0,122,
+  239,1,32,25,238,41,68,250,72,250,80,248,82,248,85,238,86,248,87,
+  234,88,242,90,239,119,245,120,242,122,242,200,250,211,248,212,248,
+  213,248,214,248,215,248,217,248,218,248,219,248,220,248,221,248,
+  222,239,254,242,0,1,242,0,8,250,0,14,250,0,32,250,0,36,250,0,78,
+  248,0,82,248,0,100,238,0,102,238,0,108,248,0,112,248,0,114,248,0,
+  116,248,0,122,239,1,32,25,238,41,68,250,72,250,80,248,82,248,85,
+  238,86,248,87,234,88,242,90,239,119,245,120,242,122,242,200,250,
+  211,248,212,248,213,248,214,248,215,248,217,248,218,248,219,248,
+  220,248,221,248,222,239,254,242,0,1,242,0,8,250,0,14,250,0,32,250,
+  0,36,250,0,78,248,0,82,248,0,100,238,0,102,238,0,108,248,0,112,248,
+  0,114,248,0,116,248,0,122,239,1,32,25,238,41,68,250,72,250,80,248,
+  82,248,85,238,86,248,87,234,88,242,90,239,119,245,120,242,122,242,
+  200,250,211,248,212,248,213,248,214,248,215,248,217,248,218,248,
+  219,248,220,248,221,248,222,239,254,242,0,1,242,0,8,250,0,14,250,
+  0,32,250,0,36,250,0,78,248,0,82,248,0,100,238,0,102,238,0,108,248,
+  0,112,248,0,114,248,0,116,248,0,122,239,1,32,25,238,41,68,250,72,
+  250,80,248,82,248,85,238,86,248,87,234,88,242,90,239,119,245,120,
+  242,122,242,200,250,211,248,212,248,213,248,214,248,215,248,217,
+  248,218,248,219,248,220,248,221,248,222,239,254,242,0,1,242,0,8,
+  250,0,14,250,0,32,250,0,36,250,0,78,248,0,82,248,0,100,238,0,102,
+  238,0,108,248,0,112,248,0,114,248,0,116,248,0,122,239,1,32,25,238,
+  41,68,250,72,250,80,248,82,248,85,238,86,248,87,234,88,242,90,239,
+  119,245,120,242,122,242,200,250,211,248,212,248,213,248,214,248,
+  215,248,217,248,218,248,219,248,220,248,221,248,222,239,254,242,
+  0,1,242,0,8,250,0,14,250,0,32,250,0,36,250,0,78,248,0,82,248,0,100,
+  238,0,102,238,0,108,248,0,112,248,0,114,248,0,116,248,0,122,239,
+  1,32,25,238,11,66,251,193,251,194,251,195,251,196,251,197,251,198,
+  251,0,2,251,0,4,251,0,6,251,20,66,251,85,250,87,249,88,251,89,250,
+  90,249,193,251,194,251,195,251,196,251,197,251,198,251,222,249,0,
+  2,251,0,4,251,0,6,251,0,100,250,0,102,250,0,122,249,20,66,251,85,
+  250,87,249,88,251,89,250,90,249,193,251,194,251,195,251,196,251,
+  197,251,198,251,222,249,0,2,251,0,4,251,0,6,251,0,100,250,0,102,
+  250,0,122,249,20,66,251,85,250,87,249,88,251,89,250,90,249,193,251,
+  194,251,195,251,196,251,197,251,198,251,222,249,0,2,251,0,4,251,
+  0,6,251,0,100,250,0,102,250,0,122,249,20,66,251,85,250,87,249,88,
+  251,89,250,90,249,193,251,194,251,195,251,196,251,197,251,198,251,
+  222,249,0,2,251,0,4,251,0,6,251,0,100,250,0,102,250,0,122,249,20,
+  66,251,85,250,87,249,88,251,89,250,90,249,193,251,194,251,195,251,
+  196,251,197,251,198,251,222,249,0,2,251,0,4,251,0,6,251,0,100,250,
+  0,102,250,0,122,249,20,66,251,85,250,87,249,88,251,89,250,90,249,
+  193,251,194,251,195,251,196,251,197,251,198,251,222,249,0,2,251,
+  0,4,251,0,6,251,0,100,250,0,102,250,0,122,249,11,66,250,193,250,
+  194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,
+  11,66,250,193,250,194,250,195,250,196,250,197,250,198,250,0,2,250,
+  0,4,250,0,6,250,11,66,250,193,250,194,250,195,250,196,250,197,250,
+  198,250,0,2,250,0,4,250,0,6,250,11,66,250,193,250,194,250,195,250,
+  196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,65,45,235,46,238,
+  47,235,59,242,60,242,66,237,80,252,98,240,102,240,106,248,112,239,
+  118,238,193,237,194,237,195,237,196,237,197,237,198,237,211,252,
+  212,252,213,252,214,252,215,252,217,252,225,247,226,240,227,240,
+  228,247,229,247,230,240,233,247,234,240,235,240,236,247,238,248,
+  243,245,244,239,245,239,246,245,247,245,249,239,250,245,251,238,
+  252,238,253,245,0,2,237,0,3,247,0,4,237,0,5,240,0,6,237,0,7,240,
+  0,21,247,0,25,240,0,27,240,0,29,240,0,49,248,0,78,252,0,79,245,0,
+  82,252,0,83,239,0,109,245,0,113,238,0,115,238,0,117,238,3,119,254,
+  120,254,3,119,254,120,254,3,119,254,120,254,3,119,254,120,254,3,
+  119,254,120,254,3,119,254,120,254,4,122,254,254,254,0,1,254,10,104,
+  254,119,253,120,253,121,254,122,254,254,254,0,1,254,0,33,254,0,37,
+  254,10,104,254,119,253,120,253,121,254,122,254,254,254,0,1,254,0,
+  33,254,0,37,254,10,104,254,119,253,120,253,121,254,122,254,254,254,
+  0,1,254,0,33,254,0,37,254,10,104,254,119,253,120,253,121,254,122,
+  254,254,254,0,1,254,0,33,254,0,37,254,2,119,253,2,119,253,2,119,
+  253,2,119,253,5,119,250,122,254,254,254,0,1,254,6,119,254,120,253,
+  122,255,254,255,0,1,255,6,119,254,120,253,122,255,254,255,0,1,255,
+  6,119,254,120,253,122,255,254,255,0,1,255,6,119,254,120,253,122,
+  255,254,255,0,1,255,6,119,254,120,253,122,255,254,255,0,1,255,6,
+  119,254,120,253,122,255,254,255,0,1,255,3,45,246,47,246,3,45,246,
+  47,246,41,68,250,72,250,80,248,82,248,85,238,86,248,87,234,88,242,
+  90,239,119,245,120,242,122,242,200,250,211,248,212,248,213,248,214,
+  248,215,248,217,248,218,248,219,248,220,248,221,248,222,239,254,
+  242,0,1,242,0,8,250,0,14,250,0,32,250,0,36,250,0,78,248,0,82,248,
+  0,100,238,0,102,238,0,108,248,0,112,248,0,114,248,0,116,248,0,122,
+  239,1,32,25,238,3,119,254,120,254,41,68,250,72,250,80,248,82,248,
+  85,238,86,248,87,234,88,242,90,239,119,245,120,242,122,242,200,250,
+  211,248,212,248,213,248,214,248,215,248,217,248,218,248,219,248,
+  220,248,221,248,222,239,254,242,0,1,242,0,8,250,0,14,250,0,32,250,
+  0,36,250,0,78,248,0,82,248,0,100,238,0,102,238,0,108,248,0,112,248,
+  0,114,248,0,116,248,0,122,239,1,32,25,238,3,119,254,120,254,41,68,
+  250,72,250,80,248,82,248,85,238,86,248,87,234,88,242,90,239,119,
+  245,120,248,122,248,200,250,211,248,212,248,213,248,214,248,215,
+  248,217,248,218,248,219,248,220,248,221,248,222,239,254,248,0,1,
+  248,0,8,250,0,14,250,0,32,250,0,36,250,0,78,248,0,82,248,0,100,238,
+  0,102,238,0,108,248,0,112,248,0,114,248,0,116,248,0,122,239,1,32,
+  25,238,3,119,254,120,254,4,122,254,254,254,0,1,254,4,122,254,254,
+  254,0,1,254,16,66,250,87,250,88,252,90,248,193,250,194,250,195,250,
+  196,250,197,250,198,250,222,248,0,2,250,0,4,250,0,6,250,0,122,248,
+  16,66,250,87,250,88,252,90,248,193,250,194,250,195,250,196,250,197,
+  250,198,250,222,248,0,2,250,0,4,250,0,6,250,0,122,248,10,104,254,
+  119,253,120,253,121,254,122,254,254,254,0,1,254,0,33,254,0,37,254,
+  10,104,254,119,253,120,253,121,254,122,254,254,254,0,1,254,0,33,
+  254,0,37,254,10,104,254,119,253,120,253,121,254,122,254,254,254,
+  0,1,254,0,33,254,0,37,254,10,104,254,119,253,120,253,121,254,122,
+  254,254,254,0,1,254,0,33,254,0,37,254,2,119,253,2,119,253,40,80,
+  252,102,253,112,251,118,254,122,253,211,252,212,252,213,252,214,
+  252,215,252,217,252,233,253,234,253,235,253,236,253,243,251,244,
+  251,245,251,246,251,247,251,249,251,250,254,251,254,252,254,253,
+  254,254,253,0,1,253,0,21,253,0,25,253,0,27,253,0,29,253,0,78,252,
+  0,79,251,0,82,252,0,83,251,0,109,254,0,113,254,0,115,254,0,117,254,
+  22,102,255,112,255,122,254,233,255,234,255,235,255,236,255,243,255,
+  244,255,245,255,246,255,247,255,249,255,254,254,0,1,254,0,21,255,
+  0,25,255,0,27,255,0,29,255,0,79,255,0,83,255,13,85,242,87,240,88,
+  245,90,240,122,248,222,240,254,248,0,1,248,0,100,242,0,102,242,0,
+  122,240,1,32,25,242,2,120,255,13,85,242,87,240,88,245,90,240,122,
+  248,222,240,254,248,0,1,248,0,100,242,0,102,242,0,122,240,1,32,25,
+  242,2,120,255,5,122,248,254,248,0,1,248,1,32,25,242,13,85,242,87,
+  240,88,245,90,240,122,248,222,240,254,248,0,1,248,0,100,242,0,102,
+  242,0,122,240,1,32,25,242,2,120,255,11,66,251,193,251,194,251,195,
+  251,196,251,197,251,198,251,0,2,251,0,4,251,0,6,251,5,119,250,122,
+  254,254,254,0,1,254,11,66,251,193,251,194,251,195,251,196,251,197,
+  251,198,251,0,2,251,0,4,251,0,6,251,5,119,250,122,254,254,254,0,
+  1,254,11,66,251,193,251,194,251,195,251,196,251,197,251,198,251,
+  0,2,251,0,4,251,0,6,251,5,119,250,122,254,254,254,0,1,254,20,66,
+  251,85,250,87,249,88,251,89,250,90,249,193,251,194,251,195,251,196,
+  251,197,251,198,251,222,249,0,2,251,0,4,251,0,6,251,0,100,250,0,
+  102,250,0,122,249,6,119,254,120,253,122,255,254,255,0,1,255,20,66,
+  251,85,250,87,249,88,251,89,250,90,249,193,251,194,251,195,251,196,
+  251,197,251,198,251,222,249,0,2,251,0,4,251,0,6,251,0,100,250,0,
+  102,250,0,122,249,6,119,254,120,253,122,255,254,255,0,1,255,27,80,
+  250,85,247,86,250,87,244,88,248,90,246,211,250,212,250,213,250,214,
+  250,215,250,217,250,218,250,219,250,220,250,221,250,222,246,0,78,
+  250,0,82,250,0,100,247,0,102,247,0,108,250,0,112,250,0,114,250,0,
+  116,250,0,122,246,7,45,250,46,254,47,248,104,254,0,33,254,0,37,254,
+  27,80,250,85,247,86,250,87,244,88,248,90,246,211,250,212,250,213,
+  250,214,250,215,250,217,250,218,250,219,250,220,250,221,250,222,
+  246,0,78,250,0,82,250,0,100,247,0,102,247,0,108,250,0,112,250,0,
+  114,250,0,116,250,0,122,246,7,45,250,46,254,47,248,104,254,0,33,
+  254,0,37,254,27,80,250,85,247,86,250,87,244,88,248,90,246,211,250,
+  212,250,213,250,214,250,215,250,217,250,218,250,219,250,220,250,
+  221,250,222,246,0,78,250,0,82,250,0,100,247,0,102,247,0,108,250,
+  0,112,250,0,114,250,0,116,250,0,122,246,7,45,250,46,254,47,248,104,
+  254,0,33,254,0,37,254,73,45,245,46,242,47,245,59,249,60,248,66,241,
+  80,254,98,244,102,245,106,251,112,244,115,251,118,249,120,244,122,
+  244,193,241,194,241,195,241,196,241,197,241,198,241,211,254,212,
+  254,213,254,214,254,215,254,217,254,225,250,226,244,227,244,228,
+  250,229,250,230,244,233,252,234,245,235,252,236,252,238,251,243,
+  244,244,244,245,244,246,244,247,244,249,244,250,249,251,249,252,
+  249,253,249,254,244,0,1,244,0,2,241,0,3,250,0,4,241,0,5,244,0,6,
+  241,0,7,244,0,21,245,0,25,245,0,27,245,0,29,245,0,49,251,0,78,254,
+  0,79,244,0,82,254,0,83,244,0,87,251,0,89,251,0,91,251,0,109,249,
+  0,113,249,0,115,249,0,117,249,73,45,245,46,242,47,245,59,249,60,
+  248,66,241,80,254,98,244,102,245,106,251,112,244,115,251,118,249,
+  120,244,122,244,193,241,194,241,195,241,196,241,197,241,198,241,
+  211,254,212,254,213,254,214,254,215,254,217,254,225,250,226,244,
+  227,244,228,250,229,250,230,244,233,245,234,245,235,252,236,252,
+  238,251,243,244,244,244,245,244,246,244,247,244,249,244,250,249,
+  251,249,252,249,253,249,254,244,0,1,244,0,2,241,0,3,250,0,4,241,
+  0,5,244,0,6,241,0,7,244,0,21,252,0,25,245,0,27,245,0,29,245,0,49,
+  251,0,78,254,0,79,244,0,82,254,0,83,244,0,87,251,0,89,251,0,91,251,
+  0,109,249,0,113,249,0,115,249,0,117,249,11,66,250,193,250,194,250,
+  195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,11,66,250,
+  193,250,194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,
+  0,6,250,11,66,250,193,250,194,250,195,250,196,250,197,250,198,250,
+  0,2,250,0,4,250,0,6,250,11,66,250,193,250,194,250,195,250,196,250,
+  197,250,198,250,0,2,250,0,4,250,0,6,250,65,45,235,46,238,47,235,
+  59,242,60,242,66,237,80,252,98,240,102,240,106,248,112,239,118,238,
+  193,237,194,237,195,237,196,237,197,237,198,237,211,252,212,252,
+  213,252,214,252,215,252,217,252,225,247,226,240,227,240,228,240,
+  229,247,230,240,233,247,234,240,235,240,236,247,238,248,243,245,
+  244,239,245,239,246,245,247,245,249,239,250,245,251,238,252,238,
+  253,245,0,2,237,0,3,247,0,4,237,0,5,240,0,6,237,0,7,240,0,21,247,
+  0,25,240,0,27,240,0,29,240,0,49,248,0,78,252,0,79,245,0,82,252,0,
+  83,239,0,109,245,0,113,238,0,115,238,0,117,238,12,66,244,193,244,
+  194,244,195,244,196,244,197,244,198,244,0,2,244,0,4,244,0,6,244,
+  1,32,24,245,21,33,245,101,249,109,255,115,249,116,248,117,254,119,
+  249,0,19,249,0,60,255,0,62,255,0,68,255,0,87,249,0,89,249,0,91,249,
+  0,93,248,0,97,248,0,99,248,0,101,254,1,2,25,248,1,32,25,245,11,66,
+  244,193,244,194,244,195,244,196,244,197,244,198,244,0,2,244,0,4,
+  244,0,6,244
+};
+static afm_cuint16 afm_Times_Roman_highchars_index[] = { /* 220 */
+  161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+  178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+  194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+  210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+  226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+  242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+  258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+  280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+  313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+  336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+  354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+  380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+  8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+  8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+  64257,64258
+};
+static afm_cunicode afm_Times_Roman_ligatures[] = { /* 3 */
+  102,105,64257
+};
+const afm_fontinfo afm_fontinfolist[] = {
+  { /* Courier.afm   761 bytes */
+      "Courier", "Courier",
+      629, -157,
+      afm_Courier_widths,
+      NULL,
+      NULL,
+      afm_Courier_highchars_index, 220,
+      afm_Courier_ligatures, 1},
+  { /* Courier-Bold.afm   761 bytes */
+      "Courier-Bold", "Courier Bold",
+      629, -157,
+      afm_Courier_Bold_widths,
+      NULL,
+      NULL,
+      afm_Courier_Bold_highchars_index, 220,
+      afm_Courier_Bold_ligatures, 1},
+  { /* Courier-BoldOblique.afm   761 bytes */
+      "Courier-BoldOblique", "Courier Bold Oblique",
+      629, -157,
+      afm_Courier_BoldOblique_widths,
+      NULL,
+      NULL,
+      afm_Courier_BoldOblique_highchars_index, 220,
+      afm_Courier_BoldOblique_ligatures, 1},
+  { /* Courier-Oblique.afm   761 bytes */
+      "Courier-Oblique", "Courier Oblique",
+      629, -157,
+      afm_Courier_Oblique_widths,
+      NULL,
+      NULL,
+      afm_Courier_Oblique_highchars_index, 220,
+      afm_Courier_Oblique_ligatures, 1},
+  { /* Helvetica.afm   7841 bytes */
+      "Helvetica", "Helvetica",
+      718, -207,
+      afm_Helvetica_widths,
+      afm_Helvetica_kerning_index,
+      afm_Helvetica_kerning_data,
+      afm_Helvetica_highchars_index, 220,
+      afm_Helvetica_ligatures, 1},
+  { /* Helvetica-Bold.afm   7336 bytes */
+      "Helvetica-Bold", "Helvetica Bold",
+      718, -207,
+      afm_Helvetica_Bold_widths,
+      afm_Helvetica_Bold_kerning_index,
+      afm_Helvetica_Bold_kerning_data,
+      afm_Helvetica_Bold_highchars_index, 220,
+      afm_Helvetica_Bold_ligatures, 1},
+  { /* Helvetica-BoldOblique.afm   7336 bytes */
+      "Helvetica-BoldOblique", "Helvetica Bold Oblique",
+      718, -207,
+      afm_Helvetica_BoldOblique_widths,
+      afm_Helvetica_BoldOblique_kerning_index,
+      afm_Helvetica_BoldOblique_kerning_data,
+      afm_Helvetica_BoldOblique_highchars_index, 220,
+      afm_Helvetica_BoldOblique_ligatures, 1},
+  { /* Helvetica-Oblique.afm   7841 bytes */
+      "Helvetica-Oblique", "Helvetica Oblique",
+      718, -207,
+      afm_Helvetica_Oblique_widths,
+      afm_Helvetica_Oblique_kerning_index,
+      afm_Helvetica_Oblique_kerning_data,
+      afm_Helvetica_Oblique_highchars_index, 220,
+      afm_Helvetica_Oblique_ligatures, 1},
+  { /* ZapfDingbats.afm   416 bytes */
+      "ZapfDingbats", "ITC Zapf Dingbats",
+      0, 0,
+      afm_ZapfDingbats_widths,
+      NULL,
+      NULL,
+      afm_ZapfDingbats_highchars_index, 107,
+      NULL, 0},
+  { /* Symbol.afm   563 bytes */
+      "Symbol", "Symbol",
+      0, 0,
+      afm_Symbol_widths,
+      NULL,
+      NULL,
+      afm_Symbol_highchars_index, 156,
+      NULL, 0},
+  { /* Times-Bold.afm   6761 bytes */
+      "Times-Bold", "Times Bold",
+      683, -217,
+      afm_Times_Bold_widths,
+      afm_Times_Bold_kerning_index,
+      afm_Times_Bold_kerning_data,
+      afm_Times_Bold_highchars_index, 220,
+      afm_Times_Bold_ligatures, 1},
+  { /* Times-BoldItalic.afm   6270 bytes */
+      "Times-BoldItalic", "Times Bold Italic",
+      683, -217,
+      afm_Times_BoldItalic_widths,
+      afm_Times_BoldItalic_kerning_index,
+      afm_Times_BoldItalic_kerning_data,
+      afm_Times_BoldItalic_highchars_index, 220,
+      afm_Times_BoldItalic_ligatures, 1},
+  { /* Times-Italic.afm   6975 bytes */
+      "Times-Italic", "Times Italic",
+      683, -217,
+      afm_Times_Italic_widths,
+      afm_Times_Italic_kerning_index,
+      afm_Times_Italic_kerning_data,
+      afm_Times_Italic_highchars_index, 220,
+      afm_Times_Italic_ligatures, 1},
+  { /* Times-Roman.afm   6287 bytes */
+      "Times-Roman", "Times Roman",
+      683, -217,
+      afm_Times_Roman_widths,
+      afm_Times_Roman_kerning_index,
+      afm_Times_Roman_kerning_data,
+      afm_Times_Roman_highchars_index, 220,
+      afm_Times_Roman_ligatures, 1},
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+const int afm_fontinfo_count = 14;
diff --git a/program/src/rrd_afm_data.h b/program/src/rrd_afm_data.h
new file mode 100644 (file)
index 0000000..84b1422
--- /dev/null
@@ -0,0 +1,191 @@
+/****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd_afm_data.h  Encoded afm (Adobe Font Metrics) for selected fonts.
+ ****************************************************************************/
+
+#ifndef  RRD_AFM_DATA_H
+#define RRD_AFM_DATA_H
+
+/*
+Description of data structures:
+
+  Ideally, the struct should be a list of fonts, and each font
+  is a list of character-info.
+  Each character has a structure:
+    struct charinfo {
+      char16 thechar;
+      int width;
+      struct {
+       char16 nextchar;
+       int deltawidth;
+      } kernings[];
+      struct {
+       char16 nextchar;
+       char16 resultingchar;
+      } ligatures[];
+    }
+
+    The data for typical fonts makes this a very sparse data structure.
+    For most fonts, only the letter "f" has ligatures.
+    All fonts have all (or almost all) of the characters 32-126,
+    most fonts have all 161-255,
+    and all fonts have very few 256-65535.
+    Most kerning pairs have both chars 32-126.
+
+    The most basic design decisionÊis to have all this data as 
+    const C globals all set up by array/struct initialisers
+    so runtime setup overhead is minimal.
+    The complete other possibility would be to parse and load
+    this info at runtime, but for rrdtool I have preferred
+    speed for flexibility as the same few fonts will be used 
+    zillions of times.
+
+    So the idea is to rewrite the above structure into
+    something which:
+    1) uses/wastes minimal memory
+    2) is fast for most characters
+    3) supports at least Iso-Latin-1, prefer full unicode.
+    4) doesn't need full precision in char width
+       (we can afford to loose 0.2% as rrdtool only needs to calculate
+       overall layout of elements, not positioning individual
+       characters)
+    5) can be written as constant initialisers to C structs/arrays
+       so we don't have runtime overhead starting rrdtool.
+    6) can be easily generated by some script so it is easy
+       to select a set of fonts and have the C data updated.
+       So adding/removing fonts is a matter of a recompile.
+
+Implementation design:
+    All character structs are sorted by unicode value. Info for
+    characters below 32 is discarded and the chars are treated 
+    as a space. Missing characters in the 32-126 range are 
+    substituted with default values so we can use direct array 
+    access for those. For characters above 126, binary search 
+    is used (not yet, liniar now but uses good guess for most latin 1
+    characters).
+
+    Ligature handling can be discarded as ligatures have very small
+    effects on string width. The width of the "fi" ligature
+    is the same (or very close) to the width of "f" plus the width 
+    of "i".
+    If implemented, it can be a simple list (global for the font,
+    not for each character) because all fonts I've seen that have
+    ligatures has max 3 pairs: "fi", "fl", "ffl" and no other. 
+
+    Most characters has less than 10 kern pairs, few 10-20, and
+    extremly few 20-30. This is implemented as a simple
+    linear search with characters 256-65536 encoding using a prefix
+    so most kern pairs only take 2 bytes:
+    unsigned 8 bit char value and signed 8 bit kern width.
+    Using a non-packed format would enable binary search, but
+    would use almost twice as much memory for a yet unknown
+    gain in speed.
+
+    Character widths are stored as unsigned bytes. Width of
+    one character is font-size * bytevalue * (1000 / 6)
+    AFM specifies widths as integers with 1000 representing 1 * font-size.
+    Kerning delta widths has same scaling factor, but the value
+    is a signed byte as many kerning widths are negative and smaller
+    than avarage character width.
+
+    Kerning info is stored in a shared packed int8 array
+    to reduce the number of structs and memory usage.
+    This sets the maximum number of kerning pairs to
+    approx 15000.
+      The font I have seen with most kern pairs is
+      "Bodoni Old Face BE Bold Italic Oldstyle Figures"
+      which has 1718 pairs for 62 chars.
+      Typical fonts have 100-150 pairs.
+    For each character needs then only a 16 bit index
+    into this shared table.
+    The format of the sub-arrays are:
+      count ( unicode deltawidth )
+    with the (...) repeated count times.
+    The count and the unicode is packed because a lot
+    entries is less than 256, and most below 400.
+    Therefore an escape sequence is used.
+    If the value is >= 510
+      1, high-8bits, low-8bits
+    else if the value is >= 254
+      0, value minus 254
+    else
+      value plus 1
+    An index of zero is treated as a NULL pointer,
+    and the first byte in a shared array is
+    therefore not used (and filled with a dummy value).
+    The array is only created if non-empty.
+       No entries can be zero (they are redundant),
+       and no subarray can be empty (as the index pointer
+       then is 0 meaning no sub array).
+       The deltawidth is stored as a non-escaped signed byte.
+
+    So for each character needed info is:
+      width: unsigned 8 bit int.
+      kerning-subarray-index: unsigned 16 bit int.
+
+    The first 126-32+1 entries are for the characters
+    32-126. If any is missing, a dummy entry is created.
+    For characters 126-65535 a font-global
+    array of struct {unicode, char-index} is
+    used for binary search (not yet, liniar now).
+
+    Ligatures can be implemented as a font-global
+    array of struct {
+      unicode char1, char2, resultingchar;
+    }
+
+    Font-global info is stored in a struct afm_fontinfo (see below).
+
+    The highchars_index and ligatures structures are flattened
+    to a simple array to avoid accidental padding between
+    structs if the structsize is problematic for some platforms.
+
+    All fonts are stored in an array of this struct,
+    sorted by fullname for binary search (not yet sorted).
+
+    The .afm files are compiled by a perl script which creates
+    rrd_afm_data.c 
+    The only thing rrd_afm_data.c contains is this compiled data.
+
+    Compiled on Mac OS X the size of rrd_afm_data.o
+    is 67 Kb for the standard 14 postscript fonts,
+    and 490 Kb for a set of 276 Adobe fonts.
+*/
+
+typedef unsigned char  afm_uint8;
+typedef signed   char  afm_sint8;
+typedef unsigned short afm_uint16;
+typedef signed   short afm_sint16;
+typedef unsigned short afm_unicode;
+
+typedef const afm_uint8   afm_cuint8;
+typedef const afm_sint8   afm_csint8;
+typedef const afm_uint16  afm_cuint16;
+typedef const afm_sint16  afm_csint16;
+typedef const afm_unicode afm_cunicode;
+
+typedef struct afm_fontinfo {
+  const char   *fullname; /* e.g. "Futura Bold Oblique" */
+  const char   *postscript_name; /* e.g. "Futura-BoldOblique" */
+  afm_cuint16 ascender, descender;
+  afm_cuint8   *widths;
+  afm_csint16  *kerning_index;
+  afm_cuint8   *kerning_data;
+  afm_cuint16  *highchars_index;
+  afm_cuint16   highchars_count;
+  afm_cunicode *ligatures;
+  afm_cuint16   ligatures_count;
+}      afm_fontinfo;
+
+typedef struct old_afm_fontinfo {
+  const char *fontname, *fullname;
+  const unsigned short *charinfo, *intarray;
+  const unsigned short charinfocount;
+  const unsigned short fixedpitch;
+} old_afm_fontinfo;
+
+extern const afm_fontinfo afm_fontinfolist[];
+extern const int afm_fontinfo_count;
+
+#endif
diff --git a/program/src/rrd_cgi.c b/program/src/rrd_cgi.c
new file mode 100644 (file)
index 0000000..5c2aa1b
--- /dev/null
@@ -0,0 +1,1412 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_cgi.c  RRD Web Page Generator
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+
+
+#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()
+{
+       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()
+{
+       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);
+    }
+  }
+}
+
+/* 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);
+}
+
+int main(int argc, char *argv[]) {
+       long length;
+       char *buffer;
+       char *server_url = NULL;
+       long i;
+       long filter=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) {
+               static struct option long_options[] = {
+                       { "filter", no_argument, 0, 'f' },
+                       { 0, 0, 0, 0}
+               };
+               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: %d\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){
+       struct  rrd_time_value 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 */
+       parsetime("end-24h", &start_tv);
+       parsetime("now", &end_tv);
+
+       /* Parse the start and end times we were given */
+       if( (parsetime_error = parsetime( args[1], &start_tv))) {
+               rrd_set_error( "start time: %s", parsetime_error);
+               return stralloc("");
+       }
+       if( (parsetime_error = parsetime( args[2], &end_tv))) {
+               rrd_set_error( "end time: %s", parsetime_error);
+               return stralloc("");
+       }
+       if( proc_start_end( &start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+               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();
+      calfree();
+      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]");
+    };
+    last = rrd_last(argc+1, (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;
+  }
+  if ( argc < 2 ) {
+    return stralloc("[ERROR: too few arguments for RRD::TIME::LAST]");
+  }
+  return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]");
+}
+
+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 = 0;
+       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;
+       }
+
+       /* 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 > 0) {
+               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 */
+       *arguments = argv;
+       *argument_count = argc;
+
+       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);
+       }
+       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;
+           fgets(line, length+1, stdin);
+       } 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) == 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_create.c b/program/src/rrd_create.c
new file mode 100644 (file)
index 0000000..e9ac860
--- /dev/null
@@ -0,0 +1,714 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_create.c  creates new rrds
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include "rrd_hw.h"
+
+#include "rrd_is_thread_safe.h"
+
+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);
+
+int
+rrd_create(int argc, char **argv) 
+{
+    time_t            last_up = time(NULL)-10;
+    unsigned long     pdp_step = 300;
+    struct rrd_time_value last_up_tv;
+    char *parsetime_error = NULL;
+    long              long_tmp;
+    int               rc;
+    optind = 0; opterr = 0;  /* initialize getopt */
+
+    while (1){
+       static struct option long_options[] =
+       {
+           {"start",      required_argument, 0, 'b'},
+           {"step",        required_argument,0,'s'},
+           {0,0,0,0}
+       };
+       int option_index = 0;
+       int opt;
+       opt = getopt_long(argc, argv, "b:s:", 
+                         long_options, &option_index);
+       
+       if (opt == EOF)
+           break;
+       
+       switch(opt) {
+       case 'b':
+            if ((parsetime_error = 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 '?':
+            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("what is the name of the rrd file you want 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 = calloc(1,sizeof(stat_head_t)))==NULL){
+       rrd_set_error("allocating rrd.stat_head");
+       rrd_free(&rrd);
+       return(-1);
+    }
+
+    /* live header */
+    if((rrd.live_head = calloc(1,sizeof(live_head_t)))==NULL){
+       rrd_set_error("allocating rrd.live_head");
+       rrd_free(&rrd);
+       return(-1);
+    }
+
+    /* set some defaults */
+    strcpy(rrd.stat_head->cookie,RRD_COOKIE);
+    strcpy(rrd.stat_head->version,RRD_VERSION);
+    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 = rrd_realloc(rrd.ds_def,
+                                         old_size+sizeof(ds_def_t)))==NULL){
+               rrd_set_error("allocating rrd.ds_def");
+               rrd_free(&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_free(&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_free(&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);
+           if((rrd.rra_def = rrd_realloc(rrd.rra_def,
+                                          old_size+sizeof(rra_def_t)))==NULL)
+           {
+                rrd_set_error("allocating rrd.rra_def");
+                rrd_free(&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_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;
+                        /* 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_DEVSEASONAL:
+                    case CF_SEASONAL:
+                    case CF_DEVPREDICT:
+                    case CF_FAILURES:
+                        rrd.rra_def[rrd.stat_head->rra_cnt].row_cnt = atoi(token);
+                        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:
+                        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);
+                        break;
+                    }
+                    break;
+                case 3:
+                    switch(cf_conv(rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam))
+                    {
+                    case CF_HWPREDICT:
+                        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:
+                        rrd.rra_def[rrd.stat_head->rra_cnt].row_cnt = atoi(token);
+                        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_HWPREDICT:
+                        /* 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_free(&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
+               && 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_free(&rrd);
+                    return -1;
+                }
+           }
+           rrd.stat_head->rra_cnt++;                   
+       } else {
+           rrd_set_error("can't parse argument '%s'",argv[i]);
+           rrd_free(&rrd);
+            return -1;
+       }
+    }
+    
+    
+    if (rrd.stat_head->rra_cnt < 1){
+       rrd_set_error("you must define at least one Round Robin Archive");
+       rrd_free(&rrd);
+       return(-1);
+    }
+    
+    if (rrd.stat_head->ds_cnt < 1){
+       rrd_set_error("you must define at least one Data Source");
+       rrd_free(&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];     
+    /*
+      int temp;
+      
+      temp = sscanf(def,"%lu:%18[^:]:%18[^:]", 
+      &(rrd -> ds_def[ds_idx].par[DS_mrhb_cnt].u_cnt),
+      minstr,maxstr);
+    */
+    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");
+            return;            
+        }
+    } else {
+        rrd_set_error("failed to parse data source %s", def);
+    }
+}
+
+/* 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 = rrd_realloc(rrd -> rra_def,
+                                      old_size+4*sizeof(rra_def_t)))==NULL)
+    {
+        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;
+    FILE             *rrd_file;
+    rrd_value_t      *unknown;
+    int        unkn_cnt;
+
+    long rrd_head_size;
+
+    if ((rrd_file = fopen(file_name,"wb")) == NULL ) {
+       rrd_set_error("creating '%s': %s",file_name, rrd_strerror(errno));
+       free(rrd->stat_head);
+        rrd->stat_head = NULL; 
+       free(rrd->ds_def);
+        rrd->ds_def = NULL; 
+       free(rrd->rra_def);
+        rrd->rra_def = NULL;
+       return(-1);
+    }
+    
+    fwrite(rrd->stat_head,
+          sizeof(stat_head_t), 1, rrd_file);
+    
+    fwrite(rrd->ds_def,
+          sizeof(ds_def_t), rrd->stat_head->ds_cnt, rrd_file);
+    
+    fwrite(rrd->rra_def,
+          sizeof(rra_def_t), rrd->stat_head->rra_cnt, rrd_file);
+    
+    fwrite(rrd->live_head,
+          sizeof(live_head_t),1, rrd_file);
+
+    if((rrd->pdp_prep = calloc(1,sizeof(pdp_prep_t))) == NULL){
+       rrd_set_error("allocating pdp_prep");
+       rrd_free(rrd);
+       fclose(rrd_file);
+       return(-1);
+    }
+
+    strcpy(rrd->pdp_prep->last_ds,"UNKN");
+
+    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++)
+       fwrite( rrd->pdp_prep,sizeof(pdp_prep_t),1,rrd_file);
+    
+    if((rrd->cdp_prep = calloc(1,sizeof(cdp_prep_t))) == NULL){
+       rrd_set_error("allocating cdp_prep");
+       rrd_free(rrd);
+       fclose(rrd_file);
+       return(-1);
+    }
+
+
+    for(i=0; i < rrd->stat_head->rra_cnt; i++) {
+       switch (cf_conv(rrd->rra_def[i].cf_nam))
+          {
+           case CF_HWPREDICT:
+               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++) 
+       {
+           fwrite( rrd->cdp_prep,sizeof(cdp_prep_t),1,rrd_file);
+       }
+    }
+    
+    /* now, we must make sure that the rest of the rrd
+       struct is properly initialized */
+    
+    if((rrd->rra_ptr = calloc(1,sizeof(rra_ptr_t))) == NULL) {
+       rrd_set_error("allocating rra_ptr");
+       rrd_free(rrd);
+       fclose(rrd_file);
+       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->rra_def[i].row_cnt - 1;
+        fwrite( rrd->rra_ptr, sizeof(rra_ptr_t),1,rrd_file);
+    }
+    rrd_head_size = ftell(rrd_file);
+
+    /* write the empty data area */
+    if ((unknown = (rrd_value_t *)malloc(512 * sizeof(rrd_value_t))) == NULL) {
+       rrd_set_error("allocating unknown");
+       rrd_free(rrd);
+       fclose(rrd_file);
+       return(-1);
+    }
+    for (i = 0; i < 512; ++i)
+       unknown[i] = DNAN;
+    
+    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;
+                     
+    while (unkn_cnt > 0) {
+       fwrite(unknown, sizeof(rrd_value_t), min(unkn_cnt, 512), rrd_file);
+       unkn_cnt -= 512;
+     }
+    free(unknown);
+    
+    /* lets see if we had an error */
+    if(ferror(rrd_file)){
+       rrd_set_error("a file error occurred while creating '%s'",file_name);
+       fclose(rrd_file);       
+       rrd_free(rrd);
+       return(-1);
+    }
+    
+#ifdef HAVE_POSIX_FADVISE
+    /* this file is not going to be read again any time
+       soon, so we drop everything except the header portion from
+       the buffer cache. for this to work, we have to fdsync the file
+       first though. This will not be all that fast, but 'good' data
+       like other rrdfiles headers will stay in cache. Now this only works if creating
+       a single rrd file is not too large, but I assume this should not be the case
+       in general. Otherwhise we would have to sync and release while writing all
+       the unknown data. */
+    fflush(rrd_file);
+    fdatasync(fileno(rrd_file));
+    if (0 != posix_fadvise(fileno(rrd_file), rrd_head_size, 0, POSIX_FADV_DONTNEED)) {
+        rrd_set_error("setting POSIX_FADV_DONTNEED on '%s': %s",file_name, rrd_strerror(errno));
+        fclose(rrd_file);
+        return(-1);
+    }    
+#endif
+
+    fclose(rrd_file);    
+    rrd_free(rrd);
+    return (0);
+}
diff --git a/program/src/rrd_datalang.c b/program/src/rrd_datalang.c
new file mode 100644 (file)
index 0000000..42e870c
--- /dev/null
@@ -0,0 +1,34 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * 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..22cef90
--- /dev/null
@@ -0,0 +1,118 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ * 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 "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..c8cc7ee
--- /dev/null
@@ -0,0 +1,367 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * 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"
+
+#if !(defined(NETWARE) || defined(WIN32))
+extern char *tzname[2];
+#endif
+
+int
+rrd_dump(int argc, char **argv) 
+{
+    int                 rc;
+
+    if (argc < 2) {
+       rrd_set_error("Not enough arguments");
+       return -1;
+    }
+
+    if (argc == 3)
+    {
+      rc = rrd_dump_r(argv[1], argv[2]);
+    }
+    else
+    {
+      rc = rrd_dump_r(argv[1], NULL);                  
+    }
+
+    return rc;
+}
+
+int
+rrd_dump_r(const char *filename, char *outname)    
+{   
+    unsigned int i,ii,ix,iii=0;
+    time_t       now;
+    char         somestring[255];
+    rrd_value_t  my_cdp;
+    long         rra_base, rra_start, rra_next;
+    FILE        *in_file;
+               FILE                            *out_file;
+    rrd_t        rrd;
+    rrd_value_t  value;
+    struct tm    tm;
+    if(rrd_open(filename, &in_file,&rrd, RRD_READONLY)==-1){
+       rrd_free(&rrd);
+       return(-1);
+    }
+
+    out_file = NULL;
+    if (outname)
+    {
+      if (!(out_file = fopen(outname, "w")))
+      {
+        return (-1);     
+      }
+    }
+    else 
+    {
+      out_file = stdout;
+    }
+               
+    fputs("<!-- Round Robin Database Dump -->", out_file);
+    fputs("<rrd>", out_file);
+    fprintf(out_file, "\t<version> %s </version>\n",RRD_VERSION);
+    fprintf(out_file, "\t<step> %lu </step> <!-- Seconds -->\n",rrd.stat_head->pdp_step);
+#if HAVE_STRFTIME
+    localtime_r(&rrd.live_head->last_up, &tm);
+    strftime(somestring,200,"%Y-%m-%d %H:%M:%S %Z",
+            &tm);
+#else
+# error "Need strftime"
+#endif
+    fprintf(out_file, "\t<lastupdate> %ld </lastupdate> <!-- %s -->\n\n",
+          rrd.live_head->last_up,somestring);
+    for(i=0;i<rrd.stat_head->ds_cnt;i++){
+          fprintf(out_file, "\t<ds>\n");
+          fprintf(out_file, "\t\t<name> %s </name>\n",rrd.ds_def[i].ds_nam);
+          fprintf(out_file, "\t\t<type> %s </type>\n",rrd.ds_def[i].dst);
+       if (dst_conv(rrd.ds_def[i].dst) != DST_CDEF) {
+          fprintf(out_file, "\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)){
+                 fprintf(out_file, "\t\t<min> NaN </min>\n");
+             } else {
+                 fprintf(out_file, "\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)){
+                 fprintf(out_file, "\t\t<max> NaN </max>\n");
+             } else {
+                 fprintf(out_file, "\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);
+             fprintf(out_file, "\t\t<cdef> %s </cdef>\n", str);
+             free(str);
+          }
+          fprintf(out_file, "\n\t\t<!-- PDP Status -->\n");
+          fprintf(out_file, "\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)){
+             fprintf(out_file, "\t\t<value> NaN </value>\n");
+          } else {
+             fprintf(out_file, "\t\t<value> %0.10e </value>\n",rrd.pdp_prep[i].scratch[PDP_val].u_val);
+          }
+             fprintf(out_file, "\t\t<unknown_sec> %lu </unknown_sec>\n",
+                     rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
+       
+             fprintf(out_file, "\t</ds>\n\n");
+       }
+
+    fputs("<!-- Round Robin Archives -->", out_file);
+
+    rra_base=ftell(in_file);    
+    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));
+       fprintf(out_file, "\t<rra>\n");
+       fprintf(out_file, "\t\t<cf> %s </cf>\n",rrd.rra_def[i].cf_nam);
+       fprintf(out_file, "\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 */
+       fprintf(out_file, "\t\t<params>\n");
+       switch(cf_conv(rrd.rra_def[i].cf_nam)) {
+       case CF_HWPREDICT:
+          fprintf(out_file, "\t\t<hw_alpha> %0.10e </hw_alpha>\n", 
+                 rrd.rra_def[i].par[RRA_hw_alpha].u_val);
+          fprintf(out_file, "\t\t<hw_beta> %0.10e </hw_beta>\n", 
+                 rrd.rra_def[i].par[RRA_hw_beta].u_val);
+          fprintf(out_file, "\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:
+          fprintf(out_file, "\t\t<seasonal_gamma> %0.10e </seasonal_gamma>\n", 
+                 rrd.rra_def[i].par[RRA_seasonal_gamma].u_val);
+          fprintf(out_file, "\t\t<seasonal_smooth_idx> %lu </seasonal_smooth_idx>\n",
+                 rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt);
+          fprintf(out_file, "\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:
+          fprintf(out_file, "\t\t<delta_pos> %0.10e </delta_pos>\n", 
+                 rrd.rra_def[i].par[RRA_delta_pos].u_val);
+          fprintf(out_file, "\t\t<delta_neg> %0.10e </delta_neg>\n", 
+                 rrd.rra_def[i].par[RRA_delta_neg].u_val);
+          fprintf(out_file, "\t\t<window_len> %lu </window_len>\n",
+                 rrd.rra_def[i].par[RRA_window_len].u_cnt);
+          fprintf(out_file, "\t\t<failure_threshold> %lu </failure_threshold>\n",
+                 rrd.rra_def[i].par[RRA_failure_threshold].u_cnt);
+                 /* fall thru */
+       case CF_DEVPREDICT:
+          fprintf(out_file, "\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:
+          fprintf(out_file, "\t\t<xff> %0.10e </xff>\n", rrd.rra_def[i].par[RRA_cdp_xff_val].u_val);
+          break;
+       }
+       fprintf(out_file, "\t\t</params>\n");
+       fprintf(out_file, "\t\t<cdp_prep>\n");
+       for(ii=0;ii<rrd.stat_head->ds_cnt;ii++){
+               unsigned long ivalue;
+               fprintf(out_file, "\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)) {
+                          fprintf(out_file, "\t\t\t<primary_value> NaN </primary_value>\n");
+                       } else {
+                          fprintf(out_file, "\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)) {
+                          fprintf(out_file, "\t\t\t<secondary_value> NaN </secondary_value>\n");
+                       } else {
+                          fprintf(out_file, "\t\t\t<secondary_value> %0.10e </secondary_value>\n", value);
+                       }
+               switch(cf_conv(rrd.rra_def[i].cf_nam)) {
+               case CF_HWPREDICT:
+               value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_intercept].u_val;
+                       if (isnan(value)) {
+                          fprintf(out_file, "\t\t\t<intercept> NaN </intercept>\n");
+                       } else {
+                          fprintf(out_file, "\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)) {
+                          fprintf(out_file, "\t\t\t<last_intercept> NaN </last_intercept>\n");
+                       } else {
+                          fprintf(out_file, "\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)) {
+                          fprintf(out_file, "\t\t\t<slope> NaN </slope>\n");
+                       } else {
+                          fprintf(out_file, "\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)) {
+                          fprintf(out_file, "\t\t\t<last_slope> NaN </last_slope>\n");
+                       } else {
+                          fprintf(out_file, "\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;
+                       fprintf(out_file, "\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;
+                       fprintf(out_file, "\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)) {
+                          fprintf(out_file, "\t\t\t<seasonal> NaN </seasonal>\n");
+                       } else {
+                          fprintf(out_file, "\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)) {
+                          fprintf(out_file, "\t\t\t<last_seasonal> NaN </last_seasonal>\n");
+                       } else {
+                          fprintf(out_file, "\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;
+                       fprintf(out_file, "\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);
+                       fprintf(out_file, "\t\t\t<history> ");
+                       for (vidx = 0; vidx < rrd.rra_def[i].par[RRA_window_len].u_cnt; ++vidx)
+                       {
+                               fprintf(out_file, "%d",violations_array[vidx]);
+                       }
+                       fprintf(out_file, " </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)) {
+                          fprintf(out_file, "\t\t\t<value> NaN </value>\n");
+                       } else {
+                          fprintf(out_file, "\t\t\t<value> %0.10e </value>\n", value);
+                       }
+                   fprintf(out_file, "\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;
+               }
+        fprintf(out_file, "\t\t\t</ds>\n");     
+    }
+       fprintf(out_file, "\t\t</cdp_prep>\n");
+
+       fprintf(out_file, "\t\t<database>\n");
+       fseek(in_file,(rra_start
+                      +(rrd.rra_ptr[i].cur_row+1)
+                      * rrd.stat_head->ds_cnt
+                      * sizeof(rrd_value_t)),SEEK_SET);
+       timer = - (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) {
+               fseek(in_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,200,"%Y-%m-%d %H:%M:%S %Z", &tm);
+#else
+# error "Need strftime"
+#endif
+           fprintf(out_file, "\t\t\t<!-- %s / %d --> <row>",somestring,(int)now);
+           for(iii=0;iii<rrd.stat_head->ds_cnt;iii++){                  
+               fread(&my_cdp,sizeof(rrd_value_t),1,in_file);           
+               if (isnan(my_cdp)){
+                 fprintf(out_file, "<v> NaN </v>");
+               } else {
+                 fprintf(out_file, "<v> %0.10e </v>",my_cdp);
+               };
+           }
+           fprintf(out_file, "</row>\n");
+       }
+       fprintf(out_file, "\t\t</database>\n\t</rra>\n");
+       
+    }
+    fprintf(out_file, "</rrd>\n");
+    rrd_free(&rrd);
+    fclose(in_file);
+    if (out_file != stdout)
+    {
+      fclose(out_file);
+    }
+    return(0);
+}
+
+
+
+
diff --git a/program/src/rrd_error.c b/program/src/rrd_error.c
new file mode 100644 (file)
index 0000000..9f46a62
--- /dev/null
@@ -0,0 +1,145 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * 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 "rrd_tool.h"
+#include <stdarg.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, CTX->len, 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(struct rrd_context *rrd_ctx, char *fmt, ...)
+{
+    va_list argp;
+    rrd_clear_error_r(rrd_ctx);
+    va_start(argp, fmt);
+#ifdef HAVE_VSNPRINTF
+    vsnprintf((char *)rrd_ctx->rrd_error, rrd_ctx->len, fmt, argp);
+    rrd_ctx->rrd_error[rrd_ctx->len]='\0';
+#else
+    vsprintf((char *)rrd_ctx->rrd_error, fmt, argp);
+#endif
+    va_end(argp);
+}
+
+int
+rrd_test_error_r(struct rrd_context *rrd_ctx) {
+    return rrd_ctx->rrd_error[0] != '\0';
+}
+
+void
+rrd_clear_error_r(struct rrd_context *rrd_ctx) {
+    rrd_ctx->rrd_error[0] = '\0';
+}
+
+char *
+rrd_get_error_r(struct rrd_context *rrd_ctx) {
+    return (char *)rrd_ctx->rrd_error;
+}
+#endif
+
+/* PS: Should we move this to some other file? It is not really error
+   related. */
+struct rrd_context *
+rrd_new_context(void) {
+    struct rrd_context *rrd_ctx = 
+       (struct rrd_context *) malloc(sizeof(struct rrd_context));
+
+    if (rrd_ctx) {
+       rrd_ctx->rrd_error = malloc(MAXLEN+10);
+       rrd_ctx->lib_errstr = malloc(ERRBUFLEN+10);
+       if (rrd_ctx->rrd_error && rrd_ctx->lib_errstr) {
+           *rrd_ctx->rrd_error = 0;
+           *rrd_ctx->lib_errstr = 0;
+           rrd_ctx->len = MAXLEN;
+           rrd_ctx->errlen = ERRBUFLEN;
+           return rrd_ctx;
+       }
+       if (rrd_ctx->rrd_error) free(rrd_ctx->rrd_error);
+       if (rrd_ctx->lib_errstr) free(rrd_ctx->lib_errstr);
+       free(rrd_ctx);
+    }
+    return NULL;
+}
+
+void
+rrd_free_context(struct rrd_context *rrd_ctx) {
+    if (rrd_ctx) {
+       if (rrd_ctx->rrd_error) free(rrd_ctx->rrd_error);
+       if (rrd_ctx->lib_errstr) free(rrd_ctx->lib_errstr);
+       free(rrd_ctx);
+    }
+}
+
+#if 0
+void rrd_globalize_error(struct rrd_context *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..1b85edb
--- /dev/null
@@ -0,0 +1,493 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * 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_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;
+
+    struct rrd_time_value start_tv, end_tv;
+    char     *parsetime_error = NULL;
+    optind = 0; opterr = 0;  /* initialize getopt */
+
+    /* init start and end time */
+    parsetime("end-24h", &start_tv);
+    parsetime("now", &end_tv);
+
+    while (1){
+       static struct option long_options[] =
+       {
+           {"resolution",      required_argument, 0, 'r'},
+           {"start",      required_argument, 0, 's'},
+           {"end",      required_argument, 0, 'e'},
+           {0,0,0,0}
+       };
+       int option_index = 0;
+       int opt;
+       opt = getopt_long(argc, argv, "r:s:e:", 
+                         long_options, &option_index);
+
+       if (opt == EOF)
+           break;
+
+       switch(opt) {
+       case 's':
+            if ((parsetime_error = parsetime(optarg, &start_tv))) {
+                rrd_set_error( "start time: %s", parsetime_error );
+                return -1;
+           }
+           break;
+       case 'e':
+            if ((parsetime_error = parsetime(optarg, &end_tv))) {
+                rrd_set_error( "end time: %s", parsetime_error );
+                return -1;
+           }
+           break;
+       case 'r':
+           step_tmp = atol(optarg);
+           break;
+       case '?':
+           rrd_set_error("unknown option '-%c'",optopt);
+           return(-1);
+       }
+    }
+
+    
+    if (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("not enough arguments");
+       return -1;
+    }
+
+    cf = argv[optind+1];
+
+    if (rrd_fetch_r(argv[optind],cf,start,end,step,ds_cnt,ds_namv,data) == -1)
+       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_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;
+    FILE           *in_file;
+    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;
+    long           start_offset, end_offset;
+    int            first_full = 1;
+    int            first_part = 1;
+    rrd_t     rrd;
+    rrd_value_t    *data_ptr;
+    unsigned long  rows;
+    long  rrd_head_size;
+
+#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
+
+    if(rrd_open(filename,&in_file,&rrd, RRD_READONLY)==-1)
+       return(-1);
+
+    rrd_head_size = ftell(in_file);
+    
+    /* 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");
+       rrd_free(&rrd);
+       fclose(in_file);
+       return(-1);
+    }
+    
+    for(i=0;(unsigned long)i<rrd.stat_head->ds_cnt;i++){
+       if ((((*ds_namv)[i]) = malloc(sizeof(char) * DS_NAM_SIZE))==NULL){
+           rrd_set_error("malloc fetch ds_namv entry");
+           rrd_free(&rrd);
+           free(*ds_namv);
+           fclose(in_file);
+           return(-1);
+       }
+       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_end >= *end 
+              && 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");
+#endif
+               } else {
+#ifdef DEBUG
+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 (cal_end<*end)
+                   tmp_match -= (*end-cal_end);                
+               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");
+       rrd_free(&rrd);
+       fclose(in_file);
+       return(-1);
+    }
+       
+    /* 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) = malloc(*ds_cnt * rows * sizeof(rrd_value_t)))==NULL){
+       rrd_set_error("malloc fetch data area");
+       for (i=0;(unsigned long)i<*ds_cnt;i++)
+             free((*ds_namv)[i]);
+       free(*ds_namv);
+       rrd_free(&rrd);
+       fclose(in_file);
+       return(-1);
+    }
+    
+    data_ptr=(*data);
+    
+    /* find base address of rra */
+    rra_base=ftell(in_file);
+    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
+
+    /* fill the gap at the start if needs be */
+
+    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;
+    
+    if(fseek(in_file,(rra_base 
+                  + (rra_pointer
+                     * *ds_cnt
+                     * sizeof(rrd_value_t))),SEEK_SET) != 0){
+       rrd_set_error("seek error in RRA");
+       for (i=0;(unsigned)i<*ds_cnt;i++)
+             free((*ds_namv)[i]);
+       free(*ds_namv);
+       rrd_free(&rrd);
+       free(*data);
+       *data = NULL;
+       fclose(in_file);
+       return(-1);
+
+    }
+#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,"post 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(fseek(in_file,(rra_base+rra_pointer
+                              * *ds_cnt
+                              * sizeof(rrd_value_t)),SEEK_SET) != 0){
+                   rrd_set_error("wrap seek in RRA did fail");
+                   for (ii=0;(unsigned)ii<*ds_cnt;ii++)
+                       free((*ds_namv)[ii]);
+                   free(*ds_namv);
+                   rrd_free(&rrd);
+                   free(*data);
+                   *data = NULL;
+                   fclose(in_file);
+                   return(-1);
+               }
+#ifdef DEBUG
+               fprintf(stderr,"wrap seek ...\n");
+#endif     
+           }
+           
+           if(fread(data_ptr,
+                    sizeof(rrd_value_t),
+                    *ds_cnt,in_file) != rrd.stat_head->ds_cnt){
+               rrd_set_error("fetching cdp from rra");
+               for (ii=0;(unsigned)ii<*ds_cnt;ii++)
+                   free((*ds_namv)[ii]);
+               free(*ds_namv);
+               rrd_free(&rrd);
+               free(*data);
+               *data = NULL;
+               fclose(in_file);
+               return(-1);
+           }
+#ifdef HAVE_POSIX_FADVISE
+       /* don't pollute the buffer cache with data read from the file. We do this while reading to 
+          keep damage minimal */
+       if (0 != posix_fadvise(fileno(in_file), rrd_head_size, 0, POSIX_FADV_DONTNEED)) {
+           rrd_set_error("setting POSIX_FADV_DONTNEED on '%s': %s",filename, rrd_strerror(errno));
+           fclose(in_file);
+           return(-1);
+       } 
+#endif
+
+#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_free(&rrd);
+#ifdef HAVE_POSIX_FADVISE
+    /* and just to be sure we drop everything except the header at the end */
+    if (0 != posix_fadvise(fileno(in_file), rrd_head_size, 0, POSIX_FADV_DONTNEED)) {
+           rrd_set_error("setting POSIX_FADV_DONTNEED on '%s': %s",filename, rrd_strerror(errno));
+           fclose(in_file);
+           return(-1);
+    } 
+#endif     
+    fclose(in_file);
+    return(0);
+}
diff --git a/program/src/rrd_first.c b/program/src/rrd_first.c
new file mode 100644 (file)
index 0000000..f4e8d40
--- /dev/null
@@ -0,0 +1,100 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_first Return
+ *****************************************************************************
+ * Initial version by Burton Strauss, ntopSupport.com - 3/2005
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+
+
+time_t
+rrd_first(int argc, char **argv)
+{
+    int target_rraindex=0;
+    char *endptr;
+    optind = 0; opterr = 0;  /* initialize getopt */
+
+    while (1){
+        static struct option long_options[] =
+        {
+            {"rraindex", required_argument, 0, 129},
+            {0,0,0,0}
+        };
+        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)
+{
+    long rra_start,
+         timer;
+    time_t then;
+    FILE *in_file;
+    rrd_t rrd;
+
+    if(rrd_open(filename,&in_file,&rrd, RRD_READONLY)==-1){
+        rrd_set_error("could not open RRD");
+        return(-1);
+    }
+
+    if((rraindex < 0) || (rraindex >= (int)rrd.stat_head->rra_cnt)) {
+        rrd_set_error("invalid rraindex number");
+        rrd_free(&rrd);
+        fclose(in_file);
+        return(-1);
+    }
+
+    rra_start = ftell(in_file);    
+    fseek(in_file,
+          (rra_start +
+           (rrd.rra_ptr[rraindex].cur_row+1) *
+           rrd.stat_head->ds_cnt *
+           sizeof(rrd_value_t)),
+          SEEK_SET);
+    timer = - (rrd.rra_def[rraindex].row_cnt-1);
+    if (rrd.rra_ptr[rraindex].cur_row + 1 > rrd.rra_def[rraindex].row_cnt) {
+      fseek(in_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);
+
+    rrd_free(&rrd);
+    fclose(in_file);
+    return(then);
+}
+
+
+
+
diff --git a/program/src/rrd_format.c b/program/src/rrd_format.c
new file mode 100644 (file)
index 0000000..990f94a
--- /dev/null
@@ -0,0 +1,94 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * 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(-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(DEVPREDICT,CF_DEVPREDICT)
+    converter(SEASONAL,CF_SEASONAL)
+    converter(DEVSEASONAL,CF_DEVSEASONAL)
+    converter(FAILURES,CF_FAILURES)
+    rrd_set_error("unknown consolidation function '%s'",string);
+    return(-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;
+}
diff --git a/program/src/rrd_format.h b/program/src/rrd_format.h
new file mode 100644 (file)
index 0000000..6f94438
--- /dev/null
@@ -0,0 +1,398 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_format.h  RRD Database Format header
+ *****************************************************************************/
+
+#ifndef _RRD_FORMAT_H
+#define _RRD_FORMAT_H
+
+#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   "0003"
+#define FLOAT_COOKIE  8.642135E130
+
+#include "rrd_nan_inf.h"
+
+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};
+                                          /* 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 */
+                                       RRA_hw_alpha,
+                                       /* exponential smoothing parameter for the intercept in
+                                        * the Holt-Winters prediction algorithm. */
+                                       RRA_hw_beta,
+                                       /* exponential smoothing parameter for the slope in
+                                        * the Holt-Winters prediction algorithm. */
+                                       RRA_dependent_rra_idx,
+                                       /* 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.
+                                        * */
+                                       RRA_seasonal_smooth_idx,
+                                       /* For CF_SEASONAL and CF_DEVSEASONAL:
+                                        * an integer between 0 and row_count - 1 which
+                                        * is index in the seasonal cycle for applying
+                                        * the period smoother. */
+                                   RRA_failure_threshold,
+                                       /* For CF_FAILURES, number of violations within the last
+                                        * window required to mark a failure. */
+                    RRA_seasonal_gamma = RRA_hw_alpha,
+                                       /* exponential smoothing parameter for seasonal effects.
+                                        * */
+                    RRA_delta_pos = RRA_hw_alpha,
+                    RRA_delta_neg = RRA_hw_beta,
+                                       /* confidence bound scaling parameters for the
+                                        * the FAILURES RRA. */
+                    RRA_window_len = RRA_seasonal_smooth_idx};
+                                       /* 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;
+    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..b715ab0
--- /dev/null
@@ -0,0 +1,1002 @@
+/* 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
+
+#ifdef HAVE_CONFIG_H
+#include "../rrd_config.h"
+#endif
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#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
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.
+   When compiling libc, the _ macro is predefined.  */
+#ifdef HAVE_LIBINTL_H
+# include <libintl.h>
+# define _(msgid)      gettext (msgid)
+#else
+# define _(msgid)      (msgid)
+#endif
+#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.  */
+
+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.  */
+
+/* 1003.2 says this must be 1 before any call.  */
+int optind = 1;
+
+/* 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;
+
+/* 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 = '?';
+
+/* 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 (str, chr)
+     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 (argv)
+     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 (argc, argv, optstring)
+     int argc;
+     char *const *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 (argc, argv, optstring, longopts, longind, long_only)
+     int argc;
+     char *const *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 (argc, argv, optstring)
+     int argc;
+     char *const *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..7dad11b
--- /dev/null
@@ -0,0 +1,133 @@
+/* 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__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#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..14e1e88
--- /dev/null
@@ -0,0 +1,189 @@
+/* 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
+#ifdef HAVE_CONFIG_H
+#include "../rrd_config.h"
+#endif
+
+#include "rrd_getopt.h"
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#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 (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *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 (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *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;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+      int option_index = 0;
+      static 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}
+      };
+
+      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..2cdf20c
--- /dev/null
@@ -0,0 +1,2520 @@
+/****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * 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 <png.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+
+#include "rrd_gfx.h"
+#include "rrd_afm.h"
+#include "unused.h"
+
+/* lines are better drawn on the pixle than between pixles */
+#define LINEOFFSET 0.5
+
+#define USE_PDF_FAKE_ALPHA 1
+#define USE_EPS_FAKE_ALPHA 1
+
+typedef struct gfx_char_s *gfx_char;
+struct gfx_char_s {
+  FT_UInt     index;    /* glyph index */
+  FT_Vector   pos;      /* location from baseline in 26.6 */
+  FT_Glyph    image;    /* glyph bitmap */
+};
+
+typedef struct gfx_string_s *gfx_string;
+struct gfx_string_s {
+  unsigned int    width;
+  unsigned int    height;
+  int            count;  /* number of characters */
+  gfx_char        glyphs;
+  size_t          num_glyphs;
+  FT_BBox         bbox;
+  FT_Matrix       transform;
+};
+
+/* compute string bbox */
+static void compute_string_bbox(gfx_string string);
+
+/* create a freetype glyph string */
+gfx_string gfx_string_create ( gfx_canvas_t *canvas, FT_Face face,
+                               const char *text, int rotation, double tabwidth, double size);
+
+/* create a freetype glyph string */
+static void gfx_string_destroy ( gfx_string string );
+
+static
+gfx_node_t *gfx_new_node( gfx_canvas_t *canvas,enum gfx_en type){
+  gfx_node_t *node = art_new(gfx_node_t,1);
+  if (node == NULL) return NULL;
+  node->type = type;
+  node->color = 0x0;        /* color of element  0xRRGGBBAA  alpha 0xff is solid*/
+  node->size =0.0;         /* font size, line width */
+  node->path = NULL;        /* path */
+  node->points = 0;
+  node->points_max =0;
+  node->closed_path = 0;
+  node->filename = NULL;             /* font or image filename */
+  node->text = NULL;
+  node->x = 0.0;
+  node->y = 0.0;          /* position */
+  node->angle = 0;  
+  node->halign = GFX_H_NULL; /* text alignement */
+  node->valign = GFX_V_NULL; /* text alignement */
+  node->tabwidth = 0.0; 
+  node->next = NULL; 
+  if (canvas->lastnode != NULL){
+      canvas->lastnode->next = node;
+  }
+  if (canvas->firstnode == NULL){
+      canvas->firstnode = node;
+  }  
+  canvas->lastnode = node;
+  return node;
+}
+
+gfx_canvas_t *gfx_new_canvas (void) {
+    gfx_canvas_t *canvas = art_new(gfx_canvas_t,1);
+    canvas->firstnode = NULL;
+    canvas->lastnode = NULL;
+    canvas->imgformat = IF_PNG; /* we default to PNG output */
+    canvas->interlaced = 0;
+    canvas->zoom = 1.0;
+    canvas->font_aa_threshold = -1.0;
+    canvas->aa_type = AA_NORMAL;
+    return canvas;
+}
+
+/* create a new line */
+gfx_node_t  *gfx_new_line(gfx_canvas_t *canvas, 
+                          double X0, double Y0, 
+                          double X1, double Y1,
+                          double width, gfx_color_t color){
+  return gfx_new_dashed_line(canvas, X0, Y0, X1, Y1, width, color, 0, 0);
+}
+
+gfx_node_t  *gfx_new_dashed_line(gfx_canvas_t *canvas, 
+                          double X0, double Y0, 
+                          double X1, double Y1,
+                          double width, gfx_color_t color,
+                          double dash_on, double dash_off){
+
+  gfx_node_t *node;
+  ArtVpath *vec;
+  node = gfx_new_node(canvas,GFX_LINE);
+  if (node == NULL) return NULL;
+  vec = art_new(ArtVpath, 3);
+  if (vec == NULL) return NULL;
+  vec[0].code = ART_MOVETO_OPEN; vec[0].x=X0+LINEOFFSET; vec[0].y=Y0+LINEOFFSET;
+  vec[1].code = ART_LINETO; vec[1].x=X1+LINEOFFSET; vec[1].y=Y1+LINEOFFSET;
+  vec[2].code = ART_END; vec[2].x=0;vec[2].y=0;
+  
+  node->points = 3;
+  node->points_max = 3;
+  node->color = color;
+  node->size  = width;
+  node->dash_on = dash_on;
+  node->dash_off = dash_off;
+  node->path  = vec;
+  return node;
+}
+
+/* create a new area */
+gfx_node_t   *gfx_new_area   (gfx_canvas_t *canvas, 
+                             double X0, double Y0,
+                             double X1, double Y1,
+                             double X2, double Y2,
+                             gfx_color_t color) {
+
+  gfx_node_t *node;
+  ArtVpath *vec;
+  node = gfx_new_node(canvas,GFX_AREA);
+  if (node == NULL) return NULL;
+  vec = art_new(ArtVpath, 5);
+  if (vec == NULL) return NULL;
+  vec[0].code = ART_MOVETO; vec[0].x=X0; vec[0].y=Y0;
+  vec[1].code = ART_LINETO; vec[1].x=X1; vec[1].y=Y1;
+  vec[2].code = ART_LINETO; vec[2].x=X2; vec[2].y=Y2;
+  vec[3].code = ART_LINETO; vec[3].x=X0; vec[3].y=Y0;
+  vec[4].code = ART_END; vec[4].x=0; vec[4].y=0;
+  
+  node->points = 5;
+  node->points_max = 5;
+  node->color = color;
+  node->path  = vec;
+
+  return node;
+}
+
+/* add a point to a line or to an area */
+int           gfx_add_point  (gfx_node_t *node, 
+                             double x, double y){
+  if (node == NULL) return 1;
+  if (node->type == GFX_AREA) {
+    double X0 = node->path[0].x;
+    double Y0 = node->path[0].y;
+    node->points -= 2;
+    art_vpath_add_point (&(node->path),
+                         &(node->points),
+                         &(node->points_max),
+                         ART_LINETO,
+                         x,y);
+    art_vpath_add_point (&(node->path),
+                         &(node->points),
+                         &(node->points_max),
+                         ART_LINETO,
+                         X0,Y0);
+    art_vpath_add_point (&(node->path),
+                         &(node->points),
+                         &(node->points_max),
+                         ART_END,
+                         0,0);
+  } else if (node->type == GFX_LINE) {
+    node->points -= 1;
+    art_vpath_add_point (&(node->path),
+                         &(node->points),
+                         &(node->points_max),
+                         ART_LINETO,
+                         x+LINEOFFSET,y+LINEOFFSET);
+    art_vpath_add_point (&(node->path),
+                         &(node->points),
+                         &(node->points_max),
+                         ART_END,
+                         0,0);
+    
+  } else {
+    /* can only add point to areas and lines */
+    return 1;
+  }
+  return 0;
+}
+
+void           gfx_close_path  (gfx_node_t *node) {
+    node->closed_path = 1;
+    if (node->path[0].code == ART_MOVETO_OPEN)
+       node->path[0].code = ART_MOVETO;
+}
+
+/* create a text node */
+gfx_node_t   *gfx_new_text   (gfx_canvas_t *canvas,  
+                             double x, double y, gfx_color_t color,
+                             char* font, double size,                        
+                             double tabwidth, double angle,
+                             enum gfx_h_align_en h_align,
+                             enum gfx_v_align_en v_align,
+                              char* text){
+   gfx_node_t *node = gfx_new_node(canvas,GFX_TEXT);
+   
+   node->text = strdup(text);
+   node->size = size;
+   node->filename = strdup(font);
+   node->x = x;
+   node->y = y;
+   node->angle = angle;   
+   node->color = color;
+   node->tabwidth = tabwidth;
+   node->halign = h_align;
+   node->valign = v_align;
+#if 0
+  /* debugging: show text anchor
+     green is along x-axis, red is downward y-axis */
+   if (1) {
+     double a = 2 * M_PI * -node->angle / 360.0;
+     double cos_a = cos(a);
+     double sin_a = sin(a);
+     double len = 3;
+     gfx_new_line(canvas,
+        x, y,
+        x + len * cos_a, y - len * sin_a,
+        0.2, 0x00FF0000);
+     gfx_new_line(canvas,
+        x, y,
+        x + len * sin_a, y + len * cos_a,
+        0.2, 0xFF000000);
+   }
+#endif
+   return node;
+}
+
+int           gfx_render(gfx_canvas_t *canvas, 
+                             art_u32 width, art_u32 height, 
+                             gfx_color_t background, FILE *fp){
+  switch (canvas->imgformat) {
+  case IF_PNG: 
+    return gfx_render_png (canvas, width, height, background, fp);
+  case IF_SVG: 
+    return gfx_render_svg (canvas, width, height, background, fp);
+  case IF_EPS:
+    return gfx_render_eps (canvas, width, height, background, fp);
+  case IF_PDF:
+    return gfx_render_pdf (canvas, width, height, background, fp);
+  default:
+    return -1;
+  }
+}
+
+static void gfx_string_destroy ( gfx_string string ) {
+  unsigned int n;
+  if (string->glyphs) {
+    for (n=0; n<string->num_glyphs; ++n)
+      FT_Done_Glyph (string->glyphs[n].image);
+    free (string->glyphs);
+  }
+  free (string);
+}
+
+
+double gfx_get_text_width ( gfx_canvas_t *canvas,
+                           double start, char* font, double size,
+                           double tabwidth, char* text, int rotation){
+  switch (canvas->imgformat) {
+  case IF_PNG: 
+    return gfx_get_text_width_libart (canvas, start, font, size, tabwidth, text, rotation);
+  case IF_SVG: /* fall through */ 
+  case IF_EPS:
+  case IF_PDF:
+    return afm_get_text_width(start, font, size, tabwidth, text);
+  default:
+    return size * strlen(text);
+  }
+}
+
+double gfx_get_text_width_libart (
+                           gfx_canvas_t *canvas, double UNUSED(start), char* font, double size,
+                           double tabwidth, char* text, int rotation ){
+
+  int           error;
+  double        text_width=0;
+  FT_Face       face;
+  FT_Library    library=NULL;  
+  gfx_string    string;
+
+  FT_Init_FreeType( &library );
+  error = FT_New_Face( library, font, 0, &face );
+  if ( error ) {
+    FT_Done_FreeType(library);
+    return -1;
+  }
+  error = FT_Set_Char_Size(face,  size*64,size*64,  100,100);
+  if ( error ) {
+    FT_Done_FreeType(library);
+    return -1;
+  }
+  string = gfx_string_create( canvas, face, text, rotation, tabwidth, size );
+  text_width = string->width;
+  gfx_string_destroy(string);
+  FT_Done_FreeType(library);
+  return text_width/64;
+}
+
+static void gfx_libart_close_path(gfx_node_t *node, ArtVpath **vec)
+{
+    /* libart must have end==start for closed paths,
+       even if using ART_MOVETO and not ART_MOVETO_OPEN
+       so add extra point which is the same as the starting point */
+    int points_max = node->points; /* scaled array has exact size */
+    int points = node->points - 1;
+    art_vpath_add_point (vec, &points, &points_max, ART_LINETO,
+           (**vec).x, (**vec).y);
+    art_vpath_add_point (vec, &points, &points_max, ART_END, 0, 0);
+}
+
+
+/* find bbox of a string */
+static void compute_string_bbox(gfx_string string) {
+    unsigned int n;
+    FT_BBox bbox;
+
+    bbox.xMin = bbox.yMin = 32000;
+    bbox.xMax = bbox.yMax = -32000;
+    for ( n = 0; n < string->num_glyphs; n++ ) {
+      FT_BBox glyph_bbox;
+      FT_Glyph_Get_CBox( string->glyphs[n].image, ft_glyph_bbox_gridfit,
+       &glyph_bbox );
+      if (glyph_bbox.xMin < bbox.xMin) {
+         bbox.xMin = glyph_bbox.xMin;
+      }
+      if (glyph_bbox.yMin < bbox.yMin) {
+        bbox.yMin = glyph_bbox.yMin;
+      }
+      if (glyph_bbox.xMax > bbox.xMax) {
+         bbox.xMax = glyph_bbox.xMax;
+      }
+      if (glyph_bbox.yMax > bbox.yMax) {
+         bbox.yMax = glyph_bbox.yMax;
+      }
+    }
+    if ( bbox.xMin > bbox.xMax ) { 
+      bbox.xMin = 0;
+      bbox.yMin = 0;
+      bbox.xMax = 0;
+      bbox.yMax = 0;
+    }
+    string->bbox.xMin = bbox.xMin;
+    string->bbox.xMax = bbox.xMax;
+    string->bbox.yMin = bbox.yMin;
+    string->bbox.yMax = bbox.yMax;
+} 
+
+/* create a free type glyph string */
+gfx_string gfx_string_create(gfx_canvas_t *canvas, FT_Face face,const char *text,
+        int rotation, double tabwidth, double size )
+{
+
+  FT_GlyphSlot  slot = face->glyph;  /* a small shortcut */
+  FT_Bool       use_kerning;
+  FT_UInt       previous;
+  FT_Vector     ft_pen;
+
+  gfx_string    string = (gfx_string) malloc (sizeof(struct gfx_string_s));
+
+  gfx_char      glyph;          /* current glyph in table */
+  int          n;
+  int           error;
+  int        gottab = 0;    
+
+#ifdef HAVE_MBSTOWCS
+  wchar_t      *cstr;
+  size_t       clen = strlen(text)+1;
+  cstr = malloc(sizeof(wchar_t) * clen); /* yes we are allocating probably too much here, I know */
+  string->count=mbstowcs(cstr,text,clen);
+  if ( string->count == -1){
+  /* conversion did not work, so lets fall back to just use what we got */
+       string->count=clen-1;
+        for(n=0;text[n] != '\0';n++){
+            cstr[n]=(unsigned char)text[n];
+        }
+  }
+#else
+  char         *cstr = strdup(text);
+  string->count = strlen (text);
+#endif
+
+  ft_pen.x = 0;   /* start at (0,0) !! */
+  ft_pen.y = 0;
+
+
+  string->width = 0;
+  string->height = 0;
+  string->glyphs = (gfx_char) calloc (string->count,sizeof(struct gfx_char_s));
+  string->num_glyphs = 0;
+  string->transform.xx = (FT_Fixed)( cos(M_PI*(rotation)/180.0)*0x10000);
+  string->transform.xy = (FT_Fixed)(-sin(M_PI*(rotation)/180.0)*0x10000);
+  string->transform.yx = (FT_Fixed)( sin(M_PI*(rotation)/180.0)*0x10000);
+  string->transform.yy = (FT_Fixed)( cos(M_PI*(rotation)/180.0)*0x10000);
+
+  use_kerning = FT_HAS_KERNING(face);
+  previous    = 0;
+  glyph = string->glyphs;
+  for (n=0; n<string->count;glyph++,n++) {
+    FT_Vector   vec;
+    /* handle the tabs ...
+       have a witespace glyph inserted, but set its width such that the distance
+    of the new right edge is x times tabwidth from 0,0 where x is an integer. */    
+    unsigned int letter = cstr[n];
+       letter = afm_fix_osx_charset(letter); /* unsafe macro */
+          
+    gottab = 0;
+    if (letter == '\\' && n+1 < string->count && cstr[n+1] == 't'){
+            /* we have a tab here so skip the backslash and
+               set t to ' ' so that we get a white space */
+            gottab = 1;
+            n++;
+            letter  = ' ';            
+    }            
+    if (letter == '\t'){
+       letter = ' ';
+        gottab = 1 ;
+    }            
+    /* initialize each struct gfx_char_s */
+    glyph->index = 0;
+    glyph->pos.x = 0;
+    glyph->pos.y = 0;
+    glyph->image = NULL;
+    glyph->index = FT_Get_Char_Index( face, letter );
+
+    /* compute glyph origin */
+    if ( use_kerning && previous && glyph->index ) {
+      FT_Vector kerning;
+      FT_Get_Kerning (face, previous, glyph->index,
+          ft_kerning_default, &kerning);
+      ft_pen.x += kerning.x;
+      ft_pen.y += kerning.y;
+    }
+
+    /* load the glyph image (in its native format) */
+    /* for now, we take a monochrome glyph bitmap */
+    error = FT_Load_Glyph (face, glyph->index, size > canvas->font_aa_threshold ?
+                            canvas->aa_type == AA_NORMAL ? FT_LOAD_TARGET_NORMAL :
+                            canvas->aa_type == AA_LIGHT ? FT_LOAD_TARGET_LIGHT :
+                            FT_LOAD_TARGET_MONO : FT_LOAD_TARGET_MONO);
+    if (error) {
+      RRDPRINTF("couldn't load glyph:  %c\n", letter)
+      continue;
+    }
+    error = FT_Get_Glyph (slot, &glyph->image);
+    if (error) {
+      RRDPRINTF("couldn't get glyph %c from slot %d\n", letter, (int)slot)
+      continue;
+    }
+    /* if we are in tabbing mode, we replace the tab with a space and shift the position
+       of the space so that its left edge is where the tab was supposed to land us */
+    if (gottab){
+       /* we are in gridfitting mode so the calculations happen in 1/64 pixles */
+        ft_pen.x = tabwidth*64.0 * (float)(1 + (long)(ft_pen.x / (tabwidth * 64.0))) - slot->advance.x;
+    }
+    /* store current pen position */
+    glyph->pos.x = ft_pen.x;
+    glyph->pos.y = ft_pen.y;
+
+
+    ft_pen.x   += slot->advance.x;    
+    ft_pen.y   += slot->advance.y;
+
+    /* rotate glyph */
+    vec = glyph->pos;
+    FT_Vector_Transform (&vec, &string->transform);
+    error = FT_Glyph_Transform (glyph->image, &string->transform, &vec);
+    if (error) {
+      RRDPRINTF("couldn't transform glyph id %d\n", letter)
+      continue;
+    }
+
+    /* convert to a bitmap - destroy native image */
+    error = FT_Glyph_To_Bitmap (&glyph->image, size > canvas->font_aa_threshold ?
+                            canvas->aa_type == AA_NORMAL ? FT_RENDER_MODE_NORMAL :
+                            canvas->aa_type == AA_LIGHT ? FT_RENDER_MODE_LIGHT :
+                            FT_RENDER_MODE_MONO : FT_RENDER_MODE_MONO, 0, 1);
+    if (error) {
+      RRDPRINTF("couldn't convert glyph id %d to bitmap\n", letter)
+      continue;
+    }
+
+    /* increment number of glyphs */
+    previous = glyph->index;
+    string->num_glyphs++;
+  }
+  free(cstr);
+/*  printf ("number of glyphs = %d\n", string->num_glyphs);*/
+  compute_string_bbox( string );
+  /* the last character was a tab */  
+  /* if (gottab) { */
+      string->width = ft_pen.x;
+  /* } else {
+      string->width = string->bbox.xMax - string->bbox.xMin;
+  } */
+  string->height = string->bbox.yMax - string->bbox.yMin;
+  return string;
+}
+
+
+static int gfx_save_png (art_u8 *buffer, FILE *fp,
+                     long width, long height, long bytes_per_pixel);
+/* render grafics into png image */
+
+int           gfx_render_png (gfx_canvas_t *canvas, 
+                             art_u32 width, art_u32 height, 
+                             gfx_color_t background, FILE *fp){
+    
+    
+    FT_Library    library;
+    gfx_node_t *node = canvas->firstnode;    
+    /*
+    art_u8 red = background >> 24, green = (background >> 16) & 0xff;
+    art_u8 blue = (background >> 8) & 0xff, alpha = ( background & 0xff );
+    */
+    unsigned long pys_width = width * canvas->zoom;
+    unsigned long pys_height = height * canvas->zoom;
+    const int bytes_per_pixel = 4;
+    unsigned long rowstride = pys_width*bytes_per_pixel; /* bytes per pixel */
+    
+    /* fill that buffer with out background color */
+    gfx_color_t *buffp = art_new (gfx_color_t, pys_width*pys_height);
+    art_u8 *buffer = (art_u8 *)buffp;
+    unsigned long i;
+    for (i=0;i<pys_width*pys_height;
+        i++){
+       *(buffp++)=background;
+    }
+    FT_Init_FreeType( &library );
+    while(node){
+        switch (node->type) {
+        case GFX_LINE:
+        case GFX_AREA: {   
+            ArtVpath *vec;
+            double dst[6];     
+            ArtSVP *svp;
+            art_affine_scale(dst,canvas->zoom,canvas->zoom);
+            vec = art_vpath_affine_transform(node->path,dst);
+           if (node->closed_path)
+               gfx_libart_close_path(node, &vec);
+           /* gfx_round_scaled_coordinates(vec); */
+            /* pvec = art_vpath_perturb(vec);
+              art_free(vec); */
+            if(node->type == GFX_LINE){
+                svp = art_svp_vpath_stroke ( vec, ART_PATH_STROKE_JOIN_ROUND,
+                                             ART_PATH_STROKE_CAP_ROUND,
+                                             node->size*canvas->zoom,4,0.25);
+            } else {
+                svp  = art_svp_from_vpath ( vec );
+               /* this takes time and is unnecessary since we make
+                  sure elsewhere that the areas are going clock-whise */
+               /*  svpt = art_svp_uncross( svp );
+                    art_svp_free(svp);
+                   svp  = art_svp_rewind_uncrossed(svpt,ART_WIND_RULE_NONZERO); 
+                    art_svp_free(svpt);
+                 */
+            }
+            art_free(vec);
+           /* this is from gnome since libart does not have this yet */
+            gnome_print_art_rgba_svp_alpha (svp ,0,0, pys_width, pys_height,
+                                node->color, buffer, rowstride, NULL);
+            art_svp_free(svp);
+            break;
+        }
+        case GFX_TEXT: {
+            unsigned int  n;
+            int  error;
+            art_u8 fcolor[4],falpha;
+            FT_Face       face;
+            gfx_char      glyph;
+            gfx_string    string;
+            FT_Vector     vec;  /* 26.6 */
+
+            float pen_x = 0.0 , pen_y = 0.0;
+            /* double x,y; */
+            long   ix,iy;
+            
+            fcolor[0] = node->color >> 24;
+            fcolor[1] = (node->color >> 16) & 0xff;
+            fcolor[2] = (node->color >> 8) & 0xff;
+            falpha = node->color & 0xff;
+            error = FT_New_Face( library,
+                                 (char *)node->filename,
+                                 0,
+                                 &face );
+           if ( error ) {
+               rrd_set_error("failed to load %s",node->filename);
+               
+               break;
+           }
+            error = FT_Set_Char_Size(face,   /* handle to face object            */
+                                     (long)(node->size*64),
+                                     (long)(node->size*64),
+                                     (long)(100*canvas->zoom),
+                                     (long)(100*canvas->zoom));
+            if ( error ) {
+                FT_Done_Face(face);
+                break;
+            }
+            pen_x = node->x * canvas->zoom;
+            pen_y = node->y * canvas->zoom;
+
+            string = gfx_string_create (canvas, face, node->text, node->angle, node->tabwidth, node->size);
+            FT_Done_Face(face);
+
+            switch(node->halign){
+            case GFX_H_RIGHT:  vec.x = -string->bbox.xMax;
+                               break;          
+            case GFX_H_CENTER: vec.x = abs(string->bbox.xMax) >= abs(string->bbox.xMin) ?
+                                       -string->bbox.xMax/2:-string->bbox.xMin/2;
+                               break;          
+            case GFX_H_LEFT:   vec.x = -string->bbox.xMin;
+                              break;
+            case GFX_H_NULL:   vec.x = 0;
+                               break;          
+            }
+
+            switch(node->valign){
+            case GFX_V_TOP:    vec.y = string->bbox.yMax;
+                               break;
+            case GFX_V_CENTER: vec.y = abs(string->bbox.yMax) >= abs(string->bbox.yMin) ?
+                                       string->bbox.yMax/2:string->bbox.yMin/2;
+                               break;
+            case GFX_V_BOTTOM: vec.y = 0;
+                               break;
+            case GFX_V_NULL:   vec.y = 0;
+                               break;
+            }
+           pen_x += vec.x/64;
+           pen_y += vec.y/64;
+            glyph = string->glyphs;
+            for(n=0; n<string->num_glyphs; n++, glyph++) {
+                int gr;
+                FT_Glyph        image;
+                FT_BitmapGlyph  bit;
+               /* long buf_x,comp_n; */
+               /* make copy to transform */
+                if (! glyph->image) {
+                  RRDPRINTF("no image\n")
+                  continue;
+                }
+                error = FT_Glyph_Copy (glyph->image, &image);
+                if (error) {
+                  RRDPRINTF("couldn't copy image\n")
+                  continue;
+                }
+
+                /* transform it */
+                vec = glyph->pos;
+                FT_Vector_Transform (&vec, &string->transform);
+
+                bit = (FT_BitmapGlyph) image;
+                gr = bit->bitmap.num_grays -1;
+/* 
+               buf_x = (pen_x + 0.5) + (double)bit->left;
+               comp_n = buf_x + bit->bitmap.width > pys_width ? pys_width - buf_x : bit->bitmap.width;
+                if (buf_x < 0 || buf_x >= (long)pys_width) continue;
+               buf_x *=  bytes_per_pixel ;
+               for (iy=0; iy < bit->bitmap.rows; iy++){                    
+                   long buf_y = iy+(pen_y+0.5)-(double)bit->top;
+                   if (buf_y < 0 || buf_y >= (long)pys_height) continue;
+                    buf_y *= rowstride;
+                   for (ix=0;ix < bit->bitmap.width;ix++){             
+                       *(letter + (ix*bytes_per_pixel+3)) = *(bit->bitmap.buffer + iy * bit->bitmap.width + ix);
+                   }
+                   art_rgba_rgba_composite(buffer + buf_y + buf_x ,letter,comp_n);
+                }
+                art_free(letter);
+*/
+                switch ( bit->bitmap.pixel_mode ) {
+                    case FT_PIXEL_MODE_GRAY:
+                        for (iy=0; iy < bit->bitmap.rows; iy++){
+                            long buf_y = iy+(pen_y+0.5)-bit->top;
+                            if (buf_y < 0 || buf_y >= (long)pys_height) continue;
+                            buf_y *= rowstride;
+                            for (ix=0;ix < bit->bitmap.width;ix++){
+                                long buf_x = ix + (pen_x + 0.5) + (double)bit->left ;
+                                art_u8 font_alpha;
+
+                                if (buf_x < 0 || buf_x >= (long)pys_width) continue;
+                                buf_x *=  bytes_per_pixel ;
+                                font_alpha =  *(bit->bitmap.buffer + iy * bit->bitmap.pitch + ix);
+                    if (font_alpha > 0){
+                                    fcolor[3] =  (art_u8)((double)font_alpha / gr * falpha);
+                        art_rgba_rgba_composite(buffer + buf_y + buf_x ,fcolor,1);
+                                }
+                            }
+                        }
+                        break;
+
+                    case FT_PIXEL_MODE_MONO:
+                        for (iy=0; iy < bit->bitmap.rows; iy++){
+                            long buf_y = iy+(pen_y+0.5)-bit->top;
+                            if (buf_y < 0 || buf_y >= (long)pys_height) continue;
+                            buf_y *= rowstride;
+                            for (ix=0;ix < bit->bitmap.width;ix++){
+                                long buf_x = ix + (pen_x + 0.5) + (double)bit->left ;
+
+                                if (buf_x < 0 || buf_x >= (long)pys_width) continue;
+                                buf_x *=  bytes_per_pixel ;
+                                if ( (fcolor[3] = falpha * ((*(bit->bitmap.buffer + iy * bit->bitmap.pitch + ix/8) >> (7 - (ix % 8))) & 1)) > 0 )
+                                    art_rgba_rgba_composite(buffer + buf_y + buf_x ,fcolor,1);
+                            }
+                        }
+                        break;
+
+                        default:
+                            rrd_set_error("unknown freetype pixel mode: %d", bit->bitmap.pixel_mode);
+                            break;
+                }
+
+/*
+                for (iy=0; iy < bit->bitmap.rows; iy++){                   
+                    long buf_y = iy+(pen_y+0.5)-bit->top;
+                    if (buf_y < 0 || buf_y >= (long)pys_height) continue;
+                    buf_y *= rowstride;
+                    for (ix=0;ix < bit->bitmap.width;ix++){
+                        long buf_x = ix + (pen_x + 0.5) + (double)bit->left ;
+                        art_u8 font_alpha;
+                        
+                        if (buf_x < 0 || buf_x >= (long)pys_width) continue;
+                        buf_x *=  bytes_per_pixel ;
+                        font_alpha =  *(bit->bitmap.buffer + iy * bit->bitmap.width + ix);
+                        font_alpha =  (art_u8)((double)font_alpha / gr * falpha);
+                        for (iz = 0; iz < 3; iz++){
+                            art_u8 *orig = buffer + buf_y + buf_x + iz;
+                            *orig =  (art_u8)((double)*orig / gr * ( gr - font_alpha) +
+                                              (double)fcolor[iz] / gr * (font_alpha));
+                        }
+                    }
+                }
+*/
+                FT_Done_Glyph (image);
+            }
+            gfx_string_destroy(string);
+        }
+        }
+        node = node->next;
+    }  
+    gfx_save_png(buffer,fp , pys_width,pys_height,bytes_per_pixel);
+    art_free(buffer);
+    FT_Done_FreeType( library );
+    return 0;    
+}
+
+/* free memory used by nodes this will also remove memory required for
+   associated paths and svcs ... but not for text strings */
+int
+gfx_destroy    (gfx_canvas_t *canvas){  
+  gfx_node_t *next,*node = canvas->firstnode;
+  while(node){
+    next = node->next;
+    art_free(node->path);
+    free(node->text);
+    free(node->filename);
+    art_free(node);
+    node = next;
+  }
+  art_free(canvas);
+  return 0;
+}
+static int gfx_save_png (art_u8 *buffer, FILE *fp,  long width, long height, long bytes_per_pixel){
+  png_structp png_ptr = NULL;
+  png_infop   info_ptr = NULL;
+  int i;
+  png_bytep *row_pointers;
+  int rowstride = width * bytes_per_pixel;
+  png_text text[2];
+  
+  if (fp == NULL)
+    return (1);
+
+  png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
+  if (png_ptr == NULL)
+   {
+      return (1);
+   }
+   row_pointers = (png_bytepp)png_malloc(png_ptr,
+                                     height*sizeof(png_bytep));
+
+  info_ptr = png_create_info_struct(png_ptr);
+
+  if (info_ptr == NULL)
+    {
+      png_free(png_ptr,row_pointers);
+      png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
+      return (1);
+    }
+
+  if (setjmp(png_jmpbuf(png_ptr)))
+    {
+      /* If we get here, we had a problem writing the file */
+      png_destroy_write_struct(&png_ptr, &info_ptr);
+      return (1);
+    }
+
+  png_init_io(png_ptr, fp);
+  png_set_IHDR (png_ptr, info_ptr,width, height,
+                8, PNG_COLOR_TYPE_RGB_ALPHA,
+                PNG_INTERLACE_NONE,
+                PNG_COMPRESSION_TYPE_DEFAULT,
+                PNG_FILTER_TYPE_DEFAULT);
+
+  text[0].key = "Software";
+  text[0].text = "RRDtool, Tobias Oetiker <tobi@oetike.ch>, http://tobi.oetiker.ch";
+  text[0].compression = PNG_TEXT_COMPRESSION_NONE;
+  png_set_text (png_ptr, info_ptr, text, 1);
+
+  /* lets make this fast while ending up with some increass in image size */
+  png_set_filter(png_ptr,0,PNG_FILTER_NONE);
+  /* png_set_filter(png_ptr,0,PNG_FILTER_SUB); */
+  png_set_compression_level(png_ptr,1);
+  /* png_set_compression_strategy(png_ptr,Z_HUFFMAN_ONLY); */
+  /* 
+  png_set_filter(png_ptr,PNG_FILTER_TYPE_BASE,PNG_FILTER_SUB);
+  png_set_compression_strategy(png_ptr,Z_HUFFMAN_ONLY);
+  png_set_compression_level(png_ptr,Z_BEST_SPEED); */
+  
+  /* Write header data */
+  png_write_info (png_ptr, info_ptr);
+  for (i = 0; i < height; i++)
+    row_pointers[i] = (png_bytep) (buffer + i*rowstride);
+  
+  png_write_image(png_ptr, row_pointers);
+  png_write_end(png_ptr, info_ptr);
+  png_free(png_ptr,row_pointers);
+  png_destroy_write_struct(&png_ptr, &info_ptr);
+  return 1;
+}
+
+/* ----- COMMON ROUTINES for pdf, svg and eps */
+#define min3(a, b, c) (a < b ? (a < c ? a : c) : (b < c ? b : c))
+#define max3(a, b, c) (a > b ? (a > c ? a : c) : (b > c ? b : c))
+
+#define PDF_CALC_DEBUG 0
+
+typedef struct pdf_point
+{
+       double x, y;
+} pdf_point;
+
+typedef struct
+{
+       double ascender, descender, baselineY;
+       pdf_point sizep, minp, maxp;
+       double x, y, tdx, tdy;
+       double r, cos_r, sin_r;
+       double ma, mb, mc, md, mx, my; /* pdf coord matrix */
+       double tmx, tmy; /* last 2 coords of text coord matrix */
+#if PDF_CALC_DEBUG
+       int debug;
+#endif
+} pdf_coords;
+
+#if PDF_CALC_DEBUG
+static void pdf_dump_calc(gfx_node_t *node, pdf_coords *g)
+{
+       fprintf(stderr, "PDF CALC =============================\n");
+       fprintf(stderr, "   '%s' at %f pt\n", node->text, node->size);
+       fprintf(stderr, "   align h = %s, v = %s,  sizep = %f, %f\n",
+               (node->halign == GFX_H_RIGHT ? "r" :
+                       (node->halign == GFX_H_CENTER ? "c" :
+                               (node->halign == GFX_H_LEFT ? "l" : "N"))),
+               (node->valign == GFX_V_TOP ? "t" :
+                       (node->valign == GFX_V_CENTER ? "c" :
+                               (node->valign == GFX_V_BOTTOM ? "b" : "N"))),
+                       g->sizep.x, g->sizep.y);
+       fprintf(stderr, "   r = %f = %f, cos = %f, sin = %f\n",
+                       g->r, node->angle, g->cos_r, g->sin_r);
+       fprintf(stderr, "   ascender = %f, descender = %f, baselineY = %f\n",
+               g->ascender, g->descender, g->baselineY);
+       fprintf(stderr, "   sizep: %f, %f\n", g->sizep.x, g->sizep.y);
+       fprintf(stderr, "   minp: %f, %f     maxp = %f, %f\n", 
+                       g->minp.x, g->minp.y, g->maxp.x, g->maxp.y);
+       fprintf(stderr, "   x = %f, y = %f\n", g->x, g->y);
+       fprintf(stderr, "   tdx = %f, tdy = %f\n", g->tdx, g->tdy);
+       fprintf(stderr, "   GM = %f, %f, %f, %f, %f, %f\n",
+                       g->ma, g->mb, g->mc, g->md, g->mx, g->my);
+       fprintf(stderr, "   TM = %f, %f, %f, %f, %f, %f\n",
+                       g->ma, g->mb, g->mc, g->md, g->tmx, g->tmy);
+}
+#endif
+#if PDF_CALC_DEBUG
+#define PDF_DD(x) if (g->debug) x;
+#else
+#define PDF_DD(x)
+#endif
+
+static void pdf_rotate(pdf_coords *g, pdf_point *p)
+{
+    double x2 = g->cos_r * p->x - g->sin_r * p->y;
+    double y2 = g->sin_r * p->x + g->cos_r * p->y;
+       PDF_DD( fprintf(stderr, "  rotate(%f, %f) -> %f, %f\n", p->x, p->y, x2, y2))
+    p->x = x2;
+       p->y = y2;
+}
+
+
+static void pdf_calc(int page_height, gfx_node_t *node, pdf_coords *g)
+{
+       pdf_point a, b, c;
+#if PDF_CALC_DEBUG
+       /* g->debug = !!strstr(node->text, "RevProxy-1") || !!strstr(node->text, "08:00"); */
+       g->debug = !!strstr(node->text, "sekunder") || !!strstr(node->text, "Web");
+#endif
+       g->x = node->x;
+       g->y = page_height - node->y;
+       if (node->angle) {
+               g->r = 2 * M_PI * node->angle / 360.0;
+               g->cos_r = cos(g->r);
+               g->sin_r = sin(g->r);
+       } else {
+               g->r = 0;
+               g->cos_r = 1;
+               g->sin_r = 0;
+       }
+       g->ascender = afm_get_ascender(node->filename, node->size);
+       g->descender = afm_get_descender(node->filename, node->size);
+       g->sizep.x = afm_get_text_width(0, node->filename, node->size, node->tabwidth, node->text);
+       /* seems like libart ignores the descender when doing vertial-align = bottom,
+          so we do that too, to get labels v-aligning properly */
+       g->sizep.y = -g->ascender; /* + afm_get_descender(font->ps_font, node->size); */
+       g->baselineY = -g->ascender - g->sizep.y / 2;
+       a.x = g->sizep.x; a.y = g->sizep.y;
+       b.x = g->sizep.x; b.y = 0;
+       c.x = 0; c.y = g->sizep.y;
+       if (node->angle) {
+               pdf_rotate(g, &a);
+               pdf_rotate(g, &b);
+               pdf_rotate(g, &c);
+       }
+       g->minp.x = min3(a.x, b.x, c.x);
+       g->minp.y = min3(a.y, b.y, c.y);
+       g->maxp.x = max3(a.x, b.x, c.x);
+       g->maxp.y = max3(a.y, b.y, c.y);
+  /* The alignment parameters in node->valign and node->halign
+     specifies the alignment in the non-rotated coordinate system
+     (very unlike pdf/postscript), which complicates matters.
+  */
+       switch (node->halign) {
+       case GFX_H_RIGHT:  g->tdx = -g->maxp.x; break;
+       case GFX_H_CENTER: g->tdx = -(g->maxp.x + g->minp.x) / 2; break;
+       case GFX_H_LEFT:   g->tdx = -g->minp.x; break;
+       case GFX_H_NULL:   g->tdx = 0; break;
+       }
+       switch(node->valign){
+       case GFX_V_TOP:    g->tdy = -g->maxp.y; break;
+       case GFX_V_CENTER: g->tdy = -(g->maxp.y + g->minp.y) / 2; break;
+       case GFX_V_BOTTOM: g->tdy = -g->minp.y; break;
+       case GFX_V_NULL:   g->tdy = 0; break;          
+       }
+       g->ma = g->cos_r;
+       g->mb = g->sin_r;
+       g->mc = -g->sin_r;
+       g->md = g->cos_r;
+       g->mx = g->x + g->tdx;
+       g->my = g->y + g->tdy;
+       g->tmx = g->mx - g->ascender * g->mc;
+       g->tmy = g->my - g->ascender * g->md;
+       PDF_DD(pdf_dump_calc(node, g))
+}
+
+/* ------- SVG -------
+   SVG reference:
+   http://www.w3.org/TR/SVG/
+*/
+static int svg_indent = 0;
+static int svg_single_line = 0;
+static const char *svg_default_font = "-dummy-";
+typedef struct svg_dash
+{
+  int dash_enable;
+  double dash_adjust, dash_len, dash_offset;
+  double adjusted_on, adjusted_off;
+} svg_dash;
+
+
+static void svg_print_indent(FILE *fp)
+{
+  int i;
+   for (i = svg_indent - svg_single_line; i > 0; i--) {
+     putc(' ', fp);
+     putc(' ', fp);
+   }
+}
+static void svg_start_tag(FILE *fp, const char *name)
+{
+   svg_print_indent(fp);
+   putc('<', fp);
+   fputs(name, fp);
+   svg_indent++;
+}
+static void svg_close_tag_single_line(FILE *fp)
+{
+   svg_single_line++;
+   putc('>', fp);
+}
+static void svg_close_tag(FILE *fp)
+{
+   putc('>', fp);
+   if (!svg_single_line)
+     putc('\n', fp);
+}
+static void svg_end_tag(FILE *fp, const char *name)
+{
+   /* name is NULL if closing empty-node tag */
+   svg_indent--;
+   if (svg_single_line)
+     svg_single_line--;
+   else if (name)
+     svg_print_indent(fp);
+   if (name != NULL) {
+     fputs("</", fp);
+     fputs(name, fp);
+   } else {
+     putc('/', fp);
+   }
+   svg_close_tag(fp);
+}
+static void svg_close_tag_empty_node(FILE *fp)
+{
+   svg_end_tag(fp, NULL);
+}
+static void svg_write_text(FILE *fp, const char *text)
+{
+#ifdef HAVE_MBSTOWCS
+    size_t clen;
+    wchar_t *p, *cstr, ch;
+    int text_count;
+    if (!text)
+       return;
+    clen = strlen(text) + 1;
+    cstr = malloc(sizeof(wchar_t) * clen);
+    text_count = mbstowcs(cstr, text, clen);
+    if (text_count == -1)
+       text_count = mbstowcs(cstr, "Enc-Err", 6);
+    p = cstr;
+#else
+    unsigned char *p = text;
+    unsigned char *cstr;
+    char ch;
+    if (!p)
+       return;
+#endif
+  while (1) {
+    ch = *p++;
+    ch = afm_fix_osx_charset(ch); /* unsafe macro */
+    switch (ch) {
+    case 0:
+#ifdef HAVE_MBSTOWCS
+    free(cstr);
+#endif
+    return;
+    case '&': fputs("&amp;", fp); break;
+    case '<': fputs("&lt;", fp); break;
+    case '>': fputs("&gt;", fp); break;
+    case '"': fputs("&quot;", fp); break;
+    default:
+        if (ch == 32) {
+#ifdef HAVE_MBSTOWCS
+            if (p <= cstr + 1 || !*p || *p == 32)
+                fputs("&#160;", fp); /* non-breaking space in unicode */
+            else
+#endif
+                fputc(32, fp);
+        } else if (ch < 32 || ch >= 127)
+       fprintf(fp, "&#%d;", (int)ch);
+      else
+       putc((char)ch, fp);
+     }
+   }
+}
+static void svg_format_number(char *buf, int bufsize, double d)
+{
+   /* omit decimals if integer to reduce filesize */
+   char *p;
+   snprintf(buf, bufsize, "%.2f", d);
+   p = buf; /* doesn't trust snprintf return value */
+   while (*p)
+     p++;
+   while (--p > buf) {
+     char ch = *p;
+     if (ch == '0') {
+       *p = '\0'; /* zap trailing zeros */
+       continue;
+     }
+     if (ch == '.')
+       *p = '\0'; /* zap trailing dot */
+     break;
+   }
+}
+static void svg_write_number(FILE *fp, double d)
+{
+   char buf[60];
+   svg_format_number(buf, sizeof(buf), d);
+   fputs(buf, fp);
+}
+
+static int svg_color_is_black(int c)
+{
+  /* gfx_color_t is RRGGBBAA */
+  return c == 0x000000FF;
+}
+static void svg_write_color(FILE *fp, gfx_color_t c, const char *attr)
+{
+  /* gfx_color_t is RRGGBBAA, svg can use #RRGGBB and #RGB like html */
+  gfx_color_t rrggbb = (int)((c >> 8) & 0xFFFFFF);
+  gfx_color_t opacity = c & 0xFF;
+  fprintf(fp, " %s=\"", attr);
+  if ((rrggbb & 0x0F0F0F) == ((rrggbb >> 4) & 0x0F0F0F)) {
+     /* css2 short form, #rgb is #rrggbb, not #r0g0b0 */
+    fprintf(fp, "#%03lX",
+          ( ((rrggbb >> 8) & 0xF00)
+          | ((rrggbb >> 4) & 0x0F0)
+          | ( rrggbb       & 0x00F)));
+   } else {
+    fprintf(fp, "#%06lX", rrggbb);
+   }
+  fputs("\"", fp);
+  if (opacity != 0xFF) {
+    fprintf(fp, " opacity=\"");
+    svg_write_number(fp, opacity / 255.0);
+    fputs("\"", fp);
+ }
+}
+static void svg_get_dash(gfx_node_t *node, svg_dash *d)
+{
+  double offset;
+  int mult;
+  if (node->dash_on <= 0 || node->dash_off <= 0) {
+    d->dash_enable = 0;
+    return;
+  }
+  d->dash_enable = 1;
+  d->dash_len = node->dash_on + node->dash_off;
+  /* dash on/off adjustment due to round caps */
+  d->dash_adjust = 0.8 * node->size;
+  d->adjusted_on = node->dash_on - d->dash_adjust;
+  if (d->adjusted_on < 0.01)
+      d->adjusted_on = 0.01;
+  d->adjusted_off = d->dash_len - d->adjusted_on;
+  /* dash offset calc */
+  if (node->path[0].x == node->path[1].x) /* only good for horz/vert lines */
+    offset = node->path[0].y;
+  else
+    offset = node->path[0].x;
+  mult = (int)fabs(offset / d->dash_len);
+  d->dash_offset = offset - mult * d->dash_len;
+  if (node->path[0].x < node->path[1].x || node->path[0].y < node->path[1].y)
+    d->dash_offset = d->dash_len - d->dash_offset;
+}
+
+static int svg_dash_equal(svg_dash *a, svg_dash *b)
+{
+  if (a->dash_enable != b->dash_enable)
+    return 0;
+  if (a->adjusted_on != b->adjusted_on)
+    return 0;
+  if (a->adjusted_off != b->adjusted_off)
+    return 0;
+  /* rest of properties will be the same when on+off are */
+  return 1;
+}
+
+static void svg_common_path_attributes(FILE *fp, gfx_node_t *node)
+{
+  svg_dash dash_info;
+  svg_get_dash(node, &dash_info);
+  fputs(" stroke-width=\"", fp);
+  svg_write_number(fp, node->size);
+  fputs("\"", fp);
+  svg_write_color(fp, node->color, "stroke");
+  fputs(" fill=\"none\"", fp);
+  if (dash_info.dash_enable) {
+    if (dash_info.dash_offset != 0) {
+      fputs(" stroke-dashoffset=\"", fp);
+      svg_write_number(fp, dash_info.dash_offset);
+      fputs("\"", fp);
+    }
+    fputs(" stroke-dasharray=\"", fp);
+    svg_write_number(fp, dash_info.adjusted_on);
+    fputs(",", fp);
+    svg_write_number(fp, dash_info.adjusted_off);
+    fputs("\"", fp);
+  }
+}
+
+static int svg_is_int_step(double a, double b)
+{
+   double diff = fabs(a - b);
+   return floor(diff) == diff;
+}
+static int svg_path_straight_segment(FILE *fp,
+     double lastA, double currentA, double currentB,
+     gfx_node_t *node,
+     int segment_idx, int isx, char absChar, char relChar)
+{
+   if (!svg_is_int_step(lastA, currentA)) {
+     putc(absChar, fp);
+     svg_write_number(fp, currentA);
+     return 0;
+   }
+   if (segment_idx < node->points - 1) {
+     ArtVpath *vec = node->path + segment_idx + 1;
+     if (vec->code == ART_LINETO) {
+       double nextA = (isx ? vec->x : vec->y) - LINEOFFSET;
+       double nextB = (isx ? vec->y : vec->x) - LINEOFFSET;
+       if (nextB == currentB
+           && ((currentA >= lastA) == (nextA >= currentA))
+           && svg_is_int_step(currentA, nextA)) {
+         return 1; /* skip to next as it is a straight line  */
+       }
+     }
+   }
+   putc(relChar, fp);
+   svg_write_number(fp, currentA - lastA);
+   return 0;
+}
+static void svg_path(FILE *fp, gfx_node_t *node, int multi)
+{
+   int i;
+   double lastX = 0, lastY = 0;
+   /* for straight lines <path..> tags take less space than
+      <line..> tags because of the efficient packing
+      in the 'd' attribute */
+   svg_start_tag(fp, "path");
+  if (!multi)
+    svg_common_path_attributes(fp, node);
+   fputs(" d=\"", fp);
+   /* specification of the 'd' attribute: */
+   /* http://www.w3.org/TR/SVG/paths.html#PathDataGeneralInformation */
+   for (i = 0; i < node->points; i++) {
+     ArtVpath *vec = node->path + i;
+     double x = vec->x - LINEOFFSET;
+     double y = vec->y - LINEOFFSET;
+     switch (vec->code) {
+     case ART_MOVETO_OPEN: /* fall-through */
+     case ART_MOVETO:
+       putc('M', fp);
+       svg_write_number(fp, x);
+       putc(',', fp);
+       svg_write_number(fp, y);
+       break;
+     case ART_LINETO:
+       /* try optimize filesize by using minimal lineto commands */
+       /* without introducing rounding errors. */
+       if (x == lastX) {
+         if (svg_path_straight_segment(fp, lastY, y, x, node, i, 0, 'V', 'v'))
+           continue;
+       } else if (y == lastY) {
+         if (svg_path_straight_segment(fp, lastX, x, y, node, i, 1, 'H', 'h'))
+           continue;
+       } else {
+         putc('L', fp);
+         svg_write_number(fp, x);
+         putc(',', fp);
+         svg_write_number(fp, y);
+       }
+       break;
+     case ART_CURVETO: break; /* unsupported */
+     case ART_END: break; /* nop */
+     }
+     lastX = x;
+     lastY = y;
+   }
+  if (node->closed_path)
+    fputs(" Z", fp);
+   fputs("\"", fp);
+   svg_close_tag_empty_node(fp);
+}
+static void svg_multi_path(FILE *fp, gfx_node_t **nodeR)
+{
+   /* optimize for multiple paths with the same color, penwidth, etc. */
+   int num = 1;
+   gfx_node_t *node = *nodeR;
+   gfx_node_t *next = node->next;
+   while (next) {
+     if (next->type != node->type
+         || next->size != node->size
+        || next->color != node->color
+        || next->dash_on != node->dash_on
+        || next->dash_off != node->dash_off)
+       break;
+     next = next->next;
+     num++;
+   }
+   if (num == 1) {
+     svg_path(fp, node, 0);
+     return;
+   }
+   svg_start_tag(fp, "g");
+  svg_common_path_attributes(fp, node);
+   svg_close_tag(fp);
+   while (num && node) {
+     svg_path(fp, node, 1);
+     if (!--num)
+       break;
+     node = node->next;
+     *nodeR = node;
+   }
+   svg_end_tag(fp, "g");
+}
+static void svg_area(FILE *fp, gfx_node_t *node)
+{
+   int i;
+   double startX = 0, startY = 0;
+   svg_start_tag(fp, "polygon");
+  fputs(" ", fp);
+  svg_write_color(fp, node->color, "fill");
+  fputs(" points=\"", fp);
+   for (i = 0; i < node->points; i++) {
+     ArtVpath *vec = node->path + i;
+     double x = vec->x - LINEOFFSET;
+     double y = vec->y - LINEOFFSET;
+     switch (vec->code) {
+       case ART_MOVETO_OPEN: /* fall-through */
+       case ART_MOVETO:
+         svg_write_number(fp, x);
+         putc(',', fp);
+         svg_write_number(fp, y);
+         startX = x;
+         startY = y;
+         break;
+       case ART_LINETO:
+         if (i == node->points - 2
+                       && node->path[i + 1].code == ART_END
+             && fabs(x - startX) < 0.001 && fabs(y - startY) < 0.001) {
+           break; /* poly area always closed, no need for last point */
+         }
+         putc(' ', fp);
+         svg_write_number(fp, x);
+         putc(',', fp);
+         svg_write_number(fp, y);
+         break;
+       case ART_CURVETO: break; /* unsupported */
+       case ART_END: break; /* nop */
+     }
+   }
+   fputs("\"", fp);
+   svg_close_tag_empty_node(fp);
+}
+static void svg_text(FILE *fp, gfx_node_t *node)
+{
+   pdf_coords g;
+   const char *fontname;
+   /* as svg has 0,0 in top-left corner (like most screens) instead of
+         bottom-left corner like pdf and eps, we have to fake the coords
+         using offset and inverse sin(r) value */
+   int page_height = 1000;
+   pdf_calc(page_height, node, &g);
+   if (node->angle != 0) {
+     svg_start_tag(fp, "g");
+        /* can't use svg_write_number as 2 decimals is far from enough to avoid
+               skewed text */
+     fprintf(fp, " transform=\"matrix(%f,%f,%f,%f,%f,%f)\"",
+                        g.ma, -g.mb, -g.mc, g.md, g.tmx, page_height - g.tmy);
+     svg_close_tag(fp);
+   }
+   svg_start_tag(fp, "text");
+   if (!node->angle) {
+     fputs(" x=\"", fp);
+     svg_write_number(fp, g.tmx);
+     fputs("\" y=\"", fp);
+     svg_write_number(fp, page_height - g.tmy);
+     fputs("\"", fp);
+   }
+   fontname = afm_get_font_name(node->filename);
+   if (strcmp(fontname, svg_default_font))
+     fprintf(fp, " font-family=\"%s\"", fontname);
+   fputs(" font-size=\"", fp);
+   svg_write_number(fp, node->size);
+   fputs("\"", fp);
+  if (!svg_color_is_black(node->color))
+    svg_write_color(fp, node->color, "fill");
+   svg_close_tag_single_line(fp);
+   /* support for node->tabwidth missing */
+   svg_write_text(fp, node->text);
+   svg_end_tag(fp, "text");
+   if (node->angle != 0)
+     svg_end_tag(fp, "g");
+}
+int       gfx_render_svg (gfx_canvas_t *canvas,
+                 art_u32 width, art_u32 height,
+                 gfx_color_t background, FILE *fp){
+   gfx_node_t *node = canvas->firstnode;
+   /* Find the first font used, and assume it is the mostly used
+         one. It reduces the number of font-familty attributes. */
+   while (node) {
+          if (node->type == GFX_TEXT && node->filename) {
+                  svg_default_font = afm_get_font_name(node->filename);
+                  break;
+          }
+          node = node->next;
+   }
+   fputs(
+"<?xml version=\"1.0\" standalone=\"no\"?>\n"
+"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\"\n"
+"   \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"
+"<!--\n"
+"   SVG file created by\n"
+"        RRDtool " PACKAGE_VERSION " Tobias Oetiker, http://tobi.oetiker.ch\n"
+"\n"
+"   The width/height attributes in the outhermost svg node\n"
+"   are just default sizes for the browser which is used\n"
+"   if the svg file is openened directly without being\n"
+"   embedded in an html file.\n"
+"   The viewBox is the local coord system for rrdtool.\n"
+"-->\n", fp);
+   svg_start_tag(fp, "svg");
+   fputs(" width=\"", fp);
+   svg_write_number(fp, width * canvas->zoom);
+   fputs("\" height=\"", fp);
+   svg_write_number(fp, height * canvas->zoom);
+   fputs("\" x=\"0\" y=\"0\" viewBox=\"", fp);
+   svg_write_number(fp, -LINEOFFSET);
+   fputs(" ", fp);
+   svg_write_number(fp, -LINEOFFSET);
+   fputs(" ", fp);
+   svg_write_number(fp, width - LINEOFFSET);
+   fputs(" ", fp);
+   svg_write_number(fp, height - LINEOFFSET);
+   fputs("\" preserveAspectRatio=\"xMidYMid\"", fp);
+   fprintf(fp, " font-family=\"%s\"", svg_default_font); /* default font */
+   fputs(" stroke-linecap=\"round\" stroke-linejoin=\"round\"", fp);
+   fputs(" xmlns=\"http://www.w3.org/2000/svg\"", fp);
+   fputs(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"", fp);
+   svg_close_tag(fp);
+   svg_start_tag(fp, "rect");
+   fprintf(fp, " x=\"0\" y=\"0\" width=\"%d\" height=\"%d\"", width, height);
+  svg_write_color(fp, background, "fill");
+   svg_close_tag_empty_node(fp);
+   node = canvas->firstnode;
+   while (node) {
+     switch (node->type) {
+     case GFX_LINE:
+       svg_multi_path(fp, &node);
+       break;
+     case GFX_AREA:
+       svg_area(fp, node);
+       break;
+     case GFX_TEXT:
+       svg_text(fp, node);
+     }
+     node = node->next;
+   }
+   svg_end_tag(fp, "svg");
+   return 0;
+}
+
+/* ------- EPS -------
+   EPS and Postscript references:
+   http://partners.adobe.com/asn/developer/technotes/postscript.html
+*/
+
+typedef struct eps_font
+{
+  const char *ps_font;
+  int id;
+  struct eps_font *next;
+} eps_font;
+
+typedef struct eps_state
+{
+  FILE *fp;
+  gfx_canvas_t *canvas;
+  art_u32 page_width, page_height;
+  eps_font *font_list;
+  /*--*/
+  gfx_color_t color;
+  const char *font;
+  double font_size;
+  double line_width;
+  int linecap, linejoin;
+  int has_dash;
+} eps_state;
+
+static void eps_set_color(eps_state *state, gfx_color_t color)
+{
+#if USE_EPS_FAKE_ALPHA
+   double a1, a2;
+#endif
+   /* gfx_color_t is RRGGBBAA */
+  if (state->color == color)
+    return;
+#if USE_EPS_FAKE_ALPHA
+  a1 = (color & 255) / 255.0;
+  a2 = 255 * (1 - a1);
+#define eps_color_calc(x) (int)( ((x) & 255) * a1 + a2)
+#else
+#define eps_color_calc(x) (int)( (x) & 255)
+#endif
+   /* gfx_color_t is RRGGBBAA */
+  if (state->color == color)
+    return;
+  fprintf(state->fp, "%d %d %d Rgb\n",
+      eps_color_calc(color >> 24),
+      eps_color_calc(color >> 16),
+      eps_color_calc(color >>  8));
+  state->color = color;
+}
+
+static int eps_add_font(eps_state *state, gfx_node_t *node)
+{
+  /* The fonts list could be postponed to the end using
+     (atend), but let's be nice and have them in the header. */
+  const char *ps_font = afm_get_font_postscript_name(node->filename);
+  eps_font *ef;
+  for (ef = state->font_list; ef; ef = ef->next) {
+    if (!strcmp(ps_font, ef->ps_font))
+      return 0;
+  }
+  ef = malloc(sizeof(eps_font));
+  if (ef == NULL) {
+    rrd_set_error("malloc for eps_font");
+    return -1;
+  }
+  ef->next = state->font_list;
+  ef->ps_font = ps_font;
+  state->font_list = ef;
+  return 0;
+}
+
+static void eps_list_fonts(eps_state *state, const char *dscName)
+{
+  eps_font *ef;
+  int lineLen = strlen(dscName);
+  if (!state->font_list)
+    return;
+  fputs(dscName, state->fp);
+  for (ef = state->font_list; ef; ef = ef->next) {
+    int nameLen = strlen(ef->ps_font);
+    if (lineLen + nameLen > 100 && lineLen) {
+      fputs("\n", state->fp);
+      fputs("%%- \n", state->fp);
+      lineLen = 5;
+    } else {
+      fputs(" ", state->fp);
+      lineLen++;
+    }
+    fputs(ef->ps_font, state->fp);
+    lineLen += nameLen;
+  }
+  fputs("\n", state->fp);
+}
+
+static void eps_define_fonts(eps_state *state)
+{
+  eps_font *ef;
+  if (!state->font_list)
+    return;
+  for (ef = state->font_list; ef; ef = ef->next) {
+    /* PostScript¨ LANGUAGE REFERENCE third edition
+       page 349 */
+    fprintf(state->fp,
+        "%%\n"
+        "/%s findfont dup length dict begin\n"
+        "{ 1 index /FID ne {def} {pop pop} ifelse } forall\n"
+        "/Encoding ISOLatin1Encoding def\n"
+        "currentdict end\n"
+        "/%s-ISOLatin1 exch definefont pop\n"
+        "/SetFont-%s { /%s-ISOLatin1 findfont exch scalefont setfont } bd\n",
+        ef->ps_font, ef->ps_font, ef->ps_font, ef->ps_font);
+  }
+}
+
+static int eps_prologue(eps_state *state)
+{
+  gfx_node_t *node;
+  fputs(
+    "%!PS-Adobe-3.0 EPSF-3.0\n"
+    "%%Creator: RRDtool " PACKAGE_VERSION " Tobias Oetiker, http://tobi.oetiker.ch\n"
+    /* can't like weird chars here */
+    "%%Title: (RRDtool output)\n"
+    "%%DocumentData: Clean7Bit\n"
+    "", state->fp);
+  fprintf(state->fp, "%%%%BoundingBox: 0 0 %d %d\n",
+    state->page_width, state->page_height);
+  for (node = state->canvas->firstnode; node; node = node->next) {
+    if (node->type == GFX_TEXT && eps_add_font(state, node) == -1)
+      return -1;
+  }
+  eps_list_fonts(state, "%%DocumentFonts:");
+  eps_list_fonts(state, "%%DocumentNeededFonts:");
+  fputs(
+      "%%EndComments\n"
+      "%%BeginProlog\n"
+      "%%EndProlog\n" /* must have, or BoundingBox is ignored */
+      "/bd { bind def } bind def\n"
+      "", state->fp);
+  fprintf(state->fp, "/X { %.2f add } bd\n", LINEOFFSET);
+  fputs(
+      "/X2 {X exch X exch} bd\n"
+      "/M {X2 moveto} bd\n"
+      "/L {X2 lineto} bd\n"
+      "/m {moveto} bd\n"
+      "/l {lineto} bd\n"
+      "/S {stroke} bd\n"
+      "/CP {closepath} bd\n"
+      "/WS {setlinewidth stroke} bd\n"
+      "/F {fill} bd\n"
+      "/T1 {gsave} bd\n"
+      "/T2 {concat 0 0 moveto show grestore} bd\n"
+      "/T   {moveto show} bd\n"
+      "/Rgb { 255.0 div 3 1 roll\n"
+      "       255.0 div 3 1 roll \n"
+      "       255.0 div 3 1 roll setrgbcolor } bd\n"
+      "", state->fp);
+  eps_define_fonts(state);
+  return 0;
+}
+
+static void eps_clear_dash(eps_state *state)
+{
+  if (!state->has_dash)
+    return;
+  state->has_dash = 0;
+  fputs("[1 0] 0 setdash\n", state->fp);
+}
+
+static void eps_write_linearea(eps_state *state, gfx_node_t *node)
+{
+  int i;
+  FILE *fp = state->fp;
+  int useOffset = 0;
+  int clearDashIfAny = 1;
+  eps_set_color(state, node->color);
+  if (node->type == GFX_LINE) {
+    svg_dash dash_info;
+    if (state->linecap != 1) {
+      fputs("1 setlinecap\n", fp);
+      state->linecap = 1;
+    }
+    if (state->linejoin != 1) {
+      fputs("1 setlinejoin\n", fp);
+      state->linejoin = 1;
+    }
+    svg_get_dash(node, &dash_info);
+    if (dash_info.dash_enable) {
+      clearDashIfAny = 0;
+      state->has_dash = 1;
+      fputs("[", fp);
+      svg_write_number(fp, dash_info.adjusted_on);
+      fputs(" ", fp);
+      svg_write_number(fp, dash_info.adjusted_off);
+      fputs("] ", fp);
+      svg_write_number(fp, dash_info.dash_offset);
+      fputs(" setdash\n", fp);
+    }
+  }
+  if (clearDashIfAny)
+    eps_clear_dash(state);
+  for (i = 0; i < node->points; i++) {
+    ArtVpath *vec = node->path + i;
+    double x = vec->x;
+    double y = state->page_height - vec->y;
+    if (vec->code == ART_MOVETO_OPEN || vec->code == ART_MOVETO)
+      useOffset = (fabs(x - floor(x) - 0.5) < 0.01 && fabs(y - floor(y) - 0.5) < 0.01);
+    if (useOffset) {
+      x -= LINEOFFSET;
+      y -= LINEOFFSET;
+    }
+    switch (vec->code) {
+    case ART_MOVETO_OPEN: /* fall-through */
+    case ART_MOVETO:
+      svg_write_number(fp, x);
+      fputc(' ', fp);
+      svg_write_number(fp, y);
+      fputc(' ', fp);
+      fputs(useOffset ? "M\n" : "m\n", fp);
+      break;
+    case ART_LINETO:
+      svg_write_number(fp, x);
+      fputc(' ', fp);
+      svg_write_number(fp, y);
+      fputc(' ', fp);
+      fputs(useOffset ? "L\n" : "l\n", fp);
+      break;
+    case ART_CURVETO: break; /* unsupported */
+    case ART_END: break; /* nop */
+    }
+  }
+  if (node->type == GFX_LINE) {
+    if (node->closed_path)
+      fputs("CP ", fp);
+    if (node->size != state->line_width) {
+      state->line_width = node->size;
+      svg_write_number(fp, state->line_width);
+      fputs(" WS\n", fp);
+    } else {
+      fputs("S\n", fp);
+    }
+   } else {
+    fputs("F\n", fp);
+   }
+}
+
+static void eps_write_text(eps_state *state, gfx_node_t *node)
+{
+  FILE *fp = state->fp;
+  const char *ps_font = afm_get_font_postscript_name(node->filename);
+  int lineLen = 0;
+  pdf_coords g;
+#ifdef HAVE_MBSTOWCS
+    size_t clen;
+    wchar_t *p, *cstr, ch;
+    int text_count;
+    if (!node->text)
+       return;
+    clen = strlen(node->text) + 1;
+    cstr = malloc(sizeof(wchar_t) * clen);
+    text_count = mbstowcs(cstr, node->text, clen);
+    if (text_count == -1)
+       text_count = mbstowcs(cstr, "Enc-Err", 6);
+    p = cstr;
+#else
+    const unsigned char *p = node->text;
+    unsigned char ch;
+    if (!p)
+       return;
+#endif
+  pdf_calc(state->page_height, node, &g);
+  eps_set_color(state, node->color);
+  if (strcmp(ps_font, state->font) || node->size != state->font_size) {
+    state->font = ps_font;
+    state->font_size = node->size;
+    svg_write_number(fp, state->font_size);
+    fprintf(fp, " SetFont-%s\n", state->font);
+  }
+  if (node->angle)
+         fputs("T1 ", fp);
+  fputs("(", fp);
+  lineLen = 20;
+  while (1) {
+    ch = *p;
+    if (!ch)
+      break;
+       ch = afm_fix_osx_charset(ch); /* unsafe macro */
+    if (++lineLen > 70) {
+      fputs("\\\n", fp); /* backslash and \n */
+      lineLen = 0;
+    }
+    switch (ch) {
+      case '%':
+      case '(':
+      case ')':
+      case '\\':
+        fputc('\\', fp);
+        fputc(ch, fp);
+        break;
+      case '\n':
+        fputs("\\n", fp);
+        break;
+      case '\r':
+        fputs("\\r", fp);
+        break;
+      case '\t':
+        fputs("\\t", fp);
+        break;
+      default:
+        if (ch > 255) {
+            fputc('?', fp);
+        } else if (ch >= 126 || ch < 32) {
+          fprintf(fp, "\\%03o", (unsigned int)ch);
+          lineLen += 3;
+        } else {
+          fputc(ch, fp);
+        }
+      }
+      p++;
+  }
+#ifdef HAVE_MBSTOWCS
+  free(cstr);
+#endif
+  if (node->angle) {
+        /* can't use svg_write_number as 2 decimals is far from enough to avoid
+               skewed text */
+         fprintf(fp, ") [%f %f %f %f %f %f] T2\n",
+                         g.ma, g.mb, g.mc, g.md, g.tmx, g.tmy);
+  } else {
+         fputs(") ", fp);
+         svg_write_number(fp, g.tmx);
+         fputs(" ", fp);
+         svg_write_number(fp, g.tmy);
+         fputs(" T\n", fp);
+  }
+}
+
+static int eps_write_content(eps_state *state)
+{
+  gfx_node_t *node;
+  fputs("%\n", state->fp);
+  for (node = state->canvas->firstnode; node; node = node->next) {
+    switch (node->type) {
+    case GFX_LINE:
+    case GFX_AREA:
+      eps_write_linearea(state, node);
+      break;
+    case GFX_TEXT:
+      eps_write_text(state, node);
+      break;
+    }
+  }
+  return 0;
+}
+
+int       gfx_render_eps (gfx_canvas_t *canvas,
+                 art_u32 width, art_u32 height,
+                 gfx_color_t background, FILE *fp){
+  struct eps_state state;
+  state.fp = fp;
+  state.canvas = canvas;
+  state.page_width = width;
+  state.page_height = height;
+  state.font = "no-default-font";
+  state.font_size = -1;
+  state.color = 0; /* black */
+  state.font_list = NULL;
+  state.linecap = -1;
+  state.linejoin = -1;
+  state.has_dash = 0;
+  state.line_width = 1;
+  if (eps_prologue(&state) == -1)
+    return -1;
+  eps_set_color(&state, background);
+  fprintf(fp, "0 0 M 0 %d L %d %d L %d 0 L fill\n",
+      height, width, height, width);
+  if (eps_write_content(&state) == -1)
+    return 0;
+  fputs("showpage\n", fp);
+  fputs("%%EOF\n", fp);
+  while (state.font_list) {
+    eps_font *next = state.font_list->next;
+    free(state.font_list);
+    state.font_list = next;
+  }
+  return 0;
+}
+
+/* ------- PDF -------
+   PDF references page:
+   http://partners.adobe.com/public/developer/pdf/index_reference.html
+*/
+
+typedef struct pdf_buffer
+{
+  int id, is_obj, is_dict, is_stream, pdf_file_pos;
+  char *data;
+  int alloc_size, current_size;
+  struct pdf_buffer *previous_buffer, *next_buffer;
+  struct pdf_state *state;
+} pdf_buffer;
+
+typedef struct pdf_font
+{
+  const char *ps_font;
+  pdf_buffer obj;
+  struct pdf_font *next;
+} pdf_font;
+
+typedef struct pdf_state
+{
+  FILE *fp;
+  gfx_canvas_t *canvas;
+  art_u32 page_width, page_height;
+  pdf_font *font_list;
+  pdf_buffer *first_buffer, *last_buffer;
+  int pdf_file_pos;
+  int has_failed;
+  /*--*/
+  gfx_color_t stroke_color, fill_color;
+  int font_id;
+  double font_size;
+  double line_width;
+  svg_dash dash;
+  int linecap, linejoin;
+  int last_obj_id;
+  /*--*/
+  pdf_buffer pdf_header;
+  pdf_buffer info_obj, catalog_obj, pages_obj, page1_obj;
+  pdf_buffer fontsdict_obj;
+  pdf_buffer graph_stream;
+} pdf_state;
+
+static void pdf_init_buffer(pdf_state *state, pdf_buffer *buf)
+{
+  int initial_size = 32;
+  buf->state = state;
+  buf->id = -42;
+  buf->alloc_size = 0;
+  buf->current_size = 0;
+  buf->data = (char*)malloc(initial_size);
+  buf->is_obj = 0;
+  buf->previous_buffer = NULL;
+  buf->next_buffer = NULL;
+  if (buf->data == NULL) {
+    rrd_set_error("malloc for pdf_buffer data");
+    state->has_failed = 1;
+    return;
+  }
+  buf->alloc_size = initial_size;
+  if (state->last_buffer)
+    state->last_buffer->next_buffer = buf;
+  if (state->first_buffer == NULL)
+    state->first_buffer = buf;
+  buf->previous_buffer = state->last_buffer;
+  state->last_buffer = buf;
+}
+
+static void pdf_put(pdf_buffer *buf, const char *text, int len)
+{
+  if (len <= 0)
+    return;
+  if (buf->alloc_size < buf->current_size + len) {
+    int new_size = buf->alloc_size;
+    char *new_buf;
+    while (new_size < buf->current_size + len)
+      new_size *= 4;
+    new_buf = (char*)malloc(new_size);
+    if (new_buf == NULL) {
+      rrd_set_error("re-malloc for pdf_buffer data");
+      buf->state->has_failed = 1;
+      return;
+    }
+    memcpy(new_buf, buf->data, buf->current_size);
+    free(buf->data);
+    buf->data = new_buf;
+    buf->alloc_size = new_size;
+  }
+  memcpy(buf->data + buf->current_size, text, len);
+  buf->current_size += len;
+}
+
+static void pdf_put_char(pdf_buffer *buf, char c)
+{
+    if (buf->alloc_size >= buf->current_size + 1) {
+       buf->data[buf->current_size++] = c;
+    } else {
+       char tmp[1];
+       tmp[0] = (char)c;
+       pdf_put(buf, tmp, 1);
+    }
+}
+
+static void pdf_puts(pdf_buffer *buf, const char *text)
+{
+  pdf_put(buf, text, strlen(text));
+}
+
+static void pdf_indent(pdf_buffer *buf)
+{
+  pdf_puts(buf, "\t");
+}
+
+static void pdf_putsi(pdf_buffer *buf, const char *text)
+{
+  pdf_indent(buf);
+  pdf_puts(buf, text);
+}
+
+static void pdf_putint(pdf_buffer *buf, int i)
+{
+  char tmp[20];
+  sprintf(tmp, "%d", i);
+  pdf_puts(buf, tmp);
+}
+
+static void pdf_putnumber(pdf_buffer *buf, double d)
+{
+  char tmp[50];
+  svg_format_number(tmp, sizeof(tmp), d);
+  pdf_puts(buf, tmp);
+}
+
+static void pdf_put_string_contents_wide(pdf_buffer *buf, const afm_char *text)
+{
+    const afm_char *p = text;
+    while (1) {
+       afm_char ch = *p;
+       ch = afm_fix_osx_charset(ch); /* unsafe macro */
+       switch (ch) {
+           case 0:
+               return;
+           case '(':
+               pdf_puts(buf, "\\(");
+               break;
+           case ')':
+               pdf_puts(buf, "\\)");
+               break;
+           case '\\':
+               pdf_puts(buf, "\\\\");
+               break;
+           case '\n':
+               pdf_puts(buf, "\\n");
+               break;
+           case '\r':
+               pdf_puts(buf, "\\r");
+               break;
+           case '\t':
+               pdf_puts(buf, "\\t");
+               break;
+           default:
+               if (ch > 255) {
+                   pdf_put_char(buf, '?');
+               } else if (ch > 125 || ch < 32) {
+                   pdf_put_char(buf, ch);
+               } else {
+                   char tmp[10];
+                   snprintf(tmp, sizeof(tmp), "\\%03o", (int)ch);
+                   pdf_puts(buf, tmp);
+               }
+       }
+       p++;
+    }
+}
+
+static void pdf_put_string_contents(pdf_buffer *buf, const char *text)
+{
+#ifdef HAVE_MBSTOWCS
+    size_t clen = strlen(text) + 1;
+    wchar_t *cstr = malloc(sizeof(wchar_t) * clen);
+    int text_count = mbstowcs(cstr, text, clen);
+    if (text_count == -1)
+       text_count = mbstowcs(cstr, "Enc-Err", 6);
+    pdf_put_string_contents_wide(buf, cstr);
+#if 0
+    if (*text == 'W') {
+       fprintf(stderr, "Decoding utf8 for '%s'\n", text);
+       wchar_t *p = cstr;
+       char *pp = text;
+       fprintf(stderr, "sz wc = %d\n", sizeof(wchar_t));
+       while (*p) {
+           fprintf(stderr, "  %d = %c  versus %d = %c\n", *p, (char)*p, 255 & (int)*pp, *pp);
+           p++;
+           pp++;
+       }
+    }
+#endif
+    free(cstr);
+#else
+    pdf_put_string_contents_wide(buf, text);
+#endif
+}
+
+static void pdf_init_object(pdf_state *state, pdf_buffer *buf)
+{
+  pdf_init_buffer(state, buf);
+  buf->id = ++state->last_obj_id;
+  buf->is_obj = 1;
+  buf->is_stream = 0;
+}
+
+static void pdf_init_dict(pdf_state *state, pdf_buffer *buf)
+{
+  pdf_init_object(state, buf);
+  buf->is_dict = 1;
+}
+
+static void pdf_set_color(pdf_buffer *buf, gfx_color_t color,
+       gfx_color_t *current_color, const char *op)
+{
+#if USE_PDF_FAKE_ALPHA
+   double a1, a2;
+#endif
+   /* gfx_color_t is RRGGBBAA */
+  if (*current_color == color)
+    return;
+#if USE_PDF_FAKE_ALPHA
+  a1 = (color & 255) / 255.0;
+  a2 = 1 - a1;
+#define pdf_color_calc(x) ( ((x)  & 255) / 255.0 * a1 + a2)
+#else
+#define pdf_color_calc(x) ( ((x)  & 255) / 255.0)
+#endif
+  pdf_putnumber(buf, pdf_color_calc(color >> 24));
+  pdf_puts(buf, " ");
+  pdf_putnumber(buf, pdf_color_calc(color >> 16));
+  pdf_puts(buf, " ");
+  pdf_putnumber(buf, pdf_color_calc(color >>  8));
+  pdf_puts(buf, " ");
+  pdf_puts(buf, op);
+  pdf_puts(buf, "\n");
+  *current_color = color;
+}
+
+static void pdf_set_stroke_color(pdf_buffer *buf, gfx_color_t color)
+{
+    pdf_set_color(buf, color, &buf->state->stroke_color, "RG");
+}
+
+static void pdf_set_fill_color(pdf_buffer *buf, gfx_color_t color)
+{
+    pdf_set_color(buf, color, &buf->state->fill_color, "rg");
+}
+
+static pdf_font *pdf_find_font(pdf_state *state, gfx_node_t *node)
+{
+  const char *ps_font = afm_get_font_postscript_name(node->filename);
+  pdf_font *ef;
+  for (ef = state->font_list; ef; ef = ef->next) {
+    if (!strcmp(ps_font, ef->ps_font))
+      return ef;
+  }
+  return NULL;
+}
+
+static void pdf_add_font(pdf_state *state, gfx_node_t *node)
+{
+  pdf_font *ef = pdf_find_font(state, node);
+  if (ef)
+    return;
+  ef = malloc(sizeof(pdf_font));
+  if (ef == NULL) {
+    rrd_set_error("malloc for pdf_font");
+    state->has_failed = 1;
+    return;
+  }
+  pdf_init_dict(state, &ef->obj);
+  ef->next = state->font_list;
+  ef->ps_font = afm_get_font_postscript_name(node->filename);
+  state->font_list = ef;
+  /* fonts dict */
+  pdf_putsi(&state->fontsdict_obj, "/F");
+  pdf_putint(&state->fontsdict_obj, ef->obj.id);
+  pdf_puts(&state->fontsdict_obj, " ");
+  pdf_putint(&state->fontsdict_obj, ef->obj.id);
+  pdf_puts(&state->fontsdict_obj, " 0 R\n");
+  /* fonts def */
+  pdf_putsi(&ef->obj, "/Type /Font\n");
+  pdf_putsi(&ef->obj, "/Subtype /Type1\n");
+  pdf_putsi(&ef->obj, "/Name /F");
+  pdf_putint(&ef->obj, ef->obj.id);
+  pdf_puts(&ef->obj, "\n");
+  pdf_putsi(&ef->obj, "/BaseFont /");
+  pdf_puts(&ef->obj, ef->ps_font);
+  pdf_puts(&ef->obj, "\n");
+  pdf_putsi(&ef->obj, "/Encoding /WinAnsiEncoding\n");
+  /*  'Cp1252' (this is latin 1 extended with 27 characters;
+      the encoding is also known as 'winansi')
+      http://www.lowagie.com/iText/tutorial/ch09.html */
+}
+
+static void pdf_create_fonts(pdf_state *state)
+{
+  gfx_node_t *node;
+  for (node = state->canvas->firstnode; node; node = node->next) {
+    if (node->type == GFX_TEXT)
+      pdf_add_font(state, node);
+  }
+}
+
+static void pdf_write_linearea(pdf_state *state, gfx_node_t *node)
+{
+  int i;
+  pdf_buffer *s = &state->graph_stream;
+  if (node->type == GFX_LINE) {
+    svg_dash dash_info;
+    svg_get_dash(node, &dash_info);
+    if (!svg_dash_equal(&dash_info, &state->dash)) {
+      state->dash = dash_info;
+      if (dash_info.dash_enable) {
+       pdf_puts(s, "[");
+       pdf_putnumber(s, dash_info.adjusted_on);
+       pdf_puts(s, " ");
+       pdf_putnumber(s, dash_info.adjusted_off);
+       pdf_puts(s, "] ");
+       pdf_putnumber(s, dash_info.dash_offset);
+       pdf_puts(s, " d\n");
+      } else {
+       pdf_puts(s, "[] 0 d\n");
+      }
+    }
+    pdf_set_stroke_color(s, node->color);
+    if (state->linecap != 1) {
+      pdf_puts(s, "1 j\n");
+      state->linecap = 1;
+    }
+    if (state->linejoin != 1) {
+      pdf_puts(s, "1 J\n");
+      state->linejoin = 1;
+    }
+    if (node->size != state->line_width) {
+      state->line_width = node->size;
+      pdf_putnumber(s, state->line_width);
+      pdf_puts(s, " w\n");
+    }
+  } else {
+    pdf_set_fill_color(s, node->color);
+  }
+  for (i = 0; i < node->points; i++) {
+    ArtVpath *vec = node->path + i;
+    double x = vec->x;
+    double y = state->page_height - vec->y;
+    if (node->type == GFX_AREA) {
+      x += LINEOFFSET; /* adjust for libart handling of areas */
+      y -= LINEOFFSET;
+    }
+    switch (vec->code) {
+    case ART_MOVETO_OPEN: /* fall-through */
+    case ART_MOVETO:
+      pdf_putnumber(s, x);
+      pdf_puts(s, " ");
+      pdf_putnumber(s, y);
+      pdf_puts(s, " m\n");
+      break;
+    case ART_LINETO:
+      pdf_putnumber(s, x);
+      pdf_puts(s, " ");
+      pdf_putnumber(s, y);
+      pdf_puts(s, " l\n");
+      break;
+    case ART_CURVETO: break; /* unsupported */
+    case ART_END: break; /* nop */
+    }
+  }
+  if (node->type == GFX_LINE) {
+    pdf_puts(s, node->closed_path ? "s\n" : "S\n");
+   } else {
+    pdf_puts(s, "f\n");
+   }
+}
+
+
+static void pdf_write_matrix(pdf_state *state, gfx_node_t *node, pdf_coords *g, int useTM)
+{
+       char tmp[150];
+       pdf_buffer *s = &state->graph_stream;
+       if (node->angle == 0) {
+               pdf_puts(s, "1 0 0 1 ");
+               pdf_putnumber(s, useTM ? g->tmx : g->mx);
+               pdf_puts(s, " ");
+               pdf_putnumber(s, useTM ? g->tmy : g->my);
+       } else {
+                /* can't use svg_write_number as 2 decimals is far from enough to avoid
+                       skewed text */
+               sprintf(tmp, "%f %f %f %f %f %f",
+                               g->ma, g->mb, g->mc, g->md, 
+                               useTM ? g->tmx : g->mx,
+                               useTM ? g->tmy : g->my);
+               pdf_puts(s, tmp);
+       }
+}
+
+static void pdf_write_text(pdf_state *state, gfx_node_t *node, 
+    int last_was_text, int next_is_text)
+{
+  pdf_coords g;
+  pdf_buffer *s = &state->graph_stream;
+  pdf_font *font = pdf_find_font(state, node);
+  if (font == NULL) {
+    rrd_set_error("font disappeared");
+    state->has_failed = 1;
+    return;
+  }
+  pdf_calc(state->page_height, node, &g);
+#if PDF_CALC_DEBUG
+  pdf_puts(s, "q % debug green box\n");
+  pdf_write_matrix(state, node, &g, 0);
+  pdf_puts(s, " cm\n");
+  pdf_set_fill_color(s, 0x90FF9000);
+  pdf_puts(s, "0 0.4 0 rg\n");
+  pdf_puts(s, "0 0 ");
+  pdf_putnumber(s, g.sizep.x);
+  pdf_puts(s, " ");
+  pdf_putnumber(s, g.sizep.y);
+  pdf_puts(s, " re\n");
+  pdf_puts(s, "f\n");
+  pdf_puts(s, "Q\n");
+#endif
+  pdf_set_fill_color(s, node->color);
+  if (PDF_CALC_DEBUG || !last_was_text)
+    pdf_puts(s, "BT\n");
+  if (state->font_id != font->obj.id || node->size != state->font_size) {
+    state->font_id = font->obj.id;
+    state->font_size = node->size;
+    pdf_puts(s, "/F");
+    pdf_putint(s, font->obj.id);
+    pdf_puts(s, " ");
+    pdf_putnumber(s, node->size);
+    pdf_puts(s, " Tf\n");
+  }
+  pdf_write_matrix(state, node, &g, 1);
+  pdf_puts(s, " Tm\n");
+  pdf_puts(s, "(");
+  pdf_put_string_contents(s, node->text);
+  pdf_puts(s, ") Tj\n");
+  if (PDF_CALC_DEBUG || !next_is_text)
+    pdf_puts(s, "ET\n");
+}
+static void pdf_write_content(pdf_state *state)
+{
+  gfx_node_t *node;
+  int last_was_text = 0, next_is_text;
+  for (node = state->canvas->firstnode; node; node = node->next) {
+    switch (node->type) {
+    case GFX_LINE:
+    case GFX_AREA:
+      pdf_write_linearea(state, node);
+      break;
+    case GFX_TEXT:
+      next_is_text = node->next && node->next->type == GFX_TEXT;
+      pdf_write_text(state, node, last_was_text, next_is_text);
+      break;
+    }
+    last_was_text = node->type == GFX_TEXT;
+  }
+}
+
+static void pdf_init_document(pdf_state *state)
+{
+  pdf_init_buffer(state, &state->pdf_header);
+  pdf_init_dict(state, &state->catalog_obj);
+  pdf_init_dict(state, &state->info_obj);
+  pdf_init_dict(state, &state->pages_obj);
+  pdf_init_dict(state, &state->page1_obj);
+  pdf_init_dict(state, &state->fontsdict_obj);
+  pdf_create_fonts(state);
+  if (state->has_failed)
+    return;
+  /* make stream last object in file */
+  pdf_init_object(state, &state->graph_stream);
+  state->graph_stream.is_stream = 1;
+}
+
+static void pdf_setup_document(pdf_state *state)
+{
+  const char *creator = "RRDtool " PACKAGE_VERSION " Tobias Oetiker, http://tobi.oetiker.ch";
+  /* all objects created by now, so init code can reference them */
+  /* HEADER */
+  pdf_puts(&state->pdf_header, "%PDF-1.3\n");
+  /* following 8 bit comment is recommended by Adobe for
+     indicating binary file to file transfer applications */
+  pdf_puts(&state->pdf_header, "%\xE2\xE3\xCF\xD3\n");
+  /* INFO */
+  pdf_putsi(&state->info_obj, "/Creator (");
+  pdf_put_string_contents(&state->info_obj, creator);
+  pdf_puts(&state->info_obj, ")\n");
+  /* CATALOG */
+  pdf_putsi(&state->catalog_obj, "/Type /Catalog\n");
+  pdf_putsi(&state->catalog_obj, "/Pages ");
+  pdf_putint(&state->catalog_obj, state->pages_obj.id);
+  pdf_puts(&state->catalog_obj, " 0 R\n");
+  /* PAGES */
+  pdf_putsi(&state->pages_obj, "/Type /Pages\n");
+  pdf_putsi(&state->pages_obj, "/Kids [");
+  pdf_putint(&state->pages_obj, state->page1_obj.id);
+  pdf_puts(&state->pages_obj, " 0 R]\n");
+  pdf_putsi(&state->pages_obj, "/Count 1\n");
+  /* PAGE 1 */
+  pdf_putsi(&state->page1_obj, "/Type /Page\n");
+  pdf_putsi(&state->page1_obj, "/Parent ");
+  pdf_putint(&state->page1_obj, state->pages_obj.id);
+  pdf_puts(&state->page1_obj, " 0 R\n");
+  pdf_putsi(&state->page1_obj, "/MediaBox [0 0 ");
+  pdf_putint(&state->page1_obj, state->page_width);
+  pdf_puts(&state->page1_obj, " ");
+  pdf_putint(&state->page1_obj, state->page_height);
+  pdf_puts(&state->page1_obj, "]\n");
+  pdf_putsi(&state->page1_obj, "/Contents ");
+  pdf_putint(&state->page1_obj, state->graph_stream.id);
+  pdf_puts(&state->page1_obj, " 0 R\n");
+  pdf_putsi(&state->page1_obj, "/Resources << /Font ");
+  pdf_putint(&state->page1_obj, state->fontsdict_obj.id);
+  pdf_puts(&state->page1_obj, " 0 R >>\n");
+}
+
+static void pdf_write_string_to_file(pdf_state *state, const char *text)
+{
+    fputs(text, state->fp);
+    state->pdf_file_pos += strlen(text);
+}
+
+static void pdf_write_buf_to_file(pdf_state *state, pdf_buffer *buf)
+{
+  char tmp[40];
+  buf->pdf_file_pos = state->pdf_file_pos;
+  if (buf->is_obj) {
+    snprintf(tmp, sizeof(tmp), "%d 0 obj\n", buf->id);
+    pdf_write_string_to_file(state, tmp);
+  }
+  if (buf->is_dict)
+    pdf_write_string_to_file(state, "<<\n");
+  if (buf->is_stream) {
+    snprintf(tmp, sizeof(tmp), "<< /Length %d >>\n", buf->current_size);
+    pdf_write_string_to_file(state, tmp);
+    pdf_write_string_to_file(state, "stream\n");
+  }
+  fwrite(buf->data, 1, buf->current_size, state->fp);
+  state->pdf_file_pos += buf->current_size;
+  if (buf->is_stream)
+    pdf_write_string_to_file(state, "endstream\n");
+  if (buf->is_dict)
+    pdf_write_string_to_file(state, ">>\n");
+  if (buf->is_obj)
+    pdf_write_string_to_file(state, "endobj\n");
+}
+
+static void pdf_write_to_file(pdf_state *state)
+{
+  pdf_buffer *buf = state->first_buffer;
+  int xref_pos;
+  state->pdf_file_pos = 0;
+  pdf_write_buf_to_file(state, &state->pdf_header);
+  while (buf) {
+    if (buf->is_obj)
+      pdf_write_buf_to_file(state, buf);
+    buf = buf->next_buffer;
+  }
+  xref_pos = state->pdf_file_pos;
+  fprintf(state->fp, "xref\n");
+  fprintf(state->fp, "%d %d\n", 0, state->last_obj_id + 1);
+  /* TOC lines must be exactly 20 bytes including \n */
+  fprintf(state->fp, "%010d %05d f\x20\n", 0, 65535);
+  for (buf = state->first_buffer; buf; buf = buf->next_buffer) {
+    if (buf->is_obj)
+      fprintf(state->fp, "%010d %05d n\x20\n", buf->pdf_file_pos, 0);
+  }
+  fprintf(state->fp, "trailer\n");
+  fprintf(state->fp, "<<\n");
+  fprintf(state->fp, "\t/Size %d\n", state->last_obj_id + 1);
+  fprintf(state->fp, "\t/Root %d 0 R\n", state->catalog_obj.id);
+  fprintf(state->fp, "\t/Info %d 0 R\n", state->info_obj.id);
+  fprintf(state->fp, ">>\n");
+  fprintf(state->fp, "startxref\n");
+  fprintf(state->fp, "%d\n", xref_pos);
+  fputs("%%EOF\n", state->fp);
+}
+
+static void pdf_free_resources(pdf_state *state)
+{
+  pdf_buffer *buf = state->first_buffer;
+  while (buf) {
+    free(buf->data);
+    buf->data = NULL;
+    buf->alloc_size = buf->current_size = 0;
+    buf = buf->next_buffer;
+  }
+  while (state->font_list) {
+    pdf_font *next = state->font_list->next;
+    free(state->font_list);
+    state->font_list = next;
+  }
+}
+
+int       gfx_render_pdf (gfx_canvas_t *canvas,
+                 art_u32 width, art_u32 height,
+                 gfx_color_t UNUSED(background), FILE *fp){
+  struct pdf_state state;
+  memset(&state, 0, sizeof(pdf_state));
+  state.fp = fp;
+  state.canvas = canvas;
+  state.page_width = width;
+  state.page_height = height;
+  state.font_id = -1;
+  state.font_size = -1;
+  state.font_list = NULL;
+  state.linecap = -1;
+  state.linejoin = -1;
+  pdf_init_document(&state);
+  /*
+  pdf_set_color(&state, background);
+  fprintf(fp, "0 0 M 0 %d L %d %d L %d 0 L fill\n",
+      height, width, height, width);
+  */
+  if (!state.has_failed)
+    pdf_write_content(&state);
+  if (!state.has_failed)
+    pdf_setup_document(&state);
+  if (!state.has_failed)
+    pdf_write_to_file(&state);
+  pdf_free_resources(&state);
+  return state.has_failed ? -1 : 0;
+}
+
diff --git a/program/src/rrd_gfx.h b/program/src/rrd_gfx.h
new file mode 100644 (file)
index 0000000..40b0ee0
--- /dev/null
@@ -0,0 +1,135 @@
+/****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd_gfx.h generic graphics adapter library
+ ****************************************************************************/
+
+#ifndef  RRD_GFX_H
+#define RRD_GFX_H
+#define LIBART_COMPILATION
+
+#define y0 libart_y0
+#define y1 libart_y1
+#define gamma libart_gamma
+#include <libart_lgpl/libart.h>
+#include <libart_lgpl/art_rgba.h>
+#include "art_rgba_svp.h"
+#undef gamma
+#undef y0
+#undef y1
+
+
+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 };
+enum gfx_aa_type_en {AA_NORMAL=0,AA_LIGHT,AA_NONE};
+typedef unsigned long gfx_color_t;
+
+typedef struct  gfx_node_t {
+  enum gfx_en   type;         /* type of graph element */
+  gfx_color_t   color;        /* color of element  0xRRGGBBAA  alpha 0xff is solid*/
+  double        size;         /* font size, line width */
+  double        dash_on, dash_off; /* dash line fragments lengths */
+  int           closed_path;
+  int           points;
+  int           points_max;
+  char *filename;             /* font or image filename */
+  char *text;
+  ArtVpath      *path;        /* path */
+  double        x,y;          /* position */
+  double        angle;        /* text angle */
+  enum gfx_h_align_en halign; /* text alignement */
+  enum gfx_v_align_en valign; /* text alignement */
+  double        tabwidth; 
+  struct gfx_node_t  *next; 
+} gfx_node_t;
+
+
+typedef struct gfx_canvas_t 
+{
+    struct gfx_node_t *firstnode;
+    struct gfx_node_t *lastnode;
+    enum gfx_if_en imgformat;      /* image format */
+    int            interlaced;     /* will the graph be interlaced? */
+    double         zoom;           /* zoom for graph */
+    double         font_aa_threshold; /* no anti-aliasing for sizes <= */
+    enum gfx_aa_type_en aa_type;   /* anti-aliasing type (normal/light/none) */
+} gfx_canvas_t;
+
+gfx_canvas_t *gfx_new_canvas (void);
+
+/* create a new line */
+gfx_node_t   *gfx_new_line   (gfx_canvas_t *canvas, 
+                             double X0, double Y0, 
+                             double X1, double Y1,
+                             double width, gfx_color_t color);
+
+gfx_node_t   *gfx_new_dashed_line   (gfx_canvas_t *canvas, 
+                             double X0, double Y0, 
+                             double X1, double Y1,
+                             double width, gfx_color_t color,
+                             double dash_on, double dash_off);
+
+/* create a new area */
+gfx_node_t   *gfx_new_area   (gfx_canvas_t *canvas, 
+                             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 */
+int           gfx_add_point  (gfx_node_t *node, double x, double y);
+
+/* close current path so it ends at the same point as it started */
+void          gfx_close_path  (gfx_node_t *node);
+
+
+/* create a text node */
+gfx_node_t   *gfx_new_text   (gfx_canvas_t *canvas,  
+                             double x, double y, gfx_color_t color,
+                             char* font, double size,                        
+                             double tabwidth, double angle,
+                             enum gfx_h_align_en h_align,
+                             enum gfx_v_align_en v_align,
+                              char* text);
+
+/* measure width of a text string */
+double gfx_get_text_width ( gfx_canvas_t *canvas,
+                           double start, char* font, double size,
+                           double tabwidth, char* text, int rotation);
+
+/* save image to file */
+int       gfx_render (gfx_canvas_t *canvas,
+                              art_u32 width, art_u32 height,
+                              gfx_color_t background, FILE *fo);
+
+/* free memory used by nodes this will also remove memory required for
+   node chain and associated material */
+int           gfx_destroy    (gfx_canvas_t *canvas); 
+
+
+/* PNG support*/
+int       gfx_render_png (gfx_canvas_t *canvas,
+                              art_u32 width, art_u32 height,
+                              gfx_color_t background, FILE *fo);
+double gfx_get_text_width_libart ( gfx_canvas_t *canvas, double start, 
+                char* font, double size, double tabwidth, 
+                char* text, int rotation );
+
+/* SVG support */
+int       gfx_render_svg (gfx_canvas_t *canvas,
+                              art_u32 width, art_u32 height,
+                              gfx_color_t background, FILE *fo);
+
+/* EPS support */
+int       gfx_render_eps (gfx_canvas_t *canvas,
+                              art_u32 width, art_u32 height,
+                              gfx_color_t background, FILE *fo);
+
+/* PDF support */
+int       gfx_render_pdf (gfx_canvas_t *canvas,
+                              art_u32 width, art_u32 height,
+                              gfx_color_t background, FILE *fo);
+
+#endif
diff --git a/program/src/rrd_graph.c b/program/src/rrd_graph.c
new file mode 100644 (file)
index 0000000..22439b5
--- /dev/null
@@ -0,0 +1,3974 @@
+/****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd__graph.c  produce graphs from data in rrdfiles
+ ****************************************************************************/
+
+
+#include <sys/stat.h>
+
+#ifdef WIN32
+#include "strftime.h"
+#endif
+#include "rrd_tool.h"
+
+#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#include <io.h>
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#include "rrd_graph.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 "DejaVuSansMono-Roman.ttf"
+#endif
+
+text_prop_t text_prop[] = {   
+     { 8.0, RRD_DEFAULT_FONT }, /* default */
+     { 9.0, RRD_DEFAULT_FONT }, /* title */
+     { 7.0,  RRD_DEFAULT_FONT }, /* axis */
+     { 8.0, RRD_DEFAULT_FONT }, /* unit */
+     { 8.0, RRD_DEFAULT_FONT }  /* legend */
+};
+
+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,4,           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 */
+{    0xFFFFFFFF,   /* canvas     */
+     0xF0F0F0FF,   /* background */
+     0xD0D0D0FF,   /* shade A    */
+     0xA0A0A0FF,   /* shade B    */
+     0x90909080,   /* grid       */
+     0xE0505080,   /* major grid */
+     0x000000FF,   /* font       */ 
+     0x802020FF,   /* arrow      */
+     0x202020FF,   /* axis       */
+     0x000000FF    /* 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));
+      }
+    }
+    /* make sure we don't return anything too unreasonable. GD lib can
+       get terribly slow when drawing lines outside its scope. This is 
+       especially problematic in connection with the rigid option */
+    if (! im->rigid) {
+      /* keep yval as-is */
+    } else if (yval > im->yorigin) {
+      yval = im->yorigin +0.00001;
+    } else if (yval < im->yorigin - im->ysize){
+      yval = im->yorigin - im->ysize - 0.00001;
+    } 
+    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(DEF,GF_DEF)
+    conv_if(CDEF,GF_CDEF)
+    conv_if(VDEF,GF_VDEF)
+#ifdef WITH_PIECHART
+    conv_if(PART,GF_PART)
+#endif
+    conv_if(XPORT,GF_XPORT)
+    conv_if(SHIFT,GF_SHIFT)
+    
+    return (-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 (-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 (-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 -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)
+    return -1;
+}
+
+
+#undef conv_if
+
+int
+im_free(image_desc_t *im)
+{
+    unsigned long        i,ii;
+
+    if (im == NULL) return 0;
+    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 (im->gdes[i].p_data);
+      free (im->gdes[i].rpnp);
+    }
+    free(im->gdes);
+    gfx_destroy(im->canvas);
+    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(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_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_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 */
+            
+            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 largest common denominator 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 =
+                                 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 = 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;
+}
+
+/* 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 = 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)) minval = 0.2;
+        if (isnan(maxval)) maxval = 5.1;
+    }
+    else {
+        if (isnan(minval)) minval = 0.0;
+        if (isnan(maxval)) maxval = 1.0;
+    }
+    
+    /* adjust min and max values */
+    if (isnan(im->minval) 
+        /* don't adjust low-end with log scale */ /* why not? */
+        || ((!im->rigid) && im->minval > minval)
+        ) {
+        if (im->logarithmic)
+            im->minval = minval * 0.5;
+        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) {
+            im->minval = 0.99 * im->maxval;
+    }
+                      
+    /* make sure min and max are not equal */
+    if (im->minval == im->maxval) {
+        im->maxval *= 1.01; 
+        if (! im->logarithmic) {
+            im->minval *= 0.99;
+        }
+        /* make sure min and max are not both zero */
+        if (im->maxval == 0.0) {
+            im->maxval = 1.0;
+        }
+    }
+    return 0;
+}
+
+
+
+/* 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 -1;        /* -1 because we want the monday */
+        if (tm.tm_wday==0) tm.tm_mday -= 7; /* we want the *previous* monday */
+        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, char ***prdata) 
+{
+    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 prlines = 1;
+    /* wow initializing tmvdef is quite a task :-) */
+    time_t now = time(NULL);
+    localtime_r(&now,&tmvdef);
+    if (im->imginfo) prlines++;
+    for(i=0;i<im->gdes_c;i++){
+            vidx = im->gdes[i].vidx;
+        switch(im->gdes[i].gf){
+        case GF_PRINT:
+            prlines++;
+            if(((*prdata) = rrd_realloc((*prdata),prlines*sizeof(char *)))==NULL){
+                rrd_set_error("realloc prdata");
+                return 0;
+            }
+        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_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){
+                (*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char));
+                (*prdata)[prlines-1] = NULL;
+                if (im->gdes[i].strftm){
+                        strftime((*prdata)[prlines-2],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;
+                  }
+
+#ifdef HAVE_SNPRINTF
+                  snprintf((*prdata)[prlines-2],FMT_LEG_LEN,im->gdes[i].format,printval,si_symb);
+#else
+                  sprintf((*prdata)[prlines-2],im->gdes[i].format,printval,si_symb);
+#endif
+               }
+             } 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_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)
+{
+    /* 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;
+    int   leg_c = 0;
+    int   leg_x = border, leg_y = im->yimg;
+    int   leg_y_prev = im->yimg;
+    int   leg_cc;
+    int   glue = 0;
+    int   i,ii, mark = 0;
+    char  prt_fctn; /*special printfunctions */
+    int  *legspace;
+
+  if( !(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH) ) {
+    if ((legspace = malloc(im->gdes_c*sizeof(int)))==NULL){
+       rrd_set_error("malloc for legspace");
+       return -1;
+    }
+
+    for(i=0;i<im->gdes_c;i++){
+        fill_last = fill;
+        
+        /* hid legends for rules which are not displayed */
+        
+        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';
+        }
+
+        leg_cc = strlen(im->gdes[i].legend);
+        
+        /* is there a controle code ant the end of the legend string ? */ 
+        /* and it is not a tab \\t */
+        if (leg_cc >= 2 && im->gdes[i].legend[leg_cc-2] == '\\' && im->gdes[i].legend[leg_cc-1] != 't') {
+            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 != 's' &&
+            prt_fctn != 't' &&
+            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;
+
+        }
+
+        /* remove exess space */
+        if ( prt_fctn == 'n' ){
+            prt_fctn='l';
+        }
+
+        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 ){
+           legspace[i]=(prt_fctn=='g' ? 0 : interleg);
+           
+           if (fill > 0){ 
+                /* no interleg space if string ends in \g */
+               fill += legspace[i];
+            }
+           fill += gfx_get_text_width(im->canvas, fill+border,
+                                      im->text_prop[TEXT_PROP_LEGEND].font,
+                                      im->text_prop[TEXT_PROP_LEGEND].size,
+                                      im->tabwidth,
+                                      im->gdes[i].legend, 0);
+            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 (i == im->gdes_c -1 ) prt_fctn ='l';
+            
+            /* is it time to place the legends ? */
+            if (fill > im->ximg - 2*border){
+                if (leg_c > 1) {
+                    /* go back one */
+                    i--; 
+                    fill = fill_last;
+                    leg_c--;
+                    prt_fctn = 'j';
+                } else {
+                    prt_fctn = 'l';
+                }
+                
+            }
+        }
+
+
+        if (prt_fctn != '\0'){        
+            leg_x = border;
+            if (leg_c >= 2 && prt_fctn == 'j') {
+                glue = (im->ximg - fill - 2* border) / (leg_c-1);
+            } else {
+                glue = 0;
+            }
+            if (prt_fctn =='c') leg_x =  (im->ximg - fill) / 2.0;
+            if (prt_fctn =='r') leg_x =  im->ximg - 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;
+                leg_x += 
+                 gfx_get_text_width(im->canvas, leg_x,
+                                      im->text_prop[TEXT_PROP_LEGEND].font,
+                                      im->text_prop[TEXT_PROP_LEGEND].size,
+                                      im->tabwidth,
+                                      im->gdes[ii].legend, 0) 
+                   + legspace[ii]
+                   + glue;
+            }                        
+            leg_y_prev = leg_y;
+            /* only add y space if there was text on the line */
+            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;           
+            fill = 0;
+            leg_c = 0;
+            mark = ii;
+        }           
+    }
+    im->yimg = leg_y_prev;
+    /* if we did place some legends we have to add vertical space */
+    if (leg_y != im->yimg){
+        im->yimg += im->text_prop[TEXT_PROP_LEGEND].size*1.8;
+    }
+    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 /= 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.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 {
+            for(i=0;ylab[i].grid > 0;i++){
+                pixel = im->ysize / (scaledrange / ylab[i].grid);
+                   gridind = i;
+                if (pixel > 7)
+                    break;
+            }
+            
+            for(i=0; i<4;i++) {
+               if (pixel * ylab[gridind].lfac[i] >=  2.5 * 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;
+    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++;
+
+               gfx_new_text ( im->canvas,
+                              X0-im->text_prop[TEXT_PROP_AXIS].size, Y0,
+                              im->graph_col[GRC_FONT],
+                              im->text_prop[TEXT_PROP_AXIS].font,
+                              im->text_prop[TEXT_PROP_AXIS].size,
+                              im->tabwidth, 0.0, GFX_H_RIGHT, GFX_V_CENTER,
+                              graph_label );
+               gfx_new_dashed_line ( im->canvas,
+                              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_new_dashed_line ( im->canvas,
+                              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(fabs(x)) / log(10));
+    mnt = x / pow(10.0, iexp);
+    if(mnt >= 10.0) {
+        iexp++;
+        mnt = x / pow(10.0, iexp);
+    }
+    *e = iexp;
+    return mnt;
+}
+
+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;
+}
+
+/* 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_new_dashed_line ( im->canvas,
+            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);
+        gfx_new_text ( im->canvas,
+            X0-im->text_prop[TEXT_PROP_AXIS].size, Y0,
+            im->graph_col[GRC_FONT],
+            im->text_prop[TEXT_PROP_AXIS].font,
+            im->text_prop[TEXT_PROP_AXIS].size,
+            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_new_dashed_line ( im->canvas,
+                    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_new_dashed_line ( im->canvas,
+                    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_new_dashed_line ( im->canvas,
+                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_new_dashed_line ( im->canvas,
+                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_new_dashed_line(im->canvas,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_new_dashed_line(im->canvas,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_new_text ( im->canvas,
+                      xtr(im,tilab), Y0+im->text_prop[TEXT_PROP_AXIS].size*1.4+5,
+                      im->graph_col[GRC_FONT],
+                      im->text_prop[TEXT_PROP_AXIS].font,
+                      im->text_prop[TEXT_PROP_AXIS].size,
+                      im->tabwidth, 0.0, GFX_H_CENTER, GFX_V_BOTTOM,
+                      graph_label );
+       
+    }
+
+}
+
+
+void 
+axis_paint(
+   image_desc_t   *im
+           )
+{   
+    /* draw x and y axis */
+    /* gfx_new_line ( im->canvas, im->xorigin+im->xsize,im->yorigin,
+                      im->xorigin+im->xsize,im->yorigin-im->ysize,
+                      GRIDWIDTH, im->graph_col[GRC_AXIS]);
+       
+       gfx_new_line ( im->canvas, im->xorigin,im->yorigin-im->ysize,
+                         im->xorigin+im->xsize,im->yorigin-im->ysize,
+                         GRIDWIDTH, im->graph_col[GRC_AXIS]); */
+   
+       gfx_new_line ( im->canvas, im->xorigin-4,im->yorigin,
+                         im->xorigin+im->xsize+4,im->yorigin,
+                         MGRIDWIDTH, im->graph_col[GRC_AXIS]);
+   
+       gfx_new_line ( im->canvas, 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->canvas, 
+                   im->xorigin+im->xsize+2,  im->yorigin-2,
+                   im->xorigin+im->xsize+2,  im->yorigin+3,
+                   im->xorigin+im->xsize+7,  im->yorigin+0.5, /* LINEOFFSET */
+                   im->graph_col[GRC_ARROW]);
+
+    gfx_new_area ( im->canvas, 
+                   im->xorigin-2,  im->yorigin-im->ysize-2,
+                   im->xorigin+3,  im->yorigin-im->ysize-2,
+                   im->xorigin+0.5,    im->yorigin-im->ysize-7, /* LINEOFFSET */
+                   im->graph_col[GRC_ARROW]);
+
+}
+
+void
+grid_paint(image_desc_t   *im)
+{   
+    long i;
+    int res=0;
+    double X0,Y0; /* points for filled graph and more*/
+    gfx_node_t *node;
+
+    /* draw 3d border */
+    node = gfx_new_area (im->canvas, 0,im->yimg,
+                                 2,im->yimg-2,
+                                 2,2,im->graph_col[GRC_SHADEA]);
+    gfx_add_point( node , im->ximg - 2, 2 );
+    gfx_add_point( node , im->ximg, 0 );
+    gfx_add_point( node , 0,0 );
+/*    gfx_add_point( node , 0,im->yimg ); */
+   
+    node =  gfx_new_area (im->canvas, 2,im->yimg-2,
+                                  im->ximg-2,im->yimg-2,
+                                  im->ximg - 2, 2,
+                                 im->graph_col[GRC_SHADEB]);
+    gfx_add_point( node ,   im->ximg,0);
+    gfx_add_point( node ,   im->ximg,im->yimg);
+    gfx_add_point( node ,   0,im->yimg);
+/*    gfx_add_point( node , 0,im->yimg ); */
+   
+   
+    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_new_text(im->canvas,im->ximg/2, (2*im->yorigin-im->ysize) / 2,
+                        im->graph_col[GRC_FONT],
+                        im->text_prop[TEXT_PROP_AXIS].font,
+                        im->text_prop[TEXT_PROP_AXIS].size,
+                        im->tabwidth, 0.0, GFX_H_CENTER, GFX_V_CENTER,
+                        nodata );           
+        }
+    }
+
+    /* yaxis unit description */
+    gfx_new_text( im->canvas,
+                  10, (im->yorigin - im->ysize/2),
+                  im->graph_col[GRC_FONT],
+                  im->text_prop[TEXT_PROP_UNIT].font,
+                  im->text_prop[TEXT_PROP_UNIT].size, im->tabwidth, 
+                  RRDGRAPH_YLEGEND_ANGLE,
+                  GFX_H_LEFT, GFX_V_CENTER,
+                  im->ylegend);
+
+    /* graph title */
+    gfx_new_text( im->canvas,
+                  im->ximg/2, im->text_prop[TEXT_PROP_TITLE].size*1.3+4,
+                  im->graph_col[GRC_FONT],
+                  im->text_prop[TEXT_PROP_TITLE].font,
+                  im->text_prop[TEXT_PROP_TITLE].size, im->tabwidth, 0.0,
+                  GFX_H_CENTER, GFX_V_CENTER,
+                  im->title);
+    /* rrdtool 'logo' */
+    gfx_new_text( im->canvas,
+                  im->ximg-7, 7,
+                  ( im->graph_col[GRC_FONT] & 0xffffff00 ) | 0x00000044,
+                  im->text_prop[TEXT_PROP_AXIS].font,
+                  5.5, im->tabwidth, 270,
+                  GFX_H_RIGHT, GFX_V_TOP,
+                  "RRDTOOL / TOBI OETIKER");
+
+    /* graph watermark */
+    if(im->watermark[0] != '\0') {
+        gfx_new_text( im->canvas,
+                  im->ximg/2, im->yimg-6,
+                  ( im->graph_col[GRC_FONT] & 0xffffff00 ) | 0x00000044,
+                  im->text_prop[TEXT_PROP_AXIS].font,
+                  5.5, 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->gdes[i].leg_x;
+                    Y0 = im->gdes[i].leg_y;
+                    gfx_new_text ( im->canvas, X0, Y0,
+                                   im->graph_col[GRC_FONT],
+                                   im->text_prop[TEXT_PROP_LEGEND].font,
+                                   im->text_prop[TEXT_PROP_LEGEND].size,
+                                   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) {
+                            int boxH, boxV;
+                            
+                            boxH = gfx_get_text_width(im->canvas, 0,
+                                                      im->text_prop[TEXT_PROP_LEGEND].font,
+                                                      im->text_prop[TEXT_PROP_LEGEND].size,
+                                                      im->tabwidth,"o", 0) * 1.2;
+                            boxV = boxH*1.1;
+                            
+                            /* make sure transparent colors show up the same way as in the graph */
+                             node = gfx_new_area(im->canvas,
+                                                X0,Y0-boxV,
+                                                X0,Y0,
+                                                X0+boxH,Y0,
+                                                im->graph_col[GRC_BACK]);
+                            gfx_add_point ( node, X0+boxH, Y0-boxV );
+
+                            node = gfx_new_area(im->canvas,
+                                                X0,Y0-boxV,
+                                                X0,Y0,
+                                                X0+boxH,Y0,
+                                                im->gdes[i].col);
+                            gfx_add_point ( node, X0+boxH, Y0-boxV );
+                            node = gfx_new_line(im->canvas,
+                                                X0,Y0-boxV,
+                                                X0,Y0,
+                                                1.0,im->graph_col[GRC_FRAME]);
+                            gfx_add_point(node,X0+boxH,Y0);
+                            gfx_add_point(node,X0+boxH,Y0-boxV);
+                            gfx_close_path(node);
+                    }
+            }
+    }
+}
+
+
+/*****************************************************
+ * 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 (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->canvas->imgformat) {
+    case IF_PNG:
+           size = PngSize(fd,&(im->ximg),&(im->yimg));
+           break;
+    default:
+           size = 1;
+    }
+    fclose(fd);
+    return size;
+}
+
+#ifdef WITH_PIECHART
+void
+pie_part(image_desc_t *im, gfx_color_t color,
+            double PieCenterX, double PieCenterY, double Radius,
+            double startangle, double endangle)
+{
+    gfx_node_t *node;
+    double angle;
+    double step=M_PI/50; /* Number of iterations for the circle;
+                         ** 10 is definitely too low, more than
+                         ** 50 seems to be overkill
+                         */
+
+    /* Strange but true: we have to work clockwise or else
+    ** anti aliasing nor transparency don't work.
+    **
+    ** This test is here to make sure we do it right, also
+    ** this makes the for...next loop more easy to implement.
+    ** The return will occur if the user enters a negative number
+    ** (which shouldn't be done according to the specs) or if the
+    ** programmers do something wrong (which, as we all know, never
+    ** happens anyway :)
+    */
+    if (endangle<startangle) return;
+
+    /* Hidden feature: Radius decreases each full circle */
+    angle=startangle;
+    while (angle>=2*M_PI) {
+        angle  -= 2*M_PI;
+        Radius *= 0.8;
+    }
+
+    node=gfx_new_area(im->canvas,
+                PieCenterX+sin(startangle)*Radius,
+                PieCenterY-cos(startangle)*Radius,
+                PieCenterX,
+                PieCenterY,
+                PieCenterX+sin(endangle)*Radius,
+                PieCenterY-cos(endangle)*Radius,
+                color);
+    for (angle=endangle;angle-startangle>=step;angle-=step) {
+        gfx_add_point(node,
+                PieCenterX+sin(angle)*Radius,
+                PieCenterY-cos(angle)*Radius );
+    }
+}
+
+#endif
+
+int
+graph_size_location(image_desc_t *im, int elements
+
+#ifdef WITH_PIECHART
+, int piechart
+#endif
+
+ )
+{
+    /* 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
+    */
+
+    /* +-+-------------------------------------------+
+    ** |l|.................title.....................|
+    ** |e+--+-------------------------------+--------+
+    ** |b| b|                               |        |
+    ** |a| a|                               |  pie   |
+    ** |l| l|          main graph area      | chart  |
+    ** |.| .|                               |  area  |
+    ** |t| y|                               |        |
+    ** |r+--+-------------------------------+--------+
+    ** |e|  | x-axis labels                 |        |
+    ** |v+--+-------------------------------+--------+
+    ** | |..............legends......................|
+    ** +-+-------------------------------------------+
+    ** |                 watermark                   |
+    ** +---------------------------------------------+
+    */
+    int Xvertical=0,        
+                        Ytitle   =0,
+        Xylabel  =0,        
+        Xmain    =0,        Ymain    =0,
+#ifdef WITH_PIECHART
+        Xpie     =0,        Ypie     =0,
+#endif
+                        Yxlabel  =0,
+#if 0
+        Xlegend  =0,        Ylegend  =0,
+#endif
+        Xspacing =15,  Yspacing =15,
+       
+                      Ywatermark =4;
+
+    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->ylegend[0] != '\0' ) {
+           Xvertical = im->text_prop[TEXT_PROP_UNIT].size *2;
+    }
+
+
+    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.
+        */
+        /* don't care for the with of the title
+                Xtitle = gfx_get_text_width(im->canvas, 0,
+                im->text_prop[TEXT_PROP_TITLE].font,
+                im->text_prop[TEXT_PROP_TITLE].size,
+                im->tabwidth,
+                im->title, 0) + 2*Xspacing; */
+        Ytitle = im->text_prop[TEXT_PROP_TITLE].size*2.6+10;
+    }
+
+    if (elements) {
+        Xmain=im->xsize;
+        Ymain=im->ysize;
+        if (im->draw_x_grid) {
+            Yxlabel=im->text_prop[TEXT_PROP_AXIS].size *2.5;
+        }
+        if (im->draw_y_grid || im->forceleftspace ) {
+            Xylabel=gfx_get_text_width(im->canvas, 0,
+                        im->text_prop[TEXT_PROP_AXIS].font,
+                        im->text_prop[TEXT_PROP_AXIS].size,
+                        im->tabwidth,
+                        "0", 0) * im->unitslength;
+        }
+    }
+
+#ifdef WITH_PIECHART
+    if (piechart) {
+        im->piesize=im->xsize<im->ysize?im->xsize:im->ysize;
+        Xpie=im->piesize;
+        Ypie=im->piesize;
+    }
+#endif
+
+    /* 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. */
+
+    /* The legend width cannot yet be determined, as a result we
+    ** have problems adjusting the image to it.  For now, we just
+    ** forget about it at all; the legend will have to fit in the
+    ** size already allocated.
+    */
+    im->ximg = Xylabel + Xmain + 2 * Xspacing;
+
+#ifdef WITH_PIECHART
+    im->ximg  += Xpie;
+#endif
+
+    if (Xmain) im->ximg += Xspacing;
+#ifdef WITH_PIECHART
+    if (Xpie) im->ximg += Xspacing;
+#endif
+
+    im->xorigin = Xspacing + Xylabel;
+
+    /* the length of the title should not influence with width of the graph
+       if (Xtitle > im->ximg) im->ximg = Xtitle; */
+
+    if (Xvertical) { /* unit description */
+        im->ximg += Xvertical;
+        im->xorigin += Xvertical;
+    }
+    xtr(im,0);
+
+    /* The vertical size is interesting... we need to compare
+    ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend, Ywatermark} with 
+    ** Yvertical however we need to know {Ytitle+Ymain+Yxlabel}
+    ** in order to start even thinking about Ylegend or Ywatermark.
+    **
+    ** Do it in three portions: First calculate the inner part,
+    ** then do the legend, then adjust the total height of the img,
+    ** adding space for a watermark if one exists;
+    */
+
+    /* reserve space for main and/or pie */
+
+    im->yimg = Ymain + Yxlabel;
+    
+#ifdef WITH_PIECHART
+    if (im->yimg < Ypie) im->yimg = Ypie;
+#endif
+
+    im->yorigin = im->yimg - Yxlabel;
+
+    /* reserve space for the title *or* some padding above the graph */
+    if (Ytitle) {
+        im->yimg += Ytitle;
+        im->yorigin += Ytitle;
+    } else {
+        im->yimg += 1.5*Yspacing;
+        im->yorigin += 1.5*Yspacing;
+    }
+    /* reserve space for padding below the graph */
+    im->yimg += Yspacing;
+     
+    /* Determine where to place the legends onto the image.
+    ** Adjust im->yimg to match the space requirements.
+    */
+    if(leg_place(im)==-1)
+        return -1;
+        
+    if (im->watermark[0] != '\0') {
+        im->yimg += Ywatermark;
+    }
+
+#if 0
+    if (Xlegend > im->ximg) {
+        im->ximg = Xlegend;
+        /* reposition Pie */
+    }
+#endif
+
+#ifdef WITH_PIECHART
+    /* The pie is placed in the upper right hand corner,
+    ** just below the title (if any) and with sufficient
+    ** padding.
+    */
+    if (elements) {
+        im->pie_x = im->ximg - Xspacing - Xpie/2;
+        im->pie_y = im->yorigin-Ymain+Ypie/2;
+    } else {
+        im->pie_x = im->ximg/2;
+        im->pie_y = im->yorigin-Ypie/2;
+    }
+#endif
+
+    ytr(im,DNAN);
+    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. */
+   
+
+/* draw that picture thing ... */
+int
+graph_paint(image_desc_t *im, char ***calcpr)
+{
+  int i,ii;
+  int lazy =     lazy_check(im);
+#ifdef WITH_PIECHART
+  int piechart = 0;
+  double PieStart=0.0;
+#endif
+  FILE  *fo;
+  gfx_node_t *node;
+  
+  double areazero = 0.0;
+  graph_desc_t *lastgdes = NULL;    
+
+  /* if we are lazy and there is nothing to PRINT ... quit now */
+  if (lazy && im->prt_c==0) return 0;
+
+  /* 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;
+
+#ifdef WITH_PIECHART  
+  /* check if we need to draw a piechart */
+  for(i=0;i<im->gdes_c;i++){
+    if (im->gdes[i].gf == GF_PART) {
+      piechart=1;
+      break;
+    }
+  }
+#endif
+
+  /* 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 we stop here ... 
+   * if we are lazy, try to quit ... 
+   */
+  i=print_calc(im,calcpr);
+  if(i<0) return -1;
+  if(((i==0)
+#ifdef WITH_PIECHART
+&&(piechart==0)
+#endif
+) || lazy) return 0;
+
+#ifdef WITH_PIECHART
+  /* If there's only the pie chart to draw, signal this */
+  if (i==0) piechart=2;
+#endif
+  
+  /* 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 */
+
+  if (!calc_horizontal_grid(im))
+    return -1;
+
+  if (im->gridfit)
+    apply_gridfit(im);
+
+
+/**************************************************************
+ *** Calculating sizes and locations became a bit confusing ***
+ *** so I moved this into a separate function.              ***
+ **************************************************************/
+  if(graph_size_location(im,i
+#ifdef WITH_PIECHART
+,piechart
+#endif
+)==-1)
+    return -1;
+
+  /* the actual graph is created by going through the individual
+     graph elements and then drawing them */
+  
+  node=gfx_new_area ( im->canvas,
+                      0, 0,
+                      0, im->yimg,
+                      im->ximg, im->yimg,                      
+                      im->graph_col[GRC_BACK]);
+
+  gfx_add_point(node,im->ximg, 0);
+
+#ifdef WITH_PIECHART
+  if (piechart != 2) {
+#endif
+    node=gfx_new_area ( im->canvas,
+                      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(node,im->xorigin, im->yorigin - im->ysize);
+
+    if (im->minval > 0.0)
+      areazero = im->minval;
+    if (im->maxval < 0.0)
+      areazero = im->maxval;
+#ifdef WITH_PIECHART
+   }
+#endif
+
+#ifdef WITH_PIECHART
+  if (piechart) {
+    pie_part(im,im->graph_col[GRC_CANVAS],im->pie_x,im->pie_y,im->piesize*0.5,0,2*M_PI);
+  }
+#endif
+
+  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_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_new_line(im->canvas,
+                                   im -> xorigin + ii, im->yorigin,
+                                   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_new_line(im->canvas,
+                                   im -> xorigin + ii, im->yorigin - im -> ysize,
+                                   im -> xorigin + ii, im->yorigin - ( 1 - 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 != 0x0){   
+        /* GF_LINE and friend */
+        if(im->gdes[i].gf == GF_LINE ){
+          double last_y=0.0;
+          node = NULL;
+          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]))){
+                node = NULL;
+                continue;
+            }
+            if ( node == NULL ) {
+                     last_y = ytr(im,im->gdes[i].p_data[ii]);
+                if ( im->slopemode == 0 ){
+                  node = gfx_new_line(im->canvas,
+                                    ii-1+im->xorigin,last_y,
+                                    ii+im->xorigin,last_y,
+                                    im->gdes[i].linewidth,
+                                    im->gdes[i].col);
+                } else {
+                  node = gfx_new_line(im->canvas,
+                                    ii-1+im->xorigin,ytr(im,im->gdes[i].p_data[ii-1]),
+                                    ii+im->xorigin,last_y,
+                                    im->gdes[i].linewidth,
+                                    im->gdes[i].col);
+                }
+             } else {
+               double new_y = ytr(im,im->gdes[i].p_data[ii]);
+               if ( im->slopemode==0 && ! AlmostEqual2sComplement(new_y,last_y,4)){
+                   gfx_add_point(node,ii-1+im->xorigin,new_y);
+               };
+               last_y = new_y;
+               gfx_add_point(node,ii+im->xorigin,new_y);
+             };
+
+          }
+        } else {
+          int idxI=-1;
+          double *foreY=malloc(sizeof(double)*im->xsize*2);
+          double *foreX=malloc(sizeof(double)*im->xsize*2);
+          double *backY=malloc(sizeof(double)*im->xsize*2);
+          double *backX=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++;}
+               node = gfx_new_area(im->canvas,
+                                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(node,foreX[cntI],foreY[cntI]);
+               }
+               gfx_add_point(node,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(node,backX[idxI],backY[idxI]);
+               }
+               idxI=-1;
+               drawem = 0;
+            }
+            if (drawem != 0){
+              drawem = 0;
+              idxI=-1;
+            }
+            if (ii == im->xsize) break;
+            
+            /* keep things simple for now, just draw these bars
+               do not try to build a big and complex area */
+
+                                                               
+            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;        
+            }
+            /* every area has to be wound clock-wise,
+               so we have to make sur base remains base  */                
+            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;
+#ifdef WITH_PIECHART
+    case GF_PART:
+      if(isnan(im->gdes[i].yrule)) /* fetch variable */
+        im->gdes[i].yrule = im->gdes[im->gdes[i].vidx].vf.val;
+     
+      if (finite(im->gdes[i].yrule)) {        /* even the fetched var can be NaN */
+        pie_part(im,im->gdes[i].col,
+                im->pie_x,im->pie_y,im->piesize*0.4,
+                M_PI*2.0*PieStart/100.0,
+                M_PI*2.0*(PieStart+im->gdes[i].yrule)/100.0);
+        PieStart += im->gdes[i].yrule;
+      }
+      break;
+#endif
+    case GF_STACK:
+      rrd_set_error("STACK should already be turned into LINE or AREA here");
+      return -1;
+      break;
+        
+    } /* switch */
+  }
+#ifdef WITH_PIECHART
+  if (piechart==2) {
+    im->draw_x_grid=0;
+    im->draw_y_grid=0;
+  }
+#endif
+
+
+  /* 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)
+        gfx_new_line(im->canvas,
+                     im->xorigin,ytr(im,im->gdes[i].yrule),
+                     im->xorigin+im->xsize,ytr(im,im->gdes[i].yrule),
+                     1.0,im->gdes[i].col); 
+      break;
+    case GF_VRULE:
+      if(im->gdes[i].xrule >= im->start
+         && im->gdes[i].xrule <= im->end)
+        gfx_new_line(im->canvas,
+                     xtr(im,im->gdes[i].xrule),im->yorigin,
+                     xtr(im,im->gdes[i].xrule),im->yorigin-im->ysize,
+                     1.0,im->gdes[i].col); 
+      break;
+    default:
+      break;
+    }
+  }
+
+  
+  if (strcmp(im->graphfile,"-")==0) {
+    fo = im->graphhandle ? im->graphhandle : stdout;
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+    /* Change translation mode for stdout to BINARY */
+    _setmode( _fileno( fo ), O_BINARY );
+#endif
+  } else {
+    if ((fo = fopen(im->graphfile,"wb")) == NULL) {
+      rrd_set_error("Opening '%s' for write: %s",im->graphfile,
+                    rrd_strerror(errno));
+      return (-1);
+    }
+  }
+  gfx_render (im->canvas,im->ximg,im->yimg,0x00000000,fo);
+  if (strcmp(im->graphfile,"-") != 0)
+    fclose(fo);
+  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].shift=0;
+    im->gdes[im->gdes_c-1].col = 0x0;
+    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].p_data=NULL;    
+    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;
+}
+/* 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()
+*/
+int 
+rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize, FILE *stream, double *ymin, double *ymax)
+{
+    image_desc_t   im;
+    rrd_graph_init(&im);
+    im.graphhandle = stream;
+    
+    rrd_graph_options(argc,argv,&im);
+    if (rrd_test_error()) {
+        im_free(&im);
+        return -1;
+    }
+    
+    if (strlen(argv[optind])>=MAXPATH) {
+        rrd_set_error("filename (including path) too long");
+        im_free(&im);
+        return -1;
+    }
+    strncpy(im.graphfile,argv[optind],MAXPATH-1);
+    im.graphfile[MAXPATH-1]='\0';
+
+    rrd_graph_script(argc,argv,&im,1);
+    if (rrd_test_error()) {
+        im_free(&im);
+        return -1;
+    }
+
+    /* Everything is now read and the actual work can start */
+
+    (*prdata)=NULL;
+    if (graph_paint(&im,prdata)==-1){
+        im_free(&im);
+        return -1;
+    }
+
+    /* The image is generated and needs to be output.
+    ** Also, if needed, print a line with information about the image.
+    */
+
+    *xsize=im.ximg;
+    *ysize=im.yimg;
+    *ymin=im.minval;
+    *ymax=im.maxval;
+    if (im.imginfo) {
+        char *filename;
+        if (!(*prdata)) {
+            /* maybe prdata is not allocated yet ... lets do it now */
+            if ((*prdata = calloc(2,sizeof(char *)))==NULL) {
+                rrd_set_error("malloc imginfo");
+                return -1; 
+            };
+        }
+        if(((*prdata)[0] = malloc((strlen(im.imginfo)+200+strlen(im.graphfile))*sizeof(char)))
+         ==NULL){
+            rrd_set_error("malloc imginfo");
+            return -1;
+        }
+        filename=im.graphfile+strlen(im.graphfile);
+        while(filename > im.graphfile) {
+            if (*(filename-1)=='/' || *(filename-1)=='\\' ) break;
+            filename--;
+        }
+
+        sprintf((*prdata)[0],im.imginfo,filename,(long)(im.canvas->zoom*im.ximg),(long)(im.canvas->zoom*im.yimg));
+    }
+    im_free(&im);
+    return 0;
+}
+
+void
+rrd_graph_init(image_desc_t *im)
+{
+    unsigned int i;
+
+#ifdef HAVE_TZSET
+    tzset();
+#endif
+#ifdef HAVE_SETLOCALE
+    setlocale(LC_TIME,"");
+#ifdef HAVE_MBSTOWCS
+    setlocale(LC_CTYPE,"");
+#endif
+#endif
+    im->yorigin=0;
+    im->xorigin=0;
+    im->minval=0;
+    im->xlab_user.minsec = -1;
+    im->ximg=0;
+    im->yimg=0;
+    im->xsize = 400;
+    im->ysize = 100;
+    im->step = 0;
+    im->ylegend[0] = '\0';
+    im->title[0] = '\0';
+    im->watermark[0] = '\0';
+    im->minval = DNAN;
+    im->maxval = DNAN;    
+    im->unitsexponent= 9999;
+    im->unitslength= 6; 
+    im->forceleftspace = 0;
+    im->symbol = ' ';
+    im->viewfactor = 1.0;
+    im->extra_flags= 0;
+    im->rigid = 0;
+    im->gridfit = 1;
+    im->imginfo = NULL;
+    im->lazy = 0;
+    im->slopemode = 0;
+    im->logarithmic = 0;
+    im->ygridstep = DNAN;
+    im->draw_x_grid = 1;
+    im->draw_y_grid = 1;
+    im->base = 1000;
+    im->prt_c = 0;
+    im->gdes_c = 0;
+    im->gdes = NULL;
+    im->canvas = gfx_new_canvas();
+    im->grid_dash_on = 1;
+    im->grid_dash_off = 1;
+    im->tabwidth = 40.0;
+    
+    for(i=0;i<DIM(graph_col);i++)
+        im->graph_col[i]=graph_col[i];
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+    {
+            char *windir; 
+            char rrd_win_default_font[1000];
+            windir = getenv("windir");
+            /* %windir% is something like D:\windows or C:\winnt */
+            if (windir != NULL) {
+                    strncpy(rrd_win_default_font,windir,500);
+                    rrd_win_default_font[500] = '\0';
+                    strcat(rrd_win_default_font,"\\fonts\\");
+                    strcat(rrd_win_default_font,RRD_DEFAULT_FONT);         
+                    for(i=0;i<DIM(text_prop);i++){
+                            strncpy(text_prop[i].font,rrd_win_default_font,sizeof(text_prop[i].font)-1);
+                            text_prop[i].font[sizeof(text_prop[i].font)-1] = '\0';
+                     }
+             }
+    }
+#endif
+    {
+            char *deffont; 
+            deffont = getenv("RRD_DEFAULT_FONT");
+            if (deffont != NULL) {
+                 for(i=0;i<DIM(text_prop);i++){
+                        strncpy(text_prop[i].font,deffont,sizeof(text_prop[i].font)-1);
+                        text_prop[i].font[sizeof(text_prop[i].font)-1] = '\0';
+                 }
+            }
+    }
+    for(i=0;i<DIM(text_prop);i++){        
+      im->text_prop[i].size = text_prop[i].size;
+      strcpy(im->text_prop[i].font,text_prop[i].font);
+    }
+}
+
+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;
+    struct rrd_time_value        start_tv, end_tv;
+    gfx_color_t         color;
+    optind = 0; opterr = 0;  /* initialize getopt */
+
+    parsetime("end-24h", &start_tv);
+    parsetime("now", &end_tv);
+
+    /* 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
+
+    while (1){
+        static struct option long_options[] =
+        {
+            {"start",      required_argument, 0,  's'},
+            {"end",        required_argument, 0,  'e'},
+            {"x-grid",     required_argument, 0,  'x'},
+            {"y-grid",     required_argument, 0,  'y'},
+            {"vertical-label",required_argument,0,'v'},
+            {"width",      required_argument, 0,  'w'},
+            {"height",     required_argument, 0,  'h'},
+            {"interlaced", no_argument,       0,  'i'},
+            {"upper-limit",required_argument, 0,  'u'},
+            {"lower-limit",required_argument, 0,  'l'},
+            {"rigid",      no_argument,       0,  'r'},
+            {"base",       required_argument, 0,  'b'},
+            {"logarithmic",no_argument,       0,  'o'},
+            {"color",      required_argument, 0,  'c'},
+            {"font",       required_argument, 0,  'n'},
+            {"title",      required_argument, 0,  't'},
+            {"imginfo",    required_argument, 0,  'f'},
+            {"imgformat",  required_argument, 0,  'a'},
+            {"lazy",       no_argument,       0,  'z'},
+            {"zoom",       required_argument, 0,  'm'},
+            {"no-legend",  no_argument,       0,  'g'},
+            {"force-rules-legend",no_argument,0,  'F'},
+            {"only-graph", no_argument,       0,  'j'},
+            {"alt-y-grid", no_argument,       0,  'Y'},
+            {"no-minor",   no_argument,       0,  'I'},
+            {"slope-mode", no_argument,              0,  'E'},
+            {"alt-autoscale", no_argument,    0,  'A'},
+           {"alt-autoscale-min", no_argument, 0, 'J'},
+            {"alt-autoscale-max", no_argument, 0, 'M'},
+            {"no-gridfit", no_argument,       0,   'N'},
+            {"units-exponent",required_argument, 0, 'X'},
+            {"units-length",required_argument, 0, 'L'},
+            {"units",      required_argument, 0,  LONGOPT_UNITS_SI },
+            {"step",       required_argument, 0,    'S'},
+            {"tabwidth",   required_argument, 0,    'T'},            
+            {"font-render-mode", required_argument, 0, 'R'},
+            {"font-smoothing-threshold", required_argument, 0, 'B'},
+            {"watermark",  required_argument, 0,  'W'},
+            {"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 */
+            {0,0,0,0}};
+        int option_index = 0;
+        int opt;
+        int col_start,col_end;
+
+        opt = getopt_long(argc, argv, 
+                         "s:e:x:y:v:w:h:iu:l:rb:oc:n:m:t:f:a:I:zgjFYAMEX:L:S:T:NR:B:W:",
+                          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 'F':
+            im->extra_flags |= FORCE_RULES_LEGEND;
+            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 's':
+            if ((parsetime_error = parsetime(optarg, &start_tv))) {
+                rrd_set_error( "start time: %s", parsetime_error );
+                return;
+            }
+            break;
+        case 'e':
+            if ((parsetime_error = 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 '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 'i':
+            im->canvas->interlaced = 1;
+            break;
+        case 'r':
+            im->rigid = 1;
+            break;
+        case 'f':
+            im->imginfo = optarg;
+            break;
+            case 'a':
+            if((int)(im->canvas->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]=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;
+            char font[1024] = "";
+
+            if(sscanf(optarg,
+                                "%10[A-Z]:%lf:%1000s",
+                                prop,&size,font) >= 2){
+                int sindex,propidx;
+                if((sindex=text_prop_conv(prop)) != -1){
+                  for (propidx=sindex;propidx<TEXT_PROP_LAST;propidx++){                        
+                        if (size > 0){
+                              im->text_prop[propidx].size=size;              
+                      }
+                       if (strlen(font) > 0){
+                          strcpy(im->text_prop[propidx].font,font);
+                      }
+                      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->canvas->zoom = atof(optarg);
+            if (im->canvas->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 )
+                        im->canvas->aa_type = AA_NORMAL;
+                else if ( strcmp( optarg, "light" ) == 0 )
+                        im->canvas->aa_type = AA_LIGHT;
+                else if ( strcmp( optarg, "mono" ) == 0 )
+                        im->canvas->aa_type = AA_NONE;
+                else
+                {
+                        rrd_set_error("unknown font-render-mode '%s'", optarg );
+                        return;
+                }
+                break;
+
+        case 'B':
+            im->canvas->font_aa_threshold = atof(optarg);
+                break;
+
+        case 'W':
+            strncpy(im->watermark,optarg,100);
+            im->watermark[99]='\0';
+            break;
+
+        case '?':
+            if (optopt != 0)
+                rrd_set_error("unknown option '%c'", optopt);
+            else
+                rrd_set_error("unknown option '%s'",argv[optind-1]);
+            return;
+        }
+    }
+    
+    if (optind >= argc) {
+       rrd_set_error("missing filename");
+       return;
+    }
+
+    if (im->logarithmic == 1 && im->minval <= 0){
+        rrd_set_error("for a logarithmic yaxis you must specify a lower-limit > 0");        
+        return;
+    }
+
+    if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
+        /* error string is set in 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;
+        gfx_color_t    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 = 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(gdes,str)
+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("MAXIMUM",func)) gdes->vf.op = VDEF_MAXIMUM;
+    else if        (!strcmp("AVERAGE",func)) gdes->vf.op = VDEF_AVERAGE;
+    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:
+            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_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(im,gdi)
+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 = 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 = (steps-1)*dst->vf.param/100;
+                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_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_AVERAGE: {
+            int cnt=0;
+            double sum=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 {
+                    dst->vf.val = sum/cnt;
+                    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(a,b)
+const void *a,*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;
+}
diff --git a/program/src/rrd_graph.h b/program/src/rrd_graph.h
new file mode 100644 (file)
index 0000000..e83b442
--- /dev/null
@@ -0,0 +1,263 @@
+#ifndef _RRD_GRAPH_H
+#define _RRD_GRAPH_H
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include "rrd_gfx.h"
+
+#define MAX_VNAME_LEN 255
+#define DEF_NAM_FMT "%255[-_A-Za-z0-9]"
+
+#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) */
+
+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_DEF, GF_CDEF, GF_VDEF, GF_SHIFT,
+#ifdef WITH_PIECHART
+           GF_PART,
+#endif
+            GF_XPORT};
+
+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_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 */
+               };
+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,      /* fot the legend below the graph */
+                   TEXT_PROP_LAST };
+
+typedef struct text_prop_t {
+  double       size;
+  char         font[1024];
+} 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() */
+    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 */
+} graph_desc_t;
+
+typedef struct image_desc_t {
+
+    /* configuration of graph */
+
+    char           graphfile[MAXPATH]; /* filename for graphic */
+    FILE         *graphhandle;        /* FILE to use if filename is "-" */
+    long           xsize,ysize;        /* graph area size in pixels */
+#ifdef WITH_PIECHART
+    long          piesize;            /* size of the piechart */
+#endif
+    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 x-grid at all */
+    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         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 */
+    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 */
+    int            logarithmic;    /* scale the yaxis logarithmic */
+    
+    /* status information */
+           
+    long           xorigin,yorigin;/* where is (0,0) of the graph */
+#ifdef WITH_PIECHART
+    long           pie_x,pie_y;    /* where is the centerpoint */
+#endif
+    long           ximg,yimg;      /* total size of the image */
+    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 */
+
+    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 */
+    gfx_canvas_t   *canvas;        /* graphics library */
+} 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 *, char ***);
+int leg_place(image_desc_t *);
+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 *, char ***);
+#ifdef WITH_PIECHART
+void pie_part(image_desc_t *, gfx_color_t, double, double, double, double, double);
+#endif
+int gdes_alloc(image_desc_t *);
+int scan_for_col(const char *const , int, char *const);
+int rrd_graph(int, char **, char ***, int *, int *, FILE *, double *, double *);
+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
+#ifdef WITH_PIECHART
+ ,int
+#endif
+);
+
+#endif
diff --git a/program/src/rrd_graph_helper.c b/program/src/rrd_graph_helper.c
new file mode 100644 (file)
index 0000000..20198ed
--- /dev/null
@@ -0,0 +1,866 @@
+/****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * 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_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 = 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 {
+       rrd_clear_error();
+       i=0; sscanf(&line[*eaten],"%li%n",&gdp->shval,&i);
+       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;
+}
+
+/* Parsing of PART, VRULE, HRULE, LINE, AREA, STACK and TICK
+** is done in one function.
+**
+** Stacking PART, 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->canvas, 0,
+                               im->text_prop[TEXT_PROP_LEGEND].font,
+                               im->text_prop[TEXT_PROP_LEGEND].size,
+                               im->tabwidth,"    ", 0) / 4.0;
+       float target_space = gfx_get_text_width(im->canvas, 0,
+                               im->text_prop[TEXT_PROP_LEGEND].font,
+                               im->text_prop[TEXT_PROP_LEGEND].size,
+                               im->tabwidth,"oo", 0);
+       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) {
+#ifdef WITH_PIECHART
+           case GF_PART:
+#endif
+           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 {
+       dprintf("- it is not an existing vname\n");
+       switch (gdp->gf) {
+           case GF_VRULE:
+               k=0;sscanf(tmpstr,"%li%n",&gdp->xrule,&k);
+               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: %f\n",gdp->yrule);
+               } else {
+                   dprintf("- is is not a valid number: %li\n",gdp->xrule);
+                   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 0x%08x\n",(unsigned int)gdp->col);
+       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 */
+
+    /* PART, HRULE, VRULE and TICK cannot be stacked. */
+    if (   (gdp->gf == GF_HRULE)
+       || (gdp->gf == GF_VRULE)
+#ifdef WITH_PIECHART
+       || (gdp->gf == GF_PART)
+#endif
+       || (gdp->gf == GF_TICK)
+       ) return 0;
+
+    dprintf("- parsing '%s'\n",&line[*eaten]);
+    if (line[*eaten]!='\0') {
+       dprintf("- still more, should be STACK\n");
+       j=scan_for_col(&line[*eaten],5,tmpstr);
+       if (line[*eaten+j]!='\0' && line[*eaten+j]!=':') {
+           /* not 5 chars */
+           rrd_set_error("Garbage found where STACK expected");
+           return 1;
+       }
+       if (!strcmp("STACK",tmpstr)) {
+           dprintf("- found STACK\n");
+           gdp->stack=1;
+           (*eaten)+=j;
+       } else {
+           rrd_set_error("Garbage found where STACK expected");
+           return 1;
+       }
+    }
+    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;
+    }
+    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];
+    struct rrd_time_value      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 = 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 = 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 (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
+       /* error string is set in 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_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;
+#ifdef WITH_PIECHART
+           case GF_PART:       /* value[#color[:legend]] */
+#endif
+           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 %i 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("Garbage '%s' after command:\n%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..dccde50
--- /dev/null
@@ -0,0 +1,843 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_hw.c : Support for Holt-Winters Smoothing/ Aberrant Behavior Detection
+ *****************************************************************************
+ * Initial version by Jake Brutlag, WebTV Networks, 5/1/00
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_hw.h"
+
+/* #define DEBUG */
+
+/* private functions */
+unsigned long MyMod(signed long val, unsigned long mod);
+int update_hwpredict(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx, 
+                 unsigned long ds_idx, unsigned short CDP_scratch_idx);
+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);
+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);
+int update_failures(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx, 
+                               unsigned long ds_idx, unsigned short CDP_scratch_idx);
+
+int
+update_hwpredict(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx, 
+                 unsigned long ds_idx, unsigned short CDP_scratch_idx)
+{
+   rrd_value_t prediction, seasonal_coef;
+   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]);
+
+   /* 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;
+   if (dependent_rra_idx < rra_idx)
+         seasonal_coef = rrd -> cdp_prep[seasonal_cdp_idx].scratch[CDP_hw_last_seasonal].u_val;
+   else
+         seasonal_coef = 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)) 
+     {
+#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;
+     }
+     /* if seasonal coefficient is NA, then don't update intercept, slope */
+   } else {
+     prediction = 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\n",prediction);
+#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 {
+#ifdef DEBUG
+       fprintf(stderr,"Updating intercept, slope\n");
+#endif
+       /* update the intercept */
+       coefs[CDP_hw_intercept].u_val = (current_rra -> par[RRA_hw_alpha].u_val)*
+               (coefs[CDP_scratch_idx].u_val - seasonal_coef) +
+               (1 - current_rra -> par[RRA_hw_alpha].u_val)*(coefs[CDP_hw_intercept].u_val
+               + (coefs[CDP_hw_slope].u_val)*(coefs[CDP_null_count].u_cnt));
+       /* update the slope */
+       coefs[CDP_hw_slope].u_val = (current_rra -> par[RRA_hw_beta].u_val)*
+               (coefs[CDP_hw_intercept].u_val - coefs[CDP_hw_last_intercept].u_val) +
+               (1 - current_rra -> par[RRA_hw_beta].u_val)*(coefs[CDP_hw_slope].u_val);
+       /* reset the null count */
+       coefs[CDP_null_count].u_cnt = 1;
+     }
+   }
+
+   /* store the prediction for writing */
+   coefs[CDP_scratch_idx].u_val = prediction;
+   return 0;
+}
+
+int
+lookup_seasonal(rrd_t *rrd, unsigned long rra_idx, unsigned long rra_start,
+                               FILE *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 (!fseek(rrd_file,pos_tmp,SEEK_SET))
+   {
+      if (fread(*seasonal_coef,sizeof(rrd_value_t),rrd->stat_head->ds_cnt,rrd_file)
+                 == 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;
+}
+
+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)
+{
+/* 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];
+
+   /* 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(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;
+         } else 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 
+                       -= coefs[CDP_hw_last_intercept].u_val; 
+         } else {
+                intercept = coefs[CDP_hw_intercept].u_val;
+#ifdef DEBUG
+                fprintf(stderr,
+                       "Updating seasonal, params: gamma %f, new intercept %f, old seasonal %f\n",
+                       current_rra -> par[RRA_seasonal_gamma].u_val,
+                       intercept, seasonal);
+#endif
+                rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+                       (current_rra -> par[RRA_seasonal_gamma].u_val)*
+                       (rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val - intercept) +
+                       (1 - current_rra -> par[RRA_seasonal_gamma].u_val)*seasonal;
+         }
+   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(rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val))
+         {
+                /* no update, simple store the old value unchanged,
+                 * doesn't matter if it is NA */
+                rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =  seasonal;
+         } else 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. */
+                rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val= 0.0;
+         } else if (isnan(seasonal))
+         {
+                /* initialization: intercept will not be updated
+                 * CDP_hw_intercept = CDP_hw_last_intercept; just need to 
+                 * subtract 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 -= 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 = (hw_rra -> par[RRA_hw_alpha].u_val)*
+                   (rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val - seasonal)
+                   + (1 - hw_rra -> par[RRA_hw_alpha].u_val)*(coefs[CDP_hw_intercept].u_val
+                   + (coefs[CDP_hw_slope].u_val)*(coefs[CDP_null_count].u_cnt));
+                rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+                  (current_rra -> par[RRA_seasonal_gamma].u_val)*
+                  (rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val - intercept) +
+                  (1 - current_rra -> par[RRA_seasonal_gamma].u_val)*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)
+{
+   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 = 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 = 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 =
+            fabs(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 =
+           (rrd -> rra_def[rra_idx].par[RRA_seasonal_gamma].u_val)*
+           fabs(prediction - rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val)
+           + (1 -  rrd -> rra_def[rra_idx].par[RRA_seasonal_gamma].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)
+{
+   /* 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 = 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 = 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 (rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val > prediction + 
+                (current_rra -> par[RRA_delta_pos].u_val)*deviation
+            || rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val < prediction - 
+                (current_rra -> par[RRA_delta_neg].u_val)*deviation)
+                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);
+}
+
+/* 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,
+               FILE *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;
+
+   offset = floor(0.025*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 (fseek(rrd_file,rra_start,SEEK_SET))
+   {
+         rrd_set_error("seek to rra %d failed", rra_start);
+         free(rrd_values);
+         return -1;
+   }
+   fflush(rrd_file);
+   /* 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)
+         {
+                fread(&(rrd_values[i*row_length + j]),sizeof(rrd_value_t),1,rrd_file);
+                /* should check fread for errors... */
+                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) {
+   for (j = 0; j < row_length; ++j)
+   {
+   for (i = 0; i < row_count; ++i)
+   {
+        rrd_values[i*row_length + j] -= baseline[j];
+   }
+        /* update the baseline coefficient,
+         * first, compute the cdp_index. */
+        offset = (rrd->rra_def[rra_idx].par[RRA_dependent_rra_idx].u_cnt)
+         * row_length + j;
+        (rrd->cdp_prep[offset]).scratch[CDP_hw_intercept].u_val += baseline[j];
+   }
+   /* flush cdp to disk */
+   fflush(rrd_file);
+   if (fseek(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 (fwrite( rrd -> cdp_prep,
+         sizeof(cdp_prep_t),
+         (rrd->stat_head->rra_cnt) * rrd->stat_head->ds_cnt, rrd_file) 
+         != (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 */
+   fflush(rrd_file);
+   if (fseek(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 (fwrite(rrd_values,sizeof(rrd_value_t),row_length*row_count,rrd_file)
+         != row_length*row_count)
+   {
+         rrd_set_error("apply_smoother: write failed to %lu",rra_start);
+         free(rrd_values);
+         return -1;
+   }
+
+   fflush(rrd_file);
+   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, FILE *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:
+               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 */
+                       fseek(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 (fwrite(&nan_buffer,sizeof(rrd_value_t),1,rrd_file) != 1)
+                          {
+                  rrd_set_error(
+                                 "reset_aberrant_coefficients: write failed data source %lu rra %s",
+                                 ds_idx,rrd->rra_def[rra_idx].cf_nam);
+                                 return;
+                          } 
+                          fseek(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);
+   }
+   fseek(rrd_file,cdp_start,SEEK_SET);
+   if (fwrite( rrd -> cdp_prep,
+         sizeof(cdp_prep_t),
+         (rrd->stat_head->rra_cnt) * rrd->stat_head->ds_cnt, rrd_file) 
+         != (rrd->stat_head->rra_cnt) * (rrd->stat_head->ds_cnt) )
+   {
+         rrd_set_error("reset_aberrant_coefficients: cdp_prep write failed");
+         return;
+   }
+}
+
+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)
+{
+   rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = pdp_val;
+   switch (current_cf) {
+   case CF_AVERAGE:
+   default:
+        return 0;
+   case CF_HWPREDICT:
+     return update_hwpredict(rrd,cdp_idx,rra_idx,ds_idx,CDP_scratch_idx);
+   case CF_DEVPREDICT:
+        return update_devpredict(rrd,cdp_idx,rra_idx,ds_idx,CDP_scratch_idx);
+   case CF_SEASONAL:
+     return update_seasonal(rrd,cdp_idx,rra_idx,ds_idx,CDP_scratch_idx,seasonal_coef);
+   case CF_DEVSEASONAL:
+     return update_devseasonal(rrd,cdp_idx,rra_idx,ds_idx,CDP_scratch_idx,seasonal_coef);
+   case CF_FAILURES:
+     return update_failures(rrd,cdp_idx,rra_idx,ds_idx,CDP_scratch_idx);
+   }
+   return -1;
+}
+
+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..89220b4
--- /dev/null
@@ -0,0 +1,33 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * 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,
+   FILE *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,
+   FILE *rrd_file);
+void reset_aberrant_coefficients(rrd_t *rrd, FILE *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_info.c b/program/src/rrd_info.c
new file mode 100644 (file)
index 0000000..d5ccbc5
--- /dev/null
@@ -0,0 +1,238 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_info  Get Information about the configuration of an RRD
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include <stdarg.h>
+
+/* proto */
+info_t *rrd_info(int, char **);
+info_t *rrd_info_r(char *filename);
+
+/* allocate memory for string */
+char *
+sprintf_alloc(char *fmt, ...) {
+#ifdef HAVE_VSNPRINTF    
+    int maxlen = 50;
+#else
+    int maxlen = 1000;
+#endif
+    char *str = NULL;
+    va_list argp;
+    str = malloc(sizeof(char)*(strlen(fmt)+maxlen));
+    if (str != NULL) {
+       va_start(argp, fmt);
+#ifdef HAVE_VSNPRINTF
+       vsnprintf(str, maxlen-1, fmt, argp);
+#else
+       vsprintf(str, fmt, argp);
+#endif
+    }
+    va_end(argp);
+    return str;
+}
+/* the function formerly known as push was renamed info_push because
+ * it is now used outside the scope of this file */
+info_t 
+*info_push(info_t *info, char *key, enum info_type type, infoval value){
+    info_t *next;
+    next = malloc(sizeof(*next));
+    next->next = (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 = malloc(sizeof(char)*(strlen(value.u_str)+1));
+       strcpy(next->value.u_str,value.u_str);
+       break;
+    }
+    return(next);
+}
+
+
+info_t *
+rrd_info(int argc, char **argv) {   
+    info_t             *info;
+
+    if(argc < 2){
+        rrd_set_error("please specify an rrd");
+        return NULL;
+    }
+
+    info = rrd_info_r(argv[1]);
+
+    return(info);
+}
+
+
+  
+info_t *
+rrd_info_r(char *filename) {   
+    unsigned int i,ii=0;
+    FILE         *in_file;
+    rrd_t        rrd;
+    info_t       *data,*cd;
+    infoval      info;
+       enum cf_en   current_cf;
+       enum dst_en  current_ds;
+
+    if(rrd_open(filename,&in_file,&rrd, RRD_READONLY)==-1){
+       return(NULL);
+    }
+    fclose(in_file);
+
+    info.u_str=filename;
+    cd=info_push(NULL,sprintf_alloc("filename"),    RD_I_STR, info);
+    data=cd;
+
+    info.u_str=rrd.stat_head->version;
+    cd=info_push(cd,sprintf_alloc("rrd_version"),    RD_I_STR, info);
+
+    info.u_cnt=rrd.stat_head->pdp_step;
+    cd=info_push(cd,sprintf_alloc("step"),       RD_I_CNT, info);
+
+    info.u_cnt=rrd.live_head->last_up;
+    cd=info_push(cd,sprintf_alloc("last_update"), RD_I_CNT, info);
+
+    for(i=0;i<rrd.stat_head->ds_cnt;i++){
+
+       info.u_str=rrd.ds_def[i].dst;
+       cd=info_push(cd,sprintf_alloc("ds[%s].type",             rrd.ds_def[i].ds_nam), 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=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=info_push(cd,sprintf_alloc("ds[%s].minimal_heartbeat",rrd.ds_def[i].ds_nam), RD_I_CNT, info);
+
+          info.u_val=rrd.ds_def[i].par[DS_min_val].u_val;
+          cd=info_push(cd,sprintf_alloc("ds[%s].min",rrd.ds_def[i].ds_nam), RD_I_VAL, info);
+       
+          info.u_val=rrd.ds_def[i].par[DS_max_val].u_val;
+          cd=info_push(cd,sprintf_alloc("ds[%s].max",rrd.ds_def[i].ds_nam), RD_I_VAL, info);
+          break;
+       }
+       
+       info.u_str=rrd.pdp_prep[i].last_ds;
+       cd=info_push(cd,sprintf_alloc("ds[%s].last_ds",          rrd.ds_def[i].ds_nam), RD_I_STR, info);
+
+       info.u_val=rrd.pdp_prep[i].scratch[PDP_val].u_val;
+        cd=info_push(cd,sprintf_alloc("ds[%s].value",            rrd.ds_def[i].ds_nam), RD_I_VAL, info);
+
+       info.u_cnt=rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt;
+       cd=info_push(cd,sprintf_alloc("ds[%s].unknown_sec",      rrd.ds_def[i].ds_nam), RD_I_CNT, info);
+    }
+
+    for(i=0;i<rrd.stat_head->rra_cnt;i++){
+       info.u_str=rrd.rra_def[i].cf_nam;
+       cd=info_push(cd,sprintf_alloc("rra[%d].cf",         i),  RD_I_STR,   info);
+       current_cf = cf_conv(rrd.rra_def[i].cf_nam);
+
+       info.u_cnt=rrd.rra_def[i].row_cnt;
+       cd=info_push(cd,sprintf_alloc("rra[%d].rows",i),  RD_I_CNT,   info);
+
+       info.u_cnt=rrd.rra_def[i].pdp_cnt;
+       cd=info_push(cd,sprintf_alloc("rra[%d].pdp_per_row",i),  RD_I_CNT,   info);
+
+       switch(current_cf)
+       {
+          case CF_HWPREDICT:
+                 info.u_val=rrd.rra_def[i].par[RRA_hw_alpha].u_val;
+                 cd=info_push(cd,sprintf_alloc("rra[%d].alpha",i),RD_I_VAL,info);
+                 info.u_val=rrd.rra_def[i].par[RRA_hw_beta].u_val;
+                 cd=info_push(cd,sprintf_alloc("rra[%d].beta",i),RD_I_VAL,info);
+                 break;
+          case CF_SEASONAL:
+          case CF_DEVSEASONAL:
+                 info.u_val=rrd.rra_def[i].par[RRA_seasonal_gamma].u_val;
+                 cd=info_push(cd,sprintf_alloc("rra[%d].gamma",i),RD_I_VAL,info);
+                 break;
+          case CF_FAILURES:
+                 info.u_val=rrd.rra_def[i].par[RRA_delta_pos].u_val;
+                 cd=info_push(cd,sprintf_alloc("rra[%d].delta_pos",i),RD_I_VAL,info);
+                 info.u_val=rrd.rra_def[i].par[RRA_delta_neg].u_val;
+                 cd=info_push(cd,sprintf_alloc("rra[%d].delta_neg",i),RD_I_VAL,info);
+                 info.u_cnt=rrd.rra_def[i].par[RRA_failure_threshold].u_cnt;
+                 cd=info_push(cd,sprintf_alloc("rra[%d].failure_threshold",i),RD_I_CNT,info);
+                 info.u_cnt=rrd.rra_def[i].par[RRA_window_len].u_cnt;
+                 cd=info_push(cd,sprintf_alloc("rra[%d].window_length",i),RD_I_CNT,info);
+                 break;
+          case CF_DEVPREDICT:
+                 break;
+          default:
+                 info.u_val=rrd.rra_def[i].par[RRA_cdp_xff_val].u_val;
+                 cd=info_push(cd,sprintf_alloc("rra[%d].xff",i),RD_I_VAL,info);
+                 break;
+       }
+
+       for(ii=0;ii<rrd.stat_head->ds_cnt;ii++){
+        switch(current_cf)
+               {
+               case CF_HWPREDICT:
+           info.u_val=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_intercept].u_val;
+           cd=info_push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].intercept",i,ii), RD_I_VAL, info);
+           info.u_val=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_slope].u_val;
+           cd=info_push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].slope",i,ii), RD_I_VAL, info);
+           info.u_cnt=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_null_count].u_cnt;
+           cd=info_push(cd,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=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=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=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=info_push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].value",i,ii), RD_I_VAL, info);
+           info.u_cnt=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_unkn_pdp_cnt].u_cnt;
+           cd=info_push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].unknown_datapoints",i,ii), RD_I_CNT, info);
+                  break;
+        }
+    }
+       }
+       rrd_free(&rrd);
+    return(data);
+
+}
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..6fe480e
--- /dev/null
@@ -0,0 +1,29 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ * 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..c4b4b26
--- /dev/null
@@ -0,0 +1,40 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_last.c
+ *****************************************************************************
+ * Initial version by Russ Wright, @Home Network, 9/28/98
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+
+time_t
+rrd_last(int argc, char **argv)
+{
+    if(argc < 2){
+        rrd_set_error("please specify an rrd");
+        return(-1);
+    }
+
+    return( rrd_last_r(argv[1]) );
+}
+
+time_t
+rrd_last_r(const char *filename)
+{
+    FILE       *in_file;
+    time_t       lastup;
+
+    rrd_t       rrd;
+
+    if(rrd_open(filename, &in_file, &rrd, RRD_READONLY)==-1){
+        return(-1);
+    }
+    lastup = rrd.live_head->last_up;
+    rrd_free(&rrd);
+    fclose(in_file);
+    return(lastup);
+}
+
+
diff --git a/program/src/rrd_lastupdate.c b/program/src/rrd_lastupdate.c
new file mode 100644 (file)
index 0000000..169c49d
--- /dev/null
@@ -0,0 +1,54 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_lastupdate  Get the last datum entered for each DS
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include <stdarg.h>
+
+int
+rrd_lastupdate(int argc, char **argv, time_t *last_update,
+                 unsigned long *ds_cnt, char ***ds_namv, char ***last_ds) {
+    unsigned long i=0;
+    char        *filename;
+    FILE         *in_file;
+    rrd_t        rrd;
+
+    if(argc < 2){
+        rrd_set_error("please specify an rrd");
+        return -1;
+    }
+    filename = argv[1];
+
+    if(rrd_open(filename,&in_file,&rrd, RRD_READONLY)==-1){
+       return(-1);
+    }
+    fclose(in_file);
+
+    *last_update=rrd.live_head->last_up;
+    *ds_cnt = rrd.stat_head->ds_cnt;
+    if (((*ds_namv) =
+               (char **) malloc(rrd.stat_head->ds_cnt * sizeof(char*)))==NULL){
+        rrd_set_error("malloc fetch ds_namv array");
+       rrd_free(&rrd);
+       return(-1);
+    } 
+
+    if (((*last_ds) =
+               (char **) malloc(rrd.stat_head->ds_cnt * sizeof(char*)))==NULL){
+        rrd_set_error("malloc fetch last_ds array");
+       rrd_free(&rrd);
+       free(*ds_namv);
+       return(-1);
+    } 
+
+    for(i=0;i<rrd.stat_head->ds_cnt;i++){
+       (*ds_namv)[i] = sprintf_alloc("%s", rrd.ds_def[i].ds_nam);
+       (*last_ds)[i] = sprintf_alloc("%s", rrd.pdp_prep[i].last_ds);
+    }
+
+    rrd_free(&rrd);
+    return(0); 
+}
diff --git a/program/src/rrd_nan_inf.c b/program/src/rrd_nan_inf.c
new file mode 100644 (file)
index 0000000..2a5ac14
--- /dev/null
@@ -0,0 +1,32 @@
+#include "rrd_nan_inf.h"
+
+int done_nan = 0;
+int done_inf = 0;
+
+double dnan;
+double dinf;
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#include <math.h>
+
+#define NAN_FUNC (double)fmod(0.0,0.0)
+#define INF_FUNC (double)fabs((double)log(0.0))
+
+#else
+
+#define NAN_FUNC (double)(0.0/0.0)
+#define INF_FUNC (double)(1.0/0.0)
+
+#endif
+
+double set_to_DNAN(void)
+{
+  if ( !done_nan ) { dnan = NAN_FUNC; done_nan = 1; }
+  return dnan;
+}
+
+double set_to_DINF(void)
+{
+  if ( !done_inf ) { dinf = INF_FUNC; done_inf = 1; }
+  return dinf;
+}
diff --git a/program/src/rrd_nan_inf.h b/program/src/rrd_nan_inf.h
new file mode 100644 (file)
index 0000000..7934fdb
--- /dev/null
@@ -0,0 +1,5 @@
+#define DNAN          set_to_DNAN()
+#define DINF          set_to_DINF()
+
+double set_to_DNAN(void);
+double set_to_DINF(void);
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..b2b5227
--- /dev/null
@@ -0,0 +1,47 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ * 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
+
+static char rrd_error[MAXLEN+10];
+static char rrd_liberror[ERRBUFLEN+10];
+static int  rrd_context_init = 0;
+/* The global context is very useful in the transition period to even
+   more thread-safe stuff, it can be used whereever we need a context
+   and do not need to worry about concurrency. */
+static struct rrd_context global_ctx = {
+    MAXLEN,
+    ERRBUFLEN,
+    rrd_error, 
+    rrd_liberror
+};
+/* #include <stdarg.h> */
+
+struct rrd_context *rrd_get_context(void) {
+    if (! rrd_context_init ){
+       rrd_context_init = 1;
+        global_ctx.rrd_error[0]='\0';
+        global_ctx.lib_errstr[0]='\0';
+    }
+    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..3740750
--- /dev/null
@@ -0,0 +1,254 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_open.c  Open an RRD File
+ *****************************************************************************
+ * $Id$
+ * $Log$
+ * Revision 1.10  2004/05/26 22:11:12  oetiker
+ * reduce compiler warnings. Many small fixes. -- Mike Slifcak <slif@bellsouth.net>
+ *
+ * Revision 1.9  2003/04/29 21:56:49  oetiker
+ * 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>
+ *
+ * Revision 1.8  2003/04/11 19:43:44  oetiker
+ * 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>
+ *
+ * Revision 1.7  2003/03/31 21:22:12  oetiker
+ * 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>
+ *
+ * Revision 1.6  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.5  2002/06/20 00:21:03  jake
+ * More Win32 build changes; thanks to Kerry Calvert.
+ *
+ * Revision 1.4  2002/02/01 20:34:49  oetiker
+ * fixed version number and date/time
+ *
+ * Revision 1.3  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>
+ *
+ * Revision 1.2  2001/03/04 10:29:20  oetiker
+ * fixed filedescriptor leak
+ * -- Mike Franusich <mike@franusich.com>
+ *
+ * Revision 1.1.1.1  2001/02/25 22:25:05  oetiker
+ * checkin
+ *
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#define MEMBLK 8192
+
+/* open a database file, return its header and a open filehandle */
+/* positioned to the first cdp in the first rra */
+
+int
+rrd_open(const char *file_name, FILE **in_file, rrd_t *rrd, int rdwr)    
+{
+
+    
+    char *mode = NULL;
+    int version;
+    
+    rrd_init(rrd);
+    if (rdwr == RRD_READONLY) {
+        mode = "rb";
+    } else {
+        mode = "rb+";
+    }
+    
+    if (((*in_file) = fopen(file_name,mode)) == NULL ){
+        rrd_set_error("opening '%s': %s",file_name, rrd_strerror(errno));
+        return (-1);
+    }
+
+#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. */       
+    if (0 != posix_fadvise(fileno(*in_file), 0, 0, POSIX_FADV_RANDOM)) {
+        rrd_set_error("setting POSIX_FADV_RANDOM on '%s': %s",file_name, rrd_strerror(errno));
+        fclose(*in_file);
+        return(-1);
+     }    
+#endif
+
+/*
+        if (rdwr == RRD_READWRITE)
+        {
+           if (setvbuf((*in_file),NULL,_IONBF,2)) {
+                  rrd_set_error("failed to disable the stream buffer\n");
+                  return (-1);
+           }
+        }
+*/
+    
+#define MYFREAD(MYVAR,MYVART,MYCNT) \
+    if ((MYVAR = malloc(sizeof(MYVART) * MYCNT)) == NULL) {\
+        rrd_set_error("" #MYVAR " malloc"); \
+        fclose(*in_file); \
+        return (-1); } \
+    fread(MYVAR,sizeof(MYVART),MYCNT, *in_file); 
+
+
+    MYFREAD(rrd->stat_head, stat_head_t,  1)
+    /* lets see if the first read worked */
+    if (ferror( *in_file ) || feof(*in_file)) {
+        rrd_set_error("reading the cookie off %s faild",file_name);
+        fclose(*in_file);
+        return(-1);
+    }        
+
+        /* lets do some test if we are on track ... */
+        if (strncmp(rrd->stat_head->cookie,RRD_COOKIE,4) != 0){
+            rrd_set_error("'%s' is not an RRD file",file_name);
+            free(rrd->stat_head);
+            rrd->stat_head = NULL; 
+            fclose(*in_file);
+            return(-1);}
+
+        if (rrd->stat_head->float_cookie != FLOAT_COOKIE){
+            rrd_set_error("This RRD was created on other architecture");
+            free(rrd->stat_head);
+            rrd->stat_head = NULL; 
+            fclose(*in_file);
+            return(-1);}
+
+    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);
+            free(rrd->stat_head);
+            rrd->stat_head = NULL; 
+            fclose(*in_file);
+            return(-1);}
+
+
+    MYFREAD(rrd->ds_def,    ds_def_t,     rrd->stat_head->ds_cnt)
+    MYFREAD(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");
+                fclose(*in_file); 
+                return (-1);
+            }
+                fread(&rrd->live_head->last_up, sizeof(long), 1, *in_file); 
+                rrd->live_head->last_up_usec = 0;
+    }
+    else {
+            MYFREAD(rrd->live_head, live_head_t, 1)
+    }
+    MYFREAD(rrd->pdp_prep,  pdp_prep_t,   rrd->stat_head->ds_cnt)
+    MYFREAD(rrd->cdp_prep,  cdp_prep_t,   (rrd->stat_head->rra_cnt
+                                             * rrd->stat_head->ds_cnt))
+    MYFREAD(rrd->rra_ptr,   rra_ptr_t,    rrd->stat_head->rra_cnt)
+#undef MYFREAD
+
+    return(0);
+}
+
+void rrd_init(rrd_t *rrd)
+{
+    rrd->stat_head = NULL;
+    rrd->ds_def = NULL;
+    rrd->rra_def = NULL;
+    rrd->live_head = NULL;
+    rrd->rra_ptr = NULL;
+    rrd->pdp_prep = NULL;
+    rrd->cdp_prep = NULL;
+    rrd->rrd_value = NULL;
+}
+
+void rrd_free(rrd_t *rrd)
+{
+    if (rrd->stat_head) free(rrd->stat_head);
+    if (rrd->ds_def) free(rrd->ds_def);
+    if (rrd->rra_def) free(rrd->rra_def);
+    if (rrd->live_head) free(rrd->live_head);
+    if (rrd->rra_ptr) free(rrd->rra_ptr);
+    if (rrd->pdp_prep) free(rrd->pdp_prep);
+    if (rrd->cdp_prep) free(rrd->cdp_prep);
+    if (rrd->rrd_value) free(rrd->rrd_value);
+}
+
+/* routine used by external libraries to free memory allocated by
+ * rrd library */
+void rrd_freemem(void *mem)
+{
+
+    if (mem) free(mem);
+}
+
+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;
+}
+
+
diff --git a/program/src/rrd_resize.c b/program/src/rrd_resize.c
new file mode 100644 (file)
index 0000000..c1bdda2
--- /dev/null
@@ -0,0 +1,205 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_resize.c Alters size of an RRA
+ *****************************************************************************
+ * Initial version by Alex van den Bogaerdt
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+
+int
+rrd_resize(int argc, char **argv)
+{
+    char               *infilename,outfilename[11]="resize.rrd";
+    FILE               *infile,*outfile;
+    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;
+
+    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;
+
+
+    if (rrd_open(infilename, &infile, &rrdold, RRD_READWRITE)==-1) {
+        rrd_set_error("could not open RRD");
+        return(-1);
+    }
+    if (LockRRD(infile) != 0) {
+        rrd_set_error("could not lock original RRD");
+        rrd_free(&rrdold);
+        fclose(infile);
+        return(-1);
+    }
+
+    if (target_rra >= rrdold.stat_head->rra_cnt) {
+        rrd_set_error("no such RRA in this RRD");
+        rrd_free(&rrdold);
+        fclose(infile);
+        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);
+           fclose(infile);
+           return(-1);
+       }
+
+    rrdnew.stat_head = rrdold.stat_head;
+    rrdnew.ds_def    = rrdold.ds_def;
+    rrdnew.rra_def   = rrdold.rra_def;
+    rrdnew.live_head = rrdold.live_head;
+    rrdnew.pdp_prep  = rrdold.pdp_prep;
+    rrdnew.cdp_prep  = rrdold.cdp_prep;
+    rrdnew.rra_ptr   = rrdold.rra_ptr;
+
+    version = atoi(rrdold.stat_head->version);
+    switch (version) {
+       case 3: break;
+       case 1: rrdold.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(&rrdold);      
+               fclose(infile);
+               return(-1);
+               }
+    }
+
+    if ((outfile=fopen(outfilename,"wb"))==NULL) {
+        rrd_set_error("Can't create '%s'",outfilename);
+        return(-1);
+    }
+    if (LockRRD(outfile) != 0) {
+        rrd_set_error("could not lock new RRD");
+        rrd_free(&rrdold);
+        fclose(infile);
+        fclose(outfile);
+        return(-1);
+    }
+    fwrite(rrdnew.stat_head, sizeof(stat_head_t),1,outfile);
+    fwrite(rrdnew.ds_def,sizeof(ds_def_t),rrdnew.stat_head->ds_cnt,outfile);
+    fwrite(rrdnew.rra_def,sizeof(rra_def_t),rrdnew.stat_head->rra_cnt,outfile);
+    fwrite(rrdnew.live_head,sizeof(live_head_t),1,outfile);
+    fwrite(rrdnew.pdp_prep,sizeof(pdp_prep_t),rrdnew.stat_head->ds_cnt,outfile);
+    fwrite(rrdnew.cdp_prep,sizeof(cdp_prep_t),rrdnew.stat_head->ds_cnt*rrdnew.stat_head->rra_cnt,outfile);
+    fwrite(rrdnew.rra_ptr,sizeof(rra_ptr_t),rrdnew.stat_head->rra_cnt,outfile);
+
+    /* 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) {
+        fread(&buffer,sizeof(rrd_value_t),1,infile);
+        fwrite(&buffer,sizeof(rrd_value_t),1,outfile);
+        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) {
+            fread(&buffer,sizeof(rrd_value_t),1,infile);
+            fwrite(&buffer,sizeof(rrd_value_t),1,outfile);
+            l--;
+        }
+        buffer=DNAN;
+        l=rrdnew.stat_head->ds_cnt * modify;
+        while (l>0) {
+            fwrite(&buffer,sizeof(rrd_value_t),1,outfile);
+            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) {
+                fseek(infile,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++) {
+                fread(&buffer,sizeof(rrd_value_t),1,infile);
+                fwrite(&buffer,sizeof(rrd_value_t),1,outfile);
+            }
+        }
+        while (modify<0) {
+            fseek(infile,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) {
+       fread(&buffer,sizeof(rrd_value_t),1,infile);
+       if (feof(infile))
+           break;
+        fwrite(&buffer,sizeof(rrd_value_t),1,outfile);
+    }
+    rrdnew.rra_def[target_rra].row_cnt += modify;
+    fseek(outfile,sizeof(stat_head_t)+sizeof(ds_def_t)*rrdnew.stat_head->ds_cnt,SEEK_SET);
+    fwrite(rrdnew.rra_def,sizeof(rra_def_t),rrdnew.stat_head->rra_cnt, outfile);
+    fseek(outfile,sizeof(live_head_t),SEEK_CUR);
+    fseek(outfile,sizeof(pdp_prep_t)*rrdnew.stat_head->ds_cnt,SEEK_CUR);
+    fseek(outfile,sizeof(cdp_prep_t)*rrdnew.stat_head->ds_cnt*rrdnew.stat_head->rra_cnt,SEEK_CUR);
+    fwrite(rrdnew.rra_ptr,sizeof(rra_ptr_t),rrdnew.stat_head->rra_cnt, outfile);
+    
+    fclose(outfile);
+    rrd_free(&rrdold);
+    fclose(infile);
+    return(0);
+}
diff --git a/program/src/rrd_restore.c b/program/src/rrd_restore.c
new file mode 100644 (file)
index 0000000..1da3a59
--- /dev/null
@@ -0,0 +1,652 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_restore.c  creates new rrd from data dumped by rrd_dump.c
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include <fcntl.h>
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#include <io.h>
+#define open _open
+#define close _close
+#endif
+
+/* Prototypes */
+
+void xml_lc(char*);
+int skip(char **);
+int skipxml(char **);
+int eat_tag(char **, char *);
+int read_tag(char **, char *, char *, void *);
+int xml2rrd(char*, rrd_t*, char);
+int rrd_write(char *, rrd_t *, char);
+void parse_patch1028_RRA_params(char **buf, rrd_t *rrd, int rra_index);
+void parse_patch1028_CDP_params(char **buf, rrd_t *rrd, int rra_index, int ds_index);
+void parse_FAILURES_history(char **buf, rrd_t *rrd, int rra_index, int ds_index);
+
+/* convert all occurrences of <BlaBlaBla> to <blablabla> */
+
+void xml_lc(char* buf){
+  int intag=0;
+  while((*buf)){
+    if (intag ==0 && (*buf) == '<') {
+      intag = 1;
+    }
+    else if (intag ==1 && (*buf) == '>') {
+      intag = 0;
+      continue;
+    } else  if (intag ==1) {
+      *buf = tolower(*buf);
+    }
+    buf++;    
+  }
+}
+
+int skipxml(char **buf){
+  char *ptr;  
+  ptr=(*buf);
+  do {
+    (*buf)=ptr;
+    while((*(ptr+1)) && ((*ptr)==' ' ||  (*ptr)=='\r' || (*ptr)=='\n' || (*ptr)=='\t')) ptr++;
+    if (strncmp(ptr,"<?xml",4) == 0) {
+      ptr= strstr(ptr,"?>");
+      if (ptr) ptr+=2; else {
+       rrd_set_error("Dangling XML header");
+       (*buf) = NULL;
+       return -1;
+      }
+    }
+  } while ((*buf)!=ptr);  
+  return 1;
+}
+
+int skip(char **buf){
+  char *ptr;
+  if ((buf == NULL) || (*buf == NULL))
+    return -1;  
+  ptr=(*buf);
+  do {
+    (*buf)=ptr;
+    while((*(ptr+1)) && ((*ptr)==' ' ||  (*ptr)=='\r' || (*ptr)=='\n' || (*ptr)=='\t')) ptr++;
+    if (strncmp(ptr,"<!--",4) == 0) {
+      ptr= strstr(ptr,"-->");
+      if (ptr) ptr+=3; else {
+       rrd_set_error("Dangling Comment");
+       (*buf) = NULL;
+       return -1;
+      }
+    }
+  } while ((*buf)!=ptr);  
+  return 1;
+}
+
+int eat_tag(char **buf, char *tag){ 
+  if ((*buf)==NULL) return -1;   /* fall though clause */
+
+  rrd_clear_error();
+  skip(buf);
+  if ((**buf)=='<' 
+      && strncmp((*buf)+1,tag,strlen(tag)) == 0 
+      && *((*buf)+strlen(tag)+1)=='>') {
+    (*buf) += strlen(tag)+2;
+  }
+  else {
+    rrd_set_error("No <%s> tag found",tag);
+    (*buf) = NULL;
+    return -1;
+  }
+  skip(buf);
+  return 1;
+}
+
+int read_tag(char **buf, char *tag, char *format, void *value){
+    char *end_tag;
+    int matches;
+    if ((*buf)==NULL) return -1;   /* fall though clause */
+    rrd_clear_error();
+    if (eat_tag(buf,tag)==1){
+       char *temp;
+       temp = (*buf);
+       while(*((*buf)+1) && (*(*buf) != '<')) (*buf)++; /*find start of endtag*/
+       *(*buf) = '\0';
+       matches =sscanf(temp,format,value);
+       *(*buf) = '<';
+       end_tag = malloc((strlen(tag)+2)*sizeof(char));
+       sprintf(end_tag,"/%s",tag);
+       eat_tag(buf,end_tag);
+       free(end_tag);
+       if (matches == 0 && strcmp(format,"%lf") == 0)
+           (*((double* )(value))) = DNAN;
+       if (matches != 1)       return 0;       
+       return 1;
+    }
+    return -1;
+}
+
+
+/* parse the data stored in buf and return a filled rrd structure */
+int xml2rrd(char* buf, rrd_t* rrd, char rc){
+  /* pass 1 identify number of RRAs  */
+  char *ptr,*ptr2,*ptr3; /* walks thought the buffer */
+  long rows=0,mempool=0,i=0;
+  int rra_index;
+  int input_version;
+  xml_lc(buf); /* lets lowercase all active parts of the xml */
+  ptr=buf;
+  ptr2=buf;
+  ptr3=buf;
+  /* start with an RRD tag */
+  
+  skipxml(&ptr);
+
+  eat_tag(&ptr,"rrd");
+  /* allocate static header */
+  if((rrd->stat_head = calloc(1,sizeof(stat_head_t)))==NULL){
+    rrd_set_error("allocating rrd.stat_head");
+    return -1;    
+  };
+
+  strcpy(rrd->stat_head->cookie,RRD_COOKIE);
+  read_tag(&ptr,"version","%4[0-9]",rrd->stat_head->version);
+  input_version = atoi(rrd->stat_head->version);
+  /* added primitive version checking */
+  if (input_version > atoi(RRD_VERSION) || input_version < 1)
+  {
+    rrd_set_error("Incompatible file version, detected version %s. This is not supported by the version %s restore tool.\n",
+                 rrd -> stat_head -> version, RRD_VERSION );
+    free(rrd -> stat_head); 
+    rrd->stat_head = NULL; 
+    return -1;
+  }
+  /* make sure we output the right version */
+  strcpy(rrd->stat_head->version,RRD_VERSION);
+
+  /*  if (atoi(rrd -> stat_head -> version) < 2) 
+  {
+    rrd_set_error("Can only restore version >= 2 (Not %s). Dump your old rrd using a current rrdtool dump.",  rrd -> stat_head -> version );
+    return -1;
+  } */
+
+  rrd->stat_head->float_cookie = FLOAT_COOKIE;
+  rrd->stat_head->ds_cnt = 0;
+  rrd->stat_head->rra_cnt = 0;
+  read_tag(&ptr,"step","%lu",&(rrd->stat_head->pdp_step));
+
+  /* allocate live head */
+  if((rrd->live_head = calloc(1,sizeof(live_head_t)))==NULL){
+    rrd_set_error("allocating rrd.live_head");
+    return -1;    
+  }
+  read_tag(&ptr,"lastupdate","%lu",&(rrd->live_head->last_up));
+
+  /* Data Source Definition Part */
+  ptr2 = ptr;
+  while (eat_tag(&ptr2,"ds") == 1){
+      rrd->stat_head->ds_cnt++;
+      if((rrd->ds_def = rrd_realloc(rrd->ds_def,rrd->stat_head->ds_cnt*sizeof(ds_def_t)))==NULL){
+         rrd_set_error("allocating rrd.ds_def");
+         return -1;
+      };
+      /* clean out memory to make sure no data gets stored from previous tasks */
+      memset(&(rrd->ds_def[rrd->stat_head->ds_cnt-1]), 0, sizeof(ds_def_t));
+      if((rrd->pdp_prep = rrd_realloc(rrd->pdp_prep,rrd->stat_head->ds_cnt
+                                 *sizeof(pdp_prep_t)))==NULL){
+       rrd_set_error("allocating pdp_prep");
+       return(-1);
+      }
+      /* clean out memory to make sure no data gets stored from previous tasks */
+      memset(&(rrd->pdp_prep[rrd->stat_head->ds_cnt-1]), 0, sizeof(pdp_prep_t));
+
+      read_tag(&ptr2,"name",DS_NAM_FMT,rrd->ds_def[rrd->stat_head->ds_cnt-1].ds_nam);
+
+      read_tag(&ptr2,"type",DST_FMT,rrd->ds_def[rrd->stat_head->ds_cnt-1].dst);
+      /* test for valid type */
+      if( (int)dst_conv(rrd->ds_def[rrd->stat_head->ds_cnt-1].dst) == -1) return -1;      
+
+         if (dst_conv(rrd->ds_def[rrd->stat_head->ds_cnt-1].dst) != DST_CDEF)
+         {
+      read_tag(&ptr2,"minimal_heartbeat","%lu",
+              &(rrd->ds_def[rrd->stat_head->ds_cnt-1].par[DS_mrhb_cnt].u_cnt));
+      read_tag(&ptr2,"min","%lf",&(rrd->ds_def[rrd->stat_head->ds_cnt-1].par[DS_min_val].u_val));
+      read_tag(&ptr2,"max","%lf",&(rrd->ds_def[rrd->stat_head->ds_cnt-1].par[DS_max_val].u_val));
+         } else { /* DST_CDEF */
+                char buffer[1024];
+                read_tag(&ptr2,"cdef","%1000s",buffer);
+                parseCDEF_DS(buffer,rrd,rrd -> stat_head -> ds_cnt - 1);
+                if (rrd_test_error()) return -1;
+         }
+
+      read_tag(&ptr2,"last_ds","%30s",rrd->pdp_prep[rrd->stat_head->ds_cnt-1].last_ds);
+      read_tag(&ptr2,"value","%lf",&(rrd->pdp_prep[rrd->stat_head->ds_cnt-1].scratch[PDP_val].u_val));
+      read_tag(&ptr2,"unknown_sec","%lu",&(rrd->pdp_prep[rrd->stat_head->ds_cnt-1].scratch[PDP_unkn_sec_cnt].u_cnt));      
+      eat_tag(&ptr2,"/ds");
+      ptr=ptr2;
+  }
+  
+  ptr2 = ptr;
+  while (eat_tag(&ptr2,"rra") == 1){
+      rrd->stat_head->rra_cnt++;
+
+      /* allocate and reset rra definition areas */
+      if((rrd->rra_def = rrd_realloc(rrd->rra_def,rrd->stat_head->rra_cnt*sizeof(rra_def_t)))==NULL){
+         rrd_set_error("allocating rra_def"); return -1; }      
+      memset(&(rrd->rra_def[rrd->stat_head->rra_cnt-1]), 0, sizeof(rra_def_t));
+
+      /* allocate and reset consolidation point areas */
+      if((rrd->cdp_prep = rrd_realloc(rrd->cdp_prep,
+                                 rrd->stat_head->rra_cnt
+                                 *rrd->stat_head->ds_cnt*sizeof(cdp_prep_t)))==NULL){
+         rrd_set_error("allocating cdp_prep"); return -1; }
+
+      memset(&(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rrd->stat_head->rra_cnt-1)]), 
+            0, rrd->stat_head->ds_cnt*sizeof(cdp_prep_t));
+
+      
+      read_tag(&ptr2,"cf",CF_NAM_FMT,rrd->rra_def[rrd->stat_head->rra_cnt-1].cf_nam);
+      /* test for valid type */
+      if( (int)cf_conv(rrd->rra_def[rrd->stat_head->rra_cnt-1].cf_nam) == -1) return -1;
+
+      read_tag(&ptr2,"pdp_per_row","%lu",&(rrd->rra_def[rrd->stat_head->rra_cnt-1].pdp_cnt));
+      /* support to read RRA parameters */
+      rra_index = rrd->stat_head->rra_cnt - 1;
+      if ( input_version < 2 ){
+         read_tag(&ptr2, "xff","%lf",
+            &(rrd->rra_def[rra_index].par[RRA_cdp_xff_val].u_val));
+      } else {
+        if (eat_tag(&ptr2, "params") != 1) {
+         rrd_set_error("could not find params tag to eat and skip");
+          return -1;
+        }
+        skip(&ptr2);
+        /* backwards compatibility w/ old patch */
+      if (strncmp(ptr2, "<value>",7) == 0) {
+          parse_patch1028_RRA_params(&ptr2,rrd,rra_index); 
+      } else {
+      switch(cf_conv(rrd -> rra_def[rra_index].cf_nam)) {
+      case CF_HWPREDICT:
+         read_tag(&ptr2, "hw_alpha", "%lf", 
+            &(rrd->rra_def[rra_index].par[RRA_hw_alpha].u_val));
+         read_tag(&ptr2, "hw_beta", "%lf", 
+            &(rrd->rra_def[rra_index].par[RRA_hw_beta].u_val));
+         read_tag(&ptr2, "dependent_rra_idx", "%lu", 
+            &(rrd->rra_def[rra_index].par[RRA_dependent_rra_idx].u_cnt));
+         break;
+      case CF_SEASONAL:
+      case CF_DEVSEASONAL:
+         read_tag(&ptr2, "seasonal_gamma", "%lf", 
+            &(rrd->rra_def[rra_index].par[RRA_seasonal_gamma].u_val));
+         read_tag(&ptr2, "seasonal_smooth_idx", "%lu", 
+            &(rrd->rra_def[rra_index].par[RRA_seasonal_smooth_idx].u_cnt));
+         read_tag(&ptr2, "dependent_rra_idx", "%lu", 
+            &(rrd->rra_def[rra_index].par[RRA_dependent_rra_idx].u_cnt));
+         break;
+      case CF_FAILURES:
+         read_tag(&ptr2, "delta_pos", "%lf", 
+            &(rrd->rra_def[rra_index].par[RRA_delta_pos].u_val));
+         read_tag(&ptr2, "delta_neg", "%lf", 
+            &(rrd->rra_def[rra_index].par[RRA_delta_neg].u_val));
+         read_tag(&ptr2, "window_len", "%lu", 
+            &(rrd->rra_def[rra_index].par[RRA_window_len].u_cnt));
+         read_tag(&ptr2, "failure_threshold", "%lu", 
+            &(rrd->rra_def[rra_index].par[RRA_failure_threshold].u_cnt));
+         /* fall thru */
+      case CF_DEVPREDICT:
+         read_tag(&ptr2, "dependent_rra_idx", "%lu", 
+            &(rrd->rra_def[rra_index].par[RRA_dependent_rra_idx].u_cnt));
+         break;
+      case CF_AVERAGE:
+      case CF_MAXIMUM:
+      case CF_MINIMUM:
+      case CF_LAST:
+      default:
+         read_tag(&ptr2, "xff","%lf",
+            &(rrd->rra_def[rra_index].par[RRA_cdp_xff_val].u_val));
+      }
+      }
+      eat_tag(&ptr2, "/params");
+   }
+
+
+      eat_tag(&ptr2,"cdp_prep");
+      for(i=0;i< (int)rrd->stat_head->ds_cnt;i++)
+      {
+      eat_tag(&ptr2,"ds");
+      /* support to read CDP parameters */
+      rra_index = rrd->stat_head->rra_cnt-1; 
+      skip(&ptr2);
+      if ( input_version < 2 ){
+          rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)+i].scratch[CDP_primary_val].u_val = 0.0;
+          rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)+i].scratch[CDP_secondary_val].u_val = 0.0;
+          read_tag(&ptr2,"value","%lf",&(rrd->cdp_prep[rrd->stat_head->ds_cnt
+               *(rra_index) +i].scratch[CDP_val].u_val));
+          read_tag(&ptr2,"unknown_datapoints","%lu",&(rrd->cdp_prep[rrd->stat_head->ds_cnt
+              *(rra_index) +i].scratch[CDP_unkn_pdp_cnt].u_cnt));
+      } else {
+
+      if (strncmp(ptr2, "<value>",7) == 0) {
+         parse_patch1028_CDP_params(&ptr2,rrd,rra_index,i);
+      } else {
+         read_tag(&ptr2, "primary_value","%lf",
+               &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+               +i].scratch[CDP_primary_val].u_val));
+         read_tag(&ptr2, "secondary_value","%lf",
+               &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+               +i].scratch[CDP_secondary_val].u_val));
+         switch(cf_conv(rrd->rra_def[rra_index].cf_nam)) {
+         case CF_HWPREDICT:
+            read_tag(&ptr2,"intercept","%lf", 
+               &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+               +i].scratch[CDP_hw_intercept].u_val));
+            read_tag(&ptr2,"last_intercept","%lf", 
+               &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+               +i].scratch[CDP_hw_last_intercept].u_val));
+            read_tag(&ptr2,"slope","%lf", 
+               &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+               +i].scratch[CDP_hw_slope].u_val));
+            read_tag(&ptr2,"last_slope","%lf", 
+               &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+               +i].scratch[CDP_hw_last_slope].u_val));
+            read_tag(&ptr2,"nan_count","%lu", 
+               &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+               +i].scratch[CDP_null_count].u_cnt));
+            read_tag(&ptr2,"last_nan_count","%lu", 
+               &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+               +i].scratch[CDP_last_null_count].u_cnt));
+            break;
+         case CF_SEASONAL:
+         case CF_DEVSEASONAL:
+            read_tag(&ptr2,"seasonal","%lf", 
+               &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+               +i].scratch[CDP_hw_seasonal].u_val));
+            read_tag(&ptr2,"last_seasonal","%lf", 
+               &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+               +i].scratch[CDP_hw_last_seasonal].u_val));
+            read_tag(&ptr2,"init_flag","%lu", 
+               &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+               +i].scratch[CDP_init_seasonal].u_cnt));
+            break;
+         case CF_DEVPREDICT:
+            break;
+         case CF_FAILURES:
+            parse_FAILURES_history(&ptr2,rrd,rra_index,i); 
+            break;
+         case CF_AVERAGE:
+         case CF_MAXIMUM:
+         case CF_MINIMUM:
+         case CF_LAST:
+         default:
+            read_tag(&ptr2,"value","%lf",&(rrd->cdp_prep[rrd->stat_head->ds_cnt
+               *(rra_index) +i].scratch[CDP_val].u_val));
+            read_tag(&ptr2,"unknown_datapoints","%lu",&(rrd->cdp_prep[rrd->stat_head->ds_cnt
+               *(rra_index) +i].scratch[CDP_unkn_pdp_cnt].u_cnt));
+            break;
+        }
+      }
+      }
+      eat_tag(&ptr2,"/ds");
+      }
+      eat_tag(&ptr2,"/cdp_prep");
+      rrd->rra_def[rrd->stat_head->rra_cnt-1].row_cnt=0;
+      eat_tag(&ptr2,"database");
+      ptr3 = ptr2;      
+      while (eat_tag(&ptr3,"row") == 1){
+       
+         if(mempool==0){
+           mempool = 1000;
+           if((rrd->rrd_value = rrd_realloc(rrd->rrd_value,
+                                        (rows+mempool)*(rrd->stat_head->ds_cnt)
+                                        *sizeof(rrd_value_t)))==NULL) {
+             rrd_set_error("allocating rrd_values"); return -1; }
+         }
+         rows++;
+         mempool--;
+         rrd->rra_def[rrd->stat_head->rra_cnt-1].row_cnt++;
+         for(i=0;i< (int)rrd->stat_head->ds_cnt;i++){
+
+                 rrd_value_t  * value = &(rrd->rrd_value[(rows-1)*rrd->stat_head->ds_cnt+i]);
+
+                 read_tag(&ptr3,"v","%lf", value);
+                 
+                 if (
+                         (rc == 1)                     /* do we have to check for the ranges */
+                         &&
+                     (!isnan(*value))  /* not a NAN value */
+                     &&
+                         (dst_conv(rrd->ds_def[i].dst) != DST_CDEF)
+                         &&
+                     (                                 /* min defined and in the range ? */
+                         (!isnan(rrd->ds_def[i].par[DS_min_val].u_val) 
+                               && (*value < rrd->ds_def[i].par[DS_min_val].u_val)) 
+                         ||                            /* max defined and in the range ? */
+                         (!isnan(rrd->ds_def[i].par[DS_max_val].u_val) 
+                               && (*value > rrd->ds_def[i].par[DS_max_val].u_val))
+                     )
+                 ) {
+                     fprintf (stderr, "out of range found [ds: %lu], [value : %0.10e]\n", i, *value);
+                     *value = DNAN;
+                 }
+         }
+         eat_tag(&ptr3,"/row");                  
+         ptr2=ptr3;
+      }
+      eat_tag(&ptr2,"/database");
+      eat_tag(&ptr2,"/rra");                  
+      ptr=ptr2;
+  }  
+  eat_tag(&ptr,"/rrd");
+
+  if((rrd->rra_ptr = calloc(1,sizeof(rra_ptr_t)*rrd->stat_head->rra_cnt)) == NULL) {
+      rrd_set_error("allocating rra_ptr");
+      return(-1);
+  }
+
+  for(i=0; i < (int)rrd->stat_head->rra_cnt; i++) {
+         /* last row in the xml file is the most recent; as
+          * rrd_update increments the current row pointer, set cur_row
+          * here to the last row. */
+      rrd->rra_ptr[i].cur_row = rrd->rra_def[i].row_cnt-1;
+  }
+  if (ptr==NULL)
+      return -1;
+  return 1;
+}
+  
+    
+
+
+
+/* create and empty rrd file according to the specs given */
+
+int
+rrd_write(char *file_name, rrd_t *rrd, char force_overwrite)
+{
+    unsigned long    i,ii,val_cnt;
+    FILE             *rrd_file=NULL;
+    int                        fdflags;
+    int                        fd;
+
+    if (strcmp("-",file_name)==0){
+      rrd_file= stdout;
+    } else {
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+      fdflags = O_RDWR|O_BINARY|O_CREAT;
+#else
+      fdflags = O_WRONLY|O_CREAT;
+#endif            
+      if (force_overwrite == 0) {
+       fdflags |= O_EXCL;
+      }
+      fd = open(file_name,fdflags,0666);
+      if (fd == -1 || (rrd_file = fdopen(fd,"wb")) == NULL) {
+       rrd_set_error("creating '%s': %s",file_name,rrd_strerror(errno));
+        if (fd != -1)
+          close(fd);
+       return(-1);
+      }
+    }
+    fwrite(rrd->stat_head,
+          sizeof(stat_head_t), 1, rrd_file);
+
+    fwrite(rrd->ds_def,
+          sizeof(ds_def_t), rrd->stat_head->ds_cnt, rrd_file);
+
+    fwrite(rrd->rra_def,
+          sizeof(rra_def_t), rrd->stat_head->rra_cnt, rrd_file);
+
+    fwrite(rrd->live_head, sizeof(live_head_t),1, rrd_file);
+
+    fwrite( rrd->pdp_prep, sizeof(pdp_prep_t),rrd->stat_head->ds_cnt,rrd_file);
+    
+    fwrite( rrd->cdp_prep, sizeof(cdp_prep_t),rrd->stat_head->rra_cnt*
+           rrd->stat_head->ds_cnt,rrd_file);
+    fwrite( rrd->rra_ptr, sizeof(rra_ptr_t), rrd->stat_head->rra_cnt,rrd_file);
+
+
+
+    /* calculate the number of rrd_values to dump */
+    val_cnt=0;
+    for(i=0; i <  rrd->stat_head->rra_cnt; i++)
+       for(ii=0; ii <  rrd->rra_def[i].row_cnt * rrd->stat_head->ds_cnt;ii++)
+           val_cnt++;
+    fwrite( rrd->rrd_value, sizeof(rrd_value_t),val_cnt,rrd_file);
+
+    /* lets see if we had an error */
+    if(ferror(rrd_file)){
+       rrd_set_error("a file error occurred while creating '%s'",file_name);
+       fclose(rrd_file);       
+       return(-1);
+    }
+    
+    fclose(rrd_file);    
+    return 0;
+}
+
+
+int
+rrd_restore(int argc, char **argv) 
+{
+    rrd_t          rrd;
+    char          *buf;
+       char                    rc = 0;
+       char                    force_overwrite = 0;    
+
+    /* init rrd clean */
+    optind = 0; opterr = 0;  /* initialize getopt */
+       while (1) {
+               static struct option long_options[] =
+               {
+                       {"range-check",      no_argument, 0,  'r'},
+                       {"force-overwrite",  no_argument, 0,  'f'},
+                       {0,0,0,0}
+               };
+               int option_index = 0;
+               int opt;
+               
+               
+               opt = getopt_long(argc, argv, "rf", long_options, &option_index);
+               
+               if (opt == EOF)
+                       break;
+               
+               switch(opt) {
+               case 'r':
+                       rc=1;
+                       break;
+               case 'f':
+                       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;
+               }
+    }
+
+    if (argc-optind != 2) {
+               rrd_set_error("usage rrdtool %s [--range-check/-r] [--force-overwrite/-f] file.xml file.rrd",argv[0]);
+               return -1;
+    }
+       
+    if (readfile(argv[optind],&buf,0)==-1){
+      return -1;
+    }
+
+    rrd_init(&rrd);
+
+    if (xml2rrd(buf,&rrd,rc)==-1) {
+       rrd_free(&rrd);
+       free(buf);
+       return -1;
+    }
+
+    free(buf);
+
+    if(rrd_write(argv[optind+1],&rrd,force_overwrite)==-1){
+       rrd_free(&rrd); 
+       return -1;      
+    };
+    rrd_free(&rrd);    
+    return 0;
+}
+
+/* a backwards compatibility routine that will parse the RRA params section
+ * generated by the aberrant patch to 1.0.28. */
+
+void
+parse_patch1028_RRA_params(char **buf, rrd_t *rrd, int rra_index)
+{
+   int i;
+   for (i = 0; i < MAX_RRA_PAR_EN; i++)
+   {
+   if (i == RRA_dependent_rra_idx ||
+       i == RRA_seasonal_smooth_idx ||
+       i == RRA_failure_threshold)
+      read_tag(buf, "value","%lu",
+         &(rrd->rra_def[rra_index].par[i].u_cnt));
+   else
+      read_tag(buf, "value","%lf",
+         &(rrd->rra_def[rra_index].par[i].u_val));
+   }
+}
+
+/* a backwards compatibility routine that will parse the CDP params section
+ * generated by the aberrant patch to 1.0.28. */
+void
+parse_patch1028_CDP_params(char **buf, rrd_t *rrd, int rra_index, int ds_index)
+{
+   int ii;
+   for (ii = 0; ii < MAX_CDP_PAR_EN; ii++)
+   {
+   if (cf_conv(rrd->rra_def[rra_index].cf_nam) == CF_FAILURES ||
+       ii == CDP_unkn_pdp_cnt ||
+       ii == CDP_null_count ||
+       ii == CDP_last_null_count)
+   {
+      read_tag(buf,"value","%lu",
+       &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index) + ds_index].scratch[ii].u_cnt));
+   } else {
+      read_tag(buf,"value","%lf",&(rrd->cdp_prep[rrd->stat_head->ds_cnt*
+       (rra_index) + ds_index].scratch[ii].u_val));
+   }
+   }
+}
+
+void
+parse_FAILURES_history(char **buf, rrd_t *rrd, int rra_index, int ds_index)
+{
+   char history[MAX_FAILURES_WINDOW_LEN + 1];
+   char *violations_array;
+   unsigned short i;
+
+   /* 28 = MAX_FAILURES_WINDOW_LEN */ 
+   read_tag(buf, "history", "%28[0-1]", history);
+   violations_array = (char*) rrd -> cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+      + ds_index].scratch;
+   
+   for (i = 0; i < rrd -> rra_def[rra_index].par[RRA_window_len].u_cnt; ++i)
+      violations_array[i] = (history[i] == '1') ? 1 : 0;
+
+}
diff --git a/program/src/rrd_rpncalc.c b/program/src/rrd_rpncalc.c
new file mode 100644 (file)
index 0000000..eb7c94b
--- /dev/null
@@ -0,0 +1,830 @@
+/****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd_rpncalc.c  RPN calculator functions
+ ****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include "rrd_graph.h"
+#include <limits.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 = (long) 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(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(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_RAD2DEG,RAD2DEG)
+         add_op(OP_DEG2RAD,DEG2RAD)
+         add_op(OP_AVG,AVG)
+         add_op(OP_ABS,ABS)
+#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];
+    
+    rpnp=NULL;
+    expr=(char *)expr_const;
+    
+    while(*expr){
+       if ((rpnp = (rpnp_t *) rrd_realloc(rpnp, (++steps + 2)* 
+                                      sizeof(rpnp_t)))==NULL){
+           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_RAD2DEG,RAD2DEG)
+       match_op(OP_DEG2RAD,DEG2RAD)
+       match_op(OP_AVG,AVG)
+       match_op(OP_ABS,ABS)
+#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 {
+           free(rpnp);
+           return NULL;
+       }
+       if (*expr == 0)
+            break;
+       if (*expr == ',')
+           expr++;
+       else {
+           free(rpnp);
+           return NULL;
+       }  
+    }
+    rpnp[steps+1].op = OP_END;
+    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 = 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_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] = rpnstack->s[stptr-2] != 0.0 ?
+                               rpnstack->s[stptr-1] : rpnstack->s[stptr];
+               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_TREND:
+               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)) {
+                       double accum = 0.0;
+                       int i = 0;
+               
+                       do {
+                           accum += rpnp[rpi-2].data[rpnp[rpi-2].ds_cnt * i--];
+                           dur -= step;
+                       } while (dur > 0);
+
+                       rpnstack -> s[--stptr] = (accum / -i);
+                   } 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..17264dc
--- /dev/null
@@ -0,0 +1,59 @@
+/****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * 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_ATAN2,OP_RAD2DEG,OP_DEG2RAD,
+           OP_AVG,OP_ABS};
+
+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;
+
+/* 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_stat.c b/program/src/rrd_stat.c
new file mode 100644 (file)
index 0000000..74969bd
--- /dev/null
@@ -0,0 +1,145 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_stat Retreive the header part of an RRD
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+
+extern char *tzname[2];
+
+stat_node
+rrd_stat(int argc, char **argv)    
+{   
+    int          i,ii,ix,iii=0;
+    time_t       now;
+    char         somestring[255];
+    rrd_value_t  my_cdp;
+    long         rra_base, rra_start, rra_next;
+    FILE                  *in_file;
+    rrd_t             rrd;
+
+
+    if(rrd_open(argv[1],&in_file,&rrd, RRD_READONLY)==-1){
+       return(-1);
+    }
+    puts("<!-- Round Robin Database Dump -->");
+    puts("<rrd>");
+    printf("\t<version> %s </version>\n",rrd.stat_head->version);
+    printf("\t<step> %lu </step> <!-- Seconds -->\n",rrd.stat_head->pdp_step);
+#if HAVE_STRFTIME
+    strftime(somestring,200,"%Y-%m-%d %H:%M:%S %Z",
+            localtime_r(&rrd.live_head->last_up, &tm));
+#else
+# error "Need strftime"
+#endif
+    printf("\t<lastupdate> %ld </lastupdate> <!-- %s -->\n\n",
+          rrd.live_head->last_up,somestring);
+    for(i=0;i<rrd.stat_head->ds_cnt;i++){
+       printf("\t<ds>\n");
+       printf("\t\t<name> %s </name>\n",rrd.ds_def[i].ds_nam);
+       printf("\t\t<type> %s </type>\n",rrd.ds_def[i].dst);
+       printf("\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)){
+         printf("\t\t<min> NaN </min>\n");
+       } else {
+         printf("\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)){
+         printf("\t\t<max> NaN </max>\n");
+       } else {
+         printf("\t\t<max> %0.10e </max>\n",rrd.ds_def[i].par[DS_max_val].u_val);
+       }
+       printf("\n\t\t<!-- PDP Status -->\n");
+       printf("\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)){
+         printf("\t\t<value> NaN </value>\n");
+       } else {
+         printf("\t\t<value> %0.10e </value>\n",rrd.pdp_prep[i].scratch[PDP_val].u_val);
+       }
+       printf("\t\t<unknown_sec> %lu </unknown_sec>\n",
+              rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
+       
+       printf("\t</ds>\n\n");
+    }
+
+    puts("<!-- Round Robin Archives -->");
+
+    rra_base=ftell(in_file);    
+    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));
+       printf("\t<rra>\n");
+       printf("\t\t<cf> %s </cf>\n",rrd.rra_def[i].cf_nam);
+       printf("\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);
+       printf("\t\t<cdp_prep>\n");
+       for(ii=0;ii<rrd.stat_head->ds_cnt;ii++){
+           double value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_val].u_val;
+           printf("\t\t\t<ds>");       
+           if (isnan(value)){
+             printf("<value> NaN </value>");
+           } else {
+             printf("<value> %0.10e </value>", value);
+           }
+           printf("  <unknown_datapoints> %lu </unknown_datapoints>",
+                   rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_unkn_pdp_cnt].u_cnt);
+          printf("</ds>\n");    
+        }
+       printf("\t\t</cdp_prep>\n");
+
+       printf("\t\t<database>\n");
+       fseek(in_file,(rra_start
+                      +(rrd.rra_ptr[i].cur_row+1)
+                      * rrd.stat_head->ds_cnt
+                      * sizeof(rrd_value_t)),SEEK_SET);
+       timer = - (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) {
+               fseek(in_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
+           strftime(somestring,200,"%Y-%m-%d %H:%M:%S %Z", localtime_r(&now, &tm));
+#else
+# error "Need strftime"
+#endif
+           printf("\t\t\t<!-- %s --> <row>",somestring);
+           for(iii=0;iii<rrd.stat_head->ds_cnt;iii++){                  
+               fread(&my_cdp,sizeof(rrd_value_t),1,in_file);           
+               if (isnan(my_cdp)){
+                 printf("<v> NaN </v>");
+               } else {
+                 printf("<v> %0.10e </v>",my_cdp);
+               };
+           }
+           printf("</row>\n");
+       }
+       printf("\t\t</database>\n\t</rra>\n");
+       
+    }
+    printf("</rrd>\n");
+    rrd_free(&rrd);
+    fclose(in_file);
+    return(0);
+}
+
+
+
+
diff --git a/program/src/rrd_thread_safe.c b/program/src/rrd_thread_safe.c
new file mode 100644 (file)
index 0000000..ca99202
--- /dev/null
@@ -0,0 +1,72 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ * 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_)
+{
+    struct rrd_context *ctx = ctx_;
+    if (ctx) rrd_free_context(ctx);
+}
+
+/* Allocate the key */
+static void context_get_key()
+{
+    pthread_key_create(&context_key, context_destroy_context);
+}
+
+struct rrd_context *rrd_get_context(void) {
+    struct rrd_context *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) {
+    struct rrd_context *ctx = rrd_get_context();
+    if (strerror_r(err, ctx->lib_errstr, ctx->errlen)) 
+         return "strerror_r faild. sorry!"; 
+    else 
+         return ctx->lib_errstr; 
+}
+#else
+#undef strerror
+const char *rrd_strerror(int err) {
+    static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
+    struct rrd_context *ctx;
+    ctx = rrd_get_context();
+    pthread_mutex_lock(&mtx);
+    strncpy(ctx->lib_errstr, strerror(err), ctx->errlen);
+    ctx->lib_errstr[ctx->errlen]='\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..53ebda3
--- /dev/null
@@ -0,0 +1,142 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ * 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(&context_key_once, 1)) {
+               context_key = TlsAlloc();
+               InitializeCriticalSection(&CriticalSection);
+               atexit(context_destroy_context);
+       }
+}
+struct rrd_context *rrd_get_context(void) {
+    struct rrd_context *ctx;
+               
+       context_init_context();
+    ctx = TlsGetValue(context_key);
+    if (!ctx) {
+               ctx = rrd_new_context();
+               TlsSetValue(context_key, ctx);
+    } 
+    return ctx;
+}
+#undef strerror
+const char *rrd_strerror(int err) {
+    struct rrd_context *ctx;
+       context_init_context();
+
+       ctx = rrd_get_context();
+
+    EnterCriticalSection(&CriticalSection); 
+    strncpy(ctx->lib_errstr, strerror(err), ctx->errlen);
+    ctx->lib_errstr[ctx->errlen] = '\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..f1eb116
--- /dev/null
@@ -0,0 +1,874 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_tool.c  Startup wrapper
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_xport.h"
+
+void PrintUsage(char *cmd);
+int CountArgs(char *aLine);
+int CreateArgs(char *, char *, int, 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)
+{
+
+    char help_main[] =
+          "RRDtool " PACKAGE_VERSION "  Copyright 1997-2007 by Tobias Oetiker <tobi@oetiker.ch>\n"
+           "               Compiled " __DATE__ " " __TIME__ "\n\n"
+          "Usage: rrdtool [options] command command_options\n\n";
+
+    char help_list[] =
+          "Valid commands: create, update, updatev, graph, dump, restore,\n"
+          "\t\tlast, lastupdate, first, info, fetch, tune,\n"
+          "\t\tresize, xport\n\n";
+
+    char help_listremote[] =
+           "Valid remote commands: quit, ls, cd, mkdir, pwd\n\n";
+
+
+    char help_create[] =
+          "* create - create a new RRD\n\n"
+          "\trrdtool create filename [--start|-b start time]\n"
+          "\t\t[--step|-s step]\n"
+          "\t\t[DS:ds-name:DST:dst arguments]\n" 
+          "\t\t[RRA:CF:cf arguments]\n\n";
+
+    char help_dump[] =
+          "* dump - dump an RRD to XML\n\n"
+          "\trrdtool dump filename.rrd >filename.xml\n\n";
+
+    char help_info[] =
+          "* info - returns the configuration and status of the RRD\n\n"
+          "\trrdtool info filename.rrd\n\n";
+
+    char help_restore[] =
+          "* restore - restore an RRD file from its XML form\n\n"
+          "\trrdtool restore [--range-check|-r] [--force-overwrite|-f] filename.xml filename.rrd\n\n";
+
+    char help_last[] =
+           "* last - show last update time for RRD\n\n"
+           "\trrdtool last filename.rrd\n\n";
+
+    char help_lastupdate[] =
+          "* lastupdate - returns the most recent datum stored for\n"
+          "  each DS in an RRD\n\n"
+          "\trrdtool lastupdate filename.rrd\n\n"; 
+
+    char help_first[] =
+           "* first - show first update time for RRA within an RRD\n\n"
+           "\trrdtool first filename.rrd [--rraindex number]\n\n";
+
+    char help_update[] =
+          "* update - update an RRD\n\n"
+          "\trrdtool update 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\n";
+    
+       char help_updatev[] =
+          "* 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\n";
+    char help_fetch[] =
+          "* 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\n";
+
+/* break up very large strings (help_graph, help_tune) for ISO C89 compliance*/
+
+    char help_graph1[] =
+          "* graph - generate a graph from one or several RRD\n\n"
+          "\trrdtool graph filename [-s|--start seconds] [-e|--end seconds]\n"
+          "\t\t[-x|--x-grid x-axis grid and label]\n"
+          "\t\t[-Y|--alt-y-grid]\n"
+          "\t\t[-y|--y-grid y-axis grid and label]\n"
+          "\t\t[-v|--vertical-label string] [-w|--width pixels]\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]\n"
+          "\t\t[-F|--force-rules-legend]\n"
+           "\t\t[-j|--only-graph]\n";
+    char help_graph2[] =
+          "\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[-R|--font-render-mode {normal,light,mono}]\n"
+          "\t\t[-B|--font-smoothing-threshold size]\n"
+          "\t\t[-E|--slope-mode]\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]] [-t|--title string]\n"
+          "\t\t[-W|--watermark string]\n"
+          "\t\t[DEF:vname=rrd:ds-name:CF]\n";
+    char help_graph3[] =
+          "\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[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\n";
+
+    char help_tune1[] =
+          " * 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";
+    char help_tune2[] =
+          " * tune -  Modify some basic properties of an RRD\n\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\n";
+
+    char help_resize[] =
+          " * resize - alter the length of one of the RRAs in an RRD\n\n"
+          "\trrdtool resize filename rranum GROW|SHRINK rows\n\n";
+
+    char help_xport[] =
+          "* 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\n";
+
+    char help_quit[] =
+          " * quit - closing a session in remote mode\n\n"
+          "\trrdtool quit\n\n";
+
+    char help_ls[] =
+          " * ls - lists all *.rrd files in current directory\n\n"
+          "\trrdtool ls\n\n";
+
+    char help_cd[] =
+          " * cd - changes the current directory\n\n"
+          "\trrdtool cd new directory\n\n";
+
+    char help_mkdir[] =
+          " * mkdir - creates a new directory\n\n"
+          "\trrdtool mkdir newdirectoryname\n\n";
+
+    char help_pwd[] =
+          " * pwd - returns the current working directory\n\n"
+          "\trrdtool pwd\n\n";
+
+    char help_lic[] =
+          "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\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_TUNE,
+          C_RESIZE, C_XPORT, C_QUIT, C_LS, C_CD, C_MKDIR, C_PWD,
+          C_UPDATEV };
+
+    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,"graph"))
+               help_cmd = C_GRAPH;
+           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;
+       }
+    fputs(help_main, stdout);
+    switch (help_cmd)
+       {
+           case C_NONE:
+               fputs(help_list, stdout);
+                if (RemoteMode){
+                   fputs(help_listremote, stdout);
+                }
+               break;
+           case C_CREATE:
+               fputs(help_create, stdout);
+               break;
+           case C_DUMP:
+               fputs(help_dump, stdout);
+               break;
+           case C_INFO:
+               fputs(help_info, stdout);
+               break;
+           case C_RESTORE:
+               fputs(help_restore, stdout);
+               break;
+           case C_LAST:
+               fputs(help_last, stdout);
+               break;
+           case C_LASTUPDATE:
+               fputs(help_lastupdate, stdout);
+               break;
+           case C_FIRST:
+               fputs(help_first, stdout);
+               break;
+           case C_UPDATE:
+               fputs(help_update, stdout);
+               break;
+           case C_UPDATEV:
+               fputs(help_updatev, stdout);
+               break;
+           case C_FETCH:
+               fputs(help_fetch, stdout);
+               break;
+           case C_GRAPH:
+               fputs(help_graph1, stdout);
+               fputs(help_graph2, stdout);
+               fputs(help_graph3, stdout);
+               break;
+           case C_TUNE:
+               fputs(help_tune1, stdout);
+               fputs(help_tune2, stdout);
+               break;
+           case C_RESIZE:
+               fputs(help_resize, stdout);
+               break;
+           case C_XPORT:
+               fputs(help_xport, stdout);
+               break;
+           case C_QUIT:
+               fputs(help_quit, stdout);
+               break;
+           case C_LS:
+               fputs(help_ls, stdout);
+               break;
+           case C_CD:
+               fputs(help_cd, stdout);
+               break;
+           case C_MKDIR:
+               fputs(help_mkdir, stdout);
+               break;
+           case C_PWD:
+               fputs(help_pwd, stdout);
+               break;
+       }
+    fputs(help_lic, stdout);
+}
+
+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))) {
+         perror("fgetslong: realloc");
+         exit(1);
+      }
+   }
+   return *aLinePtr = linebuf[0] ? linebuf : 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
+    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
+                chroot(argv[2]);
+                if (errno!=0){
+                   fprintf(stderr,"ERROR: can't change root to '%s' errno=%d\n",
+                           argv[2],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,"")){
+             chdir(firstdir);
+             if (errno!=0){
+                fprintf(stderr,"ERROR: %s\n",rrd_strerror(errno));
+                exit(errno);
+             }
+          }
+
+           while (fgetslong(&aLine, stdin)){
+               if ((argc = CountArgs(aLine)) == 0)  {
+                   printf("ERROR: not enough arguments\n");
+               }
+               if ((myargv = (char **) malloc((argc+1) * 
+                                              sizeof(char *))) == NULL)   {
+                   perror("malloc");
+                   exit(1);
+               }
+               if ((argc=CreateArgs(argv[0], aLine, argc, myargv)) < 0) {
+                   printf("ERROR: creating arguments\n");
+               } else {
+                   int ret = HandleInputLine(argc, myargv, stdout);
+                   free(myargv);
+                   if (ret == 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(aLine);
+           }
+       }
+    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
+    char* cwd; /* To hold current working dir on call to pwd */
+
+    /* Reset errno to 0 before we start.
+    */
+    errno = 0;
+
+    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
+          chdir(argv[2]);
+          if (errno!=0){
+             printf("ERROR: %s\n",rrd_strerror(errno));
+            return(1);
+          }
+          return(0);
+       }
+       if (argc>1 && strcmp("pwd", argv[1]) == 0){
+          if (argc>2){
+             printf("ERROR: invalid parameter count for pwd\n");
+             return(1);
+          }
+          cwd = getcwd(NULL, MAXPATH);
+          if(cwd == NULL) {
+             printf("ERROR: %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
+          mkdir(argv[2],0777);
+          if (errno!=0){
+             printf("ERROR: %s\n",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: %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){
+       info_t *data,*save;
+       if (strcmp("info",argv[1]) == 0)
+          data=rrd_info(argc-1, &argv[1]);
+    else
+          data=rrd_update_v(argc-1, &argv[1]);
+       while (data) {
+           save=data;
+           printf ("%s = ", data->key);
+           free(data->key);
+           
+           switch (data->type) {
+           case RD_I_VAL:
+               if (isnan (data->value.u_val))
+                   printf("NaN");
+               else
+                   printf ("%0.10e", data->value.u_val);
+               break;
+           case RD_I_CNT:
+               printf ("%lu", data->value.u_cnt);
+               break;
+           case RD_I_INT:
+               printf ("%d", data->value.u_int);
+               break;
+           case RD_I_STR:
+               printf ("\"%s\"", data->value.u_str);
+               free(data->value.u_str);
+               break;
+           }
+           data = data->next;
+           free(save);
+           printf ("\n");
+       }
+       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-2005 (%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) {
+          time_t      last_update;
+           char        **ds_namv;
+           char        **last_ds;
+           unsigned long ds_cnt,
+                       i;
+          if (rrd_lastupdate(argc-1, &argv[1], &last_update,
+                       &ds_cnt, &ds_namv, &last_ds) == 0) {
+               for (i=0; i<ds_cnt; i++)
+                       printf(" %s", ds_namv[i]);
+               printf("\n\n");
+               printf("%10lu:", last_update);
+               for (i=0; i<ds_cnt; i++) {
+                       printf(" %s", last_ds[i]);
+                       free(last_ds[i]);
+                       free(ds_namv[i]);
+               }
+               printf("\n");
+               free(last_ds);
+               free(ds_namv);
+          }
+    } 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) {
+         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, 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, 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);
+       }
+        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("tune", argv[1]) == 0) 
+               rrd_tune(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, int argc, char **argv)
+{
+    char       *getP, *putP;
+    char       **pargv = argv;
+    char        Quote = 0;
+    int inArg = 0;
+    int        len;
+
+    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..6663c56
--- /dev/null
@@ -0,0 +1,203 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * 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
+
+#ifdef MUST_DISABLE_SIGFPE
+#include <signal.h>
+#endif
+
+#ifdef MUST_DISABLE_FPMASK
+#include <floatingpoint.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#if HAVE_SYS_PARAM_H
+#  include <sys/param.h>
+#endif
+
+#ifndef MAXPATH
+#  define MAXPATH 1024
+#endif
+
+#if HAVE_MATH_H
+# include <math.h>
+#endif
+/* Sorry: don't know autoconf as well how to check the exist of
+   dirent.h ans sys/stat.h
+*/
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# if HAVE_SYS_NDIR_H
+#  include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+#  include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+#  include <ndir.h>
+# endif
+#endif
+
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#if HAVE_SYS_TIMES_H
+# include <sys/times.h>
+#endif
+
+
+#if HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+#if (defined(__svr4__) && defined(__sun__))
+/* Solaris headers (pre 2.6) don't have a getrusage prototype.
+   Use this instead. */
+extern int getrusage(int, struct rusage *);
+#endif /* __svr4__ && __sun__ */
+#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 */
+#include "rrd_getopt.h"
+#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]))
+
+/* rrd info interface */
+enum info_type   { RD_I_VAL=0,
+              RD_I_CNT,
+              RD_I_STR, 
+                  RD_I_INT };
+
+typedef union infoval { 
+    unsigned long u_cnt; 
+    rrd_value_t   u_val;
+    char         *u_str;
+    int                  u_int;
+} infoval;
+
+typedef struct info_t {
+    char            *key;
+    enum info_type  type;
+    union infoval   value;
+    struct info_t   *next;
+} info_t;
+
+info_t *rrd_info(int, char **);
+int rrd_lastupdate(int argc, char **argv, time_t *last_update,
+                unsigned long *ds_cnt, char ***ds_namv, char ***last_ds);
+info_t *rrd_update_v(int, char **);
+char * sprintf_alloc(char *, ...);
+info_t *info_push(info_t *, char *, enum info_type, infoval);
+
+/* 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);
+
+void rrd_free(rrd_t *rrd);
+void rrd_freemem(void *mem);
+void rrd_init(rrd_t *rrd);
+
+int rrd_open(const char *file_name, FILE **in_file, rrd_t *rrd, int rdwr);
+int readfile(const char *file, char **buffer, int skipfirst);
+
+#define RRD_READONLY    0
+#define RRD_READWRITE   1
+
+enum cf_en cf_conv(const char *string);
+enum dst_en dst_conv(char *string);
+long ds_match(rrd_t *rrd,char *ds_nam);
+double rrd_diff(char *a, char *b);
+
+    /* rrd_strerror is thread safe, but still it uses a global buffer
+       (but one per thread), thus subsequent calls within a single
+       thread overwrite the same buffer */
+const char *rrd_strerror(int err);
+
+#endif
+
+#ifdef  __cplusplus
+}
+#endif
diff --git a/program/src/rrd_tune.c b/program/src/rrd_tune.c
new file mode 100644 (file)
index 0000000..8aa1630
--- /dev/null
@@ -0,0 +1,406 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * 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 "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
+rrd_tune(int argc, char **argv)    
+{   
+    rrd_t               rrd;
+    FILE               *rrd_file;
+    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];
+    optind = 0; opterr = 0;  /* initialize getopt */
+
+
+    if(rrd_open(argv[1],&rrd_file,&rrd, RRD_READWRITE)==-1){
+        return -1;
+    }
+
+    
+    while (1){
+       static 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'},
+               {"aberrant-reset",required_argument,0,'b'},
+           {0,0,0,0}
+       };
+       int option_index = 0;
+       int opt;
+       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':
+           if ((matches = sscanf(optarg, DS_NAM_FMT ":%ld",ds_nam,&heartbeat)) != 2){
+               rrd_set_error("invalid arguments for heartbeat");
+               rrd_free(&rrd);
+               fclose(rrd_file);
+               return -1;
+           }
+           if ((ds=ds_match(&rrd,ds_nam))==-1){
+               rrd_free(&rrd);
+                fclose(rrd_file);
+               return -1;
+           }
+           rrd.ds_def[ds].par[DS_mrhb_cnt].u_cnt = heartbeat;
+           break;
+
+       case 'i':
+           if ((matches = sscanf(optarg,DS_NAM_FMT ":%lf",ds_nam,&min)) <1){
+               rrd_set_error("invalid arguments for minimum ds value");
+               rrd_free(&rrd);
+                fclose(rrd_file);
+               return -1;
+           }
+           if ((ds=ds_match(&rrd,ds_nam))==-1){
+               rrd_free(&rrd);
+                fclose(rrd_file);
+               return -1;
+           }
+
+           if(matches == 1)
+               min= DNAN;
+           rrd.ds_def[ds].par[DS_min_val].u_val = min;
+           break;
+
+       case 'a':
+           if ((matches = sscanf(optarg, DS_NAM_FMT ":%lf",ds_nam,&max)) <1){
+               rrd_set_error("invalid arguments for maximum ds value");
+               rrd_free(&rrd);
+               fclose(rrd_file);
+               return -1;
+           }
+           if ((ds=ds_match(&rrd,ds_nam))==-1){
+               rrd_free(&rrd);
+               fclose(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);
+               fclose(rrd_file);
+               return -1;
+           }
+           if ((ds=ds_match(&rrd,ds_nam))==-1){
+               rrd_free(&rrd);
+               fclose(rrd_file);
+               return -1;
+           }
+           if ((int)dst_conv(dst) == -1){
+               rrd_free(&rrd);
+               fclose(rrd_file);
+               return -1;
+           }
+           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);
+               fclose(rrd_file);
+               return -1;
+           }
+           if ((ds=ds_match(&rrd,ds_nam))==-1){
+               rrd_free(&rrd);
+               fclose(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)) {
+                  rrd_free(&rrd);
+                  return -1;
+               }
+               break;
+       case 'y':
+               if (set_hwarg(&rrd,CF_HWPREDICT,RRA_hw_beta,optarg)) {
+                  rrd_free(&rrd);
+                  return -1;
+               }
+               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);
+               fclose(rrd_file);
+               return -1;
+           }
+           if ((ds=ds_match(&rrd,ds_nam))==-1){
+           /* ds_match handles it own errors */        
+               rrd_free(&rrd);
+               fclose(rrd_file);
+               return -1;
+           }
+           reset_aberrant_coefficients(&rrd,rrd_file,(unsigned long) ds);
+               if (rrd_test_error()) {
+                  rrd_free(&rrd);
+                  fclose(rrd_file);
+                  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);         
+            fclose(rrd_file);
+            return -1;
+        }
+    }
+       if(optcnt>0){
+       
+       fseek(rrd_file,0,SEEK_SET);
+       fwrite(rrd.stat_head,
+              sizeof(stat_head_t),1, rrd_file);
+       fwrite(rrd.ds_def,
+              sizeof(ds_def_t), rrd.stat_head->ds_cnt, rrd_file);
+       /* need to write rra_defs for RRA parameter changes */
+       fwrite(rrd.rra_def, sizeof(rra_def_t), rrd.stat_head->rra_cnt,
+                  rrd_file);
+    } 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);
+               }
+    }
+    fclose(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_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..ed79a6a
--- /dev/null
@@ -0,0 +1,1568 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_update.c  RRD Update Function
+ *****************************************************************************
+ * $Id$
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include <sys/types.h>
+#include <fcntl.h>
+#ifdef HAVE_MMAP
+# include <sys/mman.h>
+#endif
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+ #include <sys/locking.h>
+ #include <sys/stat.h>
+ #include <io.h>
+#endif
+
+#include "rrd_hw.h"
+#include "rrd_rpncalc.h"
+
+#include "rrd_is_thread_safe.h"
+#include "unused.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
+/*
+ * normilize 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 += 1000000L;
+       }
+}
+
+/* Local prototypes */
+int LockRRD(FILE *rrd_file);
+#ifdef HAVE_MMAP
+info_t *write_RRA_row (rrd_t *rrd, unsigned long rra_idx, 
+                                       unsigned long *rra_current,
+                                       unsigned short CDP_scratch_idx,
+#ifndef DEBUG
+FILE UNUSED(*rrd_file),
+#else
+FILE *rrd_file,
+#endif
+                                       info_t *pcdp_summary, time_t *rra_time, void *rrd_mmaped_file);
+#else
+info_t *write_RRA_row (rrd_t *rrd, unsigned long rra_idx, 
+                                       unsigned long *rra_current,
+                                       unsigned short CDP_scratch_idx, FILE *rrd_file,
+                                       info_t *pcdp_summary, time_t *rra_time);
+#endif
+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, 
+                                       info_t*);
+
+#define IFDNAN(X,Y) (isnan(X) ? (Y) : (X));
+
+
+info_t *rrd_update_v(int argc, char **argv)
+{
+    char             *tmplt = NULL;          
+       info_t *result = NULL;
+       infoval rc;
+      rc.u_int = -1;
+    optind = 0; opterr = 0;  /* initialize getopt */
+
+    while (1) {
+               static struct option long_options[] =
+                       {
+                               {"template",      required_argument, 0, 't'},
+                               {0,0,0,0}
+                       };
+               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;
+               }
+    }
+
+    /* 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 = 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)
+{
+    char             *tmplt = NULL;          
+    int rc;
+    optind = 0; opterr = 0;  /* initialize getopt */
+
+    while (1) {
+               static struct option long_options[] =
+                       {
+                               {"template",      required_argument, 0, 't'},
+                               {0,0,0,0}
+                       };
+               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]);
+                       return(-1);
+               }
+    }
+
+    /* need at least 2 arguments: filename, data. */
+    if (argc-optind < 2) {
+               rrd_set_error("Not enough arguments");
+
+               return -1;
+    }
+       rc = rrd_update_r(argv[optind], tmplt,
+                     argc - optind - 1, (const char **)(argv + optind + 1));
+    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, 
+   info_t *pcdp_summary)
+{
+
+    int              arg_i = 2;
+    short            j;
+    unsigned long    i,ii,iii=1;
+
+    unsigned long    rra_begin;          /* byte pointer to the rra
+                                         * area in the rrd file.  this
+                                         * pointer never changes value */
+    unsigned long    rra_start;          /* byte pointer to the rra
+                                         * area in the rrd file.  this
+                                         * pointer changes as each rrd is
+                                         * processed. */
+    unsigned long    rra_current;        /* byte pointer to the current write
+                                         * spot in the rrd file. */
+    unsigned long    rra_pos_tmp;        /* temporary byte pointer. */
+    double           interval,
+                     pre_int,post_int;   /* interval between this and
+                                         * the last run */
+    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 */
+    rrd_value_t      *pdp_new;           /* prepare the incoming data
+                                         * to be added the the
+                                         * existing entry */
+    rrd_value_t      *pdp_temp;          /* prepare the pdp values 
+                                         * to be added the the
+                                         * cdp values */
+
+    long             *tmpl_idx;          /* index representing the settings
+                                           transported by the tmplt index */
+    unsigned long    tmpl_cnt = 2;       /* time and data */
+
+    FILE             *rrd_file;
+    rrd_t            rrd;
+    time_t           current_time = 0;
+    time_t           rra_time = 0;      /* time of update for a RRA */
+    unsigned long    current_time_usec=0;/* microseconds part of current time */
+    struct timeval   tmp_time;           /* used for time conversion */
+
+    char             **updvals;
+    int              schedule_smooth = 0;
+       rrd_value_t      *seasonal_coef = NULL, *last_seasonal_coef = NULL;
+                                        /* a vector of future Holt-Winters seasonal coefs */
+    unsigned long    elapsed_pdp_st;
+                                        /* number of elapsed PDP steps since last update */
+    unsigned long    *rra_step_cnt = NULL;
+                                        /* number of rows to be updated in an RRA for a data
+                                         * value. */
+    unsigned long    start_pdp_offset;
+                                        /* 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;
+                                        /* index into the CDP scratch array */
+    enum cf_en       current_cf;
+                                        /* numeric id of the current consolidation function */
+    rpnstack_t       rpnstack; /* used for COMPUTE DS */
+    int                     version;  /* rrd version */
+    char             *endptr; /* used in the conversion */
+
+#ifdef HAVE_MMAP
+    void            *rrd_mmaped_file;
+    unsigned long    rrd_filesize;
+#endif
+
+
+    rpnstack_init(&rpnstack);
+
+    /* need at least 1 arguments: data. */
+    if (argc < 1) {
+       rrd_set_error("Not enough arguments");
+       return -1;
+    }
+    
+    
+
+    if(rrd_open(filename,&rrd_file,&rrd, RRD_READWRITE)==-1){
+       return -1;
+    }
+
+    /* initialize time */
+    version = atoi(rrd.stat_head->version);
+    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;
+    }
+
+    rra_current = rra_start = rra_begin = ftell(rrd_file);
+    /* This is defined in the ANSI C standard, section 7.9.5.3:
+
+        When a file is opened with udpate mode ('+' as the second
+        or third character in the ... list of mode argument
+        variables), both input and ouptut may be performed on the
+        associated stream.  However, ...  input may not be directly
+        followed by output without an intervening call to a file
+        positioning function, unless the input oepration encounters
+        end-of-file. */
+#ifdef HAVE_MMAP
+    fseek(rrd_file, 0, SEEK_END);
+    rrd_filesize = ftell(rrd_file);
+    fseek(rrd_file, rra_current, SEEK_SET);
+#else
+    fseek(rrd_file, 0, SEEK_CUR);
+#endif
+
+    
+    /* get exclusive lock to whole file.
+     * lock gets removed when we close the file.
+     */
+    if (LockRRD(rrd_file) != 0) {
+      rrd_set_error("could not lock RRD");
+      rrd_free(&rrd);
+      fclose(rrd_file);
+      return(-1);   
+    } 
+
+    if((updvals = malloc( sizeof(char*) * (rrd.stat_head->ds_cnt+1)))==NULL){
+       rrd_set_error("allocating updvals pointer array");
+       rrd_free(&rrd);
+        fclose(rrd_file);
+       return(-1);
+    }
+
+    if ((pdp_temp = malloc(sizeof(rrd_value_t)
+                          *rrd.stat_head->ds_cnt))==NULL){
+       rrd_set_error("allocating pdp_temp ...");
+       free(updvals);
+       rrd_free(&rrd);
+        fclose(rrd_file);
+       return(-1);
+    }
+
+    if ((tmpl_idx = malloc(sizeof(unsigned long)
+                          *(rrd.stat_head->ds_cnt+1)))==NULL){
+       rrd_set_error("allocating tmpl_idx ...");
+       free(pdp_temp);
+       free(updvals);
+       rrd_free(&rrd);
+        fclose(rrd_file);
+       return(-1);
+    }
+    /* 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) {
+       /* we should work on a writeable copy here */
+       char *dsname;
+       unsigned int tmpl_len;
+       char *tmplt_copy = strdup(tmplt);
+       dsname = tmplt_copy;
+       tmpl_cnt = 1; /* the first entry is the time */
+       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");
+                   free(updvals); free(pdp_temp);
+                   free(tmpl_idx); rrd_free(&rrd);
+                   fclose(rrd_file); return(-1);
+               }
+               if ((tmpl_idx[tmpl_cnt++] = ds_match(&rrd,dsname)) == -1){
+                   rrd_set_error("unknown DS name '%s'",dsname);
+                   free(updvals); free(pdp_temp);
+                   free(tmplt_copy);
+                   free(tmpl_idx); rrd_free(&rrd);
+                   fclose(rrd_file); return(-1);
+               } else {
+                 /* the first element is always the time */
+                 tmpl_idx[tmpl_cnt-1]++; 
+                 /* go to the next entry on the tmplt_copy */
+                 dsname = &tmplt_copy[i+1];
+                  /* fix the damage we did before */
+                  if (i<tmpl_len) {
+                     tmplt_copy[i]=':';
+                  } 
+
+               }
+           }       
+       }
+       free(tmplt_copy);
+    }
+    if ((pdp_new = malloc(sizeof(rrd_value_t)
+                         *rrd.stat_head->ds_cnt))==NULL){
+       rrd_set_error("allocating pdp_new ...");
+       free(updvals);
+       free(pdp_temp);
+       free(tmpl_idx);
+       rrd_free(&rrd);
+        fclose(rrd_file);
+       return(-1);
+    }
+
+#ifdef HAVE_MMAP
+    rrd_mmaped_file = mmap(0, 
+                       rrd_filesize, 
+                       PROT_READ | PROT_WRITE, 
+                       MAP_SHARED, 
+                       fileno(rrd_file), 
+                       0);
+    if (rrd_mmaped_file == MAP_FAILED) {
+        rrd_set_error("error mmapping file %s", filename);
+       free(updvals);
+       free(pdp_temp);
+       free(tmpl_idx);
+       rrd_free(&rrd);
+        fclose(rrd_file);
+       return(-1);
+    }
+#ifdef HAVE_MADVISE
+    /* when we use mmaping we tell the kernel the mmap equivalent
+       of POSIX_FADV_RANDOM */
+    madvise(rrd_mmaped_file,rrd_filesize,POSIX_MADV_RANDOM);
+#endif
+#endif
+    /* loop through the arguments. */
+    for(arg_i=0; arg_i<argc;arg_i++) {
+       char *stepper = strdup(argv[arg_i]);
+        char *step_start = stepper;
+       char *p;
+       char *parsetime_error = NULL;
+       enum {atstyle, normal} timesyntax;
+       struct rrd_time_value ds_tv;
+        if (stepper == NULL){
+                rrd_set_error("failed duplication argv entry");
+               free(step_start);
+                free(updvals);
+                free(pdp_temp);  
+                free(tmpl_idx);
+                rrd_free(&rrd);
+#ifdef HAVE_MMAP
+               munmap(rrd_mmaped_file, rrd_filesize);
+#endif
+                fclose(rrd_file);
+                return(-1);
+         }
+       /* initialize all ds input to unknown except the first one
+           which has always got to be set */
+       for(ii=1;ii<=rrd.stat_head->ds_cnt;ii++) updvals[ii] = "U";
+       updvals[0]=stepper;
+       /* separate all ds elements; first must be examined separately
+          due to alternate time syntax */
+       if ((p=strchr(stepper,'@'))!=NULL) {
+           timesyntax = atstyle;
+           *p = '\0';
+           stepper = p+1;
+       } else if ((p=strchr(stepper,':'))!=NULL) {
+           timesyntax = normal;
+           *p = '\0';
+           stepper = p+1;
+       } else {
+           rrd_set_error("expected timestamp not found in data source from %s",
+                         argv[arg_i]);
+           free(step_start);
+           break;
+       }
+       ii=1;
+       updvals[tmpl_idx[ii]] = stepper;
+       while (*stepper) {
+           if (*stepper == ':') {
+               *stepper = '\0';
+               ii++;
+               if (ii<tmpl_cnt){                   
+                   updvals[tmpl_idx[ii]] = stepper+1;
+               }
+           }
+           stepper++;
+       }
+
+       if (ii != tmpl_cnt-1) {
+           rrd_set_error("expected %lu data source readings (got %lu) from %s",
+                         tmpl_cnt-1, ii, argv[arg_i]);
+           free(step_start);
+           break;
+       }
+       
+        /* get the time from the reading ... handle N */
+       if (timesyntax == atstyle) {
+            if ((parsetime_error = parsetime(updvals[0], &ds_tv))) {
+                rrd_set_error("ds time: %s: %s", updvals[0], parsetime_error );
+               free(step_start);
+               break;
+           }
+           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]);
+               free(step_start);
+               break;
+           }
+
+           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 {
+           double tmp;
+           tmp = strtod(updvals[0], 0);
+           current_time = floor(tmp);
+           current_time_usec = (long)((tmp-(double)current_time) * 1000000.0);
+       }
+       /* 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);
+           free(step_start);
+           break;
+       }
+       
+       
+       /* seek to the beginning of the rra's */
+       if (rra_current != rra_begin) {
+#ifndef HAVE_MMAP
+           if(fseek(rrd_file, rra_begin, SEEK_SET) != 0) {
+               rrd_set_error("seek error in rrd");
+               free(step_start);
+               break;
+           }
+#endif
+           rra_current = rra_begin;
+       }
+       rra_start = rra_begin;
+
+       /* 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;
+
+       /* interval = current_time - rrd.live_head->last_up; */
+       interval    = (double)(current_time - rrd.live_head->last_up) 
+                   + (double)((long)current_time_usec - (long)rrd.live_head->last_up_usec)/1000000.0;
+
+       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)/1000000.0; /* adjust usecs */
+           post_int = occu_pdp_age;                         /* how much after it */
+           post_int += ((double)current_time_usec)/1000000.0;  /* adjust usecs */
+       } else {
+           pre_int = interval;
+           post_int = 0;
+       }
+
+#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
+    
+       /* process the data sources and update the pdp_prep 
+        * area accordingly */
+       for(i=0;i<rrd.stat_head->ds_cnt;i++){
+           enum dst_en dst_idx;
+           dst_idx= dst_conv(rrd.ds_def[i].dst);
+
+            /* make sure we do not build diffs with old last_ds values */
+           if(rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt < interval) {
+               strncpy(rrd.pdp_prep[i].last_ds,"U",LAST_DS_LEN-1);
+               rrd.pdp_prep[i].last_ds[LAST_DS_LEN-1]='\0';
+           }
+
+           /* NOTE: DST_CDEF should never enter this if block, because
+             * updvals[i+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[i+1][0] != 'U') &&
+                  (dst_idx != DST_CDEF) &&
+              rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt >= interval) {
+              double rate = DNAN;
+              /* the data source type defines how to process the data */
+               /* 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:
+                   if(rrd.pdp_prep[i].last_ds[0] != 'U'){
+                      for(ii=0;updvals[i+1][ii] != '\0';ii++){
+                            if((updvals[i+1][ii] < '0' || updvals[i+1][ii] > '9') && (ii != 0 && updvals[i+1][ii] != '-')){
+                                 rrd_set_error("not a simple integer: '%s'",updvals[i+1]);
+                                 break;
+                            }
+                       }
+                       if (rrd_test_error()){
+                            break;
+                       }
+                      pdp_new[i]= rrd_diff(updvals[i+1],rrd.pdp_prep[i].last_ds);
+                      if(dst_idx == DST_COUNTER) {
+                         /* simple overflow catcher suggested by Andres Kroonmaa */
+                         /* this will fail terribly for non 32 or 64 bit counters ... */
+                         /* are there any others in SNMP land ? */
+                         if (pdp_new[i] < (double)0.0 ) 
+                           pdp_new[i] += (double)4294967296.0 ;  /* 2^32 */
+                         if (pdp_new[i] < (double)0.0 ) 
+                           pdp_new[i] += (double)18446744069414584320.0; /* 2^64-2^32 */;
+                      }
+                      rate = pdp_new[i] / interval;
+                   }
+                  else {
+                    pdp_new[i]= DNAN;          
+                  }
+                  break;
+               case DST_ABSOLUTE:
+                    errno = 0;
+                    pdp_new[i] = strtod(updvals[i+1],&endptr);
+                    if (errno > 0){
+                        rrd_set_error("converting  '%s' to float: %s",updvals[i+1],rrd_strerror(errno));
+                        break;
+                    };
+                    if (endptr[0] != '\0'){
+                        rrd_set_error("conversion of '%s' to float not complete: tail '%s'",updvals[i+1],endptr);
+                        break;
+                    }
+                   rate = pdp_new[i] / interval;                 
+                   break;
+               case DST_GAUGE:
+                    errno = 0;
+                    pdp_new[i] = strtod(updvals[i+1],&endptr) * interval;
+                    if (errno > 0){
+                        rrd_set_error("converting  '%s' to float: %s",updvals[i+1],rrd_strerror(errno));
+                        break;
+                    };
+                    if (endptr[0] != '\0'){
+                        rrd_set_error("conversion of '%s' to float not complete: tail '%s'",updvals[i+1],endptr);
+                        break;
+                    }
+                   rate = pdp_new[i] / interval;                  
+                   break;
+               default:
+                   rrd_set_error("rrd contains unknown DS type : '%s'",
+                                 rrd.ds_def[i].dst);
+                   break;
+               }
+               /* break out of this for loop if the error string is set */
+               if (rrd_test_error()){
+                   break;
+               }
+              /* 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[i].par[DS_max_val].u_val) &&
+                        rate > rrd.ds_def[i].par[DS_max_val].u_val ) ||     
+                   ( ! isnan(rrd.ds_def[i].par[DS_min_val].u_val) &&
+                       rate < rrd.ds_def[i].par[DS_min_val].u_val ))){
+                 pdp_new[i] = DNAN;
+              }               
+           } else {
+               /* no news is news all the same */
+               pdp_new[i] = 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",
+                   i,
+                   rrd.pdp_prep[i].last_ds,
+                   updvals[i+1], pdp_new[i]);
+#endif
+           strncpy(rrd.pdp_prep[i].last_ds, updvals[i+1],LAST_DS_LEN-1);
+           rrd.pdp_prep[i].last_ds[LAST_DS_LEN-1]='\0';
+       }
+       /* break out of the argument parsing loop if the error_string is set */
+       if (rrd_test_error()){
+           free(step_start);
+           break;
+       }
+       /* has a pdp_st moment occurred since the last run ? */
+
+       if (proc_pdp_st == occu_pdp_st){
+           /* no we have not passed a pdp_st moment. therefore update is simple */
+
+           for(i=0;i<rrd.stat_head->ds_cnt;i++){
+               if(isnan(pdp_new[i])) {            
+                   /* 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! */
+                   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[%lu]\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
+           }   
+       } else {
+           /* an pdp_st has occurred. */
+
+           /* 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(i=0;i<rrd.stat_head->ds_cnt;i++){
+               /* update pdp_prep to the current pdp_st. */
+                double pre_unknown = 0.0;              
+               if(isnan(pdp_new[i]))
+                    /* a final bit of unkonwn to be added bevore calculation
+                    * we use a tempaorary variable for this so that we 
+                    * don't have to turn integer lines before using the value */                
+                   pre_unknown = pre_int;
+               else {
+                    if (isnan( rrd.pdp_prep[i].scratch[PDP_val].u_val )){
+                       rrd.pdp_prep[i].scratch[PDP_val].u_val=         pdp_new[i]/interval*pre_int;
+                    } else {
+                       rrd.pdp_prep[i].scratch[PDP_val].u_val+= pdp_new[i]/interval*pre_int;
+                    }
+                }
+               
+
+               /* if too much of the pdp_prep is unknown we dump it */
+               if ( 
+                   /* removed because this does not agree with the definition
+                      a heart beat can be unknown */
+                   /* (rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt 
+                    > rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt) || */
+                   /* if the interval is larger thatn mrhb we get NAN */
+                   (interval > rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt) ||
+                   (occu_pdp_st-proc_pdp_st <= 
+                    rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt)) {
+                   pdp_temp[i] = DNAN;
+               } else {
+                   pdp_temp[i] = rrd.pdp_prep[i].scratch[PDP_val].u_val
+                       / ((double)(occu_pdp_st - proc_pdp_st
+                                    - rrd.pdp_prep[i].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[i].dst) == DST_CDEF) {
+                  rpnp_t *rpnp;
+                  rpnp = rpn_expand((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]));
+                  /* substitue data values for OP_VARIABLE nodes */
+                  for (ii = 0; rpnp[ii].op != OP_END; ii++)
+                  {
+                         if (rpnp[ii].op == OP_VARIABLE) {
+                                rpnp[ii].op = OP_NUMBER;
+                                rpnp[ii].val =  pdp_temp[rpnp[ii].ptr];
+                         }
+                  }
+                  /* run the rpn calculator */
+                  if (rpn_calc(rpnp,&rpnstack,0,pdp_temp,i) == -1) {
+                         free(rpnp);
+                         break; /* exits the data sources pdp_temp loop */
+                  }
+               }
+        
+               /* make pdp_prep ready for the next run */
+               if(isnan(pdp_new[i])){
+                   /* 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! */
+                   rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt = floor(post_int);
+                   rrd.pdp_prep[i].scratch[PDP_val].u_val = DNAN;
+               } else {
+                   rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt = 0;
+                   rrd.pdp_prep[i].scratch[PDP_val].u_val = 
+                       pdp_new[i]/interval*post_int;
+               }
+
+#ifdef DEBUG
+               fprintf(stderr,
+                       "PDP UPD ds[%lu]\t"
+                       "pdp_temp %10.2f\t"
+                       "new_prep %10.2f\t"
+                       "new_unkn_sec %5lu\n",
+                       i, pdp_temp[i],
+                       rrd.pdp_prep[i].scratch[PDP_val].u_val,
+                       rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
+#endif
+           }
+
+               /* if there were errors during the last loop, bail out here */
+           if (rrd_test_error()){
+              free(step_start);
+              break;
+           }
+
+               /* compute the number of elapsed pdp_st moments */
+               elapsed_pdp_st = (occu_pdp_st - proc_pdp_st) / rrd.stat_head -> pdp_step;
+#ifdef DEBUG
+               fprintf(stderr,"elapsed PDP steps: %lu\n", elapsed_pdp_st);
+#endif
+               if (rra_step_cnt == NULL)
+               {
+                  rra_step_cnt = (unsigned long *) 
+                         malloc((rrd.stat_head->rra_cnt)* sizeof(unsigned long));
+               }
+
+           for(i = 0, rra_start = rra_begin;
+               i < rrd.stat_head->rra_cnt;
+           rra_start += rrd.rra_def[i].row_cnt * rrd.stat_head -> ds_cnt * sizeof(rrd_value_t),
+               i++)
+               {
+               current_cf = cf_conv(rrd.rra_def[i].cf_nam);
+               start_pdp_offset = rrd.rra_def[i].pdp_cnt -
+                  (proc_pdp_st / rrd.stat_head -> pdp_step) % rrd.rra_def[i].pdp_cnt;
+        if (start_pdp_offset <= elapsed_pdp_st) {
+           rra_step_cnt[i] = (elapsed_pdp_st - start_pdp_offset) / 
+                     rrd.rra_def[i].pdp_cnt + 1;
+           } else {
+                  rra_step_cnt[i] = 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[i] > 2) 
+                  {
+                         /* skip update by resetting rra_step_cnt[i],
+                          * note that this is not data source specific; this is due
+                          * to the bulk update, not a DNAN value for the specific data
+                          * source. */
+                         rra_step_cnt[i] = 0;
+              lookup_seasonal(&rrd,i,rra_start,rrd_file,elapsed_pdp_st, 
+                            &last_seasonal_coef);
+                     lookup_seasonal(&rrd,i,rra_start,rrd_file,elapsed_pdp_st + 1,
+                            &seasonal_coef);
+                  }
+               
+                 /* periodically run a smoother for seasonal effects */
+                 /* 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). */
+                 iii = i*(rrd.stat_head -> ds_cnt);
+                 if (rrd.cdp_prep[iii].scratch[CDP_init_seasonal].u_cnt 
+                         <= BURNIN_CYCLES)
+                 {
+                    if (rrd.rra_ptr[i].cur_row + elapsed_pdp_st 
+                                > rrd.rra_def[i].row_cnt - 1) {
+                          /* mark off one of the burnin cycles */
+                          ++(rrd.cdp_prep[iii].scratch[CDP_init_seasonal].u_cnt);
+                      schedule_smooth = 1;
+                        }  
+                 } else {
+                        /* someone has no doubt invented a trick to deal with this
+                         * wrap around, but at least this code is clear. */
+                        if (rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt >
+                            rrd.rra_ptr[i].cur_row)
+                        {
+                                /* here elapsed_pdp_st = rra_step_cnt[i] because of 1-1
+                                 * mapping between PDP and CDP */
+                                if (rrd.rra_ptr[i].cur_row + elapsed_pdp_st
+                                       >= rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt)
+                                {
+#ifdef DEBUG
+                                       fprintf(stderr,
+                                       "schedule_smooth 1: cur_row %lu, elapsed_pdp_st %lu, smooth idx %lu\n",
+                    rrd.rra_ptr[i].cur_row, elapsed_pdp_st, 
+                                       rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt);
+#endif
+                                       schedule_smooth = 1;
+                                }
+             } else {
+                                /* can't rely on negative numbers because we are working with
+                                 * unsigned values */
+                                /* Don't need modulus here. If we've wrapped more than once, only
+                                 * one smooth is executed at the end. */
+                                if (rrd.rra_ptr[i].cur_row + elapsed_pdp_st >= rrd.rra_def[i].row_cnt
+                                       && rrd.rra_ptr[i].cur_row + elapsed_pdp_st - rrd.rra_def[i].row_cnt
+                                       >= rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt)
+                                {
+#ifdef DEBUG
+                                       fprintf(stderr,
+                                       "schedule_smooth 2: cur_row %lu, elapsed_pdp_st %lu, smooth idx %lu\n",
+                    rrd.rra_ptr[i].cur_row, elapsed_pdp_st, 
+                                       rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt);
+#endif
+                                       schedule_smooth = 1;
+                                }
+                        }
+                 }
+
+             rra_current = ftell(rrd_file); 
+               } /* if cf is DEVSEASONAL or SEASONAL */
+
+        if (rrd_test_error()) break;
+
+                   /* update CDP_PREP areas */
+                   /* loop over data soures within each RRA */
+                   for(ii = 0;
+                       ii < rrd.stat_head->ds_cnt;
+                       ii++)
+                       {
+                       
+                       /* iii indexes the CDP prep area for this data source within the RRA */
+                       iii=i*rrd.stat_head->ds_cnt+ii;
+
+                       if (rrd.rra_def[i].pdp_cnt > 1) {
+                         
+                          if (rra_step_cnt[i] > 0) {
+                          /* 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[ii]))
+                  {
+                                        rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt += start_pdp_offset;
+                                        rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_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. */
+                                        rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val = pdp_temp[ii];
+                                 }
+                     
+                                 if (rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt
+                                     > rrd.rra_def[i].pdp_cnt*
+                                     rrd.rra_def[i].par[RRA_cdp_xff_val].u_val)
+                                 {
+                                        rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = DNAN;
+                                        /* initialize carry over */
+                                        if (current_cf == CF_AVERAGE) {
+                                                  if (isnan(pdp_temp[ii])) { 
+                                                         rrd.cdp_prep[iii].scratch[CDP_val].u_val = DNAN;
+                                                  } else {
+                                                         rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii] *
+                                                                ((elapsed_pdp_st - start_pdp_offset) % rrd.rra_def[i].pdp_cnt);
+                                                  }
+                                        } else {
+                                               rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                        }
+                                 } else {
+                                        rrd_value_t cum_val, cur_val; 
+                                    switch (current_cf) {
+                                               case CF_AVERAGE:
+                                                 cum_val = IFDNAN(rrd.cdp_prep[iii].scratch[CDP_val].u_val, 0.0);
+                                                 cur_val = IFDNAN(pdp_temp[ii],0.0);
+                          rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val =
+                                              (cum_val + cur_val * start_pdp_offset) /
+                                          (rrd.rra_def[i].pdp_cnt
+                                              -rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt);
+                                                  /* initialize carry over value */
+                                                  if (isnan(pdp_temp[ii])) { 
+                                                         rrd.cdp_prep[iii].scratch[CDP_val].u_val = DNAN;
+                                                  } else {
+                                                         rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii] *
+                                                                ((elapsed_pdp_st - start_pdp_offset) % rrd.rra_def[i].pdp_cnt);
+                                                  }
+                                                  break;
+                                               case CF_MAXIMUM:
+                                                 cum_val = IFDNAN(rrd.cdp_prep[iii].scratch[CDP_val].u_val, -DINF);
+                                                 cur_val = IFDNAN(pdp_temp[ii],-DINF);
+#ifdef DEBUG
+                                                 if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val) &&
+                                                         isnan(pdp_temp[ii])) {
+                                                    fprintf(stderr,
+                                                               "RRA %lu, DS %lu, both CDP_val and pdp_temp are DNAN!",
+                                                               i,ii);
+                                                        exit(-1);
+                                                 }
+#endif
+                                                 if (cur_val > cum_val)
+                                                        rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = cur_val;
+                                                 else
+                                                        rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = cum_val;
+                                                 /* initialize carry over value */
+                                                 rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                                 break;
+                                               case CF_MINIMUM:
+                                                 cum_val = IFDNAN(rrd.cdp_prep[iii].scratch[CDP_val].u_val, DINF);
+                                                 cur_val = IFDNAN(pdp_temp[ii],DINF);
+#ifdef DEBUG
+                                                 if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val) &&
+                                                         isnan(pdp_temp[ii])) {
+                                                    fprintf(stderr,
+                                                               "RRA %lu, DS %lu, both CDP_val and pdp_temp are DNAN!",
+                                                               i,ii);
+                                                        exit(-1);
+                                                 }
+#endif
+                                                 if (cur_val < cum_val)
+                                                        rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = cur_val;
+                                                 else
+                                                        rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = cum_val;
+                                                 /* initialize carry over value */
+                                                 rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                                 break;
+                                               case CF_LAST:
+                                               default:
+                                                  rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = pdp_temp[ii];
+                                                  /* initialize carry over value */
+                                                  rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                               break;
+                                        }
+                                 } /* 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[ii]))
+                                        rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt = 
+                                               (elapsed_pdp_st - start_pdp_offset) % rrd.rra_def[i].pdp_cnt;
+                                 else
+                                        rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt = 0;
+               } else  /* rra_step_cnt[i]  == 0 */
+                          {
+#ifdef DEBUG
+                                 if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val)) {
+                                 fprintf(stderr,"schedule CDP_val update, RRA %lu DS %lu, DNAN\n",
+                                        i,ii);
+                                 } else {
+                                 fprintf(stderr,"schedule CDP_val update, RRA %lu DS %lu, %10.2f\n",
+                                        i,ii,rrd.cdp_prep[iii].scratch[CDP_val].u_val);
+                                 }
+#endif
+                                 if (isnan(pdp_temp[ii])) {
+                                rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt += elapsed_pdp_st;
+                                 } else if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val))
+                                 {
+                                        if (current_cf == CF_AVERAGE) {
+                                           rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii] *
+                                                  elapsed_pdp_st;
+                                        } else {
+                                           rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                        }
+#ifdef DEBUG
+                                        fprintf(stderr,"Initialize CDP_val for RRA %lu DS %lu: %10.2f\n",
+                                           i,ii,rrd.cdp_prep[iii].scratch[CDP_val].u_val);
+#endif
+                                 } else {
+                                        switch (current_cf) {
+                                        case CF_AVERAGE:
+                                           rrd.cdp_prep[iii].scratch[CDP_val].u_val += pdp_temp[ii] *
+                                                  elapsed_pdp_st;
+                                               break;
+                                        case CF_MINIMUM:
+                                               if (pdp_temp[ii] < rrd.cdp_prep[iii].scratch[CDP_val].u_val)
+                                                  rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                               break; 
+                                        case CF_MAXIMUM:
+                                               if (pdp_temp[ii] > rrd.cdp_prep[iii].scratch[CDP_val].u_val)
+                                                  rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                               break; 
+                                        case CF_LAST:
+                                        default:
+                                               rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                               break;
+                                        }
+                                 }
+                          }
+                       } else { /* rrd.rra_def[i].pdp_cnt == 1 */
+                          if (elapsed_pdp_st > 2)
+                          {
+                                  switch (current_cf) {
+                                  case CF_AVERAGE:
+                                  default:
+                                 rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val=pdp_temp[ii];
+                                 rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val=pdp_temp[ii];
+                                         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. */
+                      rrd.cdp_prep[iii].scratch[CDP_hw_last_seasonal].u_val =
+                                                last_seasonal_coef[ii];
+                                         rrd.cdp_prep[iii].scratch[CDP_hw_seasonal].u_val =
+                                                seasonal_coef[ii];
+                                         break;
+                   case CF_HWPREDICT:
+                                         /* 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. */
+                                         rrd.cdp_prep[iii].scratch[CDP_null_count].u_cnt += 
+                                                elapsed_pdp_st;
+                                         rrd.cdp_prep[iii].scratch[CDP_last_null_count].u_cnt += 
+                                                elapsed_pdp_st - 1;
+                                         /* fall through */
+                                  case CF_DEVPREDICT:
+                                 rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = DNAN;
+                                 rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val = DNAN;
+                                         break;
+                   case CF_FAILURES:
+                                         /* do not count missed bulk values as failures */
+                                 rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = 0;
+                                 rrd.cdp_prep[iii].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, iii, i);
+                                         break;
+                                  }
+                          } 
+                       } /* endif rrd.rra_def[i].pdp_cnt == 1 */
+
+                       if (rrd_test_error()) break;
+
+                       } /* endif data sources loop */
+        } /* end RRA Loop */
+
+               /* 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)
+               {
+              for(i = 0, rra_start = rra_begin;
+                  i < rrd.stat_head->rra_cnt;
+              rra_start += rrd.rra_def[i].row_cnt * rrd.stat_head -> ds_cnt * sizeof(rrd_value_t),
+                  i++)
+                  {
+                         if (rrd.rra_def[i].pdp_cnt > 1) continue;
+
+                 current_cf = cf_conv(rrd.rra_def[i].cf_nam);
+                         if (current_cf == CF_SEASONAL || current_cf == CF_DEVSEASONAL)
+                         {
+                        lookup_seasonal(&rrd,i,rra_start,rrd_file,
+                                   elapsed_pdp_st + (scratch_idx == CDP_primary_val ? 1 : 2),
+                               &seasonal_coef);
+                 rra_current = ftell(rrd_file);
+                         }
+                         if (rrd_test_error()) break;
+                     /* loop over data soures within each RRA */
+                     for(ii = 0;
+                         ii < rrd.stat_head->ds_cnt;
+                         ii++)
+                         {
+                            update_aberrant_CF(&rrd,pdp_temp[ii],current_cf,
+                                       i*(rrd.stat_head->ds_cnt) + ii,i,ii,
+                                   scratch_idx, seasonal_coef);
+                         }
+           } /* end RRA Loop */
+                  if (rrd_test_error()) break;
+           } /* end elapsed_pdp_st loop */
+
+               if (rrd_test_error()) break;
+
+               /* Ready to write to disk */
+               /* 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. */
+           for(i = 0, rra_start = rra_begin;
+               i < rrd.stat_head->rra_cnt;
+           rra_start += rrd.rra_def[i].row_cnt * rrd.stat_head -> ds_cnt * sizeof(rrd_value_t),
+               i++) {
+               /* is th5Aere anything to write for this RRA? If not, continue. */
+        if (rra_step_cnt[i] == 0) continue;
+
+               /* write the first row */
+#ifdef DEBUG
+        fprintf(stderr,"  -- RRA Preseek %ld\n",ftell(rrd_file));
+#endif
+           rrd.rra_ptr[i].cur_row++;
+           if (rrd.rra_ptr[i].cur_row >= rrd.rra_def[i].row_cnt)
+                  rrd.rra_ptr[i].cur_row = 0; /* wrap around */
+               /* positition on the first row */
+               rra_pos_tmp = rra_start +
+                  (rrd.stat_head->ds_cnt)*(rrd.rra_ptr[i].cur_row)*sizeof(rrd_value_t);
+               if(rra_pos_tmp != rra_current) {
+#ifndef HAVE_MMAP
+                  if(fseek(rrd_file, rra_pos_tmp, SEEK_SET) != 0){
+                     rrd_set_error("seek error in rrd");
+                     break;
+                  }
+#endif
+                  rra_current = rra_pos_tmp;
+               }
+
+#ifdef DEBUG
+           fprintf(stderr,"  -- RRA Postseek %ld\n",ftell(rrd_file));
+#endif
+               scratch_idx = CDP_primary_val;
+               if (pcdp_summary != NULL)
+               {
+                  rra_time = (current_time - current_time 
+                  % (rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step))
+                  - ((rra_step_cnt[i]-1)*rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step);
+               }
+#ifdef HAVE_MMAP
+               pcdp_summary = write_RRA_row(&rrd, i, &rra_current, scratch_idx, rrd_file, 
+                  pcdp_summary, &rra_time, rrd_mmaped_file);
+#else
+               pcdp_summary = write_RRA_row(&rrd, i, &rra_current, scratch_idx, rrd_file, 
+                  pcdp_summary, &rra_time);
+#endif
+               if (rrd_test_error()) break;
+
+               /* write other rows of the bulk update, if any */
+               scratch_idx = CDP_secondary_val;
+               for ( ; rra_step_cnt[i] > 1; rra_step_cnt[i]--)
+               {
+                  if (++rrd.rra_ptr[i].cur_row == rrd.rra_def[i].row_cnt)
+                  {
+#ifdef DEBUG
+              fprintf(stderr,"Wraparound for RRA %s, %lu updates left\n",
+                         rrd.rra_def[i].cf_nam, rra_step_cnt[i] - 1);
+#endif
+                         /* wrap */
+                         rrd.rra_ptr[i].cur_row = 0;
+                         /* seek back to beginning of current rra */
+                     if (fseek(rrd_file, rra_start, SEEK_SET) != 0)
+                         {
+                        rrd_set_error("seek error in rrd");
+                        break;
+                         }
+#ifdef DEBUG
+                 fprintf(stderr,"  -- Wraparound Postseek %ld\n",ftell(rrd_file));
+#endif
+                         rra_current = rra_start;
+                  }
+                  if (pcdp_summary != NULL)
+                  {
+                     rra_time = (current_time - current_time 
+                     % (rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step))
+                     - ((rra_step_cnt[i]-2)*rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step);
+                  }
+#ifdef HAVE_MMAP
+                  pcdp_summary = write_RRA_row(&rrd, i, &rra_current, scratch_idx, rrd_file,
+                     pcdp_summary, &rra_time, rrd_mmaped_file);
+#else
+                  pcdp_summary = write_RRA_row(&rrd, i, &rra_current, scratch_idx, rrd_file,
+                     pcdp_summary, &rra_time);
+#endif
+               }
+               
+               if (rrd_test_error())
+                 break;
+               } /* RRA LOOP */
+
+           /* break out of the argument parsing loop if error_string is set */
+           if (rrd_test_error()){
+                  free(step_start);
+                  break;
+           } 
+           
+       } /* endif a pdp_st has occurred */ 
+       rrd.live_head->last_up = current_time;
+       rrd.live_head->last_up_usec = current_time_usec; 
+       free(step_start);
+    } /* function argument loop */
+
+    if (seasonal_coef != NULL) free(seasonal_coef);
+    if (last_seasonal_coef != NULL) free(last_seasonal_coef);
+       if (rra_step_cnt != NULL) free(rra_step_cnt);
+    rpnstack_free(&rpnstack);
+
+#ifdef HAVE_MMAP
+    if (munmap(rrd_mmaped_file, rrd_filesize) == -1) {
+            rrd_set_error("error writing(unmapping) file: %s", filename);
+    }
+#endif    
+    /* 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()) {
+       free(updvals);
+       free(tmpl_idx);
+       rrd_free(&rrd);
+       free(pdp_temp);
+       free(pdp_new);
+       fclose(rrd_file);
+       return(-1);
+    }
+
+    /* aargh ... that was tough ... so many loops ... anyway, its done.
+     * we just need to write back the live header portion now*/
+
+    if (fseek(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");
+       free(updvals);
+       free(tmpl_idx);
+       rrd_free(&rrd);
+       free(pdp_temp);
+       free(pdp_new);
+       fclose(rrd_file);
+       return(-1);
+    }
+
+    if(version >= 3) {
+           if(fwrite( rrd.live_head,
+                      sizeof(live_head_t), 1, rrd_file) != 1){
+               rrd_set_error("fwrite live_head to rrd");
+               free(updvals);
+               rrd_free(&rrd);
+               free(tmpl_idx);
+               free(pdp_temp);
+               free(pdp_new);
+               fclose(rrd_file);
+               return(-1);
+           }
+    }
+    else {
+           if(fwrite( &rrd.live_head->last_up,
+                      sizeof(time_t), 1, rrd_file) != 1){
+               rrd_set_error("fwrite live_head to rrd");
+               free(updvals);
+               rrd_free(&rrd);
+               free(tmpl_idx);
+               free(pdp_temp);
+               free(pdp_new);
+               fclose(rrd_file);
+               return(-1);
+           }
+    }
+           
+
+    if(fwrite( rrd.pdp_prep,
+              sizeof(pdp_prep_t),
+              rrd.stat_head->ds_cnt, rrd_file) != rrd.stat_head->ds_cnt){
+       rrd_set_error("ftwrite pdp_prep to rrd");
+       free(updvals);
+       rrd_free(&rrd);
+       free(tmpl_idx);
+       free(pdp_temp);
+       free(pdp_new);
+       fclose(rrd_file);
+       return(-1);
+    }
+
+    if(fwrite( rrd.cdp_prep,
+              sizeof(cdp_prep_t),
+              rrd.stat_head->rra_cnt *rrd.stat_head->ds_cnt, rrd_file) 
+       != rrd.stat_head->rra_cnt *rrd.stat_head->ds_cnt){
+
+       rrd_set_error("ftwrite cdp_prep to rrd");
+       free(updvals);
+       free(tmpl_idx);
+       rrd_free(&rrd);
+       free(pdp_temp);
+       free(pdp_new);
+       fclose(rrd_file);
+       return(-1);
+    }
+
+    if(fwrite( rrd.rra_ptr,
+              sizeof(rra_ptr_t), 
+              rrd.stat_head->rra_cnt,rrd_file) != rrd.stat_head->rra_cnt){
+       rrd_set_error("fwrite rra_ptr to rrd");
+       free(updvals);
+       free(tmpl_idx);
+       rrd_free(&rrd);
+       free(pdp_temp);
+       free(pdp_new);
+       fclose(rrd_file);
+       return(-1);
+    }
+    
+#ifdef HAVE_POSIX_FADVISExxx
+
+    /* with update we have write ops, so they will probably not be done by now, this means
+       the buffers will not get freed. But calling this for the whole file - header
+       will let the data off the hook as soon as it is written when if it is from a previous
+       update cycle. Calling fdsync to force things is much too hard here. */
+
+    if (0 != posix_fadvise(fileno(rrd_file), rra_begin, 0, POSIX_FADV_DONTNEED)) {
+         rrd_set_error("setting POSIX_FADV_DONTNEED on '%s': %s",filename, rrd_strerror(errno));
+         fclose(rrd_file);
+         return(-1);
+    } 
+#endif
+
+    /* OK now close the files and free the memory */
+    if(fclose(rrd_file) != 0){
+       rrd_set_error("closing rrd");
+       free(updvals);
+       free(tmpl_idx);
+       rrd_free(&rrd);
+       free(pdp_temp);
+       free(pdp_new);
+       return(-1);
+    }
+
+    /* 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 burning cycles. */
+       if (schedule_smooth)
+       {
+         rrd_file = fopen(filename,"rb+");
+          
+
+         rra_start = rra_begin;
+         for (i = 0; i < rrd.stat_head -> rra_cnt; ++i)
+         {
+           if (cf_conv(rrd.rra_def[i].cf_nam) == CF_DEVSEASONAL ||
+               cf_conv(rrd.rra_def[i].cf_nam) == CF_SEASONAL)
+           {
+#ifdef DEBUG
+             fprintf(stderr,"Running smoother for rra %ld\n",i);
+#endif
+             apply_smoother(&rrd,i,rra_start,rrd_file);
+             if (rrd_test_error())
+               break;
+           }
+           rra_start += rrd.rra_def[i].row_cnt
+             *rrd.stat_head->ds_cnt*sizeof(rrd_value_t);
+         }
+#ifdef HAVE_POSIX_FADVISExxx
+          /* same procedure as above ... */
+          if (0 != posix_fadvise(fileno(rrd_file), rra_begin, 0, POSIX_FADV_DONTNEED)) {
+             rrd_set_error("setting POSIX_FADV_DONTNEED on '%s': %s",filename, rrd_strerror(errno));
+             fclose(rrd_file);
+             return(-1);
+          } 
+#endif
+         fclose(rrd_file);
+       }
+    rrd_free(&rrd);
+    free(updvals);
+    free(tmpl_idx);
+    free(pdp_new);
+    free(pdp_temp);
+    return(0);
+}
+
+/*
+ * get exclusive lock to whole file.
+ * lock gets removed when we close the file
+ *
+ * returns 0 on success
+ */
+int
+LockRRD(FILE *rrdfile)
+{
+    int        rrd_fd;         /* File descriptor for RRD */
+    int        rcstat;
+
+    rrd_fd = fileno(rrdfile);
+
+       {
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+    struct _stat st;
+
+    if ( _fstat( rrd_fd, &st ) == 0 ) {
+           rcstat = _locking ( rrd_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_fd, F_SETLK, &lock);
+#endif
+       }
+
+    return(rcstat);
+}
+
+
+#ifdef HAVE_MMAP
+info_t
+*write_RRA_row (rrd_t *rrd, unsigned long rra_idx, unsigned long *rra_current,
+              unsigned short CDP_scratch_idx, 
+#ifndef DEBUG
+FILE UNUSED(*rrd_file),
+#else
+FILE *rrd_file,
+#endif
+                  info_t *pcdp_summary, time_t *rra_time, void *rrd_mmaped_file)
+#else
+info_t
+*write_RRA_row (rrd_t *rrd, unsigned long rra_idx, unsigned long *rra_current,
+              unsigned short CDP_scratch_idx, FILE *rrd_file,
+                  info_t *pcdp_summary, time_t *rra_time)
+#endif
+{
+   unsigned long ds_idx, cdp_idx;
+   infoval 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,ftell(rrd_file),
+            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 = info_push(pcdp_summary,
+                sprintf_alloc("[%d]RRA[%s][%lu]DS[%s]",
+                *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);
+         }
+#ifdef HAVE_MMAP
+         memcpy((char *)rrd_mmaped_file + *rra_current,
+                         &(rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val),
+                         sizeof(rrd_value_t));
+#else
+         if(fwrite(&(rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val),
+                sizeof(rrd_value_t),1,rrd_file) != 1)
+         { 
+            rrd_set_error("writing rrd");
+            return 0;
+         }
+#endif
+         *rra_current += sizeof(rrd_value_t);
+       }
+       return (pcdp_summary);
+}
diff --git a/program/src/rrd_version.c b/program/src/rrd_version.c
new file mode 100644 (file)
index 0000000..7d13aeb
--- /dev/null
@@ -0,0 +1,23 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * 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..ef964a0
--- /dev/null
@@ -0,0 +1,323 @@
+/****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * 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"
+
+#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;
+    struct rrd_time_value start_tv, end_tv;
+    char           *parsetime_error = NULL;
+    optind = 0; opterr = 0;  /* initialize getopt */
+
+    rrd_graph_init(&im);
+
+    parsetime("end-24h", &start_tv);
+    parsetime("now", &end_tv);
+
+    while (1){
+       static 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 ... */
+           {0,0,0,0}
+       };
+       int option_index = 0;
+       int opt;
+       
+       opt = getopt_long(argc, argv, "s:e:m:",
+                         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 = parsetime(optarg, &start_tv))) {
+               rrd_set_error( "start time: %s", parsetime_error );
+               return -1;
+           }
+           break;
+       case 'e':
+           if ((parsetime_error = 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 '?':
+            rrd_set_error("unknown option '%s'",argv[optind-1]);
+            return -1;
+       }
+    }
+
+    if (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 a graph without contents");
+       im_free(&im);
+       return(-1); 
+    }
+    
+    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  *ds_cnt;    /* number of data sources in file */
+    unsigned long  col, dst_row, row_cnt;
+    rrd_value_t    *srcptr, *dstptr;
+
+    unsigned long nof_xports = 0;
+    unsigned long xport_counter = 0;
+    unsigned long *ref_list;
+    rrd_value_t **srcptr_list;
+    char **legend_list;
+    int ii = 0;
+
+    time_t start_tmp = 0;
+    time_t end_tmp = 0;
+    unsigned long step_tmp = 1;
+
+    /* 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? */
+    for(i = 0; i < im->gdes_c; i++) {  
+       switch(im->gdes[i].gf) {
+       case GF_XPORT:
+         nof_xports++;
+         break;
+       default:
+         break;
+       }
+    }
+
+    if(nof_xports == 0) {
+      rrd_set_error("no XPORT found, nothing to do");
+      return -1;
+    }
+
+    /* a list of referenced gdes */
+    ref_list = malloc(sizeof(int) * nof_xports);
+    if(ref_list == NULL)
+      return -1;
+
+    /* a list to save pointers into each gdes data */
+    srcptr_list = malloc(sizeof(srcptr) * nof_xports);
+    if(srcptr_list == NULL) {
+      free(ref_list);
+      return -1;
+    }
+
+    /* a list to save pointers to the column's legend entry */
+    /* this is a return value! */
+    legend_list = malloc(sizeof(char *) * nof_xports);
+    if(legend_list == NULL) {
+      free(srcptr_list);
+      free(ref_list);
+      return -1;
+    }
+
+    /* find referenced gdes and save their index and */
+    /* a pointer into their data */
+    for(i = 0; i < im->gdes_c; i++) {  
+       switch(im->gdes[i].gf) {
+       case GF_XPORT:
+         ii = im->gdes[i].vidx;
+         if(xport_counter > nof_xports) {
+           rrd_set_error( "too many xports: should not happen. Hmmm");
+           free(srcptr_list);
+           free(ref_list);
+           free(legend_list);
+           return -1;
+         } 
+         srcptr_list[xport_counter] = im->gdes[ii].data;
+         ref_list[xport_counter++] = i;
+         break;
+       default:
+         break;
+       }
+    }
+
+    start_tmp = im->gdes[0].start;
+    end_tmp = im->gdes[0].end;
+    step_tmp = im->gdes[0].step;
+
+    /* fill some return values */
+    *col_cnt = nof_xports;
+    *start = start_tmp;
+    *end = end_tmp;
+    *step = step_tmp;
+
+    row_cnt = ((*end)-(*start))/(*step);
+
+    /* room for rearranged data */
+    /* this is a return value! */
+    if (((*data) = malloc((*col_cnt) * row_cnt * sizeof(rrd_value_t)))==NULL){
+        free(srcptr_list);
+        free(ref_list);
+       free(legend_list);
+       rrd_set_error("malloc xport data area");
+       return(-1);
+    }
+    dstptr = (*data);
+
+    j = 0;
+    for(i = 0; i < im->gdes_c; i++) {  
+       switch(im->gdes[i].gf) {
+       case GF_XPORT:
+         /* reserve room for one legend entry */
+         /* is FMT_LEG_LEN + 5 the correct size? */
+         if ((legend_list[j] = malloc(sizeof(char) * (FMT_LEG_LEN+5)))==NULL) {
+           free(srcptr_list);
+           free(ref_list);
+           free(*data);  *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;
+       }
+    }
+
+    /* fill data structure */
+    for(dst_row = 0; (int)dst_row < (int)row_cnt; dst_row++) {
+      for(i = 0; i < (int)nof_xports; i++) {
+        j = ref_list[i];
+       ii = im->gdes[j].vidx;
+       ds_cnt = &im->gdes[ii].ds_cnt;
+
+       srcptr = srcptr_list[i];
+       for(col = 0; col < (*ds_cnt); col++) {
+         rrd_value_t newval = DNAN;
+         newval = srcptr[col];
+
+         if (im->gdes[ii].ds_namv && im->gdes[ii].ds_nam) {
+           if(strcmp(im->gdes[ii].ds_namv[col],im->gdes[ii].ds_nam) == 0)
+             (*dstptr++) = newval;
+         } else {
+           (*dstptr++) = newval;
+         }
+
+       }
+       srcptr_list[i] += (*ds_cnt);
+      }
+    }
+
+    *legend_v = legend_list;
+    free(srcptr_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..f8dd93e
--- /dev/null
@@ -0,0 +1,34 @@
+/****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * 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..257f2c7
--- /dev/null
@@ -0,0 +1,35 @@
+/*****************************************************************************
+ * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * 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-2007\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..c57a726
--- /dev/null
@@ -0,0 +1,356 @@
+/**
+ *
+ * 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..c9d45e3
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+**  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..b5ac841
--- /dev/null
@@ -0,0 +1,11 @@
+/* define a macro to wrap variables in that would
+   otherwhise generate UNUSED variable warnings */
+
+#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..32f5337
--- /dev/null
@@ -0,0 +1,67 @@
+// 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/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..965d525
--- /dev/null
@@ -0,0 +1,60 @@
+/* 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_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.2015
+#define PACKAGE_NAME "rrdtool"
+#define PACKAGE_VERSION "1.2.15"
+#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 "arial.ttf"
+/* #define RRD_DEFAULT_FONT "DejaVuSansMono-Roman.ttf" */
+
+/* #define WITH_PIECHART 1 */
+
+/* #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..fbc0c1d
--- /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\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.vcproj b/program/win32/rrd.vcproj
new file mode 100644 (file)
index 0000000..30ca966
--- /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="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..8a83923
--- /dev/null
@@ -0,0 +1,65 @@
+/* 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
+\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/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..7ba111a
--- /dev/null
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+       ProjectType="Visual C++"
+       Version="7.10"
+       Name="rrdtool"
+       SccProjectName=""
+       SccLocalPath="">
+       <Platforms>
+               <Platform
+                       Name="Win32"/>
+       </Platforms>
+       <Configurations>
+               <Configuration
+                       Name="Release|Win32"
+                       OutputDirectory=".\toolrelease"
+                       IntermediateDirectory=".\toolrelease"
+                       ConfigurationType="1"
+                       UseOfMFC="0"
+                       ATLMinimizesCRunTimeLibraryUsage="FALSE"
+                       CharacterSet="2">
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               Optimization="4"
+                               AdditionalIncludeDirectories="\Program Files\GnuWin32\include,\Program Files\GnuWin32\include\freetype2"
+                               PreprocessorDefinitions="NDEBUG;_WINDOWS;WIN32;_CTYPE_DISABLE_MACROS"
+                               RuntimeLibrary="2"
+                               PrecompiledHeaderFile=".\toolrelease/rrdtool.pch"
+                               AssemblerListingLocation=".\toolrelease/"
+                               ObjectFile=".\toolrelease/"
+                               ProgramDataBaseFileName=".\toolrelease/"
+                               WarningLevel="3"
+                               SuppressStartupBanner="TRUE"
+                               CompileAs="0"/>
+                       <Tool
+                               Name="VCCustomBuildTool"/>
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="libpng.lib libz.lib libart_lgpl.lib libfreetype.lib"
+                               OutputFile=".\toolrelease/rrdtool.exe"
+                               LinkIncremental="1"
+                               SuppressStartupBanner="TRUE"
+                               AdditionalLibraryDirectories="\Program Files\GnuWin32\lib"
+                               GenerateDebugInformation="TRUE"
+                               ProgramDatabaseFile=".\toolrelease/rrdtool.pdb"
+                               SubSystem="1"
+                               TargetMachine="1"/>
+                       <Tool
+                               Name="VCMIDLTool"
+                               TypeLibraryName=".\toolrelease/rrdtool.tlb"
+                               HeaderFileName=""/>
+                       <Tool
+                               Name="VCPostBuildEventTool"/>
+                       <Tool
+                               Name="VCPreBuildEventTool"/>
+                       <Tool
+                               Name="VCPreLinkEventTool"/>
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                               PreprocessorDefinitions="NDEBUG"
+                               Culture="4108"/>
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"/>
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"/>
+                       <Tool
+                               Name="VCWebDeploymentTool"/>
+                       <Tool
+                               Name="VCManagedWrapperGeneratorTool"/>
+                       <Tool
+                               Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+               </Configuration>
+               <Configuration
+                       Name="Debug|Win32"
+                       OutputDirectory=".\tooldebug"
+                       IntermediateDirectory=".\tooldebug"
+                       ConfigurationType="1"
+                       UseOfMFC="0"
+                       ATLMinimizesCRunTimeLibraryUsage="FALSE"
+                       CharacterSet="2">
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               Optimization="0"
+                               AdditionalIncludeDirectories="\Program Files\GnuWin32\include\freetype2,\Program Files\GnuWin32\include"
+                               PreprocessorDefinitions="_DEBUG;_CONSOLE;WIN32;_CTYPE_DISABLE_MACROS"
+                               RuntimeLibrary="2"
+                               PrecompiledHeaderFile=".\tooldebug/rrdtool.pch"
+                               AssemblerListingLocation=".\tooldebug/"
+                               ObjectFile=".\tooldebug/"
+                               ProgramDataBaseFileName=".\tooldebug/"
+                               BrowseInformation="1"
+                               WarningLevel="3"
+                               SuppressStartupBanner="TRUE"
+                               DebugInformationFormat="4"
+                               CompileAs="0"/>
+                       <Tool
+                               Name="VCCustomBuildTool"/>
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="libpng.lib libz.lib libart_lgpl.lib libfreetype.lib"
+                               OutputFile=".\tooldebug/rrdtool.exe"
+                               LinkIncremental="1"
+                               SuppressStartupBanner="TRUE"
+                               AdditionalLibraryDirectories="\Program Files\GnuWin32\lib"
+                               GenerateDebugInformation="TRUE"
+                               ProgramDatabaseFile=".\tooldebug/rrdtool.pdb"
+                               SubSystem="1"
+                               TargetMachine="1"/>
+                       <Tool
+                               Name="VCMIDLTool"
+                               TypeLibraryName=".\tooldebug/rrdtool.tlb"
+                               HeaderFileName=""/>
+                       <Tool
+                               Name="VCPostBuildEventTool"/>
+                       <Tool
+                               Name="VCPreBuildEventTool"/>
+                       <Tool
+                               Name="VCPreLinkEventTool"/>
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                               PreprocessorDefinitions="_DEBUG"
+                               Culture="4108"/>
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"/>
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"/>
+                       <Tool
+                               Name="VCWebDeploymentTool"/>
+                       <Tool
+                               Name="VCManagedWrapperGeneratorTool"/>
+                       <Tool
+                               Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+               </Configuration>
+       </Configurations>
+       <References>
+       </References>
+       <Files>
+               <File
+                       RelativePath="rrd_tool.c">
+                       <FileConfiguration
+                               Name="Release|Win32">
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                                       AdditionalIncludeDirectories=""
+                                       PreprocessorDefinitions=""/>
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Debug|Win32">
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                                       Optimization="0"
+                                       AdditionalIncludeDirectories=""
+                                       PreprocessorDefinitions=""
+                                       BrowseInformation="1"/>
+                       </FileConfiguration>
+               </File>
+       </Files>
+       <Globals>
+       </Globals>
+</VisualStudioProject>