* The 1.0 release.
authormyon <myon@b513b33f-fedd-0310-b452-c3deb5f4c849>
Wed, 12 Mar 2008 21:44:00 +0000 (21:44 +0000)
committermyon <myon@b513b33f-fedd-0310-b452-c3deb5f4c849>
Wed, 12 Mar 2008 21:44:00 +0000 (21:44 +0000)
* sig2dot, springgraph: new scripts joining the family, no longer provided
  as separate packages. We shamelessly bump our version number to something
  greater, and build transitional packages.

git-svn-id: svn://svn.debian.org/pgp-tools/trunk@366 b513b33f-fedd-0310-b452-c3deb5f4c849

13 files changed:
README
debian/changelog
debian/control
debian/copyright
debian/rules
sig2dot/README.sig2dot [new file with mode: 0644]
sig2dot/changelog.sig2dot [new file with mode: 0644]
sig2dot/sig2dot [new file with mode: 0755]
sig2dot/sig2dot.1 [new file with mode: 0644]
springgraph/README.springgraph [new file with mode: 0644]
springgraph/changelog.springgraph [new file with mode: 0644]
springgraph/springgraph [new file with mode: 0755]
springgraph/springgraph.1 [new file with mode: 0644]

diff --git a/README b/README
index 21e97a44f7c68ad31ba35b4a6d7f3e2a8d1b620f..c85a245fa64cc3e2fe68adcd0a0e2e13d58dd675 100644 (file)
--- a/README
+++ b/README
@@ -10,6 +10,8 @@ This is a collection of several projects relating to OpenPGP.
 * gpgparticipants: create a list of key signing party participants,
   for use by the party organiser
 * keylookup: ncurses wrapper around gpg --search
 * gpgparticipants: create a list of key signing party participants,
   for use by the party organiser
 * keylookup: ncurses wrapper around gpg --search
+* sig2dot: converts a list of GnuPG signatures to a .dot file
+* springgraph: creates a graph from a .dot file
 
 For more information on each of these tools, please see their respective
 manpages. Please note that each individual project has its own license,
 
 For more information on each of these tools, please see their respective
 manpages. Please note that each individual project has its own license,
index 8b2c107f14749532eb395b85ae1e0cdfc2419773..d29e58ff48819f498ebc7fb5e47cb9d9a3b44088 100644 (file)
@@ -1,8 +1,13 @@
-signing-party (0.4.14-1) unreleased; urgency=low
+signing-party (1.0-1) unreleased; urgency=low
+
+  * The 1.0 release.
 
   [ Christoph Berg ]
   * gpglist: Do not barf on revokers (rvk). Thanks to Faidon Liambotis for
     spotting.
 
   [ Christoph Berg ]
   * gpglist: Do not barf on revokers (rvk). Thanks to Faidon Liambotis for
     spotting.
+  * sig2dot, springgraph: new scripts joining the family, no longer provided
+    as separate packages. We shamelessly bump our version number to something
+    greater, and build transitional packages.
 
   [ Thijs Kinkhorst ]
   * keylookup: cope with 16 or 40 nibble keyids, thanks Philippe Teuwen
 
   [ Thijs Kinkhorst ]
   * keylookup: cope with 16 or 40 nibble keyids, thanks Philippe Teuwen
