Full Code of xenomachina/kotlin-argparser for AI

master 9367da9e757c cached
26 files
230.5 KB
58.5k tokens
1 requests
Download .txt
Showing preview only (241K chars total). Download the full file or copy to clipboard to get everything.
Repository: xenomachina/kotlin-argparser
Branch: master
Commit: 9367da9e757c
Files: 26
Total size: 230.5 KB

Directory structure:
gitextract_f8jhxc_z/

├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── COPYING
├── README.md
├── build.gradle
├── codecov.sh
├── detekt.yml
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src/
    ├── main/
    │   └── kotlin/
    │       └── com/
    │           └── xenomachina/
    │               └── argparser/
    │                   ├── ArgParser.kt
    │                   ├── Default.kt
    │                   ├── DefaultHelpFormatter.kt
    │                   ├── Exceptions.kt
    │                   ├── HelpFormatter.kt
    │                   ├── OptionDelegate.kt
    │                   ├── ParsingDelegate.kt
    │                   ├── PositionalDelegate.kt
    │                   ├── PosixNaming.kt
    │                   ├── SystemExitException.kt
    │                   └── WrappingDelegate.kt
    └── test/
        └── kotlin/
            └── com/
                └── xenomachina/
                    └── argparser/
                        └── ArgParserTest.kt

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
### Gradle ###
.gradle
/build/

# Ignore Gradle GUI config
gradle-app.setting

# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar

# Cache of project
.gradletasknamecache

# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.propertiesgradletasknamecache


### IntelliJ IDEA ###

.idea/

## File-based project format:
*.iws

## Plugin-specific files:

# IntelliJ
/out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties


================================================
FILE: .travis.yml
================================================
language: java

jdk:
    - openjdk8

before_cache:
    - rm -f  $HOME/.gradle/caches/modules-2/modules-2.lock
    - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
    directories:
        - $HOME/.gradle/

after_success:
    - ./gradlew jacocoTestReport && ./codecov.sh


================================================
FILE: CHANGELOG.md
================================================
# Change Log

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## 2.0.7 - 2018-05-08

### Fixed

