[med-svn] [yaggo] 05/07: New upstream version 1.5.9

Andreas Tille tille at debian.org
Thu Oct 5 20:00:31 UTC 2017


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository yaggo.

commit f058ab7ec993c2d5750aaae417de5624e4c3ad67
Author: Andreas Tille <tille at debian.org>
Date:   Thu Oct 5 21:47:20 2017 +0200

    New upstream version 1.5.9
---
 .gitignore                  |    3 +
 COPYING                     |  674 ++++++++++++++++++
 Makefile                    |   12 +
 README                      |    1 +
 README.md                   |  107 +++
 Rakefile                    |   51 ++
 bin/create_yaggo_one_file   |   45 ++
 bin/yaggo                   |   22 +
 debian/changelog            |   14 -
 debian/clean                |    1 -
 debian/compat               |    1 -
 debian/control              |   25 -
 debian/copyright            |   21 -
 debian/docs                 |    1 -
 debian/install              |    2 -
 debian/rules                |   18 -
 debian/source/format        |    1 -
 debian/watch                |    2 -
 lib/yaggo/dsl.rb            |  573 ++++++++++++++++
 lib/yaggo/general.rb        |  127 ++++
 lib/yaggo/library.rb        |  197 ++++++
 lib/yaggo/main.rb           |  152 +++++
 lib/yaggo/man_page.rb       |  449 ++++++++++++
 lib/yaggo/parser.rb         |  404 +++++++++++
 lib/yaggo/stub.rb           |   42 ++
 lib/yaggo/version.rb        |    1 +
 lib/yaggo/zsh_completion.rb |   94 +++
 license-header.txt          |   14 +
 setup.rb                    | 1585 +++++++++++++++++++++++++++++++++++++++++++
 test/Makefile               |   20 +
 test/count.cpp              |   33 +
 test/count_cmdline.yaggo    |   84 +++
 test/test_errno.cc          |   12 +
 test/test_errno.yaggo       |    7 +
 34 files changed, 4709 insertions(+), 86 deletions(-)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..17bec8c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+yaggo-*.tar.gz
