Eugene Pankov 8 年之前
父节点
当前提交
2377a8c522

+ 215 - 0
clink/CHANGES

@@ -0,0 +1,215 @@
+
+### Changes
+
+##### v0.4.8
+
+- Environment variable 'clink_profile' overrides Clink's profile path (#390).
+- Load a clink_inputrc file from Clink's profile directory (fixes #406).
+- Bug fixes;
+    - Redraw issues when prompts end in OSC ANSI codes (#387, #384).
+    - Fixed 'clink autorun --help' crash.
+
+##### v0.4.7
+
+- Bug fixes;
+    - Sometimes autorun tries to run clink.exe (#374).
+    - Autorun would cause cmd.exe to return an error if it wasn't interactive (#373).
+
+##### v0.4.6
+
+- HOME is only set if it is currently unset.
+- Readline can be initialised with .inputrc and _inputrc files too (#258).
+- Bug fixes;
+    - Executable completion;
+        - Paths from PATH were checked twice.
+        - Incorrect results were returned for words ending in '.' or '-'.
+        - Directories . and .. were incorrectly displayed.
+    - Fixed a crash if .bat script's stdout is redirected (#366).
+    - Occasional crash when injecting Clink (#351).
+    - Display errors;
+        - When editing near the window's right-hand edge (#347).
+        - Double display of multi-line prompts when resizing the terminal (#352).
+        - Very rare wrap artefacts when making the terminal window larger.
+    - Doskey emulation (#344).
+    - Improved 'clink autorun' help (#348).
+    - Fixed launching Clink when clink.bat is renamed (#357).
+
+##### v0.4.5
+
+- Improved 'clink autorun'. It now defaults to the Current User registry hive.
+- 'clink set' gives more details for enumeration-type settings.
+- Tab completion for p4vc.
+- New settings 'history_expand_mode' to control history expansion in quotes (#317).
+- Bug fixes;
+    - Use full width of the terminal (#337).
+    - Fixed MinGW compile error (#335).
+    - Autorun now defaults to the current user's hive (#332).
+    - Creating clink.html no longer needs Pandoc, plus it looks a bit better (#331).
+    - Added settings to control history expansion (#326).
+    - Correct fallback when 'use_altgr_substitute' is off (#325).
+    - Load history prior to saving it on shutdown (#318).
+    - Added Shift-Tab documentation and menu completion example (#190).
+    - Added shim for backwards menu completion (#190).
+    - Input handling now outputs '\e`Z' for Shift-Tab (#190).
+    - Updated Readme with current Premake info (#310).
+    - Guard against there being no buffer to read from (#304).
+    - Fixed artefacts when resizing conhost's buffer (#139).
+    - Clear remaining characters if scroll window was too small (#301)
+    - Escape % characters when expanding aliases (#280).
+    - Fixed leaking exception filters.
+    - Clearing the screen doesn't leave artefacts behind.
+
+##### v0.4.4
+
+- Completing .. behaves more like Bash (#277).
+- Escape from yes/no question when Ctrl+C is pressed.
+- Valid XP executables (#278, #289).
+- Fixed n-th argument yank not working as expected (#254).
+- Fixed prompt colours sometimes not working (#279, #286).
+- Fixed '!0' causing Clink to crash.
+- Save/restore cursor position in case Readline moves it.
+
+##### v0.4.3
+
+- Localised Y/N when auto-answering 'terminate?' prompt.
+- $* would early out if there was no arguments.
+- Disable ANSI code support if third party provides it.
+- Installer can now set %CLINK_DIR% to install location.
+- Improved output from 'clink set'.
+- Support for Windows 10 Technical Preview.
+- Ctrl-L now scrolls last line to the top of the window rather than clearing.
+- New option to control how newline characters are pasted to the line.
+- New options to control history;
+    - 'history_file_lines' - maximum lines saved to disk.
+    - 'history_ignore_space' - ignore lines prefixed with whitespace.
+    - 'history_dupe_mode' - how duplicate entries are handled.
+    - 'history_io' - load/save history from disk every line.
+- Fixed nonfunctional numpad keys.
+- Fixed missing WINCH signals if other processes resize the buffer.
+- Support Alt codes sent from Conhost.
+
+##### v0.4.2
+
+- Prompt colouring no longer requires third party ANSI code utility.
+- Override settings with environment variables prefixed with 'clink'.
+- Ctrl-PgUp goes up a directory.
+- Updated Go completions (by matrixik).
+- Arguments to clink.arg.new_parser() now initialise parser's flags/args (from vladimir-kotikov).
+- New clink.arg.add_flags() and clink.arg.add_arguments() functions.
+- Removed footer and Alt-H tip for more succinct stdout output.
+- Bug fixes;
+    - Windows XP works again.
+    - Fixed race condition in lua_execute().
+
+##### v0.4.1
+
+- Bug fixes;
+    - Various Unicode fixes causing corrupt environment variables.
+    - Fixed thread resume/suspend causing rare system-wide deadlock.
+    - Fixed incorrect translation of suffixed slash when completing flags.
+- Add --nolog argument to disable file logging. Fix #187 Fix #154
+- Added missing escape sequences from doskey emulation.
+- Reinstated unix-kill-line key binding.
+- Mapped PgUp/Down to search history using line typed so far.
+- Added documentation covering escape codes for special keys.
+- Added support for Windows' AltGr substitute Ctrl-Alt.
+- Support for Readline's 'menu' style completion (see docs).
+
+##### v0.4
+
+- New features;
+    - Better 'clink.arg' API. Easier, more intuitive, and more powerful.
+    - Whitespace prefix skips exec matching.
+    - Added a 'set' verb to easily change settings from the command line.
+    - Basic support for a shells other than cmd.exe.
+    - Completion for Go (contributed by Dobroslaw Zybort).
+    - Setting 'exec_match_style' to -1 disables it entirely.
+    - Make history persistence optional.
+    - Alias/doskey completion.
+    - Very basic support for Powershell.
+    - View cmd.exe's autorun entry without needing admin rights.
+- New key bindings;
+    - Ctrl-Alt-C : Copy command line to the clipboard.
+    - Ctrl-Alt-E : Expand environment variable under cursor.
+    - Ctrl-Alt-U : 'up directory' (formerly Shift-Up).
+    - Ctrl-U : Adds '..\' to the command line.
+    - Alt-H : Shows active keymap's key bindings.
+- New Lua functions;
+    - clink.execute().
+    - clink.get_host_process().
+    - clink.match_files().
+    - clink.match_words().
+    - clink.get_console_aliases().
+- Lots of bug fixes, including;
+    - Better command extraction.
+    - Fixed cmd.exe command paging and Ctrl-C/Ctrl-Break handling.
+    - Multiple locale fixes.
+    - Use localised text for 'Terminate batch job?' prompt.
+
+##### v0.3
+
+- Automatic answering of cmd.exe's 'Terminate batch script?' prompt.
+- Coloured prompts (requires ANSICON or ConEmu).
+- Added Shift-Up keyboard shortcut to automatically execute 'cd ..'
+- Mapped Ctrl-Z to undo, Microsoft style.
+- Improved integration of Readline;
+    - New input handling code (Ctrl-Alt combos now work).
+    - An implementation of the Termcap library.
+    - Fully functional Vi-mode support.
+    - Support for resizable consoles.
+    - Line wrapping now works correctly (issue 50).
+- Adjustable executable match style (issue 65).
+- Improved environment variable completion.
+- Added settings file to customise Clink.
+- New Lua features and functions;
+    - Matches can now be filtered in Lua before they are display.
+    - clink.quote_split().
+    - clink.arg.node_merge().
+    - clink.get_screen_info() (issue 71).
+    - clink.split() (for splitting strings).
+    - clink.chdir().
+    - clink.get_cwd().
+    - Functions to query Clink's settings.
+- New command line options;
+    - '--profile <dir>' to override default profile directory.
+    - '--nohostcheck' disables verification that host is cmd.exe.
+    - '--pid' specifies the process to inject into.
+- Update Mercurial completion (issue 73).
+- Start menu shortcut starts in USERPROFILE, like cmd.exe
+- Zip distribution is now portable.
+
+##### v0.2.1
+
+- The .history file now merges multiple sessions together.
+- Fixed missing y/n, pause, and other prompts.
+- Fixed segfault in loader executable.
+- Better ConEmu compatibility.
+
+##### v0.2
+
+- Basic argument completion for 'git', 'hg', 'svn', and 'p4'.
+- Traditional Bash clear screen ('Ctrl-L') and exit shortcuts ('Ctrl-D').
+- Scrollable command window using 'PgUp'/'PgDown' keys.
+- Doskey support.
+- Automatic quoting of file names with spaces.
+- Scriptable custom prompts.
+- New argument framework to ease writing context-sensitive match generators.
+- History and log file is now saved per-user rather than globally.
+- Improved Clink's command line interface ('clink --help').
+- More reliable handling of cmd.exe's autorun entry.
+- General improvements to executable and directory-command completion.
+- Symbolic link support.
+- Documentation.
+- Windows 8 support.
+- Improved hooking so Clink can be shared with other thirdparty utilities that
+  also hook cmd.exe (ConEmu, ANSICon, etc.).
+
+##### v0.1.1
+
+- Fixed AltGr+<key> on international keyboards.
+- Fixed broken completion when directories have a '-' in their name (Mark Hammond)
+- The check for single match scenarios now correctly handles case-insensitivity.
+
+##### v0.1
+
+- Initial release.

+ 675 - 0
clink/LICENSE

@@ -0,0 +1,675 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+

+ 60 - 0
clink/clink.bat

@@ -0,0 +1,60 @@
+:: Copyright (c) 2012 Martin Ridgers
+::
+:: Permission is hereby granted, free of charge, to any person obtaining a copy
+:: of this software and associated documentation files (the "Software"), to deal
+:: in the Software without restriction, including without limitation the rights
+:: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+:: copies of the Software, and to permit persons to whom the Software is
+:: furnished to do so, subject to the following conditions:
+::
+:: The above copyright notice and this permission notice shall be included in
+:: all copies or substantial portions of the Software.
+::
+:: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+:: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+:: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+:: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+:: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+:: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+:: SOFTWARE.
+
+@echo off
+
+:: Mimic cmd.exe's behaviour when starting from the start menu.
+if /i "%1"=="startmenu" (
+    cd /d "%userprofile%"
+    shift /1
+)
+
+:: Check for the --profile option.
+if /i "%1"=="--profile" (
+    set clink_profile_arg=--profile "%~2"
+    shift /1
+    shift /1
+)
+
+:: If the .bat is run without any arguments, then start a cmd.exe instance.
+if "%1"=="" (
+    call :launch
+    goto :end
+)
+
+:: Pass through to appropriate loader.
+if /i "%processor_architecture%"=="x86" (
+        "%~dp0\clink_x86.exe" %*
+) else if /i "%processor_architecture%"=="amd64" (
+    if defined processor_architew6432 (
+        "%~dp0\clink_x86.exe" %*
+    ) else (
+        "%~dp0\clink_x64.exe" %*
+    )
+)
+
+:end
+set clink_profile_arg=
+goto :eof
+
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+:launch
+start "Clink" cmd.exe /s /k ""%~dpnx0" inject %clink_profile_arg%"
+exit /b 0

文件差异内容过多而无法显示
+ 92 - 0
clink/clink.html


+ 3420 - 0
clink/clink.lua

@@ -0,0 +1,3420 @@
+--
+-- Copyright (c) 2012 Martin Ridgers
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+-- SOFTWARE.
+--
+
+--------------------------------------------------------------------------------
+clink.matches = {}
+clink.generators = {}
+
+clink.prompt = {}
+clink.prompt.filters = {}
+
+--------------------------------------------------------------------------------
+function clink.compute_lcd(text, list)
+    local list_n = #list
+    if list_n < 2 then
+        return
+    end
+
+    -- Find min and max limits
+    local max = 100000
+    for i = 1, #list, 1 do
+        local j = #(list[i])
+        if max > j then
+            max = j
+        end
+    end
+
+    -- For each character in the search range...
+    local mid = #text
+    local lcd = ""
+    for i = 1, max, 1 do
+        local same = true
+        local l = list[1]:sub(i, i)
+        local m = l:lower()
+
+        -- Compare character at the index with each other character in the
+        -- other matches.
+        for j = 2, list_n, 1 do
+            local n = list[j]:sub(i, i):lower()
+            if m ~= n then
+                same = false
+                break
+            end
+        end
+
+        -- If all characters match then use first match's character.
+        if same then
+            lcd = lcd..l 
+        else
+            -- Otherwise use what the user's typed or if we're past that then
+            -- bail out.
+            if i <= mid then
+                lcd = lcd..text:sub(i, i)
+            else
+                break
+            end
+        end
+    end
+
+    return lcd
+end
+
+--------------------------------------------------------------------------------
+function clink.is_single_match(matches)
+    if #matches <= 1 then
+        return true
+    end
+
+    local first = matches[1]:lower()
+    for i = 2, #matches, 1 do
+        if first ~= matches[i]:lower() then
+            return false
+        end
+    end
+
+    return true
+end
+
+--------------------------------------------------------------------------------
+function clink.is_point_in_quote(str, i)
+    if i > #str then
+        i = #str
+    end
+
+    local c = 1
+    local q = string.byte("\"")
+    for j = 1, i do
+        if string.byte(str, j) == q then
+            c = c * -1
+        end
+    end
+
+    if c < 0 then
+        return true
+    end
+
+    return false
+end
+
+--------------------------------------------------------------------------------
+function clink.adjust_for_separator(buffer, point, first, last)
+    local seps = nil
+    if clink.get_host_process() == "cmd.exe" then
+        seps = "|&"
+    end
+
+    if seps then
+        -- Find any valid command separators and if found, manipulate the
+        -- completion state a little bit.
+        local leading = buffer:sub(1, first - 1)
+
+        -- regex is: <sep> <not_seps> <eol>
+        local regex = "["..seps.."]([^"..seps.."]*)$"
+        local sep_found, _, post_sep = leading:find(regex)
+
+        if sep_found and not clink.is_point_in_quote(leading, sep_found) then
+            local delta = #leading - #post_sep
+            buffer = buffer:sub(delta + 1)
+            first = first - delta
+            last = last - delta
+            point = point - delta
+
+            if first < 1 then
+                first = 1
+            end
+        end
+    end
+
+    return buffer, point, first, last
+end
+
+--------------------------------------------------------------------------------
+function clink.generate_matches(text, first, last)
+    local line_buffer
+    local point
+
+    line_buffer, point, first, last = clink.adjust_for_separator(
+        rl_state.line_buffer,
+        rl_state.point,
+        first,
+        last
+    )
+
+    rl_state.line_buffer = line_buffer
+    rl_state.point = point
+
+    clink.matches = {}
+    clink.match_display_filter = nil
+
+    for _, generator in ipairs(clink.generators) do
+        if generator.f(text, first, last) == true then
+            if #clink.matches > 1 then
+                -- Catch instances where there's many entries of a single match
+                if clink.is_single_match(clink.matches) then
+                    clink.matches = { clink.matches[1] }
+                    return true;
+                end
+
+                -- First entry in the match list should be the user's input,
+                -- modified here to be the lowest common denominator.
+                local lcd = clink.compute_lcd(text, clink.matches)
+                table.insert(clink.matches, 1, lcd)
+            end
+
+            return true
+        end
+    end
+
+    return false
+end
+
+--------------------------------------------------------------------------------
+function clink.add_match(match)
+    if type(match) == "table" then
+        for _, i in ipairs(match) do
+            table.insert(clink.matches, i)
+        end
+
+        return
+    end
+
+    table.insert(clink.matches, match)
+end
+
+--------------------------------------------------------------------------------
+function clink.register_match_generator(func, priority)
+    if priority == nil then
+        priority = 999
+    end
+
+    table.insert(clink.generators, {f=func, p=priority})
+    table.sort(clink.generators, function(a, b) return a["p"] < b["p"] end)
+end
+
+--------------------------------------------------------------------------------
+function clink.is_match(needle, candidate)
+    if needle == nil then
+        error("Nil needle value when calling clink.is_match()", 2)
+    end
+
+    if clink.lower(candidate:sub(1, #needle)) == clink.lower(needle) then
+        return true
+    end
+    return false
+end
+
+--------------------------------------------------------------------------------
+function clink.match_count()
+    return #clink.matches
+end
+
+--------------------------------------------------------------------------------
+function clink.set_match(i, value)
+    clink.matches[i] = value
+end
+
+--------------------------------------------------------------------------------
+function clink.get_match(i)
+    return clink.matches[i]
+end
+
+--------------------------------------------------------------------------------
+function clink.match_words(text, words)
+    local count = clink.match_count()
+
+    for _, i in ipairs(words) do
+        if clink.is_match(text, i) then
+            clink.add_match(i)
+        end
+    end
+
+    return clink.match_count() - count
+end
+
+--------------------------------------------------------------------------------
+function clink.match_files(pattern, full_path, find_func)
+    -- Fill out default values
+    if type(find_func) ~= "function" then
+        find_func = clink.find_files
+    end
+
+    if full_path == nil then
+        full_path = true
+    end
+
+    if pattern == nil then
+        pattern = "*"
+    end
+
+    -- Glob files.
+    pattern = pattern:gsub("/", "\\")
+    local glob = find_func(pattern, true)
+
+    -- Get glob's base.
+    local base = ""
+    local i = pattern:find("[\\:][^\\:]*$")
+    if i and full_path then
+        base = pattern:sub(1, i)
+    end
+
+    -- Match them.
+    local count = clink.match_count()
+
+    for _, i in ipairs(glob) do
+        local full = base..i
+        clink.add_match(full)
+    end
+
+    return clink.match_count() - count
+end
+
+--------------------------------------------------------------------------------
+function clink.split(str, sep)
+    local i = 1
+    local ret = {}
+    for _, j in function() return str:find(sep, i, true) end do
+        table.insert(ret, str:sub(i, j - 1))
+        i = j + 1
+    end
+    table.insert(ret, str:sub(i, j))
+
+    return ret
+end
+
+--------------------------------------------------------------------------------
+function clink.quote_split(str, ql, qr)
+    if not qr then
+        qr = ql
+    end
+
+    -- First parse in "pre[ql]quote_string[qr]" chunks
+    local insert = table.insert
+    local i = 1
+    local needle = "%b"..ql..qr
+    local parts = {}
+    for l, r, quote in function() return str:find(needle, i) end do
+        -- "pre"
+        if l > 1 then
+            insert(parts, str:sub(i, l - 1))
+        end
+
+        -- "quote_string"
+        insert(parts, str:sub(l, r))
+        i = r + 1
+    end
+
+    -- Second parse what remains as "pre[ql]being_quoted"
+    local l = str:find(ql, i, true)
+    if l then
+        -- "pre"
+        if l > 1 then
+            insert(parts, str:sub(i, l - 1))
+        end
+
+        -- "being_quoted"
+        insert(parts, str:sub(l))
+    elseif i <= #str then
+        -- Finally add whatever remains...
+        insert(parts, str:sub(i))
+    end
+
+    return parts
+end
+
+--------------------------------------------------------------------------------
+function clink.prompt.register_filter(filter, priority)
+    if priority == nil then
+        priority = 999
+    end
+
+    table.insert(clink.prompt.filters, {f=filter, p=priority})
+    table.sort(clink.prompt.filters, function(a, b) return a["p"] < b["p"] end)
+end
+
+--------------------------------------------------------------------------------
+function clink.filter_prompt(prompt)
+    local function add_ansi_codes(p)
+        local c = tonumber(clink.get_setting_int("prompt_colour"))
+        if c < 0 then
+            return p
+        end
+
+        c = c % 16
+
+        --[[
+            <4              >=4             %2
+            0 0  0 Black    4 1 -3 Blue     0
+            1 4  3 Red      5 5  0 Magenta  1
+            2 2  0 Green    6 3 -3 Cyan     0
+            3 6  3 Yellow   7 7  0 Gray     1
+        --]]
+
+        -- Convert from cmd.exe colour indices to ANSI ones.
+        local colour_id = c % 8
+        if (colour_id % 2) == 1 then
+            if colour_id < 4 then
+                c = c + 3
+            end
+        elseif colour_id >= 4 then
+            c = c - 3
+        end
+
+        -- Clamp
+        if c > 15 then
+            c = 15
+        end
+
+        -- Build ANSI code
+        local code = "\x1b[0;"
+        if c > 7 then
+            c = c - 8
+            code = code.."1;"
+        end
+        code = code..(c + 30).."m"
+
+        return code..p.."\x1b[0m"
+    end
+
+    clink.prompt.value = prompt
+
+    for _, filter in ipairs(clink.prompt.filters) do
+        if filter.f() == true then
+            return add_ansi_codes(clink.prompt.value)
+        end
+    end
+
+    return add_ansi_codes(clink.prompt.value)
+end
+
+-- vim: expandtab
+--
+-- Copyright (c) 2012 Martin Ridgers
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+-- SOFTWARE.
+--
+
+--------------------------------------------------------------------------------
+clink.arg = {}
+
+--------------------------------------------------------------------------------
+local parsers               = {}
+local is_parser
+local is_sub_parser
+local new_sub_parser
+local parser_go_impl
+local merge_parsers
+
+local parser_meta_table     = {}
+local sub_parser_meta_table = {}
+
+--------------------------------------------------------------------------------
+function parser_meta_table.__concat(lhs, rhs)
+    if not is_parser(rhs) then
+        error("Right-handside must be parser.", 2)
+    end
+
+    local t = type(lhs)
+    if t == "table" then
+        local ret = {}
+        for _, i in ipairs(lhs) do
+            table.insert(ret, i .. rhs)
+        end
+
+        return ret
+    elseif t ~= "string" then
+        error("Left-handside must be a string or a table.", 2)
+    end
+
+    return new_sub_parser(lhs, rhs)
+end
+
+--------------------------------------------------------------------------------
+local function unfold_table(source, target)
+    for _, i in ipairs(source) do
+        if type(i) == "table" and getmetatable(i) == nil then
+            unfold_table(i, target)
+        else
+            table.insert(target, i)
+        end
+    end
+end
+
+--------------------------------------------------------------------------------
+local function parser_is_flag(parser, part)
+    if part == nil then
+        return false
+    end
+
+    local prefix = part:sub(1, 1)
+    return prefix == "-" or prefix == "/"
+end
+
+--------------------------------------------------------------------------------
+local function parser_add_arguments(parser, ...)
+    for _, i in ipairs({...}) do
+        -- Check all arguments are tables.
+        if type(i) ~= "table" then
+            error("All arguments to add_arguments() must be tables.", 2)
+        end
+
+        -- Only parsers are allowed to be specified without being wrapped in a
+        -- containing table.
+        if getmetatable(i) ~= nil then
+            if is_parser(i) then
+                table.insert(parser.arguments, i)
+            else
+                error("Tables can't have meta-tables.", 2)
+            end
+        else
+            -- Expand out nested tables and insert into object's arguments table.
+            local arguments = {}
+            unfold_table(i, arguments)
+            table.insert(parser.arguments, arguments)
+        end
+    end
+
+    return parser
+end
+
+--------------------------------------------------------------------------------
+local function parser_set_arguments(parser, ...)
+    parser.arguments = {}
+    return parser:add_arguments(...)
+end
+
+--------------------------------------------------------------------------------
+local function parser_add_flags(parser, ...)
+    local flags = {}
+    unfold_table({...}, flags)
+
+    -- Validate the specified flags.
+    for _, i in ipairs(flags) do
+        if is_sub_parser(i) then
+            i = i.key
+        end
+
+        -- Check all flags are strings.
+        if type(i) ~= "string" then
+            error("All parser flags must be strings. Found "..type(i), 2)
+        end
+
+        -- Check all flags start with a - or a /
+        if not parser:is_flag(i) then
+            error("Flags must begin with a '-' or a '/'", 2)
+        end
+    end
+
+    -- Append flags to parser's existing table of flags.
+    for _, i in ipairs(flags) do
+        table.insert(parser.flags, i)
+    end
+
+    return parser
+end
+
+--------------------------------------------------------------------------------
+local function parser_set_flags(parser, ...)
+    parser.flags = {}
+    return parser:add_flags(...)
+end
+
+--------------------------------------------------------------------------------
+local function parser_flatten_argument(parser, index, func_thunk)
+    -- Sanity check the 'index' param to make sure it's valid.
+    if type(index) == "number" then
+        if index <= 0 or index > #parser.arguments then
+            return parser.use_file_matching
+        end
+    end
+
+    -- index == nil is a special case that returns the parser's flags
+    local opts = {}
+    local arg_opts
+    if index == nil then
+        arg_opts = parser.flags
+    else
+        arg_opts = parser.arguments[index]
+    end
+
+    -- Convert each argument option into a string and collect them in a table.
+    for _, i in ipairs(arg_opts) do
+        if is_sub_parser(i) then
+            table.insert(opts, i.key)
+        else
+            local t = type(i)
+            if t == "function" then
+                local results = func_thunk(i)
+                local t = type(results)
+                if not results then
+                    return parser.use_file_matching
+                elseif t == "boolean" then
+                    return (results and parser.use_file_matching)
+                elseif t == "table" then
+                    for _, j in ipairs(results) do
+                        table.insert(opts, j)
+                    end
+                end
+            elseif t == "string" or t == "number" then
+                table.insert(opts, tostring(i))
+            end
+        end
+    end
+
+    return opts
+end
+
+--------------------------------------------------------------------------------
+local function parser_go_args(parser, state)
+    local exhausted_args = false
+    local exhausted_parts = false
+
+    local part = state.parts[state.part_index]
+    local arg_index = state.arg_index
+    local arg_opts = parser.arguments[arg_index]
+    local arg_count = #parser.arguments
+
+    -- Is the next argument a parser? Parse control directly on to it.
+    if is_parser(arg_opts) then
+        state.arg_index = 1
+        return parser_go_impl(arg_opts, state)
+    end
+
+    -- Advance parts state.
+    state.part_index = state.part_index + 1
+    if state.part_index > #state.parts then
+        exhausted_parts = true
+    end
+
+    -- Advance argument state.
+    state.arg_index = arg_index + 1
+    if arg_index > arg_count then
+        exhausted_args = true
+    end
+
+    -- We've exhausted all available arguments. We either loop or we're done.
+    if parser.loop_point > 0 and state.arg_index > arg_count then
+        state.arg_index = parser.loop_point
+        if state.arg_index > arg_count then
+            state.arg_index = arg_count
+        end
+    end
+
+    -- Is there some state to process?
+    if not exhausted_parts and not exhausted_args then
+        local exact = false
+        for _, arg_opt in ipairs(arg_opts) do
+            -- Is the argument a key to a sub-parser? If so then hand control
+            -- off to it.
+            if is_sub_parser(arg_opt) then
+                if arg_opt.key == part then
+                    state.arg_index = 1
+                    return parser_go_impl(arg_opt.parser, state)
+                end
+            end
+
+            -- Check so see if the part has an exact match in the argument. Note
+            -- that only string-type options are considered.
+            if type(arg_opt) == "string" then
+                exact = exact or arg_opt == part
+            else
+                exact = true
+            end
+        end
+
+        -- If the parser's required to be precise then check here.
+        if parser.precise and not exact then
+            exhausted_args = true
+        else
+            return nil
+        end
+    end
+
+    -- If we've no more arguments to traverse but there's still parts remaining
+    -- then we start skipping arguments but keep going so that flags still get
+    -- parsed (as flags have no position).
+    if exhausted_args then
+        state.part_index = state.part_index - 1
+
+        if not exhausted_parts then
+            if state.depth <= 1 then
+                state.skip_args = true
+                return
+            end
+
+            return parser.use_file_matching
+        end
+    end
+
+    -- Now we've an index into the parser's arguments that matches the line
+    -- state. Flatten it.
+    local func_thunk = function(func)
+        return func(part)
+    end
+
+    return parser:flatten_argument(arg_index, func_thunk)
+end
+
+--------------------------------------------------------------------------------
+local function parser_go_flags(parser, state)
+    local part = state.parts[state.part_index]
+
+    -- Advance parts state.
+    state.part_index = state.part_index + 1
+    if state.part_index > #state.parts then
+        return parser:flatten_argument()
+    end
+
+    for _, arg_opt in ipairs(parser.flags) do
+        if is_sub_parser(arg_opt) then
+            if arg_opt.key == part then
+                local arg_index_cache = state.arg_index
+                local skip_args_cache = state.skip_args
+
+                state.arg_index = 1
+                state.skip_args = false
+                state.depth = state.depth + 1
+
+                local ret = parser_go_impl(arg_opt.parser, state)
+                if type(ret) == "table" then
+                    return ret
+                end
+
+                state.depth = state.depth - 1
+                state.skip_args = skip_args_cache
+                state.arg_index = arg_index_cache
+            end
+        end
+    end
+end
+
+--------------------------------------------------------------------------------
+function parser_go_impl(parser, state)
+    local has_flags = #parser.flags > 0
+
+    while state.part_index <= #state.parts do
+        local part = state.parts[state.part_index]
+        local dispatch_func
+
+        if has_flags and parser:is_flag(part) then
+            dispatch_func = parser_go_flags
+        elseif not state.skip_args then
+            dispatch_func = parser_go_args
+        end
+
+        if dispatch_func ~= nil then
+            local ret = dispatch_func(parser, state)
+            if ret ~= nil then
+                return ret
+            end
+        else
+            state.part_index = state.part_index + 1
+        end
+    end
+
+    return parser.use_file_matching
+end
+
+--------------------------------------------------------------------------------
+local function parser_go(parser, parts)
+    -- Validate 'parts'.
+    if type(parts) ~= "table" then
+        error("'Parts' param must be a table of strings ("..type(parts)..").", 2)
+    else
+        if #parts == 0 then
+            part = { "" }
+        end
+
+        for i, j in ipairs(parts) do
+            local t = type(parts[i])
+            if t ~= "string" then
+                error("'Parts' table can only contain strings; "..j.."="..t, 2)
+            end
+        end
+    end
+
+    local state = {
+        arg_index = 1,
+        part_index = 1,
+        parts = parts,
+        skip_args = false,
+        depth = 1,
+    }
+
+    return parser_go_impl(parser, state)
+end
+
+--------------------------------------------------------------------------------
+local function parser_dump(parser, depth)
+    if depth == nil then
+        depth = 0
+    end
+
+    function prt(depth, index, text)
+        local indent = string.sub("                                 ", 1, depth)
+        text = tostring(text)
+        print(indent..depth.."."..index.." - "..text)
+    end
+
+    -- Print arguments
+    local i = 0
+    for _, arg_opts in ipairs(parser.arguments) do
+        for _, arg_opt in ipairs(arg_opts) do
+            if is_sub_parser(arg_opt) then
+                prt(depth, i, arg_opt.key)
+                arg_opt.parser:dump(depth + 1)
+            else
+                prt(depth, i, arg_opt)
+            end
+        end
+
+        i = i + 1
+    end
+
+    -- Print flags
+    for _, flag in ipairs(parser.flags) do
+        prt(depth, "F", flag)
+    end
+end
+
+--------------------------------------------------------------------------------
+function parser_be_precise(parser)
+    parser.precise = true
+    return parser
+end
+
+--------------------------------------------------------------------------------
+function is_parser(p)
+    return type(p) == "table" and getmetatable(p) == parser_meta_table
+end
+
+--------------------------------------------------------------------------------
+function is_sub_parser(sp)
+    return type(sp) == "table" and getmetatable(sp) == sub_parser_meta_table
+end
+
+--------------------------------------------------------------------------------
+local function get_sub_parser(argument, str)
+    for _, arg in ipairs(argument) do
+        if is_sub_parser(arg) then
+            if arg.key == str then
+                return arg.parser
+            end
+        end
+    end
+end
+
+--------------------------------------------------------------------------------
+function new_sub_parser(key, parser)
+    local sub_parser = {}
+    sub_parser.key = key
+    sub_parser.parser = parser
+
+    setmetatable(sub_parser, sub_parser_meta_table)
+    return sub_parser
+end
+
+--------------------------------------------------------------------------------
+local function parser_disable_file_matching(parser)
+    parser.use_file_matching = false
+    return parser
+end
+
+--------------------------------------------------------------------------------
+local function parser_loop(parser, loop_point)
+    if loop_point == nil or type(loop_point) ~= "number" or loop_point < 1 then
+        loop_point = 1
+    end
+
+    parser.loop_point = loop_point
+    return parser
+end
+
+--------------------------------------------------------------------------------
+local function parser_initialise(parser, ...)
+    for _, word in ipairs({...}) do
+        local t = type(word)
+        if t == "string" then
+            parser:add_flags(word)
+        elseif t == "table" then
+            if is_sub_parser(word) and parser_is_flag(nil, word.key) then
+                parser:add_flags(word)
+            else
+                parser:add_arguments(word)
+            end
+        else
+            error("Additional arguments to new_parser() must be tables or strings", 2)
+        end
+    end
+end
+
+--------------------------------------------------------------------------------
+function clink.arg.new_parser(...)
+    local parser = {}
+
+    -- Methods
+    parser.set_flags = parser_set_flags
+    parser.add_flags = parser_add_flags
+    parser.set_arguments = parser_set_arguments
+    parser.add_arguments = parser_add_arguments
+    parser.dump = parser_dump
+    parser.go = parser_go
+    parser.flatten_argument = parser_flatten_argument
+    parser.be_precise = parser_be_precise
+    parser.disable_file_matching = parser_disable_file_matching
+    parser.loop = parser_loop
+    parser.is_flag = parser_is_flag
+
+    -- Members.
+    parser.flags = {}
+    parser.arguments = {}
+    parser.precise = false
+    parser.use_file_matching = true
+    parser.loop_point = 0
+
+    setmetatable(parser, parser_meta_table)
+
+    -- If any arguments are provided treat them as parser's arguments or flags
+    if ... then
+        success, msg = pcall(parser_initialise, parser, ...)
+        if not success then
+            error(msg, 2)
+        end
+    end
+
+    return parser
+end
+
+--------------------------------------------------------------------------------
+function merge_parsers(lhs, rhs)
+    -- Merging parsers is not a trivial matter and this implementation is far
+    -- from correct. It is however sufficient for the majority of cases.
+
+    -- Merge flags.
+    for _, rflag in ipairs(rhs.flags) do
+        table.insert(lhs.flags, rflag)
+    end
+
+    -- Remove (and save value of) the first argument in RHS.
+    local rhs_arg_1 = table.remove(rhs.arguments, 1)
+    if rhs_arg_1 == nil then
+        return
+    end
+
+    -- Get reference to the LHS's first argument table (creating it if needed).
+    local lhs_arg_1 = lhs.arguments[1]
+    if lhs_arg_1 == nil then
+        lhs_arg_1 = {}
+        table.insert(lhs.arguments, lhs_arg_1)
+    end
+
+    -- Link RHS to LHS through sub-parsers.
+    for _, rarg in ipairs(rhs_arg_1) do
+        local child
+
+        -- Split sub parser
+        if is_sub_parser(rarg) then
+            child = rarg.parser     
+            rarg = rarg.key
+        else
+            child = rhs
+        end
+
+        -- If LHS's first argument has rarg in it which links to a sub-parser
+        -- then we need to recursively merge them.
+        local lhs_sub_parser = get_sub_parser(lhs_arg_1, rarg)
+        if lhs_sub_parser then
+            merge_parsers(lhs_sub_parser, child)
+        else
+            local to_add = rarg
+            if type(rarg) ~= "function" then
+                to_add = rarg .. child
+            end
+
+            table.insert(lhs_arg_1, to_add)
+        end
+    end
+end
+
+--------------------------------------------------------------------------------
+function clink.arg.register_parser(cmd, parser)
+    if not is_parser(parser) then
+        local p = clink.arg.new_parser()
+        p:set_arguments({ parser })
+        parser = p
+    end
+
+    cmd = cmd:lower()
+    local prev = parsers[cmd]
+    if prev ~= nil then
+        merge_parsers(prev, parser)
+    else
+        parsers[cmd] = parser
+    end
+end
+
+--------------------------------------------------------------------------------
+local function argument_match_generator(text, first, last)
+    local leading = rl_state.line_buffer:sub(1, first - 1):lower()
+
+    -- Extract the command.
+    local cmd_l, cmd_r
+    if leading:find("^%s*\"") then
+        -- Command appears to be surround by quotes.
+        cmd_l, cmd_r = leading:find("%b\"\"")
+        if cmd_l and cmd_r then
+            cmd_l = cmd_l + 1
+            cmd_r = cmd_r - 1
+        end
+    else
+        -- No quotes so the first, longest, non-whitespace word is extracted.
+        cmd_l, cmd_r = leading:find("[^%s]+")
+    end
+
+    if not cmd_l or not cmd_r then
+        return false
+    end
+
+    local regex = "[\\/:]*([^\\/:.]+)(%.*[%l]*)%s*$"
+    local _, _, cmd, ext = leading:sub(cmd_l, cmd_r):lower():find(regex)
+
+    -- Check to make sure the extension extracted is in pathext.
+    if ext and ext ~= "" then
+        if not clink.get_env("pathext"):lower():match(ext.."[;$]", 1, true) then
+            return false
+        end
+    end
+    
+    -- Find a registered parser.
+    local parser = parsers[cmd]
+    if parser == nil then
+        return false
+    end
+
+    -- Split the command line into parts.
+    local str = rl_state.line_buffer:sub(cmd_r + 2, last)
+    local parts = {}
+    for _, sub_str in ipairs(clink.quote_split(str, "\"")) do
+        -- Quoted strings still have their quotes. Look for those type of
+        -- strings, strip the quotes and add it completely.
+        if sub_str:sub(1, 1) == "\"" then
+            local l, r = sub_str:find("\"[^\"]+")
+            if l then
+                local part = sub_str:sub(l + 1, r)
+                table.insert(parts, part)
+            end
+        else
+            -- Extract non-whitespace parts.
+            for _, r, part in function () return sub_str:find("^%s*([^%s]+)") end do
+                table.insert(parts, part)
+                sub_str = sub_str:sub(r + 1)
+            end
+        end
+    end
+
+    -- If 'text' is empty then add it as a part as it would have been skipped
+    -- by the split loop above.
+    if text == "" then
+        table.insert(parts, text)
+    end
+
+    -- Extend rl_state with match generation state; text, first, and last.
+    rl_state.text = text
+    rl_state.first = first
+    rl_state.last = last
+
+    -- Call the parser.
+    local needle = parts[#parts]
+    local ret = parser:go(parts)
+    if type(ret) ~= "table" then
+        return not ret
+    end
+
+    -- Iterate through the matches the parser returned and collect matches.
+    for _, match in ipairs(ret) do
+        if clink.is_match(needle, match) then
+            clink.add_match(match)
+        end
+    end
+
+    return true
+end
+
+--------------------------------------------------------------------------------
+clink.register_match_generator(argument_match_generator, 25)
+
+-- vim: expandtab
+
+--{{{  history
+
+--15/03/06 DCN Created based on RemDebug
+--28/04/06 DCN Update for Lua 5.1
+--01/06/06 DCN Fix command argument parsing
+--             Add step/over N facility
+--             Add trace lines facility
+--05/06/06 DCN Add trace call/return facility
+--06/06/06 DCN Make it behave when stepping through the creation of a coroutine
+--06/06/06 DCN Integrate the simple debugger into the main one
+--07/06/06 DCN Provide facility to step into coroutines
+--13/06/06 DCN Fix bug that caused the function environment to get corrupted with the global one
+--14/06/06 DCN Allow 'sloppy' file names when setting breakpoints
+--04/08/06 DCN Allow for no space after command name
+--11/08/06 DCN Use io.write not print
+--30/08/06 DCN Allow access to array elements in 'dump'
+--10/10/06 DCN Default to breakfile for all commands that require a filename and give '-'
+--06/12/06 DCN Allow for punctuation characters in DUMP variable names
+--03/01/07 DCN Add pause on/off facility
+--19/06/07 DCN Allow for duff commands being typed in the debugger (thanks to [email protected])
+--             Allow for case sensitive file systems               (thanks to [email protected])
+--04/08/09 DCN Add optional line count param to pause
+--05/08/09 DCN Reset the debug hook in Pause() even if we think we're started
+--30/09/09 DCN Re-jig to not use co-routines (makes debugging co-routines awkward)
+--01/10/09 DCN Add ability to break on reaching any line in a file
+--24/07/13 TWW Added code for emulating setfenv/getfenv in Lua 5.2 as per
+--             http://lua-users.org/lists/lua-l/2010-06/msg00313.html
+--25/07/13 TWW Copied Alex Parrill's fix for errors when tracing back across a C frame
+--             (https://github.com/ColonelThirtyTwo/clidebugger, 26/01/12)
+--25/07/13 DCN Allow for windows and unix file name conventions in has_breakpoint
+--26/07/13 DCN Allow for \ being interpreted as an escape inside a [] pattern in 5.2
+
+--}}}
+--{{{  description
+
+--A simple command line debug system for Lua written by Dave Nichols of
+--Match-IT Limited. Its public domain software. Do with it as you wish.
+
+--This debugger was inspired by:
+-- RemDebug 1.0 Beta
+-- Copyright Kepler Project 2005 (http://www.keplerproject.org/remdebug)
+
+--Usage:
+--  require('debugger')        --load the debug library
+--  pause(message)             --start/resume a debug session
+
+--An assert() failure will also invoke the debugger.
+
+--}}}
+
+local IsWindows = string.find(string.lower(os.getenv('OS') or ''),'^windows')
+
+local coro_debugger
+local events = { BREAK = 1, WATCH = 2, STEP = 3, SET = 4 }
+local breakpoints = {}
+local watches = {}
+local step_into   = false
+local step_over   = false
+local step_lines  = 0
+local step_level  = {main=0}
+local stack_level = {main=0}
+local trace_level = {main=0}
+local trace_calls = false
+local trace_returns = false
+local trace_lines = false
+local ret_file, ret_line, ret_name
+local current_thread = 'main'
+local started = false
+local pause_off = false
+local _g      = _G
+local cocreate, cowrap = coroutine.create, coroutine.wrap
+local pausemsg = 'pause'
+
+local aliases = {
+    p = "over",
+    t = "step",
+    q = "exit",
+    g = "run",
+    dv = "dump",
+    dt = "locs",
+    k = "trace",
+    bp = "setb",
+    bc = "delb",
+    bl = "listb",
+    pt = "out",
+}
+
+--{{{  make Lua 5.2 compatible
+
+if not setfenv then -- Lua 5.2
+  --[[
+  As far as I can see, the only missing detail of these functions (except
+  for occasional bugs) to achieve 100% compatibility is the case of
+  'getfenv' over a function that does not have an _ENV variable (that is,
+  it uses no globals).
+
+  We could use a weak table to keep the environments of these functions
+  when set by setfenv, but that still misses the case of a function
+  without _ENV that was not subjected to setfenv.
+
+  -- Roberto
+  ]]--
+
+  setfenv = setfenv or function(f, t)
+    f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func)
+    local name
+    local up = 0
+    repeat
+      up = up + 1
+      name = debug.getupvalue(f, up)
+    until name == '_ENV' or name == nil
+    if name then
+      debug.upvaluejoin(f, up, function() return name end, 1) -- use unique upvalue
+      debug.setupvalue(f, up, t)
+    end
+  end
+
+  getfenv = getfenv or function(f)
+    f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func)
+    local name, val
+    local up = 0
+    repeat
+      up = up + 1
+      name, val = debug.getupvalue(f, up)
+    until name == '_ENV' or name == nil
+    return val
+  end
+
+  unpack = table.unpack
+
+end
+
+--}}}
+
+--{{{  local hints -- command help
+--The format in here is name=summary|description
+local hints = {
+
+pause =   [[
+pause(msg[,lines][,force]) -- start/resume a debugger session|
+
+This can only be used in your code or from the console as a means to
+start/resume a debug session.
+If msg is given that is shown when the session starts/resumes. Useful to
+give a context if you've instrumented your code with pause() statements.
+
+If lines is given, the script pauses after that many lines, else it pauses
+immediately.
+
+If force is true, the pause function is honoured even if poff has been used.
+This is useful when in an interactive console session to regain debugger
+control.
+]],
+
+poff =    [[
+poff                -- turn off pause() command|
+
+This causes all pause() commands to be ignored. This is useful if you have
+instrumented your code in a busy loop and want to continue normal execution
+with no further interruption.
+]],
+
+pon =     [[
+pon                 -- turn on pause() command|
+
+This re-instates honouring the pause() commands you may have instrumented
+your code with.
+]],
+
+setb =    [[
+setb [line file]    -- set a breakpoint to line/file|, line 0 means 'any'
+
+If file is omitted or is "-" the breakpoint is set at the file for the
+currently set level (see "set"). Execution pauses when this line is about
+to be executed and the debugger session is re-activated.
+
+The file can be given as the fully qualified name, partially qualified or
+just the file name. E.g. if file is set as "myfile.lua", then whenever
+execution reaches any file that ends with "myfile.lua" it will pause. If
+no extension is given, any extension will do.
+
+If the line is given as 0, then reaching any line in the file will do.
+]],
+
+delb =    [[
+delb [line file]    -- removes a breakpoint|
+
+If file is omitted or is "-" the breakpoint is removed for the file of the
+currently set level (see "set").
+]],
+
+delallb = [[
+delallb             -- removes all breakpoints|
+]],
+
+setw =    [[
+setw <exp>          -- adds a new watch expression|
+
+The expression is evaluated before each line is executed. If the expression
+yields true then execution is paused and the debugger session re-activated.
+The expression is executed in the context of the line about to be executed.
+]],
+
+delw =    [[
+delw <index>        -- removes the watch expression at index|
+
+The index is that returned when the watch expression was set by setw.
+]],
+
+delallw = [[
+delallw             -- removes all watch expressions|
+]],
+
+run     = [[
+run                 -- run until next breakpoint or watch expression|
+]],
+
+step    = [[
+step [N]            -- run next N lines, stepping into function calls|
+
+If N is omitted, use 1.
+]],
+
+over    = [[
+over [N]            -- run next N lines, stepping over function calls|
+
+If N is omitted, use 1.
+]],
+
+out     = [[
+out [N]             -- run lines until stepped out of N functions|
+
+If N is omitted, use 1.
+If you are inside a function, using "out 1" will run until you return
+from that function to the caller.
+]],
+
+gotoo   = [[
+gotoo [line file]    -- step to line in file|
+
+This is equivalent to 'setb line file', followed by 'run', followed
+by 'delb line file'.
+]],
+
+listb   = [[
+listb               -- lists breakpoints|
+]],
+
+listw   = [[
+listw               -- lists watch expressions|
+]],
+
+set     = [[
+set [level]         -- set context to stack level, omitted=show|
+
+If level is omitted it just prints the current level set.
+This sets the current context to the level given. This affects the
+context used for several other functions (e.g. vars). The possible
+levels are those shown by trace.
+]],
+
+vars    = [[
+vars [depth]        -- list context locals to depth, omitted=1|
+
+If depth is omitted then uses 1.
+Use a depth of 0 for the maximum.
+Lists all non-nil local variables and all non-nil upvalues in the
+currently set context. For variables that are tables, lists all fields
+to the given depth.
+]],
+
+fenv    = [[
+fenv [depth]        -- list context function env to depth, omitted=1|
+
+If depth is omitted then uses 1.
+Use a depth of 0 for the maximum.
+Lists all function environment variables in the currently set context.
+For variables that are tables, lists all fields to the given depth.
+]],
+
+glob    = [[
+glob [depth]        -- list globals to depth, omitted=1|
+
+If depth is omitted then uses 1.
+Use a depth of 0 for the maximum.
+Lists all global variables.
+For variables that are tables, lists all fields to the given depth.
+]],
+
+ups     = [[
+ups                 -- list all the upvalue names|
+
+These names will also be in the "vars" list unless their value is nil.
+This provides a means to identify which vars are upvalues and which are
+locals. If a name is both an upvalue and a local, the local value takes
+precedance.
+]],
+
+locs    = [[
+locs                -- list all the locals names|
+
+These names will also be in the "vars" list unless their value is nil.
+This provides a means to identify which vars are upvalues and which are
+locals. If a name is both an upvalue and a local, the local value takes
+precedance.
+]],
+
+dump    = [[
+dump <var> [depth]  -- dump all fields of variable to depth|
+
+If depth is omitted then uses 1.
+Use a depth of 0 for the maximum.
+Prints the value of <var> in the currently set context level. If <var>
+is a table, lists all fields to the given depth. <var> can be just a
+name, or name.field or name.# to any depth, e.g. t.1.f accesses field
+'f' in array element 1 in table 't'.
+
+Can also be called from a script as dump(var,depth).
+]],
+
+tron    = [[
+tron [crl]          -- turn trace on for (c)alls, (r)etuns, (l)lines|
+
+If no parameter is given then tracing is turned off.
+When tracing is turned on a line is printed to the console for each
+debug 'event' selected. c=function calls, r=function returns, l=lines.
+]],
+
+trace   = [[
+trace               -- dumps a stack trace|
+
+Format is [level] = file,line,name
+The level is a candidate for use by the 'set' command.
+]],
+
+info    = [[
+info                -- dumps the complete debug info captured|
+
+Only useful as a diagnostic aid for the debugger itself. This information
+can be HUGE as it dumps all variables to the maximum depth, so be careful.
+]],
+
+show    = [[
+show line file X Y  -- show X lines before and Y after line in file|
+
+If line is omitted or is '-' then the current set context line is used.
+If file is omitted or is '-' then the current set context file is used.
+If file is not fully qualified and cannot be opened as specified, then
+a search for the file in the package[path] is performed using the usual
+"require" searching rules. If no file extension is given, .lua is used.
+Prints the lines from the source file around the given line.
+]],
+
+exit    = [[
+exit                -- exits debugger, re-start it using pause()|
+]],
+
+help    = [[
+help [command]      -- show this list or help for command|
+]],
+
+["<statement>"] = [[
+<statement>         -- execute a statement in the current context|
+
+The statement can be anything that is legal in the context, including
+assignments. Such assignments affect the context and will be in force
+immediately. Any results returned are printed. Use '=' as a short-hand
+for 'return', e.g. "=func(arg)" will call 'func' with 'arg' and print
+the results, and "=var" will just print the value of 'var'.
+]],
+
+what    = [[
+what <func>         -- show where <func> is defined (if known)|
+]],
+
+}
+--}}}
+
+--{{{  local function getinfo(level,field)
+
+--like debug.getinfo but copes with no activation record at the given level
+--and knows how to get 'field'. 'field' can be the name of any of the
+--activation record fields or any of the 'what' names or nil for everything.
+--only valid when using the stack level to get info, not a function name.
+
+local function getinfo(level,field)
+  level = level + 1  --to get to the same relative level as the caller
+  if not field then return debug.getinfo(level) end
+  local what
+  if field == 'name' or field == 'namewhat' then
+    what = 'n'
+  elseif field == 'what' or field == 'source' or field == 'linedefined' or field == 'lastlinedefined' or field == 'short_src' then
+    what = 'S'
+  elseif field == 'currentline' then
+    what = 'l'
+  elseif field == 'nups' then
+    what = 'u'
+  elseif field == 'func' then
+    what = 'f'
+  else
+    return debug.getinfo(level,field)
+  end
+  local ar = debug.getinfo(level,what)
+  if ar then return ar[field] else return nil end
+end
+
+--}}}
+--{{{  local function indented( level, ... )
+
+local function indented( level, ... )
+  io.write( string.rep('  ',level), table.concat({...}), '\n' )
+end
+
+--}}}
+--{{{  local function dumpval( level, name, value, limit )
+
+local dumpvisited
+
+local function dumpval( level, name, value, limit )
+  local index
+  if type(name) == 'number' then
+    index = string.format('[%d] = ',name)
+  elseif type(name) == 'string'
+     and (name == '__VARSLEVEL__' or name == '__ENVIRONMENT__' or name == '__GLOBALS__' or name == '__UPVALUES__' or name == '__LOCALS__') then
+    --ignore these, they are debugger generated
+    return
+  elseif type(name) == 'string' and string.find(name,'^[_%a][_.%w]*$') then
+    index = name ..' = '
+  else
+    index = string.format('[%q] = ',tostring(name))
+  end
+  if type(value) == 'table' then
+    if dumpvisited[value] then
+      indented( level, index, string.format('ref%q;',dumpvisited[value]) )
+    else
+      dumpvisited[value] = tostring(value)
+      if (limit or 0) > 0 and level+1 >= limit then
+        indented( level, index, dumpvisited[value] )
+      else
+        indented( level, index, '{  -- ', dumpvisited[value] )
+        for n,v in pairs(value) do
+          dumpval( level+1, n, v, limit )
+        end
+        indented( level, '};' )
+      end
+    end
+  else
+    if type(value) == 'string' then
+      if string.len(value) > 40 then
+        indented( level, index, '[[', value, ']];' )
+      else
+        indented( level, index, string.format('%q',value), ';' )
+      end
+    else
+      indented( level, index, tostring(value), ';' )
+    end
+  end
+end
+
+--}}}
+--{{{  local function dumpvar( value, limit, name )
+
+local function dumpvar( value, limit, name )
+  dumpvisited = {}
+  dumpval( 0, name or tostring(value), value, limit )
+end
+
+--}}}
+--{{{  local function show(file,line,before,after)
+
+--show +/-N lines of a file around line M
+
+local function show(file,line,before,after)
+
+  line   = tonumber(line   or 1)
+  before = tonumber(before or 10)
+  after  = tonumber(after  or before)
+
+  if not string.find(file,'%.') then file = file..'.lua' end
+
+  local f = io.open(file,'r')
+  if not f then
+    --{{{  try to find the file in the path
+    
+    --
+    -- looks for a file in the package path
+    --
+    local path = package.path or LUA_PATH or ''
+    for c in string.gmatch (path, "[^;]+") do
+      local c = string.gsub (c, "%?%.lua", file)
+      f = io.open (c,'r')
+      if f then
+        break
+      end
+    end
+    
+    --}}}
+    if not f then
+      io.write('Cannot find '..file..'\n')
+      return
+    end
+  end
+
+  local i = 0
+  for l in f:lines() do
+    i = i + 1
+    if i >= (line-before) then
+      if i > (line+after) then break end
+      if i == line then
+        io.write(i..'***\t'..l..'\n')
+      else
+        io.write(i..'\t'..l..'\n')
+      end
+    end
+  end
+
+  f:close()
+
+end
+
+--}}}
+--{{{  local function tracestack(l)
+
+local function gi( i )
+  return function() i=i+1 return debug.getinfo(i),i end
+end
+
+local function gl( level, j )
+  return function() j=j+1 return debug.getlocal( level, j ) end
+end
+
+local function gu( func, k )
+  return function() k=k+1 return debug.getupvalue( func, k ) end
+end
+
+local  traceinfo
+
+local function tracestack(l)
+  local l = l + 1                        --NB: +1 to get level relative to caller
+  traceinfo = {}
+  traceinfo.pausemsg = pausemsg
+  for ar,i in gi(l) do
+    table.insert( traceinfo, ar )
+    if ar.what ~= 'C' then
+      local names  = {}
+      local values = {}
+      for n,v in gl(i,0) do
+        if string.sub(n,1,1) ~= '(' then   --ignore internal control variables
+          table.insert( names, n )
+          table.insert( values, v )
+        end
+      end
+      if #names > 0 then
+        ar.lnames  = names
+        ar.lvalues = values
+      end
+    end
+    if ar.func then
+      local names  = {}
+      local values = {}
+      for n,v in gu(ar.func,0) do
+        if string.sub(n,1,1) ~= '(' then   --ignore internal control variables
+          table.insert( names, n )
+          table.insert( values, v )
+        end
+      end
+      if #names > 0 then
+        ar.unames  = names
+        ar.uvalues = values
+      end
+    end
+  end
+end
+
+--}}}
+--{{{  local function trace()
+
+local function trace(set)
+  local mark
+  for level,ar in ipairs(traceinfo) do
+    if level == set then
+      mark = '***'
+    else
+      mark = ''
+    end
+    io.write('['..level..']'..mark..'\t'..(ar.name or ar.what)..' in '..ar.short_src..':'..ar.currentline..'\n')
+  end
+end
+
+--}}}
+--{{{  local function info()
+
+local function info() dumpvar( traceinfo, 0, 'traceinfo' ) end
+
+--}}}
+
+--{{{  local function set_breakpoint(file, line, once)
+
+local function set_breakpoint(file, line, once)
+  if not breakpoints[line] then
+    breakpoints[line] = {}
+  end
+  if once then
+    breakpoints[line][file] = 1
+  else
+    breakpoints[line][file] = true
+  end
+end
+
+--}}}
+--{{{  local function remove_breakpoint(file, line)
+
+local function remove_breakpoint(file, line)
+  if breakpoints[line] then
+    breakpoints[line][file] = nil
+  end
+end
+
+--}}}
+--{{{  local function has_breakpoint(file, line)
+
+--allow for 'sloppy' file names
+--search for file and all variations walking up its directory hierachy
+--ditto for the file with no extension
+--a breakpoint can be permenant or once only, if once only its removed
+--after detection here, these are used for temporary breakpoints in the
+--debugger loop when executing the 'gotoo' command
+--a breakpoint on line 0 of a file means any line in that file
+
+local function has_breakpoint(file, line)
+  local isLine = breakpoints[line]
+  local isZero = breakpoints[0]
+  if not isLine and not isZero then return false end
+  local noext = string.gsub(file,"(%..-)$",'',1)
+  if noext == file then noext = nil end
+  while file do
+    if isLine and isLine[file] then
+      if isLine[file] == 1 then isLine[file] = nil end
+      return true
+    end
+    if isZero and isZero[file] then
+      if isZero[file] == 1 then isZero[file] = nil end
+      return true
+    end
+    if IsWindows then
+      file = string.match(file,"[:/\\](.+)$")
+    else
+      file = string.match(file,"[:/](.+)$")
+    end
+  end
+  while noext do
+    if isLine and isLine[noext] then
+      if isLine[noext] == 1 then isLine[noext] = nil end
+      return true
+    end
+    if isZero and isZero[noext] then
+      if isZero[noext] == 1 then isZero[noext] = nil end
+      return true
+    end
+    if IsWindows then
+      noext = string.match(noext,"[:/\\](.+)$")
+    else
+      noext = string.match(noext,"[:/](.+)$")
+    end
+  end
+  return false
+end
+
+--}}}
+--{{{  local function capture_vars(ref,level,line)
+
+local function capture_vars(ref,level,line)
+  --get vars, file and line for the given level relative to debug_hook offset by ref
+
+  local lvl = ref + level                --NB: This includes an offset of +1 for the call to here
+
+  --{{{  capture variables
+  
+  local ar = debug.getinfo(lvl, "f")
+  if not ar then return {},'?',0 end
+  
+  local vars = {__UPVALUES__={}, __LOCALS__={}}
+  local i
+  
+  local func = ar.func
+  if func then
+    i = 1
+    while true do
+      local name, value = debug.getupvalue(func, i)
+      if not name then break end
+      if string.sub(name,1,1) ~= '(' then  --NB: ignoring internal control variables
+        vars[name] = value
+        vars.__UPVALUES__[i] = name
+      end
+      i = i + 1
+    end
+    vars.__ENVIRONMENT__ = getfenv(func)
+  end
+  
+  vars.__GLOBALS__ = getfenv(0)
+  
+  i = 1
+  while true do
+    local name, value = debug.getlocal(lvl, i)
+    if not name then break end
+    if string.sub(name,1,1) ~= '(' then    --NB: ignoring internal control variables
+      vars[name] = value
+      vars.__LOCALS__[i] = name
+    end
+    i = i + 1
+  end
+  
+  vars.__VARSLEVEL__ = level
+  
+  if func then
+    --NB: Do not do this until finished filling the vars table
+    setmetatable(vars, { __index = getfenv(func), __newindex = getfenv(func) })
+  end
+  
+  --NB: Do not read or write the vars table anymore else the metatable functions will get invoked!
+  
+  --}}}
+
+  local file = getinfo(lvl, "source")
+  if string.find(file, "@") == 1 then
+    file = string.sub(file, 2)
+  end
+  if IsWindows then file = string.lower(file) end
+
+  if not line then
+    line = getinfo(lvl, "currentline")
+  end
+
+  return vars,file,line
+
+end
+
+--}}}
+--{{{  local function restore_vars(ref,vars)
+
+local function restore_vars(ref,vars)
+
+  if type(vars) ~= 'table' then return end
+
+  local level = vars.__VARSLEVEL__       --NB: This level is relative to debug_hook offset by ref
+  if not level then return end
+
+  level = level + ref                    --NB: This includes an offset of +1 for the call to here
+
+  local i
+  local written_vars = {}
+
+  i = 1
+  while true do
+    local name, value = debug.getlocal(level, i)
+    if not name then break end
+    if vars[name] and string.sub(name,1,1) ~= '(' then     --NB: ignoring internal control variables
+      debug.setlocal(level, i, vars[name])
+      written_vars[name] = true
+    end
+    i = i + 1
+  end
+
+  local ar = debug.getinfo(level, "f")
+  if not ar then return end
+
+  local func = ar.func
+  if func then
+
+    i = 1
+    while true do
+      local name, value = debug.getupvalue(func, i)
+      if not name then break end
+      if vars[name] and string.sub(name,1,1) ~= '(' then   --NB: ignoring internal control variables
+        if not written_vars[name] then
+          debug.setupvalue(func, i, vars[name])
+        end
+        written_vars[name] = true
+      end
+      i = i + 1
+    end
+
+  end
+
+end
+
+--}}}
+--{{{  local function trace_event(event, line, level)
+
+local function print_trace(level,depth,event,file,line,name)
+
+  --NB: level here is relative to the caller of trace_event, so offset by 2 to get to there
+  level = level + 2
+
+  local file = file or getinfo(level,'short_src')
+  local line = line or getinfo(level,'currentline')
+  local name = name or getinfo(level,'name')
+
+  local prefix = ''
+  if current_thread ~= 'main' then prefix = '['..tostring(current_thread)..'] ' end
+
+  io.write(prefix..
+           string.format('%08.2f:%02i.',os.clock(),depth)..
+           string.rep('.',depth%32)..
+           (file or '')..' ('..(line or '')..') '..
+           (name or '')..
+           ' ('..event..')\n')
+
+end
+
+local function trace_event(event, line, level)
+
+  if event == 'return' and trace_returns then
+    --note the line info for later
+    ret_file = getinfo(level+1,'short_src')
+    ret_line = getinfo(level+1,'currentline')
+    ret_name = getinfo(level+1,'name')
+  end
+
+  if event ~= 'line' then return end
+
+  local slevel = stack_level[current_thread]
+  local tlevel = trace_level[current_thread]
+
+  if trace_calls and slevel > tlevel then
+    --we are now in the function called, so look back 1 level further to find the calling file and line
+    print_trace(level+1,slevel-1,'c',nil,nil,getinfo(level+1,'name'))
+  end
+
+  if trace_returns and slevel < tlevel then
+    print_trace(level,slevel,'r',ret_file,ret_line,ret_name)
+  end
+
+  if trace_lines then
+    print_trace(level,slevel,'l')
+  end
+
+  trace_level[current_thread] = stack_level[current_thread]
+
+end
+
+--}}}
+--{{{  local function report(ev, vars, file, line, idx_watch)
+
+local function report(ev, vars, file, line, idx_watch)
+  function show_source()
+    show(traceinfo[1].short_src, traceinfo[1].currentline, 2, 2)
+  end
+
+  local vars = vars or {}
+  local file = file or '?'
+  local line = line or 0
+  local prefix = ''
+  if current_thread ~= 'main' then prefix = '['..tostring(current_thread)..'] ' end
+  if ev == events.STEP then
+    io.write(prefix.."Paused at file "..file.." line "..line..' ('..stack_level[current_thread]..')\n')
+    show_source()
+  elseif ev == events.BREAK then
+    io.write(prefix.."Paused at file "..file.." line "..line..' ('..stack_level[current_thread]..') (breakpoint)\n')
+    show_source()
+  elseif ev == events.WATCH then
+    io.write(prefix.."Paused at file "..file.." line "..line..' ('..stack_level[current_thread]..')'.." (watch expression "..idx_watch.. ": ["..watches[idx_watch].exp.."])\n")
+    show_source()
+  elseif ev == events.SET then
+    --do nothing
+  else
+    io.write(prefix.."Error in application: "..file.." line "..line.."\n")
+  end
+  if ev ~= events.SET then
+    if pausemsg and pausemsg ~= '' then io.write('Message: '..pausemsg..'\n') end
+    pausemsg = ''
+  end
+  return vars, file, line
+end
+
+--}}}
+
+--{{{  local function debugger_loop(ev, vars, file, line, idx_watch)
+
+local last_line = ""
+
+local function debugger_loop(ev, vars, file, line, idx_watch)
+
+  local eval_env  = vars or {}
+  local breakfile = file or '?'
+  local breakline = line or 0
+
+  local command, args
+
+  --{{{  local function getargs(spec)
+  
+  --get command arguments according to the given spec from the args string
+  --the spec has a single character for each argument, arguments are separated
+  --by white space, the spec characters can be one of:
+  -- F for a filename    (defaults to breakfile if - given in args)
+  -- L for a line number (defaults to breakline if - given in args)
+  -- N for a number
+  -- V for a variable name
+  -- S for a string
+  
+  local function getargs(spec)
+    local res={}
+    local char,arg
+    local ptr=1
+    for i=1,string.len(spec) do
+      char = string.sub(spec,i,i)
+      if     char == 'F' then
+        _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
+        if not arg or arg == '' then arg = '-' end
+        if arg == '-' then arg = breakfile end
+      elseif char == 'L' then
+        _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
+        if not arg or arg == '' then arg = '-' end
+        if arg == '-' then arg = breakline end
+        arg = tonumber(arg) or 0
+      elseif char == 'N' then
+        _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
+        if not arg or arg == '' then arg = '0' end
+        arg = tonumber(arg) or 0
+      elseif char == 'V' then
+        _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
+        if not arg or arg == '' then arg = '' end
+      elseif char == 'S' then
+        _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
+        if not arg or arg == '' then arg = '' end
+      else
+        arg = ''
+      end
+      table.insert(res,arg or '')
+    end
+    return unpack(res)
+  end
+  
+  --}}}
+
+  while true do
+    io.write("[DEBUG]> ")
+    local line = io.read("*line")
+    if line == nil then io.write('\n'); line = 'exit' end
+
+    if line == "" then
+        line = last_line
+    else
+        last_line = line
+    end
+    io.write("'" .. last_line .. "'\n")
+
+    if string.find(line, "^[a-z]+") then
+      command = string.sub(line, string.find(line, "^[a-z]+"))
+      args    = string.gsub(line,"^[a-z]+%s*",'',1)            --strip command off line
+    else
+      command = ''
+    end
+
+    command = aliases[command] or command
+
+    if command == "setb" then
+      --{{{  set breakpoint
+      
+      local line, filename  = getargs('LF')
+      if filename ~= '' and line ~= '' then
+        set_breakpoint(filename,line)
+        io.write("Breakpoint set in file "..filename..' line '..line..'\n')
+      else
+        io.write("Bad request\n")
+      end
+      
+      --}}}
+
+    elseif command == "delb" then
+      --{{{  delete breakpoint
+      
+      local line, filename = getargs('LF')
+      if filename ~= '' and line ~= '' then
+        remove_breakpoint(filename, line)
+        io.write("Breakpoint deleted from file "..filename..' line '..line.."\n")
+      else
+        io.write("Bad request\n")
+      end
+      
+      --}}}
+
+    elseif command == "delallb" then
+      --{{{  delete all breakpoints
+      breakpoints = {}
+      io.write('All breakpoints deleted\n')
+      --}}}
+
+    elseif command == "listb" then
+      --{{{  list breakpoints
+      for i, v in pairs(breakpoints) do
+        for ii, vv in pairs(v) do
+          io.write("Break at: "..i..' in '..ii..'\n')
+        end
+      end
+      --}}}
+
+    elseif command == "setw" then
+      --{{{  set watch expression
+      
+      if args and args ~= '' then
+        local func = loadstring("return(" .. args .. ")")
+        local newidx = #watches + 1
+        watches[newidx] = {func = func, exp = args}
+        io.write("Set watch exp no. " .. newidx..'\n')
+      else
+        io.write("Bad request\n")
+      end
+      
+      --}}}
+
+    elseif command == "delw" then
+      --{{{  delete watch expression
+      
+      local index = tonumber(args)
+      if index then
+        watches[index] = nil
+        io.write("Watch expression deleted\n")
+      else
+        io.write("Bad request\n")
+      end
+      
+      --}}}
+
+    elseif command == "delallw" then
+      --{{{  delete all watch expressions
+      watches = {}
+      io.write('All watch expressions deleted\n')
+      --}}}
+
+    elseif command == "listw" then
+      --{{{  list watch expressions
+      for i, v in pairs(watches) do
+        io.write("Watch exp. " .. i .. ": " .. v.exp..'\n')
+      end
+      --}}}
+
+    elseif command == "run" then
+      --{{{  run until breakpoint
+      step_into = false
+      step_over = false
+      return 'cont'
+      --}}}
+
+    elseif command == "step" then
+      --{{{  step N lines (into functions)
+      local N = tonumber(args) or 1
+      step_over  = false
+      step_into  = true
+      step_lines = tonumber(N or 1)
+      return 'cont'
+      --}}}
+
+    elseif command == "over" then
+      --{{{  step N lines (over functions)
+      local N = tonumber(args) or 1
+      step_into  = false
+      step_over  = true
+      step_lines = tonumber(N or 1)
+      step_level[current_thread] = stack_level[current_thread]
+      return 'cont'
+      --}}}
+
+    elseif command == "out" then
+      --{{{  step N lines (out of functions)
+      local N = tonumber(args) or 1
+      step_into  = false
+      step_over  = true
+      step_lines = 1
+      step_level[current_thread] = stack_level[current_thread] - tonumber(N or 1)
+      return 'cont'
+      --}}}
+
+    elseif command == "gotoo" then
+      --{{{  step until reach line
+      local line, filename = getargs('LF')
+      if line ~= '' then
+        step_over  = false
+        step_into  = false
+        if has_breakpoint(filename,line) then
+          return 'cont'
+        else
+          set_breakpoint(filename,line,true)
+          return 'cont'
+        end
+      else
+        io.write("Bad request\n")
+      end
+      --}}}
+
+    elseif command == "set" then
+      --{{{  set/show context level
+      local level = args
+      if level and level == '' then level = nil end
+      if level then return level end
+      --}}}
+
+    elseif command == "vars" then
+      --{{{  list context variables
+      local depth = args
+      if depth and depth == '' then depth = nil end
+      depth = tonumber(depth) or 1
+      dumpvar(eval_env, depth+1, 'variables')
+      --}}}
+
+    elseif command == "glob" then
+      --{{{  list global variables
+      local depth = args
+      if depth and depth == '' then depth = nil end
+      depth = tonumber(depth) or 1
+      dumpvar(eval_env.__GLOBALS__,depth+1,'globals')
+      --}}}
+
+    elseif command == "fenv" then
+      --{{{  list function environment variables
+      local depth = args
+      if depth and depth == '' then depth = nil end
+      depth = tonumber(depth) or 1
+      dumpvar(eval_env.__ENVIRONMENT__,depth+1,'environment')
+      --}}}
+
+    elseif command == "ups" then
+      --{{{  list upvalue names
+      dumpvar(eval_env.__UPVALUES__,2,'upvalues')
+      --}}}
+
+    elseif command == "locs" then
+      --{{{  list locals names
+      dumpvar(eval_env.__LOCALS__,2,'upvalues')
+      --}}}
+
+    elseif command == "what" then
+      --{{{  show where a function is defined
+      if args and args ~= '' then
+        local v = eval_env
+        local n = nil
+        for w in string.gmatch(args,"[%w_]+") do
+          v = v[w]
+          if n then n = n..'.'..w else n = w end
+          if not v then break end
+        end
+        if type(v) == 'function' then
+          local def = debug.getinfo(v,'S')
+          if def then
+            io.write(def.what..' in '..def.short_src..' '..def.linedefined..'..'..def.lastlinedefined..'\n')
+          else
+            io.write('Cannot get info for '..v..'\n')
+          end
+        else
+          io.write(v..' is not a function\n')
+        end
+      else
+        io.write("Bad request\n")
+      end
+      --}}}
+
+    elseif command == "dump" then
+      --{{{  dump a variable
+      local name, depth = getargs('VN')
+      if name ~= '' then
+        if depth == '' or depth == 0 then depth = nil end
+        depth = tonumber(depth or 1)
+        local v = eval_env
+        local n = nil
+        for w in string.gmatch(name,"[^%.]+") do     --get everything between dots
+          if tonumber(w) then
+            v = v[tonumber(w)]
+          else
+            v = v[w]
+          end
+          if n then n = n..'.'..w else n = w end
+          if not v then break end
+        end
+        dumpvar(v,depth+1,n)
+      else
+        io.write("Bad request\n")
+      end
+      --}}}
+
+    elseif command == "show" then
+      --{{{  show file around a line or the current breakpoint
+      
+      local line, file, before, after = getargs('LFNN')
+      if before == 0 then before = 10     end
+      if after  == 0 then after  = before end
+      
+      if file ~= '' and file ~= "=stdin" then
+        show(file,line,before,after)
+      else
+        io.write('Nothing to show\n')
+      end
+      
+      --}}}
+
+    elseif command == "poff" then
+      --{{{  turn pause command off
+      pause_off = true
+      --}}}
+
+    elseif command == "pon" then
+      --{{{  turn pause command on
+      pause_off = false
+      --}}}
+
+    elseif command == "tron" then
+      --{{{  turn tracing on/off
+      local option = getargs('S')
+      trace_calls   = false
+      trace_returns = false
+      trace_lines   = false
+      if string.find(option,'c') then trace_calls   = true end
+      if string.find(option,'r') then trace_returns = true end
+      if string.find(option,'l') then trace_lines   = true end
+      --}}}
+
+    elseif command == "trace" then
+      --{{{  dump a stack trace
+      trace(eval_env.__VARSLEVEL__)
+      --}}}
+
+    elseif command == "info" then
+      --{{{  dump all debug info captured
+      info()
+      --}}}
+
+    elseif command == "pause" then
+      --{{{  not allowed in here
+      io.write('pause() should only be used in the script you are debugging\n')
+      --}}}
+
+    elseif command == "help" then
+      --{{{  help
+      local command = getargs('S')
+      if command ~= '' and hints[command] then
+        io.write(hints[command]..'\n')
+      else
+        for _,v in pairs(hints) do
+          local _,_,h = string.find(v,"(.+)|")
+          io.write(h..'\n')
+        end
+      end
+      --}}}
+
+    elseif command == "exit" then
+      --{{{  exit debugger
+      return 'stop'
+      --}}}
+
+    elseif line ~= '' then
+      --{{{  just execute whatever it is in the current context
+      
+      --map line starting with "=..." to "return ..."
+      if string.sub(line,1,1) == '=' then line = string.gsub(line,'=','return ',1) end
+      
+      local ok, func = pcall(loadstring,line)
+      if func == nil then                             [email protected]
+        io.write("Compile error: "..line..'\n')
+      elseif not ok then
+        io.write("Compile error: "..func..'\n')
+      else
+        setfenv(func, eval_env)
+        local res = {pcall(func)}
+        if res[1] then
+          if res[2] then
+            table.remove(res,1)
+            for _,v in ipairs(res) do
+              io.write(tostring(v))
+              io.write('\t')
+            end
+            io.write('\n')
+          end
+          --update in the context
+          return 0
+        else
+          io.write("Run error: "..res[2]..'\n')
+        end
+      end
+      
+      --}}}
+    end
+  end
+
+end
+
+--}}}
+--{{{  local function debug_hook(event, line, level, thread)
+
+local function debug_hook(event, line, level, thread)
+  if not started then debug.sethook(); coro_debugger = nil; return end
+  current_thread = thread or 'main'
+  local level = level or 2
+  trace_event(event,line,level)
+  if event == "call" then
+    stack_level[current_thread] = stack_level[current_thread] + 1
+  elseif event == "return" then
+    stack_level[current_thread] = stack_level[current_thread] - 1
+    if stack_level[current_thread] < 0 then stack_level[current_thread] = 0 end
+  else
+    local vars,file,line = capture_vars(level,1,line)
+    local stop, ev, idx = false, events.STEP, 0
+    while true do
+      for index, value in pairs(watches) do
+        setfenv(value.func, vars)
+        local status, res = pcall(value.func)
+        if status and res then
+          ev, idx = events.WATCH, index
+          stop = true
+          break
+        end
+      end
+      if stop then break end
+      if (step_into)
+      or (step_over and (stack_level[current_thread] <= step_level[current_thread] or stack_level[current_thread] == 0)) then
+        step_lines = step_lines - 1
+        if step_lines < 1 then
+          ev, idx = events.STEP, 0
+          break
+        end
+      end
+      if has_breakpoint(file, line) then
+        ev, idx = events.BREAK, 0
+        break
+      end
+      return
+    end
+    tracestack(level)
+    if not coro_debugger then
+      io.write("\nLua Debugger\n")
+      vars, file, line = report(ev, vars, file, line, idx)
+      io.write("Type 'help' for commands\n")
+      coro_debugger = true
+    else
+      vars, file, line = report(ev, vars, file, line, idx)
+    end
+    local last_next = 1
+    local next = 'ask'
+    local silent = false
+    while true do
+      if next == 'ask' then
+        next = debugger_loop(ev, vars, file, line, idx)
+      elseif next == 'cont' then
+        return
+      elseif next == 'stop' then
+        started = false
+        debug.sethook()
+        coro_debugger = nil
+        return
+      elseif tonumber(next) then --get vars for given level or last level
+        next = tonumber(next)
+        if next == 0 then silent = true; next = last_next else silent = false end
+        last_next = next
+        restore_vars(level,vars)
+        vars, file, line = capture_vars(level,next)
+        if not silent then
+          if vars and vars.__VARSLEVEL__ then
+            io.write('Level: '..vars.__VARSLEVEL__..'\n')
+          else
+            io.write('No level set\n')
+          end
+        end
+        ev = events.SET
+        next = 'ask'
+      else
+        io.write('Unknown command from debugger_loop: '..tostring(next)..'\n')
+        io.write('Stopping debugger\n')
+        next = 'stop'
+      end
+    end
+  end
+end
+
+--}}}
+
+--{{{  coroutine.create
+
+--This function overrides the built-in for the purposes of propagating
+--the debug hook settings from the creator into the created coroutine.
+
+_G.coroutine.create = function(f)
+  local thread
+  local hook, mask, count = debug.gethook()
+  if hook then
+    local function thread_hook(event,line)
+      hook(event,line,3,thread)
+    end
+    thread = cocreate(function(...)
+                        stack_level[thread] = 0
+                        trace_level[thread] = 0
+                        step_level [thread] = 0
+                        debug.sethook(thread_hook,mask,count)
+                        return f(...)
+                      end)
+    return thread
+  else
+    return cocreate(f)
+  end
+end
+
+--}}}
+--{{{  coroutine.wrap
+
+--This function overrides the built-in for the purposes of propagating
+--the debug hook settings from the creator into the created coroutine.
+
+_G.coroutine.wrap = function(f)
+  local thread
+  local hook, mask, count = debug.gethook()
+  if hook then
+    local function thread_hook(event,line)
+      hook(event,line,3,thread)
+    end
+    thread = cowrap(function(...)
+                      stack_level[thread] = 0
+                      trace_level[thread] = 0
+                      step_level [thread] = 0
+                      debug.sethook(thread_hook,mask,count)
+                      return f(...)
+                    end)
+    return thread
+  else
+    return cowrap(f)
+  end
+end
+
+--}}}
+
+--{{{  function pause(x,l,f)
+
+--
+-- Starts/resumes a debug session
+--
+
+function pause(x,l,f)
+  if not f and pause_off then return end       --being told to ignore pauses
+  pausemsg = x or 'pause'
+  local lines
+  local src = getinfo(2,'short_src')
+  if l then
+    lines = l   --being told when to stop
+  elseif src == "stdin" then
+    lines = 1   --if in a console session, stop now
+  else
+    lines = 2   --if in a script, stop when get out of pause()
+  end
+  if started then
+    --we'll stop now 'cos the existing debug hook will grab us
+    step_lines = lines
+    step_into  = true
+    debug.sethook(debug_hook, "crl")         --reset it in case some external agent fiddled with it
+  else
+    --set to stop when get out of pause()
+    trace_level[current_thread] = 0
+    step_level [current_thread] = 0
+    stack_level[current_thread] = 1
+    step_lines = lines
+    step_into  = true
+    started    = true
+    debug.sethook(debug_hook, "crl")         --NB: this will cause an immediate entry to the debugger_loop
+  end
+end
+
+--}}}
+--{{{  function dump(v,depth)
+
+--shows the value of the given variable, only really useful
+--when the variable is a table
+--see dump debug command hints for full semantics
+
+function dump(v,depth)
+  dumpvar(v,(depth or 1)+1,tostring(v))
+end
+
+--}}}
+--{{{  function debug.traceback(x)
+
+local _traceback = debug.traceback       --note original function
+
+--override standard function
+debug.traceback = function(x)
+  local assertmsg = _traceback(x)        --do original function
+  pause(x)                               --let user have a look at stuff
+  return assertmsg                       --carry on
+end
+
+_TRACEBACK = debug.traceback             --Lua 5.0 function
+
+--}}}
+
+--------------------------------------------------------------------------------
+-- dir.lua
+--
+
+--
+-- Copyright (c) 2012 Martin Ridgers
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+-- SOFTWARE.
+--
+
+--------------------------------------------------------------------------------
+function dir_match_generator_impl(text)
+    -- Strip off any path components that may be on text.
+    local prefix = ""
+    local i = text:find("[\\/:][^\\/:]*$")
+    if i then
+        prefix = text:sub(1, i)
+    end
+
+    local include_dots = text:find("%.+$") ~= nil
+
+    local matches = {}
+    local mask = text.."*"
+
+    -- Find matches.
+    for _, dir in ipairs(clink.find_dirs(mask, true)) do
+        local file = prefix..dir
+
+        if include_dots or (dir ~= "." and dir ~= "..") then
+            if clink.is_match(text, file) then
+                table.insert(matches, prefix..dir)
+            end
+        end
+    end
+
+    return matches
+end
+
+--------------------------------------------------------------------------------
+local function dir_match_generator(word)
+    local matches = dir_match_generator_impl(word)
+
+    -- If there was no matches but text is a dir then use it as the single match.
+    -- Otherwise tell readline that matches are files and it will do magic.
+    if #matches == 0 then
+        if clink.is_dir(rl_state.text) then
+            table.insert(matches, rl_state.text)
+        end
+    else
+        clink.matches_are_files()
+    end
+
+    return matches
+end
+
+--------------------------------------------------------------------------------
+clink.arg.register_parser("cd", dir_match_generator)
+clink.arg.register_parser("chdir", dir_match_generator)
+clink.arg.register_parser("pushd", dir_match_generator)
+clink.arg.register_parser("rd", dir_match_generator)
+clink.arg.register_parser("rmdir", dir_match_generator)
+clink.arg.register_parser("md", dir_match_generator)
+clink.arg.register_parser("mkdir", dir_match_generator)
+
+-- vim: expandtab
+
+--------------------------------------------------------------------------------
+-- env.lua
+--
+
+--
+-- Copyright (c) 2012 Martin Ridgers
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+-- SOFTWARE.
+--
+
+--------------------------------------------------------------------------------
+local special_env_vars = {
+    "cd", "date", "time", "random", "errorlevel",
+    "cmdextversion", "cmdcmdline", "highestnumanodenumber"
+}
+
+--------------------------------------------------------------------------------
+local function env_vars_display_filter(matches)
+    local to_display = {}
+    for _, m in ipairs(matches) do
+        local _, _, out = m:find("(%%[^%%]+%%)$")
+        table.insert(to_display, out)
+    end
+
+    return to_display
+end
+
+--------------------------------------------------------------------------------
+local function env_vars_find_matches(candidates, prefix, part)
+    local part_len = #part
+    for _, name in ipairs(candidates) do
+        if clink.lower(name:sub(1, part_len)) == part then
+            clink.add_match(prefix..'%'..name:lower()..'%')
+        end
+    end
+end
+
+--------------------------------------------------------------------------------
+local function env_vars_match_generator(text, first, last)
+    local all = rl_state.line_buffer:sub(1, last)
+
+    -- Skip pairs of %s
+    local i = 1
+    for _, r in function () return all:find("%b%%", i) end do
+        i = r + 2
+    end
+
+    -- Find a solitary %
+    local i = all:find("%%", i)
+    if not i then
+        return false
+    end
+
+    if i < first then
+        return false
+    end
+
+    local part = clink.lower(all:sub(i + 1))
+    local part_len = #part
+
+    i = i - first
+    local prefix = text:sub(1, i)
+
+    env_vars_find_matches(clink.get_env_var_names(), prefix, part)
+    env_vars_find_matches(special_env_vars, prefix, part)
+
+    if clink.match_count() >= 1 then
+        clink.match_display_filter = env_vars_display_filter
+
+        clink.suppress_char_append()
+        clink.suppress_quoting()
+
+        return true
+    end
+
+    return false
+end
+
+--------------------------------------------------------------------------------
+if clink.get_host_process() == "cmd.exe" then
+    clink.register_match_generator(env_vars_match_generator, 10)
+end
+
+-- vim: expandtab
+
+--------------------------------------------------------------------------------
+-- exec.lua
+--
+
+--
+-- Copyright (c) 2012 Martin Ridgers
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+-- SOFTWARE.
+--
+
+--------------------------------------------------------------------------------
+local dos_commands = {
+    "assoc", "break", "call", "cd", "chcp", "chdir", "cls", "color", "copy",
+    "date", "del", "dir", "diskcomp", "diskcopy", "echo", "endlocal", "erase",
+    "exit", "for", "format", "ftype", "goto", "graftabl", "if", "md", "mkdir",
+    "mklink", "more", "move", "path", "pause", "popd", "prompt", "pushd", "rd",
+    "rem", "ren", "rename", "rmdir", "set", "setlocal", "shift", "start",
+    "time", "title", "tree", "type", "ver", "verify", "vol"
+}
+
+--------------------------------------------------------------------------------
+local function get_environment_paths()
+    local paths = clink.split(clink.get_env("PATH"), ";")
+
+    -- We're expecting absolute paths and as ';' is a valid path character
+    -- there maybe unneccessary splits. Here we resolve them.
+    local paths_merged = { paths[1] }
+    for i = 2, #paths, 1 do
+        if not paths[i]:find("^[a-zA-Z]:") then
+            local t = paths_merged[#paths_merged];
+            paths_merged[#paths_merged] = t..paths[i]
+        else
+            table.insert(paths_merged, paths[i])
+        end
+    end
+
+    -- Append slashes.
+    for i = 1, #paths_merged, 1 do
+        paths_merged[i] = paths_merged[i].."/"
+    end
+
+    return paths_merged
+end
+
+--------------------------------------------------------------------------------
+local function exec_find_dirs(pattern, case_map)
+    local ret = {}
+
+    for _, dir in ipairs(clink.find_dirs(pattern, case_map)) do
+        if dir ~= "." and dir ~= ".." then
+            table.insert(ret, dir)
+        end
+    end
+
+    return ret
+end
+
+--------------------------------------------------------------------------------
+local function exec_match_generator(text, first, last)
+    -- If match style setting is < 0 then consider executable matching disabled.
+    local match_style = clink.get_setting_int("exec_match_style")
+    if match_style < 0 then
+        return false
+    end
+
+    -- We're only interested in exec completion if this is the first word of the
+    -- line, or the first word after a command separator.
+    if clink.get_setting_int("space_prefix_match_files") > 0 then
+        if first > 1 then
+            return false
+        end
+    else
+        local leading = rl_state.line_buffer:sub(1, first - 1)
+        local is_first = leading:find("^%s*\"*$")
+        if not is_first then
+            return false
+        end
+    end
+
+    -- Split text into directory and name
+    local text_dir = ""
+    local text_name = text
+    local i = text:find("[\\/:][^\\/:]*$")
+    if i then
+        text_dir = text:sub(1, i)
+        text_name = text:sub(i + 1)
+    end
+
+    local paths
+    if not text:find("[\\/:]") then
+        -- If the terminal is cmd.exe check it's commands for matches.
+        if clink.get_host_process() == "cmd.exe" then
+            clink.match_words(text, dos_commands)
+        end
+
+        -- Add console aliases as matches.
+        local aliases = clink.get_console_aliases()
+        clink.match_words(text, aliases)
+
+        paths = get_environment_paths();
+    else
+        paths = {}
+
+        -- 'text' is an absolute or relative path. If we're doing Bash-style
+        -- matching should now consider directories.
+        if match_style < 1 then
+            match_style = 2
+        else
+            match_style = 1
+        end
+    end
+
+    -- Should we also consider the path referenced by 'text'?
+    if match_style >= 1 then
+        table.insert(paths, text_dir)
+    end
+
+    -- Search 'paths' for files ending in 'suffices' and look for matches
+    local suffices = clink.split(clink.get_env("pathext"), ";")
+    for _, suffix in ipairs(suffices) do
+        for _, path in ipairs(paths) do
+            local files = clink.find_files(path.."*"..suffix, false)
+            for _, file in ipairs(files) do
+                if clink.is_match(text_name, file) then
+                    clink.add_match(text_dir..file)
+                end
+            end
+        end
+    end
+
+    -- Lastly we may wish to consider directories too.
+    if clink.match_count() == 0 or match_style >= 2 then
+        clink.match_files(text.."*", true, exec_find_dirs)
+    end
+
+    clink.matches_are_files()
+    return true
+end
+
+--------------------------------------------------------------------------------
+clink.register_match_generator(exec_match_generator, 50)
+
+-- vim: expandtab
+
+--------------------------------------------------------------------------------
+-- git.lua
+--
+
+--
+-- Copyright (c) 2012 Martin Ridgers
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+-- SOFTWARE.
+--
+
+--------------------------------------------------------------------------------
+local git_argument_tree = {
+    -- Porcelain and ancillary commands from git's man page.
+    "add", "am", "archive", "bisect", "branch", "bundle", "checkout",
+    "cherry-pick", "citool", "clean", "clone", "commit", "describe", "diff",
+    "fetch", "format-patch", "gc", "grep", "gui", "init", "log", "merge", "mv",
+    "notes", "pull", "push", "rebase", "reset", "revert", "rm", "shortlog",
+    "show", "stash", "status", "submodule", "tag", "config", "fast-export",
+    "fast-import", "filter-branch", "lost-found", "mergetool", "pack-refs",
+    "prune", "reflog", "relink", "remote", "repack", "replace", "repo-config",
+    "annotate", "blame", "cherry", "count-objects", "difftool", "fsck",
+    "get-tar-commit-id", "help", "instaweb", "merge-tree", "rerere",
+    "rev-parse", "show-branch", "verify-tag", "whatchanged"
+}
+
+clink.arg.register_parser("git", git_argument_tree)
+
+-- vim: expandtab
+
+--------------------------------------------------------------------------------
+-- go.lua
+--
+
+--
+-- Copyright (c) 2013 Dobroslaw Zybort
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+-- SOFTWARE.
+--
+
+--------------------------------------------------------------------------------
+local function flags(...)
+    local p = clink.arg.new_parser()
+    p:set_flags(...)
+    return p
+end
+
+--------------------------------------------------------------------------------
+local go_tool_parser = clink.arg.new_parser()
+go_tool_parser:set_flags("-n")
+go_tool_parser:set_arguments({
+    "8a", "8c", "8g", "8l", "addr2line", "cgo", "dist", "nm", "objdump",
+    "pack",
+    "cover" .. flags("-func", "-html", "-mode", "-o", "-var"),
+    "fix"   .. flags("-diff", "-force", "-r"),
+    "prof"  .. flags("-p", "-t", "-d", "-P", "-h", "-f", "-l", "-r", "-s",
+                     "-hs"),
+    "pprof" .. flags(-- Options:
+                     "--cum", "--base", "--interactive", "--seconds",
+                     "--add_lib", "--lib_prefix",
+                     -- Reporting Granularity:
+                     "--addresses", "--lines", "--functions", "--files",
+                     -- Output type:
+                     "--text", "--callgrind", "--gv", "--web", "--list",
+                     "--disasm", "--symbols", "--dot", "--ps", "--pdf",
+                     "--svg", "--gif", "--raw",
+                     -- Heap-Profile Options:
+                     "--inuse_space", "--inuse_objects", "--alloc_space",
+                     "--alloc_objects", "--show_bytes", "--drop_negative",
+                     -- Contention-profile options:
+                     "--total_delay", "--contentions", "--mean_delay",
+                     -- Call-graph Options:
+                     "--nodecount", "--nodefraction", "--edgefraction",
+                     "--focus", "--ignore", "--scale", "--heapcheck",
+                     -- Miscellaneous:
+                     "--tools", "--test", "--help", "--version"),
+    "vet"   .. flags("-all", "-asmdecl", "-assign", "-atomic", "-buildtags",
+                     "-composites", "-compositewhitelist", "-copylocks",
+                     "-methods", "-nilfunc", "-printf", "-printfuncs",
+                     "-rangeloops", "-shadow", "-shadowstrict", "-structtags",
+                     "-test", "-unreachable", "-v"),
+    "yacc"  .. flags("-l", "-o", "-p", "-v"),
+})
+
+--------------------------------------------------------------------------------
+local go_parser = clink.arg.new_parser()
+go_parser:set_arguments({
+    "env",
+    "fix",
+    "version",
+    "build"    .. flags("-o", "-a", "-n", "-p", "-installsuffix", "-v", "-x",
+                        "-work", "-gcflags", "-ccflags", "-ldflags",
+                        "-gccgoflags", "-tags", "-compiler", "-race"),
+    "clean"    .. flags("-i", "-n", "-r", "-x"),
+    "fmt"      .. flags("-n", "-x"),
+    "get"      .. flags("-d", "-fix", "-t", "-u",
+                        -- Build flags
+                        "-a", "-n", "-p", "-installsuffix", "-v", "-x",
+                        "-work", "-gcflags", "-ccflags", "-ldflags",
+                        "-gccgoflags", "-tags", "-compiler", "-race"),
+    "install"  .. flags(-- All `go build` flags
+                        "-o", "-a", "-n", "-p", "-installsuffix", "-v", "-x",
+                        "-work", "-gcflags", "-ccflags", "-ldflags",
+                        "-gccgoflags", "-tags", "-compiler", "-race"),
+    "list"     .. flags("-e", "-race", "-f", "-json", "-tags"),
+    "run"      .. flags("-exec",
+                        -- Build flags
+                        "-a", "-n", "-p", "-installsuffix", "-v", "-x",
+                        "-work", "-gcflags", "-ccflags", "-ldflags",
+                        "-gccgoflags", "-tags", "-compiler", "-race"),
+    "test"     .. flags(-- Local.
+                        "-c", "-file", "-i", "-cover", "-coverpkg",
+                        -- Build flags
+                        "-a", "-n", "-p", "-x", "-work", "-ccflags",
+                        "-gcflags", "-exec", "-ldflags", "-gccgoflags",
+                        "-tags", "-compiler", "-race", "-installsuffix", 
+                        -- Passed to 6.out
+                        "-bench", "-benchmem", "-benchtime", "-covermode",
+                        "-coverprofile", "-cpu", "-cpuprofile", "-memprofile",
+                        "-memprofilerate", "-blockprofile",
+                        "-blockprofilerate", "-outputdir", "-parallel", "-run",
+                        "-short", "-timeout", "-v"),
+    "tool"     .. go_tool_parser,
+    "vet"      .. flags("-n", "-x"),
+})
+
+--------------------------------------------------------------------------------
+local go_help_parser = clink.arg.new_parser()
+go_help_parser:set_arguments({
+    "help" .. clink.arg.new_parser():set_arguments({
+        go_parser:flatten_argument(1)
+    })
+})
+
+--------------------------------------------------------------------------------
+local godoc_parser = clink.arg.new_parser()
+godoc_parser:set_flags(
+    "-zip", "-write_index", "-analysis", "-http", "-server", "-html","-src",
+    "-url", "-q", "-v", "-goroot", "-tabwidth", "-timestamps", "-templates",
+    "-play", "-ex", "-links", "-index", "-index_files", "-maxresults",
+    "-index_throttle", "-notes", "-httptest.serve"
+)
+
+--------------------------------------------------------------------------------
+local gofmt_parser = clink.arg.new_parser()
+gofmt_parser:set_flags(
+    "-cpuprofile", "-d", "-e", "-l", "-r", "-s", "-w"
+)
+
+--------------------------------------------------------------------------------
+clink.arg.register_parser("go", go_parser)
+clink.arg.register_parser("go", go_help_parser)
+clink.arg.register_parser("godoc", godoc_parser)
+clink.arg.register_parser("gofmt", gofmt_parser)
+
+--------------------------------------------------------------------------------
+-- hg.lua
+--
+
+--
+-- Copyright (c) 2012 Martin Ridgers
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+-- SOFTWARE.
+--
+
+--------------------------------------------------------------------------------
+local hg_tree = {
+    "add", "addremove", "annotate", "archive", "backout", "bisect", "bookmarks",
+    "branch", "branches", "bundle", "cat", "clone", "commit", "copy", "diff",
+    "export", "forget", "grep", "heads", "help", "identify", "import",
+    "incoming", "init", "locate", "log", "manifest", "merge", "outgoing",
+    "parents", "paths", "pull", "push", "recover", "remove", "rename", "resolve",
+    "revert", "rollback", "root", "serve", "showconfig", "status", "summary",
+    "tag", "tags", "tip", "unbundle", "update", "verify", "version", "graft",
+    "phases"
+}
+
+clink.arg.register_parser("hg", hg_tree)
+
+-- vim: expandtab
+
+--------------------------------------------------------------------------------
+-- p4.lua
+--
+
+--
+-- Copyright (c) 2012 Martin Ridgers
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+-- SOFTWARE.
+--
+
+--------------------------------------------------------------------------------
+local p4_tree = {
+    "add", "annotate", "attribute", "branch", "branches", "browse", "change",
+    "changes", "changelist", "changelists", "client", "clients", "copy",
+    "counter", "counters", "cstat", "delete", "depot", "depots", "describe",
+    "diff", "diff2", "dirs", "edit", "filelog", "files", "fix", "fixes",
+    "flush", "fstat", "grep", "group", "groups", "have", "help", "info",
+    "integrate", "integrated", "interchanges", "istat", "job", "jobs", "label",
+    "labels", "labelsync", "legal", "list", "lock", "logger", "login",
+    "logout", "merge", "move", "opened", "passwd", "populate", "print",
+    "protect", "protects", "reconcile", "rename", "reopen", "resolve",
+    "resolved", "revert", "review", "reviews", "set", "shelve", "status",
+    "sizes", "stream", "streams", "submit", "sync", "tag", "tickets", "unlock",
+    "unshelve", "update", "user", "users", "where", "workspace", "workspaces"
+}
+
+clink.arg.register_parser("p4", p4_tree)
+
+--------------------------------------------------------------------------------
+local p4vc_tree = {
+    "help", "branchmappings", "branches", "diff", "groups", "branch", "change",
+    "client", "workspace", "depot", "group", "job", "label", "user", "jobs",
+    "labels", "pendingchanges", "resolve", "revisiongraph", "revgraph",
+    "streamgraph", "streams", "submit", "submittedchanges", "timelapse",
+    "timelapseview", "tlv", "users", "workspaces", "clients", "shutdown"
+}
+
+clink.arg.register_parser("p4vc", p4vc_tree)
+
+-- vim: expandtab
+
+--------------------------------------------------------------------------------
+-- powershell.lua
+--
+
+--
+-- Copyright (c) 2013 Martin Ridgers
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+-- SOFTWARE.
+--
+
+--------------------------------------------------------------------------------
+local function powershell_prompt_filter()
+    local l, r, path = clink.prompt.value:find("([a-zA-Z]:\\.*)> $")
+    if path ~= nil then
+        clink.chdir(path)
+    end
+end
+
+--------------------------------------------------------------------------------
+if clink.get_host_process() == "powershell.exe" then
+    clink.prompt.register_filter(powershell_prompt_filter, -493)
+end
+
+--------------------------------------------------------------------------------
+-- self.lua
+--
+
+--
+-- Copyright (c) 2012 Martin Ridgers
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+-- SOFTWARE.
+--
+
+--------------------------------------------------------------------------------
+local null_parser = clink.arg.new_parser()
+null_parser:disable_file_matching()
+
+local inject_parser = clink.arg.new_parser()
+inject_parser:set_flags(
+    "--help",
+    "--nohostcheck",
+    "--pid",
+    "--profile",
+    "--quiet",
+    "--scripts"
+)
+
+local autorun_dashdash_parser = clink.arg.new_parser()
+autorun_dashdash_parser:set_arguments({ "--" .. inject_parser })
+
+local autorun_parser = clink.arg.new_parser()
+autorun_parser:set_flags("--allusers", "--help")
+autorun_parser:set_arguments(
+    {
+        "install"   .. autorun_dashdash_parser,
+        "uninstall" .. null_parser,
+        "show"      .. null_parser,
+        "set"
+    }
+)
+
+local set_parser = clink.arg.new_parser()
+set_parser:disable_file_matching()
+set_parser:set_flags("--help")
+set_parser:set_arguments(
+    {
+        "ansi_code_support",
+        "ctrld_exits",
+        "esc_clears_line",
+        "exec_match_style",
+        "history_dupe_mode",
+        "history_expand_mode",
+        "history_file_lines",
+        "history_ignore_space",
+        "history_io",
+        "match_colour",
+        "prompt_colour",
+        "space_prefix_match_files",
+        "strip_crlf_on_paste",
+        "terminate_autoanswer",
+        "use_altgr_substitute",
+    }
+)
+
+local self_parser = clink.arg.new_parser()
+self_parser:set_arguments(
+    {
+        "inject" .. inject_parser,
+        "autorun" .. autorun_parser,
+        "set" .. set_parser,
+    }
+)
+
+clink.arg.register_parser("clink", self_parser)
+clink.arg.register_parser("clink_x86", self_parser)
+clink.arg.register_parser("clink_x64", self_parser)
+
+-- vim: expandtab
+
+--------------------------------------------------------------------------------
+-- set.lua
+--
+
+--
+-- Copyright (c) 2012 Martin Ridgers
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+-- SOFTWARE.
+--
+
+--------------------------------------------------------------------------------
+local function set_match_generator(word)
+    -- Skip this generator if first is in the rvalue.
+    local leading = rl_state.line_buffer:sub(1, rl_state.first - 1)
+    if leading:find("=") then
+        return false
+    end
+
+    -- Enumerate environment variables and check for potential matches.
+    local matches = {}
+    for _, name in ipairs(clink.get_env_var_names()) do
+        if clink.is_match(word, name) then
+            table.insert(matches, name:lower())
+        end
+    end
+
+    clink.suppress_char_append()
+    return matches
+end
+
+--------------------------------------------------------------------------------
+clink.arg.register_parser("set", set_match_generator)
+
+-- vim: expandtab
+
+--------------------------------------------------------------------------------
+-- svn.lua
+--
+
+--
+-- Copyright (c) 2012 Martin Ridgers
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+-- SOFTWARE.
+--
+
+--------------------------------------------------------------------------------
+local svn_tree = {
+    "add", "blame", "praise", "annotate", "ann", "cat", "changelist", "cl",
+    "checkout", "co", "cleanup", "commit", "ci", "copy", "cp", "delete", "del",
+    "remove", "rm", "diff", "di", "export", "help", "h", "import", "info",
+    "list", "ls", "lock", "log", "merge", "mergeinfo", "mkdir", "move", "mv",
+    "rename", "ren", "propdel", "pdel", "pd", "propedit", "pedit", "pe",
+    "propget", "pget", "pg", "proplist", "plist", "pl", "propset", "pset", "ps",
+    "resolve", "resolved", "revert", "status", "stat", "st", "switch", "sw",
+    "unlock", "update", "up"
+}
+
+clink.arg.register_parser("svn", svn_tree)
+
+-- vim: expandtab

二进制
clink/clink_dll_x64.dll


二进制
clink/clink_dll_x86.dll


+ 72 - 0
clink/clink_inputrc_base

@@ -0,0 +1,72 @@
+set bell-style                  visible
+set completion-ignore-case      on
+set completion-map-case         on
+set completion-display-width    106
+set output-meta                 on
+set skip-completed-text         on
+set convert-meta                on
+
+"\e`s": backward-word           # ctrl-left
+"\e`t": forward-word            # ctrl-right
+"\e`O": end-of-line             # end
+"\e`G": beginning-of-line       # home
+"\e`S": delete-char             # del
+"\e`c": page-up                 # shift page-up
+"\e`u": kill-line               # ctrl+end
+"\e`w": backward-kill-line      # ctrl+home
+"\e`I": history-search-backward # page-up
+"\e`Q": history-search-forward  # page-down
+
+set keymap emacs
+"\t":               clink-completion-shim
+C-v:                paste-from-clipboard
+C-q:                reload-lua-state
+C-z:                undo
+M-h:                show-rl-help
+M-C-c:              copy-line-to-clipboard
+C-c:                ctrl-c
+M-a:                "..\\"
+
+set keymap vi-insert
+"\t":               clink-completion-shim
+C-v:                paste-from-clipboard
+C-z:                undo
+M-h:                show-rl-help
+M-C-c:              copy-line-to-clipboard
+C-c:                ctrl-c
+M-a:                "..\\"
+
+set keymap vi-move
+C-v:                paste-from-clipboard
+C-z:                undo
+M-h:                show-rl-help
+M-C-c:              copy-line-to-clipboard
+C-c:                ctrl-c
+M-a:                "..\\"
+
+$if cmd.exe
+    set keymap emacs
+    "\e`U":         up-directory
+    M-C-u:          up-directory
+    M-C-e:          expand-env-vars
+
+    set keymap vi-insert
+    "\e`U":         up-directory
+    M-C-u:          up-directory
+    M-C-e:          expand-env-vars
+
+    set keymap vi-move
+    "\e`U":         up-directory
+    M-C-u:          up-directory
+    M-C-e:          expand-env-vars
+$endif
+
+set keymap emacs
+
+# Uncomment these two lines for vanilla cmd.exe style completion.
+# "\t": clink-menu-completion-shim
+# "\e`Z": clink-backward-menu-completion-shim
+
+$include ~/clink_inputrc
+$include ~/_inputrc
+$include ~/.inputrc

二进制
clink/clink_x64.exe


二进制
clink/clink_x86.exe


+ 1 - 0
clink/profile/.history

@@ -0,0 +1 @@
+Ls

+ 116 - 0
clink/profile/settings

@@ -0,0 +1,116 @@
+# name: Pressing Ctrl-D exits session
+# type: bool
+# Ctrl-D exits cmd.exe when it is pressed on an empty line.
+ctrld_exits = 1
+
+# name: Toggle if pressing Esc clears line
+# type: bool
+# Clink clears the current line when Esc is pressed (unless Readline's Vi mode
+# is enabled).
+esc_clears_line = 1
+
+# name: Match display colour
+# type: int
+# Colour to use when displaying matches. A value less than 0 will be the
+# opposite brightness of the default colour.
+match_colour = -1
+
+# name: Executable match style
+# type: enum
+#  0 = PATH only
+#  1 = PATH and CWD
+#  2 = PATH, CWD, and directories
+# Changes how Clink will match executables when there is no path separator on
+# the line. 0 = PATH only, 1 = PATH and CWD, 2 = PATH, CWD, and directories. In
+# all cases both executables and directories are matched when there is a path
+# separator present. A value of -1 will disable executable matching completely.
+exec_match_style = 2
+
+# name: Whitespace prefix matches files
+# type: bool
+# If the line begins with whitespace then Clink bypasses executable matching and
+# will match all files and directories instead.
+space_prefix_match_files = 1
+
+# name: Colour of the prompt
+# type: int
+# Surrounds the prompt in ANSI escape codes to set the prompt's colour. Disabled
+# when the value is less than 0.
+prompt_colour = -1
+
+# name: Auto-answer terminate prompt
+# type: enum
+#  0 = Disabled
+#  1 = Answer 'Y'
+#  2 = Answer 'N'
+# Automatically answers cmd.exe's 'Terminate batch job (Y/N)?' prompts. 0 =
+# disabled, 1 = answer 'Y', 2 = answer 'N'.
+terminate_autoanswer = 0
+
+# name: Lines of history saved to disk
+# type: int
+# When set to a positive integer this is the number of lines of history that
+# will persist when Clink saves the command history to disk. Use 0 for infinite
+# lines and <0 to disable history persistence.
+history_file_lines = 10000
+
+# name: Skip adding lines prefixed with whitespace
+# type: bool
+# Ignore lines that begin with whitespace when adding lines in to the history.
+history_ignore_space = 0
+
+# name: Controls how duplicate entries are handled
+# type: enum
+#  0 = Always add
+#  1 = Ignore
+#  2 = Erase previous
+# If a line is a duplicate of an existing history entry Clink will erase the
+# duplicate when this is set 2. A value of 1 will not add duplicates to the
+# history and a value of 0 will always add lines. Note that history is not
+# deduplicated when reading/writing to disk.
+history_dupe_mode = 2
+
+# name: Read/write history file each line edited
+# type: bool
+# When non-zero the history will be read from disk before editing a new line and
+# written to disk afterwards.
+history_io = 0
+
+# name: Sets how command history expansion is applied
+# type: enum
+#  0 = Off
+#  1 = On
+#  2 = Not in single quotes
+#  3 = Not in double quote
+#  4 = Not in any quotes
+# The '!' character in an entered line can be interpreted to introduce words
+# from the history. This can be enabled and disable by setting this value to 1
+# or 0. Values or 2, 3 or 4 will skip any ! character quoted in single, double,
+# or both quotes respectively.
+history_expand_mode = 4
+
+# name: Support Windows' Ctrl-Alt substitute for AltGr
+# type: bool
+# Windows provides Ctrl-Alt as a substitute for AltGr, historically to support
+# keyboards with no AltGr key. This may collide with some of Readline's
+# bindings.
+use_altgr_substitute = 1
+
+# name: Strips CR and LF chars on paste
+# type: enum
+#  0 = Paste unchanged
+#  1 = Strip
+#  2 = As space
+# Setting this to a value >0 will make Clink strip CR and LF characters from
+# text pasted into the current line. Set this to 1 to strip all newline
+# characters and 2 to replace them with a space.
+strip_crlf_on_paste = 2
+
+# name: Enables basic ANSI escape code support
+# type: bool
+# When printing the prompt, Clink has basic built-in support for SGR ANSI escape
+# codes to control the text colours. This is automatically disabled if a third
+# party tool is detected that also provides this facility. It can also be
+# disabled by setting this to 0.
+ansi_code_support = 1
+

+ 2 - 1
package.json

@@ -44,7 +44,8 @@
     "productName": "Terminus",
     "compression": "normal",
     "extraResources": [
-      "builtin-plugins"
+      "builtin-plugins",
+      "clink"
     ],
     "win": {
       "icon": "./build/windows/icon.ico"

+ 19 - 2
terminus-terminal/src/buttonProvider.ts

@@ -1,5 +1,6 @@
+import * as path from 'path'
 import { Injectable } from '@angular/core'
-import { HotkeysService, ToolbarButtonProvider, IToolbarButton, AppService, ConfigService } from 'terminus-core'
+import { HotkeysService, ToolbarButtonProvider, IToolbarButton, AppService, ConfigService, ElectronService } from 'terminus-core'
 
 import { SessionsService } from './services/sessions.service'
 import { TerminalTabComponent } from './components/terminalTab.component'
@@ -10,6 +11,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
         private app: AppService,
         private sessions: SessionsService,
         private config: ConfigService,
+        private electron: ElectronService,
         hotkeys: HotkeysService,
     ) {
         super()
@@ -26,9 +28,24 @@ export class ButtonProvider extends ToolbarButtonProvider {
             cwd = await this.app.activeTab.session.getWorkingDirectory()
         }
         let command = this.config.store.terminal.shell
+        let args = []
+        // TODO move this?
+        if (command === '~clink~') {
+            command = 'cmd.exe'
+            args = [
+                '/k',
+                path.join(
+                    path.dirname(this.electron.app.getPath('exe')),
+                    (process.platform == 'darwin') ? '../Resources' : 'resources',
+                    'clink',
+                    `clink_${process.arch}.exe`,
+                ),
+                'inject',
+            ]
+        }
         this.app.openNewTab(
             TerminalTabComponent,
-            { session: await this.sessions.createNewSession({ command, cwd }) }
+            { session: await this.sessions.createNewSession({ command, args, cwd }) }
         )
     }
 

+ 2 - 1
terminus-terminal/src/components/terminalSettingsTab.component.ts

@@ -55,7 +55,8 @@ export class TerminalSettingsTabComponent {
         }
         if (this.hostApp.platform === Platform.Windows) {
             this.shells = [
-                { name: 'CMD', command: 'cmd.exe' },
+                { name: 'CMD (clink)', command: '~clink~' },
+                { name: 'CMD (stock)', command: 'cmd.exe' },
                 { name: 'PowerShell', command: 'powershell.exe' },
             ]
             const wslPath = `${process.env.windir}\\system32\\bash.exe`

+ 1 - 1
terminus-terminal/src/config.ts

@@ -36,7 +36,7 @@ export class TerminalConfigProvider extends ConfigProvider {
         [Platform.Windows]: {
             terminal: {
                 font: 'Consolas',
-                shell: 'cmd.exe',
+                shell: '~clink~',
             },
             hotkeys: {
                 'new-tab': [

+ 2 - 0
terminus-terminal/src/persistenceProviders.ts

@@ -7,6 +7,8 @@ import { Injectable } from '@angular/core'
 import { Logger, LogService } from 'terminus-core'
 import { SessionOptions, SessionPersistenceProvider } from './api'
 
+declare function delay(ms: number): Promise<void>
+
 interface IChildProcess {
     pid: number
     ppid: number

部分文件因为文件数量过多而无法显示