@@ -15,7 +20,7 @@ signing-party (0.4.14-1) unreleased; urgency=low
   * caff: always update the user's key from their GnuPG home, to adequately
     cope with changed keys (Closes: #462897).
 
   * caff: always update the user's key from their GnuPG home, to adequately
     cope with changed keys (Closes: #462897).
 
- -- Thijs Kinkhorst <thijs@debian.org>  Wed, 05 Mar 2008 17:27:01 +0100
+ -- Christoph Berg <myon@debian.org>  Wed, 12 Mar 2008 22:34:59 +0100
 
 signing-party (0.4.13-1) unstable; urgency=low
 
 
 signing-party (0.4.13-1) unstable; urgency=low
 
index b0528f5518b6713df1575294148382951c5d332d..b94d56f9ff6a0db8b2cc16a4c4e03b745b9496ad 100644 (file)
@@ -12,13 +12,16 @@ Vcs-Browser: http://svn.debian.org/wsvn/pgp-tools
 Package: signing-party
 Architecture: all
 Depends: gnupg, libgnupg-interface-perl, libtext-template-perl, libmime-perl, libmailtools-perl
 Package: signing-party
 Architecture: all
 Depends: gnupg, libgnupg-interface-perl, libtext-template-perl, libmime-perl, libmailtools-perl
-Recommends: exim4 | mail-transport-agent, libtext-iconv-perl | libintl-perl | recode, dialog | whiptail, libpaper-utils
+Recommends: exim4 | mail-transport-agent, libtext-iconv-perl | libintl-perl | recode, dialog | whiptail, libpaper-utils,
+# springraph
+ libgd-gd2-noxpm-perl | libgd-gd2-perl
 Suggests: imagemagick | graphicsmagick-imagemagick-compat
 Suggests: imagemagick | graphicsmagick-imagemagick-compat
-Replaces: keylookup (<< 3.0)
-Conflicts: keylookup (<< 3.0)
+Replaces: keylookup (<< 3.0), sig2dot (<< 1), springgraph (<< 1)
+Conflicts: keylookup (<< 3.0), sig2dot (<< 1), springgraph (<< 1)
+Provides: sig2dot, springgraph
 Description: Various OpenPGP related tools
  signing-party is a collection for all kinds of PGP/GnuPG related things,
 Description: Various OpenPGP related tools
  signing-party is a collection for all kinds of PGP/GnuPG related things,
- including signing scripts, party preparation scripts, etc.
+ including tools for signing keys, keyring analysis, and party preparation.
  .
   * caff: CA - Fire and Forget signs and mails a key
   * pgp-clean: removes all non-self signatures from key
  .
   * caff: CA - Fire and Forget signs and mails a key
   * pgp-clean: removes all non-self signatures from key
@@ -29,4 +32,19 @@ Description: Various OpenPGP related tools
   * gpgsigs: annotates list of GnuPG keys with already done signatures
   * gpgparticipants: create list of party participants for the organiser
   * keylookup: ncurses wrapper around gpg --search
   * gpgsigs: annotates list of GnuPG keys with already done signatures
   * gpgparticipants: create list of party participants for the organiser
   * keylookup: ncurses wrapper around gpg --search
+  * sig2dot: converts a list of GnuPG signatures to a .dot file
+  * springgraph: creates a graph from a .dot file
 
 
+Package: sig2dot
+Architecture: all
+Depends: signing-party (= ${binary:Version})
+Description: transitional package to pull in signing-party
+ This is an empty package to pull in signing-party in which sig2dot is
+ now included. It can safely be removed.
+
+Package: springgraph
+Architecture: all
+Depends: signing-party (= ${binary:Version})
+Description: transitional package to pull in signing-party
+ This is an empty package to pull in signing-party in which springgraph is
+ now included. It can safely be removed.
index dd6b1bc84b33bf3ba48f30ae7ce5ca6a7c86aa74..74e825650d54f18150dcdabdbc03dd9647acea12 100644 (file)
@@ -10,10 +10,17 @@ Availability:
 Authors and Copyright:
   (c) 2000, 2002, 2004, 2005, 2006  Peter Palfrader <peter@palfrader.org>
   (c) 2004  Uli Martens <uli@youam.net>
 Authors and Copyright:
   (c) 2000, 2002, 2004, 2005, 2006  Peter Palfrader <peter@palfrader.org>
   (c) 2004  Uli Martens <uli@youam.net>
-  (c) 2004, 2005, 2006  Christoph Berg <cb@df7cb.de>
+  (c) 2004-2008  Christoph Berg <cb@df7cb.de>
   (c) 2001-2005  Simon Richter <sjr@debian.org>
   (c) 2005, 2006, 2007  Thijs Kinkhorst <thijs@debian.org>
   (c) 2000, 2002  Christian Kurz <shorty@debian.org>
   (c) 2001-2005  Simon Richter <sjr@debian.org>
   (c) 2005, 2006, 2007  Thijs Kinkhorst <thijs@debian.org>
   (c) 2000, 2002  Christian Kurz <shorty@debian.org>
+
+  sig2dot and springgraph were originally written by:
+  (c) 2002 Darxus@ChaosReigns.com
+  http://www.chaosreigns.com/code/sig2dot/
+  http://www.chaosreigns.com/code/springgraph/
+  Modifications by Kevin Rosenberg.
+
   Other contributors:
   Enrico Zini
   Benjamin Hill
   Other contributors:
   Enrico Zini
   Benjamin Hill
@@ -44,7 +51,8 @@ License for caff and gpgsigs:
   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-Licence for gpg-key2ps, gpg-mailkeys, gpgparticipants and keylookup:
+Licence for gpg-key2ps, gpg-mailkeys, gpgparticipants, keylookup, sig2dot,
+and springgraph:
   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
   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
index c8772619028dd44cb4c22cff360a55745823f03c..f962cc5d3c0e91c230179b503be49d3ffdf73e04 100755 (executable)
@@ -25,6 +25,7 @@ install: build
        dh_testroot
        dh_clean -k
        dh_installdirs
        dh_testroot
        dh_clean -k
        dh_installdirs
+       $(MAKE) install DESTDIR=$(CURDIR)/debian/signing-party
        dh_install
        dh_installman */*.1
        install -d $(CURDIR)/debian/signing-party/usr/share/doc/signing-party/caff
        dh_install
        dh_installman */*.1
        install -d $(CURDIR)/debian/signing-party/usr/share/doc/signing-party/caff
diff --git a/sig2dot/README.sig2dot b/sig2dot/README.sig2dot
new file mode 100644 (file)
index 0000000..2690910
--- /dev/null
@@ -0,0 +1,12 @@
+sig2dot
+-------
+
+Sig2dot parses the output of "gpg --list-sigs" into a format suitable
+for rendering into a graph by springgraph or graphviz.
+
+The syntax definition of the .dot files which sig2dot produces
+can be found in the graphviz man pages. A copy is here:
+ * http://www.graphviz.org/Documentation.php
+ * http://www.graphviz.org/cvs/doc/info/lang.html
+
+ -- Christoph Berg <cb@df7cb.de>  Sun,  6 Mar 2005 18:11:08 +0100
diff --git a/sig2dot/changelog.sig2dot b/sig2dot/changelog.sig2dot
new file mode 100644 (file)
index 0000000..b5d3f89
--- /dev/null
@@ -0,0 +1,169 @@
+sig2dot (0.37) unstable; urgency=low
+
+  * Add patch by Matteo Corti to make the graph title configurable.
+  * Fix targets in debian/rules.
+  * Bump Standards-Version.
+
+ -- Christoph Berg <myon@debian.org>  Wed, 31 May 2006 16:33:41 +0200
+
+sig2dot (0.36) unstable; urgency=low
+
+  * Patch by Thomas Huriaux: sig2dot must not include external signatures if
+    using a subset of your keyring (Closes: #302693).
+  * Patch by Eero Häkkinen: recognize trust signatures with a level >= 10
+    (Closes: #348783).
+
+ -- Christoph Berg <myon@debian.org>  Sat, 21 Jan 2006 10:08:40 +0100
+
+sig2dot (0.35) unstable; urgency=low
+
+  * Use strict and warnings.
+  * New -h/-q/-v options.
+  * Remove comment fields from UIDs (Closes: #318751).
+  * Support localized strings for revoked keys (Closes: #318740).
+  * Bump Standards-Version.
+
+ -- Christoph Berg <myon@debian.org>  Fri, 23 Sep 2005 13:27:33 +0200
+
+sig2dot (0.34-4) unstable; urgency=high
+
+  * Fix breakage with gnupg 1.4 (keys with trust signatures) noted by Matthew
+    Wilcox (Closes: #308271).
+
+ -- Christoph Berg <cb@df7cb.de>  Wed, 18 May 2005 23:48:47 +0200
+
+sig2dot (0.34-3) unstable; urgency=low
+
+  * New maintainer (Closes: #297446).
+  * Fixed to work with gpg 1.4 output, fixed some perl -w warnings.
+  * Updated pointer to dot syntax documentation.
+  * Fixed unescaped hyphens in sig2dot.1 and removed some whitespace.
+  * Removed redundant stuff in debian/rules and fixed non-functional dh_perl
+    call.
+  * Recommends: gnupg.
+
+ -- Christoph Berg <cb@df7cb.de>  Sun,  6 Mar 2005 20:18:41 +0100
+
+sig2dot (0.34-2) unstable; urgency=low
+
+  * Orphan as in bug #297446
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Sat,  5 Mar 2005 02:43:15 -0700
+
+sig2dot (0.34-1) unstable; urgency=low
+
+  * Incorporate patch from Thomas Huriaux
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Fri, 28 Jan 2005 20:00:15 -0700
+
+sig2dot (0.33-1) unstable; urgency=low
+
+  * Fix option -d processing (closes:285706)
+
+ -- root <kmr@debian.org>  Thu, 16 Dec 2004 05:44:21 -0700
+
+sig2dot (0.32-1) unstable; urgency=low
+
+  * Fix typo (closes:207025) 
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Sun, 24 Aug 2003 13:27:57 -0600
+
+sig2dot (0.31-1) unstable; urgency=low
+
+  * Avoid potential division by 0
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Wed, 20 Aug 2003 10:47:04 -0600
+
+sig2dot (0.30k-2) unstable; urgency=low
+
+  * Add -u parameter to man page.
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Thu, 31 Jul 2003 07:58:06 -0600
+
+sig2dot (0.30k-1) unstable; urgency=low
+
+  * Add localization patch by Marcus Frings (closes:202503)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Thu, 31 Jul 2003 00:55:01 -0600
+
+sig2dot (0.30j-1) unstable; urgency=low
+
+  * Revert patch (closes: 202484)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Tue, 22 Jul 2003 21:27:22 -0600
+
+sig2dot (0.30i-1) unstable; urgency=low
+
+  * Incorporate patch for renderdate option setting (closes: 201339)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Tue, 15 Jul 2003 10:24:28 -0600
+
+sig2dot (0.30h-1) unstable; urgency=low
+
+  * Incorporate patch for non-7bit names (closes:200329)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Mon,  7 Jul 2003 10:25:19 -0600
+
+sig2dot (0.30g-1) unstable; urgency=low
+
+  * Remove double quotes around names (closes:193123)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Fri, 16 May 2003 23:23:56 -0600
+
+sig2dot (0.30f-1) unstable; urgency=low
+
+  * Add -a option (closes:189417)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Sun, 11 May 2003 22:27:36 -0600
+
+sig2dot (0.30e-1) unstable; urgency=low
+
+  * Add N to regex (closes:188277)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Tue,  8 Apr 2003 19:34:50 -0600
+
+sig2dot (0.30d-1) unstable; urgency=low
+
+  * Improve comment (closes: 186608)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Fri, 28 Mar 2003 07:52:26 -0700
+
+sig2dot (0.30c-1) unstable; urgency=low
+
+  * Add another flag to regex (closes: 186138)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Mon, 24 Mar 2003 18:42:05 -0700
+
+sig2dot (0.30b-1) unstable; urgency=low
+
+  * Fix comment lines (closes: 183818)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Fri,  7 Mar 2003 19:42:39 -0700
+
+sig2dot (0.30a-1) unstable; urgency=low
+
+  * Fix manpage and copyright (closes: 183613) 
+  Thanks again for Marco Bodrato
+  * Update sig2dot comments with v0.30 information
+  
+ -- Kevin M. Rosenberg <kmr@debian.org>  Thu,  6 Mar 2003 07:01:09 -0700
+
+sig2dot (0.30-1) unstable; urgency=low
+
+  * Make regex more robust. Closes:#183496.
+  Thanks Marco Bodrato.
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Wed,  5 Mar 2003 11:11:09 -0700
+
+sig2dot (0.29-2) unstable; urgency=low
+
+  * Change architecture to all
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Fri, 21 Feb 2003 12:21:08 -0700
+
+sig2dot (0.29-1) unstable; urgency=low
+
+  * Initial Release.
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Tue, 18 Feb 2003 14:11:08 -0700
+
diff --git a/sig2dot/sig2dot b/sig2dot/sig2dot
new file mode 100755 (executable)
index 0000000..5d3fb3f
--- /dev/null
@@ -0,0 +1,433 @@
+#!/usr/bin/perl -w
+
+# sig2dot v0.29 (c) Darxus@ChaosReigns.com, released under the GPL
+# Download from: http://www.chaosreigns.com/code/sig2dot/
+# sig2dot v0.35-0.37 (c) 2005, 2006 Christoph Berg <cb@df7cb.de>
+# Download from: http://ftp.debian.org/debian/pool/main/s/sig2dot/
+
+# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+
+# Parses output of "gpg --list-sigs" into a format
+# suitable for rendering into a graph by graphviz 
+# (http://www.research.att.com/sw/tools/graphviz/) like so:
+#
+# $ gpg --list-sigs --keyring ./phillylinux.gpg | ./sig2dot.pl > phillylinux.dot
+# $ neato -Tps phillylinux.dot > phillylinux.ps
+# $ convert phillylinux.ps phillylinux.jpg
+#
+# Commandline options:
+#
+# -b
+#   Black and white / do not colorize.
+#
+# -d <date>
+#   Render graph as it appeared on <date> (ignores more recent
+#   signatures).  Date must be in the format "YYYY-MM-DD".
+#   Will also ignore keys that have since been revoked.
+#
+# -a
+#   Render all keys, even if they're not signed by any other key.
+#
+# -u <"string">
+#   Support localized output of GnuPG for unknown user IDs. For
+#   example, German users have to write (with sh quotation marks!)
+#   "[User-ID nicht gefunden]" if they use GnuPG with German
+#   messages. Default is "[User id not found]".
+#
+# -r <"string">
+#   Support localized output of GnuPG for revoked keys. For
+#   example, French users have to write "révoqué" if they use
+#   GnuPG with French messages. Default is "[revoked".
+#
+# -s stats.html
+#   Produces statistics file with number of signatures per node
+#
+# -t <"string">
+#   Graph title
+#
+# -h  print help
+# -v  print version
+# -q  be quiet
+#
+# Changes:
+#
+# v0.9 2000-09-14 19:20  strip trailing whitespace from $id more cleanly
+# v0.10 2000-09-14 19:33 skip revoked keys at the request of Peter Palfrader <ppalfrad@cosy.sbg.ac.at>
+# v0.11 Nov 22 21:38     use ID for node name instead of username for uniqueness
+# v0.12 Dec 15 16:20 use names instead of IDs again in stats.html
+# v0.13 Jun 19 03:15 red is proportional to signatures
+# v0.14 Jun 19 03:25 blue is proportional to other keys signed
+# v0.15 Jun 20 17:16 fixed blue, green is proportional to ratio
+# v0.16 Jun 20 18:55 uniqed %signedby
+# v0.17 Jan 10 19:10 Use overlap=scale instead of fixed edge lengths.  Requires new version of graphviz.
+# v0.18 Jan 23 11:53 stats.html is now valid html (v.01 transitional)
+# v0.23 May  3 18:52 bunch of new stuff, including -b flag (black & white), and fixes devision by zero error
+# v0.24 May  3 18:59 add black outline to nodes, prettier (changed node attribute "color" to "fillcolor")
+# v0.25 May  3 19:06 cleaned up anti- devision by zero code a little
+# v0.26 May  4 00:08 strip all non-digit characters from $renderdate
+# v0.27 May 10 00:23:49 2002 use {}'s to write 1 line per public key instead of one line per signature (much shorter)
+# v0.28 Feb 13 2003 Change regex to handle option trust digit 
+#                   <kevin@rosenberg.net>
+# v0.29 Feb 18 2003 Add -s option to optionally produce statistics file 
+#                   <kevin@rosenberg.net>
+# v0.30 Feb 18 2003 Make --list-sigs regex more robust 
+#                   Marco Bodrato <bodrato@gulp.linux.it>
+# v0.31 Jul 28 2003 Add -u option for localized output of GnuPG
+#                   Marcus Frings <protagonist@gmx.net>
+# further changes are documented in debian/changelog
+
+use strict;
+
+my $version = "0.37";
+
+my $chartchar = "*";
+my $renderdate = "";
+my ($stats, $color, $all, $not_found, $revokestr, $title);
+
+use Getopt::Std;
+my %opt;
+getopts('d:u:r:s:bahqvt:', \%opt);
+
+sub version {
+  print <<EOT;
+sig2dot $version
+Copyright (c) 2002 Darxus\@ChaosReigns.com
+Copyright (c) 2005, 2006 Christoph Berg <cb\@df7cb.de>
+EOT
+}
+
+if ($opt{h}) {
+  version();
+  print <<EOT;
+gpg --list-sigs | $0 [-abdhqsuv] > sigs.dot
+-a              Graph all keys, even if they do not have a signature
+-b              Black and white / do not colorize.
+-d YYYY-MM-DD   Render graph as it appeared on date.
+-h              Print this help and exit.
+-q              Be quiet.
+-r sting        key-is-revoked string (default: "[revoked").
+-s stats.html   Produces statistics file with number of signatures per node.
+-t title        Graph title
+-u string       user-id-not-found string (default: "[user id not found]").
+-v              Print version and exit.
+EOT
+  exit 0;
+}
+if ($opt{v}) {
+  version();
+  exit 0;
+}
+
+if ($opt{d}) { 
+  $renderdate = $opt{d}; 
+  print STDERR "Printing from date: $renderdate.\n";
+  $renderdate =~ s/\D+//g;
+}
+if ($opt{s}) { 
+  $stats = $opt{s}; 
+  print STDERR "Print statistics to $stats.\n";
+}
+if ($opt{b}) 
+{ 
+  $color = 0; 
+  print STDERR "Black and White.\n" unless $opt{q};
+} else { 
+  $color = 1; 
+  print STDERR "Color.\n" unless $opt{q};
+}
+if ($opt{a}) {
+  $all = 1;
+} else {
+  $all = 0;
+}
+
+if ($opt{u}) {
+  $not_found = lc $opt{u};
+} else {
+  $not_found = "[user id not found]"; # this changed from gpg 1.2 -> 1.4
+}
+
+if ($opt{r}) {
+  $revokestr = lc $opt{r};
+} else {
+  $revokestr = "[revoked"; # this changed from gpg 1.2 -> 1.4
+}
+
+if ($opt{t}) {
+  $title = lc $opt{t};
+} else {
+  $title = "Keyring Statistics";
+}
+
+my ($owner, %name, %revlist, %sigstmp, %signedbytmp, %idlist, @names, %revs);
+
+while (my $line = <>)
+{
+  chomp $line;
+
+# gpg 1.2
+#pub  1024D/807CAC25 2003-08-01 Michael Ablassmeier (abi) <abi#grinser.de>
+#sig         B3B2A12C 2004-01-28   [User id not found]
+#sig 3       9456ADE2 2004-02-07   Michael Schiansky <michael#schiansky.de>
+# gpg 1.4:
+#pub   1024D/807CAC25 2003-08-01
+#uid                  Michael Ablassmeier (abi) <abi#grinser.de>
+#sig          B3B2A12C 2004-01-28  [User ID not found]
+#sig 3        9456ADE2 2004-02-07  Michael Schiansky <michael#schiansky.de>
+
+                 # type                          id       date       name
+   if ($line =~ m#([\w]+)[ !\?][ \dLNPRTX]{0,8} +([^ ]+) +([^ ]+)(?: +"?([^<"]*))?#)
+# differences:
+# " " -> "[ !\?]" (to use 'gpg --check-sigs|sig2dot.mio|springgraph|display')
+# "[ \d]" -> "[ \dLRXP]" (signature attributes)
+# "[^<]+" -> "[^<]*" (to recognise "pub" lines whitout a name)
+#  if ($line =~ m#([\w]+) [ \d]? +([^ ]+) +([^ ]+) +([^<]+)#)
+#  if ($line =~ m#([\w]+) +([^ ]+) +([^ ]+) +([^<]+)#)
+
+  {
+    my $type = $1;
+    my $id = $2;
+    my $date = $3;
+    my $name = $4 || "";
+
+    $date =~ tr/-//d;
+    if ($type eq "pub" or $renderdate eq "" or $date <= $renderdate)
+    {
+      print STDERR "Using: $line\n" unless $opt{q};
+      # strip trailing whitespace more cleanly:
+      $name =~ s/\s+$//g;
+
+      #Remove re: http://bugs.debian.org/202484
+      #$name =~ s/[^a-zA-Z \.0-9]/_/g; # handle non-7bit names
+
+      if ($type eq "pub")
+      {
+        $id = (split('/',$id))[1];
+        $owner = $id;
+        $idlist{$id} = 1 if (index($name, $revokestr) < 0);
+      }
+
+      # remove comment field
+      $name{$id} = (split ' \(', $name)[0] if $name; # gpg 1.4 fixup
+
+      # skip revoked keys 
+      if (index($name, $revokestr) >= 0) {
+        $revlist{$id} = 1;
+        next;
+      }
+
+      if ($type eq "uid") {
+       $name{$owner} = $id; # gpg 1.4 fixup
+      }
+  
+#      unless (defined @{$sigs{$owner}})
+#      {
+#        @{$sigs{$owner}} = ();
+#      }
+      if ($type eq "sig" and lc $name ne $not_found)
+      {
+       if ($id ne $owner) {
+         push (@{$sigstmp{$owner}},$id);
+         push (@{$signedbytmp{$id}},$owner);
+       }
+       if ($all or $id ne $owner) {
+         push (@names,$id,$owner);
+       }
+      }
+      if ($type eq "rev" and lc $name ne $not_found)
+      {
+       if ($id ne $owner) {
+         push (@{$revs{$owner}},$id);
+         #push (@{$revokedby{$id}},$owner);
+       }
+      }
+    } else {
+      print STDERR "Skipping due to date: $line\n";
+    }
+  } else {
+    print STDERR "Skipping due to regex: $line\n" if $line ne "";
+  }
+}
+
+my (%sigs, %signedby);
+
+for my $id (sort {$sigstmp{$a} <=> $sigstmp{$b}} keys %sigstmp) {
+  next if (defined $revlist{$id});
+  foreach my $owner (@{$signedbytmp{$id}}) {
+    next if (defined $revlist{$owner});
+    my $revoke = 0;
+    foreach my $revid (@{$revs{$owner}}) {
+      if ($revid eq $id) {
+        $revoke = 1;
+      }
+    }
+    #$res = $revlist{$id};
+    if (($revoke == 0)) {
+      push (@{$sigs{$owner}},$id);
+      push (@{$signedby{$id}},$owner);
+    }
+  }
+}
+
+print "digraph \"$title\" {\noverlap=scale\nsplines=true\nsep=.1\n";
+
+my %saw;
+@saw{@names} = ();
+@names = keys %saw;
+undef %saw;
+
+my $maxsigcount = 0;
+my (%sigcount);
+
+for my $owner (sort {$sigs{$a} <=> $sigs{$b}} keys %sigs)
+{
+  undef %saw;
+  @saw{@{$sigs{$owner}}} = ();
+  @{$sigs{$owner}} = keys %saw;
+  undef %saw;
+  undef %saw;
+  $signedby{$owner} ||= [];
+  @saw{@{$signedby{$owner}}} = ();
+  @{$signedby{$owner}} = keys %saw;
+  undef %saw;
+
+  $sigcount{$owner} = scalar(@{$sigs{$owner}});
+  if ($sigcount{$owner} > $maxsigcount)
+  {
+    $maxsigcount = $sigcount{$owner};
+  }
+}
+
+my %signedbycount;
+my ($maxsignedbycount, $maxratio) = (0, 0);
+
+for my $owner (sort {$signedby{$a} <=> $signedby{$b}} keys %signedby)
+{
+  $signedbycount{$owner} = scalar(@{$signedby{$owner}});
+  if ($signedbycount{$owner} > $maxsignedbycount)
+  {
+    $maxsignedbycount = $signedbycount{$owner};
+  }
+  if ($sigcount{$owner} and $sigcount{$owner} > 0) {
+    if ($signedbycount{$owner} / $sigcount{$owner} > $maxratio)
+    {
+      $maxratio = $signedbycount{$owner} / $sigcount{$owner};
+    }
+  }
+}
+print "//$maxratio\n";
+
+if ($stats) {
+    open (STATS,">$stats");
+    print STATS "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<html><head><title>$title</title></head><body><table border=1>\n";
+
+    for my $owner (sort {$sigcount{$b} <=> $sigcount{$a}} keys %sigs)
+    {
+       print STATS "<tr><td>$name{$owner}<td>$sigcount{$owner}<td><img src=\"/images/pipe0.jpg\" height=15 width=",$sigcount{$owner} * 20," alt=\"". $chartchar x $sigcount{$owner} ."\">\n";
+    }
+    
+    print STATS "</table></body></html>\n";
+    close STATS;
+}
+
+print "node [style=filled]\n";
+for my $id (@names)
+{
+  if ((not exists $sigcount{$id}) and (not exists $signedbycount{$id}) and not $all) {
+    next;
+  }
+  next unless (defined $idlist{$id});
+  if ($color)
+  {
+    my ($red, $green, $blue) = (0, 1/3, 1/3);
+    if ($sigcount{$id}) {
+      $red = $sigcount{$id} / $maxsigcount;
+    }
+    if ($sigcount{$id} && $maxratio != 0)
+    {
+      $green = ($signedbycount{$id} / $sigcount{$id} / $maxratio * .75) * 2/3 + 1/3;
+    }
+    if ($signedbycount{$id} and $maxsignedbycount != 0) {
+      $blue = ($signedbycount{$id} / $maxsignedbycount) * 2/3 + 1/3;
+    }
+
+    my ($hue,$saturation,$value) = rgb2hsv($red,$green,$blue);
+    printf "//%d %d $red,$green,$blue\n", $sigcount{$id} || 0, $signedbycount{$id} || 0;
+    print "\"$id\" [fillcolor=\"$hue,$saturation,$value\",label=\"$name{$id}\"]\n";
+  } else {
+    print "\"$id\" [label=\"$name{$id}\"]\n";
+  }
+}
+#print "node [style=solid]\n";
+
+for my $owner (sort keys %sigs)
+{
+ print "{ ";
+  for my $id (@{$sigs{$owner}})
+  {
+    print "\"$id\" ";
+  }
+  print "} -> \"$owner\"\n";
+}
+
+print "}\n";
+
+#  Converts rgb to hsv.  All numbers are within range 0 to 1
+#  from http://twiki.org/cgi-bin/view/Codev/WebMap
+sub rgb2hsv {
+    my ($r, $g ,$b) = @_;
+    my $max = maxof($r, maxof($g, $b));
+    my $min = minof($r, minof($g, $b));
+    my $v = $max;
+    my ($s, $h);
+
+    if ($max > 0.0) {
+        $s = ($max - $min) / $max;
+    } else {
+        $s = 0;
+    }
+    if ($s > 0.0) {
+        my ($rc, $gc, $bc, $diff);
+            $diff = $max - $min;
+        $rc = ($max - $r) / $diff;
+        $gc = ($max - $g) / $diff;
+        $bc = ($max - $b) / $diff;
+        if ($r == $max) {
+            $h = ($bc - $gc) / 6.0;
+        } elsif ($g == $max) {
+            $h = (2.0 + $rc - $bc) / 6.0;
+        } else {
+            $h = (4.0 + $gc - $rc) / 6.0;
+        }
+    } else {
+       $h = 0.0;
+    }
+    if ($h < 0.0) {
+       $h += 1.0;
+    }
+    return ($h, $s, $v);
+}
+sub maxof {
+   my ($a, $b) = @_;
+
+   return $a>$b?$a:$b;
+}
+sub minof {
+   my ($a, $b) = @_;
+
+   return $a<$b?$a:$b;
+}
+
+# vim:sw=2:
diff --git a/sig2dot/sig2dot.1 b/sig2dot/sig2dot.1
new file mode 100644 (file)
index 0000000..45547c8
--- /dev/null
@@ -0,0 +1,80 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.\" First parameter, NAME, should be all caps
+.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
+.\" other parameters are allowed: see man(7), man(1)
+.TH SIG2DOT 1 "May 31, 2006"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh        disable hyphenation
+.\" .hy        enable hyphenation
+.\" .ad l      left justify
+.\" .ad b      justify to both left and right margins
+.\" .nf        disable filling
+.\" .fi        enable filling
+.\" .br        insert line break
+.\" .sp <n>    insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+sig2dot \- creates a .dot file from a GPG signature listing
+.SH SYNOPSIS
+.B sig2dot [OPTIONS] < input-file > output-file
+.br
+.SH DESCRIPTION
+This manual page documents briefly the
+.B sig2dot
+command.
+Sig2dot parses the output of "gpg \-\-list\-sigs" and produces a .dot file
+that can be used as input to a graphing program.
+.SH OPTIONS
+\fIsig2dot\fP
+accepts the following options:
+.TP 16
+.B \-b
+Black and white / do not colorize.
+.TP 16
+.B \-d <date>
+Render graph as it appeared on <date> (ignores more recent
+signatures).  Date must be in the format "YYYY\-MM\-DD".
+Will also ignore keys that have since been revoked.
+.TP 16
+.B \-s <html-file-name>
+Produces a sory HTML statistics file with the number of signatures per node.
+.TP 16
+.B \-a
+Graph all keys, even if they do not have a signature
+.TP 16
+.B \-r "<key-revoked-string>"
+Sets the text for the key-revoked string used by GnuPG. The default is 
+what is used in the English version. This parameter can be set for versions 
+of GnuPG localized to other languages. Default: "[revoked".
+.TP 16
+.TP 16
+.B \-t "<title>"
+Sets the title for the graph generated. Default: "Keyring Statistics".
+.TP 16
+.B \-u "<user-not-found-string>"
+Sets the text for the user-not-found string used by GnuPG. The default is 
+what is used in the English version. This parameter can be set for versions 
+of GnuPG localized to other languages. Default: "[user id not found]".
+.TP 16
+.B \-q
+Be quiet.
+.TP 16
+.B \-h
+Print help and exit.
+.TP 16
+.B \-v
+Print version and exit.
+.SH EXAMPLE
+gpg \-\-list\-sigs | sig2dot > sigs.dot
+.sp 0
+springgraph < sigs.dot > sigs.png
+.SH SEE ALSO
+.B springgraph(1)
+.B gnupg(1)
+.SH AUTHORS
+Darxus@ChaosReigns.com with modifications by Kevin Rosenberg.
+Versions 0.35 and later are by Christoph Berg <cb@df7cb.de>.
+This manual page was written by Kevin M. Rosenberg <kmr@debian.org>,
+for the Debian GNU/Linux system (but may be used by others).
diff --git a/springgraph/README.springgraph b/springgraph/README.springgraph
new file mode 100644 (file)
index 0000000..6510ff4
--- /dev/null
@@ -0,0 +1,26 @@
+springgraph
+-----------
+
+Springgraph will read in a .dot file description of a graph, which,
+for each node, specifies its name and which other nodes it is
+connected to, and then renders a graph. Each node is drawn as an
+ellipse, and each connection is drawn as an arrow. The node placement
+is a result of all of the nodes moving away from each other, while
+all nodes which are connected move toward each other. This movement
+is repeated until it stabilizes.
+
+Springgraph was written as an alternative to neato, which is part of
+graphviz. It attempts to read the same .dot files used by graphviz,
+but currently only supports a limited number of node attributes
+(label and fillcolor).
+
+Definition of the .dot files which springgraph renders can be found
+in the graphviz man pages. A copy is here:
+ * http://www.graphviz.org/Documentation.php
+ * http://www.graphviz.org/cvs/doc/info/lang.html
+
+Springgraph only supports the fillcolor and label node attributes, and can only
+handle two nodes per edge definition ("node1 -> node2", not "node1 -> node2 ->
+node3").
+
+ -- Christoph Berg <cb@df7cb.de>  Sun,  6 Mar 2005 17:44:47 +0100
diff --git a/springgraph/changelog.springgraph b/springgraph/changelog.springgraph
new file mode 100644 (file)
index 0000000..2f882a2
--- /dev/null
@@ -0,0 +1,94 @@
+springgraph (0.82-7) UNRELEASED; urgency=low
+
+  * NOT RELEASED YET
+
+ -- Christoph Berg <myon@debian.org>  Sat, 28 Oct 2006 13:46:46 +0200
+
+springgraph (0.82-6) unstable; urgency=low
+
+  * Add missing targets in debian/rules (Closes: #395791).
+  * Build package in binary-indep.
+  * Use dh_perl.
+
+ -- Christoph Berg <myon@debian.org>  Sat, 28 Oct 2006 13:32:54 +0200
+
+springgraph (0.82-5) unstable; urgency=low
+
+  * Fix typo in manpage (Closes: #310340, thanks to A Costa for the patch).
+  * Add syntax example to manpage.
+  * Use ${perl:Depends}.
+  * Bump Standards-Version.
+
+ -- Christoph Berg <myon@debian.org>  Mon,  5 Sep 2005 14:01:12 +0200
+
+springgraph (0.82-4) unstable; urgency=low
+
+  * New maintainer (Closes: #297447).
+  * Removed redundant parts of debian/rules.
+  * Updated pointer to dot language documentation (Closes: #295700).
+
+ -- Christoph Berg <cb@df7cb.de>  Sun,  6 Mar 2005 16:37:47 +0100
+
+springgraph (0.82-3) unstable; urgency=low
+
+  * Orphan as in bug #297447
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Sat,  5 Mar 2005 02:44:42 -0700
+
+springgraph (0.82-2) unstable; urgency=low
+
+  * Add more documentation (closes:267495)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Mon, 23 Aug 2004 14:45:18 -0600
+
+springgraph (0.82-1) unstable; urgency=low
+
+  * Accept patch from Tobias Gruetzma for more layout options
+    (closes:234117)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Tue, 24 Feb 2004 08:53:45 -0700
+
+springgraph (0.81-1) unstable; urgency=low
+
+  * Caption stderr progress notes (closes:206147)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Wed, 20 Aug 2003 10:37:21 -0600
+
+springgraph (0.80.1-1) unstable; urgency=low
+
+  * Allow use of libgd-gd2-noxpm-perl (closes:188856)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Mon, 14 Apr 2003 08:15:30 -0600
+
+springgraph (0.80-1) unstable; urgency=low
+
+  * Apply speed-up patch from Marco Bodrato  (closes: 185532)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Wed, 19 Mar 2003 21:06:15 -0700
+
+springgraph (0.79-4) unstable; urgency=low
+
+  * Use libgd-gd2-perl rather than libgd-gd1-perl (closes:183919)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Sat,  8 Mar 2003 05:20:43 -0700
+
+springgraph (0.79-3) unstable; urgency=low
+
+  * Change architecture to all
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Fri, 21 Feb 2003 12:19:36 -0700
+
+springgraph (0.79-2) unstable; urgency=low
+
+  * Added perl dependency
+  * Move to extra priority.
+  * Use dh_perl and ${perl:depend}
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Tue, 18 Feb 2003 19:21:04 -0700
+
+springgraph (0.79-1) unstable; urgency=low
+
+  * Initial Release.
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Tue, 18 Feb 2003 14:11:08 -0700
+
diff --git a/springgraph/springgraph b/springgraph/springgraph
new file mode 100755 (executable)
index 0000000..4a17988
--- /dev/null
@@ -0,0 +1,900 @@
+#!/usr/bin/perl -w
+
+# springgraph v0.79, (c) 2002 Darxus@ChaosReigns.com, released under the GPL
+# Download current version from:  http://www.chaosreigns.com/code/springgraph/
+#
+# This program attempts to render .dot files in a fashion similar to neato,
+# which is part of graphviz:  http://www.research.att.com/sw/tools/graphviz/.
+# I have never looked at any of the code in graphviz.
+#
+# Example usage:
+#
+# cat test.dot | ./springgraph.pl -s 3 > springgraph.png
+#
+# The "-s 3" specifies the scale, and is optional.  All of the node
+# locations are multiplied by this.  Increase the scale to eliminate
+# node overlaps.  Decrease the scale to make the graph smaller.
+#
+# Requirements:  GD.pm (http://www.perl.com/CPAN/authors/id/L/LD/LDS/)
+# 
+# Definition of the .dot files which springgraph renders
+# can be found in the graphviz man pages.  A copy is here:
+# http://www.unisa.edu.au/eie/csec/graphviz/dot.1.html.  Springgraph only
+# supports the fillcolor and label node attributes, and can only handle
+# two nodes per edge definition ("node1 -> node2", not "node1 -> node2
+# -> node3").
+# 
+# Springgraph fully supports the .dot files generated by sig2dot
+# (http://www.chaosreigns.com/code/sig2dot), which generates .dot files
+# from GPG/PGP signature relationships.
+# 
+# Thanks to the following for help with the math for the arrowheads:
+# Mike Joseph <mj@doze.net>
+# Walt Mankowski <waltman@pobox.com>
+# Jeff Weisberg <jaw+plug@tcp4me.com>
+#
+# Yes, the placement of the freaking arrowheads was by far the hardest
+# part of writing this program.
+#
+# Thanks to Hartmut Palm for cylinder translation/rotation code in
+# VRML.pm:  http://dc.gfz-potsdam.de/~palm/vrmlperl/
+
+# v0.26 May 06 16:12:30 2002 
+# v0.27 May 06 18:15:38 2002 cleanup
+# v0.44 May 06 23:56:45 2002 
+# v0.56 May 07 05:10:02 2002
+# v0.60 May 07 23:27:29 2002 arrow heads !! (not filled in due to segfault)
+# v0.61 May 07          2002 handle absence of beginning double-quote in fillcolor attribute
+# v0.62 May 08 19:44:04 2002 use getopts to get scale argument
+# v0.63 May 08 21:29:48 2002 made fillcolor optional again
+# v0.64 May 08 22:28:40 2002 render http://www.research.att.com/sw/tools/graphviz/examples/undirected/ER.dot.txt
+#                            and http://www.research.att.com/sw/tools/graphviz/examples/undirected/process.dot.txt
+#                            (added support for undirected graphs ("--" links)
+# v0.65 May 08 22:44:00 2002 render http://www.research.att.com/sw/tools/graphviz/examples/directed/fsm.dot.txt
+#                            (do not attempt to draw a line from a node to itself and cause a devision by zero)
+# v0.67 May 09 05:53:16 2002 support multiple nodes on one link line, adjusted detection of completion
+#                            render http://www.research.att.com/sw/tools/graphviz/examples/directed/unix.dot.txt
+#                            (support node names containing spaces)
+# v0.68 May 09 17:29:06 2002 cleaned up link line processing a bit (removed extraneous define checks)
+# v0.69 May 09 18:23:19 2002 render http://www.research.att.com/sw/tools/graphviz/examples/undirected/inet.dot.txt
+#                            (support {} lists in link (edge) lines)
+# v0.70 May 10 00:39:20 2002 Strip double-quotes that were getting missed to support sig2dot v0.27.
+# v0.71 May 11 20:06:17 2002 don't draw twice, added some 3D math (but not output yet)
+# v0.72 May 11 21:31:20 2002 3D output !!! (via -p flag)
+# v0.73 May 11 22:34:23 2002 added labels to 3D output
+# v0.74 May 12 02:07:29 2002 output 3D output suitable for animation
+# v0.75 May 13 01:45:41 2002 beginnings of vrml output (-v) - colored spheres
+# v0.76 May 13 04:30:13 2002 added connections between nodes to vrml
+#                            output, thanks cylinder translation/rotation
+#                            code from VRML.pm by Hartmut Palm:
+#                            http://dc.gfz-potsdam.de/~palm/vrmlperl/
+# v0.77 May 13 04:41:53 2002 made colors optional in pov and vrml output
+# v0.78 May 13 06:31:34 2002 removed extra cylinders from vrml output
+# v0.79 May 13 07:20:23 2002 made 2d output background transparent
+# v0.80 Mar 19 2003 optimization patch from Marco Bodrato
+# v0.81 Aug 20 2003 Caption stderr progress notes
+
+use GD;
+use Getopt::Std;
+use strict;
+use vars qw(
+$push
+$pull
+%node
+$im
+$source
+$dest
+$nodenum
+$blue
+$black
+$opt_b
+$bgcol
+@bgcolor
+$dist
+$iter
+$maxiter
+$percent
+$xdist
+$ydist
+$newdist2
+$xmove
+$ymove
+$movecount
+$rate
+$nodes
+%link
+$continue
+$done
+$line
+@nodelist
+%saw
+$name
+$label
+$margin
+$minx
+$miny
+$maxx
+$maxy
+$scale
+$nodesize
+$powderblue
+$linecol
+$h
+$s
+$v
+$r
+$g
+$b
+$color
+$maxxlength
+$minxlength
+$pi
+$twopi
+$angle
+@point
+$width
+$height
+$arrowlength
+$arrowwidth
+$num
+$opt_s
+$edge
+@parts
+$part
+@sources
+@dests
+$sourcesstring
+$destsstring
+$pov
+$opt_p
+$zdist
+$zmove
+$pov_or_vrml
+$opt_v
+$vrml
+$opt_t
+$trans
+$opt_h
+$opt_l
+@linecolor
+);
+
+$push = 2000;
+$pull = .1;
+$maxiter = 400;
+$rate = 2;
+$nodes = 5;
+#$done = 0.1;
+$done = 0.3;
+#$done = 3;
+$margin = 20;
+#$nodesize = 80;
+$nodesize = 40;
+$arrowlength = 10; # pixels
+$arrowwidth = 10; 
+
+srand 1; #comment out this line to generate graphs differently every time
+
+$pi = 3.141592653589793238462643383279502884197169399375105; # from memory
+$twopi = $pi * 2;
+
+getopts('s:pvhtb:l:');
+
+# -s: set scale
+if ($opt_s)
+{
+  $scale = $opt_s;
+} else {
+  $scale = 1;
+}
+
+# -p: Output as Pov-Ray
+if ($opt_p)
+{
+  $pov = 1;
+} else {
+  $pov = 0;
+}
+
+# -v: Output as VRML
+if ($opt_v)
+{
+  $vrml = 1;
+} else {
+  $vrml = 0;
+}
+
+# -h: Show some help
+if ($opt_h) {
+  usage();
+  exit 1;
+}
+
+# -t: Make background transparent
+if ($opt_t)
+{
+  $trans = 1;
+} else {
+  $trans = 0;
+}
+
+# -b: Set background color
+if ($opt_b)
+{
+  $trans = 0;
+  $opt_b =~ m/^(..)(..)(..)$/ or die "Invalid color: $opt_b";
+  @bgcolor = (hex($1), hex($2), hex($3));
+} else {
+  @bgcolor = (255, 255, 255);
+}
+
+# -l: Set line color
+if ($opt_l)
+{
+  $trans = 0;
+  $opt_l =~ m/^(..)(..)(..)$/ or die "Invalid color: $opt_l";
+  @linecolor = (hex($1), hex($2), hex($3));
+} else {
+  @linecolor = (169, 169, 169);
+}
+
+$done = $done / $scale;
+
+while ($line = <STDIN>)
+{
+  undef $name;
+  next if ($line =~ m#^//#);
+  chomp $line;
+  # 2 = arro1, 1 = no arrow
+  if ($line =~ m#^(.*-[>-][^\[]*)#)
+  {
+    $edge = $1;
+    @parts = split(/(-[->])/,$edge);
+    for $part (0 .. $#parts)
+    {
+      if (defined $parts[$part+2] and $parts[$part] ne '->' and $parts[$part] ne '--')
+      {
+        #print ":$parts[$part]:".$parts[$part+1].":".$parts[$part+2].":\n";
+        undef @sources;
+        undef @dests;
+        $parts[$part] =~ s/^\s*"?//;
+        $parts[$part] =~ s/"?\s*$//;
+        $parts[$part+2] =~ s/^\s*"?//;
+        $parts[$part+2] =~ s/"?\s*;?\s*$//;
+        if ($parts[$part] =~ m#^{(.*)}$#)
+        {
+          $sourcesstring = $1;
+          #print STDERR "sourcesstring:$sourcesstring:\n";
+          @sources = split(/[\s*;?\s*]/,$sourcesstring);
+        } else {
+          $sources[0] = $parts[$part];
+        }
+        if ($parts[$part+2] =~ m#^{(.*)}$#)
+        {
+          $destsstring = $1;
+          #print STDERR "destsstring:$destsstring:\n";
+          @dests = split(/[\s*;?\s*]/,$destsstring);
+        } else {
+          $dests[0] = $parts[$part+2];
+        }
+        for $source (@sources)
+        {
+          next if ($source eq "");
+          for $dest (@dests)
+          {
+            next if ($dest eq "");
+            $source =~ s/^\s*"?//;
+            $source =~ s/"?\s*$//;
+            $dest =~ s/^\s*"?//;
+            $dest =~ s/"?\s*;?\s*$//;
+            $link{$source}{$dest} = 2 if ($parts[$part+1] eq '->');
+            $link{$source}{$dest} = 1 if ($parts[$part+1] eq '--');
+            push (@nodelist,$source,$dest);
+            #print STDERR "$source ".$parts[$part+1]." $dest\n";
+          }
+        }
+      }
+    }
+
+#    $source = $1;
+#    $dest = $2;
+#    $source =~ s/^\W*//;
+#    $source =~ s/\W*$//;
+#    $dest =~ s/^\W*//;
+#    $dest =~ s/\W*$//;
+#    $link{$source}{$dest} = 2;
+#    push (@nodelist,$source,$dest);
+#    print STDERR "source:$source:dest:$dest:\n";
+  } else {
+#    if ($line =~ m#^edge# or $line =~ m#^node#)
+#    {
+#      print STDERR "Skipping: $line\n";
+#      next;
+#    }
+    if ($line =~ m#^(\S+).*\[.*\]#)
+    {
+      $name = $1;
+      $name =~ tr/"//d;
+      if ($name eq 'node' or $name eq 'edge')
+      {
+        next;
+      }
+      #print STDERR "name:$name:\n";
+    }
+    if ($line =~ m#\[.*label=([^,\]]*).*\]#)
+    {
+      $label = $1;
+      $label =~ tr/"//d;
+      $node{$name}{'label'} = $label;
+      #print STDERR "label:$label:\n";
+    }
+    if ($line =~ m#\[.*fillcolor="?([\d\.]+),([\d\.]+),([\d\.]+).*\]#)
+    {
+      $h = $1;
+      $s = $2;
+      $v = $3;
+      #print STDERR "hsv:$h:$s:$v:\n";
+      $h = $h * 360;
+      ($r,$g,$b) = &hsv2rgb($h,$s,$v);
+      $node{$name}{r} = $r;
+      $node{$name}{g} = $g;
+      $node{$name}{b} = $b;
+      #print STDERR "rgb:$r:$g:$b:\n";
+    }
+  }
+}
+
+undef %saw;
+@saw{@nodelist} = ();
+@nodelist = sort keys %saw;  # remove sort if undesired
+undef %saw;
+
+if ($pov or $vrml) {
+  $pov_or_vrml = 1;
+} else {
+  $pov_or_vrml = 0;
+}
+
+for $nodenum (@nodelist)
+{
+  $node{$nodenum}{x}=rand;# $maxx;
+  $node{$nodenum}{y}=rand;# $maxy;
+  $node{$nodenum}{z}=rand if $pov_or_vrml;
+  unless(defined $node{$nodenum}{'label'})
+  {
+    $node{$nodenum}{'label'} = $nodenum;
+  }
+}
+
+print STDERR "springgraph iterating until reaches $done\n\n";
+
+#&draw;
+$continue = 1;
+$iter = 0;
+while($continue > $done)
+{
+  $continue = $done;
+  $iter++;
+  for $nodenum (@nodelist)
+  {
+    $node{$nodenum}{oldx} = $node{$nodenum}{x};
+    $node{$nodenum}{oldy} = $node{$nodenum}{y};
+    $node{$nodenum}{oldz} = $node{$nodenum}{z} if $pov_or_vrml;
+    $xmove = 0;
+    $ymove = 0;
+  }
+  for $source (@nodelist)
+  {
+    $movecount = 0;
+    for $dest (@nodelist)
+    {
+      next if ($source eq $dest);
+      $xdist = $node{$source}{oldx} - $node{$dest}{oldx};
+      $ydist = $node{$source}{oldy} - $node{$dest}{oldy};
+      $dist = $xdist*$xdist + $ydist*$ydist;
+      if ($pov_or_vrml)
+      {
+       $zdist = $node{$source}{oldz} - $node{$dest}{oldz};
+        $dist += $zdist*$zdist;
+      }
+      # $distance = sqrt($dist);
+      $percent = $push / $dist;
+      if ($link{$source}{$dest})
+      {
+         $percent -= $pull;
+      }
+      if ($link{$dest}{$source})
+      {
+         $percent -= $pull;
+      }
+      $percent *= $rate;
+      $xmove -= $xdist * $percent;
+      $ymove -= $ydist * $percent;
+      $zmove -= $zdist * $percent if $pov_or_vrml;
+      $movecount++;
+      # $pullmove = $pull * $dist;
+      # $pushmove = $push / $dist;
+      # print STDERR "dist: $dist, pull: $pullmove, push: $pushmove\n";
+      # print STDERR "$source to ${dest}, Dist: $dist Want: $wantdist (${percent}x)\n";
+      # print STDERR "is: $node[$source]{oldx} $node[$source]{oldy} $xdist $ydist, want: $wantxdist $wantydist ($newdist2)\n";
+
+    }
+    $xmove = $xmove / $movecount;
+    $ymove = $ymove / $movecount;
+    $zmove = $zmove / $movecount if $pov_or_vrml;
+    $node{$source}{x} -= $xmove;
+    $node{$source}{y} -= $ymove;
+    $node{$source}{z} -= $zmove if $pov_or_vrml;
+    if ($xmove > $continue)
+    {
+      $continue = $xmove;
+    }
+    if ($ymove > $continue)
+    {
+      $continue = $ymove;
+    }
+    if (($pov_or_vrml) and $zmove > $continue)
+    {
+      $continue = $zmove;
+    }
+  }
+  #print STDERR "$iter\n";
+  if (0)
+  {
+    &draw;
+    open (XV,"| xv -wait 1 -");
+    #open (XV,"| xloadimage -delay 1 stdin");
+    binmode XV;
+    print XV $im->png;
+    close XV;
+  }
+  if ($iter % 20 == 0)
+  {
+    print STDERR "$continue\n";
+  }
+}
+print STDERR "Iterations: $iter\n";
+for $source (@nodelist)
+{
+  for $color ('r', 'g', 'b')
+  {
+    $node{$source}{$color} = 255 unless (defined $node{$source}{$color});
+  }
+}
+if ($pov)
+{
+  &drawpov;
+} elsif ($vrml) {
+  &drawvrml;
+} else {
+  &draw;
+}
+
+undef $maxx;
+undef $maxy;
+sub draw
+{
+  for $nodenum (@nodelist)
+  {
+    if (!(defined $maxx) or (($node{$nodenum}{x} + (length($node{$nodenum}{'label'}) * 8 + 16)/2) > $maxx + (length($node{$nodenum}{'label'}) * 8 + 16)/2))
+    {
+      $maxx = $node{$nodenum}{x};# + (length($node{$nodenum}{'label'}) * 8 + 16)/2/2
+      $maxxlength = (length($node{$nodenum}{'label'}) * 8 + 16)/2;
+    }
+    if (!(defined $minx) or (($node{$nodenum}{x} - (length($node{$nodenum}{'label'}) * 8 + 16)/2) < $minx - (length($node{$nodenum}{'label'}) * 8 + 16)/2))
+    {
+      $minx = $node{$nodenum}{x};# - (length($node{$nodenum}{'label'}) * 8 + 16)/2/2
+      $minxlength = (length($node{$nodenum}{'label'}) * 8 + 16)/2;
+    }
+
+    $maxy = $node{$nodenum}{y} if (!(defined $maxy) or $node{$nodenum}{y} > $maxy);
+    $miny = $node{$nodenum}{y} if (!(defined $miny) or $node{$nodenum}{y} < $miny);
+  }
+  for $nodenum (@nodelist)
+  {
+    #$node{$nodenum}{x} = ($node{$nodenum}{x} - $minx) * $scale + $margin;
+    $node{$nodenum}{x} = ($node{$nodenum}{x} - $minx) * $scale + $minxlength -1 ;# + $margin;
+    $node{$nodenum}{y} = ($node{$nodenum}{y} - $miny) * $scale + $nodesize/2 - 1;
+  }
+  $maxx = ($maxx - $minx) * $scale + $minxlength + $maxxlength;# + $margin*2;
+  $maxy = ($maxy - $miny) * $scale + $nodesize/2*2;
+  $im = new GD::Image($maxx,$maxy);
+  $bgcol = $im->colorAllocate(@bgcolor);
+  $im->transparent($bgcol) if $trans; # make transparent
+  $blue = $im->colorAllocate(0,0,255);
+  $powderblue = $im->colorAllocate(176,224,230);
+  $black = $im->colorAllocate(0,0,0);
+  $linecol = $im->colorAllocate(@linecolor);
+
+  for $source (@nodelist)
+  {
+    #print STDERR "node: $source $node[$source]{x},$node[$source]{y}\n";
+    for $dest (@nodelist)
+    {
+      if (defined $link{$source}{$dest} and $link{$source}{$dest} == 2 and $source ne $dest)
+      {
+        $dist = sqrt( abs($node{$source}{x}-$node{$dest}{x})**2 +  abs($node{$source}{y}-$node{$dest}{y})**2 );
+        $xdist = $node{$source}{x} - $node{$dest}{x};
+        $ydist = $node{$source}{y} - $node{$dest}{y};
+
+        $angle = &acos($xdist/$dist);
+        #$angle = atan2($ydist,$xdist);
+        #$angle += $pi if $ydist < 0;
+        #$dist = abs(cos($angle))*(length($node{$dest}{'label'}) * 8 + 16)/2 + abs(sin($angle))*$nodesize/2;
+        $width = (length($node{$dest}{'label'}) * 8 + 16)/2;
+        $height = $nodesize/2;
+        $dist = sqrt( ($height**2 * $width**2) / ( ($height**2 * (cos($angle)**2) ) + ($width**2 * (sin($angle)**2) ) ));
+        #$dist = $dist*40;
+        $xmove = cos($angle)*$dist;
+        $ymove = sin($angle)*$dist;
+        #$ymove = -$ymove if $ydist < 0; # the part mj omitted
+        $point[0]{x} = $xmove;
+        $point[0]{y} = $ymove;
+
+        $xmove = cos($angle)*($dist+$arrowlength-3);
+        $ymove = sin($angle)*($dist+$arrowlength-3);
+        #$ymove = -$ymove if $ydist < 0; # the part mj omitted
+        $point[3]{x} = $xmove;
+        $point[3]{y} = $ymove;
+
+        #$angle = $angle + $arrowwidth/2;
+        $dist = 4;
+        $xmove = $xmove + cos($angle)*$dist;
+        $ymove = $ymove + sin($angle)*$dist;
+        #$ymove = -$ymove if $ydist < 0; # the part mj omitted
+
+        $angle = $angle + $twopi/4; 
+        $dist = $arrowwidth/2;
+        $xmove = $xmove + cos($angle)*$dist;
+        $ymove = $ymove + sin($angle)*$dist;
+        #$ymove = -$ymove if $ydist < 0; # the part mj omitted
+        $point[1]{x} = $xmove;
+        $point[1]{y} = $ymove;
+
+        $angle = $angle + $twopi/2;
+        $dist = $arrowwidth;
+        $xmove = $xmove + cos($angle)*$dist;
+        $ymove = $ymove + sin($angle)*$dist;
+        #$ymove = -$ymove if $ydist < 0; # the part mj omitted
+        $point[2]{x} = $xmove;
+        $point[2]{y} = $ymove;
+
+        for $num (0 .. 3)
+        {
+          $point[$num]{y} = - $point[$num]{y} if $ydist < 0;
+        }
+        
+        $im->line($node{$dest}{x}+$point[0]{x},$node{$dest}{y}+$point[0]{y},$node{$dest}{x}+$point[1]{x},$node{$dest}{y}+$point[1]{y},$linecol);
+        $im->line($node{$dest}{x}+$point[1]{x},$node{$dest}{y}+$point[1]{y},$node{$dest}{x}+$point[2]{x},$node{$dest}{y}+$point[2]{y},$linecol);
+        $im->line($node{$dest}{x}+$point[2]{x},$node{$dest}{y}+$point[2]{y},$node{$dest}{x}+$point[0]{x},$node{$dest}{y}+$point[0]{y},$linecol);
+#        $xmove = int($node{$dest}{x}+$point[3]{x});
+#        $ymove = int($node{$dest}{y}+$point[3]{y});
+#        $im->fillToBorder($xmove,$ymove,$linecol,$powderblue);
+        #$im->fillToBorder($node{$dest}{x}+$point[3]{x},$node{$dest}{y}+$point[3]{y},$linecol,$linecol);
+        #$im->line($point[1]{x},$point[1]{y},$point[2]{x},$point[2]{y},$linecol);
+        #$im->line($point[2]{x},$point[2]{y},$point[0]{x},$point[0]{y},$linecol);
+        #$im->fillToBorder($point[3]{x},$point[3]{y},$linecol,$linecol);
+        #$im->arc($point[3]{x},$point[3]{y},10,10,0,360,$black);
+
+#        $im->arc($point[0]{x},$point[0]{y},20,20,0,360,$black);
+#        $im->arc($point[1]{x},$point[1]{y},20,20,0,360,$black);
+#        $im->arc($point[2]{x},$point[2]{y},20,20,0,360,$black);
+        #$im->arc($node{$dest}{x}+$xmove,$node{$dest}{y}+$ymove,20,20,0,360,$black);
+      }
+    }
+  }
+  for $source (@nodelist)
+  {
+    for $dest (@nodelist)
+    {
+      if ($link{$source}{$dest})
+      {
+        $im->line($node{$source}{x},$node{$source}{y},$node{$dest}{x},$node{$dest}{y},$linecol);
+      }
+    }
+  }
+
+  for $source (@nodelist)
+  {
+    $im->arc($node{$source}{x},$node{$source}{y},(length($node{$source}{'label'}) * 8 + 16),$nodesize,0,360,$black);
+    #$im->arc($node{$source}{x},$node{$source}{y},$nodesize,$nodesize,0,360,$black);
+    if (defined $node{$source}{r} and defined $node{$source}{g} and defined $node{$source}{b})
+    {
+      $color = $im->colorResolve($node{$source}{r},$node{$source}{g},$node{$source}{b});
+    } else
+    {
+      $color = $bgcol;
+    }
+    $im->fillToBorder($node{$source}{x},$node{$source}{y},$black,$color);
+  }
+  for $source (@nodelist)
+  {
+    $im->string(gdLargeFont,$node{$source}{x} - (length($node{$source}{'label'}) * 8 / 2) ,$node{$source}{y}-8,$node{$source}{'label'},$black);
+  }
+
+
+  binmode STDOUT;
+  print $im->png;
+}
+
+sub drawpov
+{
+  print'// Generated by springgraph, by Darxus@ChaosReigns.com: 
+// http://www.ChaosReigns.com/code/springgraph/
+
+#include "colors.inc"
+#include "shapes.inc"
+#include "textures.inc"
+#include "glass.inc"
+#include "stones.inc"
+light_source {<0, 400, -500> color White rotate <0, 360*clock, 0>}
+light_source {<400, 0, -500> color White rotate <0, 360*clock, 0>}
+';
+
+  for $source (@nodelist)
+  {
+    $node{$source}{x} = $node{$source}{x} * $scale;
+    $node{$source}{y} = $node{$source}{y} * $scale;
+    $node{$source}{z} = $node{$source}{z} * $scale;
+    $node{$source}{r} = $node{$source}{r} / 256;
+    $node{$source}{g} = $node{$source}{g} / 256;
+    $node{$source}{b} = $node{$source}{b} / 256;
+  }
+  for $source (@nodelist)
+  {
+    print "sphere { <$node{$source}{x},$node{$source}{y},$node{$source}{z}>, 15 pigment {color rgb<$node{$source}{r},$node{$source}{g},$node{$source}{b}>}}\n";
+    print "text { ttf \"crystal.ttf\", \"$node{$source}{'label'}\", 0.5, 0 translate 2*x rotate <0, 360*clock, 0> translate -0.375*y scale 10 translate <$node{$source}{x},$node{$source}{y},$node{$source}{z}> pigment {color rgb<$node{$source}{r},$node{$source}{g},$node{$source}{b}>}}\n";
+    #print "text { ttf \"crystal.ttf\", \"$node{$source}{'label'}\", 0.5, 0 translate -".scalar(length($node{$source}{'label'})*0.25)."*x scale 10 translate <$node{$source}{x},$node{$source}{y},$node{$source}{z}> pigment {color rgb<$node{$source}{r},$node{$source}{g},$node{$source}{b}>}}\n";
+    for $dest (@nodelist)
+    {
+      if ($link{$source}{$dest})
+      {
+        print "cylinder {<$node{$source}{x},$node{$source}{y},$node{$source}{z}>,<$node{$dest}{x},$node{$dest}{y},$node{$dest}{z}> 0.5 pigment {color rgb<0.5,0.5,0.5>}}\n";
+      }
+    }
+  }
+  print 'camera {
+   location  <0, 0, -500>
+   up        <0.0,  1.0,  0>
+   right     <4/3,  0.0,  0>
+   look_at   <0,    0,   -1>
+   rotate <0, 360*clock, 0>
+}
+';
+
+}
+
+
+sub drawvrml
+{
+  my ($t,$r,$length,$color);
+  print'#VRML V2.0 utf8
+
+WorldInfo {
+  info ["Generated by springgraph, by Darxus@ChaosReigns.com: http://www.ChaosReigns.com/code/springgraph/"]
+}
+
+';
+
+  for $source (@nodelist)
+  {
+    $node{$source}{x} = $node{$source}{x} * $scale;
+    $node{$source}{y} = $node{$source}{y} * $scale;
+    $node{$source}{z} = $node{$source}{z} * $scale;
+    for $color ('r', 'g', 'b')
+    {
+      if (defined $node{$source}{$color})
+      {
+        $node{$source}{$color} = $node{$source}{$color} / 256;
+      }
+    }
+  }
+  for $source (@nodelist)
+  {
+print "
+Transform {
+  translation $node{$source}{x} $node{$source}{y} $node{$source}{z}
+  children [
+    Shape{
+        appearance Appearance {
+           material Material {
+              diffuseColor $node{$source}{r} $node{$source}{g} $node{$source}{b}
+           }
+        }
+        geometry Sphere{radius 15}
+    }
+  ]
+}
+";
+
+    #print "sphere { <$node{$source}{x},$node{$source}{y},$node{$source}{z}>, 15 pigment {color rgb<$node{$source}{r},$node{$source}{g},$node{$source}{b}>}}\n";
+    #print "text { ttf \"crystal.ttf\", \"$node{$source}{'label'}\", 0.5, 0 translate 2*x rotate <0, 360*clock, 0> translate -0.375*y scale 10 translate <$node{$source}{x},$node{$source}{y},$node{$source}{z}> pigment {color rgb<$node{$source}{r},$node{$source}{g},$node{$source}{b}>}}\n";
+    #print "text { ttf \"crystal.ttf\", \"$node{$source}{'label'}\", 0.5, 0 translate -".scalar(length($node{$source}{'label'})*0.25)."*x scale 10 translate <$node{$source}{x},$node{$source}{y},$node{$source}{z}> pigment {color rgb<$node{$source}{r},$node{$source}{g},$node{$source}{b}>}}\n";
+    for $dest (@nodelist)
+    {
+      if ($link{$source}{$dest})
+      {
+        ($t,$r,$length) = &cylinder($node{$source}{x},$node{$source}{y},$node{$source}{z},$node{$dest}{x},$node{$dest}{y},$node{$dest}{z});
+        print "
+Transform {
+  translation $t
+  rotation $r
+  children [
+    Shape{
+        appearance Appearance {
+           material Material {
+              diffuseColor 0.5 0.5 0.5
+           }
+        }
+        geometry Cylinder {
+          radius 0.5
+          height $length
+          top FALSE
+          bottom FALSE
+        }
+    }
+  ]
+}
+";
+
+
+        
+      }
+    }
+  }
+#  print 'camera {
+#   location  <0, 0, -500>
+#   up        <0.0,  1.0,  0>
+#   right     <4/3,  0.0,  0>
+#   look_at   <0,    0,   -1>
+#   rotate <0, 360*clock, 0>
+#}
+#';
+
+}
+
+
+
+
+sub hsv2rgb
+{
+#from http://faqchest.dynhost.com/prgm/perlu-l/perl-01/perl-0101/perl-010100/perl01010410_17820.html
+
+# Given an h/s/v array, return an r/g/b array.
+# The r/g/b values will each be between 0 and 255.
+# The h value will be between 0 and 360, and
+# the s and v values will be between 0 and 1.
+#
+
+                      my $h = shift;
+                      my $s = shift;
+                      my $v = shift;
+
+                      # limit this to h values between 0 and 360 and s/v values
+                      # between 0 and 1
+
+                      unless (defined($h) && defined($s) && defined($v) &&
+                             $h >= 0 && $s >= 0 && $v >= 0 &&
+                             $h <= 360 && $s <= 1 && $v <= 1) {
+                        return (undef, undef, undef);
+                      }
+
+                      my $r;
+                      my $g;
+                      my $b;
+
+                      # 0.003 is less than 1/255; use this to make the floating point
+                      # approximation of zero, since the resulting rgb values will
+                      # normally be used as integers between 0 and 255.  Feel free to
+                      # change this approximation of zero to something else, if this
+                      # suits you.
+                      if ($s < 0.003) {
+                        $r = $g = $b = $v;
+                      }
+                      else {
+
+                        $h /= 60;
+                        my $sector = int($h);
+                        my $fraction = $h - $sector;
+
+                        my $p = $v * (1 - $s);
+                        my $q = $v * (1 - ($s * $fraction));
+                        my $t = $v * (1 - ($s * (1 - $fraction)));
+
+                        if ($sector == 0) {
+                          $r = $v;
+                          $g = $t;
+                          $b = $p;
+                        }
+                        elsif ($sector == 1) {
+                          $r = $q;
+                          $g = $v;
+                          $b = $p;
+                        }
+                        elsif ($sector == 2) {
+                          $r = $p;
+                          $g = $v;
+                          $b = $t;
+                        }
+                        elsif ($sector == 3) {
+                          $r = $p;
+                          $g = $q;
+                          $b = $v;
+                        }
+                        elsif ($sector == 4) {
+                          $r = $t;
+                          $g = $p;
+                          $b = $v;
+                        }
+                        else {
+                          $r = $v;
+                          $g = $p;
+                          $b = $q;
+                        }
+                      }
+
+                      # Convert the r/g/b values to all be between 0 and 255; use the
+                      # ol' 0.003 approximation again, with the same comment as above.
+
+                      $r = ($r < 0.003 ? 0.0 : $r * 255);
+                      $g = ($g < 0.003 ? 0.0 : $g * 255);
+                      $b = ($b < 0.003 ? 0.0 : $b * 255);
+
+                      return ($r, $g, $b);
+                    }
+
+# from perlfunc(1)
+sub acos { atan2( sqrt(1 - $_[0] * $_[0]), $_[0] ) }
+
+
+sub cylinder {
+    my ($x1,$y1,$z1,$x2,$y2,$z2) = @_;
+    my ($t, $r, $length, $rx, $ry, $rz, $dist);
+
+    $x1 = 0 unless $x1;
+    $x2 = 0 unless $x2;
+    $y1 = 0 unless $y1;
+    $y2 = 0 unless $y2;
+    $z1 = 0 unless $z1;
+    $z2 = 0 unless $z2;
+    my $dx=$x1-$x2;
+    my $dy=$y1-$y2;
+    my $dz=$z1-$z2;
+    if (1) {
+        unless (0) {
+            $length = sqrt($dx*$dx + $dy*$dy + $dz*$dz);
+            $rx = $dx;
+            $ry = ($dy+$length);
+            $rz = $dz;
+            $dist = sqrt(abs($rx)**2 + abs($ry)**2);
+            $dist = sqrt(abs($rz)**2 + abs($dist)**2);
+            $rx = $rx / $dist;
+            $ry = $ry / $dist;
+            $rz = $rz / $dist;
+            $t = ($x1-($dx/2))." ".($y1-($dy/2))." ".($z1-($dz/2));
+            $r = "$rx $ry $rz $pi";
+        }
+    }
+    return ($t,$r,$length);
+}
+
+sub usage {
+print <<END
+springgraph - Render a .dot file into a graphic
+
+Usage: springgraph [-p] [-v] [-s scale] [-t] [-b color] [-l color] [-h]
+
+   -p   Create a file that can be rendered with POV-Ray
+   -v   Create a VRML file
+   -s   This option specifies the scale. All of the node locations
+        are multiplied by this. Increase the scale to eliminate node
+        overlaps. Decrease the scale to make the graph smaller. 
+   -t   Make the background of the resulting image transpaent.
+   -b   set background color of image, specify it in the form RRGGBB,
+        in hex digits, e.g. FFFFFF is white, 000000 is black, FF0000
+        is red, ...
+   -l   set the line color, same format as the background color
+   -h   show this help
+
+END
+}
diff --git a/springgraph/springgraph.1 b/springgraph/springgraph.1
new file mode 100644 (file)
index 0000000..e52db60
--- /dev/null
@@ -0,0 +1,84 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.\" First parameter, NAME, should be all caps
+.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
+.\" other parameters are allowed: see man(7), man(1)
+.TH SPRINGGRAPH 1 "September 5, 2005"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh        disable hyphenation
+.\" .hy        enable hyphenation
+.\" .ad l      left justify
+.\" .ad b      justify to both left and right margins
+.\" .nf        disable filling
+.\" .fi        enable filling
+.\" .br        insert line break
+.\" .sp <n>    insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+springgraph \- renders a graph from a .dot file
+.SH SYNOPSIS
+.B springgraph [OPTIONS] < input-file > output-file.png
+.br
+.SH DESCRIPTION
+This manual page documents briefly the
+.B springgraph
+command.
+This manual page was written for the Debian distribution
+because the original program does not have a manual page.
+.PP
+Springgraph will read in a .dot file description of a graph, which,
+for each node, specifies its name and which other nodes it is
+connected to, and then renders a graph. The output is a PNG
+file. Each node is drawn as an
+ellipse, and each connection is drawn as an arrow. The node placement
+is a result of all of the nodes moving away from each other, while all
+nodes which are connected move toward each other. This movement is
+repeated until it stabilizes.
+
+Springgraph was written as an alternative to neato, which is part of
+graphviz. It attempts to read the same .dot files used by graphviz,
+but currently only supports a limited number of node attributes (label
+and fillcolor) and can only handle two nodes per edge definition ("node1 ->
+node2", not "node1 -> node2 -> node3").
+.SH OPTIONS
+\fIspringgraph\fP
+accepts the following options:
+.TP 16
+.B \-p
+Create a file that can be rendered with POV-Ray
+.TP 16
+.B \-v
+create a VRML file
+.TP 16
+.B \-s
+this option specifies the scale. All of the node locations
+are multiplied by this. Increase the scale to eliminate node
+overlaps. Decrease the scale to make the graph smaller. 
+.TP 16
+.B \-t
+make the background of the resulting image transparent.
+.TP 16
+.B \-b
+set background color of image, specify it in the form RRGGBB,
+in hex digits, e.g. FFFFFF is white, 000000 is black, FF0000
+is red, ...
+.TP 16
+.B \-l
+set the line color, same format as the background color
+.TP 16
+.B \-h
+display usage synopsis
+.SH EXAMPLE
+ digraph {
+  "rene" -> "myon";
+  "mvo" -> "rene";
+ }
+.SH SEE ALSO
+neato(1)
+sig2dot(1)
+http://www.graphviz.org/Documentation.php
+http://www.graphviz.org/cvs/doc/info/lang.html
+.SH AUTHOR
+This manual page was written by Kevin M. Rosenberg <kmr@debian.org>,
+for the Debian GNU/Linux system (but may be used by others).