- [Issue #54](https://github.com/xenomachina/kotlin-argparser/issues/54)
  Allow the dot character in options and arguments. Thanks @tgockel!

- [Issue #47](https://github.com/xenomachina/kotlin-argparser/issues/47)
  If an option looks at the value of another option it can see the
  current value. If no value has been set a `MissingValueException` is thrown.

## 2.0.6 - 2018-03-26

### Changed

- Help text formatting now treats multi-newlines as paragraph separators, while
  single newlines are still treated like spaces. Thanks @leomillon!

## 2.0.5 - 2018-03-20

### Changed

- Releasing to Maven Central in addition to Bintray. This is probably the only
  really externally visible change.

- Upgraded a bunch of dependencies, including gradlew.
    - gradle -> 4.5.1
    - dokka -> = 0.9.16
    - gradle_bintray -> = 1.8.0
    - gradle_release -> = 2.6.0
    - kotlin -> 1.2.30
    - xenocom -> 0.0.6

## 2.0.4 - 2018-01-18

### Added

- If the `programName` passed to `mainBody` is null, then the
  system property `com.xenomachina.argparser.programName` is used, if set.

- The `parseInto` method can be used as an inline alternative to `force`.
  Thanks @shanethehat!

- [Issue #24](https://github.com/xenomachina/kotlin-argparser/issues/18):
 `default` can now accept a lambda, making it possible to defer computation of
  defaults until actually required.
  Thanks @shanethehat!

### Changed

- All instances of `progName` have been renamed to `programName`.

- The gradle wrapper has been updated.
  Thanks @ColinHebert!

## 2.0.3 - 2017-06-12

### Fixed

- [Issue #18](https://github.com/xenomachina/kotlin-argparser/issues/18)

## 2.0.2 - 2017-06-12

### Fixed

- [Issue #19](https://github.com/xenomachina/kotlin-argparser/issues/19) by
  updating xenocom dependency to 0.0.5. Also updated kotlin to 1.1.2-5 for good
  measure.

## 2.0.1 - 2017-05-15

### Changed

- [Issue #14](https://github.com/xenomachina/kotlin-argparser/issues/14) —
  previously, automatic option naming would turn "camelCase" into
  "--camelCase". Now it is converted to "--camel-case".

- Likewise, positinal argument auto-naming used to convert "camelCase" into
  "CAMELCASE". Now it is converted to "CAMEL-CASE".

- Improve help formatting w/long program names

- README formatting improved.
  Thanks @konfilios!

### Fixed

- [Issue #17](https://github.com/xenomachina/kotlin-argparser/issues/17) —
  specifying 0 for the columns should format help without line wrapping.
  Previously, this did not work as documented, and would instead wrap text in
  very narrow columns.

- [Issue #15](https://github.com/xenomachina/kotlin-argparser/issues/15)
  — make it possible to specify 'argName' on all variants of 'storing' and
  `adding`


## 2.0.0 - 2017-04-21

### Added

- `ArgParser.option` is now a public method, so it's possible to create many
  new option types that were not previously possible. The existing option types
  are all written in terms of `option`, so they can be used to get an idea of
  how it works.

- More tests have been added.

- Started using keepachangelog.com format for CHANGELOG.md

- Made minor improvements to release process

### Changed

- The `storing`, `adding` and `positionalList` methods of `ArgParser` have had
  their parameters slightly reordered to be consistent with the other methods.
  This is an incompatible change. The name(s) come first, if any, followed by
  `help`. Other parameters appear after `help`, with the `transform` function,
  if any, last. It is recommended that clients either pass the transform as a
  block (ie: with braces) or as a named parameter, as any future new parameters
  will necessarily change its position in the list.

- Delegate and DelegateProvider are now abstract classes with internal
  constructors. This makes it much easier for me to separate internal and
  public parts of their API. This is an incompatible change, however it
  shouldn't really affect you unless you were trying to implement `Delegate`,
  which was't supported to begin with.

- `default` methods on both `Delegate` and `DelegateProvider` are now extension
  methods.  This makes it possible to generalize the type when adding a
  default. This is most noticable when using a nullable value (or `null`
  itself) for the default, though may also be useful in other cases (eg: a
  "storing" that always produces a `Rectangle`, but you want the default to be
  a `Circle`.  The resulting delegate will be a `Delegate<Shape>`.)

- Registration of delegates now takes place at binding-time rather than
  construction time. This should be pretty indistinguishable from the old
  behavior unless you're creating delegates without binding them.

- Help formatting has been improved so that it's far less likely to wrap option
  names in the usage table.

- There have been numerous bugfixes, particularly around positionals


## 1.1.0 - 2017-03-09

### Added

- Auto-naming of options and positionals.
    - Each of the ArgParser methods that takes names and returns a Delegate<T> has
      an overload that takes no name, but returns a DelegateProvider<T>.

    - A DelegateProvider<T> has an `operator fun provideDelegate` that returns a
      Delegate<T>, using a name derived from the name of the property the
      DelegateProvider is being bound to.

### Removed

- `addValidtator` is now deprecated. Use `addValidator` instead.

### Fixed

- Removed documentation of `option` from `README.md`, as it is internal

- Corrected spelling of `addValidator`. `addValidtator` is still there, but
  deprecated. It'll probably be removed in the next release, barring the
  addition of potato functionality.

## 1.0.2 - 2017-03-07

### Changed

- Upgrade to Kotlin 1.1, extract xenocom package.

## 1.0.1 - 2017-01-30

### Fixed

- Fix small bug where `runMain` didn't have a default value for `columns`
  parameter. (Now defaults to null.)

## 1.0.0 - 2017-01-27

### Added

- Initial release


================================================
FILE: COPYING
================================================
                  GNU LESSER GENERAL PUBLIC LICENSE
                       Version 2.1, February 1999

 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
not price.  Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

                  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.

  1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.

  You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.

  2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.

  If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License.  However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.

  9. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.

  11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded.  In such case, this License incorporates the limitation as if
written in the body of this License.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number.  If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation.  If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission.  For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this.  Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.

                            NO WARRANTY

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

                     END OF TERMS AND CONDITIONS

           How to Apply These Terms to Your New Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

Also add information on how to contact you by electronic and paper mail.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!


================================================
FILE: README.md
================================================
# <img alt="Kotlin --argparser" src="https://rawgit.com/xenomachina/kotlin-argparser/master/logo.svg" style="transform:scale(.6)" >

<!--
Removed until either Bintray makes a badge with a configurable label, or
shields.io can make a reliable bintray badge.

[![Bintray](https://img.shields.io/bintray/v/xenomachina/maven/kotlin-argparser.svg)](https://bintray.com/xenomachina/maven/kotlin-argparser/%5FlatestVersion)
-->

[![Maven Central](https://img.shields.io/maven-central/v/com.xenomachina/kotlin-argparser.svg)](https://mvnrepository.com/artifact/com.xenomachina/kotlin-argparser)
[![Build Status](https://travis-ci.org/xenomachina/kotlin-argparser.svg?branch=master)](https://travis-ci.org/xenomachina/kotlin-argparser)
[![codebeat badge](https://codebeat.co/badges/902174e2-31be-4f9d-a4ba-40178b075d2a)](https://codebeat.co/projects/github-com-xenomachina-kotlin-argparser-master)
[![Awesome Kotlin Badge](https://kotlin.link/awesome-kotlin.svg)](https://github.com/KotlinBy/awesome-kotlin)
[![Javadocs](https://www.javadoc.io/badge/com.xenomachina/kotlin-argparser.svg)](https://www.javadoc.io/doc/com.xenomachina/kotlin-argparser)
[![License: LGPL 2.1](https://img.shields.io/badge/license-LGPL--2.1-blue.svg)](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html)


This is a library for parsing command-line arguments.  It can parse both
options and positional arguments.  It aims to be easy to use and concise yet
powerful and robust.


## Overview

Defining options and positional arguments is as simple as:

```kotlin
import com.xenomachina.argparser.ArgParser

class MyArgs(parser: ArgParser) {
    val v by parser.flagging("enable verbose mode")

    val name by parser.storing("name of the user")

    val count by parser.storing("number of the widgets") { toInt() }

    val source by parser.positional("source filename")

    val destination by parser.positional("destination filename")
}
```

An instance of `MyArgs` will represent the set of parsed arguments. Each option
and positional argument is declared as a property that delegates through a
delegate factory method on an instance of `ArgParser`.

The name of an option is inferred from the name of the property it is bound to.
The options above are named `-v`, `--name` and `--count`, respectively. There
are also two positional arguments.

Direct control over an option's name is also possible, and for most types of
options it is also possible to have multiple names, like a short and long name:


```kotlin
class MyArgs(parser: ArgParser) {
    val verbose by parser.flagging(
        "-v", "--verbose",
        help = "enable verbose mode")

    val name by parser.storing(
        "-N", "--name",
        help = "name of the user")

    val count by parser.storing(
        "-c", "--count",
        help = "number of widgets") { toInt() }

    val source by parser.positional(
        "SOURCE",
        help = "source filename")

    val destination by parser.positional(
        "DEST",
        help = "destination filename")
}
```

The unparsed command-line arguments are passed to the `ArgParser` instance at
construction:

```kotlin
fun main(args: Array<String>) = mainBody {
    ArgParser(args).parseInto(::MyArgs).run {
        println("Hello, ${name}!")
        println("I'm going to move ${count} widgets from ${source} to ${destination}.")
        // TODO: move widgets
    }
}
```

See [kotlin-argparser-example](https://github.com/xenomachina/kotlin-argparser-example)
for a complete example project.


## Nomenclature

Options, arguments, flags... what's the difference?

An application's `main` function is passed an array of strings. These are
the *unparsed command-line arguments*, or *unparsed arguments* for short.

The unparsed arguments can then be parsed into *options*, which start
with a hyphen ("`-`"), and *positional arguments*. For example, in the command
`ls -l /tmp/`, the unparsed arguments would be `"-l", "/tmp"` where `-l`
is an option, while `/tmp/` is a positional argument.

Options can also have *option arguments*. In the command `ls --time-style=iso`,
the option is `--time-style` and that options argument is `iso`. Note that in
parsing a single unparsed argument can be split into an option and an option
argument, or even into multiple options in some cases.

A *flag* is a boolean option which has no arguments and which is false if not
provided, but true if provided. The `-l` option of `ls` is a flag.

## Option Types

### Boolean Flags

Boolean flags are created by asking the parser for a `flagging` delegate.  One
or more option names, may be provided:

```kotlin
val verbose by parser.flagging("-v", "--verbose",
                               help = "enable verbose mode")
```

Here the presence of either `-v` or `--verbose` options in the
arguments will cause the `Boolean` property `verbose` to be `true`, otherwise
it will be `false`.

### Storing a Single Argument

Single argument options are created by asking the parser for a
`storing` delegate.

```kotlin
val name by parser.storing("-N", "--name",
                           help = "name of the user")
```

Here either `-N` or `--name` with an argument will cause the `name` property to
have that argument as its value.

A function can also be supplied to transform the argument into the desired
type. Here the `size` property will be an `Int` rather than a `String`:

```kotlin
val size by parser.storing("-c", "--count",
                           help = "number of widgets") { toInt() }
```

### Adding to a Collection

Options that add to a `Collection` each time they appear in the arguments are
created with using the `adding` delegate. Just like `storing` delegates, a
transform function may optionally be supplied:

```kotlin
val includeDirs by parser.adding(
        "-I", help = "directory to search for header files") { File(this) }
```

Now each time the `-I` option appears, its transformed argument is appended to
`includeDirs`.

### Mapping from an option to a fixed value

For choosing between a fixed set of values (typically, but not necessarily,
from an enum), a `mapping` delegate can be used:

```kotlin
val mode by parser.mapping(
        "--fast" to Mode.FAST,
        "--small" to Mode.SMALL,
        "--quiet" to Mode.QUIET,
        help = "mode of operation")
```

Here the `mode` property will be set to the corresponding `ArgParser.Mode` value depending
on which of `--fast`, `--small`, and `--quiet` appears (last) in the arguments.

`mapping` is one of the few cases where it is not possible to infer the option
name from the property name.

### More advanced options

For all other types of options, the `option` method should be used. The
methods mentioned above are, in fact, convenience methods built on top of the
`option` method.

For example, it is possible to create an option that has multiple arguments:

```kotlin
  fun ArgParser.putting(vararg names: String, help: String) =
          option<MutableMap<String, String>>(*names,
                  argNames = listOf("KEY", "VALUE"),
                  help = help) {
              value.orElse { mutableMapOf<String, String>() }.apply {
                  put(arguments.first(), arguments.last()) }
          }
```

Note that the `option` method does not have an auto-naming overload. If you
need this capability, create a `DelegateProvider` that creates your `Delegate`:

```kotlin
  fun ArgParser.putting(help: String) =
          ArgParser.DelegateProvider { identifier ->
              putting(identifierToOptionName(identifier), help = help) }
```


## Positional Arguments

Positional arguments are collected by using the `positional` and
`positionalList` methods.

For a single positional argument:

```kotlin
val destination by parser.positional("destination filename")
```

An explicit name may also be specified:

```kotlin
val destination by parser.positional("DEST",
                                     help = "destination filename")
```

The name ("DEST", here) is used in error handling and help text.

For a list of positional arguments:

```kotlin
val sources by parser.positionalList("SOURCE", 1..Int.MAX_VALUE,
                                     help = "source filename")
```

The range indicates how many arguments should be collected, and defaults to the
value shown in this example. As the name suggests, the resulting property will
be a `List`.

Both of these methods accept an optional transform function for converting
arguments from `String` to whatever type is actually desired:

```kotlin
val destination by parser.positional("DEST",
                                     help = "...") { File(this) }

val sources by parser.positionalList("SOURCE", 1..Int.MAX_VALUE,
                                     help = "...") { File(this) }
```


## Modifying Delegates

The delegates returned by any of these methods also have a few methods for setting
optional attributes:

### Adding a Default Value

Certain types of delegates (notably `storing`, `mapping`, and `positional`)
have no default value, and hence will be required options unless a default
value is provided. This is done with the `default` method:

```kotlin
val name by parser.storing("-N", "--name", help = "...").default("John Doe")
```

Note that it *is* possible to use `null` for the default, though this may
require specifying the type parameter for `default` explicitly:

```kotlin
val name by parser.storing("-N", "--name", help = "...").default<String?>(null)
```

The type of the resulting property be nullable (a `String?` in this case).


### Adding a Validator

Sometimes it's easier to validate an option at the end of parsing, in which
case the `addValidator` method can be used.

```kotlin
val percentages by parser.adding("--percentages", help = "...") { toInt() }
        .addValidator {
              if (value.sum() != 100)
                  throw InvalidArgumentException(
                          "Percentages must add up to 100%")
        }
```

## Error Handling

If the parser determines that execution should not continue it will throw a
`SystemExitException` which has a status code appropriate for passing to
`exitProcess` as well as a message for the user.

These exceptions can be caused by user error, or even if the user requests help
(eg: via the `--help` option).

It is recommended that transform functions (given to `storing`,
`positionalList`, etc.) and post-parsing validation, including that performed
via, `addValidator` also throw a `SystemExitException` on failure.

As a convenience, these exceptions can be handled by using the `mainBody`
function:

```kotlin
class ParsedArgs(parser: ArgParser) {
    val name by positional("The user's name").default("world")
}

fun main(args: Array<String>) = mainBody {
    ArgParser(args).parseInto(::ParsedArgs).run {
        println("Hello, {name}!")
    }
}
```

## Parsing

Parsing of command-line arguments is performed sequentially. So long as
option-processing is enabled, each not-yet-processed command-line argument that
starts with a hyphen (`-`) is treated as an option.

### Short Options

Short options start with a single hyphen. If the option takes an argument, the
argument can either be appended:

```bash
# "-o" with argument "ARGUMENT"
my_program -oARGUMENT
```

or can be the following command-line argument:

```bash
# "-o" with argument "ARGUMENT"
my_program -o ARGUMENT
```

Zero argument short options can also be appended to each other without
intermediate hyphens:

```bash
# "-x", "-y" and "-z" options
my_program -xyz
```

An option that accepts arguments is also allowed at the end of such a chain:

```bash
# "-x", "-y" and "-z" options, with argument for "-z"
my_program -xyzARGUMENT
```

### Long Options

Long options start with a double hyphen (`--`). An argument to a long option
can
either be delimited with an equal sign (`=`):

```bash
# "--foo" with argument "ARGUMENT"
my_program --foo=ARGUMENT
```

or can be the following command-line argument:

```bash
# "--foo" with argument "ARGUMENT"
my_program --foo ARGUMENT
```

### Multi-argument Options

Multi-argument options are supported, though currently not by any of the
convenience methods. Option-arguments after the first must be separate
command-line arguments, for both an long and short forms of an option.

### Positional Arguments

In GNU mode (the default), options can be interspersed with positional
arguments, but in POSIX mode the first positional argument that is encountered
disables option processing for the remaining arguments. In either mode, if the
argument "--" is encountered while option processing is enabled, then option
processing is disabled for the rest of the command-line. Once the options and
option-arguments have been eliminated, what remains are considered to be
positional arguments.

Each positional argument delegate can specify a minimum and maximum number of
arguments it is willing to collect.

The positional arguments are distributed to the delegates by allocating each
positional delegate at least as many arguments as it requires. If more than the
minimum number of positional arguments have been supplied then additional
arguments will be allocated to the first delegate up to its maximum, then the
second, and so on, until all arguments have been allocated to a delegate.

This makes it easy to create a program that behaves like `grep`:

```kotlin
class Args(parser: ArgParser) {
    // accept 1 regex followed by n filenames
    val regex by parser.positional("REGEX",
            help = "regular expression to search for")
    val files by parser.positionalList("FILE",
            help = "file to search in")
}
```

And equally easy to create a program that behaves like `cp`:

```kotlin
class Args(parser: ArgParser) {
    // accept n source files followed by 1 destination
    val sources by parser.positionalList("SOURCE",
            help = "source file")
    val destination by parser.positional("DEST",
            help = "destination file")
}
```


## Forcing Parsing

Parsing normally does not begin until a delegate's value is accessed. Sometimes
this is not desirable, so it is possible to enforce the parsing of arguments
into a class of values. This ensures that all arguments that are required are
provided, and all arguments provided are consumed.

Forcing can be done in a separate step using the `force` method:

```kotlin
val parser = ArgParser(args)
val parsedArgs = ParsedArgs(parser)
parser.force()
// now you can use parsedArgs
```

Alternatively, forcing can be done inline via the `parseInto` method:

```kotlin
val parsedArgs = ArgParser(args).parseInto(::ParsedArgs)
// now you can use parsedArgs
```

In both cases exceptions will be thrown where parsing or validation errors are found.    

## Help Formatting

By default, `ArgParser` will add a `--help` option (short name `-h`) for
displaying usage information. If this option is present a `ShowHelpException` will be thrown.
If the default exception handling is being used (see [Error Handling](#error-handling)) the
program will halt and print a help message like the one below, based on the `ArgParser`
configuration:

    usage: program_name [-h] [-n] [-I INCLUDE]... -o OUTPUT
                        [-v]... SOURCE... DEST


    This is the prologue. Lorem ipsum dolor sit amet, consectetur
    adipiscing elit. Aliquam malesuada maximus eros. Fusce
    luctus risus eget quam consectetur, eu auctor est
    ullamcorper. Maecenas eget suscipit dui, sed sodales erat.
    Phasellus.


    required arguments:
      -o OUTPUT,          directory in which all output should
      --output OUTPUT     be generated


    optional arguments:
      -h, --help          show this help message and exit

      -n, --dry-run       don't do anything

      -I INCLUDE,         search in this directory for header
      --include INCLUDE   files

      -v, --verbose       increase verbosity


    positional arguments:
      SOURCE              source file

      DEST                destination file


    This is the epilogue. Lorem ipsum dolor sit amet,
    consectetur adipiscing elit. Donec vel tortor nunc. Sed eu
    massa sed turpis auctor faucibus. Donec vel pellentesque
    tortor. Ut ultrices tempus lectus fermentum vestibulum.
    Phasellus.

The creation of the `--help` option can be disabled by passing `null` as the
`helpFormatter` when constructing the `ArgParser`, or configured by manually
constructing a `HelpFormatter` instance. In the above example a
`DefaultHelpFormatter` was created with the prologue and epilogue.


## Caveats

- This library should be considered to be *very beta*. While there are no plans
  to make any breaking changes to the API, it's possible that there may be some
  until it is mature.

- Upon reading the value any of the delegated properties created by an
  `ArgParser`, the arguments used to construct that `ArgParser` will be
  parsed. This means it's important that you don't attempt to create delegates
  on an `ArgParser` after any of its existing delegated properties have been
  read. Attempting to do so will cause an `IllegalStateException`. It would be
  nice if Kotlin had facilities for doing some of the work of `ArgParser` at
  compile time rather than run time, but so far the run time errors seem to be
  reasonably easy to avoid.


## Configuring Your Build

<!-- TODO move detailed instructions elsewhere, just have brief instructions here -->

Kotlin-argparser binaries are hosted on Maven Central and also Bintray's
JCenter.

In Gradle, add something like this in your `build.gradle`:

```groovy
// you probably already have this part
buildscript {
    repositories {
        mavenCentral() // or jcenter()
    }
}

dependencies {
    compile "com.xenomachina:kotlin-argparser:$kotlin_argparser_version"
}
```

In Maven add something like this to your `pom.xml`:

```xml
<dependency>
    <groupId>com.xenomachina</groupId>
    <artifactId>kotlin-argparser</artifactId>
    <version>VERSION</version>
</dependency>
```

Information on setting up other build systems, as well as the current version
number, can be found on
[MVN Repository's page for Kotlin-argparser](https://mvnrepository.com/artifact/com.xenomachina/kotlin-argparser/latest).

## Thanks

Thanks to the creators of Python's
[`argparse`](https://docs.python.org/3/library/argparse.html) module, which
provided the initial inspiration for this library.

Thanks also to the team behind [Kotlin](https://kotlinlang.org/).

Finally, thanks to all of the people who have contributed
[code](https://github.com/xenomachina/kotlin-argparser/graphs/contributors)
and/or
[issues](https://github.com/xenomachina/kotlin-argparser/issues).


================================================
FILE: build.gradle
================================================
// Copyright © 2016 Laurence Gonsalves
//
// This file is part of kotlin-argparser, a library which can be found at
// http://github.com/xenomachina/kotlin-argparser
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2.1 of the License, or (at your
// option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
// for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, see http://www.gnu.org/licenses/

buildscript {
    repositories {
        mavenCentral()
        jcenter()
        maven {
            url "https://plugins.gradle.org/m2/"
        }
    }

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version"
        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:$gradle_bintray_version"
        classpath "gradle.plugin.io.gitlab.arturbosch.detekt:detekt-gradle-plugin:$detekt_version"
    }
}

plugins {
    id 'com.palantir.git-version' version '0.12.0-rc2'
    id 'org.jmailen.kotlinter' version '1.12.0'
}

apply plugin: 'maven'
apply plugin: 'kotlin'
apply plugin: 'java'
apply plugin: 'org.jetbrains.dokka'
apply plugin: 'signing'
apply plugin: 'com.jfrog.bintray'
apply plugin: 'maven-publish'
apply plugin: 'jacoco'
apply plugin: "io.gitlab.arturbosch.detekt"

assert name == 'kotlin-argparser'
description = 'Concise, easy, powerful and robust command line argument parsing for Kotlin'

group 'com.xenomachina'
version = gitVersion()

project.ext {
    githubUser = "xenomachina"
    vcsDev = "https://github.com/$githubUser"
    githubRepo = "$githubUser/$name"
    vcsUrl = "https://github.com/$githubRepo"
}

sourceCompatibility = 1.6

repositories {
    mavenCentral()
    jcenter()
}

test {
  useJUnitPlatform()
}

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    compile "com.xenomachina:xenocom:$xenocom_version"

    testCompile "io.kotlintest:kotlintest-runner-junit5:$kotlintest_version"
    testCompile "org.slf4j:slf4j-simple:$slf4j_version"
    // This is to ensure that kotlintest uses the correct version of
    // kotlin-reflect
    testCompile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
}

sourceSets {
    main.java.srcDirs += 'src/main/kotlin'
}

//////////////////////////////////////////////////////////////////////////////
// Detekt config
//////////////////////////////////////////////////////////////////////////////

detekt {
    // remove this when plugin stops choosing latest version by default
    version = "$detekt_version"

    defaultProfile {
        input = file("src/main/kotlin")

        filters = ".*/resources/.*,.*/build/.*"
        config = file("detekt.yml")
    }
}

//////////////////////////////////////////////////////////////////////////////
// Dokka config
//////////////////////////////////////////////////////////////////////////////

dokka {
    moduleName = project.name

    // TODO: includes = ['Module.md']

    linkMapping {
        dir = "src/main/kotlin"
        url = "$project.ext.vcsUrl/blob/master/src/main/kotlin"
        suffix = "#L"
    }
    sourceDirs = files('src/main/kotlin')
}

task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
    outputFormat = "javadoc"
    outputDirectory = javadoc.destinationDir

    linkMapping {
        dir = "src/main/kotlin"
        url = "$project.ext.vcsUrl/blob/master/src/main/kotlin"
        suffix = "#L"
    }
    sourceDirs = files('src/main/kotlin')
}


//Based on comment by @jnizet at https://github.com/Kotlin/dokka/issues/42
task javadocJar(type: Jar, dependsOn: dokkaJavadoc) {
    classifier = 'javadoc'
    from javadoc.destinationDir
}

task sourcesJar(type: Jar, dependsOn: classes) {
    classifier = 'sources'
    from sourceSets.main.allSource
}

artifacts {
    archives javadocJar, sourcesJar
}

if (System.env.CI != "true") {
    // Some of the stuff in here can break continuous integration, and none of
    // it is necessary in CI.

    //////////////////////////////////////////////////////////////////////////////
    // POM config
    //////////////////////////////////////////////////////////////////////////////

    def pomConfig = {
        packaging 'jar'

        name project.name
        description project.description
        url project.ext.vcsUrl

        scm {
            connection "scm:git:$project.ext.vcsUrl"
            developerConnection "scm:git:$project.ext.vcsDev"
            url "$project.ext.vcsUrl"
        }

        licenses {
            license {
                name "GNU Lesser General Public License, Version 2.1"
                url "https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt"
                distribution "repo"
            }
        }

        developers {
            developer {
                id "xenomachina"
                name "Laurence Gonsalves"

                // State-of-the art anti-scraper encryption. ;-)
                email "moc.anihcamonex@ecnerual".reverse()
            }
        }
    }

    //////////////////////////////////////////////////////////////////////////////
    // Maven Central upload
    //////////////////////////////////////////////////////////////////////////////

    signing {
        useGpgCmd()
        sign configurations.archives
    }

    def ossrhUsernameOrEmpty = hasProperty('ossrhUsername') ? ossrhUsername : ''
    def ossrhPasswordOrEmpty = hasProperty('ossrhPassword') ? ossrhPassword : ''

    uploadArchives {
        repositories {
            mavenDeployer {
                beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }

                repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
                    authentication(userName: ossrhUsernameOrEmpty, password: ossrhPasswordOrEmpty)
                }

                snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") {
                    authentication(userName: ossrhUsernameOrEmpty, password: ossrhPasswordOrEmpty)
                }

                pom.project pomConfig
            }
        }
    }

    //////////////////////////////////////////////////////////////////////////////
    // bintray upload
    //////////////////////////////////////////////////////////////////////////////

    // Based on https://github.com/bintray/gradle-bintray-plugin
    bintray {
      user = System.getenv('BINTRAY_USER')
      key = System.getenv('BINTRAY_KEY')

      pkg {
        repo = 'maven'
        name = project.name
        desc = project.description

        licenses = ['LGPL-2.1']
        vcsUrl = "$project.ext.vcsUrl"
        websiteUrl = "$project.ext.vcsUrl"
        issueTrackerUrl = "$project.ext.vcsUrl/issues"
        githubRepo = project.ext.githubRepo
        githubReleaseNotesFile = 'CHANGELOG.md'

        version {
          name = project.version
          desc = "$project.name version $project.version"
          released = new Date()
          vcsTag = "$project.version"
        }
      }

      publications = ['MyPublication']
    }

    // Create the publication with the pom configuration:
    publishing {
        publications {
            MyPublication(MavenPublication) {
                from components.java
                artifact sourcesJar
                artifact javadocJar

                pom.withXml {
                    def root = asNode()
                    root.children().last() + pomConfig
                }
            }
        }
    }
}

//////////////////////////////////////////////////////////////////////////////
// jacoco (codecov.io) plugin config
//////////////////////////////////////////////////////////////////////////////

jacocoTestReport {
    reports {
        xml.enabled true
        html.enabled false
    }
}


================================================
FILE: codecov.sh
================================================
#!/usr/bin/env bash

set -e +o pipefail

VERSION="8fda091"

url="https://codecov.io"
env="$CODECOV_ENV"
service=""
token=""
search_in=""
flags=""
exit_with=0
curlargs=""
curlawsargs=""
dump="0"
clean="0"
curl_s="-s"
name="$CODECOV_NAME"
include_cov=""
exclude_cov=""
ddp="$(echo ~)/Library/Developer/Xcode/DerivedData"
xp=""
files=""
cacert="$CODECOV_CA_BUNDLE"
gcov_ignore=""
gcov_include=""

ft_gcov="1"
ft_coveragepy="1"
ft_fix="1"
ft_search="1"
ft_s3="1"
ft_xcode="1"
ft_network="1"
ft_xcodeplist="0"

_git_root=$(git rev-parse --show-toplevel 2>/dev/null || hg root 2>/dev/null || echo $PWD)
git_root="$_git_root"
remote_addr=""
if [ "$git_root" = "$PWD" ];
then
  git_root="."
fi

url_o=""
pr_o=""
build_o=""
commit_o=""
search_in_o=""
tag_o=""
branch_o=""
slug_o=""

commit="$VCS_COMMIT_ID"
branch="$VCS_BRANCH_NAME"
pr="$VCS_PULL_REQUEST"
slug="$VCS_SLUG"
tag="$VCS_TAG"
build_url="$CI_BUILD_URL"
build="$CI_BUILD_ID"
job="$CI_JOB_ID"

beta_xcode_partials=""

proj_root="$git_root"
gcov_exe="gcov"
gcov_arg=""

b="\033[0;36m"
g="\033[0;32m"
r="\033[0;31m"
e="\033[0;90m"
x="\033[0m"

show_help() {
cat << EOF

                Codecov Bash $VERSION

          Global report uploading tool for Codecov
       Documentation at https://docs.codecov.io/docs
    Contribute at https://github.com/codecov/codecov-bash


    -h          Display this help and exit
    -f FILE     Target file(s) to upload

                 -f "path/to/file"     only upload this file
                                       skips searching unless provided patterns below

                 -f '!*.bar'           ignore all files at pattern *.bar
                 -f '*.foo'            include all files at pattern *.foo
                 Must use single quotes.
                 This is non-exclusive, use -s "*.foo" to match specific paths.

    -s DIR       Directory to search for coverage reports.
                 Already searches project root and artifact folders.
    -t TOKEN     Set the private repository token
                 (option) set environment variable CODECOV_TOKEN=:uuid

                 -t @/path/to/token_file
                 -t uuid

    -n NAME      Custom defined name of the upload. Visible in Codecov UI

    -e ENV       Specify environment variables to be included with this build
                 Also accepting environment variables: CODECOV_ENV=VAR,VAR2

                 -e VAR,VAR2

    -X feature   Toggle functionalities

                 -X gcov          Disable gcov
                 -X coveragepy    Disable python coverage
                 -X fix           Disable report fixing
                 -X search        Disable searching for reports
                 -X xcode         Disable xcode processing
                 -X network       Disable uploading the file network

    -R root dir  Used when not in git/hg project to identify project root directory
    -F flag      Flag the upload to group coverage metrics

                 -F unittests        This upload is only unittests
                 -F integration      This upload is only integration tests
                 -F ui,chrome        This upload is Chrome - UI tests

    -c           Move discovered coverage reports to the trash
    -Z           Exit with 1 if not successful. Default will Exit with 0

    -- xcode --
    -D           Custom Derived Data Path for Coverage.profdata and gcov processing
                 Default '~/Library/Developer/Xcode/DerivedData'
    -J           Specify packages to build coverage.
                 This can significantly reduces time to build coverage reports.

                 -J 'MyAppName'      Will match "MyAppName" and "MyAppNameTests"
                 -J '^ExampleApp$'   Will match only "ExampleApp" not "ExampleAppTests"

    -- gcov --
    -g GLOB      Paths to ignore during gcov gathering
    -G GLOB      Paths to include during gcov gathering
    -p dir       Project root directory
                 Also used when preparing gcov
    -x gcovexe   gcov executable to run. Defaults to 'gcov'
    -a gcovargs  extra arguments to pass to gcov

    -- Override CI Environment Variables --
       These variables are automatically detected by popular CI providers

    -B branch    Specify the branch name
    -C sha       Specify the commit sha
    -P pr        Specify the pull request number
    -b build     Specify the build number
    -T tag       Specify the git tag

    -- Enterprise --
    -u URL       Set the target url for Enterprise customers
                 Not required when retrieving the bash uploader from your CCE
                 (option) Set environment variable CODECOV_URL=https://my-hosted-codecov.com
    -r SLUG      owner/repo slug used instead of the private repo token in Enterprise
                 (option) set environment variable CODECOV_SLUG=:owner/:repo
                 (option) set in your codecov.yml "codecov.slug"
    -S PATH      File path to your cacert.pem file used to verify ssl with Codecov Enterprise (optional)
                 (option) Set environment variable: CODECOV_CA_BUNDLE="/path/to/ca.pem"
    -U curlargs  Extra curl arguments to communicate with Codecov. e.g., -U "--proxy http://http-proxy"
    -A curlargs  Extra curl arguments to communicate with AWS.

    -- Debugging --
    -d           Dont upload and dump to stdout
    -K           Remove color from the output
    -v           Verbose mode

EOF
}


say() {
  echo -e "$1"
}


urlencode() {
  echo "$1" | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3- | sed -e 's/%0A//'
}


swiftcov() {
  _dir=$(dirname "$1")
  for _type in app framework xctest
  do
    find "$_dir" -name "*.$_type" | while read f
    do
      _proj=${f##*/}
      _proj=${_proj%."$_type"}
      if [ "$2" = "" ] || [ "$(echo "$_proj" | grep -i "$2")" != "" ];
      then
        say "    $g+$x Building reports for $_proj $_type"
        dest=$([ -f "$f/$_proj" ] && echo "$f/$_proj" || echo "$f/Contents/MacOS/$_proj")
        _proj_name=$(echo "$_proj" | sed -e 's/[[:space:]]//g')
        xcrun llvm-cov show $beta_xcode_partials -instr-profile "$1" "$dest" > "$_proj_name.$_type.coverage.txt" \
         || say "    ${r}x>${x} llvm-cov failed to produce results for $dest"
      fi
    done
  done
}


# Credits to: https://gist.github.com/pkuczynski/8665367
parse_yaml() {
   local prefix=$2
   local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
   sed -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \
        -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" $1 |
   awk -F$fs '{
      indent = length($1)/2;
      vname[indent] = $2;
      for (i in vname) {if (i > indent) {delete vname[i]}}
      if (length($3) > 0) {
         vn=""; if (indent > 0) {vn=(vn)(vname[0])("_")}
         printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, $2, $3);
      }
   }'
}


if [ $# != 0 ];
then
  while getopts "a:A:b:B:cC:dD:e:f:F:g:G:hJ:Kn:p:P:r:R:s:S:t:T:u:U:vx:X:Z" o
  do
    case "$o" in
      "a")
        gcov_arg=$OPTARG
        ;;
      "A")
        curlawsargs="$OPTARG"
        ;;
      "b")
        build_o="$OPTARG"
        ;;
      "B")
        branch_o="$OPTARG"
        ;;
      "c")
        clean="1"
        ;;
      "C")
        commit_o="$OPTARG"
        ;;
      "d")
        dump="1"
        ;;
      "D")
        ddp="$OPTARG"
        ;;
      "e")
        env="$env,$OPTARG"
        ;;
      "f")
        if [ "${OPTARG::1}" = "!" ];
        then
          exclude_cov="$exclude_cov -not -path '${OPTARG:1}'"

        elif [[ "$OPTARG" = *"*"* ]];
        then
          include_cov="$include_cov -or -name '$OPTARG'"

        else
          ft_search=0
          if [ "$files" = "" ];
          then
            files="$OPTARG"
          else
            files="$files
$OPTARG"
          fi
        fi
        ;;
      "F")
        if [ "$flags" = "" ];
        then
          flags="$OPTARG"
        else
          flags="$flags,$OPTARG"
        fi
        ;;
      "g")
        gcov_ignore="$gcov_ignore -not -path '$OPTARG'"
        ;;
      "G")
        gcov_include="$gcov_include -path '$OPTARG'"
        ;;
      "h")
        show_help
        exit 0;
        ;;
      "J")
        if [ "$xp" = "" ];
        then
          xp="$OPTARG"
        else
          xp="$xp\|$OPTARG"
        fi
        ;;
      "K")
        b=""
        g=""
        r=""
        e=""
        x=""
        ;;
      "n")
        name="$OPTARG"
        ;;
      "p")
        proj_root="$OPTARG"
        ;;
      "P")
        pr_o="$OPTARG"
        ;;
      "r")
        slug_o="$OPTARG"
        ;;
      "R")
        git_root="$OPTARG"
        ;;
      "s")
        if [ "$search_in_o" = "" ];
        then
          search_in_o="$OPTARG"
        else
          search_in_o="$search_in_o $OPTARG"
        fi
        ;;
      "S")
        cacert="--cacert \"$OPTARG\""
        ;;
      "t")
        if [ "${OPTARG::1}" = "@" ];
        then
          token=$(cat "${OPTARG:1}" | tr -d ' \n')
        else
          token="$OPTARG"
        fi
        ;;
      "T")
        tag_o="$OPTARG"
        ;;
      "u")
        url_o=$(echo "$OPTARG" | sed -e 's/\/$//')
        ;;
      "U")
        curlargs="$OPTARG"
        ;;
      "v")
        set -x
        curl_s=""
        ;;
      "x")
        gcov_exe=$OPTARG
        ;;
      "X")
        if [ "$OPTARG" = "gcov" ];
        then
          ft_gcov="0"
        elif [ "$OPTARG" = "coveragepy" ] || [ "$OPTARG" = "py" ];
        then
          ft_coveragepy="0"
        elif [ "$OPTARG" = "xcodeplist" ];
        then
          ft_xcodeplist="1"
          ft_xcode="0"
        elif [ "$OPTARG" = "fix" ] || [ "$OPTARG" = "fixes" ];
        then
          ft_fix="0"
        elif [ "$OPTARG" = "xcode" ];
        then
          ft_xcode="0"
        elif [ "$OPTARG" = "search" ];
        then
          ft_search="0"
        elif [ "$OPTARG" = "xcodepartials" ];
        then
          beta_xcode_partials="-use-color"
        elif [ "$OPTARG" = "network" ];
        then
          ft_network="0"
        fi
        ;;
      "Z")
        exit_with=1
        ;;
    esac
  done
