diff --git a/COPYING b/COPYING
new file mode 100644
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+	51 Franklin St, Fifth Floor, Boston, MA  02110-1301  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.
+
+		    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.)
+
+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 noncommercial 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.
+
+  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.
+
+  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
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  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 program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,26 @@
+Copyright (C) 2006-2008 John Goerzen <jgoerzen@complete.org>
+
+All code, documentation, and build scripts are under the following
+license unless otherwise noted:
+
+   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
+
+The GNU General Public License is available in the file COPYING in the source
+distribution.  Debian GNU/Linux users may find this in
+/usr/share/common-licenses/GPL-2.
+
+If the GPL is unacceptable for your uses, please e-mail me; alternative
+terms can be negotiated for your project.
+
diff --git a/Commands.hs b/Commands.hs
new file mode 100644
--- /dev/null
+++ b/Commands.hs
@@ -0,0 +1,70 @@
+{-
+Copyright (C) 2006-2008 John Goerzen <jgoerzen@complete.org>
+
+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
+-}
+
+{- |
+   Module     : Commands
+   Copyright  : Copyright (C) 2006-2008 John Goerzen
+   License    : GNU GPL, version 2 or above
+
+   Maintainer : John Goerzen <jgoerzen@complete.org>
+   Stability  : provisional
+   Portability: portable
+
+Written by John Goerzen, jgoerzen\@complete.org
+
+-}
+module Commands where
+
+import Text.Printf
+import Utils
+import Types
+import Config
+
+import qualified Commands.Follow
+import qualified Commands.Ls
+import qualified Commands.Setup
+import qualified Commands.Update
+
+--allCommands :: [(String, Command)]
+allCommands = 
+    [Commands.Update.dmsend,
+     Commands.Follow.follow,
+     Commands.Ls.lsarchive,
+     lscommands,
+     Commands.Ls.lsdm,
+     Commands.Ls.lsdmarchive,
+     Commands.Ls.lsfollowers,
+     Commands.Ls.lsfollowing,
+     Commands.Ls.lsrecent,
+     Commands.Ls.lsreplies,
+     Commands.Setup.setup,
+     Commands.Follow.unfollow,
+     Commands.Update.update
+    ]
+
+lscommands = 
+    simpleCmd "lscommands" "Display a list of all available commands" ""
+                  [] lscommands_worker
+
+lscommands_worker _ _ _ =
+    do putStrLn "All available commands:"
+       printf "%-20s %s\n" "Name" "Description"
+       putStrLn "-------------------- -------------------------------------------------------"
+       mapM_ (\(_, x) -> printf "%-20s %s\n" (cmdname x) (cmddescrip x))
+             allCommands
+                 
diff --git a/Commands/Ls.hs b/Commands/Ls.hs
new file mode 100644
--- /dev/null
+++ b/Commands/Ls.hs
@@ -0,0 +1,342 @@
+{-
+Copyright (C) 2006-2008 John Goerzen <jgoerzen@complete.org>
+
+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
+-}
+
+module Commands.Ls(lsrecent, lsreplies, lsfollowing, lsfollowers, lsarchive, lsdm, lsdmarchive) where
+import Utils
+import System.Log.Logger
+import Types
+import Text.Printf
+import System.Console.GetOpt
+import Data.List
+import Text.XML.HaXml hiding (when)
+import Download
+import FeedParser
+import Data.ConfigFile
+import Data.String.Utils(strip)
+import Config
+import Data.Either.Utils(forceEither)
+import Control.Monad(when)
+import Distribution.Simple.Utils(wrapText)
+import HSH
+import System.Console.GetOpt.Utils
+
+i = infoM "ls"
+
+stdopts = [Option "a" ["all"] (NoArg ("a", "")) 
+                      "Show ALL results, not just 1st page\n\
+                      \WARNING: may generate excessive traffic.  \
+                      \Use with caution!",
+           Option "l" ["long"] (NoArg ("l", "")) 
+                      "Long format output -- more info and \
+                      \tab-separated columns"]
+
+sinceopts = [
+           Option "e" ["exec"] (ReqArg (stdRequired "e") "COMMAND")
+                  "Suppress normal output, and instead call COMMAND\n\
+                  \once for each output item.  The command will be\n\
+                  \passed exactly four arguments: update ID,\n\
+                  \username, suggested Message-ID, and update\n\
+                  \content.  These arguments may contain shell\n\
+                  \metacharacters.",
+           Option "m" ["mailto"] (ReqArg (stdRequired "m") "ADDRESS")
+                  "Suppress normal output, and instead generate an\n\
+                  \email with the data and send it to ADDRESS.",
+           Option "s" ["saveid"] (NoArg ("s", ""))
+                      "Save topmost ID for future use with --unseen.\n\
+                      \Will write the ID to your config file.",
+           Option "u" ["unseen"] (NoArg ("u", ""))
+                      "Show only items since the last use of --saveid"]
+
+paginated workerfunc cppath cp (args, remainder)
+    | lookup "a" args == Nothing = 
+        do workerfunc cppath cp (args, remainder) 1
+           return ()
+    | otherwise = paginateloop 1
+    where paginateloop page =
+              do r <- workerfunc cppath cp (args, remainder) page
+                 if null r
+                     then return ()
+                     else paginateloop (page + 1)
+
+maybeSaveList section cpath cp args [] = return ()
+maybeSaveList section cpath cp args newids =
+    do debugM section $ "maybeSaveList called: " ++ show args ++ " " ++ show newids
+       maybeSave section cpath cp args theid
+    where theid = maximum . map (read::String -> Integer) $ newids
+
+maybeSave section cpath cp args newid =
+    case (lookup "s" args, get cp section "lastid") of
+      (Nothing, _) -> return ()
+      (_, Left _) -> saveid
+      (_, Right x) ->
+          if (read x) < (newid::Integer)
+             then return ()
+             else saveid
+    where saveid = writeCP cpath newcp
+          newcp = forceEither $
+                  do cp2 <- if (has_section cp section)
+                                then return cp
+                                else add_section cp section
+                     cp2 <- set cp2 section "lastid" (show newid)
+                     return cp2
+
+sinceArgs section cp args =
+    case (lookup "u" args, get cp section "lastid") of
+      (Nothing, _) -> []
+      (_, Left _) -> []
+      (_, Right a) -> 
+          [("since_id", strip a)]
+
+--------------------------------------------------
+-- lsrecent
+--------------------------------------------------
+
+lsrecent = simpleCmd "lsrecent" "List recent updates from those you follow"
+             lsrecent_help
+             (stdopts ++ sinceopts) (paginated (statuses_worker "lsrecent"
+                                                "/statuses/friends_timeline"))
+
+lsreplies = simpleCmd "lsreplies" "List recent replies to you"
+            lsreplies_help
+            (stdopts ++ sinceopts) (paginated (statuses_worker "lsreplies" 
+                                               "/statuses/replies"))
+
+lsarchive = simpleCmd "lsarchive" "List recent status updates you posted yourself"
+            lsarchive_help
+            (stdopts ++ sinceopts) (paginated (statuses_worker "lsarchive"
+                                               "/statuses/user_timeline"))
+
+lsdm = simpleCmd "lsdm" "List recent direct messages to you"
+       lsdm_help
+       (stdopts ++ sinceopts) (paginated (dm_worker "lsdm" 
+                                          "/direct_messages"))
+
+lsdmarchive = simpleCmd "lsdmarchive" "List recent direct messages you sent"
+              lsdmarchive_help
+              (stdopts ++ sinceopts) (paginated (dm_worker "lsdmarchive"
+                                                 "/direct_messages/sent"))
+
+statuses_worker = generic_worker handleStatus
+dm_worker = generic_worker handleDM
+
+generic_worker procfunc section command cpath cp (args, _) page =
+    do xmlstr <- sendAuthRequest cp (command ++ ".xml")
+                 (("page", show page) : sinceArgs section cp args)
+                 []
+       debugM section $ "Got doc: " ++ xmlstr
+       results <- procfunc section cp args xmlstr
+       when (page == 1) $
+            maybeSaveList section cpath cp args (map sId results)
+       return results
+
+lsrecent_help =
+ "Usage: twidge lsrecent [options]\n\n\
+ \You can see the 20 most recent items from your friends with:\n\n\
+ \   twidge lsrecent\n\n\
+ \To see items that you haven't seen yet, and remember this for the future,\n\
+ \use:\n\n\
+ \   twidge lsrecent -su\n\n\
+ \After running that once, you may want to use -asu in the future to get all\n\
+ \unseen messages, even if there are more than 20.  Don't use -a until\n\
+ \you've used -s at least once.\n"
+
+lsreplies_help =
+ "Usage: twidge lsreplies [options]\n\n\
+ \You can see the 20 most recent @replies from others to you with:\n\n\
+ \   twidge lsreplies\n\n\
+ \For more examples, including how to see only unseen replies, please\n\
+ \refer to the examples under twidge lsrecent --help, which also pertain\n\
+ \to lsreplies.\n"
+
+lsarchive_help =
+ "Usage: twidge lsarchive [options]\n\n\
+ \You can see the 20 most recent updates you posted with:\n\n\
+ \   twidge lsarchive\n\n\
+ \For more examples, including how to see only unseen updates, please\n\
+ \refer to the examples under twidge lsrecent --help, which also pertain\n\
+ \to lsarchive.\n"
+
+lsdm_help =
+ "Usage: twidge lsdm [options]\n\n\
+ \You can see the 20 most recent direct messages to you with:\n\n\
+ \   twidge lsdm\n\n\
+ \For more examples, including how to see only unseen updates, please\n\
+ \refer to the examples under twidge lsrecent --help, which also pertain\n\
+ \to lsdm.\n"
+ 
+lsdmarchive_help =
+ "Usage: twidge lsdmarchive [options]\n\n\
+ \You can see the 20 most recent direct messages you sent with:\n\n\
+ \   twidge lsdmarchive\n\n\
+ \For more examples, including how to see only unseen updates, please\n\
+ \refer to the examples under twidge lsrecent --help, which also pertain\n\
+ \to lsdmarchive.\n"
+
+handleStatus = handleGeneric (map procStatuses . getStatuses) printStatus
+handleDM = handleGeneric (map procDM . getDMs) printDM
+
+handleGeneric pfunc printfunc section cp args xmlstr = 
+    let doc = getContent . xmlParse section . stripUnicodeBOM $ xmlstr
+        statuses = pfunc doc
+    in do mapM_ (printfunc section cp args) statuses
+          return statuses
+
+procStatuses :: Content -> Message
+procStatuses item = 
+    Message {sId = s (tag "id") item,
+             sSender = s (tag "user" /> tag "screen_name") item,
+             sRecipient = "",
+             sText = s (tag "text") item,
+             sDate = s (tag "created_at") item}
+
+s f item = sanitize $ contentToString (keep /> f /> txt $ item)
+
+procDM :: Content -> Message
+procDM item =
+    Message {sId = s (tag "id") item,
+             sSender = s (tag "sender_screen_name") item,
+             sRecipient = s (tag "recipient_screen_name") item,
+             sText = s (tag "text") item,
+             sDate = s (tag "created_at") item}
+
+getStatuses = tag "statuses" /> tag "status"
+getDMs = tag "direct-messages" /> tag "direct_message"
+
+longStatus :: Message -> String
+longStatus m = printf "%s\t%s\t%s\t%s\t%s\t\n"
+               (sId m) (sSender m) (sRecipient m) (sText m) (sDate m)
+shortStatus :: Message -> String
+shortStatus m = 
+    (printf "%-22s %s\n" ("<" ++ sSender m ++ ">")
+               (head wrappedtext)) ++
+    concatMap (printf "%-22s %s\n" "") (tail wrappedtext)
+    where wrappedtext = wrapText (80 - 22 - 2) (words (sText m))
+
+shortDM :: Message -> String
+shortDM m =
+     (printf "%-22s %-22s %s\n" ("<" ++ sSender m ++ ">")
+                                 ("<" ++ sRecipient m ++ ">")
+                                 (head wrappedtext)) ++
+     concatMap (printf "%-22s %-22s %s\n" "" "") (tail wrappedtext)
+     where wrappedtext = wrapText (80 - 22 - 22 - 3) (words (sText m))
+
+printStatus section cp args m = 
+    printGeneric shortStatus longStatus section cp args m
+
+printDM section cp args m =
+    printGeneric shortDM longStatus section cp args m
+
+printGeneric shortfunc longfunc section cp args m =
+    case (lookup "m" args) of
+      Nothing -> 
+          case (lookup "e" args, lookup "l" args) of
+            (Just cmd, _) ->
+                runIO $ (cmd, [sId m, sSender m, sRecipient m, 
+                               sText m, sDate m])
+            (Nothing, Nothing) -> putStr (shortfunc m)
+            (Nothing, Just _) -> putStr (longfunc m)
+      Just recipient -> mailto section cp args m recipient
+    where msgid = genMsgId section m cp
+
+mailto section cp args m recipient =
+    runIO $ echo (message ++ "\n") -|- (sendmail, ["-t"])
+    where sendmail = (forceEither $ get cp section "sendmail")::String
+          msgid = genMsgId section m cp
+          subject = take 30 (sText m) ++ "... (twidge " ++ section ++ ")"
+          message = unlines $ 
+                    (case get cp section "mailfrom" of
+                      Left _ -> ["Subject: " ++ (sSender m) ++ ": " ++ subject]
+                      Right x -> ["From: " ++ (sSender m) ++ " <" ++ x ++ ">",
+                                  "Subject: " ++ subject]
+                    ) ++ 
+                    ["Message-ID: " ++ msgid,
+                     "X-Twidge-urlbase: " ++ forceEither (get cp "DEFAULT" "urlbase"),
+                     "X-Twidge-server-base: " ++ serverHost cp,
+                     "X-Twidge-command: " ++ section,
+                     "X-Twidge-update-id: " ++ sId m,
+                     "X-Twidge-update-sender: " ++ sSender m,
+                     "X-Twidge-update-recipient: " ++ sRecipient m,
+                     "To: " ++ recipient,
+                     "",
+                     sText m,
+                     "",
+                     "(from " ++ sSender m ++ ")"]
+
+--------------------------------------------------
+-- lsfollowing
+--------------------------------------------------
+
+lsfollowing = simpleCmd "lsfollowing" "List people you are following"
+             lsfollowing_help
+             stdopts (paginated lsfollowing_worker)
+
+lsfollowing_worker _ cp (args, user) page =
+    do xmlstr <- sendAuthRequest cp url [("page", show page)] []
+       debugM "lsfollowing" $ "Got doc: " ++ xmlstr
+       let doc = getContent . xmlParse "lsfollowing" . stripUnicodeBOM $ xmlstr
+       let users = map procUsers . getUsers $ doc
+       mapM_ (printUser args) users
+       return users
+       
+    where url = case user of
+                  [] -> "/statuses/friends.xml"
+                  [x] -> "/statuses/friends/" ++ x ++ ".xml"
+                  _ -> error "Invalid args to lsfollowing; see twidge lsfollowing --help"
+
+printUser args (name, userid) = 
+    case lookup "l" args of
+      Nothing -> putStrLn name
+      Just _ -> printf "%s\t%s\n" userid name
+
+getUsers = tag "users" /> tag "user"
+
+procUsers :: Content -> (String, String)
+procUsers item =
+    (sanitize $ contentToString (keep /> tag "screen_name" /> txt $ item),
+     sanitize $ contentToString (keep /> tag "id" /> txt $ item))
+
+lsfollowing_help =
+ "Usage: twidge lsfollowing [options] [username]\n\n\
+ \If username is given, list the twitter accounts that user is following.\n\
+ \Otherwise, list the twitter accounts your user is following.\n"
+
+--------------------------------------------------
+-- lsfollowers
+--------------------------------------------------
+
+lsfollowers = simpleCmd "lsfollowers" "List people that follow you"
+             lsfollowers_help
+             stdopts (paginated lsfollowers_worker)
+
+lsfollowers_worker _ cp (args, user) page =
+    do xmlstr <- sendAuthRequest cp url [("page", show page)] []
+       debugM "lsfollowers" $ "Got doc: " ++ xmlstr
+       let doc = getContent . xmlParse "lsfollowers" . stripUnicodeBOM $ xmlstr
+       let users = map procUsers . getUsers $ doc
+       mapM_ (printUser args) users
+       return users
+       
+    where url = case user of
+                  [] -> "/statuses/followers.xml"
+                  [x] -> "/statuses/followers/" ++ x ++ ".xml"
+                  _ -> error "Invalid args to lsfollowers; see twidge lsfollowers --help"
+
+lsfollowers_help =
+ "Usage: twidge lsfollowers [options] [username]\n\n\
+ \If username is given, list the twitter accounts that follow the user.\n\
+ \Otherwise, list the twitter accounts that follow you.\n"
diff --git a/Config.hs b/Config.hs
new file mode 100644
--- /dev/null
+++ b/Config.hs
@@ -0,0 +1,79 @@
+{- hpodder component
+Copyright (C) 2006-2008 John Goerzen <jgoerzen@complete.org>
+
+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
+-}
+
+{- |
+   Module     : Config
+   Copyright  : Copyright (C) 2006-2008 John Goerzen
+   License    : GNU GPL, version 2 or above
+
+   Maintainer : John Goerzen <jgoerzen@complete.org>
+   Stability  : provisional
+   Portability: portable
+
+Written by John Goerzen, jgoerzen\@complete.org
+
+-}
+module Config where
+
+import System.Directory
+import Data.ConfigFile
+import Control.Monad
+import Data.Either.Utils
+import Data.String.Utils(strip, split)
+import System.Posix.Files(rename)
+
+getDefaultCP =
+    do return $ forceEither $ 
+              do cp <- set startingcp "DEFAULT" "urlbase" "https://twitter.com"
+                 cp <- set cp "DEFAULT" "sendmail" "/usr/sbin/sendmail"
+                 cp <- set cp "DEFAULT" "shortenurls" "yes"
+                 return cp
+
+startingcp = emptyCP {accessfunc = interpolatingAccess 10}
+
+getCPName =
+    do appdir <- getUserDocumentsDirectory
+       return $ appdir ++ "/.twidgerc"
+
+loadCP cpgiven = 
+    do cpname <- case cpgiven of
+                   Nothing -> getCPName
+                   Just x -> return x
+       defaultcp <- getDefaultCP
+       dfe <- doesFileExist cpname
+       if dfe
+          then do cp <- readfile defaultcp cpname
+                  return $ forceEither cp
+          else do fail $ "No config file found at " ++ cpname ++ 
+                           "\nRun twidge setup to configure twidge for use."
+
+writeCP cpgiven cp =
+    do cpname <- case cpgiven of
+                   Nothing -> getCPName
+                   Just x -> return x
+       let tempname = cpname ++ ".write.tmp"
+       writeFile tempname (to_string cp)
+       rename tempname cpname
+
+getList :: ConfigParser -> String -> String -> Maybe [String]
+getList cp sect key = 
+       case get cp sect key of
+         Right x -> Just (splitit x)
+         Left _ -> Nothing
+    where splitit x = filter (/= "") . map strip . split "," $ x
+  
diff --git a/Download.hs b/Download.hs
new file mode 100644
--- /dev/null
+++ b/Download.hs
@@ -0,0 +1,76 @@
+{- hpodder component
+Copyright (C) 2006-2007 John Goerzen <jgoerzen@complete.org>
+
+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
+-}
+
+{- |
+   Module     : Download
+   Copyright  : Copyright (C) 2006-2008 John Goerzen
+   License    : GNU GPL, version 2 or above
+
+   Maintainer : John Goerzen <jgoerzen@complete.org>
+   Stability  : provisional
+   Portability: portable
+
+Written by John Goerzen, jgoerzen\@complete.org
+
+-}
+
+module Download(sendAuthRequest, simpleDownload) where
+import System.Log.Logger
+import Data.ConfigFile
+import HSH
+import Data.Either.Utils(forceEither)
+import Network.URI
+import Data.List
+import Control.Exception(evaluate)
+
+d = debugM "download"
+i = infoM "download"
+
+curl = "curl"
+curlopts = ["-A", "twidge v1.0.0; Haskell; GHC", -- Set User-Agent
+            "-s",               -- Silent mode
+            "-S",               -- Still show error messages
+            "-L",               -- Follow redirects
+            "-y", "60", "-Y", "1", -- Timeouts
+            "--retry", "2",     -- Retry twice
+            "-f"                -- Fail on server errors
+           ]
+
+simpleDownload :: String -> IO String
+simpleDownload url = run (curl, curlopts ++ [url])
+
+sendAuthRequest :: ConfigParser -> String -> [(String, String)] -> [(String, String)] -> IO String
+sendAuthRequest cp url getopts postoptlist =
+    do -- Force this to be evaluated in the parent process
+       authopts <- evaluate (getAuthOpts cp)
+       run (curl, curlopts ++ authopts ++ postopts ++ [urlbase ++ url ++ optstr])
+    where urlbase = forceEither $ get cp "DEFAULT" "urlbase"
+          optstr = case getopts of
+                     [] -> ""
+                     _ -> "?" ++ (concat . intersperse "&" . map conv $ getopts)
+          conv (k, v) = k ++ "=" ++ escapeURIString isUnreserved v
+          postopts = concatMap postopt postoptlist
+          postopt (k, v) = ["-d", escapeURIString isUnreserved k ++ "=" ++
+                                  escapeURIString isUnreserved v]
+
+getAuthOpts :: ConfigParser -> [String]
+getAuthOpts cp =
+    case (get cp "DEFAULT" "username", get cp "DEFAULT" "password") of
+      (Right user, Right pass) ->
+          ["--user", user ++ ":" ++ pass]
+      _ -> error "Missing username or password option in config file"
diff --git a/FeedParser.hs b/FeedParser.hs
new file mode 100644
--- /dev/null
+++ b/FeedParser.hs
@@ -0,0 +1,70 @@
+{- 
+Copyright (C) 2006-2008 John Goerzen <jgoerzen@complete.org>
+
+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
+-}
+
+{- |
+   Module     : FeedParser
+   Copyright  : Copyright (C) 2006-2008 John Goerzen
+   License    : GNU GPL, version 2 or above
+
+   Maintainer : John Goerzen <jgoerzen@complete.org>
+   Stability  : provisional
+   Portability: portable
+
+Written by John Goerzen, jgoerzen\@complete.org
+
+-}
+module FeedParser where
+
+import Text.XML.HaXml
+import Data.Char
+import Data.List
+import Data.String.Utils(strip)
+
+{- | Convert [Content] to a printable string, taking care to unescape it.
+
+An implementation without unescaping would simply be:
+
+> contentToString = concatMap (show . content)
+
+Because HaXml's unescaping only works on Elements, we must make sure that
+whatever Content we have is wrapped in an Element, then use txt to
+pull the insides back out. -}
+contentToString :: [Content] -> String
+contentToString = 
+    concatMap procContent
+    where procContent x = 
+              verbatim $ keep /> txt $ CElem (unesc (fakeElem x))
+
+          fakeElem :: Content -> Element
+          fakeElem x = Elem "fake" [] [x]
+
+          unesc :: Element -> Element
+          unesc = xmlUnEscape stdXmlEscaper
+
+stripUnicodeBOM :: String -> String
+stripUnicodeBOM ('\xef':'\xbb':'\xbf':x) = x
+stripUnicodeBOM x = x
+
+{- | Removes potentially problematic or malicious stuff -}
+sanitize :: String -> String
+sanitize = strip . map sanitizer
+    where sanitizer c 
+              | c `elem` "\n\r\0\t" = ' '
+              | otherwise = c
+
+getContent (Document _ _ e _) = CElem e
diff --git a/INSTALL b/INSTALL
new file mode 100644
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,10 @@
+Sorry, need more here...
+
+Try:
+
+ghc --make -o setup Setup.lhs
+./setup configure
+./setup build
+./setup install
+
+To run, you will need to have curl installed
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,7 @@
+#!/usr/bin/env runhugs
+arch-tag: Main setup script
+
+> import Distribution.Simple
+
+> main = defaultMain
+
diff --git a/Types.hs b/Types.hs
new file mode 100644
--- /dev/null
+++ b/Types.hs
@@ -0,0 +1,46 @@
+{- hpodder component
+Copyright (C) 2006-2007 John Goerzen <jgoerzen@complete.org>
+
+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
+-}
+
+{- |
+   Module     : Types
+   Copyright  : Copyright (C) 2006-2007 John Goerzen
+   License    : GNU GPL, version 2 or above
+
+   Maintainer : John Goerzen <jgoerzen@complete.org>
+   Stability  : provisional
+   Portability: portable
+
+Written by John Goerzen, jgoerzen\@complete.org
+
+-}
+module Types where
+import Data.ConfigFile
+
+data Command =
+    Command {cmdname :: String,
+             cmddescrip :: String,
+             execcmd :: [String] -> Maybe FilePath -> ConfigParser -> IO ()}
+
+data Message = Message {
+      sId :: String,
+      sSender :: String,
+      sRecipient :: String,
+      sText :: String,
+      sDate :: String
+    } deriving (Eq, Read, Show, Ord)
+
diff --git a/Utils.hs b/Utils.hs
new file mode 100644
--- /dev/null
+++ b/Utils.hs
@@ -0,0 +1,119 @@
+{-
+Copyright (C) 2006-2008 John Goerzen <jgoerzen@complete.org>
+
+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
+-}
+
+{- |
+   Module     : Utils
+   Copyright  : Copyright (C) 2006-2008 John Goerzen
+   License    : GNU GPL, version 2 or above
+
+   Maintainer : John Goerzen <jgoerzen@complete.org>
+   Stability  : provisional
+   Portability: portable
+
+Written by John Goerzen, jgoerzen\@complete.org
+
+-}
+
+module Utils where
+import System.Console.GetOpt
+import Types
+import System.Exit
+import System.IO
+import Data.ConfigFile
+import Text.Regex.Posix
+import Data.Either.Utils(forceEither)
+import Network.URI
+import Data.Maybe.Utils
+import Text.Printf
+
+simpleCmd :: String             -- ^ Command name
+          -> String             -- ^ Command description
+          -> String             -- ^ Command help text
+          -> [OptDescr (String, String)] -- ^ Option descriptions
+          -> (Maybe String -> ConfigParser -> ([(String, String)], [String]) -> IO ()) -- ^ Function to call 
+          -> (String, Command)
+simpleCmd name descrip helptext optionsinp func =
+    (name, Command {cmdname = name, cmddescrip = descrip,
+                    execcmd = worker})
+    where options =
+              optionsinp ++ [Option "" ["help"] (NoArg ("help", "")) "Display this help"]
+          worker argv cpath gi =
+              case getOpt RequireOrder options argv of
+                (o, n, []) -> 
+                    if (lookup "help" o == Just "") 
+                       then usageerror []
+                       else func cpath gi (o, n)
+                (_, _, errors) -> usageerror (concat errors)
+          usageerror errormsg =
+              do putStrLn $ "Error processing arguments for command " ++ 
+                          name ++ ":"
+                 putStrLn errormsg
+                 putStrLn (usageInfo header options)
+                 putStrLn helptext
+                 exitFailure
+          header = "Available command-options for " ++ name ++ " are:\n"
+                                                               
+{-
+lock func =
+    do appdir <- getAppDir
+       lockh <- openFile (appdir ++ "/.lock") WriteMode
+       lockfd <- handleToFd lockh
+       catch (placelock lockfd) errorhandler
+       r <- finally func (releaselock lockfd)
+       return r
+
+    where placelock lockfd = setLock lockfd (WriteLock, AbsoluteSeek, 0, 0)
+          releaselock lockfd = do
+               setLock lockfd (Unlock, AbsoluteSeek, 0, 0)
+               closeFd lockfd
+          errorhandler _ =
+              do putStrLn "Aborting because another twidge is already running"
+                 exitFailure
+-}
+
+ex_tempfail = 75
+ex_permfail = 69
+
+permFail :: String -> IO a
+permFail msg =
+    do hPutStrLn stderr msg
+       exitWith (ExitFailure ex_permfail)
+
+serverHost cp = host
+    where urlbase = forceEither $ get cp "DEFAULT" "urlbase"
+          uri = forceMaybeMsg "genMsgId parseURI" $ parseURI urlbase
+          host = uriRegName . forceMaybeMsg "genMsgId uriauth" . 
+                 uriAuthority $ uri
+
+genMsgId :: String -> Message -> ConfigParser -> String
+genMsgId section m cp =
+    printf "<%s.%s.%s@%s.%s.twidge>" (sId m) (sSender m) (sRecipient m)
+               section (serverHost cp) 
+-- FIXME: escape periods in serverHost
+
+{- | Parses a message id, returning (Message, host, section).
+The sText and sDate fiels will be empty. -}
+parseMsgId :: String -> Maybe (Message, String, String)
+parseMsgId msgid =
+    case msgid =~ repat of
+      [[_, id, sender, recipient, section, host]] -> 
+          Just (Message {sId = id, sSender = sender, sRecipient = recipient, 
+                         sText = "", sDate = ""},
+                host, section)
+      _ -> Nothing
+    where repat = "^<([^.@]+)\\.([^.@]+)\\.([^@.]*)@([^.]+)\\.(.+)\\.twidge>"
diff --git a/doc/SConstruct b/doc/SConstruct
new file mode 100644
--- /dev/null
+++ b/doc/SConstruct
@@ -0,0 +1,30 @@
+# Copyright (C) 2004-2006 John Goerzen <jgoerzen@complete.org>
+# <jgoerzen@complete.org>
+#
+#    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
+
+env = Environment()
+env.Export("env")
+env.Append(MASTERBASE = 'twidge', TOPNODE = 'twidge', DOINDEX = 1,
+        PDFARGS = '-s local=printlocal')
+
+execfile('sgml-common/SConstruct')
+
+manpages = []
+for manpage in ['twidge']:
+    manpages.append(env.MAN('%s.1' % manpage, 'man.%s.sgml' % manpage))
+env.Alias('manpages', manpages)
+
+
diff --git a/doc/local.dsl b/doc/local.dsl
new file mode 100644
--- /dev/null
+++ b/doc/local.dsl
@@ -0,0 +1,41 @@
+<!DOCTYPE style-sheet PUBLIC "-//James Clark//DTD DSSSL Style Sheet//EN" [
+<!ENTITY dbstyle PUBLIC "-//GtkViaAlcove//DOCUMENT Gtk-doc HTML Stylesheet//EN" CDATA DSSSL>
+]>
+
+<style-sheet>
+<style-specification use="gtk">
+<style-specification-body>
+
+(define (toc-depth nd)
+  (if (string=? (gi nd) (normalize "book"))
+      1
+      1))
+(define %generate-article-toc% #t)
+
+;; Don't split up the doc as much.
+(define (chunk-element-list)
+  (list (normalize "preface")
+        (normalize "chapter")
+        (normalize "appendix")
+        (normalize "article")
+        (normalize "glossary")
+        (normalize "bibliography")
+        (normalize "index")
+        (normalize "colophon")
+        (normalize "setindex")
+        (normalize "reference")
+        (normalize "refentry")
+        (normalize "part")
+        (normalize "book") ;; just in case nothing else matches...
+        (normalize "set")  ;; sets are definitely chunks...
+        ))
+
+
+</style-specification-body>
+</style-specification>
+<external-specification id="gtk" document="dbstyle">
+</style-sheet>
+
+<!--
+# arch-tag: web stylesheet for documentation
+-->
diff --git a/doc/man.twidge.sgml b/doc/man.twidge.sgml
new file mode 100644
--- /dev/null
+++ b/doc/man.twidge.sgml
@@ -0,0 +1,21 @@
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
+<!ENTITY twidge "<application>twidge</application>">
+<!ENTITY man.twidge SYSTEM "twidge-manpage.sgml">
+
+    ]>
+
+    <reference>
+  <title>twidge Manual</title>
+  
+  &man.twidge;
+
+    </reference>
+
+
+    <!--
+    Local Variables:
+    mode: sgml
+    sgml-set-face: T
+    End:
+
+    -->
diff --git a/doc/printlocal.dsl b/doc/printlocal.dsl
new file mode 100644
--- /dev/null
+++ b/doc/printlocal.dsl
@@ -0,0 +1,25 @@
+<!DOCTYPE style-sheet PUBLIC "-//James Clark//DTD DSSSL Style Sheet//EN" [
+<!ENTITY dbstyle PUBLIC "-//Norman Walsh//DOCUMENT DocBook Print Stylesheet//EN" CDATA DSSSL>
+]>
+
+<style-sheet>
+<style-specification use="docbook">
+<style-specification-body>
+
+(define tex-backend #t)
+(define bop-footnotes #t)
+(define %two-side% #t)
+(define %footnote-ulinks% #t)
+
+;    (declare-characteristic heading-level
+;          "UNREGISTERED::James Clark//Characteristic::heading-level" 4)
+
+
+</style-specification-body>
+</style-specification>
+<external-specification id="docbook" document="dbstyle">
+</style-sheet>
+
+<!--
+# arch-tag: print stylesheet for manual
+-->
diff --git a/doc/sgml-common/COPYING b/doc/sgml-common/COPYING
new file mode 100644
--- /dev/null
+++ b/doc/sgml-common/COPYING
@@ -0,0 +1,342 @@
+		    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.
+
+		    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.)
+
+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 noncommercial 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.
+
+  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.
+
+  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
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  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 program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+# arch-tag: License for sgml-common
diff --git a/doc/sgml-common/COPYRIGHT b/doc/sgml-common/COPYRIGHT
new file mode 100644
--- /dev/null
+++ b/doc/sgml-common/COPYRIGHT
@@ -0,0 +1,39 @@
+Copyright for all code except ps2epsi
+-------------------------------------
+# Copyright (C) 2002, 2003 John Goerzen
+# <jgoerzen@complete.org>
+#
+#    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
+
+Copyright for ps2epsi
+---------------------
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU 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, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+
+My local changes were to the page size.
+
+# arch-tag: Copyright statements and information for sgml-common
diff --git a/doc/sgml-common/ChangeLog b/doc/sgml-common/ChangeLog
new file mode 100644
--- /dev/null
+++ b/doc/sgml-common/ChangeLog
@@ -0,0 +1,265 @@
+# do not edit -- automatically generated by arch changelog
+# arch-tag: automatic-ChangeLog--jgoerzen@complete.org--projects/sgml-common--head--1.0
+#
+
+2004-05-21 14:28:00 GMT	John Goerzen <jgoerzen@complete.org>	patch-20
+
+    Summary:
+      Fixed text target
+    Revision:
+      sgml-common--head--1.0--patch-20
+
+
+    modified files:
+     ChangeLog SConstruct
+
+
+2004-02-27 15:22:59 GMT	John Goerzen <jgoerzen@complete.org>	patch-19
+
+    Summary:
+      Added manpage generation support
+    Revision:
+      sgml-common--head--1.0--patch-19
+
+
+    modified files:
+     ChangeLog SConstruct
+
+
+2004-02-03 19:50:11 GMT	John Goerzen <jgoerzen@complete.org>	patch-18
+
+    Summary:
+      More clearing up of REs
+    Revision:
+      sgml-common--head--1.0--patch-18
+
+
+    modified files:
+     ChangeLog SConstruct
+
+
+2004-02-03 19:40:22 GMT	John Goerzen <jgoerzen@complete.org>	patch-17
+
+    Summary:
+      Tightened up re for finding image tags
+    Revision:
+      sgml-common--head--1.0--patch-17
+
+
+    modified files:
+     ChangeLog SConstruct
+
+
+2004-02-03 19:28:03 GMT	John Goerzen <jgoerzen@complete.org>	patch-16
+
+    Summary:
+      More changes to support scanner
+    Revision:
+      sgml-common--head--1.0--patch-16
+
+    Removed outdated code and made more bugfixes relevant to documents without
+    images or with only pre-generated images.
+
+    new files:
+     ChangeLog
+
+    modified files:
+     SConstruct
+
+
+2004-02-03 18:41:51 GMT	John Goerzen <jgoerzen@complete.org>	patch-15
+
+    Summary:
+      HTML gen now basically functional with scanning
+    Revision:
+      sgml-common--head--1.0--patch-15
+
+
+    modified files:
+     SConstruct
+
+
+2004-02-03 17:41:51 GMT	John Goerzen <jgoerzen@complete.org>	patch-14
+
+    Summary:
+      Auto-scanning is now close for PDFs.
+    Revision:
+      sgml-common--head--1.0--patch-14
+
+
+    modified files:
+     SConstruct
+
+
+2004-02-03 16:59:02 GMT	John Goerzen <jgoerzen@complete.org>	patch-13
+
+    Summary:
+      Scanners starting to work
+    Revision:
+      sgml-common--head--1.0--patch-13
+
+
+    modified files:
+     SConstruct
+
+
+2004-02-03 16:22:51 GMT	John Goerzen <jgoerzen@complete.org>	patch-12
+
+    Summary:
+      Cleaned up HTML situation
+    Revision:
+      sgml-common--head--1.0--patch-12
+
+
+    modified files:
+     SConstruct
+
+
+2004-02-02 22:33:50 GMT	John Goerzen <jgoerzen@complete.org>	patch-11
+
+    Summary:
+      SConstruct file now working
+    Revision:
+      sgml-common--head--1.0--patch-11
+
+
+    modified files:
+     SConstruct
+
+
+2004-02-02 22:07:27 GMT	John Goerzen <jgoerzen@complete.org>	patch-10
+
+    Summary:
+      Fixed nasty PNG gen bug
+    Revision:
+      sgml-common--head--1.0--patch-10
+
+
+    modified files:
+     SConstruct
+
+
+2004-02-02 21:57:32 GMT	John Goerzen <jgoerzen@complete.org>	patch-9
+
+    Summary:
+      Checkpointing some more...
+    Revision:
+      sgml-common--head--1.0--patch-9
+
+
+    modified files:
+     SConstruct
+
+
+2004-02-02 21:37:29 GMT	John Goerzen <jgoerzen@complete.org>	patch-8
+
+    Summary:
+      Checkpointing some more...
+    Revision:
+      sgml-common--head--1.0--patch-8
+
+
+    modified files:
+     SConstruct
+
+
+2004-02-02 20:19:02 GMT	John Goerzen <jgoerzen@complete.org>	patch-7
+
+    Summary:
+      Checkpointing
+    Revision:
+      sgml-common--head--1.0--patch-7
+
+
+    modified files:
+     SConstruct
+
+
+2004-02-02 19:18:40 GMT	John Goerzen <jgoerzen@complete.org>	patch-6
+
+    Summary:
+      Added Plucker
+    Revision:
+      sgml-common--head--1.0--patch-6
+
+
+    modified files:
+     SConstruct
+
+
+2004-02-02 19:09:16 GMT	John Goerzen <jgoerzen@complete.org>	patch-5
+
+    Summary:
+      Added cleanup rules
+    Revision:
+      sgml-common--head--1.0--patch-5
+
+
+    modified files:
+     SConstruct
+
+
+2004-02-02 18:58:11 GMT	John Goerzen <jgoerzen@complete.org>	patch-4
+
+    Summary:
+      sources now checks only chapters/ to prevent dep cycle with index
+    Revision:
+      sgml-common--head--1.0--patch-4
+
+
+    modified files:
+     Makefile.common SConstruct {arch}/=tagging-method
+
+
+2004-02-02 17:51:27 GMT	John Goerzen <jgoerzen@complete.org>	patch-3
+
+    Summary:
+      Experimental SCons conversion
+    Revision:
+      sgml-common--head--1.0--patch-3
+
+
+    new files:
+     SConstruct
+
+
+2003-10-21 20:24:04 GMT	John Goerzen <jgoerzen@complete.org>	patch-2
+
+    Summary:
+      Added plain text generation target
+    Revision:
+      sgml-common--head--1.0--patch-2
+
+    Added plain text generation target
+    
+
+    modified files:
+     ./Makefile.common
+
+
+2003-09-10 14:27:58 GMT	John Goerzen <jgoerzen@complete.org>	patch-1
+
+    Summary:
+      Minor updates to PNG generation and gtk-doc icon locations
+    Revision:
+      sgml-common--head--1.0--patch-1
+
+
+    modified files:
+     Makefile.common
+
+
+2003-09-10 14:24:24 GMT	John Goerzen <jgoerzen@complete.org>	base-0
+
+    Summary:
+      initial import
+    Revision:
+      sgml-common--head--1.0--base-0
+
+    
+    (automatically generated log message)
+
+    new files:
+     COPYING COPYRIGHT Makefile.common ps2epsi
+
+
diff --git a/doc/sgml-common/Makefile.common b/doc/sgml-common/Makefile.common
new file mode 100644
--- /dev/null
+++ b/doc/sgml-common/Makefile.common
@@ -0,0 +1,229 @@
+# -*- Mode: makefile; -*-
+# arch-tag: Primary sgml-common top-level Makefile
+# Common Makefile for SGML documents
+#
+# Copyright (C) 2002, 2003 John Goerzen
+# <jgoerzen@complete.org>
+#
+#    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
+
+# The following variables should be set:
+# MASTERBASE -- basename of master file -- example: my-guide
+# BASICDEPS -- various dependencies of the master file.  For instance,
+#   this might include files included in the SGML.  It could also be empty.
+# TOPNODE -- Basename of top id for HTML link.
+
+MASTER := $(MASTERBASE).sgml
+FIGUREDIRS := $(wildcard figures/*)
+DOINDEX ?= yes
+
+######################################################################
+# Index generation
+######################################################################
+
+ifeq ($(DOINDEX), yes)
+
+INDEXSGMLFILE := index/index.sgml
+INDEXDATAFILE := index/HTML.index
+
+$(INDEXSGMLFILE): $(INDEXDATAFILE)
+	@echo " *** Generating SGML index from index list"
+	collateindex.pl -i ch.index -g -o index/index.sgml index/HTML.index
+
+$(INDEXDATAFILE): $(MASTER) $(BASICDEPS)
+#	jade -t sgml -d docbook.dsl -V html-index $(MASTER)
+#	jade -t sgml -V html-index $(MASTER)
+	@echo " *** Generating index list from document"
+	-rm -r index
+	mkdir index
+	collateindex.pl -i ch.index -N -o index/index.sgml
+	#mkdir html-temp
+	#docbook2html --output html-temp -V html-index $(MASTER)
+	docbook-2-html -O -V -O html-index $(HTMLARGS) $(MASTER)
+	mv $(MASTERBASE)-html/HTML.index index/
+	rm -r $(MASTERBASE)-html
+endif # DOINDEX
+
+######################################################################
+# Text generation
+######################################################################
+$(MASTERBASE).txt: $(MASTER) $(BASICDEPS) $(INDEXSGMLFILE)
+	@echo " *** Generating text output"
+	docbook2txt $(MASTER)
+
+######################################################################
+# PostScript generation
+######################################################################
+
+$(MASTERBASE).ps: $(MASTER) $(BASICDEPS) $(INDEXSGMLFILE) $(EPSFILES)
+	@echo " *** Generating PostScript output"
+# This works too: docbook2ps -V paper-size=Letter $(MASTER)
+	docbook-2-ps -q -O -V -O paper-size=Letter $(PSARGS) $(MASTER)
+
+######################################################################
+# Figure generation
+######################################################################
+
+%_1.epi: %.ps
+	$(get-epi)
+%_2.epi: %.ps
+	$(get-epi)
+%_3.epi: %.ps
+	$(get-epi)
+%_4.epi: %.ps
+	$(get-epi)
+%_5.epi: %.ps
+	$(get-epi)
+%_6.epi: %.ps
+	$(get-epi)
+%_7.epi: %.ps
+	$(get-epi)
+%_8.epi: %.ps
+	$(get-epi)
+%_9.epi: %.ps
+	$(get-epi)
+%_10.epi: %.ps
+	$(get-epi)
+%_11.epi: %.ps
+	$(get-epi)
+%_12.epi: %.ps
+	$(get-epi)
+
+%_1_l.epi: %.ps
+	$(get-epil)
+%_2_l.epi: %.ps
+	$(get-epil)
+%_3_l.epi: %.ps
+	$(get-epil)
+%_4_l.epi: %.ps
+	$(get-epil)
+%_5_l.epi: %.ps
+	$(get-epil)
+%_6_l.epi: %.ps
+	$(get-epil)
+%_7_l.epi: %.ps
+	$(get-epil)
+%_8_l.epi: %.ps
+	$(get-epil)
+%_9_l.epi: %.ps
+	$(get-epil)
+%_10_l.epi: %.ps
+	$(get-epil)
+%_11_l.epi: %.ps
+	$(get-epil)
+%_12_l.epi: %.ps
+	$(get-epil)
+
+%.png: %_l.epi
+	@echo " *** Generating PNG image for $<"
+	gs -q -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -r90 -dBATCH -dNOPAUSE \
+		-dSAFER -sOutputFile=$@ -sDEVICE=png16m $< -c showpage
+
+%.ps: %.pdf
+	pdftops $<
+
+######################################################################
+# HTML generation
+######################################################################
+
+define copy-figures-worker
+mkdir html/figures
+for DIRECTORY in $(FIGUREDIRS); do mkdir html/$$DIRECTORY; cp -v $$DIRECTORY/*.png html/$$DIRECTORY/; done
+endef
+
+define copy-figures
+$(if $(FIGUREDIRS),$(copy-figures-worker))
+endef
+
+html/index.html: $(MASTER) $(BASICDEPS) $(INDEXSGMLFILE) $(PNGFILES)
+	@echo " *** Generating HTML output"
+	-rm -r html
+	mkdir html
+	#docbook2html --output html $(MASTER)
+	docbook-2-html $(HTMLARGS) $(MASTER)
+	mv $(MASTERBASE)-html/* html/
+	rmdir $(MASTERBASE)-html
+	$(copy-figures)
+#	tidy -m html/*.html
+	ln -s $(TOPNODE).html html/index.html
+	-cp -v /usr/share/gtk-doc/data/*.png html/
+
+######################################################################
+# Cleaning
+######################################################################
+
+clean:
+	-rm -f `find . -name "*~"` `find . -name "*.png"` `find . -name "*.epi"`
+	-rm -r html-temp html index $(MASTERBASE).txt
+	-rm *.aux *.log *.dvi *.tex *.jtex *.ps *.html *.log *.out jadetex.cfg
+	-rm *.ps html/*.html figures/topology/*.epi figures/topology/*.png
+	-rm *.log *.pdb
+	-rm `find . -name ".ps"` `find . -name "*.epi"` *.pdf
+	-rm `find . -name "*.png"`
+
+######################################################################
+# Utility functions
+######################################################################
+
+GETPAGE=$(shell echo $(1) | sed -e "s/^.*_\([0-9]*\).epi/\\1/g")
+define get-epi
+@echo " *** Generating EPI image for $<"
+psselect -q $(call GETPAGE,$@) $< temp.ps
+psresize -w 6.375in -h 8.25in temp.ps temp2.ps
+../sgml-common/ps2epsi temp2.ps $@
+rm temp.ps temp2.ps
+endef
+
+GETPAGEL=$(shell echo $(1) | sed -e "s/^.*_\([0-9]*\)_l.epi/\\1/g")
+define get-epil
+@echo " *** Generating large EPI image for $<"
+psselect -q $(call GETPAGEL,$@) $< temp.ps
+psresize -w 8.5in -h 11in temp.ps temp2.ps
+../sgml-common/ps2epsi temp2.ps $@
+rm temp.ps temp2.ps
+endef
+
+
+pdf: $(MASTERBASE).pdf
+
+$(MASTERBASE).pdf: $(MASTERBASE).ps
+	ps2pdf14 $(MASTERBASE).ps
+
+plucker: $(MASTERBASE).pdb
+$(MASTERBASE).pdb: html
+	plucker-build --bpp=4 --compression=zlib --doc-name="$(MASTERBASE)" \
+	-H file:`pwd`/html/index.html -M 5 \
+	--maxheight=320 --maxwidth=310 \
+	--staybelow=file:`pwd`/html --title="$(MASTERBASE)" -p . \
+	-f $(MASTERBASE)
+
+###########################################################################
+# These are obsolete but should still work.
+###########################################################################
+
+
+$(MASTERBASE).dvi: $(MASTERBASE).tex
+	@echo " *** Generating DVI file."
+	jadetex unix-guide.tex
+	jadetex unix-guide.tex
+	jadetex unix-guide.tex
+
+$(MASTERBASE).tex: $(MASTER) $(BASICDEPS) $(INDEXSGMLFILE)
+	@echo " *** Generating TeX files."
+	docbook2tex -V paper-size=Letter $(MASTER)
+#	jade -t tex -V tex-backend -d \
+#		/usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \
+#		$(MASTER)
+
diff --git a/doc/sgml-common/SConstruct b/doc/sgml-common/SConstruct
new file mode 100644
--- /dev/null
+++ b/doc/sgml-common/SConstruct
@@ -0,0 +1,208 @@
+# vim: set filetype=python :
+# arch-tag: general-purpose SCons build file for sgml-common
+
+from glob import glob
+import os, re
+
+############################################################
+# Setup
+############################################################
+
+SConsignFile('.sconsign-master')
+#Import('env')
+d = env.Dictionary()
+if not 'JADE' in d:
+    d['JADE'] = 'jade'
+if not 'INDEXNODE' in d:
+    d['INDEXNODE'] = 'ch.index'
+if not 'GTKIMGPATH' in d:
+    d['GTKIMGPATH'] = '/usr/share/gtk-doc/data'
+if not 'PS2EPSI' in d:
+    d['PS2EPSI'] = '../sgml-common/ps2epsi'
+
+def removeindex(l):
+    while 'index/index.sgml' in l:
+        l.remove('index/index.sgml')
+
+master = d['MASTERBASE'] 
+mastersgml = master + '.sgml'
+sources = [mastersgml] + glob('*/*.sgml') + glob('*/*/*.sgml')
+removeindex(sources)
+db2htmlcmd = 'docbook-2-html -D $JADE ${HTMLARGS}  ${SOURCE}'
+db2htmlindexcmd = 'docbook-2-html -D $JADE -O -V -O html-index ${HTMLARGS} ${SOURCE}'
+
+##################################################
+# SCANNERS
+##################################################
+def recursescan(scanner, node, env):
+    result = scanner(node, env) 
+    retval = []
+    for item in result:
+        retval.append(item)
+        retval.extend(recursescan(scanner, item, env))
+    return retval
+
+SGML_includes_re = re.compile(r'<!ENTITY[^>]+SYSTEM[^>]+"(.+)"', re.M)
+def SGML_includes_scan(node, env, path):
+    ret = SGML_includes_re.findall(node.get_contents())
+    removeindex(ret)
+    return ret
+
+SGML_includes_scanner = Scanner(name = 'SGML_includes',
+    function = SGML_includes_scan, recursive = 1, skeys = ['.sgml', '.ent'])
+
+SGML_image_pdf_re = re.compile(r'<(graphic|imagedata).+?fileref="([^"]+\.pdf)"', re.S)
+SGML_image_png_re = re.compile(r'<(graphic|imagedata).+?fileref="([^"]+\.png)"', re.S)
+def SGML_image_scanner(node, env, path, arg):
+    root, ext = os.path.splitext(str(node))
+    contents = node.get_contents()
+    return SGML_includes_scan(node, env, path) + \
+            [os.getcwd() + '/' + x[1] for x in arg.findall(contents)]
+
+SGML_pdf_scanner = Scanner(name = 'SGML_pdf',
+         function = SGML_image_scanner, argument = SGML_image_pdf_re,
+         recursive = 1)
+SGML_png_scanner = Scanner(name = 'SGML_png',
+        function = SGML_image_scanner, argument = SGML_image_png_re,
+        recursive = 1)
+
+##################################################
+# BUILDERS
+##################################################
+
+#### PLAIN TEXT
+Btxt = Builder(action="docbook2txt $SOURCE", src_suffix='.sgml', suffix='.txt')
+
+#### PDF / POSTSCRIPT
+Bpdf = Builder(action="docbook-2-pdf -D ${JADE} -q -O -V -O paper-size=Letter ${PDFARGS} ${SOURCE}",
+        src_suffix='.sgml', suffix='.pdf')
+Bpdf2ps = Builder(action="pdftops ${SOURCE}", src_suffix='.pdf', suffix='.ps')
+
+#### MAN PAGES
+# FIXME: test this
+Bman = Builder(action="docbook2man $SOURCE", src_suffix='.sgml', suffix='.1')
+
+#### HTML
+Bhtml = Builder(action = [ \
+        'if test -d ${TARGET.dir} ; then rm -r ${TARGET.dir} ; fi',
+        'mkdir ${TARGET.dir}',
+        db2htmlcmd,
+        'mv ${MASTERBASE}-html/* ${TARGET.dir}/',
+        'rmdir ${MASTERBASE}-html',
+        'ln -s ${TOPNODE}.html ${TARGET.dir}/index.html',
+        'cp ${GTKIMGPATH}/*.png ${TARGET.dir}/'])
+
+#### PNG
+Bepip2png = Builder(action = 'gs -q -dTextAlphaBits=4 -dGraphicsAlphaBits=4 ' +\
+        '-r90 -dBATCH -dNOPAUSE -dSAFER -sOutputFile=$TARGET ' + \
+        '-sDEVICE=png16m $SOURCE -c showpage', suffix='.png', src_suffix='.pngepi')
+
+#### EPI from PS
+def getpagenumfromname(target, source, env, for_signature):
+    return re.search('^.*_(\d+)\.(png){0,1}epi$', str(target[0])).group(1)
+d['GETPAGE'] = getpagenumfromname
+
+Aps2epi = Action(['psselect -q ${GETPAGE} $SOURCE temp.ps',
+        'psresize -w ${WIDTH} -h ${HEIGHT} temp.ps temp2.ps',
+        '$PS2EPSI temp2.ps $TARGET',
+        'rm temp.ps temp2.ps'])
+Bps2epi = Builder(action=Aps2epi, src_suffix='.ps', suffix='.epi')
+Bps2epip = Builder(action=Aps2epi, src_suffix='.ps', suffix='.pngepi')
+Bepi2pdf = Builder(action="epstopdf -o=${TARGET} ${SOURCE}", suffix='.pdf',
+        src_suffix='.epi')
+
+#### PLUCKER
+Bplucker = Builder(action = 'plucker-build --bpp=4 --compression=zlib ' + \
+        '--doc-name="${MASTERBASE}" -H file:${SOURCE.abspath} -M 5 ' + \
+        '--maxheight=320 --maxwidth=310 --staybelow=file:`pwd`/${SOURCE.dir} ' + \
+        '--title="${MASTERBASE}" -p . -f ${MASTERBASE}')
+
+##################################################
+# General setup
+##################################################
+
+env.Append(BUILDERS = {'Text': Btxt, 'PDF2PS': Bpdf2ps, 'PDF': Bpdf, 'HTML': Bhtml,
+        'Plucker': Bplucker, 'PS2EPI': Bps2epi, 'PS2EPIP': Bps2epip,
+        'EPI2PDF': Bepi2pdf, 'EPIP2PNG': Bepip2png, 'MAN': Bman})
+
+#### INDEX GENERATION
+if 'DOINDEX' in d:
+    Bindex = Builder(action = ['if test -d ${TARGET.dir}  ; then rm -r ${TARGET.dir} ; fi',
+        "mkdir ${TARGET.dir}",
+        "collateindex.pl -i $INDEXNODE -N -o $TARGET",
+        db2htmlindexcmd,
+        "mv ${MASTERBASE}-html/HTML.index ${TARGET.dir}/",
+        "rm -r ${MASTERBASE}-html",
+        "collateindex.pl -i $INDEXNODE -g -o $TARGET ${TARGET.dir}/HTML.index"])
+    env['BUILDERS']['Index'] = Bindex
+    index = env.Index('index/index.sgml', mastersgml)
+    env.Depends(index, sources)
+    env.Clean(index, 'index')
+    deps = sources + [index]
+else:
+    deps = sources
+
+##################################################
+# BUILD RULES
+###################################################
+# Text
+text = env.Text(mastersgml)
+env.Depends(text, deps)
+env.Alias('text', text)
+
+# PDF
+pdfsgml = File(mastersgml)
+pdf = env.PDF(pdfsgml)
+figsindoc = [x for x in recursescan(SGML_pdf_scanner, pdfsgml, env) if str(x).endswith('.pdf')]
+epipdf = []
+for file in figsindoc:
+    pdfname = re.sub('_\d+\.pdf$', '.pdf', str(file))
+    if pdfname == str(file):
+        # This is not a filename that fits our pattern; add unmodified.
+        epipdf.append(file)
+        continue
+    psfile = env.PDF2PS(source = pdfname)
+    epifile = env.PS2EPI(str(file).replace(".pdf", ".epi"), psfile,
+            WIDTH='6.375in', HEIGHT='8.25in')
+    epipdf.append(env.EPI2PDF(source = epifile))
+
+env.Depends(pdf, deps)
+env.Depends(pdf, epipdf)
+env.Alias('pdf', pdf)
+env.Clean(pdf, ['jadetex.cfg', '${MASTERBASE}.aux', '${MASTERBASE}.dvi',
+        '${MASTERBASE}.jtex', '${MASTERBASE}.log', '${MASTERBASE}.out',
+        'jade-out.fot'])
+
+# PS
+ps = env.PDF2PS(source = pdf)
+env.Alias('ps', ps)
+
+# HTML
+htmlsgml = File(mastersgml)
+buildhtml = env.HTML('html/index.html', htmlsgml)
+figsindoc = [x for x in recursescan(SGML_png_scanner, htmlsgml, env) if str(x).endswith('.png')]
+epipng = []
+for file in figsindoc:
+    pdfname = re.sub('_\d+\.png$', '.pdf', str(file))
+    if pdfname == str(file):
+        # This is not a filename that fits our pattern; add unmodified. 
+        epipng.append(file)
+        continue
+    psfile = env.PDF2PS(source = pdfname)
+    epifile = env.PS2EPIP(str(file).replace(".png", ".pngepi"), psfile,
+            WIDTH='8.5in', HEIGHT='11in')
+    epipng.append(env.EPIP2PNG(source = epifile))
+
+env.Depends(buildhtml, epipng)
+env.Depends(buildhtml, deps)
+pnginstalls = env.InstallAs(['html/' + str(x) for x in epipng], epipng)
+env.Depends(pnginstalls, buildhtml)
+html = env.Alias('html', buildhtml)
+html = env.Alias('html', pnginstalls)
+env.Clean(buildhtml, 'html')
+
+# Plucker
+plucker = env.Plucker(master + '.pdb', 'html/index.html')
+env.Alias('plucker', plucker)
+
+env.Default(html)
diff --git a/doc/sgml-common/ps2epsi b/doc/sgml-common/ps2epsi
new file mode 100644
--- /dev/null
+++ b/doc/sgml-common/ps2epsi
@@ -0,0 +1,77 @@
+#!/bin/sh
+# $RCSfile: ps2epsi,v $ $Revision: 1.4.2.2 $
+# arch-tag: ps2epsi customized for sgml-common
+
+tmpfile=/tmp/ps2epsi$$
+
+export outfile
+
+if [ $# -lt 1 -o $# -gt 2 ]; then
+	echo "Usage: `basename $0` file.ps [file.epsi]" 1>&2
+	exit 1
+fi
+
+infile=$1;
+
+if [ $# -eq 1 ]
+then
+	case "${infile}" in
+	  *.ps)		base=`basename ${infile} .ps` ;;
+	  *.cps)	base=`basename ${infile} .cps` ;;
+	  *.eps)	base=`basename ${infile} .eps` ;;
+	  *.epsf)	base=`basename ${infile} .epsf` ;;
+	  *)		base=`basename ${infile}` ;;
+	esac
+	outfile=${base}.epsi
+else
+	outfile=$2
+fi
+
+ls -l ${infile} |
+awk 'F==1	{
+		cd="%%CreationDate: " $6 " " $7 " " $8;
+		t="%%Title: " $9;
+		f="%%For:" U " " $3;
+		c="%%Creator: Ghostscript ps2epsi from " $9;
+		next;
+		}
+	/^%!/	{next;}
+	/^%%Title:/	{t=$0; next;}
+	/^%%Creator:/	{c=$0; next;}
+	/^%%CreationDate:/	{cd=$0; next;}
+	/^%%For:/	{f=$0; next;}
+	!/^%/	{
+		print "/ps2edict 30 dict def";
+		print "ps2edict begin";
+		print "/epsititle (" t "\\n) def";
+		print "/epsicreator (" c "\\n) def";
+		print "/epsicrdt (" cd "\\n) def";
+		print "/epsifor (" f "\\n) def";
+		print "end";
+		exit(0);
+		}
+	' U="$USERNAME$LOGNAME"  F=1 - F=2 ${infile} >$tmpfile
+
+gs -q -dNOPAUSE -dSAFER -dDELAYSAFER -r72 -sDEVICE=bit -sOutputFile=/dev/null $tmpfile ps2epsi.ps $tmpfile <${infile} 1>&2
+rm -f $tmpfile
+
+(
+cat << BEGINEPS
+save countdictstack mark newpath /showpage {} def /setpagedevice {pop} def
+%%EndProlog
+%%Page 1 1
+BEGINEPS
+
+cat ${infile} |
+sed -e '/^%%BeginPreview:/,/^%%EndPreview[^!-~]*$/d' -e '/^%!PS-Adobe/d'\
+    -e '/^%%[A-Za-z][A-Za-z]*[^!-~]*$/d' -e '/^%%[A-Za-z][A-Za-z]*: /d'
+
+cat << ENDEPS
+%%Trailer
+cleartomark countdictstack exch sub { end } repeat restore
+%%EOF
+ENDEPS
+
+) >> ${outfile}
+
+exit 0
diff --git a/doc/twidge-manpage.sgml b/doc/twidge-manpage.sgml
new file mode 100644
--- /dev/null
+++ b/doc/twidge-manpage.sgml
@@ -0,0 +1,872 @@
+  <refentry id="twidge">
+  <indexterm><primary>twidge</><secondary>Reference For</></>
+  <refentryinfo>
+    <address><email>jgoerzen@complete.org</email></address>
+    <author><firstname>John</firstname><surname>Goerzen</surname></author>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>twidge</refentrytitle>
+    <manvolnum>1</manvolnum>
+    <refmiscinfo>John Goerzen</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>twidge</refname>
+    <refpurpose>Microblogging client for Twitter, Identica</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>twidge</command>
+      <arg choice="opt">-d</arg>
+      <arg choice="opt">-c <replaceable>FILE</replaceable></arg>
+      <arg choice="plain"><replaceable>command</replaceable></arg>
+      <arg choice="opt"><replaceable>command_args</replaceable></arg>
+    </cmdsynopsis>
+    <cmdsynopsis>
+      <command>twidge</command>
+      <arg choice="plain">--help</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1 id="twidge.description">
+    <title>Description</title>
+    <para>
+      &twidge; is a client for microblogging sites such as 
+      <ulink url="http://twitter.com/">Twitter</ulink> and
+      <ulink url="http://identi.ca/">Identica</ulink> (identi.ca).
+      Microblogging sites let you post short one-paragraph updates,
+      follow the updates that your friends post, and interact with
+      everyone in the site in a conversation style.
+    </para>
+    <para>
+      &twidge; is a client to make working with microblogging sites
+      faster and easier.  It is extremely versatile, and can be
+      customized to work the way you want to work, and combined with
+      other tools to do just about anything.
+    </para>
+    <para>
+      &twidge; can be used quite nicely interactively from the shell.
+      It is useful directly as-is, and with simple shell aliases can
+      make a highly efficient system to do exactly what you want.  It
+      is perfectly capable of being your only client for
+      microblogging.
+    </para>
+    <para>
+      &twidge; also can be used in an automated way, via
+      <command>cron(1)</command>, or it can even integrate with your
+      email system.
+    </para>
+    <para>
+      A full list of &twidge; features, along with numerous
+      suggestions on how to use it, can be found at the &twidge;
+      website at <ulink
+      url="http://software.complete.org/twidge"></ulink>.
+    </para>
+    
+    <para>
+      But, if you'd like to get going RIGHT NOW, skip to the Quick
+      Start section below.  For now, I'll summarize a few &twidge;
+      features; a more comprehensive list is on the website.
+    </para>
+
+    <refsect2 id="twidge.features">
+      <title>Feature List</title>
+      <itemizedlist>
+        <listitem><para>Convenient, easy to learn, and fast command-line
+            interface (it's simple to do simple things, and
+            advanced things are possible)</para>
+        </listitem>
+
+        <listitem><para>This extensive manual, and numerous examples
+        on the website</para></listitem>
+
+        <listitem><para>Compatible with any microblogging service that
+            implements the Twitter API.  Tested with Twitter and
+            Identica (identi.ca).  Should be compatible with any other
+            system.
+          </para></listitem>
+
+        <listitem><para>Full support for reading the activity of your
+            friends, replies to you, and your own
+            activity.</para></listitem>
+
+        <listitem><para>Optional capability to remember what you have
+            seen already and suppress those updates in future
+            runs.</para></listitem>
+
+        <listitem><para>Optional automatic shortening of long URLs via tinyurl.com</para></listitem>
+        
+        <listitem><para>Optional integration with your email system --
+            send and receive updates via email.</para></listitem>
+
+        <listitem><para>Specifically designed to be friendly to use in
+            shell scripts.</para></listitem>
+
+        <listitem><para>Shell scripting makes it easy to do many
+            things, such as scheduling future updates (with
+            <command>at</command>), ignoring certain updates
+            (with <command>grep</command>), etc.</para></listitem>
+        
+        <listitem><para>Easy use of multiple accounts by having
+            multiple configuration profiles.   Mechanism lends itself
+            to shell aliases and tools.</para></listitem>
+
+        <listitem><para>Small, minimalist footprint</para>
+        </listitem>
+
+        <listitem><para>Output formats are easily parsed.  Input
+            formats are easily generated.  Configuration file format is
+            liberal and easy to generate.
+          </para></listitem>
+
+        <listitem><para>Robust error detection and recovery
+        throughout.
+          </para></listitem>
+      </itemizedlist>
+    </refsect2>
+
+  </refsect1>
+
+  <refsect1 id="twidge.quickstart">
+    <title>Quick Start</title>
+    <para>
+      This section will describe how a first-time &twidge; user can
+      get up and running quickly.  It assumes you already have
+      &twidge; compiled or installed on your system.  If not,
+      please follow the instructions in the
+      <filename>INSTALL</filename>
+      file in the
+      source distribution.
+    </para>
+    <para>
+      To get started, simply run <command>twidge setup</command> at your
+      shell prompt.  &twidge; will lead you through the first-time
+      configuration -- which is only two questions and completely
+      self-explanatory!
+    </para>
+    <para>
+      Now, let's start exploring.  <command>twidge lsrecent
+      -su</command> will show you the 20 most recent updates from the
+      people you follow.  After the first time, it will remember what
+      you've seen and only show you new updates.  In place of
+      <command>lsrecent</command>, you could substitue
+      <command>lsreplies</command> to replies to you.  You can also
+      run <command>twidge lsrecent --help</command>, or refer to this
+      document, for more details on the command.  In short, -s tells
+      the system to save what you've seen, and -u tells it to only
+      show you unseen items.
+    </para>
+    <para>
+      You can subscribe to updates from friends by running
+      <command>twidge follow
+      <replaceable>nick</replaceable></command>.  To subscribe to
+      updates for &twidge; itself, you'd run <command>twidge follow
+      unixtwidge</command>.  If you've had enough of updates from
+      someone, just use the <command>twidge unfollow</command> command.
+    </para>
+    <para>
+      Now, how about posting your own updates?  Just type
+      <command>twidge update</command>.  Type your update, and press
+      Enter when done.  Your update may scroll past the end of the
+      screen; don't worry about it.  Just don't hit Enter until you're
+      done.  You can also pass your update on the command line, taking
+      care to quote it if it contains spaces or shell characters.
+    </para>
+    <para>
+      That's the quick tour.  For many more examples, refer to the
+      website.
+    </para>
+  </refsect1>
+
+  <refsect1 id="twidge.options">
+    <indexterm><primary>Options (command-line)</primary></indexterm>
+    <title>Options</title>
+    <para>
+      &twidge; always is invoked with the name of a specific
+      operation, such as <option>update</option> or
+      <option>lsrecent</option>.  In &twidge;, these operations are
+      called <emphasis>commands</emphasis>.  Each command has its
+      own options, which are given after the command on the
+      &twidge; command line.  A full summary of each command's
+      options is given later in this manual.
+    </para>
+    
+    <para>
+      You may obtain a list of all commands with <command>twidge
+        lscommands</command>.  Help is available for any individual
+      command with <command>twidge <replaceable>command</replaceable>
+        --help</command>.  Global help is available with
+      <command>twidge --help</command>.
+    </para>
+    <refsect2 id="twidge.options.global">
+      <title>Global Options</title>
+      <para>
+        These options may be specified <emphasis>before</emphasis> any
+        command.
+      </para>
+      <variablelist>
+        <varlistentry><term>-c <replaceable>FILE</replaceable></term>
+                      <term>--config=<replaceable>FILE</replaceable></term>
+          <listitem><para>
+              By default, &twidge; uses
+              <filename>~/.twidgerc</filename> for configuration.
+              With this option, you can specify an alternate file.
+              &twidge; will read configuration information
+              from it.  In certain cases, &twidge; will also write to
+              it, such as with the <command>twidge setup</command>
+              command, or the -s option to one of the ls commands.
+            </para>
+          </listitem>
+        </varlistentry>
+        <varlistentry><term>-d</term><term>--debug</term>
+          <listitem><para>
+              Enables debugging output.  This verbose output helps you
+              learn what &twidge; is doing every step of the way and
+              diagnose any problems you may encounter.
+            </para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+  </refsect1>
+
+  <refsect1 id="twidge.commands">
+    <indexterm><primary>Commands</primary></indexterm>
+    <title>Commands in Twidge</title>
+    <para>
+      &twidge; has many different commands.  You must specify a
+      command when using &twidge;.
+      This section will discuss each command in
+      detail.  Note that all commands are case-sensitive and should be
+      <emphasis>given in lowercase</emphasis>.
+    </para>
+    <para>
+      All commands support the option <option>--help</option>.  Running
+      <command>twidge <replaceable>command</replaceable>
+      --help</command> will display information about the command and
+      its options.  Since all commands support this, it won't be
+      explicitly listed for each command below.
+    </para>
+
+  </refsect1>
+  <refsect1 id="twidge.commands.updatedisplay">
+    <title>Commands to Display Updates</title>
+    <cmdsynopsis>
+        <command>twidge ls<replaceable>*</replaceable></command>
+        <arg choice="opt">-a</arg>
+        <arg choice="opt">-l</arg>
+        <arg choice="opt">-s</arg>
+        <arg choice="opt">-u</arg>
+    </cmdsynopsis>
+    <para>
+      Several commands can display updates from yourself or others.
+      They all share a common syntax and common set of options.  The
+      commands are <command>lsarchive</command>,
+      <command>lsdm</command>, <command>lsdmarchive</command>,
+      <command>lsrecent</command>, and <command>lsreplies</command>.
+      Here are the common options:
+    </para>
+    
+    <variablelist>
+      <varlistentry><term>-a</term>
+        <term>--all</term>
+        <listitem><para>
+            Normally, microblogging sites return one "page" of results
+            -- typically, 20 or 100 results, depending on the
+            operation.  Specifying --all requests &twidge; to
+            continually send requests to the microblogging site until
+            it receives an entire set of information.  Normally this
+            is a bad idea, but when used in combination with -u --
+            especially if you have used -s before -- it can be a
+            useful way to poll for all new updates.
+          </para>
+          <para>
+            If not used carefully, this command can generate many
+            requests to the server and can use a significant amount of
+            bandwidth.  Microblogging sites have traffic limits, and
+            will not take kindly to this.  Be careful when you use
+            --all.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry><term>-e <replaceable>command</replaceable></term>
+        <term>--exec=<replaceable>command</replaceable></term>
+        <listitem><para>
+            Causes any normal output to be suppressed.  Instead, for
+            each item that is retrieved, call the given command.  The
+            command will be passed exactly four arguments.  Care
+            should be exercised when using them in shell scripts due
+            to the likelihood of the presence of shell
+            metacharacters.  The arguments are:
+          </para>
+          <orderedlist>
+            <listitem><para>
+                The update ID for the given update.
+              </para></listitem>
+            <listitem><para>
+                The username that created the update</para></listitem>
+            <listitem><para>
+                A suggested Message-ID for representing the update in
+                email.  Contains the enclosing angle brackets.
+              </para></listitem>
+            <listitem><para>
+                The update text itself.  Likely contains spaces and
+                other shell metacharacters.
+              </para></listitem>
+          </orderedlist>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry><term>-l</term>
+        <term>--long</term>
+        <listitem><para>
+            The default output format shows a nickname and a message,
+            formatted nicely for display.  Using this option causes
+            output to include these columns: message ID, sender nick,
+            recipient nick (empty unless a direct message), update
+            text, and date created.
+            The output format is suitable for machine parsing. 
+            horizontal tab character \t separates the columns,
+            and is guaranteed to appear nowhere else.  It is also
+            guaranteed that \n will appear nowhere within a line when
+            using -l.
+          </para>
+          <para>
+            The columns as presented are anticipated to remain
+            stable.  However, additional columns could be added to the
+            right of these columns in future versions of &twidge;.
+            Well-behaved parsers should be prepared to discard any
+            surplus columns to the right of those specified in this
+            document.
+          </para>
+        </listitem>
+        </varlistentry>
+
+      <varlistentry>
+        <term>-m <replaceable>address</replaceable></term>
+        <term>--mailto=<replaceable>address</replaceable></term>
+        <listitem><para>
+            Causes any normal output to be suppressed.  Instead, for
+            each item that is retrieved, generate an email and send it
+            to the given address.  The subject line will contain the
+            sender nickname and the first 30 characters of the text.
+            The body will contain
+            the entire text.  The message is sent by using the program
+            listed as sendmail in the configuration file.  If mailfrom
+            is listed in the configuration file, it is listed as the
+            from address and the name of the sender is listed as the
+            from comment.
+          </para>
+          <para>
+            A Message-ID designed to uniquely identify this update is
+            generated and included.  In addition, headers beginning
+            with X-Twidge- will be added, with various metadata.
+          </para>
+          <para>
+            It should be noted that a simple script could be used with
+            --exec to achieve this same purpose, but with greater
+            flexibility.
+            The functionality is built into &twidge; as well for
+            convenience.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry><term>-s</term>
+        <term>--saveid</term>
+        <listitem><para>
+            Saves the ID of the most recent fetched message for future
+            use with -u.  Requires write access to your configuration
+            file.
+          </para></listitem>
+      </varlistentry>
+
+      <varlistentry><term>-u</term>
+        <term>--unseen</term>
+        <listitem><para>
+            Causes the command to show only items since the last use
+            of -s with this particular command.  If -s has never
+            before been used with this command, has no effect.
+          </para>
+          <para>
+            As of September 2008, Identi.ca does not support the
+            since_id parameter that is required for this option to be
+            effective.  A future version of &twidge; may implement a
+            workaround.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>
+      Some of these options don't make sense together; for instance,
+      --long, --exec, and --mailto are mutually exclusive.  Results
+      are undefined if you use such options simultaneously.
+    </para>
+
+    <refsect2 id="twidge.commands.lsarchive">
+      <title>lsarchive</title>
+      <para>
+        Lists your own posts.
+      </para>
+    </refsect2>
+
+    <refsect2 id="twidge.commands.lsdm">
+      <title>lsdm</title>
+      <para>
+        Lists direct messages sent to you.  Note: identi.ca does not
+        support direct messages, and this will cause an error with
+        that service.
+      </para>
+    </refsect2>
+
+    <refsect2 id="twidge.commands.lsdmarchive">
+      <title>lsdmarchive</title>
+      <para>
+        Lists direct messages you have sent.  Note: identi.ca does not
+        support direct messages, and this command will cause an error
+        with that service.
+      </para>
+    </refsect2>
+
+    <refsect2 id="twidge.commands.lsrecent">
+      <title>lsrecent</title>
+      <para>
+        Lists recent posts made by you or the people you follow.
+      </para>
+    </refsect2>
+
+    <refsect2 id="twidge.commands.lsreplies">
+      <title>lsreplies</title>
+      <para>
+        Lists recent replies made by anyone on the microblogging site
+        to you.
+      </para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1 id="twidge.commands.displayinfo">
+    <title>Commands to Display Other Information</title>
+    <para>
+      These commands honor --all and --long like the other commands,
+      but do not honor --saveid or --unseen, as these options make no
+      sense.
+    </para>
+
+    <refsect2 id="twidge.commands.lsfollowers">
+      <title>lsfollowers</title>
+      <cmdsynopsis>
+        <command>twidge lsfollowers</command>
+        <arg choice="opt"><replaceable>options</replaceable></arg>
+        <arg choice="opt"><replaceable>username</replaceable></arg>
+      </cmdsynopsis>
+      <para>
+        Will slow a list of people following you.  If given an
+        optional username, shows the people that follow that user.  If
+        given --long, shows the IDs for each person.  You may need to
+        use --all to get a complete list, but again this may generate
+        significant traffic.
+      </para>
+    </refsect2>
+
+    <refsect2 id="twidge.commands.lsfollowing">
+      <title>lsfollowing</title>
+      <cmdsynopsis>
+        <command>twidge lsfollowing</command>
+        <arg choice="opt"><replaceable>options</replaceable></arg>
+        <arg choice="opt"><replaceable>username</replaceable></arg>
+      </cmdsynopsis>
+      <para>
+        Shows a list of the people you follow.  Arguments and actions
+        are the same as with lsfollowers.
+      </para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1 id="twidge.commands.action">
+    <title>Action Commands</title>
+    <para>
+      These commands perform an action on the server.
+    </para>
+
+
+    <refsect2 id="twidge.commands.dmsend">
+      <title>dmsend</title>
+      <cmdsynopsis>
+        <command>twidge dmsend</command>
+        <arg choice="plain"><replaceable>recipient</replaceable></arg>
+        <arg choice="opt"><replaceable>status</replaceable></arg>
+      </cmdsynopsis>
+      <para>
+        Causes &twidge; to send a new direct message to the designated
+        recipient.  If a
+        status is given on the command line, it must be presented as a
+        single argument to &twidge; by the use of shell quoting if
+        needed.  If no status is given on the command line, a single
+        line will be read from stdin and used as the status.
+      </para>
+      <para>
+        The maximum length of updates on various microblogging sites
+        is 140 characters.  Twidge will abort with an error if your
+        update attempt exceeds that length.
+      </para>
+      <para>
+        By default, Twidge will attempt to shorten URLs in your
+        updates via the TinyURL.com service.  You can disable this by
+        setting shortenurls = no in the [DEFAULT] or [dmsend] section of your
+        configuration file.
+      </para>
+    </refsect2>
+
+    <refsect2 id="twidge.commands.follow">
+      <title>follow</title>
+      <cmdsynopsis>
+        <command>twidge follow</command>
+        <arg choice="plain"><replaceable>username</replaceable></arg>
+      </cmdsynopsis>
+      <para>
+        This command will cause &twidge; to request that the
+        microblogging site add the given username to the list of
+        people you follow.  It is considered an error to attempt to
+        add someone you already follow.
+      </para>
+    </refsect2>
+
+    <refsect2 id="twidge.commands.setop">
+      <title>setup</title>
+      <cmdsynopsis><command>twidge setup</command></cmdsynopsis>
+      <para>
+        Enters the interactive first-time setup tool.
+      </para>
+    </refsect2>
+
+    <refsect2 id="twidge.commands.unfollow">
+      <title>unfollow</title>
+      <cmdsynopsis>
+        <command>twidge unfollow</command>
+        <arg choice="plain"><replaceable>username</replaceable></arg>
+      </cmdsynopsis>
+      <para>
+        This command will cause &twidge; to request that the
+        microblogging site remove the given username from the list of
+        people you follow.  It is considered an error to attempt to
+        remove someone you do not follow.
+      </para>
+    </refsect2>
+
+    <refsect2 id="twidge.commands.update">
+      <title>update</title>
+      <cmdsynopsis>
+        <command>twidge update</command>
+        <arg choice="opt"><replaceable>status</replaceable></arg>
+      </cmdsynopsis>
+      <cmdsynopsis>
+        <command>twidge update -r</command>
+      </cmdsynopsis>
+      <para>
+        Causes &twidge; to post a new status to the server.  If a
+        status is given on the command line, it must be presented as a
+        single argument to &twidge; by the use of shell quoting if
+        needed.  If no status is given on the command line, a single
+        line will be read from stdin and used as the status.
+      </para>
+      <para>
+        The maximum length of updates on various microblogging sites
+        is 140 characters.  Twidge will abort with an error if your
+        update attempt exceeds that length.
+      </para>
+      <para>
+        By default, Twidge will attempt to shorten URLs in your
+        updates via the TinyURL.com service.  You can disable this by
+        setting shortenurls = no in the [DEFAULT] or [update] section of your
+        configuration file.
+      </para>
+      <para>
+        When -r is given, &twidge; expects to read an RFC2822 email
+        message in stdin.  The body of the message will be used as the
+        content of the update.  The References header of the message
+        will be briefly scanned, and if appropriate, the reply-to
+        attribute of the update will reflect the Message-ID referred
+        to therein.  This way, if you use <command>twidge lsrecent
+        --mailto</command> to email recent items to you, then use your
+        email reply button to reply to them, &twidge; will link the
+        two on Twitter.  This linkage can be done even if you omit
+        @reply, but will likely confuse many people because most other
+        clients can't do this.  You should still include @reply.
+      </para>
+      <para>
+        It should be noted that, in an effort to minimize size and
+        complexity, &twidge; has an extremely simple email parser.
+        You should send it only plain text messages.  Do not use HTML,
+        RTF, attachments, or anything that would cause MIME headers to
+        appear in the RFC2822 body.  You probably want to turn off
+        your signature as well. &twidge; will convert newline
+        characters to spaces when processing your message body.  
+      </para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1 id="twidge.commands.other">
+    <title>Other Command</title>
+    
+    <refsect2 id="twidge.commands.lscommands">
+      <indexterm><primary>Commands</><secondary>lscommands</></>
+      <title>lscommands</title>
+      <cmdsynopsis><command>twidge lscommands</command></cmdsynopsis>
+      <para>
+        This command will display a list of all available &twidge;
+        commands along with a brief description of each.
+      </para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1 id="twidge.config">
+    <title>twidge Configuration File</title>
+    <para>
+      &twidge; has a configuration file in which you can set various
+      options.  This file normally lives under
+      <filename>~/.twidgerc</filename>.
+    </para>
+    <para>
+      The configuration file has multiple sections.  Each section has
+      a name and is introduced with the name in brackets.  Each
+      section has one or more options.
+    </para>
+    <para>
+      The section named DEFAULT is special in that it provides
+      defaults that will be used whenever an option can't be found
+      under a different section.  If you specify no section names,
+      DEFAULT is the assumed section.  Some items, such as username,
+      password, and urlbase are assumed to be in DEFAULT.
+    </para>
+    <para>
+      Let's start by looking at an example file, and then proceed to
+      examine all the options that are available.
+    </para>
+    <programlisting><![CDATA[
+[DEFAULT]
+
+; Username and password
+username = ilovetwidge
+
+password = secret
+
+; If your password contains a percent sign (%), list it twice (%%)
+
+; Path to server API interface -- no trailing slash
+urlbase = https://twitter.com
+
+shortenurls = yes
+
+; Last IDs seen by different commands.  Written to with -s.
+
+[lsarchive]
+lastid = 917383569
+
+[lsrecent]
+lastid = 914881241
+]]></programlisting>
+    <para>
+      Whenever &twidge; looks for information about a particular
+      option, it first checks to see if it can find that option in a
+      section for that option.  If not, it checks the
+      <option>DEFAULT</option> section.  If it still doesn't find an
+      answer, it consults its built-in defaults.  The only built-in
+      default at present is urlbase.
+    </para>
+    <refsect2 id="twidge.config.general">
+      <title>General Options</title>
+      <para>These are specified in the <option>DEFAULT</option>
+        section.
+      </para>
+      <variablelist>
+        <varlistentry><term>username</term>
+          <listitem><para>
+              Your username on the microblogging site.  No default.
+            </para></listitem>
+        </varlistentry>
+        <varlistentry><term>password</term>
+          <listitem><para>
+              Your password on the microblogging site.  No default.
+            </para></listitem>
+        </varlistentry>
+        <varlistentry><term>urlbase</term>
+          <listitem><para>
+              The URL to access the API of the microblogging site.
+              The default, <literal>https://twitter.com</literal> is
+              for Twitter.  To use Identica, you would specify
+              <literal>http://identi.ca/api</literal>.  Do not put a
+              trailing slash on this option.
+            </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>              
+
+    <refsect2 id="twidge.config.options">
+      <title>Per-Command Options</title>
+      <para>
+        These options may be specified in <option>DEFAULT</option> or
+        in a per-option section.  If placed in
+        <option>DEFAULT</option>, they will apply to all relevant options
+        unless overridden.
+      </para>
+      <variablelist>
+        <varlistentry><term>lastid</term>
+          <listitem><para>
+              Stores the last ID seen for commands that support -s or
+          -u.
+            </para></listitem>
+        </varlistentry>
+
+        <varlistentry><term>mailfrom</term>
+          <listitem><para>
+              Gives the email address to use for the From: header on
+              messages generated by &twidge;.  Normally specified in
+              [DEFAULT] so as to impact all commands.  If not given, 
+              &twidge; specifies no From: line, leaving the system's
+              mail transport to assign a default one.
+            </para>
+            <para>
+              Should contain a bare email address only -- no brackets,
+              parens, quotes, or comments.
+            </para>
+            <para>
+              When given, Twidge uses the nickname and this address
+              for the From line.  When not given, the nickname is
+              prepended to the Subject line.
+            </para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry><term>sendmail</term>
+          <listitem><para>
+              Stores the path to the sendmail executable on your
+              system, used for sending mail.  Normally specified in
+              [DEFAULT] so as to impact all commands.  Defaults to
+              /usr/sbin/sendmail.
+            </para></listitem>
+        </varlistentry>
+        
+        <varlistentry><term>shortenurls</term>
+          <listitem>
+            <para>
+              You may specify <literal>shortenurls</literal> in the
+              <literal>[update]</literal> section.  It defaults to yes.  If
+              set to no, will cause &twidge; to omit its attempt to shorten
+              URLs in your updates via <literal>tinyurl.com</literal>.
+            </para>
+          </listitem>
+        </varlistentry>
+
+      </variablelist>
+
+    </refsect2>
+
+  </refsect1>
+              
+
+  <refsect1 id="twidge.tips">
+    <title>Tips & Hints</title>
+    <para>
+      Here are a few tips and hints to make &twidge; more pleasant
+      for you.
+    </para>
+    <refsect2 id="twidge.tips.proxy">
+      <title>Going Through a Proxy</title>
+      <para>
+        If your connections must go through a proxy, you'll need to
+        set an environment variable.
+        If you use an
+        environment variable, your settings may will also impact other
+        applications -- and that's probably what you want.  See the
+        Environment section later for tips on doing that.
+      </para>
+    </refsect2>
+
+  </refsect1>
+
+  <refsect1 id="twidge.environment">
+    <title>Environment</title>
+    <para>
+      &twidge; does not read any environment variables directly.
+      However, it does pass on the environment to the
+      programs it calls, such as Curl.  This can be useful for
+      specifying proxies.  Please see
+      <application>curl</application>(1) for more details.
+    </para>
+  </refsect1>
+
+  <refsect1 id="twidge.conforming">
+    <title>Conforming To</title>
+    <itemizedlist>
+      <listitem><para>The <ulink
+      url="http://www.w3.org/XML/">Extensible Markup Language
+            (XML)</ulink> standard (W3C)</para></listitem>
+      </itemizedlist>
+  </refsect1>
+
+  <refsect1 id="twidge.copyright">
+    <title>Copyright</title>
+    <indexterm><primary>Copyright</></>
+    <para>
+      &twidge;, all code, documentation, files, and build scripts are
+      Copyright &copy; 2006-2008 John Goerzen.  All code, documentation,
+      sripts, and files are under the following license unless
+      otherwise noted:
+    </para>
+    <para>
+      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.
+    </para>
+    
+    <para>
+      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.
+    </para>
+    
+    <para>
+      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
+    </para>
+    <para>
+      The GNU General Public License is available in the file COPYING
+      in the source distribution.  Debian GNU/Linux users may find this in
+      /usr/share/common-licenses/GPL-2.
+    </para>
+    <para>
+      If the GPL is unacceptable for your uses, please e-mail me; alternative
+      terms can be negotiated for your project.
+    </para>
+  </refsect1>
+  
+  <refsect1 id="twidge.author">
+    <title>Author</title>
+    <para>
+      &twidge;, its modules, documentation, executables, and all
+      included files, except where noted, was written by
+      John Goerzen <email>jgoerzen@complete.org</email> and
+      copyright is held as stated in the COPYRIGHT section.
+    </para>
+  </refsect1>
+
+  <refsect1 id="twidge.seealso">
+    <title>See Also</title>
+    <para>
+      <application>curl</application>(1)
+    </para>
+    <para>
+      The &twidge; homepage at <ulink
+      url="http://software.complete.org/twidge"></>.
+    </para>
+  </refsect1>
+  
+  </refentry>
diff --git a/doc/twidge.sgml b/doc/twidge.sgml
new file mode 100644
--- /dev/null
+++ b/doc/twidge.sgml
@@ -0,0 +1,20 @@
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
+  <!ENTITY index SYSTEM "index/index.sgml">
+  <!ENTITY man.twidge SYSTEM "twidge-manpage.sgml">
+  <!ENTITY twidge "<application>twidge</application>">
+
+]>
+
+  <reference>
+    <title>twidge Manual</title>
+     &man.twidge;
+  </reference>
+
+<!--
+Local Variables:
+mode: sgml
+sgml-set-face: T
+End:
+
+-->
+
diff --git a/twidge.cabal b/twidge.cabal
new file mode 100644
--- /dev/null
+++ b/twidge.cabal
@@ -0,0 +1,52 @@
+Name: twidge
+Version: 0.99.0
+License: GPL
+Maintainer: John Goerzen <jgoerzen@complete.org>
+Author: John Goerzen
+Stability: Stable
+Copyright: Copyright (c) 2006-2008 John Goerzen
+license-file: COPYRIGHT
+extra-source-files: COPYING, INSTALL, doc/SConstruct,
+  doc/twidge-manpage.sgml, doc/twidge.sgml, doc/local.dsl,
+  doc/man.twidge.sgml, doc/printlocal.dsl, doc/sgml-common/COPYING,
+  doc/sgml-common/COPYRIGHT, doc/sgml-common/ChangeLog,
+  doc/sgml-common/Makefile.common, doc/sgml-common/SConstruct,
+  doc/sgml-common/ps2epsi
+homepage: http://software.complete.org/twidge
+Build-type: Simple
+Category: Network
+Synopsis: Unix Command-Line Twitter and Identica Client
+Description: twidge is a client for microblogging sites such as Twitter and
+ Identica (identi.ca). Microblogging sites let you post short
+ one-paragraph updates, follow the updates that your friends post, and
+ interact with everyone in the site in a conversation style.
+ .
+ twidge is a client to make working with microblogging sites faster
+ and easier. It is extremely versatile, and can be customized to work
+ the way you want to work, and combined with other tools to do just
+ about anything.
+ .
+ twidge can be used quite nicely interactively from the shell. It is
+ useful directly as-is, and with simple shell aliases can make a
+ highly efficient system to do exactly what you want. It is perfectly
+ capable of being your only client for microblogging.
+ .
+ twidge also can be used in an automated way, via cron(1), or it can
+ even integrate with your email system.
+ .
+ A full list of twidge features, along with numerous suggestions on
+ how to use it, can be found at the twidge website at
+ http://software.complete.org/twidge.
+
+Build-Depends: network, unix, parsec, MissingH>=1.0.0,
+ mtl, base, HaXml>=1.13.2, HaXml<1.19, hslogger,
+ ConfigFile, directory, HSH, Cabal, regex-posix
+
+Executable: twidge
+Main-Is: twidge.hs
+Other-Modules: Commands, Config, Download, FeedParser,
+        Types, Utils, 
+        Commands.Ls
+GHC-Options: -O2
+Extensions: ExistentialQuantification, OverlappingInstances,
+    UndecidableInstances
diff --git a/twidge.hs b/twidge.hs
new file mode 100644
--- /dev/null
+++ b/twidge.hs
@@ -0,0 +1,89 @@
+{- component
+Copyright (C) 2006-2008 John Goerzen <jgoerzen@complete.org>
+
+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
+-}
+
+{- |
+   Module     : Main
+   Copyright  : Copyright (C) 2006-2008 John Goerzen
+   License    : GNU GPL, version 2 or above
+
+   Maintainer : John Goerzen <jgoerzen@complete.org>
+   Stability  : provisional
+   Portability: portable
+
+Written by John Goerzen, jgoerzen\@complete.org
+
+-}
+
+import Config
+import System.Log.Logger
+import System.Log.Handler.Simple
+import System.Console.GetOpt.Utils
+import System.Console.GetOpt
+import System.Environment
+import Data.List
+import System.Exit
+import Commands
+import Types
+import Control.Monad
+import Data.ConfigFile(emptyCP)
+import System.IO
+import Paths_twidge(version)
+import Data.Version
+
+main = 
+    do updateGlobalLogger "" (setLevel INFO)
+       argv <- getArgs
+       
+       case getOpt RequireOrder options argv of
+         (o, n, []) -> worker o n
+         (_, _, errors) -> usageerror (concat errors) -- ++ usageInfo header options)
+       
+options = [Option "d" ["debug"] (NoArg ("d", "")) "Enable debugging",
+           Option "c" ["config"] (ReqArg (stdRequired "c") "FILE")
+                  "Use specified config file",
+           Option "" ["help"] (NoArg ("help", "")) "Display this help"]
+
+worker args commandargs =
+    do when (lookup "help" args == Just "") $ usageerror ""
+       when (lookup "d" args == Just "") 
+            (updateGlobalLogger "" (setLevel DEBUG))
+       handler <- streamHandler stderr DEBUG -- stdout has issues with HSH
+       updateGlobalLogger "" (setHandlers [handler])
+       let commandname = head cmdargs
+       case lookup commandname allCommands of
+         Just command -> 
+             do cp <- if commandname `elem` ["lscommands", "setup"] -- no config file needed
+                      then return emptyCP
+                      else loadCP (lookup "c" args)
+                execcmd command (tail cmdargs) (lookup "c" args) cp
+         Nothing -> usageerror ("Invalid command name " ++ commandname)
+       where cmdargs = case commandargs of
+                         [] -> ["help"]
+                         x -> x
+
+usageerror errormsg =
+    do putStrLn errormsg
+       putStrLn (usageInfo header options)
+       putStrLn "Run \"twidge lscommands\" for a list of available commands.\n\
+                \Run \"twidge command --help\" for help on a particular command.\n"
+       putStr $ "This is Twidge, version " ++ showVersion version
+       putStrLn $ ".  Copyright (c) 2008 John Goerzen"
+       exitFailure
+
+header = "Usage: twidge [global-options] command [command-options]\n\n\
+         \Available global-options are:\n"