+.tup
+pkg
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  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.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  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.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     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
+state 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 3 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, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU 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 Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..73d681a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,12 @@
+prefix ?= /usr/local
+
+all: bin/create_yaggo_one_file
+	ruby bin/create_yaggo_one_file ./yaggo
+
+install: all
+	mkdir -p $(prefix)/bin
+	mkdir -p $(prefix)/share/doc/yaggo
+	mkdir -p $(prefix)/share/man/man1
+	cp ./yaggo $(prefix)/bin
+	cp ./README.md $(prefix)/share/doc/yaggo
+	./yaggo -m $(prefix)/share/man/man1/yaggo.1
diff --git a/README b/README
new file mode 100644
index 0000000..96dc92f
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+See README.md
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9ab9d10
--- /dev/null
+++ b/README.md
@@ -0,0 +1,107 @@
+# What is yaggo?
+
+Yaggo is a tool to generate command line parsers for C++. Yaggo stands
+for "Yet Another GenGetOpt" and is inspired by [GNU Gengetopt](https://www.gnu.org/software/gengetopt/gengetopt.html).
+
+It reads a configuration file describing the switches and argument for
+a C++ program and it generates one header file that parses the command
+line using getopt_long(3). See the Example section below for more details.
+
+# Installation
+
+## Quick and easy
+
+Download the standalone script from the [release](https://github.com/gmarcais/yaggo/releases)
+and copy it into a directory in your PATH (e.g. `~/bin`)
+
+From the source tree, the same is achieved with:
+
+```Shell
+make DEST=$HOME/bin
+```
+
+## As a gem
+
+Download the gem from the [release](https://github.com/gmarcais/yaggo/releases) and install it
+with `sudo gem install ./yaggo-1.5.8.gem` (adjust the version!).
+
+Similarly, from the source tree, first generate the gem
+and then install it. For example here with version 1.5.3:
+
+```Shell
+rake gem
+sudo gem install ./pkg/yaggo-1.5.3.gem
+```
+
+# Documentation
+
+After installation, documentation is available with `yaggo --man`.
+
+# Simple example
+
+Given the following configuration file 'parser.yaggo':
+
+```Ruby
+purpose "Demonstrate yaggo capabilities"
+description "This simple configuration file shows some of the capabilities of yaggo.
+This is supposed to be a longer description of the program.
+"
+
+option("f", "flag") {
+  description "This is a flag"
+  off
+}
+option("i", "int") {
+  description "This take an integer"
+  int
+  default 20
+}
+arg("path") {
+  description "Path to file"
+  c_string
+}
+```
+
+The following C++ program ('parser.cc') does switch parsing, generate
+appropriate errors and has an automatically generated help (accessible
+with '-h' or '--help').
+
+```C
+#include <iostream>
+#include "parser.hpp"
+
+int main(int argc, char* argv[]) {
+  parser args(argc, argv); // Does all the parsing
+
+  std::cout << "--flag " << (args.flag_flag ? "not passed" : "passed") << "\n"
+            << "--int: " << args.int_arg << "\n"
+            << "path: " << args.path_arg << "\n";
+
+  return 0;
+}
+```
+
+All of this is compiled with:
+
+```Shell
+yaggo parser.yaggo
+g++ -o parser parser.cc
+```
+
+Then, './parser --help' returns:
+
+```
+Usage: parser [options] path:string
+
+Demonstrate yaggo capabilities
+
+This simple configuration file shows some of the capabilities of yaggo.
+This is supposed to be a longer description of the program.
+
+Options (default value in (), *required):
+ -f, --flag                               This is a flag (false)
+ -i, --int=int                            This take an integer (20)
+ -U, --usage                              Usage
+ -h, --help                               This message
+ -V, --version                            Version
+```
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..99ab75c
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+require 'rubygems'
+require 'rubygems/package_task'
+load 'lib/yaggo/version.rb'
+load 'bin/create_yaggo_one_file'
+
+spec = Gem::Specification.new do |s|
+  s.name        = "yaggo"
+  s.version     = $yaggo_version
+  s.platform    = Gem::Platform::RUBY
+  s.authors     = ["Guillaume Marçais"]
+  s.email       = ["gmarcais at umd.edu"]
+  s.homepage    = "https://github.com/gmarcais/yaggo"
+  s.summary     = "Yet Another Generator for getopt"
+  s.licenses    = ['GPL-3.0']
+  s.description = "Yaggo defines a DSL to generate GNU compatible command line parsers for C++ using getopt."
+
+  s.required_rubygems_version = ">= 1.3.6"
+
+  # If you need to check in files that aren't .rb files, add them here
+  s.files        = Dir["{lib}/**/*.rb", "bin/*", "LICENSE", "*.md"]
+  s.require_path = 'lib'
+
+  # If you need an executable, add it here
+  s.executables = ["yaggo"]
+end
+
+Gem::PackageTask.new(spec) do |pkg|
+  pkg.need_zip = false
+  pkg.need_tar = true
+end
+
+desc "Run yaggo"
+task :yaggo do |t|
+  ARGV.shift if ARGV[0] == "yaggo"
+  ruby("-Ilib", "./bin/yaggo", *ARGV)
+end
+
+task :default => :yaggo
+
+desc "Create a distribution tarball"
+task :dist do |t|
+  system("tar", "-zc", "-f", "yaggo-#{spec.version}.tar.gz",
+         "--transform", "s|^|yaggo-#{spec.version}/|",
+         "README", "COPYING", "setup.rb", "bin", "lib")
+end
+
+desc "Create a single file executable"
+task :exec do |t|
+  create_binary("lib/yaggo/main.rb", "yaggo")
+end
diff --git a/bin/create_yaggo_one_file b/bin/create_yaggo_one_file
new file mode 100644
index 0000000..15bd522
--- /dev/null
+++ b/bin/create_yaggo_one_file
@@ -0,0 +1,45 @@
+#! /usr/bin/env ruby
+
+def create_binary src, dest
+  to_load = []
+  loaded = {}
+  open(dest, "w", 0755) do |wfd|
+    wfd.puts(<<'EOS')
+#! /usr/bin/env ruby
+
+if !$load_self
+  $load_self = true
+  load(__FILE__)
+  main
+  exit(0)
+end
+
+EOS
+
+    open(src, "r") do |rfd|
+      rfd.each_line { |l|
+        if l =~ /^\s*require\s+['"]yaggo\/(\w+)['"]\s*$/
+          to_load << $1
+        else
+          wfd.print(l)
+        end
+      }
+    end
+
+    to_load.each { |f|
+      next if loaded[f]
+      wfd.puts("", "# Loading yaggo/#{f}", "")
+      open(File.join("lib", "yaggo", f + ".rb"), "r") { |nfd|
+        nfd.each_line { |l|
+          wfd.print(l) unless l =~ /^\s*require\s+['"]yaggo\/(\w+)['"]\s*$/
+        }
+      }
+      loaded[f] = true
+    }
+  end
+end
+
+if __FILE__ == $0
+  dest = ARGV.shift || "yaggo"
+  create_binary("lib/yaggo/main.rb", dest)
+end
diff --git a/bin/yaggo b/bin/yaggo
new file mode 100755
index 0000000..12605b9
--- /dev/null
+++ b/bin/yaggo
@@ -0,0 +1,22 @@
+#! /usr/bin/env ruby
+
+#   Yaggo. Yet Another GenGetOpt. Generate command line switch parsers
+#   using getopt_long.
+#   Copyright (C) 2011 Guillaume Marcais.
+#
+#   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 3 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, see
+#   <http://www.gnu.org/licenses/>.
+
+require 'yaggo/main.rb'
+main
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644
index 84f3c5f..0000000
--- a/debian/changelog
+++ /dev/null
@@ -1,14 +0,0 @@
-yaggo (1.5.9-1) unstable; urgency=medium
-
-  * New upstream version
-  * debhelper 10
-  * cme fix dpkg-control
-  * d/watch: version=4
-
- -- Andreas Tille <tille at debian.org>  Mon, 16 Jan 2017 17:26:36 +0100
-
-yaggo (1.5.4-1) unstable; urgency=low
-
-  * Initial release (Closes: #764211)
-
- -- Andreas Tille <tille at debian.org>  Mon, 06 Oct 2014 12:13:09 +0200
diff --git a/debian/clean b/debian/clean
deleted file mode 100644
index 19d4f94..0000000
--- a/debian/clean
+++ /dev/null
@@ -1 +0,0 @@
-yaggo
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index f599e28..0000000
--- a/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-10
diff --git a/debian/control b/debian/control
deleted file mode 100644
index 6def99b..0000000
--- a/debian/control
+++ /dev/null
@@ -1,25 +0,0 @@
-Source: yaggo
-Maintainer: Debian Med Packaging Team <debian-med-packaging at lists.alioth.debian.org>
-Uploaders: Andreas Tille <tille at debian.org>
-Section: misc
-Priority: optional
-Build-Depends: debhelper (>= 10),
-               ruby,
-               help2man
-Standards-Version: 3.9.8
-Vcs-Browser: https://anonscm.debian.org/viewvc/debian-med/trunk/packages/yaggo/trunk/
-Vcs-Svn: svn://anonscm.debian.org/debian-med/trunk/packages/yaggo/trunk/
-Homepage: https://github.com/gmarcais/yaggo
-
-Package: yaggo
-Architecture: all
-Depends: ${shlibs:Depends},
-         ${misc:Depends},
-         ruby | ruby-interpreter
-Description: generate command line parser using getopt_long
- Yaggo is a tool to generate command line parsers for C++. Yaggo stands
- for "Yet Another GenGetOpt" and is inspired by GNU Gengetopt.
- .
- It reads a configuration file describing the switches and argument for
- a C++ program and it generates one header file that parses the command
- line using getopt_long(3).
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644
index ff303e7..0000000
--- a/debian/copyright
+++ /dev/null
@@ -1,21 +0,0 @@
-Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
-Upstream-Name: yaggo
-Source: https://github.com/gmarcais/yaggo/releases
-
-Files: *
-Copyright: 2012-2014 Guillaume Marçais <gmarcais at umd.edu>
-License: GPL-3+
-
-Files: setup.rb
-Copyright: 2000-2005 Minero Aoki
-License: LGPL-2.1
- On Debian systems you can find the full text of the Lesser General
- Public License version 2.1 at /usr/share/common-licenses/LGPL-2.1.
-
-Files: debian/*
-Copyright: 2014 Andreas Tille <tille at debian.org>
-License: GPL-3+
-
-License: GPL-3+
- On Debian systems you can find the full text of the GNU General Public
- License version 3 at /usr/share/common-licenses/GPL-3.
diff --git a/debian/docs b/debian/docs
deleted file mode 100644
index b43bf86..0000000
--- a/debian/docs
+++ /dev/null
@@ -1 +0,0 @@
-README.md
diff --git a/debian/install b/debian/install
deleted file mode 100644
index f17862a..0000000
--- a/debian/install
+++ /dev/null
@@ -1,2 +0,0 @@
-yaggo	usr/bin
-
diff --git a/debian/rules b/debian/rules
deleted file mode 100755
index 40b9ae0..0000000
--- a/debian/rules
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/make -f
-
-pkg := $(shell dpkg-parsechangelog | sed -n 's/^Source: //p')
-version=$(shell dpkg-parsechangelog | grep Version: | cut -f2 -d' ' | cut -f1 -d- )
-mandir=$(CURDIR)/debian/$(pkg)/usr/share/man/man1/
-HELP2MAN = help2man --no-info --version-string="$(version)"
-
-%:
-	dh $@
-
-override_dh_auto_install:
-	dh_auto_install -- prefix=$(CURDIR)/debian/$(pkg)/usr
-
-override_dh_installman:
-	mkdir -p $(mandir)
-	$(HELP2MAN) \
-		--name='generate command line parser using getopt_long' \
-		$(CURDIR)/$(pkg) > $(mandir)/$(pkg).1
diff --git a/debian/source/format b/debian/source/format
deleted file mode 100644
index 163aaf8..0000000
--- a/debian/source/format
+++ /dev/null
@@ -1 +0,0 @@
-3.0 (quilt)
diff --git a/debian/watch b/debian/watch
deleted file mode 100644
index f4e919b..0000000
--- a/debian/watch
+++ /dev/null
@@ -1,2 +0,0 @@
-version=4
-https://github.com/gmarcais/yaggo/releases .*/archive/v(\d[\d.-]+)\.(?:tar(?:\.gz|\.bz2)?|tgz)
diff --git a/lib/yaggo/dsl.rb b/lib/yaggo/dsl.rb
new file mode 100644
index 0000000..6186bc7
--- /dev/null
+++ b/lib/yaggo/dsl.rb
@@ -0,0 +1,573 @@
+# This file is part of Yaggo.
+
+# Yaggo 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 3 of the License, or
+# (at your option) any later version.
+
+# Yaggo 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 Yaggo.  If not, see <http://www.gnu.org/licenses/>.
+
+
+##############################
+# Process an input files. Define the Domain Specific Language.
+##############################
+$options = []
+$opt_hash = {}
+$args = []
+
+class NoTarget
+  def description str; $description = str; end
+  def description= str; $description = str; end
+  def method_missing(m, *args)
+    raise "'#{m}' used outside of option or arg description"
+  end
+end
+$target = NoTarget.new
+
+def output str; $output = str; end
+def name str; $klass = str; end
+def purpose str; $purpose = str; end
+def package str; $package = str; end
+def usage str; $usage = str; end
+def text str; $after_text = str; end
+def description str; $target.description = str; end
+def version str; $version = str; end
+def posix *args; $posix = true; end
+def license str; $license = str; end
+$global_variables = [:output, :name, :purpose, :package,
+                     :description, :version, :license, :posix]
+output = name = purpose = package = description = version = license = nil
+
+# def set_type t
+#   raise "More than 1 type specified: '#{$target.type}' and '#{t}'" unless $target.type.nil?
+#   $target.type = t
+# end
+
+def int32;    $target.type = :int32; end
+def int64;    $target.type = :int64; end
+def uint32;   $target.type = :uint32; end
+def uint64;   $target.type = :uint64; end
+def int;      $target.type = :int; end
+def long;     $target.type = :long; end
+def double;   $target.type = :double; end
+def string;   $target.type = :string; end
+def c_string; $target.type = :c_string; end
+def flag;     $target.type = :flag; end
+def enum(*argv); $target.type = :enum; $target.enum = argv; end
+
+def suffix; $target.suffix = true; end
+def required; $target.required = true; end
+def hidden; $target.hidden = true; end
+def secret; $target.secret = true; end
+def on; $target.on; end
+def off; $target.off; end
+def no; $target.no; end
+def default str; $target.default = str; end
+def typestr str; $target.typestr = str; end
+def multiple; $target.multiple = true; end
+def at_least n; $target.at_least = n; end
+def conflict *a; $target.conflict= a; end
+def imply *a; $target.imply= a; end
+def access *types; $target.access= types; end
+# Define the following local variables and check their value after
+# yielding the block to catch syntax such as default="value".
+$option_variables = [:default, :typestr, :at_least]
+default = typestr = at_least = nil
+$main_binding = binding
+
+def default_val(val, type, *argv)
+  case type
+  when :string, :c_string
+    "\"#{val || $type_default[type]}\""
+  when :uint32, :uint64, :int32, :int64, :int, :long, :double
+    val ? "(#{$type_to_C_type[type]})#{val}" : $type_default[type]
+  else
+    val.to_s || $type_default[type]
+  end
+end
+
+class BaseOptArg
+  def at_least=(n)
+    multiple = true
+    nb = case n
+         when Integer
+           n
+         when String
+           n =~ /^\d+$/ ? n.to_i : nil
+         else
+           nil
+         end
+    raise "Invalid minimum number for at_least (#{n})" if nb.nil?
+    self.multiple = true
+    @at_least = nb
+  end
+
+  def type=(t)
+    raise "More than 1 type specified: '#{type}' and '#{t}'" unless @type.nil? || @type == t
+    @type = t
+  end
+
+  def suffix=(t)
+    case type
+    when nil
+      raise "A numerical type must be specify before suffix"
+    when :flag, :string, :c_string
+      raise "Suffix is meaningless with the type #{type}"
+    end
+    @suffix = t
+  end
+
+  def access=(types)
+    types.all? { |t| ["read", "write", "exec"].include?(t) } or
+      raise "Invalid access type(s): #{types.join(", ")}"
+    @access_types = types
+  end
+
+  def check
+    if !@access_types.empty? && @type != :c_string
+      raise "Access checking is valid only with a path (a c_string)"
+    end
+  end
+end
+
+class Option < BaseOptArg
+  attr_accessor :description, :required, :typestr
+  attr_accessor :hidden, :secret, :conflict, :multiple, :access_types, :noflag
+  attr_reader :long, :short, :var, :type, :at_least, :default, :suffix, :enum
+  attr_reader :imply
+
+  def initialize(long, short)
+    @long, @short = long, short
+    @var = (@long || @short).gsub(/[^a-zA-Z0-9_]/, "_")
+    @type = nil
+    @no = false # Also generate the --noswitch for a flag
+    @default = nil
+    @suffix = false
+    @at_least = nil
+    @conflict = []
+    @enum = []
+    @imply = []
+    @access_types = []
+  end
+
+  def on
+    self.type = :flag
+    self.default = "true"
+  end
+
+  def off
+    self.type = :flag
+    self.default = "false"
+  end
+
+  def no
+    self.type = :flag
+    self.noflag = true
+  end
+
+  def tf_to_on_off v
+    case v
+    when "true"
+      "on"
+    when "false"
+      "off"
+    else
+      v
+    end
+  end
+
+  def convert_int(x, signed = true)
+    x =~ /^([+-]?\d+)([kMGTPE]?)$/ or return nil
+    v = $1.to_i
+    return nil if v < 0 && !signed
+    case $2
+    when "k"
+      v *= 1000
+    when "M"
+      v *= 1000_000
+    when "G"
+      v *= 1000_000_000
+    when "T"
+      v *= 1000_000_000_000
+    when "P"
+      v *= 1000_000_000_000_000
+    when "E"
+      v *= 1000_000_000_000_000_000
+    end
+    return v
+  end
+
+  def convert_double(x)
+    x =~ /^([+-]?[\d]+(?:\.\d*))?(?:([afpnumkMGTPE])|([eE][+-]?\d+))?$/ or return nil
+    v = "#{$1}#{$3}".to_f
+    case $2
+    when "a"
+      v *= 1e-18
+    when "f"
+      v *= 1e-15
+    when "p"
+      v *= 1e-12
+    when "n"
+      v *= 1e-9
+    when "u"
+      v *= 1e-6
+    when "m"
+      v *= 1e-3
+    when "k"
+      v *= 1e3
+    when "M"
+      v *= 1e6
+    when "G"
+      v *= 1e9
+    when "T"
+      v *= 1e12
+    when "P"
+      v *= 1e15
+    when "E"
+      v *= 1e18
+    end
+    return v
+  end
+
+  def default=(v)
+    type.nil? and raise "A type must be specified before defining a default value"
+    unless default.nil?
+      if type == :flag
+        v1, v2 = tf_to_on_off(default), tf_to_on_off(v)
+      else
+        v1, v2 = default, v
+      end
+      raise "More than 1 default value specified: '#{v1}' and '#{v2}'"
+    end
+    pref = "Option #{long || ""}|#{short || ""}:"
+    bv = v # Backup v for display
+    case @type
+    when nil
+      raise "#{pref} No type specified"
+    when :uint32, :uint64
+      (Integer === v && v >= 0) || (String === v && v = convert_int(v, false)) or
+        raise "#{pref} Invalid unsigned integer '#{bv}'"
+    when :int32, :int64, :int, :long
+      (Integer === v) || (String === v && v = convert_int(v, true)) or
+        raise "#{pref} Invalid integer #{bv}"
+    when :double
+      (Float === v) || (String === v && v = convert_double(v)) or
+        raise "#{pref} Invalid double #{bv}"
+    when :enum
+      v = v.to_i if v =~ /^\d+$/
+      case v
+      when Integer
+        (v >= 0 && v < @enum.size) or
+          raise "Default is out of range [0, #{@enum.size-1}]"
+      when String
+        nv = @enum.index(v) or
+          raise "Unknown constant '#{v}'. Should be one of { #{@enum.join(", ")} }"
+        v = nv
+      else
+        raise "Expected an Integer or a String"
+      end
+    end
+    @default = v
+  end
+
+  def enum=(*argv)
+    @type == :enum or raise "#{pref} Enum valid only for enum types."
+    @enum = argv.flatten
+  end
+
+  def conflict= a; @conflict += a.map { |x| x.gsub(/^-+/, "") }; end
+  def imply= a; @imply += a.map { |x| x.gsub(/^-+/, "") }; end
+
+  def check
+    pref = "Option #{long || ""}|#{short || ""}:"
+    raise "#{pref} No type specified" if type.nil?
+
+    if multiple
+      raise "#{pref} Multiple is meaningless with a flag" if type == :flag
+      raise "#{pref} An option marked multiple cannot have a default value" unless default.nil?
+      raise "#{pref} Multiple is incompatible with enum type" if type == :enum
+    end
+
+    if @type == :flag && noflag && !short.nil?
+      raise "#{pref} flag with 'no' option cannot have a short switch"
+    end
+
+    super
+
+    # case @type
+    # when nil
+    #   raise "#{pref} No type specified"
+    # when :uint32, :uint64
+    #   @default.nil? || @default =~ /^\d+$/ or
+    #     raise "#{pref} Invalid unsigned integer #{@default}"
+    # when :int32, :int64, :int, :long
+    #   @default.nil? || @default =~ /^[+-]?\d+$/ or
+    #     raise "#{pref} Invalid integer #{@default}"
+    # when :double
+    #   @default.nil? || @default =~ /^[+-]?[\d.]+([eE][+-]?\d+)?$/ or
+    #     raise "#{pref} Invalid double #{@default}"
+    # when :flag
+    #   raise "#{pref} A flag cannot be declared multiple" if @multiple
+    #   raise "#{pref} Suffix is meaningless for a flag" if @suffix
+    # end
+  end
+
+  def static_decl
+    a = []
+    if @type == :enum
+      a << "struct #{@var} {"
+      a << "  enum { #{@enum.map { |x| x.gsub(/[^a-zA-Z0-9_]/, "_") }.join(", ")} };"
+      a << "  static const char* const  strs[#{@enum.size + 1}];"
+      a << "};"
+    end
+    a
+  end
+
+  def var_decl
+    if @type == :flag
+      ["#{"bool".ljust($typejust)} #{@var}_flag;"]
+    else
+      a = []
+      if @multiple
+        c_type = "::std::vector<#{$type_to_C_type[@type]}>"
+        a << (c_type.ljust($typejust) + " #{@var}_arg;")
+        a << ("typedef #{c_type}::iterator #{@var}_arg_it;")
+        a << ("typedef #{c_type}::const_iterator #{@var}_arg_const_it;")
+      else
+        a << "#{$type_to_C_type[@type].ljust($typejust)} #{@var}_arg;"
+      end
+      a << "#{"bool".ljust($typejust)} #{@var}_given;"
+    end
+  end
+
+  def init
+    s = "#{@var}_#{@type == :flag ? "flag" : "arg"}("
+    s += default_val(@default, @type, @enum) unless @multiple
+    s += ")"
+    unless @type == :flag
+      s += ", #{@var}_given(false)"
+    end
+    s
+  end
+
+  def long_enum
+    return nil if !@short.nil?
+    res = [@var.upcase + "_OPT"]
+    if @type == :flag && noflag
+      res << "NO#{@var.upcase}_OPT"
+    end
+    res
+  end
+
+  def struct
+    res = ["{\"#{long}\", #{@type == :flag ? 0 : 1}, 0, #{@short ? "'" + @short + "'" : long_enum[0]}}"]
+    if @type == :flag && noflag
+      res << "{\"no#{long}\", 0, 0, #{long_enum()[1]}}"
+    end
+    res
+  end
+  def short_str
+    return nil if @short.nil?
+    @short + (@type == :flag ? "" : ":")
+  end
+  def switches
+    s  = @short.nil? ? "    " : "-#{@short}"
+    s += ", " unless @short.nil? || @long.nil?
+    unless @long.nil?
+      if @type == :flag && @noflag
+        s += "--[no]#{@long}"
+      else
+        s += "--#{@long}"
+      end
+      s += "=#{@typestr || dflt_typestr(@type, @enum)}" unless @type == :flag
+    end
+    s
+  end
+
+  def default_str
+    return @default unless @type == :enum
+    @enum[@default || 0]
+  end
+
+  def help
+    s  = @required ? "*" : " "
+    @description ||= "Switch #{switches}"
+    s += @description.gsub(/"/, '\"') || ""
+    default = default_str
+    s += " (#{default})" unless default.nil?
+    s
+  end
+
+  def dump
+    case @type
+    when :flag
+      ["\"#{@var}_flag:\"", "#{@var}_flag"]
+    when :enum
+      ["\"#{@var}_given:\"", "#{@var}_given",
+       "\" #{@var}_arg:\"", "#{@var}_arg", '"|"', "#{@var}::strs[#{@var}_arg]"]
+    else
+      ["\"#{@var}_given:\"", "#{@var}_given", 
+       "\" #{@var}_arg:\"", @multiple ? "vec_str(#{@var}_arg)" : "#{@var}_arg"]
+    end
+  end
+
+  def parse_arg(no = false)
+    a = @imply.map { |ios| "#{$opt_hash[ios].var}_flag = true;" }
+    a << "#{@var}_given = true;" unless @type == :flag
+    case @type
+    when :flag
+      if @noflag
+        a << ["#{@var}_flag = #{no ? "false" : "true"};"]
+      else
+        a << ["#{@var}_flag = #{@default == "true" ? "false" : "true"};"]
+      end
+    when :string
+      a << (@multiple ? "#{@var}_arg.push_back(#{str_conv("optarg", @type, false)});" : "#{@var}_arg.assign(optarg);")
+    when :c_string
+      a << (@multiple ? "#{@var}_arg.push_back(#{str_conv("optarg", @type, false)});" : "#{@var}_arg = optarg;")
+    when :uint32, :uint64, :int32, :int64, :int, :long, :double
+      a << (@multiple ? "#{@var}_arg.push_back(#{str_conv("optarg", @type, @suffix)});" : "#{@var}_arg = #{str_conv("optarg", @type, @suffix)};")
+      a << "CHECK_ERR(#{@type}_t, optarg, \"#{switches}\")" 
+    when :enum
+      a << "#{@var}_arg = #{str_conv("optarg", @type, "#{@var}::strs")};"
+      a << "CHECK_ERR(#{@type}, optarg, \"#{switches}\")"
+    end
+    a
+  end
+end
+
+class Arg < BaseOptArg
+  attr_accessor :description, :type, :typestr, :multiple, :access_types
+  attr_reader :name, :at_least, :suffix, :var
+  def initialize(str)
+    @name = str
+    @var = @name.gsub(/[^a-zA-Z0-9_]/, "_")
+    @type = nil
+    @at_least = 0
+    @suffix = false
+    @access_types = []
+  end
+
+  def type=(t)
+    super
+    raise "An arg cannot be of type '#{t}'" if t == :flag
+  end
+
+  def on; raise "An arg cannot be a flag with default value on"; end
+  def off; raise "An arg cannot be a flag with default value off"; end
+
+  def default=(*args)
+    raise "An arg cannot have a default value (#{args[0]})"
+  end
+
+  def hidden=(*args)
+    raise "An arg cannot be marked hidden"
+  end
+
+  def secret=(*args)
+    raise "An arg cannot be marked secret"
+  end
+
+  def required=(*args)
+    raise "An arg cannot be marked required"
+  end
+
+  def check
+    super
+
+    pref = "Arg #{name}:"
+    raise "#{pref} No type specified" if type.nil?
+  end
+
+  def var_decl
+    if @multiple
+      c_type = "::std::vector<#{$type_to_C_type[@type]}>"
+      [c_type.ljust($typejust) + " #{@var}_arg;",
+       "typedef #{c_type}::iterator #{@var}_arg_it;",
+       "typedef #{c_type}::const_iterator #{@var}_arg_const_it;"]
+    else
+      ["#{$type_to_C_type[@type]}".ljust($typejust) + " #{@var}_arg;"]
+    end
+  end
+
+  def init
+    s = "#{@var}_arg("
+    s += default_val(@default, @type) unless @multiple
+    s += ")"
+    s
+  end
+
+  def dump
+    ["\"#{@var}_arg:\"",
+     @multiple ? "vec_str(#{@var}_arg)" : "#{@var}_arg"]
+  end
+
+  def parse_arg
+    a = []
+    off = ""
+    if @multiple
+      a << "for( ; optind < argc; ++optind) {"
+      a << "  #{@var}_arg.push_back(#{str_conv("argv[optind]", @type, @suffix)});"
+      off = "  "
+    else
+      a << "#{@var}_arg = #{str_conv("argv[optind]", @type, @suffix)};"
+    end
+    unless @type == :string || @type == :c_string
+      a << (off + "CHECK_ERR(#{@type}_t, argv[optind], \"#{@var}\")")
+    end
+    a << (@multiple ? "}" : "++optind;")
+    a
+  end
+end
+
+def option(name1, name2 = nil, &b)
+  long = short = nil
+  if name1 =~ /^--/ || name1.length >= 2
+    long, short = name1, name2
+  elsif !name2.nil? && (name2 =~ /^--/ || name2.length >= 2)
+    long, short = name2, name1
+  else
+    long, short = nil, name1
+  end
+
+  long.gsub!(/^--/, "") unless long.nil?
+  short.gsub!(/^-/, "") unless short.nil?
+  o = Option.new(long, short)
+  $options.each { |lo| 
+    if (!long.nil? && lo.long == long) || (!short.nil? && lo.short == short)
+      raise "#{b.source_location.join(":")}: Option #{long}|#{short} conflicts with existing option #{lo.long}|#{lo.short}"
+    end
+  }
+  $options << o
+  $target = o
+  name  = "Option #{long || ""}|#{short || ""}"
+  run_block(name, b)
+  $target = NoTarget.new
+  begin
+    o.check
+  rescue => e
+    raise "#{b.source_location.join(":")}: #{e.message}"
+  end
+end
+
+def arg(name, &b)
+  a = Arg.new(name)
+  $args.any? { |la| la.name == name } and
+    raise "#{b.source_location.join(":")}: Arg '#{name}' already exists"
+  $args << a
+  $target = a
+  name = "Arg #{name}"
+  run_block(name, b)
+  $target = NoTarget.new
+  begin
+    a.check
+  rescue => e
+    raise "#{b.source_location.join(":")}: #{e.message}"
+  end
+end
diff --git a/lib/yaggo/general.rb b/lib/yaggo/general.rb
new file mode 100644
index 0000000..b477e01
--- /dev/null
+++ b/lib/yaggo/general.rb
@@ -0,0 +1,127 @@
+# This file is part of Yaggo.
+
+# Yaggo 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 3 of the License, or
+# (at your option) any later version.
+
+# Yaggo 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 Yaggo.  If not, see <http://www.gnu.org/licenses/>.
+
+
+$typejust = 30
+$switchesjust = 40
+
+$type_to_C_type = { 
+  :uint32 => "uint32_t",
+  :uint64 => "uint64_t",
+  :int32 => "int32_t",
+  :int64 => "int64_t",
+  :int => "int",
+  :long => "long",
+  :double => "double",
+  :string => "string",
+  :c_string => "const char *",
+  :enum => "int",
+}
+$type_default = {
+  :uint32 => "0",
+  :uint64 => "0",
+  :int32 => "0",
+  :int64 => "0",
+  :int => "0",
+  :long => "0",
+  :double => "0.0",
+  :string => "",
+  :c_string => "",
+  :enum => "0",
+}
+
+def dflt_typestr(type, *argv)
+  case type
+  when :c_string
+    "string"
+  when :enum
+    argv[0].join("|")
+  else
+    type.to_s
+  end
+end
+
+def suffix_arg(suffix)
+  case suffix
+  when true
+    "true"
+  when false
+    "false"
+  when String
+    suffix
+  else
+    raise "Invalid suffix specifier"
+  end
+end
+
+def str_conv(arg, type, *argv)
+  case type
+  when :string
+    "string(#{arg})"
+  when :c_string
+    arg
+  when :uint32, :uint64
+    "conv_uint<#{$type_to_C_type[type]}>((const char*)#{arg}, err, #{suffix_arg(argv[0])})"
+  when :int32, :int64, :long, :int
+    "conv_int<#{$type_to_C_type[type]}>((const char*)#{arg}, err, #{suffix_arg(argv[0])})"
+  when :double
+    "conv_double((const char*)#{arg}, err, #{suffix_arg(argv[0])})"
+  when :enum
+    # Convert a string to its equivalent enum value
+    "conv_enum((const char*)#{arg}, err, #{argv[0]})"
+  end
+end
+
+def find_error_header bt
+  bt.each { |l| l =~ /^\(eval\):\d+:/ and return $& }
+  return ""
+end
+
+def run_block(name, b)
+  eval("#{$option_variables.join(" = ")} = nil", $main_binding)
+  b.call
+  $option_variables.each { |n| eval("#{n} #{n} unless #{n}.nil?", $main_binding) }
+rescue NoMethodError => e
+  header = find_error_header(e.backtrace)
+  raise "#{header} In #{name}: invalid keyword '#{e.name}' in statement '#{e.name} #{e.args.map { |s| "\"#{s}\"" }.join(" ")}'"
+rescue NameError => e
+  header = find_error_header(e.backtrace)
+  raise "#{header} In #{name}: invalid keyword '#{e.name}'"
+rescue RuntimeError, ArgumentError => e
+  header = find_error_header(e.backtrace)
+  raise "#{header} In #{name}: #{e.message}"
+end
+
+
+def check_conflict_exclude
+  $options.each { |o|
+    $opt_hash[o.long] = o unless o.long.nil?
+    $opt_hash[o.short] = o unless o.short.nil?
+  }
+  $options.each { |o|
+    o.conflict.each { |co|
+      $opt_hash[co] or 
+      raise "Unknown conflict option '#{co}' for switch #{o.long}|#{o.short}"
+    }
+  }
+  $options.each { |o|
+    o.imply.each { |ios|
+      io = $opt_hash[ios] or
+      raise "Unknown implied option '#{io}' for switch #{o.long}|#{o.short}"
+      io.type == :flag or
+      raise "Implied option '#{io}' for switch #{o.long}|#{o.short} is not a flag"
+    }
+  }
+end
diff --git a/lib/yaggo/library.rb b/lib/yaggo/library.rb
new file mode 100644
index 0000000..4dd7bde
--- /dev/null
+++ b/lib/yaggo/library.rb
@@ -0,0 +1,197 @@
+# This file is part of Yaggo.
+
+# Yaggo 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 3 of the License, or
+# (at your option) any later version.
+
+# Yaggo 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 Yaggo.  If not, see <http://www.gnu.org/licenses/>.
+
+
+def output_conversion_code file
+  file.puts(<<EOS)
+  static bool adjust_double_si_suffix(double &res, const char *suffix) {
+    if(*suffix == '\\0')
+      return true;
+    if(*(suffix + 1) != '\\0')
+      return false;
+
+    switch(*suffix) {
+    case 'a': res *= 1e-18; break;
+    case 'f': res *= 1e-15; break;
+    case 'p': res *= 1e-12; break;
+    case 'n': res *= 1e-9;  break;
+    case 'u': res *= 1e-6;  break;
+    case 'm': res *= 1e-3;  break;
+    case 'k': res *= 1e3;   break;
+    case 'M': res *= 1e6;   break;
+    case 'G': res *= 1e9;   break;
+    case 'T': res *= 1e12;  break;
+    case 'P': res *= 1e15;  break;
+    case 'E': res *= 1e18;  break;
+    default: return false;
+    }
+    return true;
+  }
+
+  static double conv_double(const char *str, ::std::string &err, bool si_suffix) {
+    char *endptr = 0;
+    errno = 0;
+    double res = strtod(str, &endptr);
+    if(errno) {
+      err.assign(strerror(errno));
+      return (double)0.0;
+    }
+    bool invalid =
+      si_suffix ? !adjust_double_si_suffix(res, endptr) : *endptr != '\\0';
+    if(invalid) {
+      err.assign("Invalid character");
+      return (double)0.0;
+    }
+    return res;
+  }
+
+  static int conv_enum(const char* str, ::std::string& err, const char* const strs[]) {
+    int res = 0;
+    for(const char* const* cstr = strs; *cstr; ++cstr, ++res)
+      if(!strcmp(*cstr, str))
+        return res;
+    err += "Invalid constant '";
+    err += str;
+    err += "'. Expected one of { ";
+    for(const char* const* cstr = strs; *cstr; ++cstr) {
+      if(cstr != strs)
+        err += ", ";
+      err += *cstr;
+    }
+    err += " }";
+    return -1;
+  }
+
+  template<typename T>
+  static bool adjust_int_si_suffix(T &res, const char *suffix) {
+    if(*suffix == '\\0')
+      return true;
+    if(*(suffix + 1) != '\\0')
+      return false;
+
+    switch(*suffix) {
+    case 'k': res *= (T)1000; break;
+    case 'M': res *= (T)1000000; break;
+    case 'G': res *= (T)1000000000; break;
+    case 'T': res *= (T)1000000000000; break;
+    case 'P': res *= (T)1000000000000000; break;
+    case 'E': res *= (T)1000000000000000000; break;
+    default: return false;
+    }
+    return true;
+  }
+
+  template<typename T>
+  static T conv_int(const char *str, ::std::string &err, bool si_suffix) {
+    char *endptr = 0;
+    errno = 0;
+    long long int res = strtoll(str, &endptr, 0);
+    if(errno) {
+      err.assign(strerror(errno));
+      return (T)0;
+    }
+    bool invalid =
+      si_suffix ? !adjust_int_si_suffix(res, endptr) : *endptr != '\\0';
+    if(invalid) {
+      err.assign("Invalid character");
+      return (T)0;
+    }
+    if(res > ::std::numeric_limits<T>::max() ||
+       res < ::std::numeric_limits<T>::min()) {
+      err.assign("Value out of range");
+      return (T)0;
+    }
+    return (T)res;
+  }
+
+  template<typename T>
+  static T conv_uint(const char *str, ::std::string &err, bool si_suffix) {
+    char *endptr = 0;
+    errno = 0;
+    while(isspace(*str)) { ++str; }
+    if(*str == '-') {
+      err.assign("Negative value");
+      return (T)0;
+    }
+    unsigned long long int res = strtoull(str, &endptr, 0);
+    if(errno) {
+      err.assign(strerror(errno));
+      return (T)0;
+    }
+    bool invalid =
+      si_suffix ? !adjust_int_si_suffix(res, endptr) : *endptr != '\\0';
+    if(invalid) {
+      err.assign("Invalid character");
+      return (T)0;
+    }
+    if(res > ::std::numeric_limits<T>::max()) {
+      err.assign("Value out of range");
+      return (T)0;
+    }
+    return (T)res;
+  }
+
+  template<typename T>
+  static ::std::string vec_str(const std::vector<T> &vec) {
+    ::std::ostringstream os;
+    for(typename ::std::vector<T>::const_iterator it = vec.begin();
+        it != vec.end(); ++it) {
+      if(it != vec.begin())
+        os << ",";
+      os << *it;
+    }
+    return os.str();
+  }
+
+  class string : public ::std::string {
+  public:
+    string() : ::std::string() {}
+    explicit string(const ::std::string &s) : std::string(s) {}
+    explicit string(const char *s) : ::std::string(s) {}
+    int as_enum(const char* const strs[]) {
+      ::std::string err;
+      int res = #{str_conv("this->c_str()", :enum, "strs")};
+      if(!err.empty())
+        throw ::std::runtime_error(err);
+      return res;
+    }
+
+
+EOS
+  [:uint32, :uint64, :int32, :int64, :int, :long, :double].each do |type|
+  file.puts(<<EOS)
+    #{$type_to_C_type[type]} as_#{type}_suffix() const { return as_#{type}(true); }
+    #{$type_to_C_type[type]} as_#{type}(bool si_suffix = false) const {
+      ::std::string err;
+      #{$type_to_C_type[type]} res = #{str_conv("this->c_str()", type, "si_suffix")};
+      if(!err.empty()) {
+        ::std::string msg("Invalid conversion of '");
+        msg += *this;
+        msg += "' to #{type}_t: ";
+        msg += err;
+        throw ::std::runtime_error(msg);
+      }
+      return res;
+    }
+EOS
+  end
+
+  file.puts(<<EOS)
+  };
+
+EOS
+# }
+    end
+
diff --git a/lib/yaggo/main.rb b/lib/yaggo/main.rb
new file mode 100644
index 0000000..f665488
--- /dev/null
+++ b/lib/yaggo/main.rb
@@ -0,0 +1,152 @@
+# This file is part of Yaggo.
+
+# Yaggo 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 3 of the License, or
+# (at your option) any later version.
+
+# Yaggo 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 Yaggo.  If not, see <http://www.gnu.org/licenses/>.
+
+require 'optparse'
+
+require 'yaggo/version'
+require 'yaggo/man_page'
+require 'yaggo/stub'
+require 'yaggo/general'
+require 'yaggo/library'
+require 'yaggo/dsl'
+require 'yaggo/parser'
+require 'yaggo/zsh_completion'
+
+def main
+  $yaggo_options = {
+    :output => nil,
+    :license => nil,
+    :stub => false,
+    :zc => nil,
+    :extended => false,
+    :debug => false,
+  }
+
+  parser = OptionParser.new do |o|
+    o.version = $yaggo_version
+    o.banner = "Usage: #{$0} [options] [file.yaggo]"
+    o.separator ""
+    o.separator "Specific options:"
+
+    o.on("-o", "--output FILE", "Output file") { |v|
+      $yaggo_options[:output] = v
+    }
+    o.on("-l", "--license PATH", "License file to copy in header") { |v|
+      $yaggo_options[:license] = v
+    }
+    o.on("-m", "--man [FILE]", "Display or write manpage") { |v|
+      display_man_page v
+      exit 0;
+    }
+    o.on("-s", "--stub", "Output a stub yaggo file") {
+      $yaggo_options[:stub] = true
+    }
+    o.on("--zc PATH", "Write zsh completion file") { |v|
+      $yaggo_options[:zc] = v
+    }
+    o.on("-e", "--extended-syntax", "Use extended syntax") {
+      $yaggo_options[:extended] = true
+    }
+    o.on("--debug", "Debug yaggo") {
+      $yaggo_options[:debug] = true
+    }
+
+    o.on_tail("-h", "--help", "Show this message") {
+      puts o
+      exit 0
+    }
+  end
+  parser.parse! ARGV
+
+  if $yaggo_options[:stub]
+    begin
+      display_stub_yaggo_file $yaggo_options[:output]
+    rescue => e
+      STDERR.puts("Failed to write stub: #{e.message}")
+      exit 1
+    end
+
+    exit
+  end
+
+  if !$yaggo_options[:stub] && !$yaggo_options[:manual] && ARGV.empty?
+    STDERR.puts "Error: some yaggo files and/or --lib switch is required", parser
+    exit 1
+  end
+  if !$yaggo_options[:output].nil?
+    if $yaggo_options[:stub]
+      if ARGV.size > 0
+        STDERR.puts "Error: no input file needed with the --stub switch", parser
+        exit 1
+      end
+    elsif ARGV.size != 1
+      STDERR.puts "Error: output switch meaningfull only with 1 input file", parser
+      exit 1
+    end
+  end
+
+  ARGV.each do |input_file|
+    pid = fork do
+      begin
+        yaggo_script = File.read(input_file)
+        if $yaggo_options[:extended]
+          yaggo_script.gsub!(/\)\s*\n\s*\{/, ") {")
+        end
+        eval(File.read(input_file))
+        parsed = true
+        check_conflict_exclude
+      rescue RuntimeError, SyntaxError, Errno::ENOENT, Errno::EACCES => e
+        raise e if $yaggo_options[:debug]
+        STDERR.puts(e.message.gsub(/^\(eval\)/, input_file))
+        exit 1
+      rescue NoMethodError => e
+        raise e if $yaggo_options[:debug]
+        STDERR.puts("Invalid keyword '#{e.name}'")
+        exit 1
+      end
+
+      fsplit    = File.basename(input_file).split(/\./)
+      $klass  ||= fsplit.size > 1 ? fsplit[0..-2].join(".") : fsplit[0]
+      $output   = $yaggo_options[:output] if $yaggo_options[:output]
+      $output ||= input_file.gsub(/\.yaggo$/, "") + ".hpp"
+      
+      begin
+        out_fd = open($output, "w")
+        output_cpp_parser(out_fd, $klass)
+      rescue RuntimeError => e
+        raise e if $yaggo_options[:debug]
+        STDERR.puts("#{input_file}: #{e.message}")
+        exit 1
+      ensure
+        out_fd.close if out_fd
+      end
+
+      if $yaggo_options[:zc]
+        begin
+          out_fd = open($yaggo_options[:zc], "w")
+          output_zsh_completion(out_fd, $yaggo_options[:zc])
+        rescue RuntimeError => e
+          raise e if $yaggo_options[:debug]
+          STDERR.puts("#{input_file}: #{e.message}")
+          exit 1
+        ensure
+          out_fd.close if out_fd
+        end
+      end
+    end
+    Process.waitpid pid
+    exit 1 if !$?.exited? || ($?.exited? && $?.exitstatus != 0)
+  end
+end
diff --git a/lib/yaggo/man_page.rb b/lib/yaggo/man_page.rb
new file mode 100644
index 0000000..39287eb
--- /dev/null
+++ b/lib/yaggo/man_page.rb
@@ -0,0 +1,449 @@
+# This file is part of Yaggo.
+
+# Yaggo 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 3 of the License, or
+# (at your option) any later version.
+
+# Yaggo 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 Yaggo.  If not, see <http://www.gnu.org/licenses/>.
+
+require 'pathname'
+
+def display_man_page out
+  manual = <<EOS
+.TH yaggo 1  "2015-06-24" "version #{$yaggo_version}" "USER COMMANDS"
+
+.SH NAME
+yaggo \- command line switch parser generator
+
+.SH SYNOPSIS
+.B yaggo
+[-o|--output FILE] [-l|--license PATH] [-s|--stub] [--zc PATH] [-e|--extended-syntax] [--man] [-h|--help]
+
+.SH DESCRIPTION
+Yaggo stands for Yet Another GenGetOpt. It is inspired by gengetopt
+software from the FSF.
+
+Yaggo generates a C++ class to parse command line switches (usually
+argc and argv passed to main) using getopt_long. The switches and
+arguments to the program are specified in a description file. To each
+description file, yaggo generates one C++ header file containing the
+parsing code.
+.PP
+See the EXAMPLES section for a complete and simple example.
+
+.SH OPTIONS
+.TP
+\-l|\-\-license
+Display the file at the top of the generated headers. It usually
+contains the license governing the distribution of the headers.
+.TP
+-m|\-\-man
+Display this man page
+.TP
+\-s|\-\-stub
+Generate a stub: a simple yaggo file that can be modified for one's use.
+.TP
+\-e|--extended-syntax
+Use the extended syntax: blocks can be defined on the next line of a command.
+.TP
+\-h|--help
+Display a short help text
+.PP
+
+.SH EXAMPLE
+
+Consider the description files 'example_args.yaggo' which defines a
+switch "-i" (or "--int") that takes an unsigned integer and defaults
+to 42; a switch "-s" (or "--string") that takes a string and can be
+given multiple times; a switch "--flag" which does not take any
+argument; a switch "--severity" which can take only 3 values: "low",
+"middle" and "high".
+
+It takes the following arguments: a string followed by zero or more floating point numbers.
+
+.nf
+purpose "Example of yaggo usage"
+package "example"
+description "This is just an example.
+And a multi-line description."
+
+option("int", "i") {
+  description "Integer switch"
+  uint32; default "42" }
+option("string", "s") {
+  description "Many strings"
+  string; multiple }
+option("flag") {
+  description "A flag switch"
+  flag; off }
+option("severity") {
+  description "An enum switch"
+  enum "low", "middle", "high" }
+arg("first") {
+  description "First arg"
+  c_string }
+arg("rest") {
+  description "Rest of'em"
+  double; multiple }
+.fi
+
+The associated simple C++ program 'examples.cpp' which display information about the switches and arguments passed:
+
+.nf
+#include <iostream>
+#include "example_args.hpp"
+
+int main(int argc, char *argv[]) {
+  example_args args(argc, argv);
+
+  std::cout << "Integer switch: " << args.int_arg << "\\\\n";
+  if(args.string_given)
+    std::cout << "Number of string(s): " << args.string_arg.size() << "\\\\n";
+  else
+    std::cout << "No string switch\\\\n";
+  std::cout << "Flag is " << (args.flag_flag ? "on" : "off") << "\\\\n";
+  std::cout << "First arg: " << args.first_arg << "\\\\n";
+  std::cout << "Severity arg: " << args.severity_arg << " " << example_args::severity::strs[args.severity_arg] << "\\\\n";
+  if(args.severity_arg == example_args::severity::high)
+    std::cout << "Warning: severity is high\\\\n";
+  std::cout << "Rest:";
+  for(example_args::rest_arg_it it = args.rest_arg.begin(); it != args.rest_arg.end(); ++it)
+    std::cout << " " << *it;
+  std::cout << std::endl;
+
+  return 0;
+}
+.fi
+
+This can be compiled with the following commands:
+
+.nf
+% yaggo example_args.yaggo
+% g++ -o example example.cpp
+.fi
+
+The yaggo command above will create by default the file
+'example_args.hpp' (changed '.yaggo' extension to '.hpp'). The output
+file name can be changed with the 'output' keyword explained below.
+
+.SH DESCRIPTION FORMAT
+
+A description file is a sequence of statements. A statement is a
+keyword followed by some arguments. Strings must be surrounded by
+quotes ("" or '') and can span multiple lines. The order of the
+statements is irrelevant. Statements are separated by new lines or
+semi-colons ';'.
+
+.IP *
+Technically speaking, yaggo is implemented as a DSL (Domain Specific
+Language) using ruby. The description file is a valid ruby script and
+the keywords are ruby functions.
+.PP
+
+The following statements are global, not attached to a particular option or argument.
+
+.TP
+purpose
+A one line description of the program.
+.TP
+package
+The name of the package for the usage string. Defaults to the name of the class.
+.TP
+usage
+The usage string. If none given a standard one is generated by yaggo.
+.TP
+description
+A longer description of the program displayed before the list of switch. Displayed by the help.
+.TP
+text
+Some text to be displayed after the list of switches. Displayed by the help.
+.TP
+version
+The version string of the software.
+.TP
+license
+The license and copyright string of the software.
+.TP
+name
+The name of the class generated. Defaults to the name of the
+description file minus the .yaggo extension.
+.TP
+posix
+Posix correct behavior (instead of GNU behavior): switch processing
+stops at the first non-option argument
+.TP
+output
+The name of the output file. Defaults to the name of the
+description file with the .yaggo extension changed to .hpp.
+.PP
+
+The 'option' statement takes one or two arguments, which must be in
+parentheses, and a block of statements surrounded by curly braces
+({...}). The arguments are the long and short version of the
+option. Either one of the long or short version can be omitted. The
+block of statements describe the option in more details, as described
+below.
+
+A switch is named after the long version, or the short version if no
+long version. An 'option' statement for an option named 'switch'
+defines one or two public members in the class. For a flag, it
+creates 'switch_flag' as a boolean. Otherwise, it
+creates 'switch_arg', with a type as specified, and 'switch_given', a
+boolean indicating whether or not the switch was given on the command
+line.
+
+For example, the statement:
+
+.nf
+option("integer", "i") {
+  int; default 5
+}
+.fi
+
+will add the following members to the C++ class:
+
+.nf
+int integer_arg;
+bool integer_given;
+.fi
+
+where "integer_arg" is initialized to 5 and "integer_given" is
+initialized to "false". If the switch "--integer 10" or "-i 10" is
+passed on the command line "integer_arg" is set to 10 and
+integer_given is set to "true".
+
+The statement:
+
+.nf
+option("verbose") {
+  off
+}
+.fi
+
+will add the following member to the C++ class:
+
+.nf
+bool verbose_flag;
+.fi
+
+where "verbose_flag" is initialized to "false". Passing the switch
+"--verbose" on the command line sets "verbose_flag" to true".
+
+
+In addition to the switch created by 'option', the following switches
+are defined by default (unless some option statement overrides them):
+
+.TP
+\-h, \-\-help
+Display the help message.
+.TP
+\-\-full\-help
+Display hidden options as well.
+.TP
+\-\-version
+Display version string.
+.PP
+
+The following statement are recognized in an option block:
+
+.TP
+description "str"
+A short description for this switch.
+
+.TP
+int32, int64, uint32, uint64, double, int, long
+This switch is parsed as a number with the corresponding type int32_t,
+int64_t, uint32_t, uint64_t, double, int and long.
+
+.TP
+suffix
+Valid for numerical type switches as above. It can be appended
+with a SI suffix (e.g. 1M mean 1000000). The suffixes k, M, G, T, P,
+and E are supported for all the numerical types. The suffixes m, u, n,
+p, f, and a are supported for the double type.
+
+.TP
+c_string, string
+This switch is taken as a C string (const char *) or a C++ string
+(inherits from std::string). The C++ string type has the extra
+methods '<type> as_<type>(bool suffix)', where <type> is any numerical
+type as above, to convert the string into that type. If the 'suffix'
+boolean is true, parsing is done using SI suffixes.
+
+.TP
+enum
+This statement must be followed by a comma separated list of strings
+(as in 'enum "choice0", "choice1", "choice2"'). This switch takes value
+a string in the list and is converted to int. C enum type named
+"switchname::enum" is defined with the same choices in the given order.
+
+.TP
+required
+This switch is required. An error is generated if not given on the
+command line.
+.TP
+conflict
+Specify a comma separated list of switches that conflicts with this
+one.
+.TP
+imply
+Specify a comma separated list of switches (of type flag) which are
+implied by this one.
+.TP
+hidden
+This switch is not shown with --help. Use --full-help to see the
+hidden switches, if any.
+.TP
+secret
+This switch is not shown in any help message. Neither --help nor
+--full-help.
+.TP
+multiple
+This switch can be passed multiple times. The values are stored in a
+std::vector. A type for the iterator is also defined in the class with
+the name 'switch_arg_it', where 'switch' is the name of the option.
+.TP
+flag
+This switch is a flag and does not take an argument.
+.TP
+on, off
+The default state for a flag switch. Implies flag. Unless the 'no'
+option is used (see below), with 'off', the default value of the flag
+is "false" and passing --flag sets it to true. With 'on', the default
+value of the flag is "true" and passing --flag sets it to false.
+.TP
+no
+A flag with two switches. If the switch is named "flag", two switches
+are generated: --flag and --noflag, respectively setting it to "true"
+and "false". The 'on' and 'off' options define the default value.
+.TP
+default "val"
+The default value for this switch. It can be a string or a valid
+number. SI suffixes are supported as well (for example "1M" means 1
+m`illion).
+.TP
+typestr "str"
+In the help message, by default, the type of the option is
+displayed. It can be replaced by the string given to 'typestr'.
+.TP
+at_least n
+The given switch must be given at least n times. Implies multiple.
+.TP
+access "type"
+Make sure that the string passed is a path to which we have
+access. "type" is a comma separated list of "read", "write" or
+"exec". It is checked with access(2). The same warning applies:
+
+"Warning: Using access() to check if a user is authorized to, for
+example, open a file before actually doing so using open(2) creates a
+security hole, because the user might exploit the short time interval
+between checking and opening the file to manipulate it.  For this
+reason, the use of this system call should be avoided.  (In the
+example just described, a safer alternative would be to temporarily
+switch the process's effective user ID to the real ID and then call
+open(2).)"
+
+.PP
+
+A 'arg' statement defines an arg passed to the command line. The
+statement takes a single argument, the name of the arg, and a block of
+statements. The block of statements are similar to the option block,
+except that "hidden", "flag", "on", "off" and "no" are not allowed. At
+most one arg can have the 'multiple' statement, and it must be the
+last one.
+
+.SH EXAMPLE USAGE
+
+The argument object parses the switches on construction or later on
+using the parse method. For example, the two pieces code show these
+two different usage.
+
+Using parse method:
+.nf
+  example_args args; // Global variable with switches
+
+  int main(int argc, char* argv[]) {
+    args.parse(argc, argv);
+  }
+.fi
+
+Parse on construction:
+.nf
+  int main(int argc, char* argv[]) {
+    example_args args(argc, argv);
+  }
+.fi
+
+The subclass error can be used to output error messsage (and terminate
+program). It output an error message, the usage string, etc. The error
+class behave like an output stream, it can be used to create
+complicated error message. For example:
+
+.nf
+  if(false_condition)
+    example_args::error() << "Failed to open file '" << args.file_arg << "'";
+.fi
+
+An error object prints an error message and terminate the program with
+exit upon destruction. An exit code can be passed to error. By default
+the exit code (passed to exit) is the constant EXIT_FAILURE (normally
+1). For example:
+
+.nf
+  example_args::error(77) << "Failed with return code 77";
+.fi
+
+.SH LICENSE
+
+There are 2 parts to the software: the yaggo ruby script itself, and
+the header files generated by yaggo from the description files. The
+licenses are as follow:
+
+.TP
+yaggo the ruby script
+This software is licensed under the GNU General
+Public License version 3 or any later version. Copyright (c) 2011
+Guillaume Marcais.
+
+.TP The generated header files.  These files have the license and
+copyright that you, the user of yaggo, assign with the 'license'
+keyword.  .PP In short: only yaggo the software is GPL. The generated
+header files are considered derivative of your work (e.g. the
+description), and you define the copyright and license of those as you
+see fit.
+
+.SH BUGS
+.IP *
+The error message returned by ruby can be a little confusing.
+
+.SH AUTHOR
+Guillaume Marcais (gmarcais at umd.edu)
+.SH SEE ALSO
+getopt_long(3), gengetopt(1), exit(2)
+EOS
+
+  if !out && STDOUT.isatty
+    require 'tempfile'
+    Tempfile.open("yaggo_man") do |fd|
+      begin
+        fd.write(manual)
+        fd.flush
+        system("man", fd.path)
+      ensure
+        fd.unlink
+      end
+    end
+  elsif !out
+    STDOUT.puts(manual)
+  else
+    path = Pathname.new(out)
+    path.write manual
+  end
+end
diff --git a/lib/yaggo/parser.rb b/lib/yaggo/parser.rb
new file mode 100644
index 0000000..9bbdd8b
--- /dev/null
+++ b/lib/yaggo/parser.rb
@@ -0,0 +1,404 @@
+# This file is part of Yaggo.
+
+# Yaggo 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 3 of the License, or
+# (at your option) any later version.
+
+# Yaggo 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 Yaggo.  If not, see <http://www.gnu.org/licenses/>.
+
+def quote_newline_dquotes str, spaces = ""
+  str.gsub(/"/, '\\"').split(/\n/).join("\\n\" \\\n#{spaces}\"")
+end
+
+def output_options_descriptions out, opts, hidden
+  opts.each { |o|
+    # need to be improved. break lines if too long
+    next if o.secret || (o.hidden ^ hidden)
+    s = " " + o.switches
+    if s.size >= $switchesjust
+      s += "\\n" + "".ljust($switchesjust)
+    else
+      s = s.ljust($switchesjust)
+    end
+    out.puts("    \"#{s} #{o.help}\\n\"") 
+  }
+end
+
+def output_cpp_parser(h, class_name)
+  $options.each { |o| o.check }
+  $args.each { |a| a.check }
+  if $args.size > 1
+    mul_args = $args[0..-2].select { |a| a.multiple }
+    if mul_args.size > 0
+      gram = mul_args.size > 1 ? "s are" : " is"
+      raise "The following#{gram} not the last arg but marked multiple: #{mul_args.map { |a| a.name }.join(", ")}"
+    end
+  end
+
+  # Headers
+
+  h.puts(<<EOS)
+/***** This code was generated by Yaggo. Do not edit ******/
+
+EOS
+
+  if $license
+    lines = $license.split(/\n/)
+    h.puts("/* #{lines[0]}", *(lines[1..-1].map { |l| " * " + l }))
+    h.puts(" */", "")
+  elsif $yaggo_options[:license]
+    open($yaggo_options[:license]) { |fd|
+      h.puts(fd.read)
+    }
+    h.puts("")
+  end
+
+h.puts(<<EOS)
+#ifndef __#{class_name.upcase()}_HPP__
+#define __#{class_name.upcase()}_HPP__
+
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <string.h>
+#include <stdexcept>
+#include <string>
+#include <limits>
+#include <vector>
+#include <iostream>
+#include <sstream>
+#include <memory>
+
+class #{class_name} {
+ // Boiler plate stuff. Conversion from string to other formats
+EOS
+
+  output_conversion_code h
+
+  h.puts(<<EOS)
+public:
+EOS
+
+  static_decl = $options.map { |o| o.static_decl }.flatten
+  h.puts("  " + static_decl.join("\n  "), "") unless static_decl.empty?
+
+  ($options + $args).each { |o| h.puts("  " + o.var_decl.join("\n  ")) }
+  h.puts("")
+
+  # Create enum if option with no short version
+  only_long = $options.map { |o| o.long_enum }.flatten.compact
+  need_full = $options.any? { |o| o.hidden }
+
+  help_no_h = $options.any? { |o| o.short == "h" }
+  version_no_V = $options.any? { |o| o.short == "V" }
+  usage_no_U = $options.any? { |o| o.short == "U" }
+  h.print("  enum {\n    START_OPT = 1000")
+  h.print(",\n    FULL_HELP_OPT") if need_full
+  h.print(",\n    HELP_OPT") if help_no_h
+  h.print(",\n    VERSION_OPT") if version_no_V
+  h.print(",\n    USAGE_OPT") if usage_no_U
+  if only_long.empty?
+    h.puts("\n  };")
+  else
+    h.puts(",", "    " + only_long.join(",\n    "), "  };")
+  end
+
+  # Constructors and initialization
+  h.puts("", "  #{class_name}() :")
+  h.puts("    " + ($options + $args).map { |o| o.init }.join(",\n    "), "  { }")
+  h.puts("", "  #{class_name}(int argc, char* argv[]) :")
+  h.puts("    " + ($options + $args).map { |o| o.init }.join(",\n    "))
+  h.puts("  { parse(argc, argv); }", "");
+
+  # Main arsing function
+  h.puts("  void parse(int argc, char* argv[]) {",
+         "    static struct option long_options[] = {")
+  $options.empty? or
+    h.puts("      " + $options.map { |o| o.struct }.flatten.join(",\n      ") + ",")
+  h.puts("      {\"help\", 0, 0, #{help_no_h ? "HELP_OPT" : "'h'"}},")
+  h.puts("      {\"full-help\", 0, 0, FULL_HELP_OPT},") if need_full
+  h.puts("      {\"usage\", 0, 0, #{usage_no_U ? "USAGE_OPT" : "'U'"}},",
+         "      {\"version\", 0, 0, #{version_no_V ? "VERSION_OPT" : "'V'"}},",
+         "      {0, 0, 0, 0}", "    };")
+  short_str = $posix ? "+" : ""
+  short_str += "h" unless help_no_h
+  short_str += "V" unless version_no_V
+  short_str += "U" unless usage_no_U
+  short_str += $options.map { |o| o.short_str }.compact.join("")
+  
+  h.puts("    static const char *short_options = \"#{short_str}\";", "")
+
+  need_err   = $options.any? { |o| o.type != :flag && o.type != :string && o.type != :c_string}
+  need_err ||= $args.any? { |a| a.type != :string && a.type != :c_string }
+  need_err ||= ($options + $args).any? { |o| !o.access_types.empty? }
+  h.puts("    ::std::string err;") if need_err
+
+  # Actual parsing
+  h.puts(<<EOS)
+#define CHECK_ERR(type,val,which) if(!err.empty()) { ::std::cerr << "Invalid " #type " '" << val << "' for [" which "]: " << err << "\\n"; exit(1); }
+    while(true) {
+      int index = -1;
+      int c = getopt_long(argc, argv, short_options, long_options, &index);
+      if(c == -1) break;
+      switch(c) {
+      case ':':
+        ::std::cerr << \"Missing required argument for \"
+                  << (index == -1 ? ::std::string(1, (char)optopt) : std::string(long_options[index].name))
+                  << ::std::endl;
+        exit(1);
+      case #{help_no_h ? "HELP_OPT" : "'h'"}:
+        ::std::cout << usage() << \"\\n\\n\" << help() << std::endl;
+        exit(0);
+      case #{usage_no_U ? "USAGE_OPT" : "'U'"}:
+        ::std::cout << usage() << \"\\nUse --help for more information.\" << std::endl;
+        exit(0);
+      case 'V':
+        print_version();
+        exit(0);
+      case '?':
+        ::std::cerr << \"Use --usage or --help for some help\\n\";
+        exit(1);
+EOS
+  if need_full
+    h.puts(<<EOS)
+      case FULL_HELP_OPT:
+        ::std::cout << usage() << \"\\n\\n\" << help() << \"\\n\\n\" << hidden() << std::flush;
+        exit(0);
+EOS
+  end
+  
+  $options.each { |o|
+    if o.type == :flag && o.noflag
+      h.puts("      case #{o.long_enum[0]}:",
+             "        " + o.parse_arg.join("\n        "),
+             "        break;",
+             "      case #{o.long_enum[1]}:",
+             "        " + o.parse_arg(true).join("\n        "),
+             "        break;")
+    else
+      h.puts("      case #{o.long_enum ? o.long_enum[0] : "'" + o.short + "'"}:",
+             "        " + o.parse_arg.join("\n        "),
+             "        break;")
+    end
+  }
+  h.puts("      }", # close case
+         "    }") # close while(true)
+
+  # Check required
+  $options.any? { |o| o.required} and
+    h.puts("", "    // Check that required switches are present")
+  $options.each { |o|
+    next unless o.required
+    h.puts(<<EOS)
+    if(!#{o.var}_given)
+      error("[#{o.switches}] required switch");
+EOS
+  }
+  # Check conflict
+  $options.any? { |o| !o.conflict.empty? } and
+    h.puts("", "    // Check mutually exlusive switches")
+  $options.each { |o|
+    o_check = o.var + (o.type == :flag ? "_flag" : "_given")
+    o.conflict.each { |cos|
+      co = $opt_hash[cos]
+      co_check = co.var + (co.type == :flag ? "_flag" : "_given")
+      h.puts(<<EOS)
+    if(#{o_check} && #{co_check})
+      error("Switches [#{o.switches}] and [#{co.switches}] are mutually exclusive");
+EOS
+    }
+  }
+  # Check at_least
+  $options.any? { |o| o.at_least } and
+    h.puts("", "    // Check at_least requirements")
+  $options.each { |o|
+    next unless o.multiple && !o.at_least.nil?
+    h.puts(<<EOS)
+    if(#{o.var}_arg.size() < #{o.at_least})
+      error("[#{o.switches}] must be given at least #{o.at_least} times");
+EOS
+  }
+  
+  # Parse arguments
+  h.puts("", "    // Parse arguments")
+  if $args.size == 0 || !$args[-1].multiple
+    h.puts(<<EOS)
+    if(argc - optind != #{$args.size})
+      error("Requires exactly #{$args.size} argument#{$args.size > 1 ? "s" : ""}.");
+EOS
+  else
+    min_args = $args.size - 1 + $args[-1].at_least
+    h.puts(<<EOS)
+    if(argc - optind < #{min_args})
+      error("Requires at least #{min_args} argument#{min_args > 1 ? "s" : ""}.");
+EOS
+  end
+  $args.each { |a| h.puts("    " + a.parse_arg.join("\n    ")) }
+
+  # Check access rights
+  if ($options + $args).any? { |o| !o.access_types.empty? }
+    r_to_f = { "read" => "R_OK", "write" => "W_OK", "exec" => "X_OK" }
+    h.puts("", "    // Check access rights")
+    ($args + $options).each { |o|
+      next if o.access_types.empty?
+      mode = o.access_types.map { |t| r_to_f[t] }.join("|")
+      msg = Arg === o ? "Argument " + o.name : "Switch " + o.switches
+      msg += ", access right (#{o.access_types.join("|")}) failed for file '"
+      h.puts("    if(access(#{o.var}_arg, #{mode})) {",
+             "      err = \"#{msg}\";",
+             "      ((err += #{o.var}_arg) += \"': \") += strerror(errno);",
+             "      error(err.c_str());",
+             "    }")
+    }
+  end
+
+  h.puts("  }") # close parser
+
+  # Usage
+  if !$usage.nil?
+    ausage = quote_newline_dquotes($usage, "  ")
+  else
+    ausage = "Usage: #{$package || class_name} [options]"
+    $args.each { |a|
+     ausage += " #{a.name}:#{a.typestr || dflt_typestr(a.type)}#{a.multiple ? "+" : ""}"
+    }
+  end
+
+  h.puts(<<EOS)
+  static const char * usage() { return "#{ausage}"; }
+  class error {
+    int code_;
+    std::ostringstream msg_;
+
+    // Select the correct version (GNU or XSI) version of
+    // strerror_r. strerror_ behaves like the GNU version of strerror_r,
+    // regardless of which version is provided by the system.
+    static const char* strerror__(char* buf, int res) {
+      return res != -1 ? buf : "Invalid error";
+    }
+    static const char* strerror__(char* buf, char* res) {
+      return res;
+    }
+    static const char* strerror_(int err, char* buf, size_t buflen) {
+      return strerror__(buf, strerror_r(err, buf, buflen));
+    }
+    struct no_t { };
+
+  public:
+    static no_t no;
+    error(int code = EXIT_FAILURE) : code_(code) { }
+    explicit error(const char* msg, int code = EXIT_FAILURE) : code_(code)
+      { msg_ << msg; }
+    error(const std::string& msg, int code = EXIT_FAILURE) : code_(code)
+      { msg_ << msg; }
+    error& operator<<(no_t) {
+      char buf[1024];
+      msg_ << ": " << strerror_(errno, buf, sizeof(buf));
+      return *this;
+    }
+    template<typename T>
+    error& operator<<(const T& x) { msg_ << x; return (*this); }
+    ~error() {
+      ::std::cerr << "Error: " << msg_.str() << "\\n"
+                  << usage() << "\\n"
+                  << "Use --help for more information"
+                  << ::std::endl;
+      exit(code_);
+    }
+  };
+EOS
+
+  # Help
+  desc = ""
+  unless $purpose.nil?
+    desc += $purpose + "\\n\\n"
+  end
+  unless $description.nil?
+    desc += $description.split(/\n/).join("\\n\" \\\n    \"") + "\\n\\n"
+  end
+
+  h.puts(<<EOS)
+  static const char * help() { return
+    "#{desc}"
+    "Options (default value in (), *required):\\n"
+EOS
+  output_options_descriptions(h, $options, false)
+  usage_switch = " -U, "
+  usage_switch = " " * usage_switch.size if usage_no_U
+  usage_switch += "--usage"
+  h.puts("    \"#{usage_switch.ljust($switchesjust)}  Usage\\n\"")
+  help_switch = " -h, "
+  help_switch = " " * help_switch.size if help_no_h
+  help_switch += "--help"
+  h.puts("    \"#{help_switch.ljust($switchesjust)}  This message\\n\"")
+  h.puts("    \"#{"     --full-help".ljust($switchesjust)}  Detailed help\\n\"") if need_full
+  version_switch = " -V, "
+  version_switch = " " * version_switch.size if version_no_V
+  version_switch += "--version"
+  h.print("    \"#{version_switch.ljust($switchesjust)}  Version")
+  if $after_text.nil?
+    h.puts("\";")
+  else
+    h.puts("\\n\" \\", "  \"\\n\"")
+    atext = quote_newline_dquotes($after_text, "  ")
+    h.puts("    \"#{atext}\";")
+  end
+  h.puts("  }")
+
+  # Hidden help
+  has_hidden = $options.any? { |o| o.hidden }
+  if has_hidden 
+    h.puts(<<EOS)
+  static const char* hidden() { return
+    "Hidden options:\\n"
+EOS
+  output_options_descriptions(h, $options, true)
+  h.puts(<<EOS)
+    "";
+  }
+EOS
+  else
+    h.puts(<<EOS)
+  static const char* hidden() { return ""; }
+EOS
+  end
+  
+
+  # Version
+  h.puts("  void print_version(::std::ostream &os = std::cout) const {",
+         "#ifndef PACKAGE_VERSION",
+         "#define PACKAGE_VERSION \"0.0.0\"",
+         "#endif",
+         "    os << #{$version ? "\"" + $version + "\"" : "PACKAGE_VERSION"} << \"\\n\";",
+         "  }")
+  
+  # Dump
+  h.puts("  void dump(::std::ostream &os = std::cout) {")
+  ($options + $args).each { |o| h.puts("    os << #{o.dump.join(" << ")} << \"\\n\";") }
+  h.puts("  }")
+
+  # Private methods
+  h.puts(<<EOS)
+};
+EOS
+
+  # Initialize static members
+  # TODO: Should we have an option to put this in a .cc file?
+  $options.each { |o|
+    next unless o.type == :enum
+    h.puts("const char* const #{class_name}::#{o.var}::strs[#{o.enum.size + 1}] = { #{o.enum.map { |x| "\"#{x}\"" }.join(", ") }, (const char*)0 };")
+  }
+
+h.puts(<<EOS)
+#endif // __#{class_name.upcase}_HPP__"
+EOS
+end
diff --git a/lib/yaggo/stub.rb b/lib/yaggo/stub.rb
new file mode 100644
index 0000000..b40a84b
--- /dev/null
+++ b/lib/yaggo/stub.rb
@@ -0,0 +1,42 @@
+# This file is part of Yaggo.
+
+# Yaggo 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 3 of the License, or
+# (at your option) any later version.
+
+# Yaggo 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 Yaggo.  If not, see <http://www.gnu.org/licenses/>.
+
+
+def display_stub_yaggo_file file
+  stub = <<EOS
+# Stub file generated by yaggo. Modify to your liking
+purpose = "Foo software to do bar and baz, one line description"
+description = "A longer multiline description of how Foo does bar and baz
+
+Really, it works great, you should try all the options below
+"
+
+option("b", "bar") {
+  description "Insist on bar"
+  flag }
+option("z", "baz") {
+  description "Baz parameter"
+  int64; default "5" }
+option("l", "long") {
+  description "Long switch can be used multiple time"
+  int32; multiple }
+arg("OneArg") {
+  description "first arg"
+  string }
+EOS
+
+  out = file ? open(file, "W") : STDOUT
+  out.write(stub)
+end
diff --git a/lib/yaggo/version.rb b/lib/yaggo/version.rb
new file mode 100644
index 0000000..02d2e89
--- /dev/null
+++ b/lib/yaggo/version.rb
@@ -0,0 +1 @@
+$yaggo_version = "1.5.9"
diff --git a/lib/yaggo/zsh_completion.rb b/lib/yaggo/zsh_completion.rb
new file mode 100644
index 0000000..5ab3e24
--- /dev/null
+++ b/lib/yaggo/zsh_completion.rb
@@ -0,0 +1,94 @@
+# This file is part of Yaggo.
+
+# Yaggo 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 3 of the License, or
+# (at your option) any later version.
+
+# Yaggo 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 Yaggo.  If not, see <http://www.gnu.org/licenses/>.
+
+
+def zsh_conflict_option o
+  conflict_options = o.conflict + $options.map { |co|
+    (co.conflict.include?(o.short) || co.conflict.include?(o.long)) ? (co.short || co.long) : nil
+  }.compact.uniq
+  return "" if conflict_options.empty?
+  "'(" + conflict_options.map { |co_name|
+    co = $opt_hash[co_name]
+    [co.short && "-#{co.short}", co.long && "--#{co.long}"]
+  }.flatten.compact.uniq.join(" ") + ")'"
+end
+
+def zsh_switches_option o
+  switches = if o.type == :flag 
+               [o.short && "-#{o.short}", o.long && "--#{o.long}"]
+             else
+               [o.short && "-#{o.short}+", o.long && "--#{o.long}="]
+             end
+  switches.compact!
+  swstr = switches.size > 1 ? "{#{switches.join(",")}}" : switches[0]
+  swstr = "\\*#{swstr}" if o.multiple
+  swstr
+end
+
+def zsh_type_completion o, with_type = true
+  typedescr = o.typestr || o.type.id2name
+  typename = with_type ? ":" + typedescr : ""
+  guard_help = "#{typedescr} #{o.description || ""}"
+  case o.type
+  when :flag
+    return ""
+  when :enum
+    return "#{typename}:(#{o.enum.join(" ")})"
+  when :string, :c_string
+    case o.typestr || ""
+    when /file|path/i
+      return "#{typename}:_files"
+    when /dir/i
+      return "#{typename}:_files -/"
+    else
+      return typename
+    end
+  when :int32, :int64, :int, :long
+    suffixes = o.suffix ? "[kMGTPE]" : ""
+    return "#{typename}:_guard \"[0-9+-]##{suffixes}\" \"#{guard_help}\""
+  when :uint32, :uint64
+    suffixes = o.suffix ? "[kMGTPE]" : ""
+    return "#{typename}:_guard \"[0-9+]##{suffixes}\" \"#{guard_help}\""
+  when :double
+    suffixes = "[munpfakMGTPE]" if o.suffix
+    return "#{typename}:_guard \"[0-9.eE+-]##{suffixes}\" \"#{guard_help}\""
+  else
+    return default
+  end
+end
+
+def output_zsh_completion(fd, filename)
+  cmdname = File.basename(filename).gsub(/^_/, "")
+  
+  fd.puts("#compdef #{cmdname}", "",
+          "local context state state_descr line",
+          "typeset -A opt_args", "")
+  return if $options.empty? && $args.empty?
+  fd.puts("_arguments -s -S \\") 
+  $options.each { |o|
+    conflicts = zsh_conflict_option o
+    switches = zsh_switches_option o
+    descr = o.description ? "[#{o.description}]" : ""
+    action = zsh_type_completion o, true
+    fd.puts("#{conflicts}#{switches}'#{descr}#{action}' \\")
+  }
+  $args.each { |a|
+    descr = a.description || " "
+    action = zsh_type_completion a, false
+    many = a.multiple ? "*" : ""
+    fd.puts("'#{many}:#{descr}#{action}' \\")
+  }
+  fd.puts(" && return 0")
+end
diff --git a/license-header.txt b/license-header.txt
new file mode 100644
index 0000000..4c87ed0
--- /dev/null
+++ b/license-header.txt
@@ -0,0 +1,14 @@
+# This file is part of Yaggo.
+
+# Yaggo 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 3 of the License, or
+# (at your option) any later version.
+
+# Yaggo 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 Yaggo.  If not, see <http://www.gnu.org/licenses/>.
diff --git a/setup.rb b/setup.rb
new file mode 100644
index 0000000..424a5f3
--- /dev/null
+++ b/setup.rb
@@ -0,0 +1,1585 @@
+#
+# setup.rb
+#
+# Copyright (c) 2000-2005 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the terms of
+# the GNU LGPL, Lesser General Public License version 2.1.
+#
+
+unless Enumerable.method_defined?(:map)   # Ruby 1.4.6
+  module Enumerable
+    alias map collect
+  end
+end
+
+unless File.respond_to?(:read)   # Ruby 1.6
+  def File.read(fname)
+    open(fname) {|f|
+      return f.read
+    }
+  end
+end
+
+unless Errno.const_defined?(:ENOTEMPTY)   # Windows?
+  module Errno
+    class ENOTEMPTY
+      # We do not raise this exception, implementation is not needed.
+    end
+  end
+end
+
+def File.binread(fname)
+  open(fname, 'rb') {|f|
+    return f.read
+  }
+end
+
+# for corrupted Windows' stat(2)
+def File.dir?(path)
+  File.directory?((path[-1,1] == '/') ? path : path + '/')
+end
+
+
+class ConfigTable
+
+  include Enumerable
+
+  def initialize(rbconfig)
+    @rbconfig = rbconfig
+    @items = []
+    @table = {}
+    # options
+    @install_prefix = nil
+    @config_opt = nil
+    @verbose = true
+    @no_harm = false
+  end
+
+  attr_accessor :install_prefix
+  attr_accessor :config_opt
+
+  attr_writer :verbose
+
+  def verbose?
+    @verbose
+  end
+
+  attr_writer :no_harm
+
+  def no_harm?
+    @no_harm
+  end
+
+  def [](key)
+    lookup(key).resolve(self)
+  end
+
+  def []=(key, val)
+    lookup(key).set val
+  end
+
+  def names
+    @items.map {|i| i.name }
+  end
+
+  def each(&block)
+    @items.each(&block)
+  end
+
+  def key?(name)
+    @table.key?(name)
+  end
+
+  def lookup(name)
+    @table[name] or setup_rb_error "no such config item: #{name}"
+  end
+
+  def add(item)
+    @items.push item
+    @table[item.name] = item
+  end
+
+  def remove(name)
+    item = lookup(name)
+    @items.delete_if {|i| i.name == name }
+    @table.delete_if {|name, i| i.name == name }
+    item
+  end
+
+  def load_script(path, inst = nil)
+    if File.file?(path)
+      MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
+    end
+  end
+
+  def savefile
+    '.config'
+  end
+
+  def load_savefile
+    begin
+      File.foreach(savefile()) do |line|
+        k, v = *line.split(/=/, 2)
+        self[k] = v.strip
+      end
+    rescue Errno::ENOENT
+      setup_rb_error $!.message + "\n#{File.basename($0)} config first"
+    end
+  end
+
+  def save
+    @items.each {|i| i.value }
+    File.open(savefile(), 'w') {|f|
+      @items.each do |i|
+        f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
+      end
+    }
+  end
+
+  def load_standard_entries
+    standard_entries(@rbconfig).each do |ent|
+      add ent
+    end
+  end
+
+  def standard_entries(rbconfig)
+    c = rbconfig
+
+    rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
+
+    major = c['MAJOR'].to_i
+    minor = c['MINOR'].to_i
+    teeny = c['TEENY'].to_i
+    version = "#{major}.#{minor}"
+
+    # ruby ver. >= 1.4.4?
+    newpath_p = ((major >= 2) or
+                 ((major == 1) and
+                  ((minor >= 5) or
+                   ((minor == 4) and (teeny >= 4)))))
+
+    if c['rubylibdir']
+      # V > 1.6.3
+      libruby         = "#{c['prefix']}/lib/ruby"
+      librubyver      = c['rubylibdir']
+      librubyverarch  = c['archdir']
+      siteruby        = c['sitedir']
+      siterubyver     = c['sitelibdir']
+      siterubyverarch = c['sitearchdir']
+    elsif newpath_p
+      # 1.4.4 <= V <= 1.6.3
+      libruby         = "#{c['prefix']}/lib/ruby"
+      librubyver      = "#{c['prefix']}/lib/ruby/#{version}"
+      librubyverarch  = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
+      siteruby        = c['sitedir']
+      siterubyver     = "$siteruby/#{version}"
+      siterubyverarch = "$siterubyver/#{c['arch']}"
+    else
+      # V < 1.4.4
+      libruby         = "#{c['prefix']}/lib/ruby"
+      librubyver      = "#{c['prefix']}/lib/ruby/#{version}"
+      librubyverarch  = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
+      siteruby        = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
+      siterubyver     = siteruby
+      siterubyverarch = "$siterubyver/#{c['arch']}"
+    end
+    parameterize = lambda {|path|
+      path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
+    }
+
+    if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
+      makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
+    else
+      makeprog = 'make'
+    end
+
+    [
+      ExecItem.new('installdirs', 'std/site/home',
+                   'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
+          {|val, table|
+            case val
+            when 'std'
+              table['rbdir'] = '$librubyver'
+              table['sodir'] = '$librubyverarch'
+            when 'site'
+              table['rbdir'] = '$siterubyver'
+              table['sodir'] = '$siterubyverarch'
+            when 'home'
+              setup_rb_error '$HOME was not set' unless ENV['HOME']
+              table['prefix'] = ENV['HOME']
+              table['rbdir'] = '$libdir/ruby'
+              table['sodir'] = '$libdir/ruby'
+            end
+          },
+      PathItem.new('prefix', 'path', c['prefix'],
+                   'path prefix of target environment'),
+      PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
+                   'the directory for commands'),
+      PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
+                   'the directory for libraries'),
+      PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
+                   'the directory for shared data'),
+      PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
+                   'the directory for man pages'),
+      PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
+                   'the directory for system configuration files'),
+      PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
+                   'the directory for local state data'),
+      PathItem.new('libruby', 'path', libruby,
+                   'the directory for ruby libraries'),
+      PathItem.new('librubyver', 'path', librubyver,
+                   'the directory for standard ruby libraries'),
+      PathItem.new('librubyverarch', 'path', librubyverarch,
+                   'the directory for standard ruby extensions'),
+      PathItem.new('siteruby', 'path', siteruby,
+          'the directory for version-independent aux ruby libraries'),
+      PathItem.new('siterubyver', 'path', siterubyver,
+                   'the directory for aux ruby libraries'),
+      PathItem.new('siterubyverarch', 'path', siterubyverarch,
+                   'the directory for aux ruby binaries'),
+      PathItem.new('rbdir', 'path', '$siterubyver',
+                   'the directory for ruby scripts'),
+      PathItem.new('sodir', 'path', '$siterubyverarch',
+                   'the directory for ruby extentions'),
+      PathItem.new('rubypath', 'path', rubypath,
+                   'the path to set to #! line'),
+      ProgramItem.new('rubyprog', 'name', rubypath,
+                      'the ruby program using for installation'),
+      ProgramItem.new('makeprog', 'name', makeprog,
+                      'the make program to compile ruby extentions'),
+      SelectItem.new('shebang', 'all/ruby/never', 'ruby',
+                     'shebang line (#!) editing mode'),
+      BoolItem.new('without-ext', 'yes/no', 'no',
+                   'does not compile/install ruby extentions')
+    ]
+  end
+  private :standard_entries
+
+  def load_multipackage_entries
+    multipackage_entries().each do |ent|
+      add ent
+    end
+  end
+
+  def multipackage_entries
+    [
+      PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
+                               'package names that you want to install'),
+      PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
+                               'package names that you do not want to install')
+    ]
+  end
+  private :multipackage_entries
+
+  ALIASES = {
+    'std-ruby'         => 'librubyver',
+    'stdruby'          => 'librubyver',
+    'rubylibdir'       => 'librubyver',
+    'archdir'          => 'librubyverarch',
+    'site-ruby-common' => 'siteruby',     # For backward compatibility
+    'site-ruby'        => 'siterubyver',  # For backward compatibility
+    'bin-dir'          => 'bindir',
+    'bin-dir'          => 'bindir',
+    'rb-dir'           => 'rbdir',
+    'so-dir'           => 'sodir',
+    'data-dir'         => 'datadir',
+    'ruby-path'        => 'rubypath',
+    'ruby-prog'        => 'rubyprog',
+    'ruby'             => 'rubyprog',
+    'make-prog'        => 'makeprog',
+    'make'             => 'makeprog'
+  }
+
+  def fixup
+    ALIASES.each do |ali, name|
+      @table[ali] = @table[name]
+    end
+    @items.freeze
+    @table.freeze
+    @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
+  end
+
+  def parse_opt(opt)
+    m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
+    m.to_a[1,2]
+  end
+
+  def dllext
+    @rbconfig['DLEXT']
+  end
+
+  def value_config?(name)
+    lookup(name).value?
+  end
+
+  class Item
+    def initialize(name, template, default, desc)
+      @name = name.freeze
+      @template = template
+      @value = default
+      @default = default
+      @description = desc
+    end
+
+    attr_reader :name
+    attr_reader :description
+
+    attr_accessor :default
+    alias help_default default
+
+    def help_opt
+      "--#{@name}=#{@template}"
+    end
+
+    def value?
+      true
+    end
+
+    def value
+      @value
+    end
+
+    def resolve(table)
+      @value.gsub(%r<\$([^/]+)>) { table[$1] }
+    end
+
+    def set(val)
+      @value = check(val)
+    end
+
+    private
+
+    def check(val)
+      setup_rb_error "config: --#{name} requires argument" unless val
+      val
+    end
+  end
+
+  class BoolItem < Item
+    def config_type
+      'bool'
+    end
+
+    def help_opt
+      "--#{@name}"
+    end
+
+    private
+
+    def check(val)
+      return 'yes' unless val
+      case val
+      when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
+      when /\An(o)?\z/i, /\Af(alse)\z/i  then 'no'
+      else
+        setup_rb_error "config: --#{@name} accepts only yes/no for argument"
+      end
+    end
+  end
+
+  class PathItem < Item
+    def config_type
+      'path'
+    end
+
+    private
+
+    def check(path)
+      setup_rb_error "config: --#{@name} requires argument"  unless path
+      path[0,1] == '$' ? path : File.expand_path(path)
+    end
+  end
+
+  class ProgramItem < Item
+    def config_type
+      'program'
+    end
+  end
+
+  class SelectItem < Item
+    def initialize(name, selection, default, desc)
+      super
+      @ok = selection.split('/')
+    end
+
+    def config_type
+      'select'
+    end
+
+    private
+
+    def check(val)
+      unless @ok.include?(val.strip)
+        setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
+      end
+      val.strip
+    end
+  end
+
+  class ExecItem < Item
+    def initialize(name, selection, desc, &block)
+      super name, selection, nil, desc
+      @ok = selection.split('/')
+      @action = block
+    end
+
+    def config_type
+      'exec'
+    end
+
+    def value?
+      false
+    end
+
+    def resolve(table)
+      setup_rb_error "$#{name()} wrongly used as option value"
+    end
+
+    undef set
+
+    def evaluate(val, table)
+      v = val.strip.downcase
+      unless @ok.include?(v)
+        setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
+      end
+      @action.call v, table
+    end
+  end
+
+  class PackageSelectionItem < Item
+    def initialize(name, template, default, help_default, desc)
+      super name, template, default, desc
+      @help_default = help_default
+    end
+
+    attr_reader :help_default
+
+    def config_type
+      'package'
+    end
+
+    private
+
+    def check(val)
+      unless File.dir?("packages/#{val}")
+        setup_rb_error "config: no such package: #{val}"
+      end
+      val
+    end
+  end
+
+  class MetaConfigEnvironment
+    def initialize(config, installer)
+      @config = config
+      @installer = installer
+    end
+
+    def config_names
+      @config.names
+    end
+
+    def config?(name)
+      @config.key?(name)
+    end
+
+    def bool_config?(name)
+      @config.lookup(name).config_type == 'bool'
+    end
+
+    def path_config?(name)
+      @config.lookup(name).config_type == 'path'
+    end
+
+    def value_config?(name)
+      @config.lookup(name).config_type != 'exec'
+    end
+
+    def add_config(item)
+      @config.add item
+    end
+
+    def add_bool_config(name, default, desc)
+      @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
+    end
+
+    def add_path_config(name, default, desc)
+      @config.add PathItem.new(name, 'path', default, desc)
+    end
+
+    def set_config_default(name, default)
+      @config.lookup(name).default = default
+    end
+
+    def remove_config(name)
+      @config.remove(name)
+    end
+
+    # For only multipackage
+    def packages
+      raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
+      @installer.packages
+    end
+
+    # For only multipackage
+    def declare_packages(list)
+      raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
+      @installer.packages = list
+    end
+  end
+
+end   # class ConfigTable
+
+
+# This module requires: #verbose?, #no_harm?
+module FileOperations
+
+  def mkdir_p(dirname, prefix = nil)
+    dirname = prefix + File.expand_path(dirname) if prefix
+    $stderr.puts "mkdir -p #{dirname}" if verbose?
+    return if no_harm?
+
+    # Does not check '/', it's too abnormal.
+    dirs = File.expand_path(dirname).split(%r<(?=/)>)
+    if /\A[a-z]:\z/i =~ dirs[0]
+      disk = dirs.shift
+      dirs[0] = disk + dirs[0]
+    end
+    dirs.each_index do |idx|
+      path = dirs[0..idx].join('')
+      Dir.mkdir path unless File.dir?(path)
+    end
+  end
+
+  def rm_f(path)
+    $stderr.puts "rm -f #{path}" if verbose?
+    return if no_harm?
+    force_remove_file path
+  end
+
+  def rm_rf(path)
+    $stderr.puts "rm -rf #{path}" if verbose?
+    return if no_harm?
+    remove_tree path
+  end
+
+  def remove_tree(path)
+    if File.symlink?(path)
+      remove_file path
+    elsif File.dir?(path)
+      remove_tree0 path
+    else
+      force_remove_file path
+    end
+  end
+
+  def remove_tree0(path)
+    Dir.foreach(path) do |ent|
+      next if ent == '.'
+      next if ent == '..'
+      entpath = "#{path}/#{ent}"
+      if File.symlink?(entpath)
+        remove_file entpath
+      elsif File.dir?(entpath)
+        remove_tree0 entpath
+      else
+        force_remove_file entpath
+      end
+    end
+    begin
+      Dir.rmdir path
+    rescue Errno::ENOTEMPTY
+      # directory may not be empty
+    end
+  end
+
+  def move_file(src, dest)
+    force_remove_file dest
+    begin
+      File.rename src, dest
+    rescue
+      File.open(dest, 'wb') {|f|
+        f.write File.binread(src)
+      }
+      File.chmod File.stat(src).mode, dest
+      File.unlink src
+    end
+  end
+
+  def force_remove_file(path)
+    begin
+      remove_file path
+    rescue
+    end
+  end
+
+  def remove_file(path)
+    File.chmod 0777, path
+    File.unlink path
+  end
+
+  def install(from, dest, mode, prefix = nil)
+    $stderr.puts "install #{from} #{dest}" if verbose?
+    return if no_harm?
+
+    realdest = prefix ? prefix + File.expand_path(dest) : dest
+    realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
+    str = File.binread(from)
+    if diff?(str, realdest)
+      verbose_off {
+        rm_f realdest if File.exist?(realdest)
+      }
+      File.open(realdest, 'wb') {|f|
+        f.write str
+      }
+      File.chmod mode, realdest
+
+      File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
+        if prefix
+          f.puts realdest.sub(prefix, '')
+        else
+          f.puts realdest
+        end
+      }
+    end
+  end
+
+  def diff?(new_content, path)
+    return true unless File.exist?(path)
+    new_content != File.binread(path)
+  end
+
+  def command(*args)
+    $stderr.puts args.join(' ') if verbose?
+    system(*args) or raise RuntimeError,
+        "system(#{args.map{|a| a.inspect }.join(' ')}) failed"
+  end
+
+  def ruby(*args)
+    command config('rubyprog'), *args
+  end
+  
+  def make(task = nil)
+    command(*[config('makeprog'), task].compact)
+  end
+
+  def extdir?(dir)
+    File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
+  end
+
+  def files_of(dir)
+    Dir.open(dir) {|d|
+      return d.select {|ent| File.file?("#{dir}/#{ent}") }
+    }
+  end
+
+  DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
+
+  def directories_of(dir)
+    Dir.open(dir) {|d|
+      return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
+    }
+  end
+
+end
+
+
+# This module requires: #srcdir_root, #objdir_root, #relpath
+module HookScriptAPI
+
+  def get_config(key)
+    @config[key]
+  end
+
+  alias config get_config
+
+  # obsolete: use metaconfig to change configuration
+  def set_config(key, val)
+    @config[key] = val
+  end
+
+  #
+  # srcdir/objdir (works only in the package directory)
+  #
+
+  def curr_srcdir
+    "#{srcdir_root()}/#{relpath()}"
+  end
+
+  def curr_objdir
+    "#{objdir_root()}/#{relpath()}"
+  end
+
+  def srcfile(path)
+    "#{curr_srcdir()}/#{path}"
+  end
+
+  def srcexist?(path)
+    File.exist?(srcfile(path))
+  end
+
+  def srcdirectory?(path)
+    File.dir?(srcfile(path))
+  end
+  
+  def srcfile?(path)
+    File.file?(srcfile(path))
+  end
+
+  def srcentries(path = '.')
+    Dir.open("#{curr_srcdir()}/#{path}") {|d|
+      return d.to_a - %w(. ..)
+    }
+  end
+
+  def srcfiles(path = '.')
+    srcentries(path).select {|fname|
+      File.file?(File.join(curr_srcdir(), path, fname))
+    }
+  end
+
+  def srcdirectories(path = '.')
+    srcentries(path).select {|fname|
+      File.dir?(File.join(curr_srcdir(), path, fname))
+    }
+  end
+
+end
+
+
+class ToplevelInstaller
+
+  Version   = '3.4.1'
+  Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
+
+  TASKS = [
+    [ 'all',      'do config, setup, then install' ],
+    [ 'config',   'saves your configurations' ],
+    [ 'show',     'shows current configuration' ],
+    [ 'setup',    'compiles ruby extentions and others' ],
+    [ 'install',  'installs files' ],
+    [ 'test',     'run all tests in test/' ],
+    [ 'clean',    "does `make clean' for each extention" ],
+    [ 'distclean',"does `make distclean' for each extention" ]
+  ]
+
+  def ToplevelInstaller.invoke
+    config = ConfigTable.new(load_rbconfig())
+    config.load_standard_entries
+    config.load_multipackage_entries if multipackage?
+    config.fixup
+    klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
+    klass.new(File.dirname($0), config).invoke
+  end
+
+  def ToplevelInstaller.multipackage?
+    File.dir?(File.dirname($0) + '/packages')
+  end
+
+  def ToplevelInstaller.load_rbconfig
+    if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
+      ARGV.delete(arg)
+      load File.expand_path(arg.split(/=/, 2)[1])
+      $".push 'rbconfig.rb'
+    else
+      require 'rbconfig'
+    end
+    ::Config::CONFIG
+  end
+
+  def initialize(ardir_root, config)
+    @ardir = File.expand_path(ardir_root)
+    @config = config
+    # cache
+    @valid_task_re = nil
+  end
+
+  def config(key)
+    @config[key]
+  end
+
+  def inspect
+    "#<#{self.class} #{__id__()}>"
+  end
+
+  def invoke
+    run_metaconfigs
+    case task = parsearg_global()
+    when nil, 'all'
+      parsearg_config
+      init_installers
+      exec_config
+      exec_setup
+      exec_install
+    else
+      case task
+      when 'config', 'test'
+        ;
+      when 'clean', 'distclean'
+        @config.load_savefile if File.exist?(@config.savefile)
+      else
+        @config.load_savefile
+      end
+      __send__ "parsearg_#{task}"
+      init_installers
+      __send__ "exec_#{task}"
+    end
+  end
+  
+  def run_metaconfigs
+    @config.load_script "#{@ardir}/metaconfig"
+  end
+
+  def init_installers
+    @installer = Installer.new(@config, @ardir, File.expand_path('.'))
+  end
+
+  #
+  # Hook Script API bases
+  #
+
+  def srcdir_root
+    @ardir
+  end
+
+  def objdir_root
+    '.'
+  end
+
+  def relpath
+    '.'
+  end
+
+  #
+  # Option Parsing
+  #
+
+  def parsearg_global
+    while arg = ARGV.shift
+      case arg
+      when /\A\w+\z/
+        setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
+        return arg
+      when '-q', '--quiet'
+        @config.verbose = false
+      when '--verbose'
+        @config.verbose = true
+      when '--help'
+        print_usage $stdout
+        exit 0
+      when '--version'
+        puts "#{File.basename($0)} version #{Version}"
+        exit 0
+      when '--copyright'
+        puts Copyright
+        exit 0
+      else
+        setup_rb_error "unknown global option '#{arg}'"
+      end
+    end
+    nil
+  end
+
+  def valid_task?(t)
+    valid_task_re() =~ t
+  end
+
+  def valid_task_re
+    @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
+  end
+
+  def parsearg_no_options
+    unless ARGV.empty?
+      task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
+      setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
+    end
+  end
+
+  alias parsearg_show       parsearg_no_options
+  alias parsearg_setup      parsearg_no_options
+  alias parsearg_test       parsearg_no_options
+  alias parsearg_clean      parsearg_no_options
+  alias parsearg_distclean  parsearg_no_options
+
+  def parsearg_config
+    evalopt = []
+    set = []
+    @config.config_opt = []
+    while i = ARGV.shift
+      if /\A--?\z/ =~ i
+        @config.config_opt = ARGV.dup
+        break
+      end
+      name, value = *@config.parse_opt(i)
+      if @config.value_config?(name)
+        @config[name] = value
+      else
+        evalopt.push [name, value]
+      end
+      set.push name
+    end
+    evalopt.each do |name, value|
+      @config.lookup(name).evaluate value, @config
+    end
+    # Check if configuration is valid
+    set.each do |n|
+      @config[n] if @config.value_config?(n)
+    end
+  end
+
+  def parsearg_install
+    @config.no_harm = false
+    @config.install_prefix = ''
+    while a = ARGV.shift
+      case a
+      when '--no-harm'
+        @config.no_harm = true
+      when /\A--prefix=/
+        path = a.split(/=/, 2)[1]
+        path = File.expand_path(path) unless path[0,1] == '/'
+        @config.install_prefix = path
+      else
+        setup_rb_error "install: unknown option #{a}"
+      end
+    end
+  end
+
+  def print_usage(out)
+    out.puts 'Typical Installation Procedure:'
+    out.puts "  $ ruby #{File.basename $0} config"
+    out.puts "  $ ruby #{File.basename $0} setup"
+    out.puts "  # ruby #{File.basename $0} install (may require root privilege)"
+    out.puts
+    out.puts 'Detailed Usage:'
+    out.puts "  ruby #{File.basename $0} <global option>"
+    out.puts "  ruby #{File.basename $0} [<global options>] <task> [<task options>]"
+
+    fmt = "  %-24s %s\n"
+    out.puts
+    out.puts 'Global options:'
+    out.printf fmt, '-q,--quiet',   'suppress message outputs'
+    out.printf fmt, '   --verbose', 'output messages verbosely'
+    out.printf fmt, '   --help',    'print this message'
+    out.printf fmt, '   --version', 'print version and quit'
+    out.printf fmt, '   --copyright',  'print copyright and quit'
+    out.puts
+    out.puts 'Tasks:'
+    TASKS.each do |name, desc|
+      out.printf fmt, name, desc
+    end
+
+    fmt = "  %-24s %s [%s]\n"
+    out.puts
+    out.puts 'Options for CONFIG or ALL:'
+    @config.each do |item|
+      out.printf fmt, item.help_opt, item.description, item.help_default
+    end
+    out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
+    out.puts
+    out.puts 'Options for INSTALL:'
+    out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
+    out.printf fmt, '--prefix=path',  'install path prefix', ''
+    out.puts
+  end
+
+  #
+  # Task Handlers
+  #
+
+  def exec_config
+    @installer.exec_config
+    @config.save   # must be final
+  end
+
+  def exec_setup
+    @installer.exec_setup
+  end
+
+  def exec_install
+    @installer.exec_install
+  end
+
+  def exec_test
+    @installer.exec_test
+  end
+
+  def exec_show
+    @config.each do |i|
+      printf "%-20s %s\n", i.name, i.value if i.value?
+    end
+  end
+
+  def exec_clean
+    @installer.exec_clean
+  end
+
+  def exec_distclean
+    @installer.exec_distclean
+  end
+
+end   # class ToplevelInstaller
+
+
+class ToplevelInstallerMulti < ToplevelInstaller
+
+  include FileOperations
+
+  def initialize(ardir_root, config)
+    super
+    @packages = directories_of("#{@ardir}/packages")
+    raise 'no package exists' if @packages.empty?
+    @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
+  end
+
+  def run_metaconfigs
+    @config.load_script "#{@ardir}/metaconfig", self
+    @packages.each do |name|
+      @config.load_script "#{@ardir}/packages/#{name}/metaconfig"
+    end
+  end
+
+  attr_reader :packages
+
+  def packages=(list)
+    raise 'package list is empty' if list.empty?
+    list.each do |name|
+      raise "directory packages/#{name} does not exist"\
+              unless File.dir?("#{@ardir}/packages/#{name}")
+    end
+    @packages = list
+  end
+
+  def init_installers
+    @installers = {}
+    @packages.each do |pack|
+      @installers[pack] = Installer.new(@config,
+                                       "#{@ardir}/packages/#{pack}",
+                                       "packages/#{pack}")
+    end
+    with    = extract_selection(config('with'))
+    without = extract_selection(config('without'))
+    @selected = @installers.keys.select {|name|
+                  (with.empty? or with.include?(name)) \
+                      and not without.include?(name)
+                }
+  end
+
+  def extract_selection(list)
+    a = list.split(/,/)
+    a.each do |name|
+      setup_rb_error "no such package: #{name}"  unless @installers.key?(name)
+    end
+    a
+  end
+
+  def print_usage(f)
+    super
+    f.puts 'Inluded packages:'
+    f.puts '  ' + @packages.sort.join(' ')
+    f.puts
+  end
+
+  #
+  # Task Handlers
+  #
+
+  def exec_config
+    run_hook 'pre-config'
+    each_selected_installers {|inst| inst.exec_config }
+    run_hook 'post-config'
+    @config.save   # must be final
+  end
+
+  def exec_setup
+    run_hook 'pre-setup'
+    each_selected_installers {|inst| inst.exec_setup }
+    run_hook 'post-setup'
+  end
+
+  def exec_install
+    run_hook 'pre-install'
+    each_selected_installers {|inst| inst.exec_install }
+    run_hook 'post-install'
+  end
+
+  def exec_test
+    run_hook 'pre-test'
+    each_selected_installers {|inst| inst.exec_test }
+    run_hook 'post-test'
+  end
+
+  def exec_clean
+    rm_f @config.savefile
+    run_hook 'pre-clean'
+    each_selected_installers {|inst| inst.exec_clean }
+    run_hook 'post-clean'
+  end
+
+  def exec_distclean
+    rm_f @config.savefile
+    run_hook 'pre-distclean'
+    each_selected_installers {|inst| inst.exec_distclean }
+    run_hook 'post-distclean'
+  end
+
+  #
+  # lib
+  #
+
+  def each_selected_installers
+    Dir.mkdir 'packages' unless File.dir?('packages')
+    @selected.each do |pack|
+      $stderr.puts "Processing the package `#{pack}' ..." if verbose?
+      Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
+      Dir.chdir "packages/#{pack}"
+      yield @installers[pack]
+      Dir.chdir '../..'
+    end
+  end
+
+  def run_hook(id)
+    @root_installer.run_hook id
+  end
+
+  # module FileOperations requires this
+  def verbose?
+    @config.verbose?
+  end
+
+  # module FileOperations requires this
+  def no_harm?
+    @config.no_harm?
+  end
+
+end   # class ToplevelInstallerMulti
+
+
+class Installer
+
+  FILETYPES = %w( bin lib ext data conf man )
+
+  include FileOperations
+  include HookScriptAPI
+
+  def initialize(config, srcroot, objroot)
+    @config = config
+    @srcdir = File.expand_path(srcroot)
+    @objdir = File.expand_path(objroot)
+    @currdir = '.'
+  end
+
+  def inspect
+    "#<#{self.class} #{File.basename(@srcdir)}>"
+  end
+
+  def noop(rel)
+  end
+
+  #
+  # Hook Script API base methods
+  #
+
+  def srcdir_root
+    @srcdir
+  end
+
+  def objdir_root
+    @objdir
+  end
+
+  def relpath
+    @currdir
+  end
+
+  #
+  # Config Access
+  #
+
+  # module FileOperations requires this
+  def verbose?
+    @config.verbose?
+  end
+
+  # module FileOperations requires this
+  def no_harm?
+    @config.no_harm?
+  end
+
+  def verbose_off
+    begin
+      save, @config.verbose = @config.verbose?, false
+      yield
+    ensure
+      @config.verbose = save
+    end
+  end
+
+  #
+  # TASK config
+  #
+
+  def exec_config
+    exec_task_traverse 'config'
+  end
+
+  alias config_dir_bin noop
+  alias config_dir_lib noop
+
+  def config_dir_ext(rel)
+    extconf if extdir?(curr_srcdir())
+  end
+
+  alias config_dir_data noop
+  alias config_dir_conf noop
+  alias config_dir_man noop
+
+  def extconf
+    ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
+  end
+
+  #
+  # TASK setup
+  #
+
+  def exec_setup
+    exec_task_traverse 'setup'
+  end
+
+  def setup_dir_bin(rel)
+    files_of(curr_srcdir()).each do |fname|
+      update_shebang_line "#{curr_srcdir()}/#{fname}"
+    end
+  end
+
+  alias setup_dir_lib noop
+
+  def setup_dir_ext(rel)
+    make if extdir?(curr_srcdir())
+  end
+
+  alias setup_dir_data noop
+  alias setup_dir_conf noop
+  alias setup_dir_man noop
+
+  def update_shebang_line(path)
+    return if no_harm?
+    return if config('shebang') == 'never'
+    old = Shebang.load(path)
+    if old
+      $stderr.puts "warning: #{path}: Shebang line includes too many args.  It is not portable and your program may not work." if old.args.size > 1
+      new = new_shebang(old)
+      return if new.to_s == old.to_s
+    else
+      return unless config('shebang') == 'all'
+      new = Shebang.new(config('rubypath'))
+    end
+    $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
+    open_atomic_writer(path) {|output|
+      File.open(path, 'rb') {|f|
+        f.gets if old   # discard
+        output.puts new.to_s
+        output.print f.read
+      }
+    }
+  end
+
+  def new_shebang(old)
+    if /\Aruby/ =~ File.basename(old.cmd)
+      Shebang.new(config('rubypath'), old.args)
+    elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
+      Shebang.new(config('rubypath'), old.args[1..-1])
+    else
+      return old unless config('shebang') == 'all'
+      Shebang.new(config('rubypath'))
+    end
+  end
+
+  def open_atomic_writer(path, &block)
+    tmpfile = File.basename(path) + '.tmp'
+    begin
+      File.open(tmpfile, 'wb', &block)
+      File.rename tmpfile, File.basename(path)
+    ensure
+      File.unlink tmpfile if File.exist?(tmpfile)
+    end
+  end
+
+  class Shebang
+    def Shebang.load(path)
+      line = nil
+      File.open(path) {|f|
+        line = f.gets
+      }
+      return nil unless /\A#!/ =~ line
+      parse(line)
+    end
+
+    def Shebang.parse(line)
+      cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
+      new(cmd, args)
+    end
+
+    def initialize(cmd, args = [])
+      @cmd = cmd
+      @args = args
+    end
+
+    attr_reader :cmd
+    attr_reader :args
+
+    def to_s
+      "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
+    end
+  end
+
+  #
+  # TASK install
+  #
+
+  def exec_install
+    rm_f 'InstalledFiles'
+    exec_task_traverse 'install'
+  end
+
+  def install_dir_bin(rel)
+    install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
+  end
+
+  def install_dir_lib(rel)
+    install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
+  end
+
+  def install_dir_ext(rel)
+    return unless extdir?(curr_srcdir())
+    install_files rubyextentions('.'),
+                  "#{config('sodir')}/#{File.dirname(rel)}",
+                  0555
+  end
+
+  def install_dir_data(rel)
+    install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
+  end
+
+  def install_dir_conf(rel)
+    # FIXME: should not remove current config files
+    # (rename previous file to .old/.org)
+    install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
+  end
+
+  def install_dir_man(rel)
+    install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
+  end
+
+  def install_files(list, dest, mode)
+    mkdir_p dest, @config.install_prefix
+    list.each do |fname|
+      install fname, dest, mode, @config.install_prefix
+    end
+  end
+
+  def libfiles
+    glob_reject(%w(*.y *.output), targetfiles())
+  end
+
+  def rubyextentions(dir)
+    ents = glob_select("*.#{@config.dllext}", targetfiles())
+    if ents.empty?
+      setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
+    end
+    ents
+  end
+
+  def targetfiles
+    mapdir(existfiles() - hookfiles())
+  end
+
+  def mapdir(ents)
+    ents.map {|ent|
+      if File.exist?(ent)
+      then ent                         # objdir
+      else "#{curr_srcdir()}/#{ent}"   # srcdir
+      end
+    }
+  end
+
+  # picked up many entries from cvs-1.11.1/src/ignore.c
+  JUNK_FILES = %w( 
+    core RCSLOG tags TAGS .make.state
+    .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
+    *~ *.old *.bak *.BAK *.orig *.rej _$* *$
+
+    *.org *.in .*
+  )
+
+  def existfiles
+    glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
+  end
+
+  def hookfiles
+    %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
+      %w( config setup install clean ).map {|t| sprintf(fmt, t) }
+    }.flatten
+  end
+
+  def glob_select(pat, ents)
+    re = globs2re([pat])
+    ents.select {|ent| re =~ ent }
+  end
+
+  def glob_reject(pats, ents)
+    re = globs2re(pats)
+    ents.reject {|ent| re =~ ent }
+  end
+
+  GLOB2REGEX = {
+    '.' => '\.',
+    '$' => '\$',
+    '#' => '\#',
+    '*' => '.*'
+  }
+
+  def globs2re(pats)
+    /\A(?:#{
+      pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
+    })\z/
+  end
+
+  #
+  # TASK test
+  #
+
+  TESTDIR = 'test'
+
+  def exec_test
+    unless File.directory?('test')
+      $stderr.puts 'no test in this package' if verbose?
+      return
+    end
+    $stderr.puts 'Running tests...' if verbose?
+    begin
+      require 'test/unit'
+    rescue LoadError
+      setup_rb_error 'test/unit cannot loaded.  You need Ruby 1.8 or later to invoke this task.'
+    end
+    runner = Test::Unit::AutoRunner.new(true)
+    runner.to_run << TESTDIR
+    runner.run
+  end
+
+  #
+  # TASK clean
+  #
+
+  def exec_clean
+    exec_task_traverse 'clean'
+    rm_f @config.savefile
+    rm_f 'InstalledFiles'
+  end
+
+  alias clean_dir_bin noop
+  alias clean_dir_lib noop
+  alias clean_dir_data noop
+  alias clean_dir_conf noop
+  alias clean_dir_man noop
+
+  def clean_dir_ext(rel)
+    return unless extdir?(curr_srcdir())
+    make 'clean' if File.file?('Makefile')
+  end
+
+  #
+  # TASK distclean
+  #
+
+  def exec_distclean
+    exec_task_traverse 'distclean'
+    rm_f @config.savefile
+    rm_f 'InstalledFiles'
+  end
+
+  alias distclean_dir_bin noop
+  alias distclean_dir_lib noop
+
+  def distclean_dir_ext(rel)
+    return unless extdir?(curr_srcdir())
+    make 'distclean' if File.file?('Makefile')
+  end
+
+  alias distclean_dir_data noop
+  alias distclean_dir_conf noop
+  alias distclean_dir_man noop
+
+  #
+  # Traversing
+  #
+
+  def exec_task_traverse(task)
+    run_hook "pre-#{task}"
+    FILETYPES.each do |type|
+      if type == 'ext' and config('without-ext') == 'yes'
+        $stderr.puts 'skipping ext/* by user option' if verbose?
+        next
+      end
+      traverse task, type, "#{task}_dir_#{type}"
+    end
+    run_hook "post-#{task}"
+  end
+
+  def traverse(task, rel, mid)
+    dive_into(rel) {
+      run_hook "pre-#{task}"
+      __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
+      directories_of(curr_srcdir()).each do |d|
+        traverse task, "#{rel}/#{d}", mid
+      end
+      run_hook "post-#{task}"
+    }
+  end
+
+  def dive_into(rel)
+    return unless File.dir?("#{@srcdir}/#{rel}")
+
+    dir = File.basename(rel)
+    Dir.mkdir dir unless File.dir?(dir)
+    prevdir = Dir.pwd
+    Dir.chdir dir
+    $stderr.puts '---> ' + rel if verbose?
+    @currdir = rel
+    yield
+    Dir.chdir prevdir
+    $stderr.puts '<--- ' + rel if verbose?
+    @currdir = File.dirname(rel)
+  end
+
+  def run_hook(id)
+    path = [ "#{curr_srcdir()}/#{id}",
+             "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
+    return unless path
+    begin
+      instance_eval File.read(path), path, 1
+    rescue
+      raise if $DEBUG
+      setup_rb_error "hook #{path} failed:\n" + $!.message
+    end
+  end
+
+end   # class Installer
+
+
+class SetupError < StandardError; end
+
+def setup_rb_error(msg)
+  raise SetupError, msg
+end
+
+if $0 == __FILE__
+  begin
+    ToplevelInstaller.invoke
+  rescue SetupError
+    raise if $DEBUG
+    $stderr.puts $!.message
+    $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
+    exit 1
+  end
+end
diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 0000000..ebb3ade
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,20 @@
+CC = g++
+CPPFLAGS = -I. -Wall -Werror
+CXXFLAGS = -O2
+YAGGO = ../bin/yaggo
+RY = ruby -I../lib $(YAGGO)
+
+all: count
+
+count: count.o
+count_cmdline.hpp _count: count.yaggo $(YAGGO)
+	$(RY) --debug --zc _count $<
+count.o: count.cpp count_cmdline.hpp
+
+test_errno.hpp: test_errno.yaggo $(YAGGO)
+	$(RY) --debug $<
+test_errno: test_errno.o
+test_errno.o: test_errno.cc test_errno.hpp
+
+clean:
+	rm -f *.o count
diff --git a/test/count.cpp b/test/count.cpp
new file mode 100644
index 0000000..12aabce
--- /dev/null
+++ b/test/count.cpp
@@ -0,0 +1,33 @@
+#include <iostream>
+#include "count_cmdline.hpp"
+
+#define CONV(type)                                                      \
+  try {                                                                 \
+    std::cout << "as_" << #type << ": "                                 \
+              << args.verra_arg.as_ ## type () << std::endl;            \
+  } catch(std::exception &e) {                                           \
+    std::cerr << "Conv to " << #type << " failed: "                     \
+              << e.what() << std::endl;                                  \
+  }
+
+
+int main(int argc, char *argv[])
+{
+  count_cmdline args(argc, argv);
+  args.dump(std::cout);
+  CONV(uint32);
+  CONV(uint64);
+  CONV(int32);
+  CONV(int64);
+  CONV(double);
+  if(args.severity_arg == count_cmdline::severity::low)
+    std::cout << "Pfiou!\n";
+  try {
+    std::cout << args.verra_arg.as_enum(count_cmdline::severity::strs) << "\n";
+  } catch(std::exception& e) {
+    std::cerr << "Conv to enum failed: " << e.what() << std::endl;
+  }
+  if(args.secret_flag)
+    std::cerr << "How did you know about the --secret option?" << std::endl;
+  return 0;
+}
diff --git a/test/count_cmdline.yaggo b/test/count_cmdline.yaggo
new file mode 100755
index 0000000..720425d
--- /dev/null
+++ b/test/count_cmdline.yaggo
@@ -0,0 +1,84 @@
+purpose = "Count k-mers or qmers in fasta or fastq files"
+package "jellyfish count"
+description <<EOS
+Count k-mers in fasta or fastq files.
+
+You see it works pretty well
+EOS
+version "0.0.1"
+
+license "My great license
+Enjoy!"
+
+output "count_cmdline.hpp"
+name "count_cmdline"
+
+option("mer-len", "m") {
+  required; description "Length of mer"
+  uint32; default "314159"
+}
+option("size", "s") {
+  uint64; required; suffix; description "Hash size"
+}
+option("threads", "t") {
+  uint32; default 1; description "Number of threads"
+}
+option("output", "o") {
+  string; default "mer_counts"; description "Output prefix"
+  conflict "c", "high"; typestr "dir"
+}
+option("counter-len", "c") {
+  uint32; default "7"; typestr "Length in bits"
+  description "Length of counting field"
+}
+option("high", "h") {
+  on; description "Am I high?"
+}
+option("severity") {
+  description "Severity description"
+  enum "low", "middle", "high"
+#  default 2
+}
+option("out-counter-len") {
+  hidden; uint32; default "4"; typestr "Length in bytes"
+  description "Length of counter fiel in output"
+}
+option("both-strands", "C") {
+  flag; off; description "Count both strand, canonical representation"
+}
+option("lib") {
+  string; multiple
+  description "Boggus lib"
+}
+option("str") {
+  description "C string"
+  c_string }
+option("vstr") {
+  description "vector of string"
+  c_string; multiple }
+option("numbers") {
+  int64; multiple
+  description "Many ints"
+}
+option("double") {
+  double; suffix; multiple
+  description "Many doubles"
+}
+option("verra") {
+  string; typestr "who knows but it is way too long anyhow."
+  description "On verra"
+}
+option("file") {
+  description "file"
+  c_string; access("write"); typestr "path" }
+option("secret") {
+  description "Very secret option"
+  off; secret }
+arg("first") {
+  c_string; typestr "path"
+  description "First"; access "read", "exec"
+}
+arg("second") {
+  uint32; multiple
+  description "Plenty of ints"
+}
diff --git a/test/test_errno.cc b/test/test_errno.cc
new file mode 100644
index 0000000..5a3368c
--- /dev/null
+++ b/test/test_errno.cc
@@ -0,0 +1,12 @@
+#include <iostream>
+#include "test_errno.hpp"
+
+int main(int argc, char *argv[])
+{
+  args_t args(argc, argv);
+
+  if(argc > 1)
+    args_t::error() << "Error" << args_t::error::no;
+
+  return 0;
+}
diff --git a/test/test_errno.yaggo b/test/test_errno.yaggo
new file mode 100644
index 0000000..198e460
--- /dev/null
+++ b/test/test_errno.yaggo
@@ -0,0 +1,7 @@
+description "Toto"
+
+name "args_t"
+
+arg("test") {
+  description "Test"
+  c_string }

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/yaggo.git



More information about the debian-med-commit mailing list