fi

say "
  _____          _
 / ____|        | |
| |     ___   __| | ___  ___ _____   __
| |    / _ \\ / _\` |/ _ \\/ __/ _ \\ \\ / /
| |___| (_) | (_| |  __/ (_| (_) \\ V /
 \\_____\\___/ \\__,_|\\___|\\___\\___/ \\_/
                              Bash-$VERSION

"

search_in="$proj_root"

if [ "$JENKINS_URL" != "" ];
then
  say "$e==>$x Jenkins CI detected."
  # https://wiki.jenkins-ci.org/display/JENKINS/Building+a+software+project
  # https://wiki.jenkins-ci.org/display/JENKINS/GitHub+pull+request+builder+plugin#GitHubpullrequestbuilderplugin-EnvironmentVariables
  service="jenkins"
  branch=$([ ! -z "$ghprbSourceBranch" ] && echo "$ghprbSourceBranch" || \
           [ ! -z "$GIT_BRANCH" ] && echo "$GIT_BRANCH" || \
           [ ! -z "$BRANCH_NAME" ] && echo "$BRANCH_NAME")
  commit=$([ ! -z "$ghprbActualCommit" ] && echo "$ghprbActualCommit" || \
           echo "$GIT_COMMIT")
  build="$BUILD_NUMBER"
  pr="${ghprbPullId:=$CHANGE_ID}"
  build_url=$(urlencode "$BUILD_URL")

elif [ "$CI" = "true" ] && [ "$TRAVIS" = "true" ] && [ "$SHIPPABLE" != "true" ];
then
  say "$e==>$x Travis CI detected."
  # http://docs.travis-ci.com/user/ci-environment/#Environment-variables
  service="travis"
  branch="$TRAVIS_BRANCH"
  commit="$TRAVIS_COMMIT"
  build="$TRAVIS_JOB_NUMBER"
  pr="$TRAVIS_PULL_REQUEST"
  job="$TRAVIS_JOB_ID"
  slug="$TRAVIS_REPO_SLUG"
  tag="$TRAVIS_TAG"
  env="$env,TRAVIS_OS_NAME"

  language=$(printenv | grep "TRAVIS_.*_VERSION" | head -1)
  if [ "$language" != "" ];
  then
    env="$env,${language%=*}"
  fi

elif [ "$CI" = "true" ] && [ "$CI_NAME" = "codeship" ];
then
  say "$e==>$x Codeship CI detected."
  # https://www.codeship.io/documentation/continuous-integration/set-environment-variables/
  service="codeship"
  branch="$CI_BRANCH"
  build="$CI_BUILD_NUMBER"
  build_url=$(urlencode "$CI_BUILD_URL")
  commit="$CI_COMMIT_ID"

elif [ "$TEAMCITY_VERSION" != "" ];
then
  say "$e==>$x TeamCity CI detected."
  # https://confluence.jetbrains.com/display/TCD8/Predefined+Build+Parameters
  # https://confluence.jetbrains.com/plugins/servlet/mobile#content/view/74847298
  if [ "$TEAMCITY_BUILD_BRANCH" = '' ];
  then
    echo "    Teamcity does not automatically make build parameters available as environment variables."
    echo "    Add the following environment parameters to the build configuration"
    echo "    env.TEAMCITY_BUILD_BRANCH = %teamcity.build.branch%"
    echo "    env.TEAMCITY_BUILD_ID = %teamcity.build.id%"
    echo "    env.TEAMCITY_BUILD_URL = %teamcity.serverUrl%/viewLog.html?buildId=%teamcity.build.id%"
    echo "    env.TEAMCITY_BUILD_COMMIT = %system.build.vcs.number%"
    echo "    env.TEAMCITY_BUILD_REPOSITORY = %vcsroot.<YOUR TEAMCITY VCS NAME>.url%"
  fi
  service="teamcity"
  branch="$TEAMCITY_BUILD_BRANCH"
  build="$TEAMCITY_BUILD_ID"
  build_url=$(urlencode "$TEAMCITY_BUILD_URL")
  if [ "$TEAMCITY_BUILD_COMMIT" != "" ];
  then
    commit="$TEAMCITY_BUILD_COMMIT"
  else
    commit="$BUILD_VCS_NUMBER"
  fi
  remote_addr="$TEAMCITY_BUILD_REPOSITORY"

elif [ "$CI" = "true" ] && [ "$CIRCLECI" = "true" ];
then
  say "$e==>$x Circle CI detected."
  # https://circleci.com/docs/environment-variables
  service="circleci"
  branch="$CIRCLE_BRANCH"
  build="$CIRCLE_BUILD_NUM"
  job="$CIRCLE_NODE_INDEX"
  if [ "$CIRCLE_PROJECT_REPONAME" != "" ];
  then
    slug="$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME"
  else
    # git@github.com:owner/repo.git
    slug="${CIRCLE_REPOSITORY_URL##*:}"
    # owner/repo.git
    slug="${slug%%.git}"
  fi
  pr="$CIRCLE_PR_NUMBER"
  commit="$CIRCLE_SHA1"
  search_in="$search_in $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS"

elif [ "$BUDDYBUILD_BRANCH" != "" ];
then
  say "$e==>$x buddybuild detected"
  # http://docs.buddybuild.com/v6/docs/custom-prebuild-and-postbuild-steps
  service="buddybuild"
  branch="$BUDDYBUILD_BRANCH"
  build="$BUDDYBUILD_BUILD_NUMBER"
  build_url="https://dashboard.buddybuild.com/public/apps/$BUDDYBUILD_APP_ID/build/$BUDDYBUILD_BUILD_ID"
  # BUDDYBUILD_TRIGGERED_BY
  if [ "$ddp" = "$(echo ~)/Library/Developer/Xcode/DerivedData" ];
  then
    ddp="/private/tmp/sandbox/${BUDDYBUILD_APP_ID}/bbtest"
  fi

elif [ "${bamboo_planRepository_revision}" != "" ];
then
  say "$e==>$x Bamboo detected"
  # https://confluence.atlassian.com/bamboo/bamboo-variables-289277087.html#Bamboovariables-Build-specificvariables
  service="bamboo"
  commit="${bamboo_planRepository_revision}"
  branch="${bamboo_planRepository_branch}"
  build="${bamboo_buildNumber}"
  build_url="${bamboo_buildResultsUrl}"
  remote_addr="${bamboo_planRepository_repositoryUrl}"

elif [ "$CI" = "true" ] && [ "$BITRISE_IO" = "true" ];
then
  # http://devcenter.bitrise.io/faq/available-environment-variables/
  say "$e==>$x Bitrise CI detected."
  service="bitrise"
  branch="$BITRISE_GIT_BRANCH"
  build="$BITRISE_BUILD_NUMBER"
  build_url=$(urlencode "$BITRISE_BUILD_URL")
  pr="$BITRISE_PULL_REQUEST"
  if [ "$GIT_CLONE_COMMIT_HASH" != "" ];
  then
    commit="$GIT_CLONE_COMMIT_HASH"
  fi

elif [ "$CI" = "true" ] && [ "$SEMAPHORE" = "true" ];
then
  say "$e==>$x Semaphore CI detected."
  # https://semaphoreapp.com/docs/available-environment-variables.html
  service="semaphore"
  branch="$BRANCH_NAME"
  build="$SEMAPHORE_BUILD_NUMBER"
  job="$SEMAPHORE_CURRENT_THREAD"
  pr="$PULL_REQUEST_NUMBER"
  slug="$SEMAPHORE_REPO_SLUG"
  commit="$REVISION"
  env="$env,SEMAPHORE_TRIGGER_SOURCE"

elif [ "$CI" = "true" ] && [ "$BUILDKITE" = "true" ];
then
  say "$e==>$x Buildkite CI detected."
  # https://buildkite.com/docs/guides/environment-variables
  service="buildkite"
  branch="$BUILDKITE_BRANCH"
  build="$BUILDKITE_BUILD_NUMBER"
  job="$BUILDKITE_JOB_ID"
  build_url=$(urlencode "$BUILDKITE_BUILD_URL")
  slug="$BUILDKITE_PROJECT_SLUG"
  commit="$BUILDKITE_COMMIT"

elif [ "$CI" = "true" ] && [ "$DRONE" = "true" ];
then
  say "$e==>$x Drone CI detected."
  # http://docs.drone.io/env.html
  # drone commits are not full shas
  service="drone.io"
  branch="$DRONE_BRANCH"
  build="$DRONE_BUILD_NUMBER"
  build_url=$(urlencode "${DRONE_BUILD_URL:-$CI_BUILD_URL}")
  pr="$DRONE_PULL_REQUEST"
  job="$DRONE_JOB_NUMBER"
  tag="$DRONE_TAG"

elif [ "$HEROKU_TEST_RUN_BRANCH" != "" ];
then
  say "$e==>$x Heroku CI detected."
  # https://devcenter.heroku.com/articles/heroku-ci#environment-variables
  service="heroku"
  branch="$HEROKU_TEST_RUN_BRANCH"
  build="$HEROKU_TEST_RUN_ID"

elif [ "$CI" = "True" ] && [ "$APPVEYOR" = "True" ];
then
  say "$e==>$x Appveyor CI detected."
  # http://www.appveyor.com/docs/environment-variables
  service="appveyor"
  branch="$APPVEYOR_REPO_BRANCH"
  build=$(urlencode "$APPVEYOR_JOB_ID")
  pr="$APPVEYOR_PULL_REQUEST_NUMBER"
  job="$APPVEYOR_ACCOUNT_NAME%2F$APPVEYOR_PROJECT_SLUG%2F$APPVEYOR_BUILD_VERSION"
  slug="$APPVEYOR_REPO_NAME"
  commit="$APPVEYOR_REPO_COMMIT"

elif [ "$CI" = "true" ] && [ "$WERCKER_GIT_BRANCH" != "" ];
then
  say "$e==>$x Wercker CI detected."
  # http://devcenter.wercker.com/articles/steps/variables.html
  service="wercker"
  branch="$WERCKER_GIT_BRANCH"
  build="$WERCKER_MAIN_PIPELINE_STARTED"
  slug="$WERCKER_GIT_OWNER/$WERCKER_GIT_REPOSITORY"
  commit="$WERCKER_GIT_COMMIT"

elif [ "$CI" = "true" ] && [ "$MAGNUM" = "true" ];
then
  say "$e==>$x Magnum CI detected."
  # https://magnum-ci.com/docs/environment
  service="magnum"
  branch="$CI_BRANCH"
  build="$CI_BUILD_NUMBER"
  commit="$CI_COMMIT"

elif [ "$CI" = "true" ] && [ "$SNAP_CI" = "true" ];
then
  say "$e==>$x Snap CI detected."
  # https://docs.snap-ci.com/environment-variables/
  service="snap"
  branch=$([ "$SNAP_BRANCH" != "" ] && echo "$SNAP_BRANCH" || echo "$SNAP_UPSTREAM_BRANCH")
  build="$SNAP_PIPELINE_COUNTER"
  job="$SNAP_STAGE_NAME"
  pr="$SNAP_PULL_REQUEST_NUMBER"
  commit=$([ "$SNAP_COMMIT" != "" ] && echo "$SNAP_COMMIT" || echo "$SNAP_UPSTREAM_COMMIT")
  env="$env,DISPLAY"

elif [ "$SHIPPABLE" = "true" ];
then
  say "$e==>$x Shippable CI detected."
  # http://docs.shippable.com/ci_configure/
  service="shippable"
  branch=$([ "$HEAD_BRANCH" != "" ] && echo "$HEAD_BRANCH" || echo "$BRANCH")
  build="$BUILD_NUMBER"
  build_url=$(urlencode "$BUILD_URL")
  pr="$PULL_REQUEST"
  slug="$REPO_FULL_NAME"
  commit="$COMMIT"

elif [ "$TDDIUM" = "true" ];
then
  say "Solano CI detected."
  # http://docs.solanolabs.com/Setup/tddium-set-environment-variables/
  service="solano"
  commit="$TDDIUM_CURRENT_COMMIT"
  branch="$TDDIUM_CURRENT_BRANCH"
  build="$TDDIUM_TID"
  pr="$TDDIUM_PR_ID"

elif [ "$GREENHOUSE" = "true" ];
then
  say "$e==>$x Greenhouse CI detected."
  # http://docs.greenhouseci.com/docs/environment-variables-files
  service="greenhouse"
  branch="$GREENHOUSE_BRANCH"
  build="$GREENHOUSE_BUILD_NUMBER"
  build_url=$(urlencode "$GREENHOUSE_BUILD_URL")
  pr="$GREENHOUSE_PULL_REQUEST"
  commit="$GREENHOUSE_COMMIT"
  search_in="$search_in $GREENHOUSE_EXPORT_DIR"

elif [ "$GITLAB_CI" != "" ];
then
  say "$e==>$x GitLab CI detected."
  # http://doc.gitlab.com/ce/ci/variables/README.html
  service="gitlab"
  branch="$CI_BUILD_REF_NAME"
  build="$CI_BUILD_ID"
  remote_addr="${CI_BUILD_REPO:-$CI_REPOSITORY_URL}"
  commit="$CI_BUILD_REF"

else
  say "${r}x>${x} No CI provider detected."
  say "    Testing inside Docker? ${b}http://docs.codecov.io/docs/testing-with-docker${x}"
  say "    Testing with Tox? ${b}https://docs.codecov.io/docs/python#section-testing-with-tox${x}"

fi

say "    ${e}project root:${x} $git_root"

# find branch, commit, repo from git command
if [ "$GIT_BRANCH" != "" ];
then
  branch="$GIT_BRANCH"

elif [ "$branch" = "" ];
then
  branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || hg branch 2>/dev/null || echo "")
  if [ "$branch" = "HEAD" ];
  then
    branch=""
  fi
fi

if [ "$commit_o" = "" ];
then
  # merge commit -> actual commit
  mc=$(git log -1 --pretty=%B 2>/dev/null | tr -d '[[:space:]]' || true)
  if [[ "$mc" =~ ^Merge[[:space:]][a-z0-9]{40}[[:space:]]into[[:space:]][a-z0-9]{40}$ ]];
  then
    # Merge xxx into yyy
    say "    Fixing merge commit sha"
    commit=$(echo "$mc" | cut -d' ' -f2)
  elif [ "$GIT_COMMIT" != "" ];
  then
    commit="$GIT_COMMIT"
  elif [ "$commit" = "" ];
  then
    commit=$(git log -1 --format="%H" 2>/dev/null || hg id -i --debug 2>/dev/null | tr -d '+' || echo "")
  fi
else
  commit="$commit_o"
fi

if [ "$CODECOV_TOKEN" != "" ] && [ "$token" = "" ];
then
  say "${e}-->${x} token set from env"
  token="$CODECOV_TOKEN"
fi

if [ "$CODECOV_URL" != "" ] && [ "$url_o" = "" ];
then
  say "${e}-->${x} url set from env"
  url_o=$(echo "$CODECOV_URL" | sed -e 's/\/$//')
fi

if [ "$CODECOV_SLUG" != "" ];
then
  say "${e}-->${x} slug set from env"
  slug_o="$CODECOV_SLUG"

elif [ "$slug" = "" ];
then
  if [ "$remote_addr" = "" ];
  then
    remote_addr=$(git config --get remote.origin.url || hg paths default || echo '')
  fi
  if [ "$remote_addr" != "" ];
  then
    if echo "$remote_addr" | grep -q "//"; then
      # https
      slug=$(echo "$remote_addr" | cut -d / -f 4,5 | sed -e 's/\.git$//')
    else
      # ssh
      slug=$(echo "$remote_addr" | cut -d : -f 2 | sed -e 's/\.git$//')
    fi
  fi
  if [ "$slug" = "/" ];
  then
    slug=""
  fi
fi

yaml=$(cd "$git_root" && \
       git ls-files "*codecov.yml" "*codecov.yaml" 2>/dev/null \
       || hg locate "*codecov.yml" "*codecov.yaml" 2>/dev/null \
       || echo '')
yaml=$(echo "$yaml" | head -1)

if [ "$yaml" != "" ];
then
  say "    ${e}Yaml found at:${x} $yaml"
  config=$(parse_yaml "$git_root/$yaml" || echo '')

  # TODO validate the yaml here

  if [ "$(echo "$config" | grep 'codecov_token="')" != "" ] && [ "$token" = "" ];
  then
    say "${e}-->${x} token set from yaml"
    token="$(echo "$config" | grep 'codecov_token="' | sed -e 's/codecov_token="//' | sed -e 's/"\.*//')"
  fi

  if [ "$(echo "$config" | grep 'codecov_url="')" != "" ] && [ "$url_o" = "" ];
  then
    say "${e}-->${x} url set from yaml"
    url_o="$(echo "$config" | grep 'codecov_url="' | sed -e 's/codecov_url="//' | sed -e 's/"\.*//')"
  fi

  if [ "$(echo "$config" | grep 'codecov_slug="')" != "" ] && [ "$slug_o" = "" ];
  then
    say "${e}-->${x} slug set from yaml"
    slug_o="$(echo "$config" | grep 'codecov_slug="' | sed -e 's/codecov_slug="//' | sed -e 's/"\.*//')"
  fi
else
  say "    ${g}Yaml not found, that's ok! Learn more at${x} ${b}http://docs.codecov.io/docs/codecov-yaml${x}"

fi

if [ "$branch_o" != "" ];
then
  branch=$(urlencode "$branch_o")
else
  branch=$(urlencode "$branch")
fi

query="branch=$branch\
       &commit=$commit\
       &build=$([ "$build_o" = "" ] && echo "$build" || echo "$build_o")\
       &build_url=$build_url\
       &name=$(urlencode "$name")\
       &tag=$([ "$tag_o" = "" ] && echo "$tag" || echo "$tag_o")\
       &slug=$([ "$slug_o" = "" ] && urlencode "$slug" || urlencode "$slug_o")\
       &yaml=$(urlencode "$yaml")\
       &service=$service\
       &flags=$flags\
       &pr=$([ "$pr_o" = "" ] && echo "${pr##\#}" || echo "${pr_o##\#}")\
       &job=$job"

if [ "$ft_search" = "1" ];
then
  # detect bower comoponents location
  bower_components="bower_components"
  bower_rc=$(cd "$git_root" && cat .bowerrc 2>/dev/null || echo "")
  if [ "$bower_rc" != "" ];
  then
    bower_components=$(echo "$bower_rc" | tr -d '\n' | grep '"directory"' | cut -d'"' -f4 | sed -e 's/\/$//')
    if [ "$bower_components" = "" ];
    then
      bower_components="bower_components"
    fi
  fi

  # Swift Coverage
  if [ "$ft_xcode" = "1" ] && [ -d "$ddp" ];
  then
    say "${e}==>${x} Processing Xcode reports"
    say "    DerivedData folder: $ddp"
    profdata_files=$(find "$ddp" -name '*.profdata' 2>/dev/null || echo '')
    if [ "$profdata_files" != "" ];
    then
      # xcode via profdata
      if [ "$xp" = "" ];
      then
        # xp=$(xcodebuild -showBuildSettings 2>/dev/null | grep -i "^\s*PRODUCT_NAME" | sed -e 's/.*= \(.*\)/\1/')
        # say " ${e}->${x} Speed up Xcode processing by adding ${e}-J '$xp'${x}"
        say "    ${g}hint${x} Speed up Swift processing by using use ${g}-J 'AppName'${x} (regexp accepted)"
        say "    ${g}hint${x} This will remove Pods/ from your report. Also ${b}https://docs.codecov.io/docs/ignoring-paths${x}"
      fi
      while read -r profdata;
      do
        if [ "$profdata" != "" ];
        then
          swiftcov "$profdata" "$xp"
        fi
      done <<< "$profdata_files"
    else
      say "    ${e}->${x} No Swift coverage found"
    fi

    # Obj-C Gcov Coverage
    if [ "$ft_gcov" = "1" ];
    then
      say "    ${e}->${x} Running $gcov_exe for Obj-C"
      bash -c "find $ddp -type f -name '*.gcda' $gcov_include $gcov_ignore -exec $gcov_exe -pbcu $gcov_arg {} +" || true
    fi
  fi

  if [ "$ft_xcodeplist" = "1" ] && [ -d "$ddp" ];
  then
    say "${e}==>${x} Processing Xcode plists"
    plists_files=$(find "$ddp" -name '*.xccoverage' 2>/dev/null || echo '')
    if [ "$plists_files" != "" ];
    then
      while read -r plist;
      do
        if [ "$plist" != "" ];
        then
          say "    ${g}Found${x} plist file at $plist"
          plutil -convert xml1 -o "$(basename "$plist").plist" -- $plist
        fi
      done <<< "$plists_files"
    fi
  fi

  # Gcov Coverage
  if [ "$ft_gcov" = "1" ];
  then
    say "${e}==>${x} Running gcov in $proj_root ${e}(disable via -X gcov)${x}"
    bash -c "find $proj_root -type f -name '*.gcno' $gcov_include $gcov_ignore -exec $gcov_exe -pb $gcov_arg {} +" || true
  else
    say "${e}==>${x} gcov disabled"
  fi

  # Python Coverage
  if [ "$ft_coveragepy" = "1" ];
  then
    if [ ! -f coverage.xml ];
    then
      if which coverage >/dev/null 2>&1;
      then
        say "${e}==>${x} Python coveragepy exists ${e}disable via -X coveragepy${x}"

        dotcoverage=$(find "$git_root" -name '.coverage' -or -name '.coverage.*' | head -1 || echo '')
        if [ "$dotcoverage" != "" ];
        then
          cd "$(dirname "$dotcoverage")"
          if [ ! -f .coverage ];
          then
            say "    ${e}->${x} Running coverage combine"
            coverage combine
          fi
          say "    ${e}->${x} Running coverage xml"
          if [ "$(coverage xml -i)" != "No data to report." ];
          then
            files="$files
$PWD/coverage.xml"
          else
            say "    ${r}No data to report.${x}"
          fi
          cd "$proj_root"
        else
          say "    ${r}No .coverage file found.${x}"
        fi
      else
        say "${e}==>${x} Python coveragepy not found"
      fi
    fi
  else
    say "${e}==>${x} Python coveragepy disabled"
  fi

  if [ "$search_in_o" != "" ];
  then
    # location override
    search_in="$search_in_o"
  fi

  say "$e==>$x Searching for coverage reports in:"
  for _path in $search_in
  do
    say "    ${g}+${x} $_path"
  done

  patterns="find $search_in -type f \( -name '*coverage*.*' \
                     -or -name 'nosetests.xml' \
                     -or -name 'jacoco*.xml' \
                     -or -name 'clover.xml' \
                     -or -name 'report.xml' \
                     -or -name '*.codecov.*' \
                     -or -name 'codecov.*' \
                     -or -name 'cobertura.xml' \
                     -or -name 'excoveralls.json' \
                     -or -name 'luacov.report.out' \
                     -or -name 'coverage-final.json' \
                     -or -name 'naxsi.info' \
                     -or -name 'lcov.info' \
                     -or -name 'lcov.dat' \
                     -or -name '*.lcov' \
                     -or -name '*.clover' \
                     -or -name 'cover.out' \
                     -or -name 'gcov.info' \
                     -or -name '*.gcov' \
                     -or -name '*.lst' \
                     $include_cov \) \
                    $exclude_cov \
                    -not -name '*.profdata' \
                    -not -name 'coverage-summary.json' \
                    -not -name 'phpunit-code-coverage.xml' \
                    -not -name 'remapInstanbul.coverage*.json' \
                    -not -name 'phpunit-coverage.xml' \
                    -not -name '*codecov.yml' \
                    -not -name '*.serialized' \
                    -not -name '.coverage*' \
                    -not -name '.*coveragerc' \
                    -not -name '*.sh' \
                    -not -name '*.bat' \
                    -not -name '*.ps1' \
                    -not -name '*.cmake' \
                    -not -name '*.dox' \
                    -not -name '*.ec' \
                    -not -name '*.rst' \
                    -not -name '*.h' \
                    -not -name '*.scss' \
                    -not -name '*.o' \
                    -not -name '*.proto' \
                    -not -name '*.sbt' \
                    -not -name '*.xcoverage.*' \
                    -not -name '*.gz' \
                    -not -name '*.conf' \
                    -not -name '*.p12' \
                    -not -name '*.csv' \
                    -not -name '*.rsp' \
                    -not -name '*.m4' \
                    -not -name '*.pem' \
                    -not -name '*~' \
                    -not -name '*.exe' \
                    -not -name '*.am' \
                    -not -name '*.template' \
                    -not -name '*.cp' \
                    -not -name '*.bw' \
                    -not -name '*.crt' \
                    -not -name '*.log' \
                    -not -name '*.cmake' \
                    -not -name '*.pth' \
                    -not -name '*.in' \
                    -not -name '*.jar*' \
                    -not -name '*.pom*' \
                    -not -name '*.png' \
                    -not -name '*.jpg' \
                    -not -name '*.sql' \
                    -not -name '*.jpeg' \
                    -not -name '*.svg' \
                    -not -name '*.gif' \
                    -not -name '*.csv' \
                    -not -name '*.snapshot' \
                    -not -name '*.mak*' \
                    -not -name '*.bash' \
                    -not -name '*.data' \
                    -not -name '*.py' \
                    -not -name '*.class' \
                    -not -name '*.xcconfig' \
                    -not -name '*.ec' \
                    -not -name '*.coverage' \
                    -not -name '*.pyc' \
                    -not -name '*.cfg' \
                    -not -name '*.egg' \
                    -not -name '*.ru' \
                    -not -name '*.css' \
                    -not -name '*.less' \
                    -not -name '*.pyo' \
                    -not -name '*.whl' \
                    -not -name '*.html' \
                    -not -name '*.ftl' \
                    -not -name '*.erb' \
                    -not -name '*.rb' \
                    -not -name '*.js' \
                    -not -name '*.jade' \
                    -not -name '*.db' \
                    -not -name '*.md' \
                    -not -name '*.cpp' \
                    -not -name '*.gradle' \
                    -not -name '*.tar.tz' \
                    -not -name '*.scss' \
                    -not -name 'include.lst' \
                    -not -name 'fullLocaleNames.lst' \
                    -not -name 'inputFiles.lst' \
                    -not -name 'createdFiles.lst' \
                    -not -name 'scoverage.measurements.*' \
                    -not -name 'test_*_coverage.txt' \
                    -not -name 'testrunner-coverage*' \
                    -not -path '*/vendor/*' \
                    -not -path '*/htmlcov/*' \
                    -not -path '*/virtualenv/*' \
                    -not -path '*/js/generated/coverage/*' \
                    -not -path '*/.virtualenv/*' \
                    -not -path '*/virtualenvs/*' \
                    -not -path '*/.virtualenvs/*' \
                    -not -path '*/.env/*' \
                    -not -path '*/.envs/*' \
                    -not -path '*/env/*' \
                    -not -path '*/envs/*' \
                    -not -path '*/.venv/*' \
                    -not -path '*/.venvs/*' \
                    -not -path '*/venv/*' \
                    -not -path '*/venvs/*' \
                    -not -path '*/.git/*' \
                    -not -path '*/.hg/*' \
                    -not -path '*/.tox/*' \
                    -not -path '*/__pycache__/*' \
                    -not -path '*/.egg-info*' \
                    -not -path '*/$bower_components/*' \
                    -not -path '*/node_modules/*' \
                    -not -path '*/conftest_*.c.gcov' 2>/dev/null"
  files=$(eval "$patterns" || echo '')

elif [ "$include_cov" != "" ];
then
  files=$(eval "find $search_in -type f \( ${include_cov:5} \)$exclude_cov 2>/dev/null" || echo '')
fi

num_of_files=$(echo "$files" | wc -l | tr -d ' ')
if [ "$num_of_files" != '' ] && [ "$files" != '' ];
then
  say "    ${e}->${x} Found $num_of_files reports"
fi

# no files found
if [ "$files" = "" ];
then
  say "${r}-->${x} No coverage report found."
  say "    Please visit ${b}http://docs.codecov.io/docs/supported-languages${x}"
  exit ${exit_with};
fi

if [ "$ft_network" == "1" ];
then
  say "${e}==>${x} Detecting git/mercurial file structure"
  network=$(cd "$git_root" && git ls-files 2>/dev/null || hg locate 2>/dev/null || echo "")
  if [ "$network" = "" ];
  then
    network=$(find "$git_root" -type f \
                   -not -path '*/virtualenv/*' \
                   -not -path '*/.virtualenv/*' \
                   -not -path '*/virtualenvs/*' \
                   -not -path '*/.virtualenvs/*' \
                   -not -path '*.png' \
                   -not -path '*.gif' \
                   -not -path '*.jpg' \
                   -not -path '*.jpeg' \
                   -not -path '*.md' \
                   -not -path '*/.env/*' \
                   -not -path '*/.envs/*' \
                   -not -path '*/env/*' \
                   -not -path '*/envs/*' \
                   -not -path '*/.venv/*' \
                   -not -path '*/.venvs/*' \
                   -not -path '*/venv/*' \
                   -not -path '*/venvs/*' \
                   -not -path '*/build/lib/*' \
                   -not -path '*/.git/*' \
                   -not -path '*/.egg-info/*' \
                   -not -path '*/shunit2-2.1.6/*' \
                   -not -path '*/vendor/*' \
                   -not -path '*/js/generated/coverage/*' \
                   -not -path '*/__pycache__/*' \
                   -not -path '*/node_modules/*' \
                   -not -path "*/$bower_components/*" 2>/dev/null || echo '')
  fi
fi

upload_file=`mktemp /tmp/codecov.XXXXXX`
adjustments_file=`mktemp /tmp/codecov.adjustments.XXXXXX`

cleanup() {
    rm -f $upload_file $adjustments_file
}

trap cleanup INT ABRT TERM

if [ "$env" != "" ];
then
  inc_env=""
  say "${e}==>${x} Appending build variables"
  for varname in $(echo "$env" | tr ',' ' ')
  do
    if [ "$varname" != "" ];
    then
      say "    ${g}+${x} $varname"
      inc_env="${inc_env}${varname}=$(eval echo "\$${varname}")
"
    fi
  done

echo "$inc_env<<<<<< ENV" >> $upload_file
fi

if [ "$ft_network" == "1" ];
then
  i="woff|eot|otf"  # fonts
  i="$i|gif|png|jpg|jpeg|psd"  # images
  i="$i|ptt|pptx|numbers|pages|md|txt|xlsx|docx|doc|pdf"  # docs
  i="$i|yml|yaml|.gitignore"  # supporting docs
  echo "$network" | grep -vwE "($i)$" >> $upload_file
  echo "<<<<<< network" >> $upload_file
fi

fr=0
say "${e}==>${x} Reading reports"
while IFS='' read -r file;
do
  # read the coverage file
  if [ "$(echo "$file" | tr -d ' ')" != '' ];
  then
    if [ -f "$file" ];
    then
      report_len=$(wc -c < "$file")
      if [ "$report_len" -ne 0 ];
      then
        say "    ${g}+${x} $file ${e}bytes=$(echo "$report_len" | tr -d ' ')${x}"
        # append to to upload
        echo "# path=$(echo "$file" | sed "s|^$git_root/||")" >> $upload_file
        cat "$file" >> $upload_file
        echo "<<<<<< EOF" >> $upload_file
        fr=1
        if [ "$clean" = "1" ];
        then
          rm "$file"
        fi
      else
        say "    ${r}-${x} Skipping empty file $file"
      fi
    else
      say "    ${r}-${x} file not found at $file"
    fi
  fi
done <<< "$(echo -e "$files")"

if [ "$fr" = "0" ];
then
  say "${r}-->${x} No coverage data found."
  say "    Please visit ${b}http://docs.codecov.io/docs/supported-languages${x}"
  say "    search for your projects language to learn how to collect reports."
  exit ${exit_with};
fi

if [ "$ft_fix" = "1" ];
then
  say "${e}==>${x} Appending adjustments"
  say "    ${b}http://docs.codecov.io/docs/fixing-reports${x}"

  empty_line='^[[:space:]]*$'
  # //
  syntax_comment='^[[:space:]]*//.*'
  # /* or */
  syntax_comment_block='^[[:space:]]*(\/\*|\*\/)[[:space:]]*$'
  # { or }
  syntax_bracket='^[[:space:]]*[\{\}][[:space:]]*(//.*)?$'
  # [ or ]
  syntax_list='^[[:space:]]*[][][[:space:]]*(//.*)?$'

  skip_dirs="-not -path '*/$bower_components/*' \
             -not -path '*/node_modules/*'"

  cut_and_join() {
    awk 'BEGIN { FS=":" }
         $3 ~ /\/\*/ || $3 ~ /\*\// { print $0 ; next }
         $1!=key { if (key!="") print out ; key=$1 ; out=$1":"$2 ; next }
         { out=out","$2 }
         END { print out }' 2>/dev/null
  }

  if echo "$network" | grep -m1 '.kt$' 1>/dev/null;
  then
    # skip brackets and comments
    find "$git_root" -type f \
                     -name '*.kt' \
                     -exec \
      grep -nIHE -e $syntax_bracket \
                 -e $syntax_comment_block {} \; \
      | cut_and_join \
      >> $adjustments_file \
      || echo ''

    # last line in file
    find "$git_root" -type f \
                     -name '*.kt' -exec \
      wc -l {} \; \
      | while read l; do echo "EOF: $l"; done \
      2>/dev/null \
      >> $adjustments_file \
      || echo ''

  fi

  if echo "$network" | grep -m1 '.go$' 1>/dev/null;
  then
    # skip empty lines, comments, and brackets
    find "$git_root" -type f \
                     -not -path '*/vendor/*' \
                     -name '*.go' \
                     -exec \
      grep -nIHE \
           -e $empty_line \
           -e $syntax_comment \
           -e $syntax_comment_block \
           -e $syntax_bracket \
           {} \; \
      | cut_and_join \
      >> $adjustments_file \
      || echo ''
  fi

  if echo "$network" | grep -m1 '.jsx$' 1>/dev/null;
  then
    # skip empty lines, comments, and brackets
    find "$git_root" -type f \
                     -name '*.jsx' \
                     $skip_dirs \
                     -exec \
      grep -nIHE \
           -e $empty_line \
           -e $syntax_comment \
           -e $syntax_bracket \
           {} \; \
      | cut_and_join \
      >> $adjustments_file \
      || echo ''
  fi

  if echo "$network" | grep -m1 '.php$' 1>/dev/null;
  then
    # skip empty lines, comments, and brackets
    find "$git_root" -type f \
                     -not -path '*/vendor/*' \
                     -name '*.php' \
                     -exec \
      grep -nIHE \
           -e $syntax_list \
           -e $syntax_bracket \
           -e '^[[:space:]]*\);[[:space:]]*(//.*)?$' \
           {} \; \
      | cut_and_join \
      >> $adjustments_file \
      || echo ''
  fi

  if echo "$network" | grep -m1 '\(.cpp\|.h\|.cxx\|.c\|.hpp\|.m\)$' 1>/dev/null;
  then
    # skip brackets
    find "$git_root" -type f \
                     $skip_dirs \
         \( \
           -name '*.h' \
           -or -name '*.cpp' \
           -or -name '*.cxx' \
           -or -name '*.m' \
           -or -name '*.c' \
           -or -name '*.hpp' \
         \) -exec \
      grep -nIHE \
           -e $empty_line \
           -e $syntax_bracket \
           -e '// LCOV_EXCL' \
           {} \; \
      | cut_and_join \
      >> $adjustments_file \
      || echo ''

    # skip brackets
    find "$git_root" -type f \
                     $skip_dirs \
         \( \
           -name '*.h' \
           -or -name '*.cpp' \
           -or -name '*.cxx' \
           -or -name '*.m' \
           -or -name '*.c' \
           -or -name '*.hpp' \
         \) -exec \
      grep -nIH '// LCOV_EXCL' \
           {} \; \
      >> $adjustments_file \
      || echo ''

  fi

  found=$(cat $adjustments_file | tr -d ' ')

  if [ "$found" != "" ];
  then
    say "    ${g}+${x} Found adjustments"
    echo "# path=fixes" >> $upload_file
    cat $adjustments_file >> $upload_file
    echo "<<<<<< EOF" >> $upload_file
    rm -rf $adjustments_file
  else
    say "    ${e}->${x} No adjustments found"
  fi
fi

if [ "$url_o" != "" ];
then
  url="$url_o"
fi

if [ "$dump" != "0" ];
then
  # trim whitespace from query
  echo "$url/upload/v4?$(echo "package=bash-$VERSION&token=$token&$query" | tr -d ' ')"
  cat $upload_file
else

  query=$(echo "${query}" | tr -d ' ')
  say "${e}==>${x} Uploading reports"
  say "    ${e}url:${x} $url"
  say "    ${e}query:${x} $query"

  # now add token to query
  query=$(echo "package=bash-$VERSION&token=$token&$query" | tr -d ' ')

  if [ "$ft_s3" = "1" ];
  then
    i="0"
    while [ $i -lt 4 ]
    do
      i=$[$i+1]
      say "    ${e}->${x} Pinging Codecov"
      res=$(curl $curl_s -X POST $curlargs $cacert "$url/upload/v4?$query" -H 'Accept: text/plain' || true)
      # a good replay is "https://codecov.io" + "\n" + "https://codecov.s3.amazonaws.com/..."
      status=$(echo "$res" | head -1 | grep 'HTTP ' | cut -d' ' -f2)
      if [ "$status" = "" ];
      then
        s3target=$(echo "$res" | sed -n 2p)
        say "    ${e}->${x} Uploading to S3 $(echo "$s3target" | cut -c1-32)"
        s3=$(curl $curl_s -fiX PUT $curlawsargs \
                  --data-binary @$upload_file \
                  -H 'Content-Type: text/plain' \
                  -H 'x-amz-acl: public-read' \
                  -H 'x-amz-storage-class: REDUCED_REDUNDANCY' \
                  "$s3target" || true)
        if [ "$s3" != "" ];
        then
          say "    ${g}->${x} View reports at ${b}$(echo "$res" | sed -n 1p)${x}"
          exit 0
        else
          say "    ${r}X>${x} Failed to upload to S3"
        fi
      elif [ "$status" = "400" ];
      then
          # 400 Error
          say "${g}${res}${x}"
          exit ${exit_with}
      fi
      say "    ${e}->${x} Sleeping for 30s and trying again..."
      sleep 30
    done
  fi

  say "    ${e}->${x} Uploading to Codecov"
  i="0"
  while [ $i -lt 4 ]
  do
    i=$[$i+1]

    res=$(curl $curl_s -X POST $curlargs $cacert --data-binary @$upload_file "$url/upload/v2?$query" -H 'Accept: text/plain' || echo 'HTTP 500')
    # HTTP 200
    # http://....
    status=$(echo "$res" | head -1 | cut -d' ' -f2)
    if [ "$status" = "" ];
    then
      say "    View reports at ${b}$(echo "$res" | head -2 | tail -1)${x}"
      exit 0

    elif [ "${status:0:1}" = "5" ];
    then
      say "    ${e}->${x} Sleeping for 30s and trying again..."
      sleep 30

    else
      say "    ${g}${res}${x}"
      exit 0
      exit ${exit_with}
    fi

  done

fi

say "    ${r}X> Failed to upload coverage reports${x}"
exit ${exit_with}

# EOF


================================================
FILE: detekt.yml
================================================
# `git diff detekt-default detekt.yml` to see what's changed from default
# settings

test-pattern: # Configure exclusions for test sources
  patterns: # Test file regexes
    - '.*/test/.*'
  exclude-rule-sets:
    - 'comments'
  exclude-rules:
    - 'NamingRules'
    - 'WildcardImport'
    - 'MagicNumber'
    - 'MaxLineLength'
    - 'LateinitUsage'
    - 'StringLiteralDuplication'
    - 'SpreadOperator'
    - 'TooManyFunctions'
    - 'ForEachOnRange'

build:
  maxIssues: 10 # TODO: reduce this threshhold
  weights:
    # complexity: 2
    # LongParameterList: 1
    # style: 1
    # comments: 1

processors:
  active: true
  exclude:
  # - 'FunctionCountProcessor'
  # - 'PropertyCountProcessor'
  # - 'ClassCountProcessor'
  # - 'PackageCountProcessor'
  # - 'KtFileCountProcessor'

console-reports:
  active: true
  exclude:
  #  - 'ProjectStatisticsReport'
  #  - 'ComplexityReport'
  #  - 'NotificationReport'
  #  - 'FindingsReport'
  #  - 'BuildFailureReport'

output-reports:
  active: true
  exclude:
  #  - 'HtmlOutputReport'
  #  - 'PlainOutputReport'
  #  - 'XmlOutputReport'

comments:
  active: true
  CommentOverPrivateFunction:
    active: false
  CommentOverPrivateProperty:
    active: false
  EndOfSentenceFormat:
    active: false
    endOfSentenceFormat: ([.?!][ \t\n\r\f<])|([.?!]$)
  UndocumentedPublicClass:
    active: false
    searchInNestedClass: true
    searchInInnerClass: true
    searchInInnerObject: true
    searchInInnerInterface: true
  UndocumentedPublicFunction:
    active: false

complexity:
  active: true
  ComplexCondition:
    active: true
    threshold: 4
  ComplexInterface:
    active: false
    threshold: 10
    includeStaticDeclarations: false
  ComplexMethod:
    active: true
    threshold: 10
    ignoreSingleWhenExpression: false
  LabeledExpression:
    active: false
  LargeClass:
    active: true
    threshold: 150
  LongMethod:
    active: true
    threshold: 20
  LongParameterList:
    active: true
    threshold: 6
    ignoreDefaultParameters: false
  MethodOverloading:
    active: false
    threshold: 6
  NestedBlockDepth:
    active: true
    threshold: 4
  StringLiteralDuplication:
    active: false
    threshold: 3
    ignoreAnnotation: true
    excludeStringsWithLessThan5Characters: true
    ignoreStringsRegex: '$^'
  TooManyFunctions:
    active: true
    thresholdInFiles: 11
    thresholdInClasses: 11
    thresholdInInterfaces: 11
    thresholdInObjects: 11
    thresholdInEnums: 11
    ignoreDeprecated: false

empty-blocks:
  active: true
  EmptyCatchBlock:
    active: true
    allowedExceptionNameRegex: "^(_|(ignore|expected).*)"
  EmptyClassBlock:
    active: true
  EmptyDefaultConstructor:
    active: true
  EmptyDoWhileBlock:
    active: true
  EmptyElseBlock:
    active: true
  EmptyFinallyBlock:
    active: true
  EmptyForBlock:
    active: true
  EmptyFunctionBlock:
    active: true
    ignoreOverriddenFunctions: false
  EmptyIfBlock:
    active: true
  EmptyInitBlock:
    active: true
  EmptyKtFile:
    active: true
  EmptySecondaryConstructor:
    active: true
  EmptyWhenBlock:
    active: true
  EmptyWhileBlock:
    active: true

exceptions:
  active: true
  ExceptionRaisedInUnexpectedLocation:
    active: false
    methodNames: 'toString,hashCode,equals,finalize'
  InstanceOfCheckForException:
    active: false
  NotImplementedDeclaration:
    active: false
  PrintStackTrace:
    active: false
  RethrowCaughtException:
    active: false
  ReturnFromFinally:
    active: false
  SwallowedException:
    active: false
  ThrowingExceptionFromFinally:
    active: false
  ThrowingExceptionInMain:
    active: false
  ThrowingExceptionsWithoutMessageOrCause:
    active: false
    exceptions: 'IllegalArgumentException,IllegalStateException,IOException'
  ThrowingNewInstanceOfSameException:
    active: false
  TooGenericExceptionCaught:
    active: true
    exceptionNames:
     - ArrayIndexOutOfBoundsException
     - Error
     - Exception
     - IllegalMonitorStateException
     - NullPointerException
     - IndexOutOfBoundsException
     - RuntimeException
     - Throwable
  TooGenericExceptionThrown:
    active: true
    exceptionNames:
     - Error
     - Exception
     - Throwable
     - RuntimeException

formatting:
  active: true
  android: false
  autoCorrect: true
  ChainWrapping:
    active: true
    autoCorrect: true
  CommentSpacing:
    active: true
    autoCorrect: true
  Filename:
    active: true
  FinalNewline:
    active: true
    autoCorrect: true
  ImportOrdering:
    active: true
    autoCorrect: true
  Indentation:
    active: true
    autoCorrect: true
    indentSize: 4
    continuationIndentSize: 4
  MaximumLineLength:
    active: true
    maxLineLength: 120
  ModifierOrdering:
    active: true
    autoCorrect: true
  NoBlankLineBeforeRbrace:
    active: true
    autoCorrect: true
  NoConsecutiveBlankLines:
    active: true
    autoCorrect: true
  NoEmptyClassBody:
    active: true
    autoCorrect: true
  NoItParamInMultilineLambda:
    active: true
  NoLineBreakAfterElse:
    active: true
    autoCorrect: true
  NoLineBreakBeforeAssignment:
    active: true
    autoCorrect: true
  NoMultipleSpaces:
    active: true
    autoCorrect: true
  NoSemicolons:
    active: true
    autoCorrect: true
  NoTrailingSpaces:
    active: true
    autoCorrect: true
  NoUnitReturn:
    active: true
    autoCorrect: true
  NoUnusedImports:
    active: true
    autoCorrect: true
  NoWildcardImports:
    active: true
    autoCorrect: true
  ParameterListWrapping:
    active: true
    autoCorrect: true
    indentSize: 4
  SpacingAroundColon:
    active: true
    autoCorrect: true
  SpacingAroundComma:
    active: true
    autoCorrect: true
  SpacingAroundCurly:
    active: true
    autoCorrect: true
  SpacingAroundKeyword:
    active: true
    autoCorrect: true
  SpacingAroundOperators:
    active: true
    autoCorrect: true
  SpacingAroundRangeOperator:
    active: true
    autoCorrect: true
  StringTemplate:
    active: true
    autoCorrect: true

naming:
  active: true
  ClassNaming:
    active: true
    classPattern: '[A-Z$][a-zA-Z0-9$]*'
  EnumNaming:
    active: true
    enumEntryPattern: '^[A-Z][_a-zA-Z0-9]*'
  ForbiddenClassName:
    active: false
    forbiddenName: ''
  FunctionMaxLength:
    active: false
    maximumFunctionNameLength: 30
  FunctionMinLength:
    active: false
    minimumFunctionNameLength: 3
  FunctionNaming:
    active: true
    functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$'
    excludeClassPattern: '$^'
  MatchingDeclarationName:
    active: true
  MemberNameEqualsClassName:
    active: false
    ignoreOverriddenFunction: true
  ObjectPropertyNaming:
    active: true
    propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
    constantPattern: '[A-Za-z][_A-Za-z0-9]*'
  PackageNaming:
    active: true
    packagePattern: '^[a-z]+(\.[a-z][a-z0-9]*)*$'
  TopLevelPropertyNaming:
    active: true
    constantPattern: '[A-Z][_A-Z0-9]*'
    propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
    privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
  VariableMaxLength:
    active: false
    maximumVariableNameLength: 64
  VariableMinLength:
    active: false
    minimumVariableNameLength: 1
  VariableNaming:
    active: true
    variablePattern: '[a-z][A-Za-z0-9]*'
    privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
    excludeClassPattern: '$^'

performance:
  active: true
  ForEachOnRange:
    active: true
  SpreadOperator:
    active: false
  UnnecessaryTemporaryInstantiation:
    active: true

potential-bugs:
  active: true
  DuplicateCaseInWhenExpression:
    active: true
  EqualsAlwaysReturnsTrueOrFalse:
    active: false
  EqualsWithHashCodeExist:
    active: true
  ExplicitGarbageCollectionCall:
    active: true
  InvalidRange:
    active: false
  IteratorHasNextCallsNextMethod:
    active: false
  IteratorNotThrowingNoSuchElementException:
    active: false
  LateinitUsage:
    active: false
    excludeAnnotatedProperties: ""
    ignoreOnClassesPattern: ""
  UnconditionalJumpStatementInLoop:
    active: false
  UnreachableCode:
    active: true
  UnsafeCallOnNullableType:
    active: false
  UnsafeCast:
    active: false
  UselessPostfixExpression:
    active: false
  WrongEqualsTypeParameter:
    active: false

style:
  active: true
  CollapsibleIfStatements:
    active: false
  DataClassContainsFunctions:
    active: false
    conversionFunctionPrefix: 'to'
  EqualsNullCall:
    active: false
  ExpressionBodySyntax:
    active: false
  ForbiddenComment:
    active: true
    values: 'FIXME:,STOPSHIP:'
  ForbiddenImport:
    active: false
    imports: ''
  FunctionOnlyReturningConstant:
    active: false
    ignoreOverridableFunction: true
    excludedFunctions: 'describeContents'
  LoopWithTooManyJumpStatements:
    active: false
    maxJumpCount: 1
  MagicNumber:
    active: true
    ignoreNumbers: '-1,0,1,2'
    ignoreHashCodeFunction: false
    ignorePropertyDeclaration: false
    ignoreConstantDeclaration: true
    ignoreCompanionObjectPropertyDeclaration: true
    ignoreAnnotation: false
    ignoreNamedArgument: true
    ignoreEnums: false
  MaxLineLength:
    active: true
    maxLineLength: 120
    excludePackageStatements: false
    excludeImportStatements: false
    excludeCommentStatements: false
  MayBeConst:
    active: false
  ModifierOrder:
    active: true
  NestedClassesVisibility:
    active: false
  NewLineAtEndOfFile:
    active: true
  NoTabs:
    active: false
  OptionalAbstractKeyword:
    active: true
  OptionalUnit:
    active: false
  OptionalWhenBraces:
    active: false
  ProtectedMemberInFinalClass:
    active: false
  RedundantVisibilityModifierRule:
    active: false
  ReturnCount:
    active: true
    max: 2
    excludedFunctions: "equals"
  SafeCast:
    active: true
  SerialVersionUIDInSerializableClass:
    active: false
  SpacingBetweenPackageAndImports:
    active: false
  ThrowsCount:
    active: true
    max: 2
  TrailingWhitespace:
    active: false
  UnnecessaryAbstractClass:
    active: false
  UnnecessaryInheritance:
    active: false
  UnnecessaryParentheses:
    active: false
  UntilInsteadOfRangeTo:
    active: false
  UnusedImports:
    active: false
  UnusedPrivateMember:
    active: false
    allowedNames: "(_|ignored|expected)"
  UseDataClass:
    active: false
    excludeAnnotatedClasses: ""
  UtilityClassWithPublicConstructor:
    active: false
  WildcardImport:
    active: true
    excludeImports: 'java.util.*,kotlinx.android.synthetic.*'

# vim: sw=2


================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists


================================================
FILE: gradle.properties
================================================
detekt_version = 1.0.0.RC7
dokka_version = 0.9.17
gradle_bintray_version = 1.8.0
kotlintest_version = 3.1.0
kotlin_version = 1.2.41
slf4j_version = 1.7.25
xenocom_version = 0.0.7


================================================
FILE: gradlew
================================================
#!/usr/bin/env sh

##############################################################################
##
##  Gradle start up script for UN*X
##
##############################################################################

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
        PRG="$link"
    else
        PRG=`dirname "$PRG"`"/$link"
    fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn () {
    echo "$*"
}

die () {
    echo
    echo "$*"
    echo
    exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
  CYGWIN* )
    cygwin=true
    ;;
  Darwin* )
    darwin=true
    ;;
  MINGW* )
    msys=true
    ;;
  NONSTOP* )
    nonstop=true
    ;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar

# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD="$JAVA_HOME/jre/sh/java"
    else
        JAVACMD="$JAVA_HOME/bin/java"
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD="java"
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
    MAX_FD_LIMIT=`ulimit -H -n`
    if [ $? -eq 0 ] ; then
        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
            MAX_FD="$MAX_FD_LIMIT"
        fi
        ulimit -n $MAX_FD
        if [ $? -ne 0 ] ; then
            warn "Could not set maximum file descriptor limit: $MAX_FD"
        fi
    else
        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
    fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
    JAVACMD=`cygpath --unix "$JAVACMD"`

    # We build the pattern for arguments to be converted via cygpath
    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
    SEP=""
    for dir in $ROOTDIRSRAW ; do
        ROOTDIRS="$ROOTDIRS$SEP$dir"
        SEP="|"
    done
    OURCYGPATTERN="(^($ROOTDIRS))"
    # Add a user-defined pattern to the cygpath arguments
    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
    fi
    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    i=0
    for arg in "$@" ; do
        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option

        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
        else
            eval `echo args$i`="\"$arg\""
        fi
        i=$((i+1))
    done
    case $i in
        (0) set -- ;;
        (1) set -- "$args0" ;;
        (2) set -- "$args0" "$args1" ;;
        (3) set -- "$args0" "$args1" "$args2" ;;
        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
    esac
fi

# Escape application args
save () {
    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
    echo " "
}
APP_ARGS=$(save "$@")

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"

# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
  cd "$(dirname "$0")"
fi

exec "$JAVACMD" "$@"


================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windows variants

if not "%OS%" == "Windows_NT" goto win9xME_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega


================================================
FILE: settings.gradle
================================================
rootProject.name = "kotlin-argparser"


================================================
FILE: src/main/kotlin/com/xenomachina/argparser/ArgParser.kt
================================================
// Copyright © 2016 Laurence Gonsalves
//
// This file is part of kotlin-argparser, a library which can be found at
// http://github.com/xenomachina/kotlin-argparser
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2.1 of the License, or (at your
// option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
// for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, see http://www.gnu.org/licenses/

package com.xenomachina.argparser

import com.xenomachina.argparser.PosixNaming.identifierToArgName
import com.xenomachina.argparser.PosixNaming.identifierToOptionName
import com.xenomachina.argparser.PosixNaming.optionNameToArgName
import com.xenomachina.argparser.PosixNaming.selectRepresentativeOptionName
import com.xenomachina.common.Holder
import com.xenomachina.common.orElse
import java.util.LinkedHashSet
import kotlin.reflect.KProperty

/**
 * A command-line option/argument parser.
 *
 * @param args the command line arguments to parse
 * @param mode parsing mode, defaults to GNU-style parsing
 * @param helpFormatter if non-null, creates `--help` and `-h` options that trigger a [ShowHelpException] which will use
 * the supplied [HelpFormatter] to generate a help message.
 */
class ArgParser(
    args: Array<out String>,
    mode: Mode = Mode.GNU,
    helpFormatter: HelpFormatter? = DefaultHelpFormatter()
) {

    enum class Mode {
        /** For GNU-style option parsing, where options may appear after positional arguments. */
        GNU,

        /** For POSIX-style option parsing, where options must appear before positional arguments. */
        POSIX
    }

    /**
     * Creates a Delegate for a zero-argument option that returns true if and only the option is present in args.
     */
    fun flagging(vararg names: String, help: String): Delegate<Boolean> =
            option<Boolean>(
                    *names,
                    help = help) { true }.default(false)

    /**
     * Creates a DelegateProvider for a zero-argument option that returns true if and only the option is present in
     * args.
     */
    fun flagging(help: String) =
            DelegateProvider { identifier -> flagging(identifierToOptionName(identifier), help = help) }

    /**
     * Creates a Delegate for a zero-argument option that returns the count of how many times the option appears in
     * args.
     */
    fun counting(vararg names: String, help: String): Delegate<Int> =
            option<Int>(
                    *names,
                    isRepeating = true,
                    help = help) { value.orElse { 0 } + 1 }.default(0)

    /**
     * Creates a DelegateProvider for a zero-argument option that returns the count of how many times the option appears
     * in args.
     */
    fun counting(help: String) = DelegateProvider {
        identifier -> counting(identifierToOptionName(identifier), help = help)
    }

    /**
     * Creates a Delegate for a single-argument option that stores and returns the option's (transformed) argument.
     */
    fun <T> storing(
        vararg names: String,
        help: String,
        argName: String? = null,
        transform: String.() -> T
    ): Delegate<T> {
        val nonNullArgName = argName ?: optionNameToArgName(selectRepresentativeOptionName(names))
        return option(
                *names,
                errorName = nonNullArgName,
                argNames = listOf(nonNullArgName),
                help = help) { transform(arguments.first()) }
    }

    /**
     * Creates a DelegateProvider for a single-argument option that stores and returns the option's (transformed)
     * argument.
     */
    fun <T> storing(
        help: String,
        argName: String? = null,
        transform: String.() -> T
    ) = DelegateProvider { identifier ->
        storing(identifierToOptionName(identifier), help = help, argName = argName, transform = transform)
    }

    /**
     * Creates a Delegate for a single-argument option that stores and returns the option's argument.
     */
    fun storing(vararg names: String, help: String, argName: String? = null): Delegate<String> =
            storing(*names, help = help, argName = argName) { this }

    /**
     * Creates a DelegateProvider for a single-argument option that stores and returns the option's argument.
     */
    fun storing(help: String, argName: String? = null) =
            DelegateProvider { identifier ->
                storing(identifierToOptionName(identifier), help = help, argName = argName) }

    /**
     * Creates a Delegate for a single-argument option that adds the option's (transformed) argument to a
     * MutableCollection each time the option appears in args, and returns said MutableCollection.
     */
    fun <E, T : MutableCollection<E>> adding(
        vararg names: String,
        help: String,
        argName: String? = null,
        initialValue: T,
        transform: String.() -> E
    ): Delegate<T> {
        val nonNullArgName = argName ?: optionNameToArgName(selectRepresentativeOptionName(names))
        return option<T>(
                *names,
                help = help,
                argNames = listOf(nonNullArgName),
                isRepeating = true) {
            val result = value.orElse { initialValue }
            result.add(transform(arguments.first()))
            result
        }.default(initialValue)
    }

    /**
     * Creates a DelegateProvider for a single-argument option that adds the option's (transformed) argument to a
     * MutableCollection each time the option appears in args, and returns said MutableCollection.
     */
    fun <E, T : MutableCollection<E>> adding(
        help: String,
        argName: String? = null,
        initialValue: T,
        transform: String.() -> E
    ) = DelegateProvider { identifier ->
        adding(
            identifierToOptionName(identifier),
            help = help,
            argName = argName,
            initialValue = initialValue,
            transform = transform)
    }

    /**
     * Creates a Delegate for a single-argument option that adds the option's (transformed) argument to a
     * MutableList each time the option appears in args, and returns said MutableCollection.
     */
    fun <T> adding(
        vararg names: String,
        help: String,
        argName: String? = null,
        transform: String.() -> T
    ) = adding(*names, help = help, argName = argName, initialValue = mutableListOf(), transform = transform)

    /**
     * Creates a DelegateProvider for a single-argument option that adds the option's (transformed) argument to a
     * MutableList each time the option appears in args, and returns said MutableCollection.
     */
    fun <T> adding(
        help: String,
        argName: String? = null,
        transform: String.() -> T
    ) = DelegateProvider { identifier ->
        adding(identifierToOptionName(identifier), help = help, argName = argName, transform = transform) }

    /**
     * Creates a Delegate for a single-argument option that adds the option's argument to a MutableList each time the
     * option appears in args, and returns said MutableCollection.
     */
    fun adding(vararg names: String, help: String, argName: String? = null): Delegate<MutableList<String>> =
            adding(*names, help = help, argName = argName) { this }

    /**
     * Creates a DelegateProvider for a single-argument option that adds the option's argument to a MutableList each
     * time the option appears in args, and returns said MutableCollection.
     */
    fun adding(help: String) = DelegateProvider { identifier ->
        adding(identifierToOptionName(identifier), help = help) }

    /**
     * Creates a Delegate for a zero-argument option that maps from the option's name as it appears in args to one of a
     * fixed set of values.
     */
    fun <T> mapping(vararg pairs: Pair<String, T>, help: String): Delegate<T> =
            mapping(mapOf(*pairs), help = help)

    /**
     * Creates a Delegate for a zero-argument option that maps from the option's name as it appears in args to one of a
     * fixed set of values.
     */
    fun <T> mapping(map: Map<String, T>, help: String): Delegate<T> {
        val names = map.keys.toTypedArray()
        return option(*names,
                errorName = map.keys.joinToString("|"),
                help = help) {
            // This cannot be null, because the optionName was added to the map
            // at the same time it was registered with the ArgParser.
            map[optionName]!!
        }
    }

    /**
     * Creates a Delegate for an option with the specified names.
     * @param names names of options, with leading "-" or "--"
     * @param errorName name to use when talking about this option in error messages, or null to base it upon the
     * option names
     * @param help the help text for this option
     * @param argNames names of this option's arguments
     * @param isRepeating whether or not it makes sense to repeat this option -- usually used for options where
     * specifying the option more than once yields a value than cannot be expressed by specifying the option only once
     * @param handler a function that computes the value of this option from an [OptionInvocation]
     */
    fun <T> option(
        // TODO: add optionalArg: Boolean
        vararg names: String,
        help: String,
        errorName: String? = null,
        argNames: List<String> = emptyList(),
        isRepeating: Boolean = false,
        handler: OptionInvocation<T>.() -> T
    ): Delegate<T> {
        val delegate = OptionDelegate<T>(
                parser = this,
                errorName = errorName ?: optionNameToArgName(selectRepresentativeOptionName(names)),
                help = help,
                optionNames = listOf(*names),
                argNames = argNames.toList(),
                isRepeating = isRepeating,
                handler = handler)
        return delegate
    }

    /**
     * Creates a Delegate for a single positional argument which returns the argument's value.
     */
    fun positional(name: String, help: String) = positional(name, help = help) { this }

    /**
     * Creates a DelegateProvider for a single positional argument which returns the argument's value.
     */
    fun positional(help: String) =
            DelegateProvider { identifier -> positional(identifierToArgName(identifier), help = help) }

    /**
     * Creates a Delegate for a single positional argument which returns the argument's transformed value.
     */
    fun <T> positional(
        name: String,
        help: String,
        transform: String.() -> T
    ): Delegate<T> {
        return WrappingDelegate(
                positionalList(name, help = help, sizeRange = 1..1, transform = transform)
        ) { it[0] }
    }

    /**
     * Creates a DelegateProvider for a single positional argument which returns the argument's transformed value.
     */
    fun <T> positional(
        help: String,
        transform: String.() -> T
    ) = DelegateProvider { identifier ->
        positional(identifierToArgName(identifier), help = help, transform = transform)
    }

    /**
     * Creates a Delegate for a sequence of positional arguments which returns a List containing the arguments.
     */
    fun positionalList(
        name: String,
        help: String,
        sizeRange: IntRange = 1..Int.MAX_VALUE
    ) = positionalList(name, help = help, sizeRange = sizeRange) { this }

    /**
     * Creates a DelegateProvider for a sequence of positional arguments which returns a List containing the arguments.
     */
    fun positionalList(
        help: String,
        sizeRange: IntRange = 1..Int.MAX_VALUE
    ) = DelegateProvider { identifier ->
        positionalList(identifierToArgName(identifier), help = help, sizeRange = sizeRange)
    }

    /**
     * Creates a Delegate for a sequence of positional arguments which returns a List containing the transformed
     * arguments.
     */
    fun <T> positionalList(
        name: String,
        help: String,
        sizeRange: IntRange = 1..Int.MAX_VALUE,
        transform: String.() -> T
    ): Delegate<List<T>> {
        sizeRange.run {
            require(step == 1) { "step must be 1, not $step" }
            require(first <= last) { "backwards ranges are not allowed: $first > $last" }
            require(first >= 0) { "sizeRange cannot start at $first, must be non-negative" }

            // Technically, last == 0 is ok but not especially useful, so we
            // disallow it as it's probably unintentional.
            require(last > 0) { "sizeRange only allows $last arguments, must allow at least 1" }
        }

        return PositionalDelegate<T>(this, name, sizeRange, help = help, transform = transform)
    }

    /**
     * Creates a DelegateProvider for a sequence of positional arguments which returns a List containing the transformed
     * arguments.
     */
    fun <T> positionalList(
        help: String,
        sizeRange: IntRange = 1..Int.MAX_VALUE,
        transform: String.() -> T
    ) = DelegateProvider { identifier -> positionalList(identifierToArgName(identifier), help, sizeRange, transform) }

    abstract class Delegate<out T> internal constructor() {
        /** The value associated with this delegate */
        abstract val value: T

        /** The name used to refer to this delegate's value in error messages */
        abstract val errorName: String

        /** The user-visible help text for this delegate */
        abstract val help: String

        /** Add validation logic. Validator should throw a [SystemExitException] on failure. */
        abstract fun addValidator(validator: Delegate<T>.() -> Unit): Delegate<T>

        /** Allows this object to act as a property delegate */
        operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value

        /**
         * Allows this object to act as a property delegate provider.
         *
         * It provides itself, and also registers itself with the [ArgParser] at that time.
         */
        operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ArgParser.Delegate<T> {
            registerRoot()
            return this
        }

        internal abstract val parser: ArgParser

        /**
         * Indicates whether or not a value has been set for this delegate
         */
        internal abstract val hasValue: Boolean

        internal fun checkHasValue() {
            if (!hasValue) throw MissingValueException(errorName)
        }

        internal abstract fun validate()

        internal abstract fun toHelpFormatterValue(): HelpFormatter.Value

        internal fun registerRoot() {
            parser.checkNotParsed()
            parser.delegates.add(this)
            registerLeaf(this)
        }

        internal abstract fun registerLeaf(root: Delegate<*>)

        internal abstract val hasValidators: Boolean
    }

    /**
     * Provides a [Delegate] when given a name. This makes it possible to infer
     * a name for the `Delegate` based on the name it is bound to, rather than
     * specifying a name explicitly.
     */
    class DelegateProvider<out T>(
        private val default: (() -> T)? = null,
        internal val ctor: (identifier: String) -> Delegate<T>
    ) {
        operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): Delegate<T> {
            val delegate = ctor(prop.name)
            return (if (default == null) delegate
                    else delegate.default(default)).provideDelegate(thisRef, prop)
        }
    }

    /**
     * @property value a Holder containing the current value associated with this option, or null if unset
     * @property optionName the name used for this option in this invocation
     * @property arguments the arguments supplied for this option
     */
    data class OptionInvocation<T> internal constructor(
        // Internal constructor so future versions can add properties
        // without breaking compatibility.
        val value: Holder<T>?,
        val optionName: String,
        val arguments: List<String>
    )

    private val shortOptionDelegates = mutableMapOf<Char, OptionDelegate<*>>()
    private val longOptionDelegates = mutableMapOf<String, OptionDelegate<*>>()
    private val positionalDelegates = mutableListOf<Pair<PositionalDelegate<*>, Boolean>>()
    private val delegates = LinkedHashSet<Delegate<*>>()

    internal fun registerOption(name: String, delegate: OptionDelegate<*>) {
        if (name.startsWith("--")) {
            require(name.length > 2) { "long option '$name' must have at least one character after hyphen" }
            require(name !in longOptionDelegates) { "long option '$name' already in use" }
            longOptionDelegates.put(name, delegate)
        } else if (name.startsWith("-")) {
            require(name.length == 2) { "short option '$name' can only have one character after hyphen" }
            val key = name.get(1)
            require(key !in shortOptionDelegates) { "short option '$name' already in use" }
            shortOptionDelegates.put(key, delegate)
        } else {
            throw IllegalArgumentException("illegal option name '$name' -- must start with '-' or '--'")
        }
    }

    internal fun registerPositional(delegate: PositionalDelegate<*>, hasDefault: Boolean) {
        positionalDelegates.add(delegate to hasDefault)
    }

    private var inValidation = false
    private var finished = false

    /**
     * Ensures that arguments have been parsed and validated.
     *
     * @throws SystemExitException if parsing or validation failed.
     */
    fun force() {
        if (!inParse) {
            if (!finished) {
                parseOptions
                if (!inValidation) {
                    inValidation = true
                    try {
                        for (delegate in delegates) delegate.checkHasValue()
                        for (delegate in delegates) delegate.validate()
                    } finally {
                        inValidation = false
                    }
                }
            }
        }
    }

    /**
     * Provides an instance of T, where all arguments have already been parsed and validated
     */
    fun <T> parseInto(constructor: (ArgParser) -> T): T {
        if (builtinDelegateCount != delegates.size) {
            throw IllegalStateException("You can only use the parseInto function with a clean ArgParser instance")
        }
        val provided = constructor(this)
        force()
        return provided
    }

    private var inParse = false

    internal fun checkNotParsed() {
        if (inParse || finished) throw IllegalStateException("arguments have already been parsed")
    }

    private val parseOptions by lazy {
        val positionalArguments = mutableListOf<String>()
        inParse = true
        try {
            var i = 0
            optionLoop@ while (i < args.size) {
                val arg = args[i]
                i += when {
                    arg == "--" -> {
                        i++
                        break@optionLoop
                    }
                    arg.startsWith("--") ->
                        parseLongOpt(i, args)
                    arg.startsWith("-") ->
                        parseShortOpts(i, args)
                    else -> {
                        positionalArguments.add(arg)
                        when (mode) {
                            Mode.GNU -> 1
                            Mode.POSIX -> {
                                i++
                                break@optionLoop
                            }
                        }
                    }
                }
            }

            // Collect remaining arguments as positional-only arguments
            positionalArguments.addAll(args.slice(i..args.size - 1))

            parsePositionalArguments(positionalArguments)
            finished = true
        } finally {
            inParse = false
        }
    }

    private fun parsePositionalArguments(args: List<String>) {
        var lastValueName: String? = null
        var index = 0
        var remaining = args.size
        var extra = (remaining - positionalDelegates.map {
            if (it.second) 0 else it.first.sizeRange.first
        }.sum()).coerceAtLeast(0)
        for ((delegate, hasDefault) in positionalDelegates) {
            val minSize = if (hasDefault) 0 else delegate.sizeRange.first
            val sizeRange = delegate.sizeRange
            val chunkSize = (minSize + extra).coerceAtMost(sizeRange.endInclusive)
            if (chunkSize > remaining) {
                throw MissingRequiredPositionalArgumentException(delegate.errorName)
            }
            if (chunkSize != 0 || !hasDefault) {
                delegate.parseArguments(args.subList(index, index + chunkSize))
            }
            lastValueName = delegate.errorName
            index += chunkSize
            remaining -= chunkSize
            extra -= chunkSize - minSize
        }
        if (remaining > 0) {
            throw UnexpectedPositionalArgumentException(lastValueName)
        }
    }

    /**
     * @param index index into args, starting at a long option, eg: "--verbose"
     * @param args array of command-line arguments
     * @return number of arguments that have been processed
     */
    private fun parseLongOpt(index: Int, args: Array<out String>): Int {
        val name: String
        val firstArg: String?
        val m = NAME_EQUALS_VALUE_REGEX.matchEntire(args[index])
        if (m == null) {
            name = args[index]
            firstArg = null
        } else {
            // if NAME_EQUALS_VALUE_REGEX then there must be groups 1 and 2
            name = m.groups[1]!!.value
            firstArg = m.groups[2]!!.value
        }
        val delegate = longOptionDelegates.get(name)
        if (delegate == null) {
            throw UnrecognizedOptionException(name)
        } else {
            var consumedArgs = delegate.parseOption(name, firstArg, index + 1, args)
            if (firstArg != null) {
                if (consumedArgs < 1) throw UnexpectedOptionArgumentException(name)
                consumedArgs -= 1
            }
            return 1 + consumedArgs
        }
    }

    /**
     * @param index index into args, starting at a set of short options, eg: "-abXv"
     * @param args array of command-line arguments
     * @return number of arguments that have been processed
     */
    private fun parseShortOpts(index: Int, args: Array<out String>): Int {
        val opts = args[index]
        var optIndex = 1
        while (optIndex < opts.length) {
            val optKey = opts[optIndex]
            val optName = "-$optKey"
            optIndex++ // optIndex now points just after optKey

            val delegate = shortOptionDelegates.get(optKey)
            if (delegate == null) {
                throw UnrecognizedOptionException(optName)
            } else {
                val firstArg = if (optIndex >= opts.length) null else opts.substring(optIndex)
                val consumed = delegate.parseOption(optName, firstArg, index + 1, args)
                if (consumed > 0) {
                    return consumed + (if (firstArg == null) 1 else 0)
                }
            }
        }
        return 1
    }

    init {
        if (helpFormatter != null) {
            option<Unit>("-h", "--help",
                    errorName = "HELP", // This should never be used, but we need to say something
                    help = "show this help message and exit") {
                throw ShowHelpException(helpFormatter, delegates.toList())
            }.default(Unit).registerRoot()
        }
    }

    private val builtinDelegateCount = delegates.size
}

private val NAME_EQUALS_VALUE_REGEX = Regex("^([^=]+)=(.*)$")
internal val LEADING_HYPHENS_REGEX = Regex("^-{1,2}")

private const val OPTION_CHAR_CLASS = "[a-zA-Z0-9]"
internal val OPTION_NAME_RE = Regex("^(-$OPTION_CHAR_CLASS)|(--$OPTION_CHAR_CLASS+([-_\\.]$OPTION_CHAR_CLASS+)*)$")

private const val ARG_INITIAL_CHAR_CLASS = "[A-Z]"
private const val ARG_CHAR_CLASS = "[A-Z0-9]"
internal val ARG_NAME_RE = Regex("^$ARG_INITIAL_CHAR_CLASS+([-_\\.]$ARG_CHAR_CLASS+)*$")


================================================
FILE: src/main/kotlin/com/xenomachina/argparser/Default.kt
================================================
// Copyright © 2016 Laurence Gonsalves
//
// This file is part of kotlin-argparser, a library which can be found at
// http://github.com/xenomachina/kotlin-argparser
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2.1 of the License, or (at your
// option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
// for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, see http://www.gnu.org/licenses/

package com.xenomachina.argparser

/**
 * Returns a new `DelegateProvider` with the specified default value.
 *
 * @param newDefault the default value for the resulting [ArgParser.Delegate]
 */
fun <T> ArgParser.DelegateProvider<T>.default(newDefault: T): ArgParser.DelegateProvider<T> {
    return ArgParser.DelegateProvider(ctor = ctor, default = { newDefault })
}

/**
 * Returns a new `DelegateProvider` with the specified default value from a lambda.
 *
 * @param newDefault the default value for the resulting [ArgParser.Delegate]
 */
fun <T> ArgParser.DelegateProvider<T>.default(newDefault: () -> T): ArgParser.DelegateProvider<T> {
    return ArgParser.DelegateProvider(ctor = ctor, default = newDefault)
}

/**
 * Returns a new `Delegate` with the specified default value.
 *
 * @param newDefault the default value for the resulting [ArgParser.Delegate]
 */
fun <T> ArgParser.Delegate<T>.default(defaultValue: T): ArgParser.Delegate<T> = default { defaultValue }

/**
 * Returns a new `Delegate` with the specified default value as a lambda.
 *
 * @param newDefault the default value for the resulting [ArgParser.Delegate]
 */
fun <T> ArgParser.Delegate<T>.default(defaultValue: () -> T): ArgParser.Delegate<T> {
    if (hasValidators) {
        throw IllegalStateException("Cannot add default after adding validators")
    }
    val inner = this

    return object : ArgParser.Delegate<T>() {

        override val hasValidators: Boolean
            get() = inner.hasValidators

        override fun toHelpFormatterValue(): HelpFormatter.Value = inner.toHelpFormatterValue().copy(isRequired = false)

        override fun validate() {
            inner.validate()
        }

        override val parser: ArgParser
            get() = inner.parser

        override val value: T
            get() {
                inner.parser.force()
                return if (inner.hasValue) inner.value else defaultValue()
            }

        override val hasValue: Boolean
            get() = true

        override val errorName: String
            get() = inner.errorName

        override val help: String
            get() = inner.help

        override fun addValidator(validator: ArgParser.Delegate<T>.() -> Unit): ArgParser.Delegate<T> =
                apply { inner.addValidator { validator(this@apply) } }

        override fun registerLeaf(root: ArgParser.Delegate<*>) {
            inner.registerLeaf(root)
        }
    }
}


================================================
FILE: src/main/kotlin/com/xenomachina/argparser/DefaultHelpFormatter.kt
================================================
// Copyright © 2016 Laurence Gonsalves
//
// This file is part of kotlin-argparser, a library which can be found at
// http://github.com/xenomachina/kotlin-argparser
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2.1 of the License, or (at your
// option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
// for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, see http://www.gnu.org/licenses/

package com.xenomachina.argparser

import com.xenomachina.text.NBSP_CODEPOINT
import com.xenomachina.text.term.codePointWidth
import com.xenomachina.text.term.columnize
import com.xenomachina.text.term.wrapText

/**
 * Default implementation of [HelpFormatter]. Output is modelled after that of common UNIX utilities and looks
 * something like this:
 *
 * ```
 * usage: program_name     [-h] [-n] [-I INCLUDE]... -o OUTPUT
 *                         [-v]... SOURCE... DEST
 *
 * Does something really useful.
 *
 * required arguments:
 *   -o OUTPUT,            directory in which all output should
 *   --output OUTPUT       be generated
 *
 * optional arguments:
 *   -h, --help            show this help message and exit
 *
 *   -n, --dry-run         don't do anything
 *
 *   -I INCLUDE,           search in this directory for header
 *   --include INCLUDE     files
 *
 *  -v, --verbose         increase verbosity
 *
 * positional arguments:
 *   SOURCE                source file
 *
 *   DEST                  destination file
 *
 * More info is available at http://program-name.example.com/
 * ```
 *
 * @property prologue Text that should appear near the beginning of the help, immediately after the usage summary.
 * @property epilogue Text that should appear at the end of the help.
 */
class DefaultHelpFormatter(
    val prologue: String? = null,
    val epilogue: String? = null
) : HelpFormatter {
    val indent = "  "
    val indentWidth = indent.codePointWidth()

    override fun format(
        programName: String?,
        columns: Int,
        values: List<HelpFormatter.Value>
    ): String {
        val effectiveColumns = when {
            columns < 0 -> throw IllegalArgumentException("columns must be non-negative")
            columns == 0 -> Int.MAX_VALUE
            else -> columns
        }
        val sb = StringBuilder()
        appendUsage(sb, effectiveColumns, programName, values)
        sb.append("\n")

        if (!prologue.isNullOrEmpty()) {
            sb.append("\n\n")
            // we just checked that prologue is non-null
            sb.append(prologue!!.wrapText(effectiveColumns))
            sb.append("\n\n")
        }

        val required = mutableListOf<HelpFormatter.Value>()
        val optional = mutableListOf<HelpFormatter.Value>()
        val positional = mutableListOf<HelpFormatter.Value>()

        for (value in values) {
            when {
                value.isPositional -> positional
                value.isRequired -> required
                else -> optional
            }.add(value)
        }
        val usageColumns = 2 * indentWidth - 1 + if (columns == 0) {
            values.map { usageText(it).length }.max() ?: 0
        } else {
            // Make left column as narrow as possible without wrapping any of the individual usages, though no wider
            // than half the screen.
            (values.map {
                usageText(it).split(" ").map { it.length }.max() ?: 0
            }.max() ?: 0).coerceAtMost(effectiveColumns / 2)
        }

        appendSection(sb, usageColumns, effectiveColumns, "required", required)
        appendSection(sb, usageColumns, effectiveColumns, "optional", optional)
        appendSection(sb, usageColumns, effectiveColumns, "positional", positional)

        if (!epilogue?.trim().isNullOrEmpty()) {
            sb.append("\n")
            // we just checked that epilogue is non-null
            sb.append(epilogue!!.trim().wrapText(effectiveColumns))
            sb.append("\n")
        }

        return sb.toString()
    }

    private fun appendSection(
        sb: StringBuilder,
        usageColumns: Int,
        columns: Int,
        name: String,
        values: List<HelpFormatter.Value>
    ) {

        if (!values.isEmpty()) {
            sb.append("\n")
            sb.append("$name arguments:\n")
            for (value in values) {
                val left = usageText(value).wrapText(usageColumns - indentWidth).prependIndent(indent)
                val right = value.help.wrapText(columns - usageColumns - 2 * indentWidth).prependIndent(indent)
                sb.append(columnize(left, right, minWidths = intArrayOf(usageColumns)))
                sb.append("\n\n")
            }
        }
    }

    private fun usageText(value: HelpFormatter.Value) =
            value.usages.map { it.replace(' ', '\u00a0') }.joinToString(", ")

    private fun appendUsage(sb: StringBuilder, columns: Int, programName: String?, values: List<HelpFormatter.Value>) {
        var usageStart = USAGE_PREFIX + (if (programName != null) " $programName" else "")

        val valueSB = StringBuilder()
        for (value in values) value.run {
            if (!usages.isEmpty()) {
                val usage = usages[0].replace(' ', NBSP_CODEPOINT.toChar())
                if (isRequired) {
                    valueSB.append(" $usage")
                } else {
                    valueSB.append(" [$usage]")
                }
                if (isRepeating) {
                    valueSB.append("...")
                }
            }
        }

        if (usageStart.length > columns / 2) {
            sb.append(usageStart)
            sb.append("\n")
            val valueIndent = (USAGE_PREFIX + " " + indent).codePointWidth()
            val valueColumns = columns - valueIndent
            sb.append(valueSB.toString().wrapText(valueColumns).prependIndent(" ".repeat(valueIndent)))
        } else {
            usageStart += " "
            val valueColumns = columns - usageStart.length
            sb.append(columnize(usageStart, valueSB.toString().wrapText(valueColumns)))
        }
    }
}

private const val USAGE_PREFIX = "usage:"


================================================
FILE: src/main/kotlin/com/xenomachina/argparser/Exceptions.kt
================================================
// Copyright © 2016 Laurence Gonsalves
//
// This file is part of kotlin-argparser, a library which can be found at
// http://github.com/xenomachina/kotlin-argparser
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2.1 of the License, or (at your
// option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
// for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, see http://www.gnu.org/licenses/

package com.xenomachina.argparser

import java.io.Writer

/**
 * Indicates that the user requested that help should be shown (with the
 * `--help` option, for example).
 */
class ShowHelpException internal constructor(
    private val helpFormatter: HelpFormatter,
    private val delegates: List<ArgParser.Delegate<*>>
) : SystemExitException("Help was requested", 0) {
    override fun printUserMessage(writer: Writer, programName: String?, columns: Int) {
        writer.write(helpFormatter.format(programName, columns, delegates.map { it.toHelpFormatterValue() }))
    }
}

/**
 * Indicates that an unrecognized option was supplied.
 *
 * @property optName the name of the option
 */
open class UnrecognizedOptionException(val optName: String) :
        SystemExitException("unrecognized option '$optName'", 2)

/**
 * Indicates that a value is missing after parsing has completed.
 *
 * @property valueName the name of the missing value
 */
open class MissingValueException(val valueName: String) :
        SystemExitException("missing $valueName", 2)

/**
 * Indicates that the value of a supplied argument is invalid.
 */
open class InvalidArgumentException(message: String) : SystemExitException(message, 2)

/**
 * Indicates that a required option argument was not supplied.
 *
 * @property optName the name of the option
 * @property argName the name of the missing argument, or null
 */
open class OptionMissingRequiredArgumentException(val optName: String, val argName: String? = null) :
        SystemExitException(
            "option '$optName' is missing " + (
                if (argName == null) "a required argument"
                else "the required argument $argName"),
            2)

/**
 * Indicates that a required positional argument was not supplied.
 *
 * @property argName the name of the positional argument
 */
open class MissingRequiredPositionalArgumentException(val argName: String) :
        SystemExitException("missing $argName operand", 2)

/**
 * Indicates that an argument was forced upon an option that does not take one.
 *
 * For example, if the arguments contained "--foo=bar" and the "--foo" option does not consume any arguments.
 *
 * @property optName the name of the option
 */
open class UnexpectedOptionArgumentException(val optName: String) :
        SystemExitException("option '$optName' doesn't allow an argument", 2)

/**
 * Indicates that there is an unhandled positional argument.
 *
 * @property valueName the name of the missing value
 */
open class UnexpectedPositionalArgumentException(val valueName: String?) :
        SystemExitException("unexpected argument${if (valueName == null) "" else " after $valueName"}", 2)


================================================
FILE: src/main/kotlin/com/xenomachina/argparser/HelpFormatter.kt
================================================
// Copyright © 2016 Laurence Gonsalves
//
// This file is part of kotlin-argparser, a library which can be found at
// http://github.com/xenomachina/kotlin-argparser
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2.1 of the License, or (at your
// option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
// for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, see http://www.gnu.org/licenses/

package com.xenomachina.argparser

/**
 * Formats help for an [ArgParser].
 */
interface HelpFormatter {
    /**
     * Formats a help message.
     *
     * @param programName name of the program as it should appear in usage information, or null if
     * program name is unknown.
     * @param columns width of display help should be formatted for, measured in character cells, or 0 for infinite
     * width.
     * @param values [Value] objects describing the arguments types available.
     */
    fun format(programName: String?, columns: Int, values: List<Value>): String

    /**
     * An option or positional argument type which should be formatted for help
     *
     * @param usages possible usage strings for this argument type
     * @param isRequired indicates whether this is required
     * @param isRepeating indicates whether it makes sense to repeat this argument
     * @param isPositional indicates whether this is a positional argument
     * @param help help text provided at Delegate construction time
     */
    data class Value(
        val usages: List<String>,
        val isRequired: Boolean,
        val isRepeating: Boolean,
        val isPositional: Boolean,
        val help: String
    )
}


================================================
FILE: src/main/kotlin/com/xenomachina/argparser/OptionDelegate.kt
================================================
// Copyright © 2016 Laurence Gonsalves
//
// This file is part of kotlin-argparser, a library which can be found at
// http://github.com/xenomachina/kotlin-argparser
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2.1 of the License, or (at your
// option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
// for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, see http://www.gnu.org/licenses/

package com.xenomachina.argparser

import com.xenomachina.common.Holder

internal class OptionDelegate<T>(
    parser: ArgParser,
    errorName: String,
    help: String,
    val optionNames: List<String>,
    val argNames: List<String>,
    val isRepeating: Boolean,
    val handler: ArgParser.OptionInvocation<T>.() -> T
) : ParsingDelegate<T>(parser, errorName, help) {
    init {
        for (optionName in optionNames) {
            if (!OPTION_NAME_RE.matches(optionName)) {
                throw IllegalArgumentException("$optionName is not a valid option name")
            }
        }
        for (argName in argNames) {
            if (!ARG_NAME_RE.matches(argName)) {
                throw IllegalArgumentException("$argName is not a valid argument name")
            }
        }
    }

    fun parseOption(name: String, firstArg: String?, index: Int, args: Array<out String>): Int {
        val arguments = mutableListOf<String>()
        if (!argNames.isEmpty()) {
            if (firstArg != null) arguments.add(firstArg)
            val required = argNames.size - arguments.size
            if (required + index > args.size) {
                // Only pass an argName if more than one argument.
                // Naming it when there's just one seems unnecessarily verbose.
                val argName = if (argNames.size > 1) argNames[args.size - index] else null
                throw OptionMissingRequiredArgumentException(name, argName)
            }
            for (i in 0 until required) {
                arguments.add(args[index + i])
            }
        }
        val input = ArgParser.OptionInvocation(holder, name, arguments)
        holder = Holder(handler(input))
        return argNames.size
    }

    override fun toHelpFormatterValue(): HelpFormatter.Value {
        return HelpFormatter.Value(
                isRequired = (holder == null),
                isRepeating = isRepeating,
                usages = if (!argNames.isEmpty()) {
                    optionNames.map { "$it ${argNames.joinToString(" ")}" }
                } else {
                    optionNames
                },
                isPositional = false,
                help = help)
    }

    override fun registerLeaf(root: ArgParser.Delegate<*>) {
        for (name in optionNames) {
            parser.registerOption(name, this)
        }
    }
}


================================================
FILE: src/main/kotlin/com/xenomachina/argparser/ParsingDelegate.kt
================================================
// Copyright © 2016 Laurence Gonsalves
//
// This file is part of kotlin-argparser, a library which can be found at
// http://github.com/xenomachina/kotlin-argparser
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2.1 of the License, or (at your
// option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
// for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, see http://www.gnu.org/licenses/

package com.xenomachina.argparser

import com.xenomachina.common.Holder

internal abstract class ParsingDelegate<T>(
    override val parser: ArgParser,
    override val errorName: String,
    override val help: String
) : ArgParser.Delegate<T>() {

    protected var holder: Holder<T>? = null

    override fun addValidator(validator: ArgParser.Delegate<T>.() -> Unit): ArgParser.Delegate<T> = apply {
        validators.add(validator)
    }

    override val hasValidators: Boolean
        get() = validators.isNotEmpty()

    override val value: T
        get() {
            parser.force()
            checkHasValue()
            return holder!!.value
        }

    override val hasValue: Boolean
        get() = holder != null

    override fun validate() {
        for (validator in validators) validator()
    }

    private val validators = mutableListOf<ArgParser.Delegate<T>.() -> Unit>()
}


================================================
FILE: src/main/kotlin/com/xenomachina/argparser/PositionalDelegate.kt
================================================
// Copyright © 2016 Laurence Gonsalves
//
// This file is part of kotlin-argparser, a library which can be found at
// http://github.com/xenomachina/kotlin-argparser
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2.1 of the License, or (at your
// option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
// for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, see http://www.gnu.org/licenses/

package com.xenomachina.argparser

import com.xenomachina.common.Holder

internal class PositionalDelegate<T>(
    parser: ArgParser,
    argName: String,
    val sizeRange: IntRange,
    help: String,
    val transform: String.() -> T
) : ParsingDelegate<List<T>>(parser, argName, help) {

    init {
        require(ARG_NAME_RE.matches(argName)) { "$argName is not a valid argument name" }
    }

    override fun registerLeaf(root: ArgParser.Delegate<*>) {
        assert(holder == null)
        val hasDefault = root.hasValue
        if (hasDefault && sizeRange.first != 1) {
            throw IllegalStateException(
                    "default value can only be applied to positional that requires a minimum of 1 arguments")
        }
        // TODO: this feels like a bit of a kludge. Consider making .default only work on positional and not
        // postionalList by having them return different types?
        parser.registerPositional(this, hasDefault)
    }

    fun parseArguments(args: List<String>) {
        holder = Holder(args.map(transform))
    }

    override fun toHelpFormatterValue(): HelpFormatter.Value {
        return HelpFormatter.Value(
                isRequired = sizeRange.first > 0,
                isRepeating = sizeRange.last > 1,
                usages = listOf(errorName),
                isPositional = true,
                help = help)
    }
}


================================================
FILE: src/main/kotlin/com/xenomachina/argparser/PosixNaming.kt
================================================
// Copyright © 2016 Laurence Gonsalves
//
// This file is part of kotlin-argparser, a library which can be found at
// http://github.com/xenomachina/kotlin-argparser
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2.1 of the License, or (at your
// option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
// for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, see http://www.gnu.org/licenses/

package com.xenomachina.argparser

/**
 * Defines rules for POSIX-style argument and option naming.
 */
internal object PosixNaming {
    fun identifierToOptionName(identifier: String): String {
        return when (identifier.length) {
            1 -> "-" + identifier
            else -> "--" + identifier.camelCaseToUnderscored()
        }
    }

    fun String.camelCaseToUnderscored(): String {
        return replace('_', '-')
                .replace(Regex("(\\p{javaLowerCase})(\\p{javaUpperCase})")) { m ->
                    m.groups[1]!!.value + "-" + m.groups[2]!!.value.toLowerCase()
                }
    }

    fun identifierToArgName(identifier: String): String {
        return identifier.camelCaseToUnderscored().toUpperCase()
    }

    fun selectRepresentativeOptionName(names: Array<out String>): String {
        if (names.size < 1)
            throw IllegalArgumentException("need at least one option name")
        // Return first long option...
        for (name in names) {
            if (name.startsWith("--")) {
                return name
            }
        }
        // ... but failing that just return first option.
        return names[0]
    }

    fun optionNameToArgName(name: String) =
            LEADING_HYPHENS_REGEX.replace(name, "").toUpperCase().replace('-', '_')
}


================================================
FILE: src/main/kotlin/com/xenomachina/argparser/SystemExitException.kt
================================================
// Copyright © 2016 Laurence Gonsalves
//
// This file is part of kotlin-argparser, a library which can be found at
// http://github.com/xenomachina/kotlin-argparser
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2.1 of the License, or (at your
// option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
// for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, see http://www.gnu.org/licenses/

package com.xenomachina.argparser

import java.io.OutputStreamWriter
import java.io.Writer
import kotlin.system.exitProcess

/**
 * An exception that wants the process to terminate with a specific status code, and also (optionally) wants to display
 * a message to [System.out] or [System.err].
 *
 * @property returnCode the return code that this process should exit with
 */
open class SystemExitException(message: String, val returnCode: Int) : Exception(message) {
    /**
     * Prints a message for the user to either `System.err` or `System.out`, and then exits with the appropriate
     * return code.
     *
     * @param programName the name of this program as invoked, or null if not known
     * @param columns the number of columns to wrap at, or 0 if not to wrap at all
     */
    fun printAndExit(programName: String? = null, columns: Int = 0): Nothing {
        val writer = OutputStreamWriter(if (returnCode == 0) System.out else System.err)
        printUserMessage(writer, programName, columns)
        writer.flush()
        exitProcess(returnCode)
    }

    /**
     * Prints a message for the user to the specified `Writer`.
     *
     * @param writer where to write message for the user
     * @param programName the name of this program as invoked, or null if not known
     * @param columns the number of columns to wrap at, or 0 if not to wrap at all
     */
    open fun printUserMessage(writer: Writer, programName: String?, columns: Int) {
        val leader = if (programName == null) "" else "$programName: "
        writer.write("$leader$message\n")
    }
}

/**
 * Calls [SystemExitException.printAndExit] on any `SystemExitException` that
 * is caught.
 *
 * @param programName the name of the program. If null, the system property com.xenomachina.argparser.programName will
 * be used, if set.
 * @param columns the number of columns to wrap any caught
 * `SystemExitException` to.  Specify null for reasonable defaults, or 0 to not
 * wrap at all.
 * @param body the code that may throw a `SystemExitException`
 */
fun <R> mainBody(programName: String? = null, columns: Int? = null, body: () -> R): R {
    try {
        return body()
    } catch (e: SystemExitException) {
        e.printAndExit(
                programName ?: System.getProperty(PROGRAM_NAME_PROPERTY),
                columns ?: System.getenv("COLUMNS")?.toInt() ?: DEFAULT_COLUMNS)
    }
}

private const val PROGRAM_NAME_PROPERTY = "com.xenomachina.argparser.programName"
private const val DEFAULT_COLUMNS = 80


================================================
FILE: src/main/kotlin/com/xenomachina/argparser/WrappingDelegate.kt
================================================
// Copyright © 2016 Laurence Gonsalves
//
// This file is part of kotlin-argparser, a library which can be found at
// http://github.com/xenomachina/kotlin-argparser
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2.1 of the License, or (at your
// option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
// for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, see http://www.gnu.org/licenses/

package com.xenomachina.argparser

internal class WrappingDelegate<U, W>(
    private val inner: ArgParser.Delegate<U>,
    private val wrap: (U) -> W
) : ArgParser.Delegate<W>() {

    override val parser: ArgParser
        get() = inner.parser

    override val value: W
        get() = wrap(inner.value)

    override val hasValue: Boolean
        get() = inner.hasValue

    override val errorName: String
        get() = inner.errorName

    override val help: String
        get() = inner.help

    override fun validate() {
        inner.validate()
    }

    override fun toHelpFormatterValue(): HelpFormatter.Value = inner.toHelpFormatterValue()

    override fun addValidator(validator: ArgParser.Delegate<W>.() -> Unit): ArgParser.Delegate<W> =
            apply { inner.addValidator { validator(this@WrappingDelegate) } }

    override val hasValidators: Boolean
        get() = inner.hasValidators

    override fun registerLeaf(root: ArgParser.Delegate<*>) {
        inner.registerLeaf(root)
    }
}


================================================
FILE: src/test/kotlin/com/xenomachina/argparser/ArgParserTest.kt
================================================
// Copyright © 2016 Laurence Gonsalves
//
// This file is part of kotlin-argparser, a library which can be found at
// http://github.com/xenomachina/kotlin-argparser
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2.1 of the License, or (at your
// option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
// for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, see http://www.gnu.org/licenses/

package com.xenomachina.argparser

import com.xenomachina.argparser.PosixNaming.identifierToOptionName
import com.xenomachina.common.orElse
import io.kotlintest.matchers.beOfType
import io.kotlintest.should
import io.kotlintest.shouldBe
import io.kotlintest.shouldThrow
import io.kotlintest.specs.FunSpec
import java.io.File
import java.io.StringWriter

class ArgParserTest : FunSpec({
    test("Option name validation") {
        val parser = parserOf()

        // These are all acceptable.
        parser.option<Int>("-x", help = TEST_HELP) { 0 }
        parser.option<Int>("--x", help = TEST_HELP) { 0 }
        parser.option<Int>("--xy", help = TEST_HELP) { 0 }
        parser.option<Int>("-X", help = TEST_HELP) { 0 }
        parser.option<Int>("--X", help = TEST_HELP) { 0 }
        parser.option<Int>("--XY", help = TEST_HELP) { 0 }
        parser.option<Int>("--X-Y", help = TEST_HELP) { 0 }
        parser.option<Int>("--X_Y", help = TEST_HELP) { 0 }
        parser.option<Int>("-5", help = TEST_HELP) { 0 }
        parser.option<Int>("--5", help = TEST_HELP) { 0 }
        parser.option<Int>("--5Y", help = TEST_HELP) { 0 }
        parser.option<Int>("--X5", help = TEST_HELP) { 0 }
        parser.option<Int>("--x.y", help = TEST_HELP) { 0 }

        shouldThrow<IllegalArgumentException> {
            parser.option<Int>("-_", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.option<Int>("---x", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.option<Int>("x", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.option<Int>("", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.option<Int>("-xx", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.option<Int>("--foo bar", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.option<Int>("--foo--bar", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.option<Int>("--f!oobar", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.option<Int>("--.", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.option<Int>("--.foo", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.option<Int>("--foo.", help = TEST_HELP) { 0 }
        }
    }

    test("Positional name validation") {
        val parser = parserOf()

        // These are all acceptable.
        parser.positional<Int>("X", help = TEST_HELP) { 0 }
        parser.positional<Int>("XYZ", help = TEST_HELP) { 0 }
        parser.positional<Int>("XY-Z", help = TEST_HELP) { 0 }
        parser.positional<Int>("XY_Z", help = TEST_HELP) { 0 }
        parser.positional<Int>("XY.Z", help = TEST_HELP) { 0 }

        shouldThrow<IllegalArgumentException> {
            parser.positional<Int>("-", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.positional<Int>("_", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.positional<Int>("x", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.positional<Int>("", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.positional<Int>("-X", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.positional<Int>("X-", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.positional<Int>("X--Y", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.positional<Int>("X!", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.positional<Int>("5", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.positional<Int>(".", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.positional<Int>(".XY", help = TEST_HELP) { 0 }
        }

        shouldThrow<IllegalArgumentException> {
            parser.positional<Int>("XY.", help = TEST_HELP) { 0 }
        }

        // This should be acceptable
        parser.option<Int>("--foobar", argNames = listOf("X-Y"), help = TEST_HELP) { 0 }

        // This should not
        shouldThrow<IllegalArgumentException> {
            parser.option<Int>("--foobar", argNames = listOf("X--Y"), help = TEST_HELP) { 0 }
        }
    }

    test("Argless short options") {
        class Args(parser: ArgParser) {
            val xyz by parser.option<MutableList<String>>("-x", "-y", "-z",
                help = TEST_HELP) {
                value.orElse { mutableListOf<String>() }.apply {
                    add("$optionName")
                }
            }
        }

        Args(parserOf("-x", "-y", "-z", "-z", "-y")).xyz shouldBe listOf("-x", "-y", "-z", "-z", "-y")

        Args(parserOf("-xyz")).xyz shouldBe listOf("-x", "-y", "-z")
    }

    test("Short options with args") {
        class Args(parser: ArgParser) {
            val a by parser.flagging("-a", help = TEST_HELP)
            val b by parser.flagging("-b", help = TEST_HELP)
            val c by parser.flagging("-c", help = TEST_HELP)
            val xyz by parser.option<MutableList<String>>("-x", "-y", "-z",
                argNames = oneArgName, help = TEST_HELP) {
                value.orElse { mutableListOf<String>() }.apply {
                    add("$optionName:${arguments.first()}")
                }
            }
        }

        // Test with value as separate arg
        Args(parserOf("-x", "0", "-y", "1", "-z", "2", "-z", "3", "-y", "4")).xyz shouldBe listOf("-x:0", "-y:1", "-z:2", "-z:3", "-y:4")

        // Test with value concatenated
        Args(parserOf("-x0", "-y1", "-z2", "-z3", "-y4")).xyz shouldBe listOf("-x:0", "-y:1", "-z:2", "-z:3", "-y:4")

        // Test with = between option and value. Note that the "=" is treated as part of the option value for short options.
        Args(parserOf("-x=0", "-y=1", "-z=2", "-z=3", "-y=4")).xyz shouldBe listOf("-x:=0", "-y:=1", "-z:=2", "-z:=3", "-y:=4")

        // Test chained options. Note that an option with arguments must be last in the chain
        val chain1 = Args(parserOf("-abxc"))
        chain1.a shouldBe true
        chain1.b shouldBe true
        chain1.c shouldBe false
        chain1.xyz shouldBe listOf("-x:c")

        val chain2 = Args(parserOf("-axbc"))
        chain2.a shouldBe true
        chain2.b shouldBe false
        chain2.c shouldBe false
        chain2.xyz shouldBe listOf("-x:bc")
    }

    test("Mixed short options") {
        class Args(parser: ArgParser) {
            val def by parser.option<MutableList<String>>("-d", "-e", "-f",
                help = TEST_HELP) {
                value.orElse { mutableListOf<String>() }.apply {
                    add("$optionName")
                }
            }
            val abc by parser.option<MutableList<String>>("-a", "-b", "-c",
                help = TEST_HELP) {
                value.orElse { mutableListOf<String>() }.apply {
                    add("$optionName")
                }
            }
        }

        Args(parserOf("-adbefccbafed")).run {
            def shouldBe listOf("-d", "-e", "-f", "-f", "-e", "-d")
            abc shouldBe listOf("-a", "-b", "-c", "-c", "-b", "-a")
        }
    }

    test("Mixed short options with args") {
        class Args(parser: ArgParser) {
            val def by parser.option<MutableList<String>>("-d", "-e", "-f",
                help = TEST_HELP) {
                value.orElse { mutableListOf<String>() }.apply {
                    add("$optionName")
                }
            }
            val abc by parser.option<MutableList<String>>("-a", "-b", "-c",
                help = TEST_HELP) {
                value.orElse { mutableListOf<String>() }.apply {
                    add("$optionName")
                }
            }
            val xyz by parser.option<MutableList<String>>("-x", "-y", "-z",
                argNames = oneArgName,
                help = TEST_HELP) {
                value.orElse { mutableListOf<String>() }.apply {
                    add("$optionName:${arguments.first()}")
                }
            }
        }

        Args(parserOf("-adecfy5", "-x0", "-bzxy")).run {
            abc shouldBe listOf("-a", "-c", "-b")
            def shouldBe listOf("-d", "-e", "-f")
            xyz shouldBe listOf("-y:5", "-x:0", "-z:xy")
        }
    }

    test("Argless long options") {
        class Args(parser: ArgParser) {
            val xyz by parser.option<MutableList<String>>("--xray", "--yellow", "--zebra",
                help = TEST_HELP) {
                value.orElse { mutableListOf<String>() }.apply {
                    add("$optionName")
                }
            }
        }

        Args(parserOf("--xray", "--yellow", "--zebra", "--zebra", "--yellow")).xyz shouldBe listOf("--xray", "--yellow", "--zebra", "--zebra", "--yellow")

        Args(parserOf("--xray", "--yellow", "--zebra")).xyz shouldBe listOf("--xray", "--yellow", "--zebra")
    }

    test("Dotted long options") {
        class Args(parser: ArgParser) {
            val xyz by parser.option<MutableList<String>>("--x.ray", "--color.yellow", "--animal.zebra",
                help = TEST_HELP) {
                value.orElse { mutableListOf<String>() }.apply {
                    add("$optionName")
                }
            }
        }

        Args(parserOf("--x.ray", "--animal.zebra", "--color.yellow", "--x.ray")).xyz shouldBe listOf("--x.ray", "--animal.zebra", "--color.yellow", "--x.ray")
    }

    test("Long options with one arg") {
        class Args(parser: ArgParser) {
            val xyz by parser.option<MutableList<String>>("--xray", "--yellow", "--zaphod",
                argNames = oneArgName,
                help = TEST_HELP) {
                value.orElse { mutableListOf<String>() }.apply {
                    add("$optionName:${arguments.first()}")
                }
            }
        }

        // Test with value as separate arg
        Args(parserOf("--xray", "0", "--yellow", "1", "--zaphod", "2", "--zaphod", "3", "--yellow", "4")).xyz shouldBe listOf("--xray:0", "--yellow:1", "--zaphod:2", "--zaphod:3", "--yellow:4")

        // Test with = between option and value
        Args(parserOf("--xray=0", "--yellow=1", "--zaphod=2", "--zaphod=3", "--yellow=4")).xyz shouldBe listOf("--xray:0", "--yellow:1", "--zaphod:2", "--zaphod:3", "--yellow:4")

        shouldThrow<UnrecognizedOptionException> {
            Args(parserOf("--xray0", "--yellow1", "--zaphod2", "--zaphod3", "--yellow4")).xyz
        }.run {
            message shouldBe "unrecognized option '--xray0'"
        }
    }

    test("Long options with multiple args") {
        class Args(parser: ArgParser) {
            val xyz by parser.option<MutableList<String>>("--xray", "--yak", "--zaphod",
                argNames = listOf("COLOR", "SIZE", "FLAVOR"),
                help = TEST_HELP) {
                value.orElse { mutableListOf<String>() }.apply { add("$optionName:$arguments") }
            }
        }

        // Test with value as separate arg
        Args(parserOf("--xray", "red", "5", "salty")).xyz shouldBe listOf("--xray:[red, 5, salty]")

        Args(parserOf("--zaphod", "green", "42", "sweet", "--yak", "blue", "7", "bitter")).xyz shouldBe listOf(
            "--zaphod:[green, 42, sweet]", "--yak:[blue, 7, bitter]")

        // Note that something that looks like an option is consumed as an argument if it appears where an argument
        // should be. This is expected behavior.
        Args(parserOf("--zaphod", "green", "42", "--yak")).xyz shouldBe listOf(
            "--zaphod:[green, 42, --yak]")

        shouldThrow<OptionMissingRequiredArgumentException> {
            Args(parserOf("--zaphod", "green", "42", "sweet", "--yak", "blue", "7")).xyz
        }.run {
            message shouldBe "option '--yak' is missing the required argument FLAVOR"
        }

        shouldThrow<OptionMissingRequiredArgumentException> {
            Args(parserOf("--zaphod", "green")).xyz
        }.run {
            message shouldBe "option '--zaphod' is missing the required argument SIZE"
        }

        shouldThrow<OptionMissingRequiredArgumentException> {
            Args(parserOf("--xray")).xyz
        }.run {
            message shouldBe "option '--xray' is missing the required argument COLOR"
        }
    }

    test("Delegate provider") {
        fun ArgParser.putting(vararg names: String, help: String) =
            option<MutableMap<String, String>>(*names,
                argNames = listOf("KEY", "VALUE"),
                help = help) {
                value.orElse { mutableMapOf<String, String>() }.apply {
                    put(arguments.first(), arguments.last()) }
            }

        fun ArgParser.putting(help: String) =
            ArgParser.DelegateProvider { identifier ->
                putting(identifierToOptionName(identifier), help = help) }

        class Args(parser: ArgParser) {
            val dict by parser.putting(TEST_HELP)
        }

        // Test with value as separate arg
        Args(parserOf("--dict", "red", "5")).dict shouldBe mapOf("red" to "5")

        Args(parserOf(
            "--dict", "green", "42",
            "--dict", "blue", "7"
        )).dict shouldBe mapOf(
            "green" to "42",
            "blue" to "7")

        // Note that something that looks like an option is consumed as an argument if it appears where an argument
        // should be. This is expected behavior.
        Args(parserOf("--dict", "green", "--dict")).dict shouldBe mapOf("green" to "--dict")

        shouldThrow<OptionMissingRequiredArgumentException> {
            Args(parserOf("--dict", "green", "42", "--dict", "blue")).dict
        }.run {
            message shouldBe "option '--dict' is missing the required argument VALUE"
        }

        shouldThrow<OptionMissingRequiredArgumentException> {
            Args(parserOf("--dict")).dict
        }.run {
            message shouldBe "option '--dict' is missing the required argument KEY"
        }
    }

    test("Default") {
        class Args(parser: ArgParser) {
            val x by parser.storing("-x",
                help = TEST_HELP) { toInt() }.default(5)
        }

        // Test with no value
        Args(parserOf()).x shouldBe 5

        // Test with value
        Args(parserOf("-x6")).x shouldBe 6

        // Test with value as separate arg
        Args(parserOf("-x", "7")).x shouldBe 7

        // Test with multiple values
        Args(parserOf("-x9", "-x8")).x shouldBe 8
    }

    test("DDefault with providerefault") {
        class Args(parser: ArgParser) {
            val x by parser.storing(help = TEST_HELP) { toInt() }.default(5)
        }

        // Test with no value
        Args(parserOf()).x shouldBe 5

        // Test with value
        Args(parserOf("-x6")).x shouldBe 6

        // Test with value as separate arg
        Args(parserOf("-x", "7")).x shouldBe 7

        // Test with multiple values
        Args(parserOf("-x9", "-x8")).x shouldBe 8
    }

    test("Default with la") {
        class Args(parser: ArgParser) {
            var defaultCalled = false
            val x by parser.storing(help = TEST_HELP) { toInt() }.default { defaultCalled = true; 5 }
        }

        // Test default hasn't been called
        Args(parserOf("-x6")).defaultCalled shouldBe false

        // Test with no value
        val args = Args(parserOf())
        args.x shouldBe 5
        args.defaultCalled shouldBe true
    }

    test("Flag") {
        class Args(parser: ArgParser) {
            val x by parser.flagging("-x", "--ecks",
                help = TEST_HELP)
            val y by parser.flagging("-y",
                help = TEST_HELP)
            val z by parser.flagging("--zed",
                help = TEST_HELP)
        }

        Args(parserOf("-x", "-y", "--zed", "--zed", "-y")).run {
            x shouldBe true
            y shouldBe true
            z shouldBe true
        }

        Args(parserOf()).run {
            x shouldBe false
            y shouldBe false
            z shouldBe false
        }

        Args(parserOf("-y", "--ecks")).run {
            x shouldBe true
            y shouldBe true
        }

        Args(parserOf("--zed")).run {
            x shouldBe false
            y shouldBe false
            z shouldBe true
        }
    }

    test("Argument no parser") {
        class Args(parser: ArgParser) {
            val x by parser.storing("--ecks", "-x",
                help = TEST_HELP)
        }

        Args(parserOf("-x", "foo")).x shouldBe "foo"

        Args(parserOf("-x", "bar", "-x", "baz")).x shouldBe "baz"

        Args(parserOf("--ecks", "long", "-x", "short")).x shouldBe "short"

        Args(parserOf("-x", "short", "--ecks", "long")).x shouldBe "long"

        val args = Args(parserOf())
        shouldThrow<MissingValueException> {
            args.x
        }.run {
            message shouldBe "missing ECKS"
        }
    }

    test("Argument missing long") {
        class Args(parser: ArgParser) {
            val x by parser.storing("--ecks",
                help = TEST_HELP)
        }

        val args = Args(parserOf())
        shouldThrow<MissingValueException> {
            args.x
        }.run {
            message shouldBe "missing ECKS"
        }
    }

    test("Argument missing short") {
        class Args(parser: ArgParser) {
            val x by parser.storing("-x",
                help = TEST_HELP)
        }

        val args = Args(parserOf())
        shouldThrow<MissingValueException> {
            args.x
        }.run {
            message shouldBe "missing X"
        }
    }

    test("Argument withParser") {
        class Args(parser: ArgParser) {
            val x by parser.storing("-x", "--ecks",
                help = TEST_HELP) { toInt() }
        }

        val opts1 = Args(parserOf("-x", "5"))
        opts1.x shouldBe 5

        val opts2 = Args(parserOf("-x", "1", "-x", "2"))
        opts2.x shouldBe 2

        val opts3 = Args(parserOf("--ecks", "3", "-x", "4"))
        opts3.x shouldBe 4

        val opts4 = Args(parserOf("-x", "5", "--ecks", "6"))
        opts4.x shouldBe 6

        val opts6 = Args(parserOf())
        shouldThrow<MissingValueException> {
            opts6.x
        }.run {
            message shouldBe "m
Download .txt
gitextract_f8jhxc_z/

├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── COPYING
├── README.md
├── build.gradle
├── codecov.sh
├── detekt.yml
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src/
    ├── main/
    │   └── kotlin/
    │       └── com/
    │           └── xenomachina/
    │               └── argparser/
    │                   ├── ArgParser.kt
    │                   ├── Default.kt
    │                   ├── DefaultHelpFormatter.kt
    │                   ├── Exceptions.kt
    │                   ├── HelpFormatter.kt
    │                   ├── OptionDelegate.kt
    │                   ├── ParsingDelegate.kt
    │                   ├── PositionalDelegate.kt
    │                   ├── PosixNaming.kt
    │                   ├── SystemExitException.kt
    │                   └── WrappingDelegate.kt
    └── test/
        └── kotlin/
            └── com/
                └── xenomachina/
                    └── argparser/
                        └── ArgParserTest.kt
Condensed preview — 26 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (249K chars).
[
  {
    "path": ".gitignore",
    "chars": 701,
    "preview": "### Gradle ###\n.gradle\n/build/\n\n# Ignore Gradle GUI config\ngradle-app.setting\n\n# Avoid ignoring Gradle wrapper jar file "
  },
  {
    "path": ".travis.yml",
    "chars": 279,
    "preview": "language: java\n\njdk:\n    - openjdk8\n\nbefore_cache:\n    - rm -f  $HOME/.gradle/caches/modules-2/modules-2.lock\n    - rm -"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 6196,
    "preview": "# Change Log\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Chang"
  },
  {
    "path": "COPYING",
    "chars": 26530,
    "preview": "                  GNU LESSER GENERAL PUBLIC LICENSE\n                       Version 2.1, February 1999\n\n Copyright (C) 19"
  },
  {
    "path": "README.md",
    "chars": 18640,
    "preview": "# <img alt=\"Kotlin --argparser\" src=\"https://rawgit.com/xenomachina/kotlin-argparser/master/logo.svg\" style=\"transform:s"
  },
  {
    "path": "build.gradle",
    "chars": 8217,
    "preview": "// Copyright © 2016 Laurence Gonsalves\n//\n// This file is part of kotlin-argparser, a library which can be found at\n// h"
  },
  {
    "path": "codecov.sh",
    "chars": 43185,
    "preview": "#!/usr/bin/env bash\n\nset -e +o pipefail\n\nVERSION=\"8fda091\"\n\nurl=\"https://codecov.io\"\nenv=\"$CODECOV_ENV\"\nservice=\"\"\ntoken"
  },
  {
    "path": "detekt.yml",
    "chars": 10458,
    "preview": "# `git diff detekt-default detekt.yml` to see what's changed from default\n# settings\n\ntest-pattern: # Configure exclusio"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 200,
    "preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
  },
  {
    "path": "gradle.properties",
    "chars": 179,
    "preview": "detekt_version = 1.0.0.RC7\ndokka_version = 0.9.17\ngradle_bintray_version = 1.8.0\nkotlintest_version = 3.1.0\nkotlin_versi"
  },
  {
    "path": "gradlew",
    "chars": 5296,
    "preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up"
  },
  {
    "path": "gradlew.bat",
    "chars": 2260,
    "preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
  },
  {
    "path": "settings.gradle",
    "chars": 38,
    "preview": "rootProject.name = \"kotlin-argparser\"\n"
  },
  {
    "path": "src/main/kotlin/com/xenomachina/argparser/ArgParser.kt",
    "chars": 24710,
    "preview": "// Copyright © 2016 Laurence Gonsalves\n//\n// This file is part of kotlin-argparser, a library which can be found at\n// h"
  },
  {
    "path": "src/main/kotlin/com/xenomachina/argparser/Default.kt",
    "chars": 3273,
    "preview": "// Copyright © 2016 Laurence Gonsalves\n//\n// This file is part of kotlin-argparser, a library which can be found at\n// h"
  },
  {
    "path": "src/main/kotlin/com/xenomachina/argparser/DefaultHelpFormatter.kt",
    "chars": 6513,
    "preview": "// Copyright © 2016 Laurence Gonsalves\n//\n// This file is part of kotlin-argparser, a library which can be found at\n// h"
  },
  {
    "path": "src/main/kotlin/com/xenomachina/argparser/Exceptions.kt",
    "chars": 3516,
    "preview": "// Copyright © 2016 Laurence Gonsalves\n//\n// This file is part of kotlin-argparser, a library which can be found at\n// h"
  },
  {
    "path": "src/main/kotlin/com/xenomachina/argparser/HelpFormatter.kt",
    "chars": 2061,
    "preview": "// Copyright © 2016 Laurence Gonsalves\n//\n// This file is part of kotlin-argparser, a library which can be found at\n// h"
  },
  {
    "path": "src/main/kotlin/com/xenomachina/argparser/OptionDelegate.kt",
    "chars": 3193,
    "preview": "// Copyright © 2016 Laurence Gonsalves\n//\n// This file is part of kotlin-argparser, a library which can be found at\n// h"
  },
  {
    "path": "src/main/kotlin/com/xenomachina/argparser/ParsingDelegate.kt",
    "chars": 1736,
    "preview": "// Copyright © 2016 Laurence Gonsalves\n//\n// This file is part of kotlin-argparser, a library which can be found at\n// h"
  },
  {
    "path": "src/main/kotlin/com/xenomachina/argparser/PositionalDelegate.kt",
    "chars": 2221,
    "preview": "// Copyright © 2016 Laurence Gonsalves\n//\n// This file is part of kotlin-argparser, a library which can be found at\n// h"
  },
  {
    "path": "src/main/kotlin/com/xenomachina/argparser/PosixNaming.kt",
    "chars": 2148,
    "preview": "// Copyright © 2016 Laurence Gonsalves\n//\n// This file is part of kotlin-argparser, a library which can be found at\n// h"
  },
  {
    "path": "src/main/kotlin/com/xenomachina/argparser/SystemExitException.kt",
    "chars": 3356,
    "preview": "// Copyright © 2016 Laurence Gonsalves\n//\n// This file is part of kotlin-argparser, a library which can be found at\n// h"
  },
  {
    "path": "src/main/kotlin/com/xenomachina/argparser/WrappingDelegate.kt",
    "chars": 1848,
    "preview": "// Copyright © 2016 Laurence Gonsalves\n//\n// This file is part of kotlin-argparser, a library which can be found at\n// h"
  },
  {
    "path": "src/test/kotlin/com/xenomachina/argparser/ArgParserTest.kt",
    "chars": 59283,
    "preview": "// Copyright © 2016 Laurence Gonsalves\n//\n// This file is part of kotlin-argparser, a library which can be found at\n// h"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the xenomachina/kotlin-argparser GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 26 files (230.5 KB), approximately 58.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!