]> git.cworth.org Git - apitrace/commitdiff
Bundle less (version 444) for Windows.
authorJosé Fonseca <jose.r.fonseca@gmail.com>
Thu, 15 Mar 2012 08:35:50 +0000 (08:35 +0000)
committerJosé Fonseca <jose.r.fonseca@gmail.com>
Sat, 17 Mar 2012 12:34:28 +0000 (12:34 +0000)
The 'more' pager included in Windows is useless.  Furthermore, less also
does ANSI escape code conversion for us on Windows.

60 files changed:
CMakeLists.txt
thirdparty/less.patch [new file with mode: 0644]
thirdparty/less/CMakeLists.txt [new file with mode: 0644]
thirdparty/less/COPYING [new file with mode: 0644]
thirdparty/less/INSTALL [new file with mode: 0644]
thirdparty/less/LICENSE [new file with mode: 0644]
thirdparty/less/NEWS [new file with mode: 0644]
thirdparty/less/README [new file with mode: 0644]
thirdparty/less/brac.c [new file with mode: 0644]
thirdparty/less/ch.c [new file with mode: 0644]
thirdparty/less/charset.c [new file with mode: 0644]
thirdparty/less/charset.h [new file with mode: 0644]
thirdparty/less/cmd.h [new file with mode: 0644]
thirdparty/less/cmdbuf.c [new file with mode: 0644]
thirdparty/less/command.c [new file with mode: 0644]
thirdparty/less/cvt.c [new file with mode: 0644]
thirdparty/less/decode.c [new file with mode: 0644]
thirdparty/less/defines.h.in [new file with mode: 0644]
thirdparty/less/defines.wn [new file with mode: 0644]
thirdparty/less/edit.c [new file with mode: 0644]
thirdparty/less/filename.c [new file with mode: 0644]
thirdparty/less/forwback.c [new file with mode: 0644]
thirdparty/less/funcs.h [new file with mode: 0644]
thirdparty/less/help.c [new file with mode: 0644]
thirdparty/less/ifile.c [new file with mode: 0644]
thirdparty/less/input.c [new file with mode: 0644]
thirdparty/less/jump.c [new file with mode: 0644]
thirdparty/less/less.h [new file with mode: 0644]
thirdparty/less/less.hlp [new file with mode: 0644]
thirdparty/less/lessecho.c [new file with mode: 0644]
thirdparty/less/lesskey.c [new file with mode: 0644]
thirdparty/less/lesskey.h [new file with mode: 0644]
thirdparty/less/lglob.h [new file with mode: 0644]
thirdparty/less/line.c [new file with mode: 0644]
thirdparty/less/linenum.c [new file with mode: 0644]
thirdparty/less/lsystem.c [new file with mode: 0644]
thirdparty/less/main.c [new file with mode: 0644]
thirdparty/less/mark.c [new file with mode: 0644]
thirdparty/less/mkhelp.c [new file with mode: 0644]
thirdparty/less/optfunc.c [new file with mode: 0644]
thirdparty/less/option.c [new file with mode: 0644]
thirdparty/less/option.h [new file with mode: 0644]
thirdparty/less/opttbl.c [new file with mode: 0644]
thirdparty/less/os.c [new file with mode: 0644]
thirdparty/less/output.c [new file with mode: 0644]
thirdparty/less/pattern.c [new file with mode: 0644]
thirdparty/less/pattern.h [new file with mode: 0644]
thirdparty/less/pckeys.h [new file with mode: 0644]
thirdparty/less/position.c [new file with mode: 0644]
thirdparty/less/position.h [new file with mode: 0644]
thirdparty/less/prompt.c [new file with mode: 0644]
thirdparty/less/regexp.c [new file with mode: 0644]
thirdparty/less/regexp.h [new file with mode: 0644]
thirdparty/less/screen.c [new file with mode: 0644]
thirdparty/less/scrsize.c [new file with mode: 0644]
thirdparty/less/search.c [new file with mode: 0644]
thirdparty/less/signal.c [new file with mode: 0644]
thirdparty/less/tags.c [new file with mode: 0644]
thirdparty/less/ttyin.c [new file with mode: 0644]
thirdparty/less/version.c [new file with mode: 0644]

index 31daa303531a45ea1439913c05050df50b9c6db2..a9b8f33c88b066924860aa42c830f754de0d5e3b 100755 (executable)
@@ -169,6 +169,10 @@ if (MSVC)
     set (GETOPT_LIBRARIES getopt_bundled)
 endif ()
 
+if (WIN32)
+    add_subdirectory (thirdparty/less)
+endif ()
+
 # The Qt website provides binaries for Windows and MacOSX, and they are
 # automatically found by cmake without any manual intervention.  The situation
 # for QJSON is substantially different: there are no binaries for QJSON
diff --git a/thirdparty/less.patch b/thirdparty/less.patch
new file mode 100644 (file)
index 0000000..f173f1e
--- /dev/null
@@ -0,0 +1,100 @@
+diff -du less.orig/defines.wn less/defines.wn
+--- less.orig/defines.wn       2011-04-10 06:59:14.000000000 +0100
++++ less/defines.wn    2012-03-15 08:32:53.848812020 +0000
+@@ -223,7 +223,11 @@
+ /* Define MUST_DEFINE_ERRNO if you have errno but it is not define 
+  * in errno.h */
+ #define HAVE_ERRNO 1
++#if defined(__MINGW32__) 
++#define MUST_DEFINE_ERRNO 0
++#else
+ #define MUST_DEFINE_ERRNO 1
++#endif
+ /* Define HAVE_SYS_ERRLIST if you have the sys_errlist[] variable */
+ #define HAVE_SYS_ERRLIST 1
+@@ -326,14 +330,18 @@
+ #define HAVE_UNISTD_H 0
+ /* Define if you have the <values.h> header file.  */
+-#ifdef _MSC_VER
++#if defined(_MSC_VER) || defined(__MINGW32__)
+ #define HAVE_VALUES_H 0
+ #else
+ #define HAVE_VALUES_H 1
+ #endif
++#if !defined(__MINGW32__)
+ #define       popen   _popen
+ #define       pclose  _pclose
+ #define snprintf      _snprintf
++#endif
++#if defined(_MSC_VER)
+ #pragma warning(disable:4996)
++#endif
+diff -du less.orig/filename.c less/filename.c
+--- less.orig/filename.c       2011-04-11 23:04:22.000000000 +0100
++++ less/filename.c    2012-03-15 08:23:13.949936491 +0000
+@@ -18,7 +18,7 @@
+ #include "lglob.h"
+ #if MSDOS_COMPILER
+ #include <dos.h>
+-#if MSDOS_COMPILER==WIN32C && !defined(_MSC_VER)
++#if MSDOS_COMPILER==WIN32C && defined(__BORLANDC__)
+ #include <dir.h>
+ #endif
+ #if MSDOS_COMPILER==DJGPPC
+@@ -563,7 +563,9 @@
+ #if HAVE_POPEN
++#if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C
+ FILE *popen();
++#endif
+ /*
+  * Execute a shell command.
+diff -du less.orig/lglob.h less/lglob.h
+--- less.orig/lglob.h  2011-01-06 00:30:14.000000000 +0000
++++ less/lglob.h       2012-03-15 08:22:10.401621327 +0000
+@@ -57,7 +57,7 @@
+                                       char ext[_MAX_EXT];     \
+                                       int handle;
+ #else
+-#if MSDOS_COMPILER==WIN32C && defined(_MSC_VER)
++#if MSDOS_COMPILER==WIN32C && (defined(_MSC_VER) || defined(__MINGW2__))
+ #define       GLOB_FIRST_NAME(filename,fndp,h) h = _findfirst(filename, fndp)
+ #define       GLOB_FIRST_FAILED(handle)       ((handle) == -1)
+@@ -73,7 +73,7 @@
+                                       long handle;
+ #else
+-#if MSDOS_COMPILER==WIN32C && !defined(_MSC_VER) /* Borland C for Windows */
++#if MSDOS_COMPILER==WIN32C && defined(__BORLANDC__) /* Borland C for Windows */
+ #define       GLOB_FIRST_NAME(filename,fndp,h) h = findfirst(filename, fndp, ~FA_LABEL)
+ #define       GLOB_FIRST_FAILED(handle)       ((handle) != 0)
+diff -du less.orig/lsystem.c less/lsystem.c
+--- less.orig/lsystem.c        2011-04-11 23:04:22.000000000 +0100
++++ less/lsystem.c     2012-03-15 08:23:56.518147471 +0000
+@@ -20,7 +20,7 @@
+ #if MSDOS_COMPILER
+ #include <dos.h>
+-#ifdef _MSC_VER
++#if defined(_MSC_VER) || defined(__MINGW32__)
+ #include <direct.h>
+ #define setdisk(n) _chdrive((n)+1)
+ #else
+@@ -292,7 +292,9 @@
+ {
+       register FILE *f;
+       register int c;
++#if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C
+       extern FILE *popen();
++#endif
+       /*
+        * This is structured much like lsystem().
diff --git a/thirdparty/less/CMakeLists.txt b/thirdparty/less/CMakeLists.txt
new file mode 100644 (file)
index 0000000..95e7175
--- /dev/null
@@ -0,0 +1,36 @@
+add_definitions (-DNDEBUG)
+if (WIN32)
+    add_definitions (-DWIN32 -D_CONSOLE)
+    configure_file (defines.wn ${CMAKE_CURRENT_BINARY_DIR}/defines.h COPYONLY)
+else ()
+    # XXX unsupported
+endif ()
+
+if (MSVC)
+    add_definitions (-wd4131) # uses old-style declarator
+endif ()
+
+include_directories (${CMAKE_CURRENT_BINARY_DIR})
+
+add_executable (less
+       main.c screen.c brac.c ch.c charset.c cmdbuf.c
+       command.c cvt.c decode.c edit.c filename.c forwback.c
+       help.c ifile.c input.c jump.c line.c linenum.c
+       lsystem.c mark.c optfunc.c option.c opttbl.c os.c
+       output.c pattern.c position.c prompt.c search.c signal.c
+       tags.c ttyin.c version.c regexp.c
+)
+
+add_executable (lesskey lesskey version)
+add_executable (lessecho lessecho version)
+
+install (
+    TARGETS less lesskey lessecho
+    RUNTIME DESTINATION bin
+)
+
+install (
+    FILES LICENSE
+    DESTINATION doc
+    RENAME LICENSE.less
+)
diff --git a/thirdparty/less/COPYING b/thirdparty/less/COPYING
new file mode 100644 (file)
index 0000000..94a9ed0
--- /dev/null
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/thirdparty/less/INSTALL b/thirdparty/less/INSTALL
new file mode 100644 (file)
index 0000000..c2ab230
--- /dev/null
@@ -0,0 +1,186 @@
+   This file describes how to build and install less using 
+the "configure" script.  This only works on Unix systems.  
+To install on other systems, read the README file.
+
+
+Basic Installation
+==================
+
+   These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+   The file `configure.in' is used to create `configure' by a program
+called `autoconf'.  You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes awhile.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  You can give `configure'
+initial values for variables by setting them in the environment.  Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+     CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+     env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory.  After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+   By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+   Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+   There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on.  Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+     CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+   If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+   If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+   `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+     Use and save the results of the tests in FILE instead of
+     `./config.cache'.  Set FILE to `/dev/null' to disable caching, for
+     debugging `configure'.
+
+`--help'
+     Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`--version'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
diff --git a/thirdparty/less/LICENSE b/thirdparty/less/LICENSE
new file mode 100644 (file)
index 0000000..c7168e7
--- /dev/null
@@ -0,0 +1,27 @@
+                          Less License
+                          ------------
+
+Less
+Copyright (C) 1984-2011  Mark Nudelman
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice in the documentation and/or other materials provided with 
+   the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
+PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/thirdparty/less/NEWS b/thirdparty/less/NEWS
new file mode 100644 (file)
index 0000000..eb38025
--- /dev/null
@@ -0,0 +1,820 @@
+
+                     NEWS about less
+
+======================================================================
+
+  For the latest news about less, see the "less" Web page:
+      http://www.greenwoodsoftware.com/less
+  You can also download the latest version of less from there.
+
+  To report bugs, suggestions or comments, send email to 
+  bug-less@gnu.org or markn@greenwoodsoftware.com.
+
+======================================================================
+
+       Major changes between "less" versions 443 and 444
+
+* Fix bug in unget handling that can cause strange effects on the
+  command line.
+
+* Remove vestiges of obsolete -l option that can cause a crash.
+
+======================================================================
+
+       Major changes between "less" versions 436 and 443
+
+* Change search behavior such that when a search is given an explicit 
+  pattern, the entire displayed screen is included in the search and 
+  not just the portion after the target line.
+
+* Add -A option to change search behavior to the old way: only
+  the portion of the screen after the target line is searched.
+
+* Add %F formatting to prompt strings, replaced by the last component
+  of the input file.
+
+* Control-G while editing a command exits the command.
+
+* Less now exits with status 2 if control-C is pressed and -K is in effect.
+
+* Fix "ungetc overflow" when passing long commands via the -p option.
+
+* Fix bug in using line filtering via the & command 
+  in combination with -i and -I.
+
+* Fix bug in handling negative arguments to the -j option.
+
+* Fix bug in handling %t in prompt strings.
+
+* Improve handling of long option names.
+
+* Improve percentage calculation for very large files.
+
+======================================================================
+
+       Major changes between "less" versions 429 and 436
+
+* Don't pass "-" to non-pipe LESSOPEN unless it starts with "-".
+
+* Allow a fraction as the argument to the -# (--shift) option.
+
+* Fix highlight bug when underlined/overstruck text matches at end of line.
+
+* Fix non-regex searches with ctrl-R.
+
+======================================================================
+
+       Major changes between "less" versions 424 and 429
+
+* LESSOPEN pipe will now be used on standard input, if the LESSOPEN
+  environment variable begins with "|-".
+
+* The -D option with one number now means use the normal background color.
+
+* Don't change permissions on history file if it is not a regular file.
+
+* Fix non-ANSI-compliant code that caused problems with some compilers.
+
+* Fix binary file detection in UTF-8 mode.
+
+* Fix display problems with long lines on "ignaw" terminals.
+
+* Fix problem interrupting the line number calculation for initial prompt.
+
+* Fix SGR emulation when dealing with multiple attributes (eg. bold+underline).
+
+* Fix highlight bug when searching for underlined/overstruck text.
+
+======================================================================
+
+       Major changes between "less" versions 418 and 424
+
+* New "&" command allows filtering of lines based on a pattern.
+
+* Status column now displays a search match, even if the matched
+  string is scrolled off screen because -S is in effect.
+
+* Improve behavior of -F option.
+
+* Allow CSI character (0x9B) to work in UTF-8 mode.
+
+* Output carriage return at startup in case terminal doesn't default
+  to column 1.
+
+* Fix bug in '' (quote, quote) command after G command.
+
+======================================================================
+
+       Major changes between "less" versions 416 and 418
+
+* Color escape sequences are now supported in WIN32 build.
+
+* Makefile now uses EXEEXT feature of autoconf.
+
+* Fix search bug when using -R and text contains ANSI color escape sequences.
+
+* Fix crash when using -r with UTF-8 text containing 0x9B bytes.
+
+* Fix display bug when using ' command to move less than one page forward.
+
+* Update GPL to version 3.
+
+======================================================================
+
+       Major changes between "less" versions 409 and 416
+
+* New --follow-name option makes F command follow the name of a file
+  rather than the file descriptor if an open file is renamed.
+
+* Make searching with -i/-I work correctly with non-ASCII text.
+
+* Fix DJGPP build.
+
+======================================================================
+
+       Major changes between "less" versions 406 and 409
+
+* Support CSI escape sequences, like SGR escape sequences.
+
+* Fix bug which caused screen to fail to repaint when window is resized.
+
+* Fix bug in using -i and -I flags with non-ASCII text.
+
+* Fix configure bug on systems which don't support langinfo.h.
+
+* Fix crash when searching text containing certain invalid UTF-8 sequences.
+
+======================================================================
+
+       Major changes between "less" versions 394 and 406
+
+* Allow decimal point in number for % (percent) command.
+
+* Allow decimal point in number for -j option (fraction of screen height).
+
+* Make n command fetch previous pattern from history file on first search.
+
+* Don't rewrite history file if it has not changed.
+
+* Don't move to bottom of screen on first page.
+
+* Don't output extraneous newlines, so copy & pasting lines from the
+  output works better.
+
+* The -c option has been made identical with the -C option.
+
+* Allow "/dev/null" as synomym for "-" in LESSHISTFILE to indicate
+  that no history file should be used.
+
+* Search can now find text which follows a null byte, if the PCRE
+  library is used, or if no-regex searching (ctrl-R) is used.
+
+* Better compatibility with POSIX more specification.
+
+* Make -f work for directories.
+
+* Make "t" cmd traverse tags in the correct order.
+
+* Allow a few binary characters in the input file before warning
+  that the file is binary.
+
+* Don't warn that file is binary if it merely contains ANSI color sequences
+  and -R is in effect.
+
+* Update Unicode character tables.
+
+* Support DESTDIR in Makefile.
+
+* Fix bug when filename contains certain shell metacharacters such as "$".
+
+* Fix bug when resizing the window while waiting for input from a pipe.
+
+* Fix configure bugs.
+
+======================================================================
+
+       Major changes between "less" versions 382 and 394
+
+* Add history file to save search and shell command history between
+  invocations of less.
+
+* Improve behavior of history list for search and shell commands.
+
+* Add -K (or --quit-on-intr) option to make less exit immediately on ctrl-C.
+
+* Improve handling of UTF-8 files and commands, including better
+  line wrapping and handling double-width chars.
+
+* Added LESSUTFBINFMT environment variable to control display of
+  non-printable characters in a UTF-8 file.
+
+* Add --with-secure option to configure, to make it easier to
+  build a secure version of less.
+
+* Show search matches in the status column even if search highlights
+  are disabled via the -G option or the ESC-u command.
+
+* Improve performance when the file contains very long lines.
+
+* Add "windows" charset.
+
+* Add man page for lessecho.
+
+* Add support for erase2 character, treated same as erase.
+
+* Use ASCII lowercase/uppercase logic when operating on the command line.
+
+* Update makefile for Borland C++ 5.5.1.
+
+* Fix bug in calculating number of pages for %D prompt.
+
+* Fix bug in handling tag file error.
+
+* Fix obscure bug if input file is deleted while viewing help.
+
+* Fix bug handling filenames which include square brackets.
+
+* Fix possible buffer overflow in "global" tag search.
+
+* Fix possible buffer overflow in usage of LESSOPEN and LESSCLOSE.
+
+* Fix buffer overflow in reverse search.
+
+======================================================================
+
+       Major changes between "less" versions 381 and 382
+
+* Removed some old copyrighted code.
+  This probably breaks OS/9 support.
+
+======================================================================
+
+       Major changes between "less" versions 378 and 381
+
+* New -L option to disable LESSOPEN processing.
+
+* Further support for large (64 bit) file addressing.
+  Large file support is now set up by the configure script.
+
+* Use autoconf 2.54.
+  Replace configure.in, acconfig.h, defines.h.top with configure.ac.
+
+* Overstriking underscore with underscore is now bold or underlined 
+  depending on context.
+
+* Use only 7 spaces for line numbers in -N mode, if possible.
+
+* Fix some bugs in handling overstriking in UTF-8 files.
+
+* Fix some nroff issues in the man page.
+
+======================================================================
+
+       Major changes between "less" versions 376 and 378
+
+* Bug fixes:
+  Default buffer space is now 64K as documented.
+  Search highlighting works properly when used with -R.
+  Windows version works properly when input file contains carriage returns.
+  Clean up some compiler warnings.
+
+======================================================================
+
+       Major changes between "less" versions 358 and 376
+
+* -x option can now specify multiple variable-width tab stops.
+
+* -X option no longer disables keypad initialization.
+  New option --no-keypad disables keypad initialization.
+
+* New commands t and T step through multiple tag matches.
+  Added support for "global(1)" tags
+  (see http://www.gnu.org/software/global/global.html).
+
+* New prompt style set by option -Pw defines the message printed 
+  while waiting for data in the F command.
+
+* System-wide lesskey file now defaults to sysless in etc directory 
+  instead of .sysless in bin directory.
+  Use "configure --sysconfdir=..." to change it.
+  (For backwards compatibility, .sysless in bin is still recognized.)
+
+* Pressing RightArrow or LeftArrow while entering a number now shifts
+  the display N columns rather than editing the number itself.
+
+* Status column (enabled with -J) now shows search results.
+
+* Windows version sets window title.
+
+* Default LESSCHARSET for MS-DOS versions is now "dos".
+
+* Searching works better with ANSI (SGR) escape sequences.
+  ANSI color escape sequences are now supported in the MS-DOS (DJGPP) version.
+
+* Improved performance in reading very large pipes.
+
+* Eliminated some dependencies on file offets being 32 bits.
+
+* Fixed problems when viewing files with very long lines.
+
+* Fixed overstriking in UTF-8 mode, and overstriking tabs.
+
+* Improved horizontal shifting of text using -R option with ANSI color.
+
+* Improved handling of filenames containing shell metacharacters.
+
+* Some fixes for EBCDIC systems.
+
+* Some fixes for OS/2 systems.
+
+======================================================================
+
+       Major changes between "less" versions 354 and 358
+
+* Add -J (--status-column) option to display a status column.
+
+* Add -# (--shift) option to set default horizontal shift distance.
+  Default horizontal shift distance is now one-half screen width.
+
+* Horizontal shifting does not shift line numbers if -N is in effect.
+
+* Horizontal shifting acts as though -S were set, to avoid confusion.
+
+======================================================================
+
+
+       Major changes between "less" versions 352 and 354
+
+* Allow space after numeric-valued command line options.
+
+* Fix problem with configuring terminal libraries on some systems.
+
+* Add support for PCRE regular expression library.
+
+* Add --with-regex option to configure to allow manually selecting
+  a regular expression library.
+
+* Fix bug compiling with SECURE = 1.
+
+======================================================================
+
+
+       Major changes between "less" versions 346 and 352
+
+* Enable UTF-8 if "UTF-8" appears in locale-related environment variables.
+
+* Add --with-editor option to configure script.
+
+* The -M prompt and = message now show the top and bottom line number.
+
+* Fix bug in running the editor on a file whose name contains quotes, etc.
+
+* Fix bug in horizontal scrolling of long lines.
+
+* Fix bug in doing :d on a file which contains marks.
+
+* Fix bug causing cleared lines to sometimes be filled with standout, 
+  bold, underline, etc. on certain terminals.
+
+* Fixes for MS-DOS (DJGPP) version.
+
+======================================================================
+
+
+       Major changes between "less" versions 340 and 346
+
+* The UTF-8 character set is now supported.
+
+* The default character set is now latin1 rather than ascii.
+
+* New option -R (--RAW-CONTROL-CHARS) is like -r but handles 
+  long (wrapped) lines correctly, as long as the input contains only 
+  normal text and ANSI color escape sequences.
+
+* New option -F (--quit-if-one-screen) quits if the text fits on
+  the first screen.
+
+* The -w option now highlights the target line of a g or p command.
+
+* A system-wide lesskey file is supported (LESSKEY_SYSTEM).
+
+* New escape for prompt strings: %c is replaced by column number.
+
+* New escape for prompt strings: %P is replaced by percentage into
+  file, based on line number rather than byte offset.
+
+* HOME and END keys now jump to beginning of file or end of file.
+
+======================================================================
+
+
+       Major changes between "less" versions 337 and 340
+
+* Command line options for less may now be given in either the old 
+  single-letter form, or a new long name form (--option-name).
+  See the less man page or "less --help" for the list of long option names.
+
+* Command line options for lesskey may now be given in a new long name
+  form.  See the lesskey man page for the list of long option names.
+
+* New command -- toggles an option using the long option name.
+
+* New command __ queries an option using the long option name.
+
+* The old -- command is renamed as -!.
+
+* If a ^P is entered between the dash and the option letter of the -
+  command, the message describing the new setting is suppressed.
+
+* Lesskey files may now contain \k escape sequences to represent the
+  "special" keys (arrows, PAGE-UP/PAGE-DOWN, HOME, END, INSERT, DELETE).
+
+* New command :d removes the current file from the list of files.
+
+* New option -~ (like -w before version 335)
+  suppresses tildes after end-of-file.
+
+* Less is now released under the GNU General Public License.
+
+======================================================================
+
+
+       Major changes between "less" versions 335 and 337
+
+* Fixed bugs in "make install".
+
+======================================================================
+
+
+       Major changes between "less" versions 332 and 335
+
+* The old -w flag (suppress tildes after end-of-file) has been removed.
+
+* New -w flag highlights the first new line after a forward-screen.
+
+* New -W flag highlights the first new line after any forward movement.
+
+* Window resize works even if LINES and/or COLUMNS environment 
+  variables are incorrect.
+
+* New percent escapes for prompt strings:
+  %d is replaced by the page number, and
+  %D is replaced by the number of pages in the file.
+
+* Added charsets "iso8859" and "ebcdic".
+
+* In Windows version, uses HOMEDRIVE and HOMEPATH if HOME is not defined.
+
+* Fixed some bugs causing incorrect display on DOS/Windows.
+
+======================================================================
+
+
+       Major changes between "less" versions 330 and 332
+
+* Filenames from the command line are entered into the command history,
+  so UPARROW/DOWNARROW can be used to retrieve them from the :e command.
+
+* Now works correctly on Windows when using a scrolling terminal
+  window (buffer larger than display window).
+
+* On Windows, now restores the console screen on exit.  
+  Use -X to get the old behavior.
+
+* Fixed bug on Windows when CAPS-LOCK or NUM-LOCK is pressed.
+
+* Fixed bug on Windows when piping output of an interactive program.
+
+* Fixed bug in tags file processing when tags file has DOS-style
+  line terminators (CR/LF).
+
+* Fixed compilation problem on OS/2.
+
+======================================================================
+
+
+       Major changes between "less" versions 321 and 330
+
+* Now supports filenames containing spaces (in double quotes).
+  New option -" can be used to change the quoting characters.
+
+* In filename completion, a slash is appended to a directory name.
+  If the environment variable LESSSEPARATOR is set, the value of
+  that variable, rather than a slash, is appended.
+
+* LeftArrow and RightArrow are same as ESC-[ and ESC-].
+
+* Added commands ESC-( and ESC-), same as ESC-[ and ESC-].
+
+* A "quit" command defined in a lesskey file may now have an "extra" 
+  string, which is used to return an exit code from less when it quits.
+
+* New environment variables LESSMETACHARS and LESSMETAESCAPE provide
+  more control over how less interfaces to the shell.
+
+* Ported to Microsoft Visual C compiler for Windows.
+
+* Ported to DJGPP compiler for MS-DOS.
+
+* Bug fixes.
+
+======================================================================
+
+
+       Major changes between "less" versions 291 and 321
+
+* Command line at bottom of screen now scrolls, so it can be longer 
+  than the screen width.
+
+* New commands ESC-] and ESC-[ scroll the display horizontally.
+
+* New command ESC-SPACE scrolls forward a full screen, even if it
+  hits end-of-file.
+
+* Alternate modifiers for search commands: ^N is same as !,
+  ^F is same as @, and ^E is same as *.
+
+* New modifier for search commands: ^K means highlight the matches
+  currently on-screen, but don't move to the first match.
+
+* New modifier for search commands: ^R means don't use regular
+  expressions in the search.
+
+* Environment variable LESSKEY gives name of default lesskey file.
+
+* Environment variable LESSSECURE will force less to run in
+  "secure" mode.
+
+* Command line argument "--" signals that the rest of the arguments
+  are files (not option flags).
+
+* Help file (less.hlp) is no longer installed.  Help text is now 
+  embedded in the less executable itself.
+
+* Added -Ph to change the prompt for the help text.
+  Added -Ps to change the default short prompt (same as plain -P).
+
+* Ported to the Borland C compiler for MS-DOS.
+
+* Ported to Windows 95 & Windows NT.
+
+* Ported to OS-9.
+
+* Ported to GNU Hurd.
+
+======================================================================
+
+
+       Major changes between "less" versions 290 and 291
+
+* Less environment variables can be specified in lesskey files.
+
+* Fixed MS-DOS build.
+
+======================================================================
+
+
+       Major changes between "less" versions 278 and 290
+
+* Accepts GNU-style options "--help" and "--version".
+
+* OS/2 version looks for less.ini in $HOME before $INIT and $PATH.
+
+* Bug fixes
+
+======================================================================
+
+
+       Major changes between "less" versions 252 and 278
+
+* A LESSOPEN preprocessor may now pipe the converted file data to less,
+  rather than writing it to a temporary file.
+
+* Search pattern highlighting has been fixed.  It now highlights 
+  reliably, even if a string is split across two screen lines,
+  contains TABs, etc.
+
+* The -F flag (which suppress search highlighting) has been changed 
+  to -G.  A new flag, -g, changes search highlighting to highlight 
+  only the string found by the last search command, instead of all 
+  strings which match the last search command.
+
+* New flag -I acts like -i, but ignores case even if the search 
+  pattern contains uppercase letters.
+
+* Less now checks for the environment variable VISUAL before EDITOR.
+
+* Ported to OS/2.
+
+======================================================================
+
+
+       Major changes between "less" versions 237 and 252
+
+* Changes in line-editing keys:
+  The literal key is now ^V or ^A rather than \ (backslash).
+  Filename completion commands (TAB and ^L) are disabled 
+  when typing a search pattern.
+
+* Line-editing command keys can be redefined using lesskey.
+
+* Lesskey with no input file defaults to $HOME/.lesskey
+  rather than standard input.
+
+* New option -V displays version number of less.
+
+* New option -V displays version number of lesskey.
+
+* Help file less.hlp is now installed by default in /usr/local/share 
+  rather than /usr/local/lib.
+
+
+======================================================================
+
+
+       Major changes between "less" versions 170 and 237
+
+* By popular demand, text which matches the current search pattern
+  is highlighted.  New -F flag disables this feature.
+
+* Henry Spencer's regexp.c is now included, for systems which do not
+  have a regular expression library.
+  regexp.c is Copyright (c) 1986 by University of Toronto.
+
+* New line-editing keys, including command history (arrow keys) and 
+  filename completion (TAB).
+
+* Input preprocessor allows modification of input files (e.g. uncompress)
+  via LESSOPEN/LESSCLOSE environment variables.
+
+* New -X flag disables sending termcap "ti" and "te" (initialize and
+  deinitialize) strings to the terminal. 
+
+* Changing -i from within less now correctly affects a subsequent
+  repeated search.  
+
+* Searching for underlined or overstruck text now works when the -u
+  flag is in effect, rather than the -i flag.
+
+* Use setlocale (LANG and LC_CTYPE environment variables) to determine
+  the character set if LESSCHARSET/LESSCHARDEF are not set.
+
+* The default format for displaying binary characters is now standout
+  (reverse video) rather than blinking.  This can still be changed by
+  setting the LESSBINFMT environment variable.
+
+* Use autoconf installation technology.
+
+* Ported to MS-DOS.
+
+        ********************************
+          Things that may surprise you
+        ********************************
+
+* When you enter text at the bottom of the screen (search string, 
+  filename, etc.), some keys act different than previously.  
+  Specifically, \ (backslash), ESC, TAB, BACKTAB, and control-L 
+  now have line editing functions.
+
+* Some previous unofficial versions of less were able to display
+  compressed files.  The new LESSOPEN/LESSCLOSE feature now provides
+  this functionality in a different way.
+
+* Some previous unofficial versions of less provided a -Z flag to 
+  set the number of lines of text to retain between full screen scrolls.
+  The -z-n flag (that is, -z with a negative number) provides this 
+  functionality.
+
+
+======================================================================
+
+
+       Major changes between "less" versions 123 and 170
+
+* New option -j allows target lines to be positioned anywhere on screen.
+
+* New option -S truncates displayed line at the screen width,
+  rather than wrapping onto the next line.
+
+* New option -y limits amount of forward scroll.
+
+* New option -T specifies a "tags" file.
+
+* Non-printable, non-control characters are displayed in octal.
+  Such characters, as well as control characters, are displayed 
+  in blinking mode.
+
+* New command -+ sets an option to its default.
+* New command -- sets an option to the opposite of its default.
+
+* Lesskey file may have a string appended to a key's action,
+  which acts as though typed in after the command.
+
+* New commands ESC-^F and ESC-^B match arbitrary types of brackets.
+
+* New command F monitors a growing file (like "tail -f").
+
+* New command | pipes a section of the input file into a shell command.
+
+* New command :x directly jumps to a file in the command line list.
+
+* Search commands have been enhanced and reorganized:
+       n       Repeat search, same direction.
+       N       Repeat search, opposite direction.
+       ESC-/   Search forward thru file boundaries
+       ESC-?   Search backward thru file boundaries
+       ESC-n   Repeat search thru file boundaries, same direction.
+       ESC-N   Repeat search thru file boundaries, opposite direction.
+  Special character * causes search to search thru file boundaries.
+  Special character @ causes search to begin at start/end of file list.
+
+* Examining a new file adds it to the command line list.
+  A list of files, or an expression which matches more than one file,
+  may be examined; all of them are added to the command line list.
+
+* Environment variables LESSCHARSET and LESSCHARDEF can define
+  a non-ASCII character set.
+
+* Partial support for MSDOS, including options -R for repainting screen
+  on quit, -v/-V to select video mode, and -W to change window size.
+
+
+======================================================================
+
+
+       Major changes between "less" versions 97 and 123
+
+* New option (-N) causes line numbers to be displayed in the
+  text of the file (like vi "set nu").
+
+* New option (-?) prints help message immediately.
+
+* New option (-r) displays "raw" control characters, without
+  mapping them to ^X notation.
+
+* New option (-f) forces less to open non-regular files
+  (directories, etc).
+
+* New option (-k) can be used to specify lesskey files by name.
+
+* New option (-y) can be used to set a forward scroll limit
+  (like -h sets a backward scroll limit).
+
+* File marks (set by the m command) are now preserved when a new
+  file is edited.  The ' command can thus be used to switch files.
+
+* New command ESC-/ searches all files (on the command line) 
+  for a pattern.
+
+* New command ESC-n repeats previous search, spanning files.
+
+* The N command has been changed to repeat the previous search
+  in the reverse direction.  The old N command is still available 
+  via :n.
+
+* New command ESC-N repeats previous search in the reverse
+  direction and spanning files.
+
+* 8 bit characters are now supported.  A new option (-g) can be 
+  used to strip off the eighth bit (the previous behavior).
+
+* Options which take a following string (like -t) may now
+  optionally have a space between the option letter and the string.
+
+* Six new commands { } ( ) [ and ] can be used to match
+  brackets of specific types, similar to vi % command.
+
+* New commands z and w move forward/backward one window and
+  simultaneously set the window size.
+
+* Prompt string expansion now has %L for line number of the last
+  line in the file, and %E for the name of the editor.
+  Also, % escapes which refer to a line (b=bottom, t=top, etc.)
+  can use j for the jump target line.
+
+* New environment variable LESSEDIT can be used to tailor the
+  command string passed to the editor by the v command.
+
+* Examining a file which was previously examined will return
+  to the same position in the file.
+
+* A "%" is expanded to the current filename and a "#" to the 
+  previous filename, in both shell commands and the E command.
+  (Previously % worked only in shell commands and # worked 
+  only in the E command.)
+
+* New command ":ta" is equivalent to "-t".
+
+* New command "s" is equivalent to "-l".
+
+* The - command may be followed by "+X" to revert to the default
+  for option X, or "-X" to get the opposite of the default.
+
+* Lesskey files may now include characters after the action as
+  extra input to be parsed after the action; for example:
+  "toggle-option X" to toggle a specific option X.
+
+
+
+
+
diff --git a/thirdparty/less/README b/thirdparty/less/README
new file mode 100644 (file)
index 0000000..c4cfa63
--- /dev/null
@@ -0,0 +1,236 @@
+
+                            Less, version 444
+
+    This is the distribution of less, version 444, released 09 Jun 2011.
+    This program is part of the GNU project (http://www.gnu.org).
+
+    This program is free software.  You may redistribute it and/or
+    modify it under the terms of either:
+
+    1. The GNU General Public License, as published by the Free
+       Software Foundation; either version 3, or (at your option) any
+       later version.  A copy of this license is in the file COPYING.
+    or
+    2. The Less License, in the file LICENSE.
+
+    Please report any problems to bug-less@gnu.org or markn@greenwoodsoftware.com.
+    See http://www.greenwoodsoftware.com/less for the latest info.
+
+=========================================================================
+
+This is the distribution of "less", a paginator similar to "more" or "pg".
+
+The formatted manual page is in less.man.
+The manual page nroff source is in less.nro.
+Major changes made since the last posted version are in NEWS.
+
+=======================================================================
+INSTALLATION (Unix systems only):
+
+1. Move the distributed source to its own directory and unpack it,
+   if you have not already done so.  
+
+2. Type "sh configure".
+   This will generate a Makefile and a defines.h.
+   Warning: if you have a GNU sed, make sure it is version 2.05 or later.
+
+   The file INSTALL describes the usage of the configure program in
+   general.  In addition, these options to configure are supported:
+
+   --with-editor=program
+     Specifies the default editor program used by the "v" command.
+     The default is "vi".
+
+   --with-regex=lib
+     Specifies the regular expression library used by less for pattern
+     matching.  The default is "auto", which means the configure program 
+     finds a regular expression library automatically.  Other values are:
+        posix          Use the POSIX-compatible regcomp.
+        pcre           Use the PCRE library.
+        regcmp         Use the regcmp library.
+        re_comp        Use the re_comp library.
+        regcomp        Use the V8-compatible regcomp.
+        regcomp-local  Use Henry Spencer's V8-compatible regcomp
+                       (source is supplied with less).
+   --with-secure
+     Builds a "secure" version of less, with some features disabled
+        to prevent users from viewing other files, accessing shell
+        commands, etc.
+
+
+3. It is a good idea to look over the generated Makefile and defines.h
+   and make sure they look ok.  If you know of any peculiarities of
+   your system that configure might not have detected, you may fix the
+   Makefile now.  Take particular notice of the list of "terminal" 
+   libraries in the LIBS definition in the Makefile; these may need 
+   to be edited.  The terminal libraries will be some subset of
+       -lncurses  -lcurses  -ltermcap  -ltermlib
+
+   If you wish, you may edit defines.h to remove some optional features.
+   If you choose not to include some features in your version, you may
+   wish to edit the manual page "less.nro" and the help page "less.hlp" 
+   to remove the descriptions of the features which you are removing.
+   If you edit less.hlp, you should run "make -f Makefile.aut help.c".
+
+4. Type "make" and watch the fun.
+
+5. If the make succeeds, it will generate the programs "less",
+   "lesskey" and "lessecho" in your current directory.  Test the 
+   generated programs.
+
+6. When satisfied that it works, if you wish to install it
+   in a public place, type "make install".
+
+   The default install destinations are:
+        Executables (less, lesskey, lessecho) in /usr/local/bin
+        Documentation (less.nro, lesskey.nro) in /usr/local/man/man1
+   If you want to install any of these files elsewhere, define
+   bindir and/or mandir to the appropriate directories.
+
+If you have any problems building or running "less", suggestions, 
+complaints, etc., you may mail to the author at markn@greenwoodsoftware.com.
+
+Note to hackers: comments noting possible improvements are enclosed
+in double curly brackets {{ like this }}.
+
+(Note that the above note was originally written at a time when 
+"hackers" most commonly meant "enthusiastic and dedicated computer 
+programmers", not "persons who attempt to circumvent computer security".)
+
+
+
+=======================================================================
+INSTALLATION (MS-DOS systems only,
+              with Microsoft C, Borland C, or DJGPP)
+
+1. Move the distributed source to its own directory.
+   Depending on your compiler, you may need to convert the source 
+   to have CR-LF rather than LF as line terminators.
+
+2. If you are using Microsoft C, rename MAKEFILE.DSU to MAKEFILE.
+   If you are using Borland C, rename MAKEFILE.DSB to MAKEFILE.
+   If you are using DJGPP, rename MAKEFILE.DSG to MAKEFILE.
+
+3. Look at MAKEFILE to make sure that the definitions for CC and LIBDIR
+   are correct.  CC should be the name of your C compiler and
+   LIBDIR should be the directory where the C libraries reside (for
+   Microsoft C only).  If these definitions need to be changed, you can
+   either modify the definitions directly in MAKEFILE, or set your
+   environment variables CC and/or LIBDIR to override the definitions
+   in MAKEFILE.
+
+4. If you wish, you may edit DEFINES.DS to remove some optional features.
+   If you choose not to include some features in your version, you may
+   wish to edit the manual page LESS.MAN and the help page HELP.C
+   to remove the descriptions of the features which you are removing.
+
+5. Run your "make" program and watch the fun.
+   If your "make" requires a flag to import environment variables,
+   you should use that flag.
+   If your compiler runs out of memory, try running "make -n >cmds.bat" 
+   and then run cmds.bat.
+
+6. If the make succeeds, it will generate the programs "LESS.EXE" and
+   "LESSKEY.EXE" in your current directory.  Test the generated programs.
+
+7. When satisfied that it works, you may wish to install LESS.EXE and
+   LESSKEY.EXE in a directory which is included in your PATH.
+
+
+
+=======================================================================
+INSTALLATION (Windows-95, Windows-98 and Windows-NT systems only,
+              with Borland C or Microsoft Visual C++)
+
+1. Move the distributed source to its own directory.
+
+2. If you are using Borland C, rename Makefile.wnb to Makefile.
+   If you are using Microsoft Visual C++, rename Makefile.wnm to Makefile.
+
+3. Check the Makefile to make sure the definitions look ok.
+
+4. If you wish, you may edit defines.wn to remove some optional features.
+   If you choose not to include some features in your version, you may
+   wish to edit the manual page less.man and the help page help.c
+   to remove the descriptions of the features which you are removing.
+
+5. Type "make" and watch the fun.
+
+6. If the make succeeds, it will generate the programs "less.exe" and
+   "lesskey.exe" in your current directory.  Test the generated programs.
+
+7. When satisfied that it works, if you wish to install it
+   in a public place, type "make install".
+   See step 6 of the Unix installation instructions for details
+   on how to change the default installation directories.
+
+
+
+=======================================================================
+INSTALLATION (OS/2 systems only,
+              with EMX C)
+
+1. Move the distributed source to its own directory.
+
+2. Rename Makefile.o2e to Makefile.
+
+3. Check the Makefile to make sure the definitions look ok.
+
+4. If you wish, you may edit defines.o2 to remove some optional features.
+   If you choose not to include some features in your version, you may
+   wish to edit the manual page less.man and the help page help.c
+   to remove the descriptions of the features which you are removing.
+
+5. Type "make" and watch the fun.
+
+6. If the make succeeds, it will generate the programs "less.exe" and
+   "lesskey.exe" in your current directory.  Test the generated programs.
+
+7. Make sure you have the emx runtime installed. You need the emx DLLs
+   emx.dll and emxlibcs.dll and also the termcap database, termcap.dat.
+   Make sure you have termcap.dat either in the default location or
+   somewhere in a directory listed in the PATH or INIT environment 
+   variables.
+
+8. When satisfied that it works, you may wish to install less.exe,
+   lesskey.exe and scrsize.exe in a directory which is included in 
+   your PATH.  scrsize.exe is required only if you use a terminal
+   emulator such as xterm or rxvt.
+
+
+
+=======================================================================
+INSTALLATION (OS-9 systems only,
+              with Microware C or Ultra C)
+
+1. Move the distributed source to its own directory.
+
+2. If you are using Microware C, rename Makefile.o9c to Makefile.
+   If you are using Ultra C, rename Makefile.o9u to Makefile.
+
+3. Check the Makefile to make sure the definitions look ok.
+
+4. If you wish, you may edit defines.o9 to remove some optional features.
+   If you choose not to include some features in your version, you may
+   wish to edit the manual page less.man and the help page help.c
+   to remove the descriptions of the features which you are removing.
+
+5. Type "dmake" and watch the fun.
+   The standard OS-9 "make" will probably not work.  If you don't
+   have dmake, you can get a copy from os9archive.rtsi.com.
+
+6. If the make succeeds, it will generate the programs "less" and
+   "lesskey" in your current directory.  Test the generated programs.
+
+7. When satisfied that it works, if you wish to install it
+   in a public place, type "dmake install".
+   See step 6 of the Unix installation instructions for details
+   on how to change the default installation directories.
+
+=======================================================================
+ACKNOWLEDGMENTS:
+  Some versions of the less distribution are packaged using 
+  Info-ZIP's compression utility.
+  Info-ZIP's software is free and can be obtained as source 
+  code or executables from various anonymous-ftp sites,
+  including ftp.uu.net:/pub/archiving/zip.
diff --git a/thirdparty/less/brac.c b/thirdparty/less/brac.c
new file mode 100644 (file)
index 0000000..22c71eb
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Routines to perform bracket matching functions.
+ */
+
+#include "less.h"
+#include "position.h"
+
+/*
+ * Try to match the n-th open bracket 
+ *  which appears in the top displayed line (forwdir),
+ * or the n-th close bracket 
+ *  which appears in the bottom displayed line (!forwdir).
+ * The characters which serve as "open bracket" and 
+ * "close bracket" are given.
+ */
+       public void
+match_brac(obrac, cbrac, forwdir, n)
+       register int obrac;
+       register int cbrac;
+       int forwdir;
+       int n;
+{
+       register int c;
+       register int nest;
+       POSITION pos;
+       int (*chget)();
+
+       extern int ch_forw_get(), ch_back_get();
+
+       /*
+        * Seek to the line containing the open bracket.
+        * This is either the top or bottom line on the screen,
+        * depending on the type of bracket.
+        */
+       pos = position((forwdir) ? TOP : BOTTOM);
+       if (pos == NULL_POSITION || ch_seek(pos))
+       {
+               if (forwdir)
+                       error("Nothing in top line", NULL_PARG);
+               else
+                       error("Nothing in bottom line", NULL_PARG);
+               return;
+       }
+
+       /*
+        * Look thru the line to find the open bracket to match.
+        */
+       do
+       {
+               if ((c = ch_forw_get()) == '\n' || c == EOI)
+               {
+                       if (forwdir)
+                               error("No bracket in top line", NULL_PARG);
+                       else
+                               error("No bracket in bottom line", NULL_PARG);
+                       return;
+               }
+       } while (c != obrac || --n > 0);
+
+       /*
+        * Position the file just "after" the open bracket
+        * (in the direction in which we will be searching).
+        * If searching forward, we are already after the bracket.
+        * If searching backward, skip back over the open bracket.
+        */
+       if (!forwdir)
+               (void) ch_back_get();
+
+       /*
+        * Search the file for the matching bracket.
+        */
+       chget = (forwdir) ? ch_forw_get : ch_back_get;
+       nest = 0;
+       while ((c = (*chget)()) != EOI)
+       {
+               if (c == obrac)
+                       nest++;
+               else if (c == cbrac && --nest < 0)
+               {
+                       /*
+                        * Found the matching bracket.
+                        * If searching backward, put it on the top line.
+                        * If searching forward, put it on the bottom line.
+                        */
+                       jump_line_loc(ch_tell(), forwdir ? -1 : 1);
+                       return;
+               }
+       }
+       error("No matching bracket", NULL_PARG);
+}
diff --git a/thirdparty/less/ch.c b/thirdparty/less/ch.c
new file mode 100644 (file)
index 0000000..1b84ec1
--- /dev/null
@@ -0,0 +1,933 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Low level character input from the input file.
+ * We use these special purpose routines which optimize moving
+ * both forward and backward from the current read pointer.
+ */
+
+#include "less.h"
+#if MSDOS_COMPILER==WIN32C
+#include <errno.h>
+#include <windows.h>
+#endif
+
+#if HAVE_STAT_INO
+#include <sys/stat.h>
+extern dev_t curr_dev;
+extern ino_t curr_ino;
+#endif
+
+typedef POSITION BLOCKNUM;
+
+public int ignore_eoi;
+
+/*
+ * Pool of buffers holding the most recently used blocks of the input file.
+ * The buffer pool is kept as a doubly-linked circular list,
+ * in order from most- to least-recently used.
+ * The circular list is anchored by the file state "thisfile".
+ */
+struct bufnode {
+       struct bufnode *next, *prev;
+       struct bufnode *hnext, *hprev;
+};
+
+#define        LBUFSIZE        8192
+struct buf {
+       struct bufnode node;
+       BLOCKNUM block;
+       unsigned int datasize;
+       unsigned char data[LBUFSIZE];
+};
+#define bufnode_buf(bn)  ((struct buf *) bn)
+
+/*
+ * The file state is maintained in a filestate structure.
+ * A pointer to the filestate is kept in the ifile structure.
+ */
+#define        BUFHASH_SIZE    64
+struct filestate {
+       struct bufnode buflist;
+       struct bufnode hashtbl[BUFHASH_SIZE];
+       int file;
+       int flags;
+       POSITION fpos;
+       int nbufs;
+       BLOCKNUM block;
+       unsigned int offset;
+       POSITION fsize;
+};
+
+#define        ch_bufhead      thisfile->buflist.next
+#define        ch_buftail      thisfile->buflist.prev
+#define        ch_nbufs        thisfile->nbufs
+#define        ch_block        thisfile->block
+#define        ch_offset       thisfile->offset
+#define        ch_fpos         thisfile->fpos
+#define        ch_fsize        thisfile->fsize
+#define        ch_flags        thisfile->flags
+#define        ch_file         thisfile->file
+
+#define        END_OF_CHAIN    (&thisfile->buflist)
+#define        END_OF_HCHAIN(h) (&thisfile->hashtbl[h])
+#define BUFHASH(blk)   ((blk) & (BUFHASH_SIZE-1))
+
+/*
+ * Macros to manipulate the list of buffers in thisfile->buflist.
+ */
+#define        FOR_BUFS(bn) \
+       for (bn = ch_bufhead;  bn != END_OF_CHAIN;  bn = bn->next)
+
+#define BUF_RM(bn) \
+       (bn)->next->prev = (bn)->prev; \
+       (bn)->prev->next = (bn)->next;
+
+#define BUF_INS_HEAD(bn) \
+       (bn)->next = ch_bufhead; \
+       (bn)->prev = END_OF_CHAIN; \
+       ch_bufhead->prev = (bn); \
+       ch_bufhead = (bn);
+
+#define BUF_INS_TAIL(bn) \
+       (bn)->next = END_OF_CHAIN; \
+       (bn)->prev = ch_buftail; \
+       ch_buftail->next = (bn); \
+       ch_buftail = (bn);
+
+/*
+ * Macros to manipulate the list of buffers in thisfile->hashtbl[n].
+ */
+#define        FOR_BUFS_IN_CHAIN(h,bn) \
+       for (bn = thisfile->hashtbl[h].hnext;  \
+            bn != END_OF_HCHAIN(h);  bn = bn->hnext)
+
+#define        BUF_HASH_RM(bn) \
+       (bn)->hnext->hprev = (bn)->hprev; \
+       (bn)->hprev->hnext = (bn)->hnext;
+
+#define        BUF_HASH_INS(bn,h) \
+       (bn)->hnext = thisfile->hashtbl[h].hnext; \
+       (bn)->hprev = END_OF_HCHAIN(h); \
+       thisfile->hashtbl[h].hnext->hprev = (bn); \
+       thisfile->hashtbl[h].hnext = (bn);
+
+static struct filestate *thisfile;
+static int ch_ungotchar = -1;
+static int maxbufs = -1;
+
+extern int autobuf;
+extern int sigs;
+extern int secure;
+extern int screen_trashed;
+extern int follow_mode;
+extern constant char helpdata[];
+extern constant int size_helpdata;
+extern IFILE curr_ifile;
+#if LOGFILE
+extern int logfile;
+extern char *namelogfile;
+#endif
+
+static int ch_addbuf();
+
+
+/*
+ * Get the character pointed to by the read pointer.
+ */
+       int
+ch_get()
+{
+       register struct buf *bp;
+       register struct bufnode *bn;
+       register int n;
+       register int slept;
+       register int h;
+       POSITION pos;
+       POSITION len;
+
+       if (thisfile == NULL)
+               return (EOI);
+
+       /*
+        * Quick check for the common case where 
+        * the desired char is in the head buffer.
+        */
+       if (ch_bufhead != END_OF_CHAIN)
+       {
+               bp = bufnode_buf(ch_bufhead);
+               if (ch_block == bp->block && ch_offset < bp->datasize)
+                       return bp->data[ch_offset];
+       }
+
+       slept = FALSE;
+
+       /*
+        * Look for a buffer holding the desired block.
+        */
+       h = BUFHASH(ch_block);
+       FOR_BUFS_IN_CHAIN(h, bn)
+       {
+               bp = bufnode_buf(bn);
+               if (bp->block == ch_block)
+               {
+                       if (ch_offset >= bp->datasize)
+                               /*
+                                * Need more data in this buffer.
+                                */
+                               break;
+                       goto found;
+               }
+       }
+       if (bn == END_OF_HCHAIN(h))
+       {
+               /*
+                * Block is not in a buffer.  
+                * Take the least recently used buffer 
+                * and read the desired block into it.
+                * If the LRU buffer has data in it, 
+                * then maybe allocate a new buffer.
+                */
+               if (ch_buftail == END_OF_CHAIN || 
+                       bufnode_buf(ch_buftail)->block != -1)
+               {
+                       /*
+                        * There is no empty buffer to use.
+                        * Allocate a new buffer if:
+                        * 1. We can't seek on this file and -b is not in effect; or
+                        * 2. We haven't allocated the max buffers for this file yet.
+                        */
+                       if ((autobuf && !(ch_flags & CH_CANSEEK)) ||
+                               (maxbufs < 0 || ch_nbufs < maxbufs))
+                               if (ch_addbuf())
+                                       /*
+                                        * Allocation failed: turn off autobuf.
+                                        */
+                                       autobuf = OPT_OFF;
+               }
+               bn = ch_buftail;
+               bp = bufnode_buf(bn);
+               BUF_HASH_RM(bn); /* Remove from old hash chain. */
+               bp->block = ch_block;
+               bp->datasize = 0;
+               BUF_HASH_INS(bn, h); /* Insert into new hash chain. */
+       }
+
+    read_more:
+       pos = (ch_block * LBUFSIZE) + bp->datasize;
+       if ((len = ch_length()) != NULL_POSITION && pos >= len)
+               /*
+                * At end of file.
+                */
+               return (EOI);
+
+       if (pos != ch_fpos)
+       {
+               /*
+                * Not at the correct position: must seek.
+                * If input is a pipe, we're in trouble (can't seek on a pipe).
+                * Some data has been lost: just return "?".
+                */
+               if (!(ch_flags & CH_CANSEEK))
+                       return ('?');
+               if (lseek(ch_file, (off_t)pos, SEEK_SET) == BAD_LSEEK)
+               {
+                       error("seek error", NULL_PARG);
+                       clear_eol();
+                       return (EOI);
+               }
+               ch_fpos = pos;
+       }
+
+       /*
+        * Read the block.
+        * If we read less than a full block, that's ok.
+        * We use partial block and pick up the rest next time.
+        */
+       if (ch_ungotchar != -1)
+       {
+               bp->data[bp->datasize] = ch_ungotchar;
+               n = 1;
+               ch_ungotchar = -1;
+       } else if (ch_flags & CH_HELPFILE)
+       {
+               bp->data[bp->datasize] = helpdata[ch_fpos];
+               n = 1;
+       } else
+       {
+               n = iread(ch_file, &bp->data[bp->datasize], 
+                       (unsigned int)(LBUFSIZE - bp->datasize));
+       }
+
+       if (n == READ_INTR)
+               return (EOI);
+       if (n < 0)
+       {
+#if MSDOS_COMPILER==WIN32C
+               if (errno != EPIPE)
+#endif
+               {
+                       error("read error", NULL_PARG);
+                       clear_eol();
+               }
+               n = 0;
+       }
+
+#if LOGFILE
+       /*
+        * If we have a log file, write the new data to it.
+        */
+       if (!secure && logfile >= 0 && n > 0)
+               write(logfile, (char *) &bp->data[bp->datasize], n);
+#endif
+
+       ch_fpos += n;
+       bp->datasize += n;
+
+       /*
+        * If we have read to end of file, set ch_fsize to indicate
+        * the position of the end of file.
+        */
+       if (n == 0)
+       {
+               ch_fsize = pos;
+               if (ignore_eoi)
+               {
+                       /*
+                        * We are ignoring EOF.
+                        * Wait a while, then try again.
+                        */
+                       if (!slept)
+                       {
+                               PARG parg;
+                               parg.p_string = wait_message();
+                               ierror("%s", &parg);
+                       }
+#if !MSDOS_COMPILER
+                       sleep(1);
+#else
+#if MSDOS_COMPILER==WIN32C
+                       Sleep(1000);
+#endif
+#endif
+                       slept = TRUE;
+
+#if HAVE_STAT_INO
+                       if (follow_mode == FOLLOW_NAME)
+                       {
+                               /* See whether the file's i-number has changed.
+                                * If so, force the file to be closed and
+                                * reopened. */
+                               struct stat st;
+                               int r = stat(get_filename(curr_ifile), &st);
+                               if (r == 0 && (st.st_ino != curr_ino ||
+                                       st.st_dev != curr_dev))
+                               {
+                                       /* screen_trashed=2 causes
+                                        * make_display to reopen the file. */
+                                       screen_trashed = 2;
+                                       return (EOI);
+                               }
+                       }
+#endif
+               }
+               if (sigs)
+                       return (EOI);
+       }
+
+    found:
+       if (ch_bufhead != bn)
+       {
+               /*
+                * Move the buffer to the head of the buffer chain.
+                * This orders the buffer chain, most- to least-recently used.
+                */
+               BUF_RM(bn);
+               BUF_INS_HEAD(bn);
+
+               /*
+                * Move to head of hash chain too.
+                */
+               BUF_HASH_RM(bn);
+               BUF_HASH_INS(bn, h);
+       }
+
+       if (ch_offset >= bp->datasize)
+               /*
+                * After all that, we still don't have enough data.
+                * Go back and try again.
+                */
+               goto read_more;
+
+       return (bp->data[ch_offset]);
+}
+
+/*
+ * ch_ungetchar is a rather kludgy and limited way to push 
+ * a single char onto an input file descriptor.
+ */
+       public void
+ch_ungetchar(c)
+       int c;
+{
+       if (c != -1 && ch_ungotchar != -1)
+               error("ch_ungetchar overrun", NULL_PARG);
+       ch_ungotchar = c;
+}
+
+#if LOGFILE
+/*
+ * Close the logfile.
+ * If we haven't read all of standard input into it, do that now.
+ */
+       public void
+end_logfile()
+{
+       static int tried = FALSE;
+
+       if (logfile < 0)
+               return;
+       if (!tried && ch_fsize == NULL_POSITION)
+       {
+               tried = TRUE;
+               ierror("Finishing logfile", NULL_PARG);
+               while (ch_forw_get() != EOI)
+                       if (ABORT_SIGS())
+                               break;
+       }
+       close(logfile);
+       logfile = -1;
+       namelogfile = NULL;
+}
+
+/*
+ * Start a log file AFTER less has already been running.
+ * Invoked from the - command; see toggle_option().
+ * Write all the existing buffered data to the log file.
+ */
+       public void
+sync_logfile()
+{
+       register struct buf *bp;
+       register struct bufnode *bn;
+       int warned = FALSE;
+       BLOCKNUM block;
+       BLOCKNUM nblocks;
+
+       nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE;
+       for (block = 0;  block < nblocks;  block++)
+       {
+               int wrote = FALSE;
+               FOR_BUFS(bn)
+               {
+                       bp = bufnode_buf(bn);
+                       if (bp->block == block)
+                       {
+                               write(logfile, (char *) bp->data, bp->datasize);
+                               wrote = TRUE;
+                               break;
+                       }
+               }
+               if (!wrote && !warned)
+               {
+                       error("Warning: log file is incomplete",
+                               NULL_PARG);
+                       warned = TRUE;
+               }
+       }
+}
+
+#endif
+
+/*
+ * Determine if a specific block is currently in one of the buffers.
+ */
+       static int
+buffered(block)
+       BLOCKNUM block;
+{
+       register struct buf *bp;
+       register struct bufnode *bn;
+       register int h;
+
+       h = BUFHASH(block);
+       FOR_BUFS_IN_CHAIN(h, bn)
+       {
+               bp = bufnode_buf(bn);
+               if (bp->block == block)
+                       return (TRUE);
+       }
+       return (FALSE);
+}
+
+/*
+ * Seek to a specified position in the file.
+ * Return 0 if successful, non-zero if can't seek there.
+ */
+       public int
+ch_seek(pos)
+       register POSITION pos;
+{
+       BLOCKNUM new_block;
+       POSITION len;
+
+       if (thisfile == NULL)
+               return (0);
+
+       len = ch_length();
+       if (pos < ch_zero() || (len != NULL_POSITION && pos > len))
+               return (1);
+
+       new_block = pos / LBUFSIZE;
+       if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block))
+       {
+               if (ch_fpos > pos)
+                       return (1);
+               while (ch_fpos < pos)
+               {
+                       if (ch_forw_get() == EOI)
+                               return (1);
+                       if (ABORT_SIGS())
+                               return (1);
+               }
+               return (0);
+       }
+       /*
+        * Set read pointer.
+        */
+       ch_block = new_block;
+       ch_offset = pos % LBUFSIZE;
+       return (0);
+}
+
+/*
+ * Seek to the end of the file.
+ */
+       public int
+ch_end_seek()
+{
+       POSITION len;
+
+       if (thisfile == NULL)
+               return (0);
+
+       if (ch_flags & CH_CANSEEK)
+               ch_fsize = filesize(ch_file);
+
+       len = ch_length();
+       if (len != NULL_POSITION)
+               return (ch_seek(len));
+
+       /*
+        * Do it the slow way: read till end of data.
+        */
+       while (ch_forw_get() != EOI)
+               if (ABORT_SIGS())
+                       return (1);
+       return (0);
+}
+
+/*
+ * Seek to the beginning of the file, or as close to it as we can get.
+ * We may not be able to seek there if input is a pipe and the
+ * beginning of the pipe is no longer buffered.
+ */
+       public int
+ch_beg_seek()
+{
+       register struct bufnode *bn;
+       register struct bufnode *firstbn;
+
+       /*
+        * Try a plain ch_seek first.
+        */
+       if (ch_seek(ch_zero()) == 0)
+               return (0);
+
+       /*
+        * Can't get to position 0.
+        * Look thru the buffers for the one closest to position 0.
+        */
+       firstbn = ch_bufhead;
+       if (firstbn == END_OF_CHAIN)
+               return (1);
+       FOR_BUFS(bn)
+       {
+               if (bufnode_buf(bn)->block < bufnode_buf(firstbn)->block)
+                       firstbn = bn;
+       }
+       ch_block = bufnode_buf(firstbn)->block;
+       ch_offset = 0;
+       return (0);
+}
+
+/*
+ * Return the length of the file, if known.
+ */
+       public POSITION
+ch_length()
+{
+       if (thisfile == NULL)
+               return (NULL_POSITION);
+       if (ignore_eoi)
+               return (NULL_POSITION);
+       if (ch_flags & CH_HELPFILE)
+               return (size_helpdata);
+       return (ch_fsize);
+}
+
+/*
+ * Return the current position in the file.
+ */
+       public POSITION
+ch_tell()
+{
+       if (thisfile == NULL)
+               return (NULL_POSITION);
+       return (ch_block * LBUFSIZE) + ch_offset;
+}
+
+/*
+ * Get the current char and post-increment the read pointer.
+ */
+       public int
+ch_forw_get()
+{
+       register int c;
+
+       if (thisfile == NULL)
+               return (EOI);
+       c = ch_get();
+       if (c == EOI)
+               return (EOI);
+       if (ch_offset < LBUFSIZE-1)
+               ch_offset++;
+       else
+       {
+               ch_block ++;
+               ch_offset = 0;
+       }
+       return (c);
+}
+
+/*
+ * Pre-decrement the read pointer and get the new current char.
+ */
+       public int
+ch_back_get()
+{
+       if (thisfile == NULL)
+               return (EOI);
+       if (ch_offset > 0)
+               ch_offset --;
+       else
+       {
+               if (ch_block <= 0)
+                       return (EOI);
+               if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1))
+                       return (EOI);
+               ch_block--;
+               ch_offset = LBUFSIZE-1;
+       }
+       return (ch_get());
+}
+
+/*
+ * Set max amount of buffer space.
+ * bufspace is in units of 1024 bytes.  -1 mean no limit.
+ */
+       public void
+ch_setbufspace(bufspace)
+       int bufspace;
+{
+       if (bufspace < 0)
+               maxbufs = -1;
+       else
+       {
+               maxbufs = ((bufspace * 1024) + LBUFSIZE-1) / LBUFSIZE;
+               if (maxbufs < 1)
+                       maxbufs = 1;
+       }
+}
+
+/*
+ * Flush (discard) any saved file state, including buffer contents.
+ */
+       public void
+ch_flush()
+{
+       register struct bufnode *bn;
+
+       if (thisfile == NULL)
+               return;
+
+       if (!(ch_flags & CH_CANSEEK))
+       {
+               /*
+                * If input is a pipe, we don't flush buffer contents,
+                * since the contents can't be recovered.
+                */
+               ch_fsize = NULL_POSITION;
+               return;
+       }
+
+       /*
+        * Initialize all the buffers.
+        */
+       FOR_BUFS(bn)
+       {
+               bufnode_buf(bn)->block = -1;
+       }
+
+       /*
+        * Figure out the size of the file, if we can.
+        */
+       ch_fsize = filesize(ch_file);
+
+       /*
+        * Seek to a known position: the beginning of the file.
+        */
+       ch_fpos = 0;
+       ch_block = 0; /* ch_fpos / LBUFSIZE; */
+       ch_offset = 0; /* ch_fpos % LBUFSIZE; */
+
+#if 1
+       /*
+        * This is a kludge to workaround a Linux kernel bug: files in
+        * /proc have a size of 0 according to fstat() but have readable 
+        * data.  They are sometimes, but not always, seekable.
+        * Force them to be non-seekable here.
+        */
+       if (ch_fsize == 0)
+       {
+               ch_fsize = NULL_POSITION;
+               ch_flags &= ~CH_CANSEEK;
+       }
+#endif
+
+       if (lseek(ch_file, (off_t)0, SEEK_SET) == BAD_LSEEK)
+       {
+               /*
+                * Warning only; even if the seek fails for some reason,
+                * there's a good chance we're at the beginning anyway.
+                * {{ I think this is bogus reasoning. }}
+                */
+               error("seek error to 0", NULL_PARG);
+       }
+}
+
+/*
+ * Allocate a new buffer.
+ * The buffer is added to the tail of the buffer chain.
+ */
+       static int
+ch_addbuf()
+{
+       register struct buf *bp;
+       register struct bufnode *bn;
+
+       /*
+        * Allocate and initialize a new buffer and link it 
+        * onto the tail of the buffer list.
+        */
+       bp = (struct buf *) calloc(1, sizeof(struct buf));
+       if (bp == NULL)
+               return (1);
+       ch_nbufs++;
+       bp->block = -1;
+       bn = &bp->node;
+
+       BUF_INS_TAIL(bn);
+       BUF_HASH_INS(bn, 0);
+       return (0);
+}
+
+/*
+ *
+ */
+       static void
+init_hashtbl()
+{
+       register int h;
+
+       for (h = 0;  h < BUFHASH_SIZE;  h++)
+       {
+               thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h);
+               thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h);
+       }
+}
+
+/*
+ * Delete all buffers for this file.
+ */
+       static void
+ch_delbufs()
+{
+       register struct bufnode *bn;
+
+       while (ch_bufhead != END_OF_CHAIN)
+       {
+               bn = ch_bufhead;
+               BUF_RM(bn);
+               free(bufnode_buf(bn));
+       }
+       ch_nbufs = 0;
+       init_hashtbl();
+}
+
+/*
+ * Is it possible to seek on a file descriptor?
+ */
+       public int
+seekable(f)
+       int f;
+{
+#if MSDOS_COMPILER
+       extern int fd0;
+       if (f == fd0 && !isatty(fd0))
+       {
+               /*
+                * In MS-DOS, pipes are seekable.  Check for
+                * standard input, and pretend it is not seekable.
+                */
+               return (0);
+       }
+#endif
+       return (lseek(f, (off_t)1, SEEK_SET) != BAD_LSEEK);
+}
+
+/*
+ * Initialize file state for a new file.
+ */
+       public void
+ch_init(f, flags)
+       int f;
+       int flags;
+{
+       /*
+        * See if we already have a filestate for this file.
+        */
+       thisfile = (struct filestate *) get_filestate(curr_ifile);
+       if (thisfile == NULL)
+       {
+               /*
+                * Allocate and initialize a new filestate.
+                */
+               thisfile = (struct filestate *) 
+                               calloc(1, sizeof(struct filestate));
+               thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN;
+               thisfile->nbufs = 0;
+               thisfile->flags = 0;
+               thisfile->fpos = 0;
+               thisfile->block = 0;
+               thisfile->offset = 0;
+               thisfile->file = -1;
+               thisfile->fsize = NULL_POSITION;
+               ch_flags = flags;
+               init_hashtbl();
+               /*
+                * Try to seek; set CH_CANSEEK if it works.
+                */
+               if ((flags & CH_CANSEEK) && !seekable(f))
+                       ch_flags &= ~CH_CANSEEK;
+               set_filestate(curr_ifile, (void *) thisfile);
+       }
+       if (thisfile->file == -1)
+               thisfile->file = f;
+       ch_flush();
+}
+
+/*
+ * Close a filestate.
+ */
+       public void
+ch_close()
+{
+       int keepstate = FALSE;
+
+       if (thisfile == NULL)
+               return;
+
+       if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE))
+       {
+               /*
+                * We can seek or re-open, so we don't need to keep buffers.
+                */
+               ch_delbufs();
+       } else
+               keepstate = TRUE;
+       if (!(ch_flags & CH_KEEPOPEN))
+       {
+               /*
+                * We don't need to keep the file descriptor open
+                * (because we can re-open it.)
+                * But don't really close it if it was opened via popen(),
+                * because pclose() wants to close it.
+                */
+               if (!(ch_flags & (CH_POPENED|CH_HELPFILE)))
+                       close(ch_file);
+               ch_file = -1;
+       } else
+               keepstate = TRUE;
+       if (!keepstate)
+       {
+               /*
+                * We don't even need to keep the filestate structure.
+                */
+               free(thisfile);
+               thisfile = NULL;
+               set_filestate(curr_ifile, (void *) NULL);
+       }
+}
+
+/*
+ * Return ch_flags for the current file.
+ */
+       public int
+ch_getflags()
+{
+       if (thisfile == NULL)
+               return (0);
+       return (ch_flags);
+}
+
+#if 0
+       public void
+ch_dump(struct filestate *fs)
+{
+       struct buf *bp;
+       struct bufnode *bn;
+       unsigned char *s;
+
+       if (fs == NULL)
+       {
+               printf(" --no filestate\n");
+               return;
+       }
+       printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n",
+               fs->file, fs->flags, fs->fpos, 
+               fs->fsize, fs->block, fs->offset);
+       printf(" %d bufs:\n", fs->nbufs);
+       for (bn = fs->next; bn != &fs->buflist;  bn = bn->next)
+       {
+               bp = bufnode_buf(bn);
+               printf("%x: blk %x, size %x \"",
+                       bp, bp->block, bp->datasize);
+               for (s = bp->data;  s < bp->data + 30;  s++)
+                       if (*s >= ' ' && *s < 0x7F)
+                               printf("%c", *s);
+                       else
+                               printf(".");
+               printf("\"\n");
+       }
+}
+#endif
diff --git a/thirdparty/less/charset.c b/thirdparty/less/charset.c
new file mode 100644 (file)
index 0000000..12b59d6
--- /dev/null
@@ -0,0 +1,1173 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Functions to define the character set
+ * and do things specific to the character set.
+ */
+
+#include "less.h"
+#if HAVE_LOCALE
+#include <locale.h>
+#include <ctype.h>
+#include <langinfo.h>
+#endif
+
+#include "charset.h"
+
+public int utf_mode = 0;
+
+/*
+ * Predefined character sets,
+ * selected by the LESSCHARSET environment variable.
+ */
+struct charset {
+       char *name;
+       int *p_flag;
+       char *desc;
+} charsets[] = {
+       { "ascii",              NULL,       "8bcccbcc18b95.b" },
+       { "utf-8",              &utf_mode,  "8bcccbcc18b95.b126.bb" },
+       { "iso8859",            NULL,       "8bcccbcc18b95.33b." },
+       { "latin3",             NULL,       "8bcccbcc18b95.33b5.b8.b15.b4.b12.b18.b12.b." },
+       { "arabic",             NULL,       "8bcccbcc18b95.33b.3b.7b2.13b.3b.b26.5b19.b" },
+       { "greek",              NULL,       "8bcccbcc18b95.33b4.2b4.b3.b35.b44.b" },
+       { "greek2005",          NULL,       "8bcccbcc18b95.33b14.b35.b44.b" },
+       { "hebrew",             NULL,       "8bcccbcc18b95.33b.b29.32b28.2b2.b" },
+       { "koi8-r",             NULL,       "8bcccbcc18b95.b." },
+       { "KOI8-T",             NULL,       "8bcccbcc18b95.b8.b6.b8.b.b.5b7.3b4.b4.b3.b.b.3b." },
+       { "georgianps",         NULL,       "8bcccbcc18b95.3b11.4b12.2b." },
+       { "tcvn",               NULL,       "b..b...bcccbccbbb7.8b95.b48.5b." },
+       { "TIS-620",            NULL,       "8bcccbcc18b95.b.4b.11b7.8b." },
+       { "next",               NULL,       "8bcccbcc18b95.bb125.bb" },
+       { "dos",                NULL,       "8bcccbcc12bc5b95.b." },
+       { "windows-1251",       NULL,       "8bcccbcc12bc5b95.b24.b." },
+       { "windows-1252",       NULL,       "8bcccbcc12bc5b95.b.b11.b.2b12.b." },
+       { "windows-1255",       NULL,       "8bcccbcc12bc5b95.b.b8.b.5b9.b.4b." },
+       { "ebcdic",             NULL,       "5bc6bcc7bcc41b.9b7.9b5.b..8b6.10b6.b9.7b9.8b8.17b3.3b9.7b9.8b8.6b10.b.b.b." },
+       { "IBM-1047",           NULL,       "4cbcbc3b9cbccbccbb4c6bcc5b3cbbc4bc4bccbc191.b" },
+       { NULL, NULL, NULL }
+};
+
+/*
+ * Support "locale charmap"/nl_langinfo(CODESET) values, as well as others.
+ */
+struct cs_alias {
+       char *name;
+       char *oname;
+} cs_aliases[] = {
+       { "UTF-8",              "utf-8" },
+       { "ANSI_X3.4-1968",     "ascii" },
+       { "US-ASCII",           "ascii" },
+       { "latin1",             "iso8859" },
+       { "ISO-8859-1",         "iso8859" },
+       { "latin9",             "iso8859" },
+       { "ISO-8859-15",        "iso8859" },
+       { "latin2",             "iso8859" },
+       { "ISO-8859-2",         "iso8859" },
+       { "ISO-8859-3",         "latin3" },
+       { "latin4",             "iso8859" },
+       { "ISO-8859-4",         "iso8859" },
+       { "cyrillic",           "iso8859" },
+       { "ISO-8859-5",         "iso8859" },
+       { "ISO-8859-6",         "arabic" },
+       { "ISO-8859-7",         "greek" },
+       { "IBM9005",            "greek2005" },
+       { "ISO-8859-8",         "hebrew" },
+       { "latin5",             "iso8859" },
+       { "ISO-8859-9",         "iso8859" },
+       { "latin6",             "iso8859" },
+       { "ISO-8859-10",        "iso8859" },
+       { "latin7",             "iso8859" },
+       { "ISO-8859-13",        "iso8859" },
+       { "latin8",             "iso8859" },
+       { "ISO-8859-14",        "iso8859" },
+       { "latin10",            "iso8859" },
+       { "ISO-8859-16",        "iso8859" },
+       { "IBM437",             "dos" },
+       { "EBCDIC-US",          "ebcdic" },
+       { "IBM1047",            "IBM-1047" },
+       { "KOI8-R",             "koi8-r" },
+       { "KOI8-U",             "koi8-r" },
+       { "GEORGIAN-PS",        "georgianps" },
+       { "TCVN5712-1",         "tcvn" },
+       { "NEXTSTEP",           "next" },
+       { "windows",            "windows-1252" }, /* backward compatibility */
+       { "CP1251",             "windows-1251" },
+       { "CP1252",             "windows-1252" },
+       { "CP1255",             "windows-1255" },
+       { NULL, NULL }
+};
+
+#define        IS_BINARY_CHAR  01
+#define        IS_CONTROL_CHAR 02
+
+static char chardef[256];
+static char *binfmt = NULL;
+static char *utfbinfmt = NULL;
+public int binattr = AT_STANDOUT;
+
+
+/*
+ * Define a charset, given a description string.
+ * The string consists of 256 letters,
+ * one for each character in the charset.
+ * If the string is shorter than 256 letters, missing letters
+ * are taken to be identical to the last one.
+ * A decimal number followed by a letter is taken to be a 
+ * repetition of the letter.
+ *
+ * Each letter is one of:
+ *     . normal character
+ *     b binary character
+ *     c control character
+ */
+       static void
+ichardef(s)
+       char *s;
+{
+       register char *cp;
+       register int n;
+       register char v;
+
+       n = 0;
+       v = 0;
+       cp = chardef;
+       while (*s != '\0')
+       {
+               switch (*s++)
+               {
+               case '.':
+                       v = 0;
+                       break;
+               case 'c':
+                       v = IS_CONTROL_CHAR;
+                       break;
+               case 'b':
+                       v = IS_BINARY_CHAR|IS_CONTROL_CHAR;
+                       break;
+
+               case '0': case '1': case '2': case '3': case '4':
+               case '5': case '6': case '7': case '8': case '9':
+                       n = (10 * n) + (s[-1] - '0');
+                       continue;
+
+               default:
+                       error("invalid chardef", NULL_PARG);
+                       quit(QUIT_ERROR);
+                       /*NOTREACHED*/
+               }
+
+               do
+               {
+                       if (cp >= chardef + sizeof(chardef))
+                       {
+                               error("chardef longer than 256", NULL_PARG);
+                               quit(QUIT_ERROR);
+                               /*NOTREACHED*/
+                       }
+                       *cp++ = v;
+               } while (--n > 0);
+               n = 0;
+       }
+
+       while (cp < chardef + sizeof(chardef))
+               *cp++ = v;
+}
+
+/*
+ * Define a charset, given a charset name.
+ * The valid charset names are listed in the "charsets" array.
+ */
+       static int
+icharset(name, no_error)
+       register char *name;
+       int no_error;
+{
+       register struct charset *p;
+       register struct cs_alias *a;
+
+       if (name == NULL || *name == '\0')
+               return (0);
+
+       /* First see if the name is an alias. */
+       for (a = cs_aliases;  a->name != NULL;  a++)
+       {
+               if (strcmp(name, a->name) == 0)
+               {
+                       name = a->oname;
+                       break;
+               }
+       }
+
+       for (p = charsets;  p->name != NULL;  p++)
+       {
+               if (strcmp(name, p->name) == 0)
+               {
+                       ichardef(p->desc);
+                       if (p->p_flag != NULL)
+                               *(p->p_flag) = 1;
+                       return (1);
+               }
+       }
+
+       if (!no_error) {
+               error("invalid charset name", NULL_PARG);
+               quit(QUIT_ERROR);
+       }
+       return (0);
+}
+
+#if HAVE_LOCALE
+/*
+ * Define a charset, given a locale name.
+ */
+       static void
+ilocale()
+{
+       register int c;
+
+       for (c = 0;  c < (int) sizeof(chardef);  c++)
+       {
+               if (isprint(c))
+                       chardef[c] = 0;
+               else if (iscntrl(c))
+                       chardef[c] = IS_CONTROL_CHAR;
+               else
+                       chardef[c] = IS_BINARY_CHAR|IS_CONTROL_CHAR;
+       }
+}
+#endif
+
+/*
+ * Define the printing format for control (or binary utf) chars.
+ */
+       static void
+setbinfmt(s, fmtvarptr, default_fmt)
+       char *s;
+       char **fmtvarptr;
+       char *default_fmt;
+{
+       if (s && utf_mode)
+       {
+               /* It would be too hard to account for width otherwise.  */
+               char *t = s;
+               while (*t)
+               {
+                       if (*t < ' ' || *t > '~')
+                       {
+                               s = default_fmt;
+                               goto attr;
+                       }
+                       t++;
+               }
+       }
+
+       /* %n is evil */
+       if (s == NULL || *s == '\0' ||
+           (*s == '*' && (s[1] == '\0' || s[2] == '\0' || strchr(s + 2, 'n'))) ||
+           (*s != '*' && strchr(s, 'n')))
+               s = default_fmt;
+
+       /*
+        * Select the attributes if it starts with "*".
+        */
+ attr:
+       if (*s == '*')
+       {
+               switch (s[1])
+               {
+               case 'd':  binattr = AT_BOLD;      break;
+               case 'k':  binattr = AT_BLINK;     break;
+               case 's':  binattr = AT_STANDOUT;  break;
+               case 'u':  binattr = AT_UNDERLINE; break;
+               default:   binattr = AT_NORMAL;    break;
+               }
+               s += 2;
+       }
+       *fmtvarptr = s;
+}
+
+/*
+ *
+ */
+       static void
+set_charset()
+{
+       char *s;
+
+       /*
+        * See if environment variable LESSCHARSET is defined.
+        */
+       s = lgetenv("LESSCHARSET");
+       if (icharset(s, 0))
+               return;
+
+       /*
+        * LESSCHARSET is not defined: try LESSCHARDEF.
+        */
+       s = lgetenv("LESSCHARDEF");
+       if (s != NULL && *s != '\0')
+       {
+               ichardef(s);
+               return;
+       }
+
+#if HAVE_LOCALE
+#ifdef CODESET
+       /*
+        * Try using the codeset name as the charset name.
+        */
+       s = nl_langinfo(CODESET);
+       if (icharset(s, 1))
+               return;
+#endif
+#endif
+
+#if HAVE_STRSTR
+       /*
+        * Check whether LC_ALL, LC_CTYPE or LANG look like UTF-8 is used.
+        */
+       if ((s = lgetenv("LC_ALL")) != NULL ||
+           (s = lgetenv("LC_CTYPE")) != NULL ||
+           (s = lgetenv("LANG")) != NULL)
+       {
+               if (   strstr(s, "UTF-8") != NULL || strstr(s, "utf-8") != NULL
+                   || strstr(s, "UTF8")  != NULL || strstr(s, "utf8")  != NULL)
+                       if (icharset("utf-8", 1))
+                               return;
+       }
+#endif
+
+#if HAVE_LOCALE
+       /*
+        * Get character definitions from locale functions,
+        * rather than from predefined charset entry.
+        */
+       ilocale();
+#if MSDOS_COMPILER
+       /*
+        * Default to "dos".
+        */
+       (void) icharset("dos", 1);
+#else
+       /*
+        * Default to "latin1".
+        */
+       (void) icharset("latin1", 1);
+#endif
+#endif
+}
+
+/*
+ * Initialize charset data structures.
+ */
+       public void
+init_charset()
+{
+       char *s;
+
+#if HAVE_LOCALE
+       setlocale(LC_ALL, "");
+#endif
+
+       set_charset();
+
+       s = lgetenv("LESSBINFMT");
+       setbinfmt(s, &binfmt, "*s<%02X>");
+       
+       s = lgetenv("LESSUTFBINFMT");
+       setbinfmt(s, &utfbinfmt, "<U+%04lX>");
+}
+
+/*
+ * Is a given character a "binary" character?
+ */
+       public int
+binary_char(c)
+       LWCHAR c;
+{
+       if (utf_mode)
+               return (is_ubin_char(c));
+       c &= 0377;
+       return (chardef[c] & IS_BINARY_CHAR);
+}
+
+/*
+ * Is a given character a "control" character?
+ */
+       public int
+control_char(c)
+       LWCHAR c;
+{
+       c &= 0377;
+       return (chardef[c] & IS_CONTROL_CHAR);
+}
+
+/*
+ * Return the printable form of a character.
+ * For example, in the "ascii" charset '\3' is printed as "^C".
+ */
+       public char *
+prchar(c)
+       LWCHAR c;
+{
+       /* {{ This buffer can be overrun if LESSBINFMT is a long string. }} */
+       static char buf[32];
+
+       c &= 0377;
+       if ((c < 128 || !utf_mode) && !control_char(c))
+               SNPRINTF1(buf, sizeof(buf), "%c", (int) c);
+       else if (c == ESC)
+               strcpy(buf, "ESC");
+#if IS_EBCDIC_HOST
+       else if (!binary_char(c) && c < 64)
+               SNPRINTF1(buf, sizeof(buf), "^%c",
+               /*
+                * This array roughly inverts CONTROL() #defined in less.h,
+                * and should be kept in sync with CONTROL() and IBM-1047.
+                */
+               "@ABC.I.?...KLMNO"
+               "PQRS.JH.XY.."
+               "\\]^_"
+               "......W[.....EFG"
+               "..V....D....TU.Z"[c]);
+#else
+       else if (c < 128 && !control_char(c ^ 0100))
+               SNPRINTF1(buf, sizeof(buf), "^%c", (int) (c ^ 0100));
+#endif
+       else
+               SNPRINTF1(buf, sizeof(buf), binfmt, c);
+       return (buf);
+}
+
+/*
+ * Return the printable form of a UTF-8 character.
+ */
+       public char *
+prutfchar(ch)
+       LWCHAR ch;
+{
+       static char buf[32];
+
+       if (ch == ESC)
+               strcpy(buf, "ESC");
+       else if (ch < 128 && control_char(ch))
+       {
+               if (!control_char(ch ^ 0100))
+                       SNPRINTF1(buf, sizeof(buf), "^%c", ((char) ch) ^ 0100);
+               else
+                       SNPRINTF1(buf, sizeof(buf), binfmt, (char) ch);
+       } else if (is_ubin_char(ch))
+               SNPRINTF1(buf, sizeof(buf), utfbinfmt, ch);
+       else
+       {
+               int len;
+               if (ch >= 0x80000000)
+               {
+                       len = 3;
+                       ch = 0xFFFD;
+               } else
+               {
+                       len =   (ch < 0x80) ? 1
+                             : (ch < 0x800) ? 2
+                             : (ch < 0x10000) ? 3
+                             : (ch < 0x200000) ? 4
+                             : (ch < 0x4000000) ? 5
+                             : 6;
+               }
+               buf[len] = '\0';
+               if (len == 1)
+                       *buf = (char) ch;
+               else
+               {
+                       *buf = ((1 << len) - 1) << (8 - len);
+                       while (--len > 0)
+                       {
+                               buf[len] = (char) (0x80 | (ch & 0x3F));
+                               ch >>= 6;
+                       }
+                       *buf |= ch;
+               }
+       }
+       return (buf);
+}
+
+/*
+ * Get the length of a UTF-8 character in bytes.
+ */
+       public int
+utf_len(ch)
+       char ch;
+{
+       if ((ch & 0x80) == 0)
+               return 1;
+       if ((ch & 0xE0) == 0xC0)
+               return 2;
+       if ((ch & 0xF0) == 0xE0)
+               return 3;
+       if ((ch & 0xF8) == 0xF0)
+               return 4;
+       if ((ch & 0xFC) == 0xF8)
+               return 5;
+       if ((ch & 0xFE) == 0xFC)
+               return 6;
+       /* Invalid UTF-8 encoding. */
+       return 1;
+}
+
+/*
+ * Is a UTF-8 character well-formed?
+ */
+       public int
+is_utf8_well_formed(s)
+       unsigned char *s;
+{
+       int i;
+       int len;
+
+       if (IS_UTF8_INVALID(s[0]))
+               return (0);
+
+       len = utf_len((char) s[0]);
+       if (len == 1)
+               return (1);
+       if (len == 2)
+       {
+               if (s[0] < 0xC2)
+                   return (0);
+       } else
+       {
+               unsigned char mask;
+               mask = (~((1 << (8-len)) - 1)) & 0xFF;
+               if (s[0] == mask && (s[1] & mask) == 0x80)
+                       return (0);
+       }
+
+       for (i = 1;  i < len;  i++)
+               if (!IS_UTF8_TRAIL(s[i]))
+                       return (0);
+       return (1);
+}
+
+/*
+ * Get the value of a UTF-8 character.
+ */
+       public LWCHAR
+get_wchar(p)
+       char *p;
+{
+       switch (utf_len(p[0]))
+       {
+       case 1:
+       default:
+               /* 0xxxxxxx */
+               return (LWCHAR)
+                       (p[0] & 0xFF);
+       case 2:
+               /* 110xxxxx 10xxxxxx */
+               return (LWCHAR) (
+                       ((p[0] & 0x1F) << 6) |
+                       (p[1] & 0x3F));
+       case 3:
+               /* 1110xxxx 10xxxxxx 10xxxxxx */
+               return (LWCHAR) (
+                       ((p[0] & 0x0F) << 12) |
+                       ((p[1] & 0x3F) << 6) |
+                       (p[2] & 0x3F));
+       case 4:
+               /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+               return (LWCHAR) (
+                       ((p[0] & 0x07) << 18) |
+                       ((p[1] & 0x3F) << 12) | 
+                       ((p[2] & 0x3F) << 6) | 
+                       (p[3] & 0x3F));
+       case 5:
+               /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+               return (LWCHAR) (
+                       ((p[0] & 0x03) << 24) |
+                       ((p[1] & 0x3F) << 18) | 
+                       ((p[2] & 0x3F) << 12) | 
+                       ((p[3] & 0x3F) << 6) | 
+                       (p[4] & 0x3F));
+       case 6:
+               /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+               return (LWCHAR) (
+                       ((p[0] & 0x01) << 30) |
+                       ((p[1] & 0x3F) << 24) | 
+                       ((p[2] & 0x3F) << 18) | 
+                       ((p[3] & 0x3F) << 12) | 
+                       ((p[4] & 0x3F) << 6) | 
+                       (p[5] & 0x3F));
+       }
+}
+
+/*
+ * Store a character into a UTF-8 string.
+ */
+       public void
+put_wchar(pp, ch)
+       char **pp;
+       LWCHAR ch;
+{
+       if (!utf_mode || ch < 0x80) 
+       {
+               /* 0xxxxxxx */
+               *(*pp)++ = (char) ch;
+       } else if (ch < 0x800)
+       {
+               /* 110xxxxx 10xxxxxx */
+               *(*pp)++ = (char) (0xC0 | ((ch >> 6) & 0x1F));
+               *(*pp)++ = (char) (0x80 | (ch & 0x3F));
+       } else if (ch < 0x10000)
+       {
+               /* 1110xxxx 10xxxxxx 10xxxxxx */
+               *(*pp)++ = (char) (0xE0 | ((ch >> 12) & 0x0F));
+               *(*pp)++ = (char) (0x80 | ((ch >> 6) & 0x3F));
+               *(*pp)++ = (char) (0x80 | (ch & 0x3F));
+       } else if (ch < 0x200000)
+       {
+               /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+               *(*pp)++ = (char) (0xF0 | ((ch >> 18) & 0x07));
+               *(*pp)++ = (char) (0x80 | ((ch >> 12) & 0x3F));
+               *(*pp)++ = (char) (0x80 | ((ch >> 6) & 0x3F));
+               *(*pp)++ = (char) (0x80 | (ch & 0x3F));
+       } else if (ch < 0x4000000)
+       {
+               /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+               *(*pp)++ = (char) (0xF0 | ((ch >> 24) & 0x03));
+               *(*pp)++ = (char) (0x80 | ((ch >> 18) & 0x3F));
+               *(*pp)++ = (char) (0x80 | ((ch >> 12) & 0x3F));
+               *(*pp)++ = (char) (0x80 | ((ch >> 6) & 0x3F));
+               *(*pp)++ = (char) (0x80 | (ch & 0x3F));
+       } else 
+       {
+               /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+               *(*pp)++ = (char) (0xF0 | ((ch >> 30) & 0x01));
+               *(*pp)++ = (char) (0x80 | ((ch >> 24) & 0x3F));
+               *(*pp)++ = (char) (0x80 | ((ch >> 18) & 0x3F));
+               *(*pp)++ = (char) (0x80 | ((ch >> 12) & 0x3F));
+               *(*pp)++ = (char) (0x80 | ((ch >> 6) & 0x3F));
+               *(*pp)++ = (char) (0x80 | (ch & 0x3F));
+       }
+}
+
+/*
+ * Step forward or backward one character in a string.
+ */
+       public LWCHAR
+step_char(pp, dir, limit)
+       char **pp;
+       signed int dir;
+       char *limit;
+{
+       LWCHAR ch;
+       int len;
+       char *p = *pp;
+
+       if (!utf_mode)
+       {
+               /* It's easy if chars are one byte. */
+               if (dir > 0)
+                       ch = (LWCHAR) ((p < limit) ? *p++ : 0);
+               else
+                       ch = (LWCHAR) ((p > limit) ? *--p : 0);
+       } else if (dir > 0)
+       {
+               len = utf_len(*p);
+               if (p + len > limit)
+               {
+                       ch = 0;
+                       p = limit;
+               } else
+               {
+                       ch = get_wchar(p);
+                       p += len;
+               }
+       } else
+       {
+               while (p > limit && IS_UTF8_TRAIL(p[-1]))
+                       p--;
+               if (p > limit)
+                       ch = get_wchar(--p);
+               else
+                       ch = 0;
+       }
+       *pp = p;
+       return ch;
+}
+
+/*
+ * Unicode characters data
+ */
+struct wchar_range { LWCHAR first, last; };
+
+/*
+ * Characters with general category values
+ *     Mn: Mark, Nonspacing
+ *     Me: Mark, Enclosing
+ * Last synched with
+ *     <http://www.unicode.org/Public/5.0.0/ucd/UnicodeData-5.0.0d7.txt>
+ *     dated 2005-11-30T00:58:48Z
+ */
+static struct wchar_range comp_table[] = {
+       {  0x0300,  0x036F} /* Mn */, {  0x0483,  0x0486} /* Mn */,
+       {  0x0488,  0x0489} /* Me */,
+       {  0x0591,  0x05BD} /* Mn */, {  0x05BF,  0x05BF} /* Mn */,
+       {  0x05C1,  0x05C2} /* Mn */, {  0x05C4,  0x05C5} /* Mn */,
+       {  0x05C7,  0x05C7} /* Mn */, {  0x0610,  0x0615} /* Mn */,
+       {  0x064B,  0x065E} /* Mn */, {  0x0670,  0x0670} /* Mn */,
+       {  0x06D6,  0x06DC} /* Mn */,
+       {  0x06DE,  0x06DE} /* Me */,
+       {  0x06DF,  0x06E4} /* Mn */, {  0x06E7,  0x06E8} /* Mn */,
+       {  0x06EA,  0x06ED} /* Mn */, {  0x0711,  0x0711} /* Mn */,
+       {  0x0730,  0x074A} /* Mn */, {  0x07A6,  0x07B0} /* Mn */,
+       {  0x07EB,  0x07F3} /* Mn */, {  0x0901,  0x0902} /* Mn */,
+       {  0x093C,  0x093C} /* Mn */, {  0x0941,  0x0948} /* Mn */,
+       {  0x094D,  0x094D} /* Mn */, {  0x0951,  0x0954} /* Mn */,
+       {  0x0962,  0x0963} /* Mn */, {  0x0981,  0x0981} /* Mn */,
+       {  0x09BC,  0x09BC} /* Mn */, {  0x09C1,  0x09C4} /* Mn */,
+       {  0x09CD,  0x09CD} /* Mn */, {  0x09E2,  0x09E3} /* Mn */,
+       {  0x0A01,  0x0A02} /* Mn */, {  0x0A3C,  0x0A3C} /* Mn */,
+       {  0x0A41,  0x0A42} /* Mn */, {  0x0A47,  0x0A48} /* Mn */,
+       {  0x0A4B,  0x0A4D} /* Mn */, {  0x0A70,  0x0A71} /* Mn */,
+       {  0x0A81,  0x0A82} /* Mn */, {  0x0ABC,  0x0ABC} /* Mn */,
+       {  0x0AC1,  0x0AC5} /* Mn */, {  0x0AC7,  0x0AC8} /* Mn */,
+       {  0x0ACD,  0x0ACD} /* Mn */, {  0x0AE2,  0x0AE3} /* Mn */,
+       {  0x0B01,  0x0B01} /* Mn */, {  0x0B3C,  0x0B3C} /* Mn */,
+       {  0x0B3F,  0x0B3F} /* Mn */, {  0x0B41,  0x0B43} /* Mn */,
+       {  0x0B4D,  0x0B4D} /* Mn */, {  0x0B56,  0x0B56} /* Mn */,
+       {  0x0B82,  0x0B82} /* Mn */, {  0x0BC0,  0x0BC0} /* Mn */,
+       {  0x0BCD,  0x0BCD} /* Mn */, {  0x0C3E,  0x0C40} /* Mn */,
+       {  0x0C46,  0x0C48} /* Mn */, {  0x0C4A,  0x0C4D} /* Mn */,
+       {  0x0C55,  0x0C56} /* Mn */, {  0x0CBC,  0x0CBC} /* Mn */,
+       {  0x0CBF,  0x0CBF} /* Mn */, {  0x0CC6,  0x0CC6} /* Mn */,
+       {  0x0CCC,  0x0CCD} /* Mn */, {  0x0CE2,  0x0CE3} /* Mn */,
+       {  0x0D41,  0x0D43} /* Mn */, {  0x0D4D,  0x0D4D} /* Mn */,
+       {  0x0DCA,  0x0DCA} /* Mn */, {  0x0DD2,  0x0DD4} /* Mn */,
+       {  0x0DD6,  0x0DD6} /* Mn */, {  0x0E31,  0x0E31} /* Mn */,
+       {  0x0E34,  0x0E3A} /* Mn */, {  0x0E47,  0x0E4E} /* Mn */,
+       {  0x0EB1,  0x0EB1} /* Mn */, {  0x0EB4,  0x0EB9} /* Mn */,
+       {  0x0EBB,  0x0EBC} /* Mn */, {  0x0EC8,  0x0ECD} /* Mn */,
+       {  0x0F18,  0x0F19} /* Mn */, {  0x0F35,  0x0F35} /* Mn */,
+       {  0x0F37,  0x0F37} /* Mn */, {  0x0F39,  0x0F39} /* Mn */,
+       {  0x0F71,  0x0F7E} /* Mn */, {  0x0F80,  0x0F84} /* Mn */,
+       {  0x0F86,  0x0F87} /* Mn */, {  0x0F90,  0x0F97} /* Mn */,
+       {  0x0F99,  0x0FBC} /* Mn */, {  0x0FC6,  0x0FC6} /* Mn */,
+       {  0x102D,  0x1030} /* Mn */, {  0x1032,  0x1032} /* Mn */,
+       {  0x1036,  0x1037} /* Mn */, {  0x1039,  0x1039} /* Mn */,
+       {  0x1058,  0x1059} /* Mn */, {  0x135F,  0x135F} /* Mn */,
+       {  0x1712,  0x1714} /* Mn */, {  0x1732,  0x1734} /* Mn */,
+       {  0x1752,  0x1753} /* Mn */, {  0x1772,  0x1773} /* Mn */,
+       {  0x17B7,  0x17BD} /* Mn */, {  0x17C6,  0x17C6} /* Mn */,
+       {  0x17C9,  0x17D3} /* Mn */, {  0x17DD,  0x17DD} /* Mn */,
+       {  0x180B,  0x180D} /* Mn */, {  0x18A9,  0x18A9} /* Mn */,
+       {  0x1920,  0x1922} /* Mn */, {  0x1927,  0x1928} /* Mn */,
+       {  0x1932,  0x1932} /* Mn */, {  0x1939,  0x193B} /* Mn */,
+       {  0x1A17,  0x1A18} /* Mn */, {  0x1B00,  0x1B03} /* Mn */,
+       {  0x1B34,  0x1B34} /* Mn */, {  0x1B36,  0x1B3A} /* Mn */,
+       {  0x1B3C,  0x1B3C} /* Mn */, {  0x1B42,  0x1B42} /* Mn */,
+       {  0x1B6B,  0x1B73} /* Mn */, {  0x1DC0,  0x1DCA} /* Mn */,
+       {  0x1DFE,  0x1DFF} /* Mn */, {  0x20D0,  0x20DC} /* Mn */,
+       {  0x20DD,  0x20E0} /* Me */,
+       {  0x20E1,  0x20E1} /* Mn */,
+       {  0x20E2,  0x20E4} /* Me */,
+       {  0x20E5,  0x20EF} /* Mn */, {  0x302A,  0x302F} /* Mn */,
+       {  0x3099,  0x309A} /* Mn */, {  0xA806,  0xA806} /* Mn */,
+       {  0xA80B,  0xA80B} /* Mn */, {  0xA825,  0xA826} /* Mn */,
+       {  0xFB1E,  0xFB1E} /* Mn */, {  0xFE00,  0xFE0F} /* Mn */,
+       {  0xFE20,  0xFE23} /* Mn */, { 0x10A01, 0x10A03} /* Mn */,
+       { 0x10A05, 0x10A06} /* Mn */, { 0x10A0C, 0x10A0F} /* Mn */,
+       { 0x10A38, 0x10A3A} /* Mn */, { 0x10A3F, 0x10A3F} /* Mn */,
+       { 0x1D167, 0x1D169} /* Mn */, { 0x1D17B, 0x1D182} /* Mn */,
+       { 0x1D185, 0x1D18B} /* Mn */, { 0x1D1AA, 0x1D1AD} /* Mn */,
+       { 0x1D242, 0x1D244} /* Mn */, { 0xE0100, 0xE01EF} /* Mn */,
+};
+
+/*
+ * Special pairs, not ranges.
+ */
+static struct wchar_range comb_table[] = {
+       {0x0644,0x0622}, {0x0644,0x0623}, {0x0644,0x0625}, {0x0644,0x0627},
+};
+
+/*
+ * Characters with general category values
+ *     Cc: Other, Control
+ *     Cf: Other, Format
+ *     Cs: Other, Surrogate
+ *     Co: Other, Private Use
+ *     Cn: Other, Not Assigned
+ *     Zl: Separator, Line
+ *     Zp: Separator, Paragraph
+ * Last synched with
+ *     <http://www.unicode.org/Public/5.0.0/ucd/UnicodeData-5.0.0d7.txt>
+ *     dated 2005-11-30T00:58:48Z
+ */
+static struct wchar_range ubin_table[] = {
+       {  0x0000,  0x0007} /* Cc */, 
+       {  0x000B,  0x000C} /* Cc */, 
+       {  0x000E,  0x001A} /* Cc */, 
+       {  0x001C,  0x001F} /* Cc */, 
+       {  0x007F,  0x009F} /* Cc */,
+#if 0
+       {  0x00AD,  0x00AD} /* Cf */,
+#endif
+       {  0x0370,  0x0373} /* Cn */, {  0x0376,  0x0379} /* Cn */,
+       {  0x037F,  0x0383} /* Cn */, {  0x038B,  0x038B} /* Cn */,
+       {  0x038D,  0x038D} /* Cn */, {  0x03A2,  0x03A2} /* Cn */,
+       {  0x03CF,  0x03CF} /* Cn */, {  0x0487,  0x0487} /* Cn */,
+       {  0x0514,  0x0530} /* Cn */, {  0x0557,  0x0558} /* Cn */,
+       {  0x0560,  0x0560} /* Cn */, {  0x0588,  0x0588} /* Cn */,
+       {  0x058B,  0x0590} /* Cn */, {  0x05C8,  0x05CF} /* Cn */,
+       {  0x05EB,  0x05EF} /* Cn */, {  0x05F5,  0x05FF} /* Cn */,
+#if 0
+       {  0x0600,  0x0603} /* Cf */,
+#endif
+       {  0x0604,  0x060A} /* Cn */, {  0x0616,  0x061A} /* Cn */,
+       {  0x061C,  0x061D} /* Cn */, {  0x0620,  0x0620} /* Cn */,
+       {  0x063B,  0x063F} /* Cn */, {  0x065F,  0x065F} /* Cn */,
+#if 0
+       {  0x06DD,  0x06DD} /* Cf */,
+#endif
+       {  0x070E,  0x070E} /* Cn */,
+#if 0
+       {  0x070F,  0x070F} /* Cf */,
+#endif
+       {  0x074B,  0x074C} /* Cn */, {  0x076E,  0x077F} /* Cn */,
+       {  0x07B2,  0x07BF} /* Cn */, {  0x07FB,  0x0900} /* Cn */,
+       {  0x093A,  0x093B} /* Cn */, {  0x094E,  0x094F} /* Cn */,
+       {  0x0955,  0x0957} /* Cn */, {  0x0971,  0x097A} /* Cn */,
+       {  0x0980,  0x0980} /* Cn */, {  0x0984,  0x0984} /* Cn */,
+       {  0x098D,  0x098E} /* Cn */, {  0x0991,  0x0992} /* Cn */,
+       {  0x09A9,  0x09A9} /* Cn */, {  0x09B1,  0x09B1} /* Cn */,
+       {  0x09B3,  0x09B5} /* Cn */, {  0x09BA,  0x09BB} /* Cn */,
+       {  0x09C5,  0x09C6} /* Cn */, {  0x09C9,  0x09CA} /* Cn */,
+       {  0x09CF,  0x09D6} /* Cn */, {  0x09D8,  0x09DB} /* Cn */,
+       {  0x09DE,  0x09DE} /* Cn */, {  0x09E4,  0x09E5} /* Cn */,
+       {  0x09FB,  0x0A00} /* Cn */, {  0x0A04,  0x0A04} /* Cn */,
+       {  0x0A0B,  0x0A0E} /* Cn */, {  0x0A11,  0x0A12} /* Cn */,
+       {  0x0A29,  0x0A29} /* Cn */, {  0x0A31,  0x0A31} /* Cn */,
+       {  0x0A34,  0x0A34} /* Cn */, {  0x0A37,  0x0A37} /* Cn */,
+       {  0x0A3A,  0x0A3B} /* Cn */, {  0x0A3D,  0x0A3D} /* Cn */,
+       {  0x0A43,  0x0A46} /* Cn */, {  0x0A49,  0x0A4A} /* Cn */,
+       {  0x0A4E,  0x0A58} /* Cn */, {  0x0A5D,  0x0A5D} /* Cn */,
+       {  0x0A5F,  0x0A65} /* Cn */, {  0x0A75,  0x0A80} /* Cn */,
+       {  0x0A84,  0x0A84} /* Cn */, {  0x0A8E,  0x0A8E} /* Cn */,
+       {  0x0A92,  0x0A92} /* Cn */, {  0x0AA9,  0x0AA9} /* Cn */,
+       {  0x0AB1,  0x0AB1} /* Cn */, {  0x0AB4,  0x0AB4} /* Cn */,
+       {  0x0ABA,  0x0ABB} /* Cn */, {  0x0AC6,  0x0AC6} /* Cn */,
+       {  0x0ACA,  0x0ACA} /* Cn */, {  0x0ACE,  0x0ACF} /* Cn */,
+       {  0x0AD1,  0x0ADF} /* Cn */, {  0x0AE4,  0x0AE5} /* Cn */,
+       {  0x0AF0,  0x0AF0} /* Cn */, {  0x0AF2,  0x0B00} /* Cn */,
+       {  0x0B04,  0x0B04} /* Cn */, {  0x0B0D,  0x0B0E} /* Cn */,
+       {  0x0B11,  0x0B12} /* Cn */, {  0x0B29,  0x0B29} /* Cn */,
+       {  0x0B31,  0x0B31} /* Cn */, {  0x0B34,  0x0B34} /* Cn */,
+       {  0x0B3A,  0x0B3B} /* Cn */, {  0x0B44,  0x0B46} /* Cn */,
+       {  0x0B49,  0x0B4A} /* Cn */, {  0x0B4E,  0x0B55} /* Cn */,
+       {  0x0B58,  0x0B5B} /* Cn */, {  0x0B5E,  0x0B5E} /* Cn */,
+       {  0x0B62,  0x0B65} /* Cn */, {  0x0B72,  0x0B81} /* Cn */,
+       {  0x0B84,  0x0B84} /* Cn */, {  0x0B8B,  0x0B8D} /* Cn */,
+       {  0x0B91,  0x0B91} /* Cn */, {  0x0B96,  0x0B98} /* Cn */,
+       {  0x0B9B,  0x0B9B} /* Cn */, {  0x0B9D,  0x0B9D} /* Cn */,
+       {  0x0BA0,  0x0BA2} /* Cn */, {  0x0BA5,  0x0BA7} /* Cn */,
+       {  0x0BAB,  0x0BAD} /* Cn */, {  0x0BBA,  0x0BBD} /* Cn */,
+       {  0x0BC3,  0x0BC5} /* Cn */, {  0x0BC9,  0x0BC9} /* Cn */,
+       {  0x0BCE,  0x0BD6} /* Cn */, {  0x0BD8,  0x0BE5} /* Cn */,
+       {  0x0BFB,  0x0C00} /* Cn */, {  0x0C04,  0x0C04} /* Cn */,
+       {  0x0C0D,  0x0C0D} /* Cn */, {  0x0C11,  0x0C11} /* Cn */,
+       {  0x0C29,  0x0C29} /* Cn */, {  0x0C34,  0x0C34} /* Cn */,
+       {  0x0C3A,  0x0C3D} /* Cn */, {  0x0C45,  0x0C45} /* Cn */,
+       {  0x0C49,  0x0C49} /* Cn */, {  0x0C4E,  0x0C54} /* Cn */,
+       {  0x0C57,  0x0C5F} /* Cn */, {  0x0C62,  0x0C65} /* Cn */,
+       {  0x0C70,  0x0C81} /* Cn */, {  0x0C84,  0x0C84} /* Cn */,
+       {  0x0C8D,  0x0C8D} /* Cn */, {  0x0C91,  0x0C91} /* Cn */,
+       {  0x0CA9,  0x0CA9} /* Cn */, {  0x0CB4,  0x0CB4} /* Cn */,
+       {  0x0CBA,  0x0CBB} /* Cn */, {  0x0CC5,  0x0CC5} /* Cn */,
+       {  0x0CC9,  0x0CC9} /* Cn */, {  0x0CCE,  0x0CD4} /* Cn */,
+       {  0x0CD7,  0x0CDD} /* Cn */, {  0x0CDF,  0x0CDF} /* Cn */,
+       {  0x0CE4,  0x0CE5} /* Cn */, {  0x0CF0,  0x0CF0} /* Cn */,
+       {  0x0CF3,  0x0D01} /* Cn */, {  0x0D04,  0x0D04} /* Cn */,
+       {  0x0D0D,  0x0D0D} /* Cn */, {  0x0D11,  0x0D11} /* Cn */,
+       {  0x0D29,  0x0D29} /* Cn */, {  0x0D3A,  0x0D3D} /* Cn */,
+       {  0x0D44,  0x0D45} /* Cn */, {  0x0D49,  0x0D49} /* Cn */,
+       {  0x0D4E,  0x0D56} /* Cn */, {  0x0D58,  0x0D5F} /* Cn */,
+       {  0x0D62,  0x0D65} /* Cn */, {  0x0D70,  0x0D81} /* Cn */,
+       {  0x0D84,  0x0D84} /* Cn */, {  0x0D97,  0x0D99} /* Cn */,
+       {  0x0DB2,  0x0DB2} /* Cn */, {  0x0DBC,  0x0DBC} /* Cn */,
+       {  0x0DBE,  0x0DBF} /* Cn */, {  0x0DC7,  0x0DC9} /* Cn */,
+       {  0x0DCB,  0x0DCE} /* Cn */, {  0x0DD5,  0x0DD5} /* Cn */,
+       {  0x0DD7,  0x0DD7} /* Cn */, {  0x0DE0,  0x0DF1} /* Cn */,
+       {  0x0DF5,  0x0E00} /* Cn */, {  0x0E3B,  0x0E3E} /* Cn */,
+       {  0x0E5C,  0x0E80} /* Cn */, {  0x0E83,  0x0E83} /* Cn */,
+       {  0x0E85,  0x0E86} /* Cn */, {  0x0E89,  0x0E89} /* Cn */,
+       {  0x0E8B,  0x0E8C} /* Cn */, {  0x0E8E,  0x0E93} /* Cn */,
+       {  0x0E98,  0x0E98} /* Cn */, {  0x0EA0,  0x0EA0} /* Cn */,
+       {  0x0EA4,  0x0EA4} /* Cn */, {  0x0EA6,  0x0EA6} /* Cn */,
+       {  0x0EA8,  0x0EA9} /* Cn */, {  0x0EAC,  0x0EAC} /* Cn */,
+       {  0x0EBA,  0x0EBA} /* Cn */, {  0x0EBE,  0x0EBF} /* Cn */,
+       {  0x0EC5,  0x0EC5} /* Cn */, {  0x0EC7,  0x0EC7} /* Cn */,
+       {  0x0ECE,  0x0ECF} /* Cn */, {  0x0EDA,  0x0EDB} /* Cn */,
+       {  0x0EDE,  0x0EFF} /* Cn */, {  0x0F48,  0x0F48} /* Cn */,
+       {  0x0F6B,  0x0F70} /* Cn */, {  0x0F8C,  0x0F8F} /* Cn */,
+       {  0x0F98,  0x0F98} /* Cn */, {  0x0FBD,  0x0FBD} /* Cn */,
+       {  0x0FCD,  0x0FCE} /* Cn */, {  0x0FD2,  0x0FFF} /* Cn */,
+       {  0x1022,  0x1022} /* Cn */, {  0x1028,  0x1028} /* Cn */,
+       {  0x102B,  0x102B} /* Cn */, {  0x1033,  0x1035} /* Cn */,
+       {  0x103A,  0x103F} /* Cn */, {  0x105A,  0x109F} /* Cn */,
+       {  0x10C6,  0x10CF} /* Cn */, {  0x10FD,  0x10FF} /* Cn */,
+       {  0x115A,  0x115E} /* Cn */, {  0x11A3,  0x11A7} /* Cn */,
+       {  0x11FA,  0x11FF} /* Cn */, {  0x1249,  0x1249} /* Cn */,
+       {  0x124E,  0x124F} /* Cn */, {  0x1257,  0x1257} /* Cn */,
+       {  0x1259,  0x1259} /* Cn */, {  0x125E,  0x125F} /* Cn */,
+       {  0x1289,  0x1289} /* Cn */, {  0x128E,  0x128F} /* Cn */,
+       {  0x12B1,  0x12B1} /* Cn */, {  0x12B6,  0x12B7} /* Cn */,
+       {  0x12BF,  0x12BF} /* Cn */, {  0x12C1,  0x12C1} /* Cn */,
+       {  0x12C6,  0x12C7} /* Cn */, {  0x12D7,  0x12D7} /* Cn */,
+       {  0x1311,  0x1311} /* Cn */, {  0x1316,  0x1317} /* Cn */,
+       {  0x135B,  0x135E} /* Cn */, {  0x137D,  0x137F} /* Cn */,
+       {  0x139A,  0x139F} /* Cn */, {  0x13F5,  0x1400} /* Cn */,
+       {  0x1677,  0x167F} /* Cn */, {  0x169D,  0x169F} /* Cn */,
+       {  0x16F1,  0x16FF} /* Cn */, {  0x170D,  0x170D} /* Cn */,
+       {  0x1715,  0x171F} /* Cn */, {  0x1737,  0x173F} /* Cn */,
+       {  0x1754,  0x175F} /* Cn */, {  0x176D,  0x176D} /* Cn */,
+       {  0x1771,  0x1771} /* Cn */, {  0x1774,  0x177F} /* Cn */,
+#if 0
+       {  0x17B4,  0x17B5} /* Cf */,
+#endif
+       {  0x17DE,  0x17DF} /* Cn */, {  0x17EA,  0x17EF} /* Cn */,
+       {  0x17FA,  0x17FF} /* Cn */, {  0x180F,  0x180F} /* Cn */,
+       {  0x181A,  0x181F} /* Cn */, {  0x1878,  0x187F} /* Cn */,
+       {  0x18AA,  0x18FF} /* Cn */, {  0x191D,  0x191F} /* Cn */,
+       {  0x192C,  0x192F} /* Cn */, {  0x193C,  0x193F} /* Cn */,
+       {  0x1941,  0x1943} /* Cn */, {  0x196E,  0x196F} /* Cn */,
+       {  0x1975,  0x197F} /* Cn */, {  0x19AA,  0x19AF} /* Cn */,
+       {  0x19CA,  0x19CF} /* Cn */, {  0x19DA,  0x19DD} /* Cn */,
+       {  0x1A1C,  0x1A1D} /* Cn */, {  0x1A20,  0x1AFF} /* Cn */,
+       {  0x1B4C,  0x1B4F} /* Cn */, {  0x1B7D,  0x1CFF} /* Cn */,
+       {  0x1DCB,  0x1DFD} /* Cn */, {  0x1E9C,  0x1E9F} /* Cn */,
+       {  0x1EFA,  0x1EFF} /* Cn */, {  0x1F16,  0x1F17} /* Cn */,
+       {  0x1F1E,  0x1F1F} /* Cn */, {  0x1F46,  0x1F47} /* Cn */,
+       {  0x1F4E,  0x1F4F} /* Cn */, {  0x1F58,  0x1F58} /* Cn */,
+       {  0x1F5A,  0x1F5A} /* Cn */, {  0x1F5C,  0x1F5C} /* Cn */,
+       {  0x1F5E,  0x1F5E} /* Cn */, {  0x1F7E,  0x1F7F} /* Cn */,
+       {  0x1FB5,  0x1FB5} /* Cn */, {  0x1FC5,  0x1FC5} /* Cn */,
+       {  0x1FD4,  0x1FD5} /* Cn */, {  0x1FDC,  0x1FDC} /* Cn */,
+       {  0x1FF0,  0x1FF1} /* Cn */, {  0x1FF5,  0x1FF5} /* Cn */,
+       {  0x1FFF,  0x1FFF} /* Cn */,
+       {  0x200B,  0x200F} /* Cf */,
+       {  0x2028,  0x2028} /* Zl */,
+       {  0x2029,  0x2029} /* Zp */,
+       {  0x202A,  0x202E} /* Cf */,
+       {  0x2060,  0x2063} /* Cf */,
+       {  0x2064,  0x2069} /* Cn */,
+       {  0x206A,  0x206F} /* Cf */,
+       {  0x2072,  0x2073} /* Cn */, {  0x208F,  0x208F} /* Cn */,
+       {  0x2095,  0x209F} /* Cn */, {  0x20B6,  0x20CF} /* Cn */,
+       {  0x20F0,  0x20FF} /* Cn */, {  0x214F,  0x2152} /* Cn */,
+       {  0x2185,  0x218F} /* Cn */, {  0x23E8,  0x23FF} /* Cn */,
+       {  0x2427,  0x243F} /* Cn */, {  0x244B,  0x245F} /* Cn */,
+       {  0x269D,  0x269F} /* Cn */, {  0x26B3,  0x2700} /* Cn */,
+       {  0x2705,  0x2705} /* Cn */, {  0x270A,  0x270B} /* Cn */,
+       {  0x2728,  0x2728} /* Cn */, {  0x274C,  0x274C} /* Cn */,
+       {  0x274E,  0x274E} /* Cn */, {  0x2753,  0x2755} /* Cn */,
+       {  0x2757,  0x2757} /* Cn */, {  0x275F,  0x2760} /* Cn */,
+       {  0x2795,  0x2797} /* Cn */, {  0x27B0,  0x27B0} /* Cn */,
+       {  0x27BF,  0x27BF} /* Cn */, {  0x27CB,  0x27CF} /* Cn */,
+       {  0x27EC,  0x27EF} /* Cn */, {  0x2B1B,  0x2B1F} /* Cn */,
+       {  0x2B24,  0x2BFF} /* Cn */, {  0x2C2F,  0x2C2F} /* Cn */,
+       {  0x2C5F,  0x2C5F} /* Cn */, {  0x2C6D,  0x2C73} /* Cn */,
+       {  0x2C78,  0x2C7F} /* Cn */, {  0x2CEB,  0x2CF8} /* Cn */,
+       {  0x2D26,  0x2D2F} /* Cn */, {  0x2D66,  0x2D6E} /* Cn */,
+       {  0x2D70,  0x2D7F} /* Cn */, {  0x2D97,  0x2D9F} /* Cn */,
+       {  0x2DA7,  0x2DA7} /* Cn */, {  0x2DAF,  0x2DAF} /* Cn */,
+       {  0x2DB7,  0x2DB7} /* Cn */, {  0x2DBF,  0x2DBF} /* Cn */,
+       {  0x2DC7,  0x2DC7} /* Cn */, {  0x2DCF,  0x2DCF} /* Cn */,
+       {  0x2DD7,  0x2DD7} /* Cn */, {  0x2DDF,  0x2DFF} /* Cn */,
+       {  0x2E18,  0x2E1B} /* Cn */, {  0x2E1E,  0x2E7F} /* Cn */,
+       {  0x2E9A,  0x2E9A} /* Cn */, {  0x2EF4,  0x2EFF} /* Cn */,
+       {  0x2FD6,  0x2FEF} /* Cn */, {  0x2FFC,  0x2FFF} /* Cn */,
+       {  0x3040,  0x3040} /* Cn */, {  0x3097,  0x3098} /* Cn */,
+       {  0x3100,  0x3104} /* Cn */, {  0x312D,  0x3130} /* Cn */,
+       {  0x318F,  0x318F} /* Cn */, {  0x31B8,  0x31BF} /* Cn */,
+       {  0x31D0,  0x31EF} /* Cn */, {  0x321F,  0x321F} /* Cn */,
+       {  0x3244,  0x324F} /* Cn */, {  0x32FF,  0x32FF} /* Cn */,
+       {  0x4DB6,  0x4DBF} /* Cn */, {  0x9FBC,  0x9FFF} /* Cn */,
+       {  0xA48D,  0xA48F} /* Cn */, {  0xA4C7,  0xA6FF} /* Cn */,
+       {  0xA71B,  0xA71F} /* Cn */, {  0xA722,  0xA7FF} /* Cn */,
+       {  0xA82C,  0xA83F} /* Cn */, {  0xA878,  0xABFF} /* Cn */,
+       {  0xD7A4,  0xD7FF} /* Cn */,
+       {  0xD800,  0xDFFF} /* Cs */,
+       {  0xE000,  0xF8FF} /* Co */,
+       {  0xFA2E,  0xFA2F} /* Cn */, {  0xFA6B,  0xFA6F} /* Cn */,
+       {  0xFADA,  0xFAFF} /* Cn */, {  0xFB07,  0xFB12} /* Cn */,
+       {  0xFB18,  0xFB1C} /* Cn */, {  0xFB37,  0xFB37} /* Cn */,
+       {  0xFB3D,  0xFB3D} /* Cn */, {  0xFB3F,  0xFB3F} /* Cn */,
+       {  0xFB42,  0xFB42} /* Cn */, {  0xFB45,  0xFB45} /* Cn */,
+       {  0xFBB2,  0xFBD2} /* Cn */, {  0xFD40,  0xFD4F} /* Cn */,
+       {  0xFD90,  0xFD91} /* Cn */, {  0xFDC8,  0xFDEF} /* Cn */,
+       {  0xFDFE,  0xFDFF} /* Cn */, {  0xFE1A,  0xFE1F} /* Cn */,
+       {  0xFE24,  0xFE2F} /* Cn */, {  0xFE53,  0xFE53} /* Cn */,
+       {  0xFE67,  0xFE67} /* Cn */, {  0xFE6C,  0xFE6F} /* Cn */,
+       {  0xFE75,  0xFE75} /* Cn */, {  0xFEFD,  0xFEFE} /* Cn */,
+       {  0xFEFF,  0xFEFF} /* Cf */,
+       {  0xFF00,  0xFF00} /* Cn */, {  0xFFBF,  0xFFC1} /* Cn */,
+       {  0xFFC8,  0xFFC9} /* Cn */, {  0xFFD0,  0xFFD1} /* Cn */,
+       {  0xFFD8,  0xFFD9} /* Cn */, {  0xFFDD,  0xFFDF} /* Cn */,
+       {  0xFFE7,  0xFFE7} /* Cn */, {  0xFFEF,  0xFFF8} /* Cn */,
+       {  0xFFF9,  0xFFFB} /* Cf */,
+       {  0xFFFE,  0xFFFF} /* Cn */, { 0x1000C, 0x1000C} /* Cn */,
+       { 0x10027, 0x10027} /* Cn */, { 0x1003B, 0x1003B} /* Cn */,
+       { 0x1003E, 0x1003E} /* Cn */, { 0x1004E, 0x1004F} /* Cn */,
+       { 0x1005E, 0x1007F} /* Cn */, { 0x100FB, 0x100FF} /* Cn */,
+       { 0x10103, 0x10106} /* Cn */, { 0x10134, 0x10136} /* Cn */,
+       { 0x1018B, 0x102FF} /* Cn */, { 0x1031F, 0x1031F} /* Cn */,
+       { 0x10324, 0x1032F} /* Cn */, { 0x1034B, 0x1037F} /* Cn */,
+       { 0x1039E, 0x1039E} /* Cn */, { 0x103C4, 0x103C7} /* Cn */,
+       { 0x103D6, 0x103FF} /* Cn */,
+       { 0x1049E, 0x1049F} /* Cn */, { 0x104AA, 0x107FF} /* Cn */,
+       { 0x10806, 0x10807} /* Cn */, { 0x10809, 0x10809} /* Cn */,
+       { 0x10836, 0x10836} /* Cn */, { 0x10839, 0x1083B} /* Cn */,
+       { 0x1083D, 0x1083E} /* Cn */, { 0x10840, 0x108FF} /* Cn */,
+       { 0x1091A, 0x1091E} /* Cn */, { 0x10920, 0x109FF} /* Cn */,
+       { 0x10A04, 0x10A04} /* Cn */, { 0x10A07, 0x10A0B} /* Cn */,
+       { 0x10A14, 0x10A14} /* Cn */, { 0x10A18, 0x10A18} /* Cn */,
+       { 0x10A34, 0x10A37} /* Cn */, { 0x10A3B, 0x10A3E} /* Cn */,
+       { 0x10A48, 0x10A4F} /* Cn */, { 0x10A59, 0x11FFF} /* Cn */,
+       { 0x1236F, 0x123FF} /* Cn */, { 0x12463, 0x1246F} /* Cn */,
+       { 0x12474, 0x1CFFF} /* Cn */, { 0x1D0F6, 0x1D0FF} /* Cn */,
+       { 0x1D127, 0x1D129} /* Cn */,
+       { 0x1D173, 0x1D17A} /* Cf */,
+       { 0x1D1DE, 0x1D1FF} /* Cn */, { 0x1D246, 0x1D2FF} /* Cn */,
+       { 0x1D357, 0x1D35F} /* Cn */, { 0x1D372, 0x1D3FF} /* Cn */,
+       { 0x1D455, 0x1D455} /* Cn */, { 0x1D49D, 0x1D49D} /* Cn */,
+       { 0x1D4A0, 0x1D4A1} /* Cn */, { 0x1D4A3, 0x1D4A4} /* Cn */,
+       { 0x1D4A7, 0x1D4A8} /* Cn */, { 0x1D4AD, 0x1D4AD} /* Cn */,
+       { 0x1D4BA, 0x1D4BA} /* Cn */, { 0x1D4BC, 0x1D4BC} /* Cn */,
+       { 0x1D4C4, 0x1D4C4} /* Cn */, { 0x1D506, 0x1D506} /* Cn */,
+       { 0x1D50B, 0x1D50C} /* Cn */, { 0x1D515, 0x1D515} /* Cn */,
+       { 0x1D51D, 0x1D51D} /* Cn */, { 0x1D53A, 0x1D53A} /* Cn */,
+       { 0x1D53F, 0x1D53F} /* Cn */, { 0x1D545, 0x1D545} /* Cn */,
+       { 0x1D547, 0x1D549} /* Cn */, { 0x1D551, 0x1D551} /* Cn */,
+       { 0x1D6A6, 0x1D6A7} /* Cn */, { 0x1D7CC, 0x1D7CD} /* Cn */,
+       { 0x1D800, 0x1FFFF} /* Cn */, { 0x2A6D7, 0x2F7FF} /* Cn */,
+       { 0x2FA1E, 0xE0000} /* Cn */,
+       { 0xE0001, 0xE0001} /* Cf */,
+       { 0xE0002, 0xE001F} /* Cn */,
+       { 0xE0020, 0xE007F} /* Cf */,
+       { 0xE0080, 0xE00FF} /* Cn */, { 0xE01F0, 0xEFFFF} /* Cn */,
+       { 0xF0000, 0xFFFFD} /* Co */,
+       { 0xFFFFE, 0xFFFFF} /* Cn */,
+       {0x100000,0x10FFFD} /* Co */,
+       {0x10FFFE,0x10FFFF} /* Cn */,
+       {0x110000,0x7FFFFFFF} /* ISO 10646?? */
+};
+
+/*
+ * Double width characters
+ *     W: East Asian Wide
+ *     F: East Asian Full-width
+ * Unassigned code points may be included when they allow ranges to be merged.
+ * Last synched with
+ *     <http://www.unicode.org/Public/5.0.0/ucd/EastAsianWidth-5.0.0d2.txt>
+ *     dated 2005-11-08T01:32:56Z
+ */
+static struct wchar_range wide_table[] = {
+       {  0x1100,  0x115F} /* W */, {  0x2329,  0x232A} /* W */,
+       {  0x2E80,  0x2FFB} /* W */,
+       {  0x3000,  0x3000} /* F */,
+       {  0x3001,  0x303E} /* W */, {  0x3041,  0x4DB5} /* W */,
+       {  0x4E00,  0x9FBB} /* W */, {  0xA000,  0xA4C6} /* W */,
+       {  0xAC00,  0xD7A3} /* W */, {  0xF900,  0xFAD9} /* W */,
+       {  0xFE10,  0xFE19} /* W */, {  0xFE30,  0xFE6B} /* W */,
+       {  0xFF01,  0xFF60} /* F */, {  0xFFE0,  0xFFE6} /* F */,
+       { 0x20000, 0x2FFFD} /* W */, { 0x30000, 0x3FFFD} /* W */,
+};
+
+       static int
+is_in_table(ch, table, tsize)
+       LWCHAR ch;
+       struct wchar_range table[];
+       int tsize;
+{
+       int hi;
+       int lo;
+
+       /* Binary search in the table. */
+       if (ch < table[0].first)
+               return 0;
+       lo = 0;
+       hi = tsize - 1;
+       while (lo <= hi)
+       {
+               int mid = (lo + hi) / 2;
+               if (ch > table[mid].last)
+                       lo = mid + 1;
+               else if (ch < table[mid].first)
+                       hi = mid - 1;
+               else
+                       return 1;
+       }
+       return 0;
+}
+
+/*
+ * Is a character a UTF-8 composing character?
+ * If a composing character follows any char, the two combine into one glyph.
+ */
+       public int
+is_composing_char(ch)
+       LWCHAR ch;
+{
+       return is_in_table(ch, comp_table, (sizeof(comp_table) / sizeof(*comp_table)));
+}
+
+/*
+ * Should this UTF-8 character be treated as binary?
+ */
+       public int
+is_ubin_char(ch)
+       LWCHAR ch;
+{
+       return is_in_table(ch, ubin_table, (sizeof(ubin_table) / sizeof(*ubin_table)));
+}
+
+/*
+ * Is this a double width UTF-8 character?
+ */
+       public int
+is_wide_char(ch)
+       LWCHAR ch;
+{
+       return is_in_table(ch, wide_table, (sizeof(wide_table) / sizeof(*wide_table)));
+}
+
+/*
+ * Is a character a UTF-8 combining character?
+ * A combining char acts like an ordinary char, but if it follows
+ * a specific char (not any char), the two combine into one glyph.
+ */
+       public int
+is_combining_char(ch1, ch2)
+       LWCHAR ch1;
+       LWCHAR ch2;
+{
+       /* The table is small; use linear search. */
+       int i;
+       for (i = 0;  i < sizeof(comb_table)/sizeof(*comb_table);  i++)
+       {
+               if (ch1 == comb_table[i].first &&
+                   ch2 == comb_table[i].last)
+                       return 1;
+       }
+       return 0;
+}
+
diff --git a/thirdparty/less/charset.h b/thirdparty/less/charset.h
new file mode 100644 (file)
index 0000000..8ccf748
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+#define IS_ASCII_OCTET(c)   (((c) & 0x80) == 0)
+#define IS_UTF8_TRAIL(c)    (((c) & 0xC0) == 0x80)
+#define IS_UTF8_LEAD2(c)    (((c) & 0xE0) == 0xC0)
+#define IS_UTF8_LEAD3(c)    (((c) & 0xF0) == 0xE0)
+#define IS_UTF8_LEAD4(c)    (((c) & 0xF8) == 0xF0)
+#define IS_UTF8_LEAD5(c)    (((c) & 0xFC) == 0xF8)
+#define IS_UTF8_LEAD6(c)    (((c) & 0xFE) == 0xFC)
+#define IS_UTF8_INVALID(c)  (((c) & 0xFE) == 0xFE)
+#define IS_UTF8_LEAD(c)     (((c) & 0xC0) == 0xC0 && !IS_UTF8_INVALID(c))
diff --git a/thirdparty/less/cmd.h b/thirdparty/less/cmd.h
new file mode 100644 (file)
index 0000000..3176b91
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+#define        MAX_USERCMD             500
+#define        MAX_CMDLEN              16
+
+#define        A_B_LINE                2
+#define        A_B_SCREEN              3
+#define        A_B_SCROLL              4
+#define        A_B_SEARCH              5
+#define        A_DIGIT                 6
+#define        A_DISP_OPTION           7
+#define        A_DEBUG                 8
+#define        A_EXAMINE               9
+#define        A_FIRSTCMD              10
+#define        A_FREPAINT              11
+#define        A_F_LINE                12
+#define        A_F_SCREEN              13
+#define        A_F_SCROLL              14
+#define        A_F_SEARCH              15
+#define        A_GOEND                 16
+#define        A_GOLINE                17
+#define        A_GOMARK                18
+#define        A_HELP                  19
+#define        A_NEXT_FILE             20
+#define        A_PERCENT               21
+#define        A_PREFIX                22
+#define        A_PREV_FILE             23
+#define        A_QUIT                  24
+#define        A_REPAINT               25
+#define        A_SETMARK               26
+#define        A_SHELL                 27
+#define        A_STAT                  28
+#define        A_FF_LINE               29
+#define        A_BF_LINE               30
+#define        A_VERSION               31
+#define        A_VISUAL                32
+#define        A_F_WINDOW              33
+#define        A_B_WINDOW              34
+#define        A_F_BRACKET             35
+#define        A_B_BRACKET             36
+#define        A_PIPE                  37
+#define        A_INDEX_FILE            38
+#define        A_UNDO_SEARCH           39
+#define        A_FF_SCREEN             40
+#define        A_LSHIFT                41
+#define        A_RSHIFT                42
+#define        A_AGAIN_SEARCH          43
+#define        A_T_AGAIN_SEARCH        44
+#define        A_REVERSE_SEARCH        45
+#define        A_T_REVERSE_SEARCH      46
+#define        A_OPT_TOGGLE            47
+#define        A_OPT_SET               48
+#define        A_OPT_UNSET             49
+#define        A_F_FOREVER             50
+#define        A_GOPOS                 51
+#define        A_REMOVE_FILE           52
+#define        A_NEXT_TAG              53
+#define        A_PREV_TAG              54
+#define        A_FILTER                55
+
+#define        A_INVALID               100
+#define        A_NOACTION              101
+#define        A_UINVALID              102
+#define        A_END_LIST              103
+#define        A_SPECIAL_KEY           104
+
+#define A_SKIP                 127
+
+#define        A_EXTRA                 0200
+
+
+/* Line editting characters */
+
+#define        EC_BACKSPACE    1
+#define        EC_LINEKILL     2
+#define        EC_RIGHT        3
+#define        EC_LEFT         4
+#define        EC_W_LEFT       5
+#define        EC_W_RIGHT      6
+#define        EC_INSERT       7
+#define        EC_DELETE       8
+#define        EC_HOME         9
+#define        EC_END          10
+#define        EC_W_BACKSPACE  11
+#define        EC_W_DELETE     12
+#define        EC_UP           13
+#define        EC_DOWN         14
+#define        EC_EXPAND       15
+#define        EC_F_COMPLETE   17
+#define        EC_B_COMPLETE   18
+#define        EC_LITERAL      19
+#define        EC_ABORT        20
+
+#define        EC_NOACTION     101
+#define        EC_UINVALID     102
+
+/* Flags for editchar() */
+#define        EC_PEEK         01
+#define        EC_NOHISTORY    02
+#define        EC_NOCOMPLETE   04
+#define        EC_NORIGHTLEFT  010
+
+/* Environment variable stuff */
+#define        EV_OK           01
+
+/* Special keys (keys which output different strings on different terminals) */
+#define SK_SPECIAL_KEY         CONTROL('K')
+#define SK_RIGHT_ARROW         1
+#define SK_LEFT_ARROW          2
+#define SK_UP_ARROW            3
+#define SK_DOWN_ARROW          4
+#define SK_PAGE_UP             5
+#define SK_PAGE_DOWN           6
+#define SK_HOME                        7
+#define SK_END                 8
+#define SK_DELETE              9
+#define SK_INSERT              10
+#define SK_CTL_LEFT_ARROW      11
+#define SK_CTL_RIGHT_ARROW     12
+#define SK_CTL_DELETE          13
+#define SK_F1                  14
+#define SK_BACKTAB             15
+#define SK_CTL_BACKSPACE       16
+#define SK_CONTROL_K           40
diff --git a/thirdparty/less/cmdbuf.c b/thirdparty/less/cmdbuf.c
new file mode 100644 (file)
index 0000000..74a74ff
--- /dev/null
@@ -0,0 +1,1503 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Functions which manipulate the command buffer.
+ * Used only by command() and related functions.
+ */
+
+#include "less.h"
+#include "cmd.h"
+#include "charset.h"
+#if HAVE_STAT
+#include <sys/stat.h>
+#endif
+
+extern int sc_width;
+extern int utf_mode;
+
+static char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */
+static int cmd_col;            /* Current column of the cursor */
+static int prompt_col;         /* Column of cursor just after prompt */
+static char *cp;               /* Pointer into cmdbuf */
+static int cmd_offset;         /* Index into cmdbuf of first displayed char */
+static int literal;            /* Next input char should not be interpreted */
+
+#if TAB_COMPLETE_FILENAME
+static int cmd_complete();
+/*
+ * These variables are statics used by cmd_complete.
+ */
+static int in_completion = 0;
+static char *tk_text;
+static char *tk_original;
+static char *tk_ipoint;
+static char *tk_trial;
+static struct textlist tk_tlist;
+#endif
+
+static int cmd_left();
+static int cmd_right();
+
+#if SPACES_IN_FILENAMES
+public char openquote = '"';
+public char closequote = '"';
+#endif
+
+#if CMD_HISTORY
+
+/* History file */
+#define HISTFILE_FIRST_LINE      ".less-history-file:"
+#define HISTFILE_SEARCH_SECTION  ".search"
+#define HISTFILE_SHELL_SECTION   ".shell"
+
+/*
+ * A mlist structure represents a command history.
+ */
+struct mlist
+{
+       struct mlist *next;
+       struct mlist *prev;
+       struct mlist *curr_mp;
+       char *string;
+       int modified;
+};
+
+/*
+ * These are the various command histories that exist.
+ */
+struct mlist mlist_search =  
+       { &mlist_search,  &mlist_search,  &mlist_search,  NULL, 0 };
+public void * constant ml_search = (void *) &mlist_search;
+
+struct mlist mlist_examine = 
+       { &mlist_examine, &mlist_examine, &mlist_examine, NULL, 0 };
+public void * constant ml_examine = (void *) &mlist_examine;
+
+#if SHELL_ESCAPE || PIPEC
+struct mlist mlist_shell =   
+       { &mlist_shell,   &mlist_shell,   &mlist_shell,   NULL, 0 };
+public void * constant ml_shell = (void *) &mlist_shell;
+#endif
+
+#else /* CMD_HISTORY */
+
+/* If CMD_HISTORY is off, these are just flags. */
+public void * constant ml_search = (void *)1;
+public void * constant ml_examine = (void *)2;
+#if SHELL_ESCAPE || PIPEC
+public void * constant ml_shell = (void *)3;
+#endif
+
+#endif /* CMD_HISTORY */
+
+/*
+ * History for the current command.
+ */
+static struct mlist *curr_mlist = NULL;
+static int curr_cmdflags;
+
+static char cmd_mbc_buf[MAX_UTF_CHAR_LEN];
+static int cmd_mbc_buf_len;
+static int cmd_mbc_buf_index;
+
+
+/*
+ * Reset command buffer (to empty).
+ */
+       public void
+cmd_reset()
+{
+       cp = cmdbuf;
+       *cp = '\0';
+       cmd_col = 0;
+       cmd_offset = 0;
+       literal = 0;
+       cmd_mbc_buf_len = 0;
+}
+
+/*
+ * Clear command line.
+ */
+       public void
+clear_cmd()
+{
+       cmd_col = prompt_col = 0;
+       cmd_mbc_buf_len = 0;
+}
+
+/*
+ * Display a string, usually as a prompt for input into the command buffer.
+ */
+       public void
+cmd_putstr(s)
+       char *s;
+{
+       LWCHAR prev_ch = 0;
+       LWCHAR ch;
+       char *endline = s + strlen(s);
+       while (*s != '\0')
+       {
+               char *ns = s;
+               ch = step_char(&ns, +1, endline);
+               while (s < ns)
+                       putchr(*s++);
+               if (!utf_mode)
+               {
+                       cmd_col++;
+                       prompt_col++;
+               } else if (!is_composing_char(ch) &&
+                          !is_combining_char(prev_ch, ch))
+               {
+                       int width = is_wide_char(ch) ? 2 : 1;
+                       cmd_col += width;
+                       prompt_col += width;
+               }
+               prev_ch = ch;
+       }
+}
+
+/*
+ * How many characters are in the command buffer?
+ */
+       public int
+len_cmdbuf()
+{
+       char *s = cmdbuf;
+       char *endline = s + strlen(s);
+       int len = 0;
+
+       while (*s != '\0')
+       {
+               step_char(&s, +1, endline);
+               len++;
+       }
+       return (len);
+}
+
+/*
+ * Common part of cmd_step_right() and cmd_step_left().
+ */
+       static char *
+cmd_step_common(p, ch, len, pwidth, bswidth)
+       char *p;
+       LWCHAR ch;
+       int len;
+       int *pwidth;
+       int *bswidth;
+{
+       char *pr;
+
+       if (len == 1)
+       {
+               pr = prchar((int) ch);
+               if (pwidth != NULL || bswidth != NULL)
+               {
+                       int len = strlen(pr);
+                       if (pwidth != NULL)
+                               *pwidth = len;
+                       if (bswidth != NULL)
+                               *bswidth = len;
+               }
+       } else
+       {
+               pr = prutfchar(ch);
+               if (pwidth != NULL || bswidth != NULL)
+               {
+                       if (is_composing_char(ch))
+                       {
+                               if (pwidth != NULL)
+                                       *pwidth = 0;
+                               if (bswidth != NULL)
+                                       *bswidth = 0;
+                       } else if (is_ubin_char(ch))
+                       {
+                               int len = strlen(pr);
+                               if (pwidth != NULL)
+                                       *pwidth = len;
+                               if (bswidth != NULL)
+                                       *bswidth = len;
+                       } else
+                       {
+                               LWCHAR prev_ch = step_char(&p, -1, cmdbuf);
+                               if (is_combining_char(prev_ch, ch))
+                               {
+                                       if (pwidth != NULL)
+                                               *pwidth = 0;
+                                       if (bswidth != NULL)
+                                               *bswidth = 0;
+                               } else
+                               {
+                                       if (pwidth != NULL)
+                                               *pwidth = is_wide_char(ch)
+                                                       ?       2
+                                                       :       1;
+                                       if (bswidth != NULL)
+                                               *bswidth = 1;
+                               }
+                       }
+               }
+       }
+
+       return (pr);
+}
+
+/*
+ * Step a pointer one character right in the command buffer.
+ */
+       static char *
+cmd_step_right(pp, pwidth, bswidth)
+       char **pp;
+       int *pwidth;
+       int *bswidth;
+{
+       char *p = *pp;
+       LWCHAR ch = step_char(pp, +1, p + strlen(p));
+
+       return cmd_step_common(p, ch, *pp - p, pwidth, bswidth);
+}
+
+/*
+ * Step a pointer one character left in the command buffer.
+ */
+       static char *
+cmd_step_left(pp, pwidth, bswidth)
+       char **pp;
+       int *pwidth;
+       int *bswidth;
+{
+       char *p = *pp;
+       LWCHAR ch = step_char(pp, -1, cmdbuf);
+
+       return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth);
+}
+
+/*
+ * Repaint the line from cp onwards.
+ * Then position the cursor just after the char old_cp (a pointer into cmdbuf).
+ */
+       static void
+cmd_repaint(old_cp)
+       char *old_cp;
+{
+       /*
+        * Repaint the line from the current position.
+        */
+       clear_eol();
+       while (*cp != '\0')
+       {
+               char *np = cp;
+               int width;
+               char *pr = cmd_step_right(&np, &width, NULL);
+               if (cmd_col + width >= sc_width)
+                       break;
+               cp = np;
+               putstr(pr);
+               cmd_col += width;
+       }
+       while (*cp != '\0')
+       {
+               char *np = cp;
+               int width;
+               char *pr = cmd_step_right(&np, &width, NULL);
+               if (width > 0)
+                       break;
+               cp = np;
+               putstr(pr);
+       }
+
+       /*
+        * Back up the cursor to the correct position.
+        */
+       while (cp > old_cp)
+               cmd_left();
+}
+
+/*
+ * Put the cursor at "home" (just after the prompt),
+ * and set cp to the corresponding char in cmdbuf.
+ */
+       static void
+cmd_home()
+{
+       while (cmd_col > prompt_col)
+       {
+               int width, bswidth;
+
+               cmd_step_left(&cp, &width, &bswidth);
+               while (bswidth-- > 0)
+                       putbs();
+               cmd_col -= width;
+       }
+
+       cp = &cmdbuf[cmd_offset];
+}
+
+/*
+ * Shift the cmdbuf display left a half-screen.
+ */
+       static void
+cmd_lshift()
+{
+       char *s;
+       char *save_cp;
+       int cols;
+
+       /*
+        * Start at the first displayed char, count how far to the
+        * right we'd have to move to reach the center of the screen.
+        */
+       s = cmdbuf + cmd_offset;
+       cols = 0;
+       while (cols < (sc_width - prompt_col) / 2 && *s != '\0')
+       {
+               int width;
+               cmd_step_right(&s, &width, NULL);
+               cols += width;
+       }
+       while (*s != '\0')
+       {
+               int width;
+               char *ns = s;
+               cmd_step_right(&ns, &width, NULL);
+               if (width > 0)
+                       break;
+               s = ns;
+       }
+
+       cmd_offset = s - cmdbuf;
+       save_cp = cp;
+       cmd_home();
+       cmd_repaint(save_cp);
+}
+
+/*
+ * Shift the cmdbuf display right a half-screen.
+ */
+       static void
+cmd_rshift()
+{
+       char *s;
+       char *save_cp;
+       int cols;
+
+       /*
+        * Start at the first displayed char, count how far to the
+        * left we'd have to move to traverse a half-screen width
+        * of displayed characters.
+        */
+       s = cmdbuf + cmd_offset;
+       cols = 0;
+       while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf)
+       {
+               int width;
+               cmd_step_left(&s, &width, NULL);
+               cols += width;
+       }
+
+       cmd_offset = s - cmdbuf;
+       save_cp = cp;
+       cmd_home();
+       cmd_repaint(save_cp);
+}
+
+/*
+ * Move cursor right one character.
+ */
+       static int
+cmd_right()
+{
+       char *pr;
+       char *ncp;
+       int width;
+       
+       if (*cp == '\0')
+       {
+               /* Already at the end of the line. */
+               return (CC_OK);
+       }
+       ncp = cp;
+       pr = cmd_step_right(&ncp, &width, NULL);
+       if (cmd_col + width >= sc_width)
+               cmd_lshift();
+       else if (cmd_col + width == sc_width - 1 && cp[1] != '\0')
+               cmd_lshift();
+       cp = ncp;
+       cmd_col += width;
+       putstr(pr);
+       while (*cp != '\0')
+       {
+               pr = cmd_step_right(&ncp, &width, NULL);
+               if (width > 0)
+                       break;
+               putstr(pr);
+               cp = ncp;
+       }
+       return (CC_OK);
+}
+
+/*
+ * Move cursor left one character.
+ */
+       static int
+cmd_left()
+{
+       char *ncp;
+       int width, bswidth;
+       
+       if (cp <= cmdbuf)
+       {
+               /* Already at the beginning of the line */
+               return (CC_OK);
+       }
+       ncp = cp;
+       while (ncp > cmdbuf)
+       {
+               cmd_step_left(&ncp, &width, &bswidth);
+               if (width > 0)
+                       break;
+       }
+       if (cmd_col < prompt_col + width)
+               cmd_rshift();
+       cp = ncp;
+       cmd_col -= width;
+       while (bswidth-- > 0)
+               putbs();
+       return (CC_OK);
+}
+
+/*
+ * Insert a char into the command buffer, at the current position.
+ */
+       static int
+cmd_ichar(cs, clen)
+       char *cs;
+       int clen;
+{
+       char *s;
+       
+       if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1)
+       {
+               /* No room in the command buffer for another char. */
+               bell();
+               return (CC_ERROR);
+       }
+               
+       /*
+        * Make room for the new character (shift the tail of the buffer right).
+        */
+       for (s = &cmdbuf[strlen(cmdbuf)];  s >= cp;  s--)
+               s[clen] = s[0];
+       /*
+        * Insert the character into the buffer.
+        */
+       for (s = cp;  s < cp + clen;  s++)
+               *s = *cs++;
+       /*
+        * Reprint the tail of the line from the inserted char.
+        */
+       cmd_repaint(cp);
+       cmd_right();
+       return (CC_OK);
+}
+
+/*
+ * Backspace in the command buffer.
+ * Delete the char to the left of the cursor.
+ */
+       static int
+cmd_erase()
+{
+       register char *s;
+       int clen;
+
+       if (cp == cmdbuf)
+       {
+               /*
+                * Backspace past beginning of the buffer:
+                * this usually means abort the command.
+                */
+               return (CC_QUIT);
+       }
+       /*
+        * Move cursor left (to the char being erased).
+        */
+       s = cp;
+       cmd_left();
+       clen = s - cp;
+
+       /*
+        * Remove the char from the buffer (shift the buffer left).
+        */
+       for (s = cp;  ;  s++)
+       {
+               s[0] = s[clen];
+               if (s[0] == '\0')
+                       break;
+       }
+
+       /*
+        * Repaint the buffer after the erased char.
+        */
+       cmd_repaint(cp);
+       
+       /*
+        * We say that erasing the entire command string causes us
+        * to abort the current command, if CF_QUIT_ON_ERASE is set.
+        */
+       if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0')
+               return (CC_QUIT);
+       return (CC_OK);
+}
+
+/*
+ * Delete the char under the cursor.
+ */
+       static int
+cmd_delete()
+{
+       if (*cp == '\0')
+       {
+               /* At end of string; there is no char under the cursor. */
+               return (CC_OK);
+       }
+       /*
+        * Move right, then use cmd_erase.
+        */
+       cmd_right();
+       cmd_erase();
+       return (CC_OK);
+}
+
+/*
+ * Delete the "word" to the left of the cursor.
+ */
+       static int
+cmd_werase()
+{
+       if (cp > cmdbuf && cp[-1] == ' ')
+       {
+               /*
+                * If the char left of cursor is a space,
+                * erase all the spaces left of cursor (to the first non-space).
+                */
+               while (cp > cmdbuf && cp[-1] == ' ')
+                       (void) cmd_erase();
+       } else
+       {
+               /*
+                * If the char left of cursor is not a space,
+                * erase all the nonspaces left of cursor (the whole "word").
+                */
+               while (cp > cmdbuf && cp[-1] != ' ')
+                       (void) cmd_erase();
+       }
+       return (CC_OK);
+}
+
+/*
+ * Delete the "word" under the cursor.
+ */
+       static int
+cmd_wdelete()
+{
+       if (*cp == ' ')
+       {
+               /*
+                * If the char under the cursor is a space,
+                * delete it and all the spaces right of cursor.
+                */
+               while (*cp == ' ')
+                       (void) cmd_delete();
+       } else
+       {
+               /*
+                * If the char under the cursor is not a space,
+                * delete it and all nonspaces right of cursor (the whole word).
+                */
+               while (*cp != ' ' && *cp != '\0')
+                       (void) cmd_delete();
+       }
+       return (CC_OK);
+}
+
+/*
+ * Delete all chars in the command buffer.
+ */
+       static int
+cmd_kill()
+{
+       if (cmdbuf[0] == '\0')
+       {
+               /* Buffer is already empty; abort the current command. */
+               return (CC_QUIT);
+       }
+       cmd_offset = 0;
+       cmd_home();
+       *cp = '\0';
+       cmd_repaint(cp);
+
+       /*
+        * We say that erasing the entire command string causes us
+        * to abort the current command, if CF_QUIT_ON_ERASE is set.
+        */
+       if (curr_cmdflags & CF_QUIT_ON_ERASE)
+               return (CC_QUIT);
+       return (CC_OK);
+}
+
+/*
+ * Select an mlist structure to be the current command history.
+ */
+       public void
+set_mlist(mlist, cmdflags)
+       void *mlist;
+       int cmdflags;
+{
+#if CMD_HISTORY
+       curr_mlist = (struct mlist *) mlist;
+       curr_cmdflags = cmdflags;
+
+       /* Make sure the next up-arrow moves to the last string in the mlist. */
+       if (curr_mlist != NULL)
+               curr_mlist->curr_mp = curr_mlist;
+#endif
+}
+
+#if CMD_HISTORY
+/*
+ * Move up or down in the currently selected command history list.
+ */
+       static int
+cmd_updown(action)
+       int action;
+{
+       char *s;
+       
+       if (curr_mlist == NULL)
+       {
+               /*
+                * The current command has no history list.
+                */
+               bell();
+               return (CC_OK);
+       }
+       cmd_home();
+       clear_eol();
+       /*
+        * Move curr_mp to the next/prev entry.
+        */
+       if (action == EC_UP)
+               curr_mlist->curr_mp = curr_mlist->curr_mp->prev;
+       else
+               curr_mlist->curr_mp = curr_mlist->curr_mp->next;
+       /*
+        * Copy the entry into cmdbuf and echo it on the screen.
+        */
+       s = curr_mlist->curr_mp->string;
+       if (s == NULL)
+               s = "";
+       strcpy(cmdbuf, s);
+       for (cp = cmdbuf;  *cp != '\0';  )
+               cmd_right();
+       return (CC_OK);
+}
+#endif
+
+/*
+ * Add a string to a history list.
+ */
+       public void
+cmd_addhist(mlist, cmd)
+       struct mlist *mlist;
+       char *cmd;
+{
+#if CMD_HISTORY
+       struct mlist *ml;
+       
+       /*
+        * Don't save a trivial command.
+        */
+       if (strlen(cmd) == 0)
+               return;
+
+       /*
+        * Save the command unless it's a duplicate of the
+        * last command in the history.
+        */
+       ml = mlist->prev;
+       if (ml == mlist || strcmp(ml->string, cmd) != 0)
+       {
+               /*
+                * Did not find command in history.
+                * Save the command and put it at the end of the history list.
+                */
+               ml = (struct mlist *) ecalloc(1, sizeof(struct mlist));
+               ml->string = save(cmd);
+               ml->next = mlist;
+               ml->prev = mlist->prev;
+               mlist->prev->next = ml;
+               mlist->prev = ml;
+       }
+       /*
+        * Point to the cmd just after the just-accepted command.
+        * Thus, an UPARROW will always retrieve the previous command.
+        */
+       mlist->curr_mp = ml->next;
+#endif
+}
+
+/*
+ * Accept the command in the command buffer.
+ * Add it to the currently selected history list.
+ */
+       public void
+cmd_accept()
+{
+#if CMD_HISTORY
+       /*
+        * Nothing to do if there is no currently selected history list.
+        */
+       if (curr_mlist == NULL)
+               return;
+       cmd_addhist(curr_mlist, cmdbuf);
+       curr_mlist->modified = 1;
+#endif
+}
+
+/*
+ * Try to perform a line-edit function on the command buffer,
+ * using a specified char as a line-editing command.
+ * Returns:
+ *     CC_PASS The char does not invoke a line edit function.
+ *     CC_OK   Line edit function done.
+ *     CC_QUIT The char requests the current command to be aborted.
+ */
+       static int
+cmd_edit(c)
+       int c;
+{
+       int action;
+       int flags;
+
+#if TAB_COMPLETE_FILENAME
+#define        not_in_completion()     in_completion = 0
+#else
+#define        not_in_completion()
+#endif
+       
+       /*
+        * See if the char is indeed a line-editing command.
+        */
+       flags = 0;
+#if CMD_HISTORY
+       if (curr_mlist == NULL)
+               /*
+                * No current history; don't accept history manipulation cmds.
+                */
+               flags |= EC_NOHISTORY;
+#endif
+#if TAB_COMPLETE_FILENAME
+       if (curr_mlist == ml_search)
+               /*
+                * In a search command; don't accept file-completion cmds.
+                */
+               flags |= EC_NOCOMPLETE;
+#endif
+
+       action = editchar(c, flags);
+
+       switch (action)
+       {
+       case EC_RIGHT:
+               not_in_completion();
+               return (cmd_right());
+       case EC_LEFT:
+               not_in_completion();
+               return (cmd_left());
+       case EC_W_RIGHT:
+               not_in_completion();
+               while (*cp != '\0' && *cp != ' ')
+                       cmd_right();
+               while (*cp == ' ')
+                       cmd_right();
+               return (CC_OK);
+       case EC_W_LEFT:
+               not_in_completion();
+               while (cp > cmdbuf && cp[-1] == ' ')
+                       cmd_left();
+               while (cp > cmdbuf && cp[-1] != ' ')
+                       cmd_left();
+               return (CC_OK);
+       case EC_HOME:
+               not_in_completion();
+               cmd_offset = 0;
+               cmd_home();
+               cmd_repaint(cp);
+               return (CC_OK);
+       case EC_END:
+               not_in_completion();
+               while (*cp != '\0')
+                       cmd_right();
+               return (CC_OK);
+       case EC_INSERT:
+               not_in_completion();
+               return (CC_OK);
+       case EC_BACKSPACE:
+               not_in_completion();
+               return (cmd_erase());
+       case EC_LINEKILL:
+               not_in_completion();
+               return (cmd_kill());
+       case EC_ABORT:
+               not_in_completion();
+               (void) cmd_kill();
+               return (CC_QUIT);
+       case EC_W_BACKSPACE:
+               not_in_completion();
+               return (cmd_werase());
+       case EC_DELETE:
+               not_in_completion();
+               return (cmd_delete());
+       case EC_W_DELETE:
+               not_in_completion();
+               return (cmd_wdelete());
+       case EC_LITERAL:
+               literal = 1;
+               return (CC_OK);
+#if CMD_HISTORY
+       case EC_UP:
+       case EC_DOWN:
+               not_in_completion();
+               return (cmd_updown(action));
+#endif
+#if TAB_COMPLETE_FILENAME
+       case EC_F_COMPLETE:
+       case EC_B_COMPLETE:
+       case EC_EXPAND:
+               return (cmd_complete(action));
+#endif
+       case EC_NOACTION:
+               return (CC_OK);
+       default:
+               not_in_completion();
+               return (CC_PASS);
+       }
+}
+
+#if TAB_COMPLETE_FILENAME
+/*
+ * Insert a string into the command buffer, at the current position.
+ */
+       static int
+cmd_istr(str)
+       char *str;
+{
+       char *s;
+       int action;
+       char *endline = str + strlen(str);
+       
+       for (s = str;  *s != '\0';  )
+       {
+               char *os = s;
+               step_char(&s, +1, endline);
+               action = cmd_ichar(os, s - os);
+               if (action != CC_OK)
+               {
+                       bell();
+                       return (action);
+               }
+       }
+       return (CC_OK);
+}
+
+/*
+ * Find the beginning and end of the "current" word.
+ * This is the word which the cursor (cp) is inside or at the end of.
+ * Return pointer to the beginning of the word and put the
+ * cursor at the end of the word.
+ */
+       static char *
+delimit_word()
+{
+       char *word;
+#if SPACES_IN_FILENAMES
+       char *p;
+       int delim_quoted = 0;
+       int meta_quoted = 0;
+       char *esc = get_meta_escape();
+       int esclen = strlen(esc);
+#endif
+       
+       /*
+        * Move cursor to end of word.
+        */
+       if (*cp != ' ' && *cp != '\0')
+       {
+               /*
+                * Cursor is on a nonspace.
+                * Move cursor right to the next space.
+                */
+               while (*cp != ' ' && *cp != '\0')
+                       cmd_right();
+       } else if (cp > cmdbuf && cp[-1] != ' ')
+       {
+               /*
+                * Cursor is on a space, and char to the left is a nonspace.
+                * We're already at the end of the word.
+                */
+               ;
+#if 0
+       } else
+       {
+               /*
+                * Cursor is on a space and char to the left is a space.
+                * Huh? There's no word here.
+                */
+               return (NULL);
+#endif
+       }
+       /*
+        * Find the beginning of the word which the cursor is in.
+        */
+       if (cp == cmdbuf)
+               return (NULL);
+#if SPACES_IN_FILENAMES
+       /*
+        * If we have an unbalanced quote (that is, an open quote
+        * without a corresponding close quote), we return everything
+        * from the open quote, including spaces.
+        */
+       for (word = cmdbuf;  word < cp;  word++)
+               if (*word != ' ')
+                       break;
+       if (word >= cp)
+               return (cp);
+       for (p = cmdbuf;  p < cp;  p++)
+       {
+               if (meta_quoted)
+               {
+                       meta_quoted = 0;
+               } else if (esclen > 0 && p + esclen < cp &&
+                          strncmp(p, esc, esclen) == 0)
+               {
+                       meta_quoted = 1;
+                       p += esclen - 1;
+               } else if (delim_quoted)
+               {
+                       if (*p == closequote)
+                               delim_quoted = 0;
+               } else /* (!delim_quoted) */
+               {
+                       if (*p == openquote)
+                               delim_quoted = 1;
+                       else if (*p == ' ')
+                               word = p+1;
+               }
+       }
+#endif
+       return (word);
+}
+
+/*
+ * Set things up to enter completion mode.
+ * Expand the word under the cursor into a list of filenames 
+ * which start with that word, and set tk_text to that list.
+ */
+       static void
+init_compl()
+{
+       char *word;
+       char c;
+       
+       /*
+        * Get rid of any previous tk_text.
+        */
+       if (tk_text != NULL)
+       {
+               free(tk_text);
+               tk_text = NULL;
+       }
+       /*
+        * Find the original (uncompleted) word in the command buffer.
+        */
+       word = delimit_word();
+       if (word == NULL)
+               return;
+       /*
+        * Set the insertion point to the point in the command buffer
+        * where the original (uncompleted) word now sits.
+        */
+       tk_ipoint = word;
+       /*
+        * Save the original (uncompleted) word
+        */
+       if (tk_original != NULL)
+               free(tk_original);
+       tk_original = (char *) ecalloc(cp-word+1, sizeof(char));
+       strncpy(tk_original, word, cp-word);
+       /*
+        * Get the expanded filename.
+        * This may result in a single filename, or
+        * a blank-separated list of filenames.
+        */
+       c = *cp;
+       *cp = '\0';
+       if (*word != openquote)
+       {
+               tk_text = fcomplete(word);
+       } else
+       {
+               char *qword = shell_quote(word+1);
+               if (qword == NULL)
+                       tk_text = fcomplete(word+1);
+               else
+               {
+                       tk_text = fcomplete(qword);
+                       free(qword);
+               }
+       }
+       *cp = c;
+}
+
+/*
+ * Return the next word in the current completion list.
+ */
+       static char *
+next_compl(action, prev)
+       int action;
+       char *prev;
+{
+       switch (action)
+       {
+       case EC_F_COMPLETE:
+               return (forw_textlist(&tk_tlist, prev));
+       case EC_B_COMPLETE:
+               return (back_textlist(&tk_tlist, prev));
+       }
+       /* Cannot happen */
+       return ("?");
+}
+
+/*
+ * Complete the filename before (or under) the cursor.
+ * cmd_complete may be called multiple times.  The global in_completion
+ * remembers whether this call is the first time (create the list),
+ * or a subsequent time (step thru the list).
+ */
+       static int
+cmd_complete(action)
+       int action;
+{
+       char *s;
+
+       if (!in_completion || action == EC_EXPAND)
+       {
+               /*
+                * Expand the word under the cursor and 
+                * use the first word in the expansion 
+                * (or the entire expansion if we're doing EC_EXPAND).
+                */
+               init_compl();
+               if (tk_text == NULL)
+               {
+                       bell();
+                       return (CC_OK);
+               }
+               if (action == EC_EXPAND)
+               {
+                       /*
+                        * Use the whole list.
+                        */
+                       tk_trial = tk_text;
+               } else
+               {
+                       /*
+                        * Use the first filename in the list.
+                        */
+                       in_completion = 1;
+                       init_textlist(&tk_tlist, tk_text);
+                       tk_trial = next_compl(action, (char*)NULL);
+               }
+       } else
+       {
+               /*
+                * We already have a completion list.
+                * Use the next/previous filename from the list.
+                */
+               tk_trial = next_compl(action, tk_trial);
+       }
+       
+       /*
+        * Remove the original word, or the previous trial completion.
+        */
+       while (cp > tk_ipoint)
+               (void) cmd_erase();
+       
+       if (tk_trial == NULL)
+       {
+               /*
+                * There are no more trial completions.
+                * Insert the original (uncompleted) filename.
+                */
+               in_completion = 0;
+               if (cmd_istr(tk_original) != CC_OK)
+                       goto fail;
+       } else
+       {
+               /*
+                * Insert trial completion.
+                */
+               if (cmd_istr(tk_trial) != CC_OK)
+                       goto fail;
+               /*
+                * If it is a directory, append a slash.
+                */
+               if (is_dir(tk_trial))
+               {
+                       if (cp > cmdbuf && cp[-1] == closequote)
+                               (void) cmd_erase();
+                       s = lgetenv("LESSSEPARATOR");
+                       if (s == NULL)
+                               s = PATHNAME_SEP;
+                       if (cmd_istr(s) != CC_OK)
+                               goto fail;
+               }
+       }
+       
+       return (CC_OK);
+       
+fail:
+       in_completion = 0;
+       bell();
+       return (CC_OK);
+}
+
+#endif /* TAB_COMPLETE_FILENAME */
+
+/*
+ * Process a single character of a multi-character command, such as
+ * a number, or the pattern of a search command.
+ * Returns:
+ *     CC_OK           The char was accepted.
+ *     CC_QUIT         The char requests the command to be aborted.
+ *     CC_ERROR        The char could not be accepted due to an error.
+ */
+       public int
+cmd_char(c)
+       int c;
+{
+       int action;
+       int len;
+
+       if (!utf_mode)
+       {
+               cmd_mbc_buf[0] = c;
+               len = 1;
+       } else
+       {
+               /* Perform strict validation in all possible cases.  */
+               if (cmd_mbc_buf_len == 0)
+               {
+                retry:
+                       cmd_mbc_buf_index = 1;
+                       *cmd_mbc_buf = c;
+                       if (IS_ASCII_OCTET(c))
+                               cmd_mbc_buf_len = 1;
+                       else if (IS_UTF8_LEAD(c))
+                       {
+                               cmd_mbc_buf_len = utf_len(c);
+                               return (CC_OK);
+                       } else
+                       {
+                               /* UTF8_INVALID or stray UTF8_TRAIL */
+                               bell();
+                               return (CC_ERROR);
+                       }
+               } else if (IS_UTF8_TRAIL(c))
+               {
+                       cmd_mbc_buf[cmd_mbc_buf_index++] = c;
+                       if (cmd_mbc_buf_index < cmd_mbc_buf_len)
+                               return (CC_OK);
+                       if (!is_utf8_well_formed(cmd_mbc_buf))
+                       {
+                               /* complete, but not well formed (non-shortest form), sequence */
+                               cmd_mbc_buf_len = 0;
+                               bell();
+                               return (CC_ERROR);
+                       }
+               } else
+               {
+                       /* Flush incomplete (truncated) sequence.  */
+                       cmd_mbc_buf_len = 0;
+                       bell();
+                       /* Handle new char.  */
+                       goto retry;
+               }
+
+               len = cmd_mbc_buf_len;
+               cmd_mbc_buf_len = 0;
+       }
+
+       if (literal)
+       {
+               /*
+                * Insert the char, even if it is a line-editing char.
+                */
+               literal = 0;
+               return (cmd_ichar(cmd_mbc_buf, len));
+       }
+               
+       /*
+        * See if it is a line-editing character.
+        */
+       if (in_mca() && len == 1)
+       {
+               action = cmd_edit(c);
+               switch (action)
+               {
+               case CC_OK:
+               case CC_QUIT:
+                       return (action);
+               case CC_PASS:
+                       break;
+               }
+       }
+       
+       /*
+        * Insert the char into the command buffer.
+        */
+       return (cmd_ichar(cmd_mbc_buf, len));
+}
+
+/*
+ * Return the number currently in the command buffer.
+ */
+       public LINENUM
+cmd_int(frac)
+       long *frac;
+{
+       char *p;
+       LINENUM n = 0;
+       int err;
+
+       for (p = cmdbuf;  *p >= '0' && *p <= '9';  p++)
+               n = (n * 10) + (*p - '0');
+       *frac = 0;
+       if (*p++ == '.')
+       {
+               *frac = getfraction(&p, NULL, &err);
+               /* {{ do something if err is set? }} */
+       }
+       return (n);
+}
+
+/*
+ * Return a pointer to the command buffer.
+ */
+       public char *
+get_cmdbuf()
+{
+       return (cmdbuf);
+}
+
+#if CMD_HISTORY
+/*
+ * Return the last (most recent) string in the current command history.
+ */
+       public char *
+cmd_lastpattern()
+{
+       if (curr_mlist == NULL)
+               return (NULL);
+       return (curr_mlist->curr_mp->prev->string);
+}
+#endif
+
+#if CMD_HISTORY
+/*
+ * Get the name of the history file.
+ */
+       static char *
+histfile_name()
+{
+       char *home;
+       char *name;
+       int len;
+       
+       /* See if filename is explicitly specified by $LESSHISTFILE. */
+       name = lgetenv("LESSHISTFILE");
+       if (name != NULL && *name != '\0')
+       {
+               if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0)
+                       /* $LESSHISTFILE == "-" means don't use a history file. */
+                       return (NULL);
+               return (save(name));
+       }
+
+       /* Otherwise, file is in $HOME. */
+       home = lgetenv("HOME");
+       if (home == NULL || *home == '\0')
+       {
+#if OS2
+               home = lgetenv("INIT");
+               if (home == NULL || *home == '\0')
+#endif
+                       return (NULL);
+       }
+       len = strlen(home) + strlen(LESSHISTFILE) + 2;
+       name = (char *) ecalloc(len, sizeof(char));
+       SNPRINTF2(name, len, "%s/%s", home, LESSHISTFILE);
+       return (name);
+}
+#endif /* CMD_HISTORY */
+
+/*
+ * Initialize history from a .lesshist file.
+ */
+       public void
+init_cmdhist()
+{
+#if CMD_HISTORY
+       struct mlist *ml = NULL;
+       char line[CMDBUF_SIZE];
+       char *filename;
+       FILE *f;
+       char *p;
+
+       filename = histfile_name();
+       if (filename == NULL)
+               return;
+       f = fopen(filename, "r");
+       free(filename);
+       if (f == NULL)
+               return;
+       if (fgets(line, sizeof(line), f) == NULL ||
+           strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0)
+       {
+               fclose(f);
+               return;
+       }
+       while (fgets(line, sizeof(line), f) != NULL)
+       {
+               for (p = line;  *p != '\0';  p++)
+               {
+                       if (*p == '\n' || *p == '\r')
+                       {
+                               *p = '\0';
+                               break;
+                       }
+               }
+               if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0)
+                       ml = &mlist_search;
+               else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0)
+               {
+#if SHELL_ESCAPE || PIPEC
+                       ml = &mlist_shell;
+#else
+                       ml = NULL;
+#endif
+               } else if (*line == '"')
+               {
+                       if (ml != NULL)
+                               cmd_addhist(ml, line+1);
+               }
+       }
+       fclose(f);
+#endif /* CMD_HISTORY */
+}
+
+/*
+ *
+ */
+#if CMD_HISTORY
+       static void
+save_mlist(ml, f)
+       struct mlist *ml;
+       FILE *f;
+{
+       int histsize = 0;
+       int n;
+       char *s;
+
+       s = lgetenv("LESSHISTSIZE");
+       if (s != NULL)
+               histsize = atoi(s);
+       if (histsize == 0)
+               histsize = 100;
+
+       ml = ml->prev;
+       for (n = 0;  n < histsize;  n++)
+       {
+               if (ml->string == NULL)
+                       break;
+               ml = ml->prev;
+       }
+       for (ml = ml->next;  ml->string != NULL;  ml = ml->next)
+               fprintf(f, "\"%s\n", ml->string);
+}
+#endif /* CMD_HISTORY */
+
+/*
+ *
+ */
+       public void
+save_cmdhist()
+{
+#if CMD_HISTORY
+       char *filename;
+       FILE *f;
+       int modified = 0;
+
+       filename = histfile_name();
+       if (filename == NULL)
+               return;
+       if (mlist_search.modified)
+               modified = 1;
+#if SHELL_ESCAPE || PIPEC
+       if (mlist_shell.modified)
+               modified = 1;
+#endif
+       if (!modified)
+               return;
+       f = fopen(filename, "w");
+       free(filename);
+       if (f == NULL)
+               return;
+#if HAVE_FCHMOD
+{
+       /* Make history file readable only by owner. */
+       int do_chmod = 1;
+#if HAVE_STAT
+       struct stat statbuf;
+       int r = fstat(fileno(f), &statbuf);
+       if (r < 0 || !S_ISREG(statbuf.st_mode))
+               /* Don't chmod if not a regular file. */
+               do_chmod = 0;
+#endif
+       if (do_chmod)
+               fchmod(fileno(f), 0600);
+}
+#endif
+
+       fprintf(f, "%s\n", HISTFILE_FIRST_LINE);
+
+       fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION);
+       save_mlist(&mlist_search, f);
+
+#if SHELL_ESCAPE || PIPEC
+       fprintf(f, "%s\n", HISTFILE_SHELL_SECTION);
+       save_mlist(&mlist_shell, f);
+#endif
+
+       fclose(f);
+#endif /* CMD_HISTORY */
+}
diff --git a/thirdparty/less/command.c b/thirdparty/less/command.c
new file mode 100644 (file)
index 0000000..bacb058
--- /dev/null
@@ -0,0 +1,1763 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * User-level command processor.
+ */
+
+#include "less.h"
+#if MSDOS_COMPILER==WIN32C
+#include <windows.h>
+#endif
+#include "position.h"
+#include "option.h"
+#include "cmd.h"
+
+extern int erase_char, erase2_char, kill_char;
+extern int sigs;
+extern int quit_if_one_screen;
+extern int squished;
+extern int sc_width;
+extern int sc_height;
+extern int swindow;
+extern int jump_sline;
+extern int quitting;
+extern int wscroll;
+extern int top_scroll;
+extern int ignore_eoi;
+extern int secure;
+extern int hshift;
+extern int show_attn;
+extern char *every_first_cmd;
+extern char *curr_altfilename;
+extern char version[];
+extern struct scrpos initial_scrpos;
+extern IFILE curr_ifile;
+extern void constant *ml_search;
+extern void constant *ml_examine;
+#if SHELL_ESCAPE || PIPEC
+extern void constant *ml_shell;
+#endif
+#if EDITOR
+extern char *editor;
+extern char *editproto;
+#endif
+extern int screen_trashed;     /* The screen has been overwritten */
+extern int shift_count;
+extern int oldbot;
+extern int forw_prompt;
+
+#if SHELL_ESCAPE
+static char *shellcmd = NULL;  /* For holding last shell command for "!!" */
+#endif
+static int mca;                        /* The multicharacter command (action) */
+static int search_type;                /* The previous type of search */
+static LINENUM number;         /* The number typed by the user */
+static long fraction;          /* The fractional part of the number */
+static struct loption *curropt;
+static int opt_lower;
+static int optflag;
+static int optgetname;
+static POSITION bottompos;
+static int save_hshift;
+#if PIPEC
+static char pipec;
+#endif
+
+struct ungot {
+       struct ungot *ug_next;
+       char ug_char;
+};
+static struct ungot* ungot = NULL;
+static int unget_end = 0;
+
+static void multi_search();
+
+/*
+ * Move the cursor to start of prompt line before executing a command.
+ * This looks nicer if the command takes a long time before
+ * updating the screen.
+ */
+       static void
+cmd_exec()
+{
+#if HILITE_SEARCH
+       clear_attn();
+#endif
+       clear_bot();
+       flush();
+}
+
+/*
+ * Set up the display to start a new multi-character command.
+ */
+       static void
+start_mca(action, prompt, mlist, cmdflags)
+       int action;
+       char *prompt;
+       void *mlist;
+       int cmdflags;
+{
+       mca = action;
+       clear_bot();
+       clear_cmd();
+       cmd_putstr(prompt);
+       set_mlist(mlist, cmdflags);
+}
+
+       public int
+in_mca()
+{
+       return (mca != 0 && mca != A_PREFIX);
+}
+
+/*
+ * Set up the display to start a new search command.
+ */
+       static void
+mca_search()
+{
+#if HILITE_SEARCH
+       if (search_type & SRCH_FILTER)
+               mca = A_FILTER;
+       else 
+#endif
+       if (search_type & SRCH_FORW)
+               mca = A_F_SEARCH;
+       else
+               mca = A_B_SEARCH;
+
+       clear_bot();
+       clear_cmd();
+
+       if (search_type & SRCH_NO_MATCH)
+               cmd_putstr("Non-match ");
+       if (search_type & SRCH_FIRST_FILE)
+               cmd_putstr("First-file ");
+       if (search_type & SRCH_PAST_EOF)
+               cmd_putstr("EOF-ignore ");
+       if (search_type & SRCH_NO_MOVE)
+               cmd_putstr("Keep-pos ");
+       if (search_type & SRCH_NO_REGEX)
+               cmd_putstr("Regex-off ");
+
+#if HILITE_SEARCH
+       if (search_type & SRCH_FILTER)
+               cmd_putstr("&/");
+       else 
+#endif
+       if (search_type & SRCH_FORW)
+               cmd_putstr("/");
+       else
+               cmd_putstr("?");
+       set_mlist(ml_search, 0);
+}
+
+/*
+ * Set up the display to start a new toggle-option command.
+ */
+       static void
+mca_opt_toggle()
+{
+       int no_prompt;
+       int flag;
+       char *dash;
+       
+       no_prompt = (optflag & OPT_NO_PROMPT);
+       flag = (optflag & ~OPT_NO_PROMPT);
+       dash = (flag == OPT_NO_TOGGLE) ? "_" : "-";
+
+       mca = A_OPT_TOGGLE;
+       clear_bot();
+       clear_cmd();
+       cmd_putstr(dash);
+       if (optgetname)
+               cmd_putstr(dash);
+       if (no_prompt)
+               cmd_putstr("(P)");
+       switch (flag)
+       {
+       case OPT_UNSET:
+               cmd_putstr("+");
+               break;
+       case OPT_SET:
+               cmd_putstr("!");
+               break;
+       }
+       set_mlist(NULL, 0);
+}
+
+/*
+ * Execute a multicharacter command.
+ */
+       static void
+exec_mca()
+{
+       register char *cbuf;
+
+       cmd_exec();
+       cbuf = get_cmdbuf();
+
+       switch (mca)
+       {
+       case A_F_SEARCH:
+       case A_B_SEARCH:
+               multi_search(cbuf, (int) number);
+               break;
+#if HILITE_SEARCH
+       case A_FILTER:
+               search_type ^= SRCH_NO_MATCH;
+               set_filter_pattern(cbuf, search_type);
+               break;
+#endif
+       case A_FIRSTCMD:
+               /*
+                * Skip leading spaces or + signs in the string.
+                */
+               while (*cbuf == '+' || *cbuf == ' ')
+                       cbuf++;
+               if (every_first_cmd != NULL)
+                       free(every_first_cmd);
+               if (*cbuf == '\0')
+                       every_first_cmd = NULL;
+               else
+                       every_first_cmd = save(cbuf);
+               break;
+       case A_OPT_TOGGLE:
+               toggle_option(curropt, opt_lower, cbuf, optflag);
+               curropt = NULL;
+               break;
+       case A_F_BRACKET:
+               match_brac(cbuf[0], cbuf[1], 1, (int) number);
+               break;
+       case A_B_BRACKET:
+               match_brac(cbuf[1], cbuf[0], 0, (int) number);
+               break;
+#if EXAMINE
+       case A_EXAMINE:
+               if (secure)
+                       break;
+               edit_list(cbuf);
+#if TAGS
+               /* If tag structure is loaded then clean it up. */
+               cleantags();
+#endif
+               break;
+#endif
+#if SHELL_ESCAPE
+       case A_SHELL:
+               /*
+                * !! just uses whatever is in shellcmd.
+                * Otherwise, copy cmdbuf to shellcmd,
+                * expanding any special characters ("%" or "#").
+                */
+               if (*cbuf != '!')
+               {
+                       if (shellcmd != NULL)
+                               free(shellcmd);
+                       shellcmd = fexpand(cbuf);
+               }
+
+               if (secure)
+                       break;
+               if (shellcmd == NULL)
+                       lsystem("", "!done");
+               else
+                       lsystem(shellcmd, "!done");
+               break;
+#endif
+#if PIPEC
+       case A_PIPE:
+               if (secure)
+                       break;
+               (void) pipe_mark(pipec, cbuf);
+               error("|done", NULL_PARG);
+               break;
+#endif
+       }
+}
+
+/*
+ * Is a character an erase or kill char?
+ */
+       static int
+is_erase_char(c)
+       int c;
+{
+       return (c == erase_char || c == erase2_char || c == kill_char);
+}
+
+/*
+ * Handle the first char of an option (after the initial dash).
+ */
+       static int
+mca_opt_first_char(c)
+    int c;
+{
+       int flag = (optflag & ~OPT_NO_PROMPT);
+       if (flag == OPT_NO_TOGGLE)
+       {
+               switch (c)
+               {
+               case '_':
+                       /* "__" = long option name. */
+                       optgetname = TRUE;
+                       mca_opt_toggle();
+                       return (MCA_MORE);
+               }
+       } else
+       {
+               switch (c)
+               {
+               case '+':
+                       /* "-+" = UNSET. */
+                       optflag = (flag == OPT_UNSET) ?
+                               OPT_TOGGLE : OPT_UNSET;
+                       mca_opt_toggle();
+                       return (MCA_MORE);
+               case '!':
+                       /* "-!" = SET */
+                       optflag = (flag == OPT_SET) ?
+                               OPT_TOGGLE : OPT_SET;
+                       mca_opt_toggle();
+                       return (MCA_MORE);
+               case CONTROL('P'):
+                       optflag ^= OPT_NO_PROMPT;
+                       mca_opt_toggle();
+                       return (MCA_MORE);
+               case '-':
+                       /* "--" = long option name. */
+                       optgetname = TRUE;
+                       mca_opt_toggle();
+                       return (MCA_MORE);
+               }
+       }
+       /* Char was not handled here. */
+       return (NO_MCA);
+}
+
+/*
+ * Add a char to a long option name.
+ * See if we've got a match for an option name yet.
+ * If so, display the complete name and stop 
+ * accepting chars until user hits RETURN.
+ */
+       static int
+mca_opt_nonfirst_char(c)
+       int c;
+{
+       char *p;
+       char *oname;
+
+       if (curropt != NULL)
+       {
+               /*
+                * Already have a match for the name.
+                * Don't accept anything but erase/kill.
+                */
+               if (is_erase_char(c))
+                       return (MCA_DONE);
+               return (MCA_MORE);
+       }
+       /*
+        * Add char to cmd buffer and try to match
+        * the option name.
+        */
+       if (cmd_char(c) == CC_QUIT)
+               return (MCA_DONE);
+       p = get_cmdbuf();
+       opt_lower = ASCII_IS_LOWER(p[0]);
+       curropt = findopt_name(&p, &oname, NULL);
+       if (curropt != NULL)
+       {
+               /*
+                * Got a match.
+                * Remember the option and
+                * display the full option name.
+                */
+               cmd_reset();
+               mca_opt_toggle();
+               for (p = oname;  *p != '\0';  p++)
+               {
+                       c = *p;
+                       if (!opt_lower && ASCII_IS_LOWER(c))
+                               c = ASCII_TO_UPPER(c);
+                       if (cmd_char(c) != CC_OK)
+                               return (MCA_DONE);
+               }
+       }
+       return (MCA_MORE);
+}
+
+/*
+ * Handle a char of an option toggle command.
+ */
+       static int
+mca_opt_char(c)
+       int c;
+{
+       PARG parg;
+
+       /*
+        * This may be a short option (single char),
+        * or one char of a long option name,
+        * or one char of the option parameter.
+        */
+       if (curropt == NULL && len_cmdbuf() == 0)
+       {
+               int ret = mca_opt_first_char(c);
+               if (ret != NO_MCA)
+                       return (ret);
+       }
+       if (optgetname)
+       {
+               /* We're getting a long option name.  */
+               if (c != '\n' && c != '\r')
+                       return (mca_opt_nonfirst_char(c));
+               if (curropt == NULL)
+               {
+                       parg.p_string = get_cmdbuf();
+                       error("There is no --%s option", &parg);
+                       return (MCA_DONE);
+               }
+               optgetname = FALSE;
+               cmd_reset();
+       } else
+       {
+               if (is_erase_char(c))
+                       return (NO_MCA);
+               if (curropt != NULL)
+                       /* We're getting the option parameter. */
+                       return (NO_MCA);
+               curropt = findopt(c);
+               if (curropt == NULL)
+               {
+                       parg.p_string = propt(c);
+                       error("There is no %s option", &parg);
+                       return (MCA_DONE);
+               }
+       }
+       /*
+        * If the option which was entered does not take a 
+        * parameter, toggle the option immediately,
+        * so user doesn't have to hit RETURN.
+        */
+       if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE ||
+           !opt_has_param(curropt))
+       {
+               toggle_option(curropt, ASCII_IS_LOWER(c), "", optflag);
+               return (MCA_DONE);
+       }
+       /*
+        * Display a prompt appropriate for the option parameter.
+        */
+       start_mca(A_OPT_TOGGLE, opt_prompt(curropt), (void*)NULL, 0);
+       return (MCA_MORE);
+}
+
+/*
+ * Handle a char of a search command.
+ */
+       static int
+mca_search_char(c)
+       int c;
+{
+       int flag = 0;
+
+       /*
+        * Certain characters as the first char of 
+        * the pattern have special meaning:
+        *      !  Toggle the NO_MATCH flag
+        *      *  Toggle the PAST_EOF flag
+        *      @  Toggle the FIRST_FILE flag
+        */
+       if (len_cmdbuf() > 0)
+               return (NO_MCA);
+
+       switch (c)
+       {
+       case CONTROL('E'): /* ignore END of file */
+       case '*':
+               if (mca != A_FILTER)
+                       flag = SRCH_PAST_EOF;
+               break;
+       case CONTROL('F'): /* FIRST file */
+       case '@':
+               if (mca != A_FILTER)
+                       flag = SRCH_FIRST_FILE;
+               break;
+       case CONTROL('K'): /* KEEP position */
+               if (mca != A_FILTER)
+                       flag = SRCH_NO_MOVE;
+               break;
+       case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */
+               flag = SRCH_NO_REGEX;
+               break;
+       case CONTROL('N'): /* NOT match */
+       case '!':
+               flag = SRCH_NO_MATCH;
+               break;
+       }
+
+       if (flag != 0)
+       {
+               search_type ^= flag;
+               mca_search();
+               return (MCA_MORE);
+       }
+       return (NO_MCA);
+}
+
+/*
+ * Handle a character of a multi-character command.
+ */
+       static int
+mca_char(c)
+       int c;
+{
+       int ret;
+
+       switch (mca)
+       {
+       case 0:
+               /*
+                * We're not in a multicharacter command.
+                */
+               return (NO_MCA);
+
+       case A_PREFIX:
+               /*
+                * In the prefix of a command.
+                * This not considered a multichar command
+                * (even tho it uses cmdbuf, etc.).
+                * It is handled in the commands() switch.
+                */
+               return (NO_MCA);
+
+       case A_DIGIT:
+               /*
+                * Entering digits of a number.
+                * Terminated by a non-digit.
+                */
+               if (!((c >= '0' && c <= '9') || c == '.') && 
+                 editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE|EC_NORIGHTLEFT) == A_INVALID)
+               {
+                       /*
+                        * Not part of the number.
+                        * End the number and treat this char 
+                        * as a normal command character.
+                        */
+                       number = cmd_int(&fraction);
+                       mca = 0;
+                       cmd_accept();
+                       return (NO_MCA);
+               }
+               break;
+
+       case A_OPT_TOGGLE:
+               ret = mca_opt_char(c);
+               if (ret != NO_MCA)
+                       return (ret);
+               break;
+
+       case A_F_SEARCH:
+       case A_B_SEARCH:
+       case A_FILTER:
+               ret = mca_search_char(c);
+               if (ret != NO_MCA)
+                       return (ret);
+               break;
+
+       default:
+               /* Other multicharacter command. */
+               break;
+       }
+
+       /*
+        * The multichar command is terminated by a newline.
+        */
+       if (c == '\n' || c == '\r')
+       {
+               /*
+                * Execute the command.
+                */
+               exec_mca();
+               return (MCA_DONE);
+       }
+
+       /*
+        * Append the char to the command buffer.
+        */
+       if (cmd_char(c) == CC_QUIT)
+               /*
+                * Abort the multi-char command.
+                */
+               return (MCA_DONE);
+
+       if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
+       {
+               /*
+                * Special case for the bracket-matching commands.
+                * Execute the command after getting exactly two
+                * characters from the user.
+                */
+               exec_mca();
+               return (MCA_DONE);
+       }
+
+       /*
+        * Need another character.
+        */
+       return (MCA_MORE);
+}
+
+/*
+ * Discard any buffered file data.
+ */
+       static void
+clear_buffers()
+{
+       if (!(ch_getflags() & CH_CANSEEK))
+               return;
+       ch_flush();
+       clr_linenum();
+#if HILITE_SEARCH
+       clr_hilite();
+#endif
+}
+
+/*
+ * Make sure the screen is displayed.
+ */
+       static void
+make_display()
+{
+       /*
+        * If nothing is displayed yet, display starting from initial_scrpos.
+        */
+       if (empty_screen())
+       {
+               if (initial_scrpos.pos == NULL_POSITION)
+                       /*
+                        * {{ Maybe this should be:
+                        *    jump_loc(ch_zero(), jump_sline);
+                        *    but this behavior seems rather unexpected 
+                        *    on the first screen. }}
+                        */
+                       jump_loc(ch_zero(), 1);
+               else
+                       jump_loc(initial_scrpos.pos, initial_scrpos.ln);
+       } else if (screen_trashed)
+       {
+               int save_top_scroll = top_scroll;
+               int save_ignore_eoi = ignore_eoi;
+               top_scroll = 1;
+               ignore_eoi = 0;
+               if (screen_trashed == 2)
+               {
+                       /* Special case used by ignore_eoi: re-open the input file
+                        * and jump to the end of the file. */
+                       reopen_curr_ifile();
+                       jump_forw();
+               }
+               repaint();
+               top_scroll = save_top_scroll;
+               ignore_eoi = save_ignore_eoi;
+       }
+}
+
+/*
+ * Display the appropriate prompt.
+ */
+       static void
+prompt()
+{
+       register char *p;
+
+       if (ungot != NULL)
+       {
+               /*
+                * No prompt necessary if commands are from 
+                * ungotten chars rather than from the user.
+                */
+               return;
+       }
+
+       /*
+        * Make sure the screen is displayed.
+        */
+       make_display();
+       bottompos = position(BOTTOM_PLUS_ONE);
+
+       /*
+        * If we've hit EOF on the last file and the -E flag is set, quit.
+        */
+       if (get_quit_at_eof() == OPT_ONPLUS &&
+           eof_displayed() && !(ch_getflags() & CH_HELPFILE) && 
+           next_ifile(curr_ifile) == NULL_IFILE)
+               quit(QUIT_OK);
+
+       /*
+        * If the entire file is displayed and the -F flag is set, quit.
+        */
+       if (quit_if_one_screen &&
+           entire_file_displayed() && !(ch_getflags() & CH_HELPFILE) && 
+           next_ifile(curr_ifile) == NULL_IFILE)
+               quit(QUIT_OK);
+
+#if MSDOS_COMPILER==WIN32C
+       /* 
+        * In Win32, display the file name in the window title.
+        */
+       if (!(ch_getflags() & CH_HELPFILE))
+               SetConsoleTitle(pr_expand("Less?f - %f.", 0));
+#endif
+       /*
+        * Select the proper prompt and display it.
+        */
+       /*
+        * If the previous action was a forward movement, 
+        * don't clear the bottom line of the display;
+        * just print the prompt since the forward movement guarantees 
+        * that we're in the right position to display the prompt.
+        * Clearing the line could cause a problem: for example, if the last
+        * line displayed ended at the right screen edge without a newline,
+        * then clearing would clear the last displayed line rather than
+        * the prompt line.
+        */
+       if (!forw_prompt)
+               clear_bot();
+       clear_cmd();
+       forw_prompt = 0;
+       p = pr_string();
+       if (is_filtering())
+               putstr("& ");
+       if (p == NULL || *p == '\0')
+               putchr(':');
+       else
+       {
+               at_enter(AT_STANDOUT);
+               putstr(p);
+               at_exit();
+       }
+       clear_eol();
+}
+
+/*
+ * Display the less version message.
+ */
+       public void
+dispversion()
+{
+       PARG parg;
+
+       parg.p_string = version;
+       error("less %s", &parg);
+}
+
+/*
+ * Get command character.
+ * The character normally comes from the keyboard,
+ * but may come from ungotten characters
+ * (characters previously given to ungetcc or ungetsc).
+ */
+       public int
+getcc()
+{
+       if (unget_end) 
+       {
+               /*
+                * We have just run out of ungotten chars.
+                */
+               unget_end = 0;
+               if (len_cmdbuf() == 0 || !empty_screen())
+                       return (getchr());
+               /*
+                * Command is incomplete, so try to complete it.
+                */
+               switch (mca)
+               {
+               case A_DIGIT:
+                       /*
+                        * We have a number but no command.  Treat as #g.
+                        */
+                       return ('g');
+
+               case A_F_SEARCH:
+               case A_B_SEARCH:
+                       /*
+                        * We have "/string" but no newline.  Add the \n.
+                        */
+                       return ('\n'); 
+
+               default:
+                       /*
+                        * Some other incomplete command.  Let user complete it.
+                        */
+                       return (getchr());
+               }
+       }
+
+       if (ungot == NULL)
+       {
+               /*
+                * Normal case: no ungotten chars, so get one from the user.
+                */
+               return (getchr());
+       }
+
+       /*
+        * Return the next ungotten char.
+        */
+       {
+               struct ungot *ug = ungot;
+               char c = ug->ug_char;
+               ungot = ug->ug_next;
+               free(ug);
+               unget_end = (ungot == NULL);
+               return (c);
+       }
+}
+
+/*
+ * "Unget" a command character.
+ * The next getcc() will return this character.
+ */
+       public void
+ungetcc(c)
+       int c;
+{
+       struct ungot *ug = (struct ungot *) ecalloc(1, sizeof(struct ungot));
+
+       ug->ug_char = c;
+       ug->ug_next = ungot;
+       ungot = ug;
+       unget_end = 0;
+}
+
+/*
+ * Unget a whole string of command characters.
+ * The next sequence of getcc()'s will return this string.
+ */
+       public void
+ungetsc(s)
+       char *s;
+{
+       register char *p;
+
+       for (p = s + strlen(s) - 1;  p >= s;  p--)
+               ungetcc(*p);
+}
+
+/*
+ * Search for a pattern, possibly in multiple files.
+ * If SRCH_FIRST_FILE is set, begin searching at the first file.
+ * If SRCH_PAST_EOF is set, continue the search thru multiple files.
+ */
+       static void
+multi_search(pattern, n)
+       char *pattern;
+       int n;
+{
+       register int nomore;
+       IFILE save_ifile;
+       int changed_file;
+
+       changed_file = 0;
+       save_ifile = save_curr_ifile();
+
+       if (search_type & SRCH_FIRST_FILE)
+       {
+               /*
+                * Start at the first (or last) file 
+                * in the command line list.
+                */
+               if (search_type & SRCH_FORW)
+                       nomore = edit_first();
+               else
+                       nomore = edit_last();
+               if (nomore)
+               {
+                       unsave_ifile(save_ifile);
+                       return;
+               }
+               changed_file = 1;
+               search_type &= ~SRCH_FIRST_FILE;
+       }
+
+       for (;;)
+       {
+               n = search(search_type, pattern, n);
+               /*
+                * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared
+                * after being used once.  This allows "n" to work after
+                * using a /@@ search.
+                */
+               search_type &= ~SRCH_NO_MOVE;
+               if (n == 0)
+               {
+                       /*
+                        * Found it.
+                        */
+                       unsave_ifile(save_ifile);
+                       return;
+               }
+
+               if (n < 0)
+                       /*
+                        * Some kind of error in the search.
+                        * Error message has been printed by search().
+                        */
+                       break;
+
+               if ((search_type & SRCH_PAST_EOF) == 0)
+                       /*
+                        * We didn't find a match, but we're
+                        * supposed to search only one file.
+                        */
+                       break;
+               /*
+                * Move on to the next file.
+                */
+               if (search_type & SRCH_FORW)
+                       nomore = edit_next(1);
+               else
+                       nomore = edit_prev(1);
+               if (nomore)
+                       break;
+               changed_file = 1;
+       }
+
+       /*
+        * Didn't find it.
+        * Print an error message if we haven't already.
+        */
+       if (n > 0)
+               error("Pattern not found", NULL_PARG);
+
+       if (changed_file)
+       {
+               /*
+                * Restore the file we were originally viewing.
+                */
+               reedit_ifile(save_ifile);
+       } else
+       {
+               unsave_ifile(save_ifile);
+       }
+}
+
+/*
+ * Main command processor.
+ * Accept and execute commands until a quit command.
+ */
+       public void
+commands()
+{
+       register int c;
+       register int action;
+       register char *cbuf;
+       int newaction;
+       int save_search_type;
+       char *extra;
+       char tbuf[2];
+       PARG parg;
+       IFILE old_ifile;
+       IFILE new_ifile;
+       char *tagfile;
+
+       search_type = SRCH_FORW;
+       wscroll = (sc_height + 1) / 2;
+       newaction = A_NOACTION;
+
+       for (;;)
+       {
+               mca = 0;
+               cmd_accept();
+               number = 0;
+               curropt = NULL;
+
+               /*
+                * See if any signals need processing.
+                */
+               if (sigs)
+               {
+                       psignals();
+                       if (quitting)
+                               quit(QUIT_SAVED_STATUS);
+               }
+
+               /*
+                * See if window size changed, for systems that don't
+                * generate SIGWINCH.
+                */
+               check_winch();
+
+               /*
+                * Display prompt and accept a character.
+                */
+               cmd_reset();
+               prompt();
+               if (sigs)
+                       continue;
+               if (newaction == A_NOACTION)
+                       c = getcc();
+
+       again:
+               if (sigs)
+                       continue;
+
+               if (newaction != A_NOACTION)
+               {
+                       action = newaction;
+                       newaction = A_NOACTION;
+               } else
+               {
+                       /*
+                        * If we are in a multicharacter command, call mca_char.
+                        * Otherwise we call fcmd_decode to determine the
+                        * action to be performed.
+                        */
+                       if (mca)
+                               switch (mca_char(c))
+                               {
+                               case MCA_MORE:
+                                       /*
+                                        * Need another character.
+                                        */
+                                       c = getcc();
+                                       goto again;
+                               case MCA_DONE:
+                                       /*
+                                        * Command has been handled by mca_char.
+                                        * Start clean with a prompt.
+                                        */
+                                       continue;
+                               case NO_MCA:
+                                       /*
+                                        * Not a multi-char command
+                                        * (at least, not anymore).
+                                        */
+                                       break;
+                               }
+
+                       /*
+                        * Decode the command character and decide what to do.
+                        */
+                       if (mca)
+                       {
+                               /*
+                                * We're in a multichar command.
+                                * Add the character to the command buffer
+                                * and display it on the screen.
+                                * If the user backspaces past the start 
+                                * of the line, abort the command.
+                                */
+                               if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
+                                       continue;
+                               cbuf = get_cmdbuf();
+                       } else
+                       {
+                               /*
+                                * Don't use cmd_char if we're starting fresh
+                                * at the beginning of a command, because we
+                                * don't want to echo the command until we know
+                                * it is a multichar command.  We also don't
+                                * want erase_char/kill_char to be treated
+                                * as line editing characters.
+                                */
+                               tbuf[0] = c;
+                               tbuf[1] = '\0';
+                               cbuf = tbuf;
+                       }
+                       extra = NULL;
+                       action = fcmd_decode(cbuf, &extra);
+                       /*
+                        * If an "extra" string was returned,
+                        * process it as a string of command characters.
+                        */
+                       if (extra != NULL)
+                               ungetsc(extra);
+               }
+               /*
+                * Clear the cmdbuf string.
+                * (But not if we're in the prefix of a command,
+                * because the partial command string is kept there.)
+                */
+               if (action != A_PREFIX)
+                       cmd_reset();
+
+               switch (action)
+               {
+               case A_DIGIT:
+                       /*
+                        * First digit of a number.
+                        */
+                       start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
+                       goto again;
+
+               case A_F_WINDOW:
+                       /*
+                        * Forward one window (and set the window size).
+                        */
+                       if (number > 0)
+                               swindow = (int) number;
+                       /* FALLTHRU */
+               case A_F_SCREEN:
+                       /*
+                        * Forward one screen.
+                        */
+                       if (number <= 0)
+                               number = get_swindow();
+                       cmd_exec();
+                       if (show_attn)
+                               set_attnpos(bottompos);
+                       forward((int) number, 0, 1);
+                       break;
+
+               case A_B_WINDOW:
+                       /*
+                        * Backward one window (and set the window size).
+                        */
+                       if (number > 0)
+                               swindow = (int) number;
+                       /* FALLTHRU */
+               case A_B_SCREEN:
+                       /*
+                        * Backward one screen.
+                        */
+                       if (number <= 0)
+                               number = get_swindow();
+                       cmd_exec();
+                       backward((int) number, 0, 1);
+                       break;
+
+               case A_F_LINE:
+                       /*
+                        * Forward N (default 1) line.
+                        */
+                       if (number <= 0)
+                               number = 1;
+                       cmd_exec();
+                       if (show_attn == OPT_ONPLUS && number > 1)
+                               set_attnpos(bottompos);
+                       forward((int) number, 0, 0);
+                       break;
+
+               case A_B_LINE:
+                       /*
+                        * Backward N (default 1) line.
+                        */
+                       if (number <= 0)
+                               number = 1;
+                       cmd_exec();
+                       backward((int) number, 0, 0);
+                       break;
+
+               case A_FF_LINE:
+                       /*
+                        * Force forward N (default 1) line.
+                        */
+                       if (number <= 0)
+                               number = 1;
+                       cmd_exec();
+                       if (show_attn == OPT_ONPLUS && number > 1)
+                               set_attnpos(bottompos);
+                       forward((int) number, 1, 0);
+                       break;
+
+               case A_BF_LINE:
+                       /*
+                        * Force backward N (default 1) line.
+                        */
+                       if (number <= 0)
+                               number = 1;
+                       cmd_exec();
+                       backward((int) number, 1, 0);
+                       break;
+               
+               case A_FF_SCREEN:
+                       /*
+                        * Force forward one screen.
+                        */
+                       if (number <= 0)
+                               number = get_swindow();
+                       cmd_exec();
+                       if (show_attn == OPT_ONPLUS)
+                               set_attnpos(bottompos);
+                       forward((int) number, 1, 0);
+                       break;
+
+               case A_F_FOREVER:
+                       /*
+                        * Forward forever, ignoring EOF.
+                        */
+                       if (ch_getflags() & CH_HELPFILE)
+                               break;
+                       cmd_exec();
+                       jump_forw();
+                       ignore_eoi = 1;
+                       while (!sigs)
+                       {
+                               make_display();
+                               forward(1, 0, 0);
+                       }
+                       ignore_eoi = 0;
+                       /*
+                        * This gets us back in "F mode" after processing 
+                        * a non-abort signal (e.g. window-change).  
+                        */
+                       if (sigs && !ABORT_SIGS())
+                               newaction = A_F_FOREVER;
+                       break;
+
+               case A_F_SCROLL:
+                       /*
+                        * Forward N lines 
+                        * (default same as last 'd' or 'u' command).
+                        */
+                       if (number > 0)
+                               wscroll = (int) number;
+                       cmd_exec();
+                       if (show_attn == OPT_ONPLUS)
+                               set_attnpos(bottompos);
+                       forward(wscroll, 0, 0);
+                       break;
+
+               case A_B_SCROLL:
+                       /*
+                        * Forward N lines 
+                        * (default same as last 'd' or 'u' command).
+                        */
+                       if (number > 0)
+                               wscroll = (int) number;
+                       cmd_exec();
+                       backward(wscroll, 0, 0);
+                       break;
+
+               case A_FREPAINT:
+                       /*
+                        * Flush buffers, then repaint screen.
+                        * Don't flush the buffers on a pipe!
+                        */
+                       clear_buffers();
+                       /* FALLTHRU */
+               case A_REPAINT:
+                       /*
+                        * Repaint screen.
+                        */
+                       cmd_exec();
+                       repaint();
+                       break;
+
+               case A_GOLINE:
+                       /*
+                        * Go to line N, default beginning of file.
+                        */
+                       if (number <= 0)
+                               number = 1;
+                       cmd_exec();
+                       jump_back(number);
+                       break;
+
+               case A_PERCENT:
+                       /*
+                        * Go to a specified percentage into the file.
+                        */
+                       if (number < 0)
+                       {
+                               number = 0;
+                               fraction = 0;
+                       }
+                       if (number > 100)
+                       {
+                               number = 100;
+                               fraction = 0;
+                       }
+                       cmd_exec();
+                       jump_percent((int) number, fraction);
+                       break;
+
+               case A_GOEND:
+                       /*
+                        * Go to line N, default end of file.
+                        */
+                       cmd_exec();
+                       if (number <= 0)
+                               jump_forw();
+                       else
+                               jump_back(number);
+                       break;
+
+               case A_GOPOS:
+                       /*
+                        * Go to a specified byte position in the file.
+                        */
+                       cmd_exec();
+                       if (number < 0)
+                               number = 0;
+                       jump_line_loc((POSITION) number, jump_sline);
+                       break;
+
+               case A_STAT:
+                       /*
+                        * Print file name, etc.
+                        */
+                       if (ch_getflags() & CH_HELPFILE)
+                               break;
+                       cmd_exec();
+                       parg.p_string = eq_message();
+                       error("%s", &parg);
+                       break;
+
+               case A_VERSION:
+                       /*
+                        * Print version number, without the "@(#)".
+                        */
+                       cmd_exec();
+                       dispversion();
+                       break;
+
+               case A_QUIT:
+                       /*
+                        * Exit.
+                        */
+                       if (curr_ifile != NULL_IFILE && 
+                           ch_getflags() & CH_HELPFILE)
+                       {
+                               /*
+                                * Quit while viewing the help file
+                                * just means return to viewing the
+                                * previous file.
+                                */
+                               hshift = save_hshift;
+                               if (edit_prev(1) == 0)
+                                       break;
+                       }
+                       if (extra != NULL)
+                               quit(*extra);
+                       quit(QUIT_OK);
+                       break;
+
+/*
+ * Define abbreviation for a commonly used sequence below.
+ */
+#define        DO_SEARCH() \
+                       if (number <= 0) number = 1;    \
+                       mca_search();                   \
+                       cmd_exec();                     \
+                       multi_search((char *)NULL, (int) number);
+
+
+               case A_F_SEARCH:
+                       /*
+                        * Search forward for a pattern.
+                        * Get the first char of the pattern.
+                        */
+                       search_type = SRCH_FORW;
+                       if (number <= 0)
+                               number = 1;
+                       mca_search();
+                       c = getcc();
+                       goto again;
+
+               case A_B_SEARCH:
+                       /*
+                        * Search backward for a pattern.
+                        * Get the first char of the pattern.
+                        */
+                       search_type = SRCH_BACK;
+                       if (number <= 0)
+                               number = 1;
+                       mca_search();
+                       c = getcc();
+                       goto again;
+
+               case A_FILTER:
+#if HILITE_SEARCH
+                       search_type = SRCH_FORW | SRCH_FILTER;
+                       mca_search();
+                       c = getcc();
+                       goto again;
+#else
+                       error("Command not available", NULL_PARG);
+                       break;
+#endif
+
+               case A_AGAIN_SEARCH:
+                       /*
+                        * Repeat previous search.
+                        */
+                       DO_SEARCH();
+                       break;
+               
+               case A_T_AGAIN_SEARCH:
+                       /*
+                        * Repeat previous search, multiple files.
+                        */
+                       search_type |= SRCH_PAST_EOF;
+                       DO_SEARCH();
+                       break;
+
+               case A_REVERSE_SEARCH:
+                       /*
+                        * Repeat previous search, in reverse direction.
+                        */
+                       save_search_type = search_type;
+                       search_type = SRCH_REVERSE(search_type);
+                       DO_SEARCH();
+                       search_type = save_search_type;
+                       break;
+
+               case A_T_REVERSE_SEARCH:
+                       /* 
+                        * Repeat previous search, 
+                        * multiple files in reverse direction.
+                        */
+                       save_search_type = search_type;
+                       search_type = SRCH_REVERSE(search_type);
+                       search_type |= SRCH_PAST_EOF;
+                       DO_SEARCH();
+                       search_type = save_search_type;
+                       break;
+
+               case A_UNDO_SEARCH:
+                       undo_search();
+                       break;
+
+               case A_HELP:
+                       /*
+                        * Help.
+                        */
+                       if (ch_getflags() & CH_HELPFILE)
+                               break;
+                       cmd_exec();
+                       save_hshift = hshift;
+                       hshift = 0;
+                       (void) edit(FAKE_HELPFILE);
+                       break;
+
+               case A_EXAMINE:
+#if EXAMINE
+                       /*
+                        * Edit a new file.  Get the filename.
+                        */
+                       if (secure)
+                       {
+                               error("Command not available", NULL_PARG);
+                               break;
+                       }
+                       start_mca(A_EXAMINE, "Examine: ", ml_examine, 0);
+                       c = getcc();
+                       goto again;
+#else
+                       error("Command not available", NULL_PARG);
+                       break;
+#endif
+                       
+               case A_VISUAL:
+                       /*
+                        * Invoke an editor on the input file.
+                        */
+#if EDITOR
+                       if (secure)
+                       {
+                               error("Command not available", NULL_PARG);
+                               break;
+                       }
+                       if (ch_getflags() & CH_HELPFILE)
+                               break;
+                       if (strcmp(get_filename(curr_ifile), "-") == 0)
+                       {
+                               error("Cannot edit standard input", NULL_PARG);
+                               break;
+                       }
+                       if (curr_altfilename != NULL)
+                       {
+                               error("WARNING: This file was viewed via LESSOPEN",
+                                       NULL_PARG);
+                       }
+                       start_mca(A_SHELL, "!", ml_shell, 0);
+                       /*
+                        * Expand the editor prototype string
+                        * and pass it to the system to execute.
+                        * (Make sure the screen is displayed so the
+                        * expansion of "+%lm" works.)
+                        */
+                       make_display();
+                       cmd_exec();
+                       lsystem(pr_expand(editproto, 0), (char*)NULL);
+                       break;
+#else
+                       error("Command not available", NULL_PARG);
+                       break;
+#endif
+
+               case A_NEXT_FILE:
+                       /*
+                        * Examine next file.
+                        */
+#if TAGS
+                       if (ntags())
+                       {
+                               error("No next file", NULL_PARG);
+                               break;
+                       }
+#endif
+                       if (number <= 0)
+                               number = 1;
+                       if (edit_next((int) number))
+                       {
+                               if (get_quit_at_eof() && eof_displayed() && 
+                                   !(ch_getflags() & CH_HELPFILE))
+                                       quit(QUIT_OK);
+                               parg.p_string = (number > 1) ? "(N-th) " : "";
+                               error("No %snext file", &parg);
+                       }
+                       break;
+
+               case A_PREV_FILE:
+                       /*
+                        * Examine previous file.
+                        */
+#if TAGS
+                       if (ntags())
+                       {
+                               error("No previous file", NULL_PARG);
+                               break;
+                       }
+#endif
+                       if (number <= 0)
+                               number = 1;
+                       if (edit_prev((int) number))
+                       {
+                               parg.p_string = (number > 1) ? "(N-th) " : "";
+                               error("No %sprevious file", &parg);
+                       }
+                       break;
+
+               case A_NEXT_TAG:
+#if TAGS
+                       if (number <= 0)
+                               number = 1;
+                       tagfile = nexttag((int) number);
+                       if (tagfile == NULL)
+                       {
+                               error("No next tag", NULL_PARG);
+                               break;
+                       }
+                       if (edit(tagfile) == 0)
+                       {
+                               POSITION pos = tagsearch();
+                               if (pos != NULL_POSITION)
+                                       jump_loc(pos, jump_sline);
+                       }
+#else
+                       error("Command not available", NULL_PARG);
+#endif
+                       break;
+
+               case A_PREV_TAG:
+#if TAGS
+                       if (number <= 0)
+                               number = 1;
+                       tagfile = prevtag((int) number);
+                       if (tagfile == NULL)
+                       {
+                               error("No previous tag", NULL_PARG);
+                               break;
+                       }
+                       if (edit(tagfile) == 0)
+                       {
+                               POSITION pos = tagsearch();
+                               if (pos != NULL_POSITION)
+                                       jump_loc(pos, jump_sline);
+                       }
+#else
+                       error("Command not available", NULL_PARG);
+#endif
+                       break;
+
+               case A_INDEX_FILE:
+                       /*
+                        * Examine a particular file.
+                        */
+                       if (number <= 0)
+                               number = 1;
+                       if (edit_index((int) number))
+                               error("No such file", NULL_PARG);
+                       break;
+
+               case A_REMOVE_FILE:
+                       if (ch_getflags() & CH_HELPFILE)
+                               break;
+                       old_ifile = curr_ifile;
+                       new_ifile = getoff_ifile(curr_ifile);
+                       if (new_ifile == NULL_IFILE)
+                       {
+                               bell();
+                               break;
+                       }
+                       if (edit_ifile(new_ifile) != 0)
+                       {
+                               reedit_ifile(old_ifile);
+                               break;
+                       }
+                       del_ifile(old_ifile);
+                       break;
+
+               case A_OPT_TOGGLE:
+                       optflag = OPT_TOGGLE;
+                       optgetname = FALSE;
+                       mca_opt_toggle();
+                       c = getcc();
+                       goto again;
+
+               case A_DISP_OPTION:
+                       /*
+                        * Report a flag setting.
+                        */
+                       optflag = OPT_NO_TOGGLE;
+                       optgetname = FALSE;
+                       mca_opt_toggle();
+                       c = getcc();
+                       goto again;
+
+               case A_FIRSTCMD:
+                       /*
+                        * Set an initial command for new files.
+                        */
+                       start_mca(A_FIRSTCMD, "+", (void*)NULL, 0);
+                       c = getcc();
+                       goto again;
+
+               case A_SHELL:
+                       /*
+                        * Shell escape.
+                        */
+#if SHELL_ESCAPE
+                       if (secure)
+                       {
+                               error("Command not available", NULL_PARG);
+                               break;
+                       }
+                       start_mca(A_SHELL, "!", ml_shell, 0);
+                       c = getcc();
+                       goto again;
+#else
+                       error("Command not available", NULL_PARG);
+                       break;
+#endif
+
+               case A_SETMARK:
+                       /*
+                        * Set a mark.
+                        */
+                       if (ch_getflags() & CH_HELPFILE)
+                               break;
+                       start_mca(A_SETMARK, "mark: ", (void*)NULL, 0);
+                       c = getcc();
+                       if (c == erase_char || c == erase2_char ||
+                           c == kill_char || c == '\n' || c == '\r')
+                               break;
+                       setmark(c);
+                       break;
+
+               case A_GOMARK:
+                       /*
+                        * Go to a mark.
+                        */
+                       start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0);
+                       c = getcc();
+                       if (c == erase_char || c == erase2_char ||
+                           c == kill_char || c == '\n' || c == '\r')
+                               break;
+                       cmd_exec();
+                       gomark(c);
+                       break;
+
+               case A_PIPE:
+#if PIPEC
+                       if (secure)
+                       {
+                               error("Command not available", NULL_PARG);
+                               break;
+                       }
+                       start_mca(A_PIPE, "|mark: ", (void*)NULL, 0);
+                       c = getcc();
+                       if (c == erase_char || c == erase2_char || c == kill_char)
+                               break;
+                       if (c == '\n' || c == '\r')
+                               c = '.';
+                       if (badmark(c))
+                               break;
+                       pipec = c;
+                       start_mca(A_PIPE, "!", ml_shell, 0);
+                       c = getcc();
+                       goto again;
+#else
+                       error("Command not available", NULL_PARG);
+                       break;
+#endif
+
+               case A_B_BRACKET:
+               case A_F_BRACKET:
+                       start_mca(action, "Brackets: ", (void*)NULL, 0);
+                       c = getcc();
+                       goto again;
+
+               case A_LSHIFT:
+                       if (number > 0)
+                               shift_count = number;
+                       else
+                               number = (shift_count > 0) ?
+                                       shift_count : sc_width / 2;
+                       if (number > hshift)
+                               number = hshift;
+                       hshift -= number;
+                       screen_trashed = 1;
+                       break;
+
+               case A_RSHIFT:
+                       if (number > 0)
+                               shift_count = number;
+                       else
+                               number = (shift_count > 0) ?
+                                       shift_count : sc_width / 2;
+                       hshift += number;
+                       screen_trashed = 1;
+                       break;
+
+               case A_PREFIX:
+                       /*
+                        * The command is incomplete (more chars are needed).
+                        * Display the current char, so the user knows
+                        * what's going on, and get another character.
+                        */
+                       if (mca != A_PREFIX)
+                       {
+                               cmd_reset();
+                               start_mca(A_PREFIX, " ", (void*)NULL,
+                                       CF_QUIT_ON_ERASE);
+                               (void) cmd_char(c);
+                       }
+                       c = getcc();
+                       goto again;
+
+               case A_NOACTION:
+                       break;
+
+               default:
+                       bell();
+                       break;
+               }
+       }
+}
diff --git a/thirdparty/less/cvt.c b/thirdparty/less/cvt.c
new file mode 100644 (file)
index 0000000..7443984
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+/*
+ * Routines to convert text in various ways.  Used by search.
+ */
+
+#include "less.h"
+#include "charset.h"
+
+extern int utf_mode;
+
+/*
+ * Get the length of a buffer needed to convert a string.
+ */
+       public int
+cvt_length(len, ops)
+       int len;
+       int ops;
+{
+       if (utf_mode)
+               /*
+                * Just copying a string in UTF-8 mode can cause it to grow 
+                * in length.
+                * Four output bytes for one input byte is the worst case.
+                */
+               len *= 4;
+       return (len + 1);
+}
+
+/*
+ * Allocate a chpos array for use by cvt_text.
+ */
+       public int *
+cvt_alloc_chpos(len)
+       int len;
+{
+       int i;
+       int *chpos = (int *) ecalloc(sizeof(int), len);
+       /* Initialize all entries to an invalid position. */
+       for (i = 0;  i < len;  i++)
+               chpos[i] = -1;
+       return (chpos);
+}
+
+/*
+ * Convert text.  Perform the transformations specified by ops.
+ * Returns converted text in odst.  The original offset of each
+ * odst character (when it was in osrc) is returned in the chpos array.
+ */
+       public void
+cvt_text(odst, osrc, chpos, lenp, ops)
+       char *odst;
+       char *osrc;
+       int *chpos;
+       int *lenp;
+       int ops;
+{
+       char *dst;
+       char *src;
+       register char *src_end;
+       LWCHAR ch;
+
+       if (lenp != NULL)
+               src_end = osrc + *lenp;
+       else
+               src_end = osrc + strlen(osrc);
+
+       for (src = osrc, dst = odst;  src < src_end;  )
+       {
+               int src_pos = src - osrc;
+               int dst_pos = dst - odst;
+               ch = step_char(&src, +1, src_end);
+               if ((ops & CVT_BS) && ch == '\b' && dst > odst)
+               {
+                       /* Delete backspace and preceding char. */
+                       do {
+                               dst--;
+                       } while (dst > odst &&
+                               !IS_ASCII_OCTET(*dst) && !IS_UTF8_LEAD(*dst));
+               } else if ((ops & CVT_ANSI) && IS_CSI_START(ch))
+               {
+                       /* Skip to end of ANSI escape sequence. */
+                       src++;  /* skip the CSI start char */
+                       while (src < src_end)
+                               if (!is_ansi_middle(*src++))
+                                       break;
+               } else
+               {
+                       /* Just copy the char to the destination buffer. */
+                       if ((ops & CVT_TO_LC) && IS_UPPER(ch))
+                               ch = TO_LOWER(ch);
+                       put_wchar(&dst, ch);
+                       /*
+                        * Record the original position of the char.
+                        * But if we've already recorded a position
+                        * for this char (due to a backspace), leave
+                        * it alone; if multiple source chars map to
+                        * one destination char, we want the position
+                        * of the first one.
+                        */
+                       if (chpos != NULL && chpos[dst_pos] < 0)
+                               chpos[dst_pos] = src_pos;
+               }
+       }
+       if ((ops & CVT_CRLF) && dst > odst && dst[-1] == '\r')
+               dst--;
+       *dst = '\0';
+       if (lenp != NULL)
+               *lenp = dst - odst;
+       if (chpos != NULL)
+               chpos[dst - odst] = src - osrc;
+}
diff --git a/thirdparty/less/decode.c b/thirdparty/less/decode.c
new file mode 100644 (file)
index 0000000..65d65bb
--- /dev/null
@@ -0,0 +1,841 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Routines to decode user commands.
+ *
+ * This is all table driven.
+ * A command table is a sequence of command descriptors.
+ * Each command descriptor is a sequence of bytes with the following format:
+ *     <c1><c2>...<cN><0><action>
+ * The characters c1,c2,...,cN are the command string; that is,
+ * the characters which the user must type.
+ * It is terminated by a null <0> byte.
+ * The byte after the null byte is the action code associated
+ * with the command string.
+ * If an action byte is OR-ed with A_EXTRA, this indicates
+ * that the option byte is followed by an extra string.
+ *
+ * There may be many command tables.
+ * The first (default) table is built-in.
+ * Other tables are read in from "lesskey" files.
+ * All the tables are linked together and are searched in order.
+ */
+
+#include "less.h"
+#include "cmd.h"
+#include "lesskey.h"
+
+extern int erase_char, erase2_char, kill_char;
+extern int secure;
+
+#define SK(k) \
+       SK_SPECIAL_KEY, (k), 6, 1, 1, 1
+/*
+ * Command table is ordered roughly according to expected
+ * frequency of use, so the common commands are near the beginning.
+ */
+
+static unsigned char cmdtable[] =
+{
+       '\r',0,                         A_F_LINE,
+       '\n',0,                         A_F_LINE,
+       'e',0,                          A_F_LINE,
+       'j',0,                          A_F_LINE,
+       SK(SK_DOWN_ARROW),0,            A_F_LINE,
+       CONTROL('E'),0,                 A_F_LINE,
+       CONTROL('N'),0,                 A_F_LINE,
+       'k',0,                          A_B_LINE,
+       'y',0,                          A_B_LINE,
+       CONTROL('Y'),0,                 A_B_LINE,
+       SK(SK_CONTROL_K),0,             A_B_LINE,
+       CONTROL('P'),0,                 A_B_LINE,
+       SK(SK_UP_ARROW),0,              A_B_LINE,
+       'J',0,                          A_FF_LINE,
+       'K',0,                          A_BF_LINE,
+       'Y',0,                          A_BF_LINE,
+       'd',0,                          A_F_SCROLL,
+       CONTROL('D'),0,                 A_F_SCROLL,
+       'u',0,                          A_B_SCROLL,
+       CONTROL('U'),0,                 A_B_SCROLL,
+       ' ',0,                          A_F_SCREEN,
+       'f',0,                          A_F_SCREEN,
+       CONTROL('F'),0,                 A_F_SCREEN,
+       CONTROL('V'),0,                 A_F_SCREEN,
+       SK(SK_PAGE_DOWN),0,             A_F_SCREEN,
+       'b',0,                          A_B_SCREEN,
+       CONTROL('B'),0,                 A_B_SCREEN,
+       ESC,'v',0,                      A_B_SCREEN,
+       SK(SK_PAGE_UP),0,               A_B_SCREEN,
+       'z',0,                          A_F_WINDOW,
+       'w',0,                          A_B_WINDOW,
+       ESC,' ',0,                      A_FF_SCREEN,
+       'F',0,                          A_F_FOREVER,
+       'R',0,                          A_FREPAINT,
+       'r',0,                          A_REPAINT,
+       CONTROL('R'),0,                 A_REPAINT,
+       CONTROL('L'),0,                 A_REPAINT,
+       ESC,'u',0,                      A_UNDO_SEARCH,
+       'g',0,                          A_GOLINE,
+       SK(SK_HOME),0,                  A_GOLINE,
+       '<',0,                          A_GOLINE,
+       ESC,'<',0,                      A_GOLINE,
+       'p',0,                          A_PERCENT,
+       '%',0,                          A_PERCENT,
+       ESC,'[',0,                      A_LSHIFT,
+       ESC,']',0,                      A_RSHIFT,
+       ESC,'(',0,                      A_LSHIFT,
+       ESC,')',0,                      A_RSHIFT,
+       SK(SK_RIGHT_ARROW),0,           A_RSHIFT,
+       SK(SK_LEFT_ARROW),0,            A_LSHIFT,
+       '{',0,                          A_F_BRACKET|A_EXTRA,    '{','}',0,
+       '}',0,                          A_B_BRACKET|A_EXTRA,    '{','}',0,
+       '(',0,                          A_F_BRACKET|A_EXTRA,    '(',')',0,
+       ')',0,                          A_B_BRACKET|A_EXTRA,    '(',')',0,
+       '[',0,                          A_F_BRACKET|A_EXTRA,    '[',']',0,
+       ']',0,                          A_B_BRACKET|A_EXTRA,    '[',']',0,
+       ESC,CONTROL('F'),0,             A_F_BRACKET,
+       ESC,CONTROL('B'),0,             A_B_BRACKET,
+       'G',0,                          A_GOEND,
+       ESC,'>',0,                      A_GOEND,
+       '>',0,                          A_GOEND,
+       SK(SK_END),0,                   A_GOEND,
+       'P',0,                          A_GOPOS,
+
+       '0',0,                          A_DIGIT,
+       '1',0,                          A_DIGIT,
+       '2',0,                          A_DIGIT,
+       '3',0,                          A_DIGIT,
+       '4',0,                          A_DIGIT,
+       '5',0,                          A_DIGIT,
+       '6',0,                          A_DIGIT,
+       '7',0,                          A_DIGIT,
+       '8',0,                          A_DIGIT,
+       '9',0,                          A_DIGIT,
+       '.',0,                          A_DIGIT,
+
+       '=',0,                          A_STAT,
+       CONTROL('G'),0,                 A_STAT,
+       ':','f',0,                      A_STAT,
+       '/',0,                          A_F_SEARCH,
+       '?',0,                          A_B_SEARCH,
+       ESC,'/',0,                      A_F_SEARCH|A_EXTRA,     '*',0,
+       ESC,'?',0,                      A_B_SEARCH|A_EXTRA,     '*',0,
+       'n',0,                          A_AGAIN_SEARCH,
+       ESC,'n',0,                      A_T_AGAIN_SEARCH,
+       'N',0,                          A_REVERSE_SEARCH,
+       ESC,'N',0,                      A_T_REVERSE_SEARCH,
+       '&',0,                          A_FILTER,
+       'm',0,                          A_SETMARK,
+       '\'',0,                         A_GOMARK,
+       CONTROL('X'),CONTROL('X'),0,    A_GOMARK,
+       'E',0,                          A_EXAMINE,
+       ':','e',0,                      A_EXAMINE,
+       CONTROL('X'),CONTROL('V'),0,    A_EXAMINE,
+       ':','n',0,                      A_NEXT_FILE,
+       ':','p',0,                      A_PREV_FILE,
+       't',0,                          A_NEXT_TAG,
+       'T',0,                          A_PREV_TAG,
+       ':','x',0,                      A_INDEX_FILE,
+       ':','d',0,                      A_REMOVE_FILE,
+       '-',0,                          A_OPT_TOGGLE,
+       ':','t',0,                      A_OPT_TOGGLE|A_EXTRA,   't',0,
+       's',0,                          A_OPT_TOGGLE|A_EXTRA,   'o',0,
+       '_',0,                          A_DISP_OPTION,
+       '|',0,                          A_PIPE,
+       'v',0,                          A_VISUAL,
+       '!',0,                          A_SHELL,
+       '+',0,                          A_FIRSTCMD,
+
+       'H',0,                          A_HELP,
+       'h',0,                          A_HELP,
+       SK(SK_F1),0,                    A_HELP,
+       'V',0,                          A_VERSION,
+       'q',0,                          A_QUIT,
+       'Q',0,                          A_QUIT,
+       ':','q',0,                      A_QUIT,
+       ':','Q',0,                      A_QUIT,
+       'Z','Z',0,                      A_QUIT
+};
+
+static unsigned char edittable[] =
+{
+       '\t',0,                         EC_F_COMPLETE,  /* TAB */
+       '\17',0,                        EC_B_COMPLETE,  /* BACKTAB */
+       SK(SK_BACKTAB),0,               EC_B_COMPLETE,  /* BACKTAB */
+       ESC,'\t',0,                     EC_B_COMPLETE,  /* ESC TAB */
+       CONTROL('L'),0,                 EC_EXPAND,      /* CTRL-L */
+       CONTROL('V'),0,                 EC_LITERAL,     /* BACKSLASH */
+       CONTROL('A'),0,                 EC_LITERAL,     /* BACKSLASH */
+       ESC,'l',0,                      EC_RIGHT,       /* ESC l */
+       SK(SK_RIGHT_ARROW),0,           EC_RIGHT,       /* RIGHTARROW */
+       ESC,'h',0,                      EC_LEFT,        /* ESC h */
+       SK(SK_LEFT_ARROW),0,            EC_LEFT,        /* LEFTARROW */
+       ESC,'b',0,                      EC_W_LEFT,      /* ESC b */
+       ESC,SK(SK_LEFT_ARROW),0,        EC_W_LEFT,      /* ESC LEFTARROW */
+       SK(SK_CTL_LEFT_ARROW),0,        EC_W_LEFT,      /* CTRL-LEFTARROW */
+       ESC,'w',0,                      EC_W_RIGHT,     /* ESC w */
+       ESC,SK(SK_RIGHT_ARROW),0,       EC_W_RIGHT,     /* ESC RIGHTARROW */
+       SK(SK_CTL_RIGHT_ARROW),0,       EC_W_RIGHT,     /* CTRL-RIGHTARROW */
+       ESC,'i',0,                      EC_INSERT,      /* ESC i */
+       SK(SK_INSERT),0,                EC_INSERT,      /* INSERT */
+       ESC,'x',0,                      EC_DELETE,      /* ESC x */
+       SK(SK_DELETE),0,                EC_DELETE,      /* DELETE */
+       ESC,'X',0,                      EC_W_DELETE,    /* ESC X */
+       ESC,SK(SK_DELETE),0,            EC_W_DELETE,    /* ESC DELETE */
+       SK(SK_CTL_DELETE),0,            EC_W_DELETE,    /* CTRL-DELETE */
+       SK(SK_CTL_BACKSPACE),0,         EC_W_BACKSPACE, /* CTRL-BACKSPACE */
+       ESC,'\b',0,                     EC_W_BACKSPACE, /* ESC BACKSPACE */
+       ESC,'0',0,                      EC_HOME,        /* ESC 0 */
+       SK(SK_HOME),0,                  EC_HOME,        /* HOME */
+       ESC,'$',0,                      EC_END,         /* ESC $ */
+       SK(SK_END),0,                   EC_END,         /* END */
+       ESC,'k',0,                      EC_UP,          /* ESC k */
+       SK(SK_UP_ARROW),0,              EC_UP,          /* UPARROW */
+       ESC,'j',0,                      EC_DOWN,        /* ESC j */
+       SK(SK_DOWN_ARROW),0,            EC_DOWN,        /* DOWNARROW */
+       CONTROL('G'),0,                 EC_ABORT,       /* CTRL-G */
+};
+
+/*
+ * Structure to support a list of command tables.
+ */
+struct tablelist
+{
+       struct tablelist *t_next;
+       char *t_start;
+       char *t_end;
+};
+
+/*
+ * List of command tables and list of line-edit tables.
+ */
+static struct tablelist *list_fcmd_tables = NULL;
+static struct tablelist *list_ecmd_tables = NULL;
+static struct tablelist *list_var_tables = NULL;
+static struct tablelist *list_sysvar_tables = NULL;
+
+
+/*
+ * Expand special key abbreviations in a command table.
+ */
+       static void
+expand_special_keys(table, len)
+       char *table;
+       int len;
+{
+       register char *fm;
+       register char *to;
+       register int a;
+       char *repl;
+       int klen;
+
+       for (fm = table;  fm < table + len; )
+       {
+               /*
+                * Rewrite each command in the table with any
+                * special key abbreviations expanded.
+                */
+               for (to = fm;  *fm != '\0'; )
+               {
+                       if (*fm != SK_SPECIAL_KEY)
+                       {
+                               *to++ = *fm++;
+                               continue;
+                       }
+                       /*
+                        * After SK_SPECIAL_KEY, next byte is the type
+                        * of special key (one of the SK_* contants),
+                        * and the byte after that is the number of bytes,
+                        * N, reserved by the abbreviation (including the
+                        * SK_SPECIAL_KEY and key type bytes).
+                        * Replace all N bytes with the actual bytes
+                        * output by the special key on this terminal.
+                        */
+                       repl = special_key_str(fm[1]);
+                       klen = fm[2] & 0377;
+                       fm += klen;
+                       if (repl == NULL || (int) strlen(repl) > klen)
+                               repl = "\377";
+                       while (*repl != '\0')
+                               *to++ = *repl++;
+               }
+               *to++ = '\0';
+               /*
+                * Fill any unused bytes between end of command and 
+                * the action byte with A_SKIP.
+                */
+               while (to <= fm)
+                       *to++ = A_SKIP;
+               fm++;
+               a = *fm++ & 0377;
+               if (a & A_EXTRA)
+               {
+                       while (*fm++ != '\0')
+                               continue;
+               }
+       }
+}
+
+/*
+ * Initialize the command lists.
+ */
+       public void
+init_cmds()
+{
+       /*
+        * Add the default command tables.
+        */
+       add_fcmd_table((char*)cmdtable, sizeof(cmdtable));
+       add_ecmd_table((char*)edittable, sizeof(edittable));
+#if USERFILE
+       /*
+        * For backwards compatibility,
+        * try to add tables in the OLD system lesskey file.
+        */
+#ifdef BINDIR
+       add_hometable(NULL, BINDIR "/.sysless", 1);
+#endif
+       /*
+        * Try to add the tables in the system lesskey file.
+        */
+       add_hometable("LESSKEY_SYSTEM", LESSKEYFILE_SYS, 1);
+       /*
+        * Try to add the tables in the standard lesskey file "$HOME/.less".
+        */
+       add_hometable("LESSKEY", LESSKEYFILE, 0);
+#endif
+}
+
+/*
+ * Add a command table.
+ */
+       static int
+add_cmd_table(tlist, buf, len)
+       struct tablelist **tlist;
+       char *buf;
+       int len;
+{
+       register struct tablelist *t;
+
+       if (len == 0)
+               return (0);
+       /*
+        * Allocate a tablelist structure, initialize it, 
+        * and link it into the list of tables.
+        */
+       if ((t = (struct tablelist *) 
+                       calloc(1, sizeof(struct tablelist))) == NULL)
+       {
+               return (-1);
+       }
+       expand_special_keys(buf, len);
+       t->t_start = buf;
+       t->t_end = buf + len;
+       t->t_next = *tlist;
+       *tlist = t;
+       return (0);
+}
+
+/*
+ * Add a command table.
+ */
+       public void
+add_fcmd_table(buf, len)
+       char *buf;
+       int len;
+{
+       if (add_cmd_table(&list_fcmd_tables, buf, len) < 0)
+               error("Warning: some commands disabled", NULL_PARG);
+}
+
+/*
+ * Add an editing command table.
+ */
+       public void
+add_ecmd_table(buf, len)
+       char *buf;
+       int len;
+{
+       if (add_cmd_table(&list_ecmd_tables, buf, len) < 0)
+               error("Warning: some edit commands disabled", NULL_PARG);
+}
+
+/*
+ * Add an environment variable table.
+ */
+       static void
+add_var_table(tlist, buf, len)
+       struct tablelist **tlist;
+       char *buf;
+       int len;
+{
+       if (add_cmd_table(tlist, buf, len) < 0)
+               error("Warning: environment variables from lesskey file unavailable", NULL_PARG);
+}
+
+/*
+ * Search a single command table for the command string in cmd.
+ */
+       static int
+cmd_search(cmd, table, endtable, sp)
+       char *cmd;
+       char *table;
+       char *endtable;
+       char **sp;
+{
+       register char *p;
+       register char *q;
+       register int a;
+
+       *sp = NULL;
+       for (p = table, q = cmd;  p < endtable;  p++, q++)
+       {
+               if (*p == *q)
+               {
+                       /*
+                        * Current characters match.
+                        * If we're at the end of the string, we've found it.
+                        * Return the action code, which is the character
+                        * after the null at the end of the string
+                        * in the command table.
+                        */
+                       if (*p == '\0')
+                       {
+                               a = *++p & 0377;
+                               while (a == A_SKIP)
+                                       a = *++p & 0377;
+                               if (a == A_END_LIST)
+                               {
+                                       /*
+                                        * We get here only if the original
+                                        * cmd string passed in was empty ("").
+                                        * I don't think that can happen,
+                                        * but just in case ...
+                                        */
+                                       return (A_UINVALID);
+                               }
+                               /*
+                                * Check for an "extra" string.
+                                */
+                               if (a & A_EXTRA)
+                               {
+                                       *sp = ++p;
+                                       a &= ~A_EXTRA;
+                               }
+                               return (a);
+                       }
+               } else if (*q == '\0')
+               {
+                       /*
+                        * Hit the end of the user's command,
+                        * but not the end of the string in the command table.
+                        * The user's command is incomplete.
+                        */
+                       return (A_PREFIX);
+               } else
+               {
+                       /*
+                        * Not a match.
+                        * Skip ahead to the next command in the
+                        * command table, and reset the pointer
+                        * to the beginning of the user's command.
+                        */
+                       if (*p == '\0' && p[1] == A_END_LIST)
+                       {
+                               /*
+                                * A_END_LIST is a special marker that tells 
+                                * us to abort the cmd search.
+                                */
+                               return (A_UINVALID);
+                       }
+                       while (*p++ != '\0')
+                               continue;
+                       while (*p == A_SKIP)
+                               p++;
+                       if (*p & A_EXTRA)
+                               while (*++p != '\0')
+                                       continue;
+                       q = cmd-1;
+               }
+       }
+       /*
+        * No match found in the entire command table.
+        */
+       return (A_INVALID);
+}
+
+/*
+ * Decode a command character and return the associated action.
+ * The "extra" string, if any, is returned in sp.
+ */
+       static int
+cmd_decode(tlist, cmd, sp)
+       struct tablelist *tlist;
+       char *cmd;
+       char **sp;
+{
+       register struct tablelist *t;
+       register int action = A_INVALID;
+
+       /*
+        * Search thru all the command tables.
+        * Stop when we find an action which is not A_INVALID.
+        */
+       for (t = tlist;  t != NULL;  t = t->t_next)
+       {
+               action = cmd_search(cmd, t->t_start, t->t_end, sp);
+               if (action != A_INVALID)
+                       break;
+       }
+       if (action == A_UINVALID)
+               action = A_INVALID;
+       return (action);
+}
+
+/*
+ * Decode a command from the cmdtables list.
+ */
+       public int
+fcmd_decode(cmd, sp)
+       char *cmd;
+       char **sp;
+{
+       return (cmd_decode(list_fcmd_tables, cmd, sp));
+}
+
+/*
+ * Decode a command from the edittables list.
+ */
+       public int
+ecmd_decode(cmd, sp)
+       char *cmd;
+       char **sp;
+{
+       return (cmd_decode(list_ecmd_tables, cmd, sp));
+}
+
+/*
+ * Get the value of an environment variable.
+ * Looks first in the lesskey file, then in the real environment.
+ */
+       public char *
+lgetenv(var)
+       char *var;
+{
+       int a;
+       char *s;
+
+       a = cmd_decode(list_var_tables, var, &s);
+       if (a == EV_OK)
+               return (s);
+       s = getenv(var);
+       if (s != NULL && *s != '\0')
+               return (s);
+       a = cmd_decode(list_sysvar_tables, var, &s);
+       if (a == EV_OK)
+               return (s);
+       return (NULL);
+}
+
+#if USERFILE
+/*
+ * Get an "integer" from a lesskey file.
+ * Integers are stored in a funny format: 
+ * two bytes, low order first, in radix KRADIX.
+ */
+       static int
+gint(sp)
+       char **sp;
+{
+       int n;
+
+       n = *(*sp)++;
+       n += *(*sp)++ * KRADIX;
+       return (n);
+}
+
+/*
+ * Process an old (pre-v241) lesskey file.
+ */
+       static int
+old_lesskey(buf, len)
+       char *buf;
+       int len;
+{
+       /*
+        * Old-style lesskey file.
+        * The file must end with either 
+        *     ...,cmd,0,action
+        * or  ...,cmd,0,action|A_EXTRA,string,0
+        * So the last byte or the second to last byte must be zero.
+        */
+       if (buf[len-1] != '\0' && buf[len-2] != '\0')
+               return (-1);
+       add_fcmd_table(buf, len);
+       return (0);
+}
+
+/* 
+ * Process a new (post-v241) lesskey file.
+ */
+       static int
+new_lesskey(buf, len, sysvar)
+       char *buf;
+       int len;
+       int sysvar;
+{
+       char *p;
+       register int c;
+       register int n;
+
+       /*
+        * New-style lesskey file.
+        * Extract the pieces.
+        */
+       if (buf[len-3] != C0_END_LESSKEY_MAGIC ||
+           buf[len-2] != C1_END_LESSKEY_MAGIC ||
+           buf[len-1] != C2_END_LESSKEY_MAGIC)
+               return (-1);
+       p = buf + 4;
+       for (;;)
+       {
+               c = *p++;
+               switch (c)
+               {
+               case CMD_SECTION:
+                       n = gint(&p);
+                       add_fcmd_table(p, n);
+                       p += n;
+                       break;
+               case EDIT_SECTION:
+                       n = gint(&p);
+                       add_ecmd_table(p, n);
+                       p += n;
+                       break;
+               case VAR_SECTION:
+                       n = gint(&p);
+                       add_var_table((sysvar) ? 
+                               &list_sysvar_tables : &list_var_tables, p, n);
+                       p += n;
+                       break;
+               case END_SECTION:
+                       return (0);
+               default:
+                       /*
+                        * Unrecognized section type.
+                        */
+                       return (-1);
+               }
+       }
+}
+
+/*
+ * Set up a user command table, based on a "lesskey" file.
+ */
+       public int
+lesskey(filename, sysvar)
+       char *filename;
+       int sysvar;
+{
+       register char *buf;
+       register POSITION len;
+       register long n;
+       register int f;
+
+       if (secure)
+               return (1);
+       /*
+        * Try to open the lesskey file.
+        */
+       filename = shell_unquote(filename);
+       f = open(filename, OPEN_READ);
+       free(filename);
+       if (f < 0)
+               return (1);
+
+       /*
+        * Read the file into a buffer.
+        * We first figure out the size of the file and allocate space for it.
+        * {{ Minimal error checking is done here.
+        *    A garbage .less file will produce strange results.
+        *    To avoid a large amount of error checking code here, we
+        *    rely on the lesskey program to generate a good .less file. }}
+        */
+       len = filesize(f);
+       if (len == NULL_POSITION || len < 3)
+       {
+               /*
+                * Bad file (valid file must have at least 3 chars).
+                */
+               close(f);
+               return (-1);
+       }
+       if ((buf = (char *) calloc((int)len, sizeof(char))) == NULL)
+       {
+               close(f);
+               return (-1);
+       }
+       if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK)
+       {
+               free(buf);
+               close(f);
+               return (-1);
+       }
+       n = read(f, buf, (unsigned int) len);
+       close(f);
+       if (n != len)
+       {
+               free(buf);
+               return (-1);
+       }
+
+       /*
+        * Figure out if this is an old-style (before version 241)
+        * or new-style lesskey file format.
+        */
+       if (buf[0] != C0_LESSKEY_MAGIC || buf[1] != C1_LESSKEY_MAGIC ||
+           buf[2] != C2_LESSKEY_MAGIC || buf[3] != C3_LESSKEY_MAGIC)
+               return (old_lesskey(buf, (int)len));
+       return (new_lesskey(buf, (int)len, sysvar));
+}
+
+/*
+ * Add the standard lesskey file "$HOME/.less"
+ */
+       public void
+add_hometable(envname, def_filename, sysvar)
+       char *envname;
+       char *def_filename;
+       int sysvar;
+{
+       char *filename;
+       PARG parg;
+
+       if (envname != NULL && (filename = lgetenv(envname)) != NULL)
+               filename = save(filename);
+       else if (sysvar)
+               filename = save(def_filename);
+       else
+               filename = homefile(def_filename);
+       if (filename == NULL)
+               return;
+       if (lesskey(filename, sysvar) < 0)
+       {
+               parg.p_string = filename;
+               error("Cannot use lesskey file \"%s\"", &parg);
+       }
+       free(filename);
+}
+#endif
+
+/*
+ * See if a char is a special line-editing command.
+ */
+       public int
+editchar(c, flags)
+       int c;
+       int flags;
+{
+       int action;
+       int nch;
+       char *s;
+       char usercmd[MAX_CMDLEN+1];
+       
+       /*
+        * An editing character could actually be a sequence of characters;
+        * for example, an escape sequence sent by pressing the uparrow key.
+        * To match the editing string, we use the command decoder
+        * but give it the edit-commands command table
+        * This table is constructed to match the user's keyboard.
+        */
+       if (c == erase_char || c == erase2_char)
+               return (EC_BACKSPACE);
+       if (c == kill_char)
+               return (EC_LINEKILL);
+               
+       /*
+        * Collect characters in a buffer.
+        * Start with the one we have, and get more if we need them.
+        */
+       nch = 0;
+       do {
+               if (nch > 0)
+                       c = getcc();
+               usercmd[nch] = c;
+               usercmd[nch+1] = '\0';
+               nch++;
+               action = ecmd_decode(usercmd, &s);
+       } while (action == A_PREFIX);
+       
+       if (flags & EC_NORIGHTLEFT)
+       {
+               switch (action)
+               {
+               case EC_RIGHT:
+               case EC_LEFT:
+                       action = A_INVALID;
+                       break;
+               }
+       }
+#if CMD_HISTORY
+       if (flags & EC_NOHISTORY) 
+       {
+               /*
+                * The caller says there is no history list.
+                * Reject any history-manipulation action.
+                */
+               switch (action)
+               {
+               case EC_UP:
+               case EC_DOWN:
+                       action = A_INVALID;
+                       break;
+               }
+       }
+#endif
+#if TAB_COMPLETE_FILENAME
+       if (flags & EC_NOCOMPLETE) 
+       {
+               /*
+                * The caller says we don't want any filename completion cmds.
+                * Reject them.
+                */
+               switch (action)
+               {
+               case EC_F_COMPLETE:
+               case EC_B_COMPLETE:
+               case EC_EXPAND:
+                       action = A_INVALID;
+                       break;
+               }
+       }
+#endif
+       if ((flags & EC_PEEK) || action == A_INVALID)
+       {
+               /*
+                * We're just peeking, or we didn't understand the command.
+                * Unget all the characters we read in the loop above.
+                * This does NOT include the original character that was 
+                * passed in as a parameter.
+                */
+               while (nch > 1) 
+               {
+                       ungetcc(usercmd[--nch]);
+               }
+       } else
+       {
+               if (s != NULL)
+                       ungetsc(s);
+       }
+       return action;
+}
+
diff --git a/thirdparty/less/defines.h.in b/thirdparty/less/defines.h.in
new file mode 100644 (file)
index 0000000..8d45063
--- /dev/null
@@ -0,0 +1,426 @@
+/* defines.h.in.  Generated from configure.ac by autoheader.  */
+
+
+/* Unix definition file for less.  -*- C -*-
+ *
+ * This file has 3 sections:
+ * User preferences.
+ * Settings always true on Unix.
+ * Settings automatically determined by configure.
+ *
+ * * * * * *  WARNING  * * * * * *
+ * If you edit defines.h by hand, do "touch stamp-h" before you run make
+ * so config.status doesn't overwrite your changes.
+ */
+
+/* User preferences.  */
+
+/*
+ * SECURE is 1 if you wish to disable a bunch of features in order to
+ * be safe to run by unprivileged users.
+ * SECURE_COMPILE is set by the --with-secure configure option.
+ */
+#define        SECURE          SECURE_COMPILE
+
+/*
+ * SHELL_ESCAPE is 1 if you wish to allow shell escapes.
+ * (This is possible only if your system supplies the system() function.)
+ */
+#define        SHELL_ESCAPE    (!SECURE)
+
+/*
+ * EXAMINE is 1 if you wish to allow examining files by name from within less.
+ */
+#define        EXAMINE         (!SECURE)
+
+/*
+ * TAB_COMPLETE_FILENAME is 1 if you wish to allow the TAB key
+ * to complete filenames at prompts.
+ */
+#define        TAB_COMPLETE_FILENAME   (!SECURE)
+
+/*
+ * CMD_HISTORY is 1 if you wish to allow keys to cycle through
+ * previous commands at prompts.
+ */
+#define        CMD_HISTORY     1
+
+/*
+ * HILITE_SEARCH is 1 if you wish to have search targets to be 
+ * displayed in standout mode.
+ */
+#define        HILITE_SEARCH   1
+
+/*
+ * EDITOR is 1 if you wish to allow editor invocation (the "v" command).
+ * (This is possible only if your system supplies the system() function.)
+ * EDIT_PGM is the name of the (default) editor to be invoked.
+ */
+#define        EDITOR          (!SECURE)
+
+/*
+ * TAGS is 1 if you wish to support tag files.
+ */
+#define        TAGS            (!SECURE)
+
+/*
+ * USERFILE is 1 if you wish to allow a .less file to specify 
+ * user-defined key bindings.
+ */
+#define        USERFILE        (!SECURE)
+
+/*
+ * GLOB is 1 if you wish to have shell metacharacters expanded in filenames.
+ * This will generally work if your system provides the "popen" function
+ * and the "echo" shell command.
+ */
+#define        GLOB            (!SECURE)
+
+/*
+ * PIPEC is 1 if you wish to have the "|" command
+ * which allows the user to pipe data into a shell command.
+ */
+#define        PIPEC           (!SECURE)
+
+/*
+ * LOGFILE is 1 if you wish to allow the -l option (to create log files).
+ */
+#define        LOGFILE         (!SECURE)
+
+/*
+ * GNU_OPTIONS is 1 if you wish to support the GNU-style command
+ * line options --help and --version.
+ */
+#define        GNU_OPTIONS     1
+
+/*
+ * ONLY_RETURN is 1 if you want RETURN to be the only input which
+ * will continue past an error message.
+ * Otherwise, any key will continue past an error message.
+ */
+#define        ONLY_RETURN     0
+
+/*
+ * LESSKEYFILE is the filename of the default lesskey output file 
+ * (in the HOME directory).
+ * LESSKEYFILE_SYS is the filename of the system-wide lesskey output file.
+ * DEF_LESSKEYINFILE is the filename of the default lesskey input 
+ * (in the HOME directory).
+ * LESSHISTFILE is the filename of the history file
+ * (in the HOME directory).
+ */
+#define        LESSKEYFILE             ".less"
+#define        LESSKEYFILE_SYS         SYSDIR "/sysless"
+#define        DEF_LESSKEYINFILE       ".lesskey"
+#define LESSHISTFILE           ".lesshst"
+
+
+/* Settings always true on Unix.  */
+
+/*
+ * Define MSDOS_COMPILER if compiling under Microsoft C.
+ */
+#define        MSDOS_COMPILER  0
+
+/*
+ * Pathname separator character.
+ */
+#define        PATHNAME_SEP    "/"
+
+/*
+ * The value returned from tgetent on success.
+ * Some HP-UX systems return 0 on success.
+ */
+#define TGETENT_OK  1
+
+/*
+ * HAVE_SYS_TYPES_H is 1 if your system has <sys/types.h>.
+ */
+#define HAVE_SYS_TYPES_H       1
+
+/*
+ * Define if you have the <sgstat.h> header file.
+ */
+#undef HAVE_SGSTAT_H
+
+/*
+ * HAVE_PERROR is 1 if your system has the perror() call.
+ * (Actually, if it has sys_errlist, sys_nerr and errno.)
+ */
+#define        HAVE_PERROR     1
+
+/*
+ * HAVE_TIME is 1 if your system has the time() call.
+ */
+#define        HAVE_TIME       1
+
+/*
+ * HAVE_SHELL is 1 if your system supports a SHELL command interpreter.
+ */
+#define        HAVE_SHELL      1
+
+/*
+ * Default shell metacharacters and meta-escape character.
+ */
+#define        DEF_METACHARS   "; *?\t\n'\"()<>[]|&^`#\\$%=~"
+#define        DEF_METAESCAPE  "\\"
+
+/* 
+ * HAVE_DUP is 1 if your system has the dup() call.
+ */
+#define        HAVE_DUP        1
+
+/* Define to 1 if you have the memcpy() function. */
+#define HAVE_MEMCPY 1
+
+/* Define to 1 if you have the strchr() function. */
+#define HAVE_STRCHR 1
+
+/* Define to 1 if you have the strstr() function. */
+#define HAVE_STRSTR 1
+
+/*
+ * Sizes of various buffers.
+ */
+#define        CMDBUF_SIZE     512     /* Buffer for multichar commands */
+#define        UNGOT_SIZE      100     /* Max chars to unget() */
+#define        LINEBUF_SIZE    1024    /* Max size of line in input file */
+#define        OUTBUF_SIZE     1024    /* Output buffer */
+#define        PROMPT_SIZE     200     /* Max size of prompt string */
+#define        TERMBUF_SIZE    2048    /* Termcap buffer for tgetent */
+#define        TERMSBUF_SIZE   1024    /* Buffer to hold termcap strings */
+#define        TAGLINE_SIZE    512     /* Max size of line in tags file */
+#define        TABSTOP_MAX     32      /* Max number of custom tab stops */
+
+/* Settings automatically determined by configure.  */
+
+
+/* Define EDIT_PGM to your editor. */
+#undef EDIT_PGM
+
+/* Define HAVE_CONST if your compiler supports the "const" modifier. */
+#undef HAVE_CONST
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#undef HAVE_CTYPE_H
+
+/* Define HAVE_ERRNO if you have the errno variable. */
+#undef HAVE_ERRNO
+
+/* Define to 1 if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define to 1 if you have the `fchmod' function. */
+#undef HAVE_FCHMOD
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define HAVE_FILENO if you have the fileno() macro. */
+#undef HAVE_FILENO
+
+/* Define HAVE_FLOAT if your compiler supports the "double" type. */
+#undef HAVE_FLOAT
+
+/* Define to 1 if you have the `fsync' function. */
+#undef HAVE_FSYNC
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the `gen' library (-lgen). */
+#undef HAVE_LIBGEN
+
+/* Define to 1 if you have the `intl' library (-lintl). */
+#undef HAVE_LIBINTL
+
+/* Define to 1 if you have the `PW' library (-lPW). */
+#undef HAVE_LIBPW
+
+/* Define to 1 if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+
+/* Define HAVE_LOCALE if you have locale.h and setlocale. */
+#undef HAVE_LOCALE
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define HAVE_OSPEED if your termcap library has the ospeed variable. */
+#undef HAVE_OSPEED
+
+/* PCRE (Perl-compatible regular expression) library */
+#undef HAVE_PCRE
+
+/* Define to 1 if you have the `popen' function. */
+#undef HAVE_POPEN
+
+/* POSIX regcomp() and regex.h */
+#undef HAVE_POSIX_REGCOMP
+
+/* System V regcmp() */
+#undef HAVE_REGCMP
+
+/* */
+#undef HAVE_REGEXEC2
+
+/* BSD re_comp() */
+#undef HAVE_RE_COMP
+
+/* Define HAVE_SIGEMPTYSET if you have the sigemptyset macro. */
+#undef HAVE_SIGEMPTYSET
+
+/* Define to 1 if you have the `sigprocmask' function. */
+#undef HAVE_SIGPROCMASK
+
+/* Define to 1 if you have the `sigsetmask' function. */
+#undef HAVE_SIGSETMASK
+
+/* Define to 1 if the system has the type `sigset_t'. */
+#undef HAVE_SIGSET_T
+
+/* Define to 1 if you have the `snprintf' function. */
+#undef HAVE_SNPRINTF
+
+/* Define to 1 if you have the `stat' function. */
+#undef HAVE_STAT
+
+/* Define HAVE_STAT_INO if your struct stat has st_ino and st_dev. */
+#undef HAVE_STAT_INO
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define HAVE_STRERROR if you have the strerror() function. */
+#undef HAVE_STRERROR
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `system' function. */
+#undef HAVE_SYSTEM
+
+/* Define HAVE_SYS_ERRLIST if you have the sys_errlist[] variable. */
+#undef HAVE_SYS_ERRLIST
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/stream.h> header file. */
+#undef HAVE_SYS_STREAM_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <termcap.h> header file. */
+#undef HAVE_TERMCAP_H
+
+/* Define HAVE_TERMIOS_FUNCS if you have tcgetattr/tcsetattr. */
+#undef HAVE_TERMIOS_FUNCS
+
+/* Define to 1 if you have the <termios.h> header file. */
+#undef HAVE_TERMIOS_H
+
+/* Define to 1 if you have the <termio.h> header file. */
+#undef HAVE_TERMIO_H
+
+/* Define to 1 if you have the <time.h> header file. */
+#undef HAVE_TIME_H
+
+/* Define HAVE_TIME_T if your system supports the "time_t" type. */
+#undef HAVE_TIME_T
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define HAVE_UPPER_LOWER if you have isupper, islower, toupper, tolower. */
+#undef HAVE_UPPER_LOWER
+
+/* Henry Spencer V8 regcomp() and regexp.h */
+#undef HAVE_V8_REGCOMP
+
+/* Define to 1 if you have the <values.h> header file. */
+#undef HAVE_VALUES_H
+
+/* Define HAVE_VOID if your compiler supports the "void" type. */
+#undef HAVE_VOID
+
+/* Define HAVE_WCTYPE if you have iswupper, iswlower, towupper, towlower. */
+#undef HAVE_WCTYPE
+
+/* Define to 1 if you have the <wctype.h> header file. */
+#undef HAVE_WCTYPE_H
+
+/* Define to 1 if you have the `_setjmp' function. */
+#undef HAVE__SETJMP
+
+/* Define MUST_DEFINE_ERRNO if you have errno but it is not define in errno.h.
+   */
+#undef MUST_DEFINE_ERRNO
+
+/* Define MUST_DEFINE_OSPEED if you have ospeed but it is not defined in
+   termcap.h. */
+#undef MUST_DEFINE_OSPEED
+
+/* pattern matching is supported, but without metacharacters. */
+#undef NO_REGEX
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#undef RETSIGTYPE
+
+/* Define SECURE_COMPILE=1 to build a secure version of less. */
+#undef SECURE_COMPILE
+
+/* Define to 1 if the `S_IS*' macros in <sys/stat.h> do not work properly. */
+#undef STAT_MACROS_BROKEN
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#undef TIME_WITH_SYS_TIME
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#undef _FILE_OFFSET_BITS
+
+/* Define for large files, on AIX-style hosts. */
+#undef _LARGE_FILES
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to `long int' if <sys/types.h> does not define. */
+#undef off_t
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
diff --git a/thirdparty/less/defines.wn b/thirdparty/less/defines.wn
new file mode 100644 (file)
index 0000000..d0c1927
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/* Windows definition file for less.  */
+/*
+ * This file has 2 sections:
+ * User preferences.
+ * Settings always true for Windows systems. 
+ */
+
+\f
+/* User preferences.  */
+
+/*
+ * SECURE is 1 if you wish to disable a bunch of features in order to
+ * be safe to run by unprivileged users.
+ */
+#define        SECURE          0
+
+/*
+ * SHELL_ESCAPE is 1 if you wish to allow shell escapes.
+ * (This is possible only if your system supplies the system() function.)
+ */
+#define        SHELL_ESCAPE    (!SECURE)
+
+/*
+ * EXAMINE is 1 if you wish to allow examining files by name from within less.
+ */
+#define        EXAMINE         (!SECURE)
+
+/*
+ * TAB_COMPLETE_FILENAME is 1 if you wish to allow the TAB key
+ * to complete filenames at prompts.
+ */
+#define        TAB_COMPLETE_FILENAME   (!SECURE)
+
+/*
+ * CMD_HISTORY is 1 if you wish to allow keys to cycle through
+ * previous commands at prompts.
+ */
+#define        CMD_HISTORY     1
+
+/*
+ * HILITE_SEARCH is 1 if you wish to have search targets to be 
+ * displayed in standout mode.
+ */
+#define        HILITE_SEARCH   1
+
+/*
+ * EDITOR is 1 if you wish to allow editor invocation (the "v" command).
+ * (This is possible only if your system supplies the system() function.)
+ * EDIT_PGM is the name of the (default) editor to be invoked.
+ */
+#define        EDITOR          (!SECURE)
+#define        EDIT_PGM        "edit"
+
+/*
+ * TAGS is 1 if you wish to support tag files.
+ */
+#define        TAGS            (!SECURE)
+
+/*
+ * USERFILE is 1 if you wish to allow a .less file to specify 
+ * user-defined key bindings.
+ */
+#define        USERFILE        (!SECURE)
+
+/*
+ * GLOB is 1 if you wish to have shell metacharacters expanded in filenames.
+ * This will generally work if your system provides the "popen" function
+ * and the "echo" shell command.
+ */
+#define        GLOB            0
+
+/*
+ * PIPEC is 1 if you wish to have the "|" command
+ * which allows the user to pipe data into a shell command.
+ */
+#define        PIPEC           1
+
+/*
+ * LOGFILE is 1 if you wish to allow the -l option (to create log files).
+ */
+#define        LOGFILE         (!SECURE)
+
+/*
+ * GNU_OPTIONS is 1 if you wish to support the GNU-style command
+ * line options --help and --version.
+ */
+#define        GNU_OPTIONS     1
+
+/*
+ * ONLY_RETURN is 1 if you want RETURN to be the only input which
+ * will continue past an error message.
+ * Otherwise, any key will continue past an error message.
+ */
+#define        ONLY_RETURN     0
+
+/*
+ * LESSKEYFILE is the filename of the default lesskey output file 
+ * (in the HOME directory).
+ * LESSKEYFILE_SYS is the filename of the system-wide lesskey output file.
+ * DEF_LESSKEYINFILE is the filename of the default lesskey input 
+ * (in the HOME directory).
+ * LESSHISTFILE is the filename of the history file
+ * (in the HOME directory).
+ */
+#define        LESSKEYFILE             "_less"
+#define        LESSKEYFILE_SYS         "c:\\_sysless"
+#define        DEF_LESSKEYINFILE       "_lesskey"
+#define LESSHISTFILE           "_lesshst"
+
+\f
+/* Settings always true for Windows systems.  */
+
+#define MSDOS_COMPILER WIN32C
+
+/*
+ * Pathname separator character.
+ */
+#define        PATHNAME_SEP    "\\"
+
+/*
+ * HAVE_SYS_TYPES_H is 1 if your system has <sys/types.h>.
+ */
+#define HAVE_SYS_TYPES_H       1
+
+/*
+ * Define if you have the <sgstat.h> header file.
+ */
+#define HAVE_SGSTAT_H  0
+
+/*
+ * HAVE_PERROR is 1 if your system has the perror() call.
+ * (Actually, if it has sys_errlist, sys_nerr and errno.)
+ */
+#define        HAVE_PERROR     1
+
+/*
+ * HAVE_TIME is 1 if your system has the time() call.
+ */
+#define        HAVE_TIME       1
+
+/*
+ * HAVE_SHELL is 1 if your system supports a SHELL command interpreter.
+ */
+#define        HAVE_SHELL      0
+
+/*
+ * Default shell metacharacters and meta-escape character.
+ */
+#define        DEF_METACHARS   "; *?\t\n'\"()<>|&"
+#define        DEF_METAESCAPE  ""
+
+/* 
+ * HAVE_DUP is 1 if your system has the dup() call.
+ */
+#define        HAVE_DUP        1
+
+/*
+ * Sizes of various buffers.
+ */
+#define        CMDBUF_SIZE     512     /* Buffer for multichar commands */
+#define        UNGOT_SIZE      100     /* Max chars to unget() */
+#define        LINEBUF_SIZE    1024    /* Max size of line in input file */
+#define        OUTBUF_SIZE     1024    /* Output buffer */
+#define        PROMPT_SIZE     200     /* Max size of prompt string */
+#define        TERMBUF_SIZE    2048    /* Termcap buffer for tgetent */
+#define        TERMSBUF_SIZE   1024    /* Buffer to hold termcap strings */
+#define        TAGLINE_SIZE    512     /* Max size of line in tags file */
+#define        TABSTOP_MAX     32      /* Max number of custom tab stops */
+
+/* Define to `long' if <sys/types.h> doesn't define.  */
+/* #define     off_t   long */
+
+/* Define if you need to in order for stat and other things to work.  */
+/* #undef _POSIX_SOURCE */
+
+/* Define as the return type of signal handlers (int or void).  */
+#define RETSIGTYPE void
+
+
+/*
+ * Regular expression library.
+ * Define exactly one of the following to be 1:
+ * HAVE_POSIX_REGCOMP: POSIX regcomp() and regex.h
+ * HAVE_RE_COMP: BSD re_comp()
+ * HAVE_REGCMP: System V regcmp()
+ * HAVE_V8_REGCOMP: Henry Spencer V8 regcomp() and regexp.h
+ * NO_REGEX: pattern matching is supported, but without metacharacters.
+ */
+/* #undef HAVE_POSIX_REGCOMP */
+/* #undef HAVE_RE_COMP */
+/* #undef HAVE_REGCMP */
+#define HAVE_V8_REGCOMP 1
+/* #undef NO_REGEX */
+#define HAVE_REGEXEC2 1
+
+/* Define HAVE_VOID if your compiler supports the "void" type. */
+#define HAVE_VOID 1
+
+/* Define HAVE_CONST if your compiler supports the "const" modifier. */
+#define HAVE_CONST 1
+
+/* Define HAVE_TIME_T if your system supports the "time_t" type. */
+#define HAVE_TIME_T 1
+
+/* Define HAVE_STRERROR if you have the strerror() function. */
+#define HAVE_STRERROR 1
+
+/* Define HAVE_FILENO if you have the fileno() macro. */
+#define HAVE_FILENO 1
+
+/* Define HAVE_ERRNO if you have the errno variable */
+/* Define MUST_DEFINE_ERRNO if you have errno but it is not define 
+ * in errno.h */
+#define HAVE_ERRNO 1
+#if defined(__MINGW32__) 
+#define MUST_DEFINE_ERRNO 0
+#else
+#define MUST_DEFINE_ERRNO 1
+#endif
+
+/* Define HAVE_SYS_ERRLIST if you have the sys_errlist[] variable */
+#define HAVE_SYS_ERRLIST 1
+
+/* Define HAVE_OSPEED if your termcap library has the ospeed variable */
+#define HAVE_OSPEED 0
+/* Define MUST_DEFINE_OSPEED if you have ospeed but it is not defined
+ * in termcap.h. */
+#define MUST_DEFINE_OSPEED 0
+
+/* Define HAVE_LOCALE if you have locale.h and setlocale. */
+#define HAVE_LOCALE 0
+
+/* Define HAVE_TERMIOS_FUNCS if you have tcgetattr/tcsetattr */
+#define HAVE_TERMIOS_FUNCS 0
+
+/* Define HAVE_UPPER_LOWER if you have isupper, islower, toupper, tolower */
+#define HAVE_UPPER_LOWER 1
+
+/* Define if you have the _setjmp function.  */
+#define HAVE__SETJMP   1
+
+/* Define if you have the memcpy function.  */
+#define HAVE_MEMCPY 1
+
+/* Define if you have the popen function.  */
+#define HAVE_POPEN 1
+
+/* Define if you have the sigsetmask function.  */
+#define HAVE_SIGSETMASK        0
+
+/* Define if you have the sigprocmask function.  */
+#define HAVE_SIGPROCMASK       0
+
+/* Define if you have the sigset_t type and sigemptyset macro */
+#define HAVE_SIGSET_T  0
+#define HAVE_SIGEMPTYSET       0
+
+/* Define if you have the stat function.  */
+#define HAVE_STAT 1
+
+/* Define if you have the strchr function.  */
+#define HAVE_STRCHR 1
+
+/* Define if you have the system function.  */
+#define HAVE_SYSTEM    1
+
+/* Define if you have the snprintf function.  */
+#define HAVE_SNPRINTF  1
+
+/* Define if you have the <ctype.h> header file.  */
+#define HAVE_CTYPE_H 1
+
+/* Define if you have the <wctype.h> header file.  */
+#define HAVE_WCTYPE_H 1
+
+/* Define if you have the <errno.h> header file.  */
+#define HAVE_ERRNO_H 1
+
+/* Define if you have the <fcntl.h> header file.  */
+#define HAVE_FCNTL_H 1
+
+/* Define HAVE_FLOAT if your compiler supports the "double" type. */
+#define HAVE_FLOAT 1
+
+/* Define if you have the <limits.h> header file.  */
+#define HAVE_LIMITS_H 1
+
+/* Define if you have the <stdio.h> header file.  */
+#define HAVE_STDIO_H 1
+
+/* Define if you have the <string.h> header file.  */
+#define HAVE_STRING_H 1
+
+/* Define if you have the <stdlib> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define if you have the <sys/ioctl.h> header file.  */
+#define HAVE_SYS_IOCTL_H 0
+
+/* Define if you have the <sys/ptem.h> header file.  */
+#define HAVE_SYS_PTEM_H        0
+
+/* Define if you have the <sys/stream.h> header file.  */
+#define HAVE_SYS_STREAM_H      0
+
+/* Define if you have the <termcap.h> header file.  */
+#define HAVE_TERMCAP_H 0
+
+/* Define if you have the <termio.h> header file.  */
+#define HAVE_TERMIO_H  0
+
+/* Define if you have the <termios.h> header file.  */
+#define HAVE_TERMIOS_H 0
+
+/* Define if you have the <time.h> header file.  */
+#define HAVE_TIME_H 1
+
+/* Define if you have the <unistd.h> header file.  */
+#define HAVE_UNISTD_H 0
+
+/* Define if you have the <values.h> header file.  */
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#define HAVE_VALUES_H 0
+#else
+#define HAVE_VALUES_H 1
+#endif
+
+#if !defined(__MINGW32__)
+#define        popen   _popen
+#define        pclose  _pclose
+#define snprintf       _snprintf
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning(disable:4996)
+#endif
diff --git a/thirdparty/less/edit.c b/thirdparty/less/edit.c
new file mode 100644 (file)
index 0000000..4781d95
--- /dev/null
@@ -0,0 +1,818 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+#include "less.h"
+#if HAVE_STAT
+#include <sys/stat.h>
+#endif
+
+public int fd0 = 0;
+
+extern int new_file;
+extern int errmsgs;
+extern int cbufs;
+extern char *every_first_cmd;
+extern int any_display;
+extern int force_open;
+extern int is_tty;
+extern int sigs;
+extern IFILE curr_ifile;
+extern IFILE old_ifile;
+extern struct scrpos initial_scrpos;
+extern void constant *ml_examine;
+#if SPACES_IN_FILENAMES
+extern char openquote;
+extern char closequote;
+#endif
+
+#if LOGFILE
+extern int logfile;
+extern int force_logfile;
+extern char *namelogfile;
+#endif
+
+#if HAVE_STAT_INO
+public dev_t curr_dev;
+public ino_t curr_ino;
+#endif
+
+char *curr_altfilename = NULL;
+static void *curr_altpipe;
+
+
+/*
+ * Textlist functions deal with a list of words separated by spaces.
+ * init_textlist sets up a textlist structure.
+ * forw_textlist uses that structure to iterate thru the list of
+ * words, returning each one as a standard null-terminated string.
+ * back_textlist does the same, but runs thru the list backwards.
+ */
+       public void
+init_textlist(tlist, str)
+       struct textlist *tlist;
+       char *str;
+{
+       char *s;
+#if SPACES_IN_FILENAMES
+       int meta_quoted = 0;
+       int delim_quoted = 0;
+       char *esc = get_meta_escape();
+       int esclen = strlen(esc);
+#endif
+       
+       tlist->string = skipsp(str);
+       tlist->endstring = tlist->string + strlen(tlist->string);
+       for (s = str;  s < tlist->endstring;  s++)
+       {
+#if SPACES_IN_FILENAMES
+               if (meta_quoted)
+               {
+                       meta_quoted = 0;
+               } else if (esclen > 0 && s + esclen < tlist->endstring &&
+                          strncmp(s, esc, esclen) == 0)
+               {
+                       meta_quoted = 1;
+                       s += esclen - 1;
+               } else if (delim_quoted)
+               {
+                       if (*s == closequote)
+                               delim_quoted = 0;
+               } else /* (!delim_quoted) */
+               {
+                       if (*s == openquote)
+                               delim_quoted = 1;
+                       else if (*s == ' ')
+                               *s = '\0';
+               }
+#else
+               if (*s == ' ')
+                       *s = '\0';
+#endif
+       }
+}
+
+       public char *
+forw_textlist(tlist, prev)
+       struct textlist *tlist;
+       char *prev;
+{
+       char *s;
+       
+       /*
+        * prev == NULL means return the first word in the list.
+        * Otherwise, return the word after "prev".
+        */
+       if (prev == NULL)
+               s = tlist->string;
+       else
+               s = prev + strlen(prev);
+       if (s >= tlist->endstring)
+               return (NULL);
+       while (*s == '\0')
+               s++;
+       if (s >= tlist->endstring)
+               return (NULL);
+       return (s);
+}
+
+       public char *
+back_textlist(tlist, prev)
+       struct textlist *tlist;
+       char *prev;
+{
+       char *s;
+       
+       /*
+        * prev == NULL means return the last word in the list.
+        * Otherwise, return the word before "prev".
+        */
+       if (prev == NULL)
+               s = tlist->endstring;
+       else if (prev <= tlist->string)
+               return (NULL);
+       else
+               s = prev - 1;
+       while (*s == '\0')
+               s--;
+       if (s <= tlist->string)
+               return (NULL);
+       while (s[-1] != '\0' && s > tlist->string)
+               s--;
+       return (s);
+}
+
+/*
+ * Close the current input file.
+ */
+       static void
+close_file()
+{
+       struct scrpos scrpos;
+       
+       if (curr_ifile == NULL_IFILE)
+               return;
+
+       /*
+        * Save the current position so that we can return to
+        * the same position if we edit this file again.
+        */
+       get_scrpos(&scrpos);
+       if (scrpos.pos != NULL_POSITION)
+       {
+               store_pos(curr_ifile, &scrpos);
+               lastmark();
+       }
+       /*
+        * Close the file descriptor, unless it is a pipe.
+        */
+       ch_close();
+       /*
+        * If we opened a file using an alternate name,
+        * do special stuff to close it.
+        */
+       if (curr_altfilename != NULL)
+       {
+               close_altfile(curr_altfilename, get_filename(curr_ifile),
+                               curr_altpipe);
+               free(curr_altfilename);
+               curr_altfilename = NULL;
+       }
+       curr_ifile = NULL_IFILE;
+#if HAVE_STAT_INO
+       curr_ino = curr_dev = 0;
+#endif
+}
+
+/*
+ * Edit a new file (given its name).
+ * Filename == "-" means standard input.
+ * Filename == NULL means just close the current file.
+ */
+       public int
+edit(filename)
+       char *filename;
+{
+       if (filename == NULL)
+               return (edit_ifile(NULL_IFILE));
+       return (edit_ifile(get_ifile(filename, curr_ifile)));
+}
+       
+/*
+ * Edit a new file (given its IFILE).
+ * ifile == NULL means just close the current file.
+ */
+       public int
+edit_ifile(ifile)
+       IFILE ifile;
+{
+       int f;
+       int answer;
+       int no_display;
+       int chflags;
+       char *filename;
+       char *open_filename;
+       char *qopen_filename;
+       char *alt_filename;
+       void *alt_pipe;
+       IFILE was_curr_ifile;
+       PARG parg;
+               
+       if (ifile == curr_ifile)
+       {
+               /*
+                * Already have the correct file open.
+                */
+               return (0);
+       }
+
+       /*
+        * We must close the currently open file now.
+        * This is necessary to make the open_altfile/close_altfile pairs
+        * nest properly (or rather to avoid nesting at all).
+        * {{ Some stupid implementations of popen() mess up if you do:
+        *    fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }}
+        */
+#if LOGFILE
+       end_logfile();
+#endif
+       was_curr_ifile = save_curr_ifile();
+       if (curr_ifile != NULL_IFILE)
+       {
+               chflags = ch_getflags();
+               close_file();
+               if ((chflags & CH_HELPFILE) && held_ifile(was_curr_ifile) <= 1)
+               {
+                       /*
+                        * Don't keep the help file in the ifile list.
+                        */
+                       del_ifile(was_curr_ifile);
+                       was_curr_ifile = old_ifile;
+               }
+       }
+
+       if (ifile == NULL_IFILE)
+       {
+               /*
+                * No new file to open.
+                * (Don't set old_ifile, because if you call edit_ifile(NULL),
+                *  you're supposed to have saved curr_ifile yourself,
+                *  and you'll restore it if necessary.)
+                */
+               unsave_ifile(was_curr_ifile);
+               return (0);
+       }
+
+       filename = save(get_filename(ifile));
+       /*
+        * See if LESSOPEN specifies an "alternate" file to open.
+        */
+       alt_pipe = NULL;
+       alt_filename = open_altfile(filename, &f, &alt_pipe);
+       open_filename = (alt_filename != NULL) ? alt_filename : filename;
+       qopen_filename = shell_unquote(open_filename);
+
+       chflags = 0;
+       if (alt_pipe != NULL)
+       {
+               /*
+                * The alternate "file" is actually a pipe.
+                * f has already been set to the file descriptor of the pipe
+                * in the call to open_altfile above.
+                * Keep the file descriptor open because it was opened 
+                * via popen(), and pclose() wants to close it.
+                */
+               chflags |= CH_POPENED;
+       } else if (strcmp(open_filename, "-") == 0)
+       {
+               /* 
+                * Use standard input.
+                * Keep the file descriptor open because we can't reopen it.
+                */
+               f = fd0;
+               chflags |= CH_KEEPOPEN;
+               /*
+                * Must switch stdin to BINARY mode.
+                */
+               SET_BINARY(f);
+#if MSDOS_COMPILER==DJGPPC
+               /*
+                * Setting stdin to binary by default causes
+                * Ctrl-C to not raise SIGINT.  We must undo
+                * that side-effect.
+                */
+               __djgpp_set_ctrl_c(1);
+#endif
+       } else if (strcmp(open_filename, FAKE_HELPFILE) == 0)
+       {
+               f = -1;
+               chflags |= CH_HELPFILE;
+       } else if ((parg.p_string = bad_file(open_filename)) != NULL)
+       {
+               /*
+                * It looks like a bad file.  Don't try to open it.
+                */
+               error("%s", &parg);
+               free(parg.p_string);
+           err1:
+               if (alt_filename != NULL)
+               {
+                       close_altfile(alt_filename, filename, alt_pipe);
+                       free(alt_filename);
+               }
+               del_ifile(ifile);
+               free(qopen_filename);
+               free(filename);
+               /*
+                * Re-open the current file.
+                */
+               if (was_curr_ifile == ifile)
+               {
+                       /*
+                        * Whoops.  The "current" ifile is the one we just deleted.
+                        * Just give up.
+                        */
+                       quit(QUIT_ERROR);
+               }
+               reedit_ifile(was_curr_ifile);
+               return (1);
+       } else if ((f = open(qopen_filename, OPEN_READ)) < 0)
+       {
+               /*
+                * Got an error trying to open it.
+                */
+               parg.p_string = errno_message(filename);
+               error("%s", &parg);
+               free(parg.p_string);
+               goto err1;
+       } else 
+       {
+               chflags |= CH_CANSEEK;
+               if (!force_open && !opened(ifile) && bin_file(f))
+               {
+                       /*
+                        * Looks like a binary file.  
+                        * Ask user if we should proceed.
+                        */
+                       parg.p_string = filename;
+                       answer = query("\"%s\" may be a binary file.  See it anyway? ",
+                               &parg);
+                       if (answer != 'y' && answer != 'Y')
+                       {
+                               close(f);
+                               goto err1;
+                       }
+               }
+       }
+
+       /*
+        * Get the new ifile.
+        * Get the saved position for the file.
+        */
+       if (was_curr_ifile != NULL_IFILE)
+       {
+               old_ifile = was_curr_ifile;
+               unsave_ifile(was_curr_ifile);
+       }
+       curr_ifile = ifile;
+       curr_altfilename = alt_filename;
+       curr_altpipe = alt_pipe;
+       set_open(curr_ifile); /* File has been opened */
+       get_pos(curr_ifile, &initial_scrpos);
+       new_file = TRUE;
+       ch_init(f, chflags);
+
+       if (!(chflags & CH_HELPFILE))
+       {
+#if LOGFILE
+               if (namelogfile != NULL && is_tty)
+                       use_logfile(namelogfile);
+#endif
+#if HAVE_STAT_INO
+               /* Remember the i-number and device of the opened file. */
+               {
+                       struct stat statbuf;
+                       int r = stat(qopen_filename, &statbuf);
+                       if (r == 0)
+                       {
+                               curr_ino = statbuf.st_ino;
+                               curr_dev = statbuf.st_dev;
+                       }
+               }
+#endif
+               if (every_first_cmd != NULL)
+                       ungetsc(every_first_cmd);
+       }
+
+       free(qopen_filename);
+       no_display = !any_display;
+       flush();
+       any_display = TRUE;
+
+       if (is_tty)
+       {
+               /*
+                * Output is to a real tty.
+                */
+
+               /*
+                * Indicate there is nothing displayed yet.
+                */
+               pos_clear();
+               clr_linenum();
+#if HILITE_SEARCH
+               clr_hilite();
+#endif
+               cmd_addhist(ml_examine, filename);
+               if (no_display && errmsgs > 0)
+               {
+                       /*
+                        * We displayed some messages on error output
+                        * (file descriptor 2; see error() function).
+                        * Before erasing the screen contents,
+                        * display the file name and wait for a keystroke.
+                        */
+                       parg.p_string = filename;
+                       error("%s", &parg);
+               }
+       }
+       free(filename);
+       return (0);
+}
+
+/*
+ * Edit a space-separated list of files.
+ * For each filename in the list, enter it into the ifile list.
+ * Then edit the first one.
+ */
+       public int
+edit_list(filelist)
+       char *filelist;
+{
+       IFILE save_ifile;
+       char *good_filename;
+       char *filename;
+       char *gfilelist;
+       char *gfilename;
+       struct textlist tl_files;
+       struct textlist tl_gfiles;
+
+       save_ifile = save_curr_ifile();
+       good_filename = NULL;
+       
+       /*
+        * Run thru each filename in the list.
+        * Try to glob the filename.  
+        * If it doesn't expand, just try to open the filename.
+        * If it does expand, try to open each name in that list.
+        */
+       init_textlist(&tl_files, filelist);
+       filename = NULL;
+       while ((filename = forw_textlist(&tl_files, filename)) != NULL)
+       {
+               gfilelist = lglob(filename);
+               init_textlist(&tl_gfiles, gfilelist);
+               gfilename = NULL;
+               while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL)
+               {
+                       if (edit(gfilename) == 0 && good_filename == NULL)
+                               good_filename = get_filename(curr_ifile);
+               }
+               free(gfilelist);
+       }
+       /*
+        * Edit the first valid filename in the list.
+        */
+       if (good_filename == NULL)
+       {
+               unsave_ifile(save_ifile);
+               return (1);
+       }
+       if (get_ifile(good_filename, curr_ifile) == curr_ifile)
+       {
+               /*
+                * Trying to edit the current file; don't reopen it.
+                */
+               unsave_ifile(save_ifile);
+               return (0);
+       }
+       reedit_ifile(save_ifile);
+       return (edit(good_filename));
+}
+
+/*
+ * Edit the first file in the command line (ifile) list.
+ */
+       public int
+edit_first()
+{
+       curr_ifile = NULL_IFILE;
+       return (edit_next(1));
+}
+
+/*
+ * Edit the last file in the command line (ifile) list.
+ */
+       public int
+edit_last()
+{
+       curr_ifile = NULL_IFILE;
+       return (edit_prev(1));
+}
+
+
+/*
+ * Edit the n-th next or previous file in the command line (ifile) list.
+ */
+       static int
+edit_istep(h, n, dir)
+       IFILE h;
+       int n;
+       int dir;
+{
+       IFILE next;
+
+       /*
+        * Skip n filenames, then try to edit each filename.
+        */
+       for (;;)
+       {
+               next = (dir > 0) ? next_ifile(h) : prev_ifile(h);
+               if (--n < 0)
+               {
+                       if (edit_ifile(h) == 0)
+                               break;
+               }
+               if (next == NULL_IFILE)
+               {
+                       /*
+                        * Reached end of the ifile list.
+                        */
+                       return (1);
+               }
+               if (ABORT_SIGS())
+               {
+                       /*
+                        * Interrupt breaks out, if we're in a long
+                        * list of files that can't be opened.
+                        */
+                       return (1);
+               }
+               h = next;
+       } 
+       /*
+        * Found a file that we can edit.
+        */
+       return (0);
+}
+
+       static int
+edit_inext(h, n)
+       IFILE h;
+       int n;
+{
+       return (edit_istep(h, n, +1));
+}
+
+       public int
+edit_next(n)
+       int n;
+{
+       return edit_istep(curr_ifile, n, +1);
+}
+
+       static int
+edit_iprev(h, n)
+       IFILE h;
+       int n;
+{
+       return (edit_istep(h, n, -1));
+}
+
+       public int
+edit_prev(n)
+       int n;
+{
+       return edit_istep(curr_ifile, n, -1);
+}
+
+/*
+ * Edit a specific file in the command line (ifile) list.
+ */
+       public int
+edit_index(n)
+       int n;
+{
+       IFILE h;
+
+       h = NULL_IFILE;
+       do
+       {
+               if ((h = next_ifile(h)) == NULL_IFILE)
+               {
+                       /*
+                        * Reached end of the list without finding it.
+                        */
+                       return (1);
+               }
+       } while (get_index(h) != n);
+
+       return (edit_ifile(h));
+}
+
+       public IFILE
+save_curr_ifile()
+{
+       if (curr_ifile != NULL_IFILE)
+               hold_ifile(curr_ifile, 1);
+       return (curr_ifile);
+}
+
+       public void
+unsave_ifile(save_ifile)
+       IFILE save_ifile;
+{
+       if (save_ifile != NULL_IFILE)
+               hold_ifile(save_ifile, -1);
+}
+
+/*
+ * Reedit the ifile which was previously open.
+ */
+       public void
+reedit_ifile(save_ifile)
+       IFILE save_ifile;
+{
+       IFILE next;
+       IFILE prev;
+
+       /*
+        * Try to reopen the ifile.
+        * Note that opening it may fail (maybe the file was removed),
+        * in which case the ifile will be deleted from the list.
+        * So save the next and prev ifiles first.
+        */
+       unsave_ifile(save_ifile);
+       next = next_ifile(save_ifile);
+       prev = prev_ifile(save_ifile);
+       if (edit_ifile(save_ifile) == 0)
+               return;
+       /*
+        * If can't reopen it, open the next input file in the list.
+        */
+       if (next != NULL_IFILE && edit_inext(next, 0) == 0)
+               return;
+       /*
+        * If can't open THAT one, open the previous input file in the list.
+        */
+       if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0)
+               return;
+       /*
+        * If can't even open that, we're stuck.  Just quit.
+        */
+       quit(QUIT_ERROR);
+}
+
+       public void
+reopen_curr_ifile()
+{
+       IFILE save_ifile = save_curr_ifile();
+       close_file();
+       reedit_ifile(save_ifile);
+}
+
+/*
+ * Edit standard input.
+ */
+       public int
+edit_stdin()
+{
+       if (isatty(fd0))
+       {
+               error("Missing filename (\"less --help\" for help)", NULL_PARG);
+               quit(QUIT_OK);
+       }
+       return (edit("-"));
+}
+
+/*
+ * Copy a file directly to standard output.
+ * Used if standard output is not a tty.
+ */
+       public void
+cat_file()
+{
+       register int c;
+
+       while ((c = ch_forw_get()) != EOI)
+               putchr(c);
+       flush();
+}
+
+#if LOGFILE
+
+/*
+ * If the user asked for a log file and our input file
+ * is standard input, create the log file.  
+ * We take care not to blindly overwrite an existing file.
+ */
+       public void
+use_logfile(filename)
+       char *filename;
+{
+       register int exists;
+       register int answer;
+       PARG parg;
+
+       if (ch_getflags() & CH_CANSEEK)
+               /*
+                * Can't currently use a log file on a file that can seek.
+                */
+               return;
+
+       /*
+        * {{ We could use access() here. }}
+        */
+       filename = shell_unquote(filename);
+       exists = open(filename, OPEN_READ);
+       close(exists);
+       exists = (exists >= 0);
+
+       /*
+        * Decide whether to overwrite the log file or append to it.
+        * If it doesn't exist we "overwrite" it.
+        */
+       if (!exists || force_logfile)
+       {
+               /*
+                * Overwrite (or create) the log file.
+                */
+               answer = 'O';
+       } else
+       {
+               /*
+                * Ask user what to do.
+                */
+               parg.p_string = filename;
+               answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg);
+       }
+
+loop:
+       switch (answer)
+       {
+       case 'O': case 'o':
+               /*
+                * Overwrite: create the file.
+                */
+               logfile = creat(filename, 0644);
+               break;
+       case 'A': case 'a':
+               /*
+                * Append: open the file and seek to the end.
+                */
+               logfile = open(filename, OPEN_APPEND);
+               if (lseek(logfile, (off_t)0, SEEK_END) == BAD_LSEEK)
+               {
+                       close(logfile);
+                       logfile = -1;
+               }
+               break;
+       case 'D': case 'd':
+               /*
+                * Don't do anything.
+                */
+               free(filename);
+               return;
+       case 'q':
+               quit(QUIT_OK);
+               /*NOTREACHED*/
+       default:
+               /*
+                * Eh?
+                */
+               answer = query("Overwrite, Append, or Don't log? (Type \"O\", \"A\", \"D\" or \"q\") ", NULL_PARG);
+               goto loop;
+       }
+
+       if (logfile < 0)
+       {
+               /*
+                * Error in opening logfile.
+                */
+               parg.p_string = filename;
+               error("Cannot write to \"%s\"", &parg);
+               free(filename);
+               return;
+       }
+       free(filename);
+       SET_BINARY(logfile);
+}
+
+#endif
diff --git a/thirdparty/less/filename.c b/thirdparty/less/filename.c
new file mode 100644 (file)
index 0000000..ed6dcc7
--- /dev/null
@@ -0,0 +1,1081 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Routines to mess around with filenames (and files).
+ * Much of this is very OS dependent.
+ */
+
+#include "less.h"
+#include "lglob.h"
+#if MSDOS_COMPILER
+#include <dos.h>
+#if MSDOS_COMPILER==WIN32C && defined(__BORLANDC__)
+#include <dir.h>
+#endif
+#if MSDOS_COMPILER==DJGPPC
+#include <glob.h>
+#include <dir.h>
+#define _MAX_PATH      PATH_MAX
+#endif
+#endif
+#ifdef _OSK
+#include <rbf.h>
+#ifndef _OSK_MWC32
+#include <modes.h>
+#endif
+#endif
+#if OS2
+#include <signal.h>
+#endif
+
+#if HAVE_STAT
+#include <sys/stat.h>
+#ifndef S_ISDIR
+#define        S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR)
+#endif
+#ifndef S_ISREG
+#define        S_ISREG(m)      (((m) & S_IFMT) == S_IFREG)
+#endif
+#endif
+
+
+extern int force_open;
+extern int secure;
+extern int use_lessopen;
+extern int ctldisp;
+extern int utf_mode;
+extern IFILE curr_ifile;
+extern IFILE old_ifile;
+#if SPACES_IN_FILENAMES
+extern char openquote;
+extern char closequote;
+#endif
+
+/*
+ * Remove quotes around a filename.
+ */
+       public char *
+shell_unquote(str)
+       char *str;
+{
+       char *name;
+       char *p;
+
+       name = p = (char *) ecalloc(strlen(str)+1, sizeof(char));
+       if (*str == openquote)
+       {
+               str++;
+               while (*str != '\0')
+               {
+                       if (*str == closequote)
+                       {
+                               if (str[1] != closequote)
+                                       break;
+                               str++;
+                       }
+                       *p++ = *str++;
+               }
+       } else
+       {
+               char *esc = get_meta_escape();
+               int esclen = strlen(esc);
+               while (*str != '\0')
+               {
+                       if (esclen > 0 && strncmp(str, esc, esclen) == 0)
+                               str += esclen;
+                       *p++ = *str++;
+               }
+       }
+       *p = '\0';
+       return (name);
+}
+
+/*
+ * Get the shell's escape character.
+ */
+       public char *
+get_meta_escape()
+{
+       char *s;
+
+       s = lgetenv("LESSMETAESCAPE");
+       if (s == NULL)
+               s = DEF_METAESCAPE;
+       return (s);
+}
+
+/*
+ * Get the characters which the shell considers to be "metacharacters".
+ */
+       static char *
+metachars()
+{
+       static char *mchars = NULL;
+
+       if (mchars == NULL)
+       {
+               mchars = lgetenv("LESSMETACHARS");
+               if (mchars == NULL)
+                       mchars = DEF_METACHARS;
+       }
+       return (mchars);
+}
+
+/*
+ * Is this a shell metacharacter?
+ */
+       static int
+metachar(c)
+       char c;
+{
+       return (strchr(metachars(), c) != NULL);
+}
+
+/*
+ * Insert a backslash before each metacharacter in a string.
+ */
+       public char *
+shell_quote(s)
+       char *s;
+{
+       char *p;
+       char *newstr;
+       int len;
+       char *esc = get_meta_escape();
+       int esclen = strlen(esc);
+       int use_quotes = 0;
+       int have_quotes = 0;
+
+       /*
+        * Determine how big a string we need to allocate.
+        */
+       len = 1; /* Trailing null byte */
+       for (p = s;  *p != '\0';  p++)
+       {
+               len++;
+               if (*p == openquote || *p == closequote)
+                       have_quotes = 1;
+               if (metachar(*p))
+               {
+                       if (esclen == 0)
+                       {
+                               /*
+                                * We've got a metachar, but this shell 
+                                * doesn't support escape chars.  Use quotes.
+                                */
+                               use_quotes = 1;
+                       } else
+                       {
+                               /*
+                                * Allow space for the escape char.
+                                */
+                               len += esclen;
+                       }
+               }
+       }
+       if (use_quotes)
+       {
+               if (have_quotes)
+                       /*
+                        * We can't quote a string that contains quotes.
+                        */
+                       return (NULL);
+               len = strlen(s) + 3;
+       }
+       /*
+        * Allocate and construct the new string.
+        */
+       newstr = p = (char *) ecalloc(len, sizeof(char));
+       if (use_quotes)
+       {
+               SNPRINTF3(newstr, len, "%c%s%c", openquote, s, closequote);
+       } else
+       {
+               while (*s != '\0')
+               {
+                       if (metachar(*s))
+                       {
+                               /*
+                                * Add the escape char.
+                                */
+                               strcpy(p, esc);
+                               p += esclen;
+                       }
+                       *p++ = *s++;
+               }
+               *p = '\0';
+       }
+       return (newstr);
+}
+
+/*
+ * Return a pathname that points to a specified file in a specified directory.
+ * Return NULL if the file does not exist in the directory.
+ */
+       static char *
+dirfile(dirname, filename)
+       char *dirname;
+       char *filename;
+{
+       char *pathname;
+       char *qpathname;
+       int len;
+       int f;
+
+       if (dirname == NULL || *dirname == '\0')
+               return (NULL);
+       /*
+        * Construct the full pathname.
+        */
+       len= strlen(dirname) + strlen(filename) + 2;
+       pathname = (char *) calloc(len, sizeof(char));
+       if (pathname == NULL)
+               return (NULL);
+       SNPRINTF3(pathname, len, "%s%s%s", dirname, PATHNAME_SEP, filename);
+       /*
+        * Make sure the file exists.
+        */
+       qpathname = shell_unquote(pathname);
+       f = open(qpathname, OPEN_READ);
+       if (f < 0)
+       {
+               free(pathname);
+               pathname = NULL;
+       } else
+       {
+               close(f);
+       }
+       free(qpathname);
+       return (pathname);
+}
+
+/*
+ * Return the full pathname of the given file in the "home directory".
+ */
+       public char *
+homefile(filename)
+       char *filename;
+{
+       register char *pathname;
+
+       /*
+        * Try $HOME/filename.
+        */
+       pathname = dirfile(lgetenv("HOME"), filename);
+       if (pathname != NULL)
+               return (pathname);
+#if OS2
+       /*
+        * Try $INIT/filename.
+        */
+       pathname = dirfile(lgetenv("INIT"), filename);
+       if (pathname != NULL)
+               return (pathname);
+#endif
+#if MSDOS_COMPILER || OS2
+       /*
+        * Look for the file anywhere on search path.
+        */
+       pathname = (char *) calloc(_MAX_PATH, sizeof(char));
+#if MSDOS_COMPILER==DJGPPC
+       {
+               char *res = searchpath(filename);
+               if (res == 0)
+                       *pathname = '\0';
+               else
+                       strcpy(pathname, res);
+       }
+#else
+       _searchenv(filename, "PATH", pathname);
+#endif
+       if (*pathname != '\0')
+               return (pathname);
+       free(pathname);
+#endif
+       return (NULL);
+}
+
+/*
+ * Expand a string, substituting any "%" with the current filename,
+ * and any "#" with the previous filename.
+ * But a string of N "%"s is just replaced with N-1 "%"s.
+ * Likewise for a string of N "#"s.
+ * {{ This is a lot of work just to support % and #. }}
+ */
+       public char *
+fexpand(s)
+       char *s;
+{
+       register char *fr, *to;
+       register int n;
+       register char *e;
+       IFILE ifile;
+
+#define        fchar_ifile(c) \
+       ((c) == '%' ? curr_ifile : \
+        (c) == '#' ? old_ifile : NULL_IFILE)
+
+       /*
+        * Make one pass to see how big a buffer we 
+        * need to allocate for the expanded string.
+        */
+       n = 0;
+       for (fr = s;  *fr != '\0';  fr++)
+       {
+               switch (*fr)
+               {
+               case '%':
+               case '#':
+                       if (fr > s && fr[-1] == *fr)
+                       {
+                               /*
+                                * Second (or later) char in a string
+                                * of identical chars.  Treat as normal.
+                                */
+                               n++;
+                       } else if (fr[1] != *fr)
+                       {
+                               /*
+                                * Single char (not repeated).  Treat specially.
+                                */
+                               ifile = fchar_ifile(*fr);
+                               if (ifile == NULL_IFILE)
+                                       n++;
+                               else
+                                       n += strlen(get_filename(ifile));
+                       }
+                       /*
+                        * Else it is the first char in a string of
+                        * identical chars.  Just discard it.
+                        */
+                       break;
+               default:
+                       n++;
+                       break;
+               }
+       }
+
+       e = (char *) ecalloc(n+1, sizeof(char));
+
+       /*
+        * Now copy the string, expanding any "%" or "#".
+        */
+       to = e;
+       for (fr = s;  *fr != '\0';  fr++)
+       {
+               switch (*fr)
+               {
+               case '%':
+               case '#':
+                       if (fr > s && fr[-1] == *fr)
+                       {
+                               *to++ = *fr;
+                       } else if (fr[1] != *fr)
+                       {
+                               ifile = fchar_ifile(*fr);
+                               if (ifile == NULL_IFILE)
+                                       *to++ = *fr;
+                               else
+                               {
+                                       strcpy(to, get_filename(ifile));
+                                       to += strlen(to);
+                               }
+                       }
+                       break;
+               default:
+                       *to++ = *fr;
+                       break;
+               }
+       }
+       *to = '\0';
+       return (e);
+}
+
+
+#if TAB_COMPLETE_FILENAME
+
+/*
+ * Return a blank-separated list of filenames which "complete"
+ * the given string.
+ */
+       public char *
+fcomplete(s)
+       char *s;
+{
+       char *fpat;
+       char *qs;
+
+       if (secure)
+               return (NULL);
+       /*
+        * Complete the filename "s" by globbing "s*".
+        */
+#if MSDOS_COMPILER && (MSDOS_COMPILER == MSOFTC || MSDOS_COMPILER == BORLANDC)
+       /*
+        * But in DOS, we have to glob "s*.*".
+        * But if the final component of the filename already has
+        * a dot in it, just do "s*".  
+        * (Thus, "FILE" is globbed as "FILE*.*", 
+        *  but "FILE.A" is globbed as "FILE.A*").
+        */
+       {
+               char *slash;
+               int len;
+               for (slash = s+strlen(s)-1;  slash > s;  slash--)
+                       if (*slash == *PATHNAME_SEP || *slash == '/')
+                               break;
+               len = strlen(s) + 4;
+               fpat = (char *) ecalloc(len, sizeof(char));
+               if (strchr(slash, '.') == NULL)
+                       SNPRINTF1(fpat, len, "%s*.*", s);
+               else
+                       SNPRINTF1(fpat, len, "%s*", s);
+       }
+#else
+       {
+       int len = strlen(s) + 2;
+       fpat = (char *) ecalloc(len, sizeof(char));
+       SNPRINTF1(fpat, len, "%s*", s);
+       }
+#endif
+       qs = lglob(fpat);
+       s = shell_unquote(qs);
+       if (strcmp(s,fpat) == 0)
+       {
+               /*
+                * The filename didn't expand.
+                */
+               free(qs);
+               qs = NULL;
+       }
+       free(s);
+       free(fpat);
+       return (qs);
+}
+#endif
+
+/*
+ * Try to determine if a file is "binary".
+ * This is just a guess, and we need not try too hard to make it accurate.
+ */
+       public int
+bin_file(f)
+       int f;
+{
+       int n;
+       int bin_count = 0;
+       char data[256];
+       char* p;
+       char* pend;
+
+       if (!seekable(f))
+               return (0);
+       if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK)
+               return (0);
+       n = read(f, data, sizeof(data));
+       pend = &data[n];
+       for (p = data;  p < pend;  )
+       {
+               LWCHAR c = step_char(&p, +1, pend);
+               if (ctldisp == OPT_ONPLUS && IS_CSI_START(c))
+               {
+                       do {
+                               c = step_char(&p, +1, pend);
+                       } while (p < pend && is_ansi_middle(c));
+               } else if (binary_char(c))
+                       bin_count++;
+       }
+       /*
+        * Call it a binary file if there are more than 5 binary characters
+        * in the first 256 bytes of the file.
+        */
+       return (bin_count > 5);
+}
+
+/*
+ * Try to determine the size of a file by seeking to the end.
+ */
+       static POSITION
+seek_filesize(f)
+       int f;
+{
+       off_t spos;
+
+       spos = lseek(f, (off_t)0, SEEK_END);
+       if (spos == BAD_LSEEK)
+               return (NULL_POSITION);
+       return ((POSITION) spos);
+}
+
+/*
+ * Read a string from a file.
+ * Return a pointer to the string in memory.
+ */
+       static char *
+readfd(fd)
+       FILE *fd;
+{
+       int len;
+       int ch;
+       char *buf;
+       char *p;
+       
+       /* 
+        * Make a guess about how many chars in the string
+        * and allocate a buffer to hold it.
+        */
+       len = 100;
+       buf = (char *) ecalloc(len, sizeof(char));
+       for (p = buf;  ;  p++)
+       {
+               if ((ch = getc(fd)) == '\n' || ch == EOF)
+                       break;
+               if (p - buf >= len-1)
+               {
+                       /*
+                        * The string is too big to fit in the buffer we have.
+                        * Allocate a new buffer, twice as big.
+                        */
+                       len *= 2;
+                       *p = '\0';
+                       p = (char *) ecalloc(len, sizeof(char));
+                       strcpy(p, buf);
+                       free(buf);
+                       buf = p;
+                       p = buf + strlen(buf);
+               }
+               *p = ch;
+       }
+       *p = '\0';
+       return (buf);
+}
+
+
+
+#if HAVE_POPEN
+
+#if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C
+FILE *popen();
+#endif
+
+/*
+ * Execute a shell command.
+ * Return a pointer to a pipe connected to the shell command's standard output.
+ */
+       static FILE *
+shellcmd(cmd)
+       char *cmd;
+{
+       FILE *fd;
+
+#if HAVE_SHELL
+       char *shell;
+
+       shell = lgetenv("SHELL");
+       if (shell != NULL && *shell != '\0')
+       {
+               char *scmd;
+               char *esccmd;
+
+               /*
+                * Read the output of <$SHELL -c cmd>.  
+                * Escape any metacharacters in the command.
+                */
+               esccmd = shell_quote(cmd);
+               if (esccmd == NULL)
+               {
+                       fd = popen(cmd, "r");
+               } else
+               {
+                       int len = strlen(shell) + strlen(esccmd) + 5;
+                       scmd = (char *) ecalloc(len, sizeof(char));
+                       SNPRINTF3(scmd, len, "%s %s %s", shell, shell_coption(), esccmd);
+                       free(esccmd);
+                       fd = popen(scmd, "r");
+                       free(scmd);
+               }
+       } else
+#endif
+       {
+               fd = popen(cmd, "r");
+       }
+       /*
+        * Redirection in `popen' might have messed with the
+        * standard devices.  Restore binary input mode.
+        */
+       SET_BINARY(0);
+       return (fd);
+}
+
+#endif /* HAVE_POPEN */
+
+
+/*
+ * Expand a filename, doing any system-specific metacharacter substitutions.
+ */
+       public char *
+lglob(filename)
+       char *filename;
+{
+       char *gfilename;
+       char *ofilename;
+
+       ofilename = fexpand(filename);
+       if (secure)
+               return (ofilename);
+       filename = shell_unquote(ofilename);
+
+#ifdef DECL_GLOB_LIST
+{
+       /*
+        * The globbing function returns a list of names.
+        */
+       int length;
+       char *p;
+       char *qfilename;
+       DECL_GLOB_LIST(list)
+
+       GLOB_LIST(filename, list);
+       if (GLOB_LIST_FAILED(list))
+       {
+               free(filename);
+               return (ofilename);
+       }
+       length = 1; /* Room for trailing null byte */
+       for (SCAN_GLOB_LIST(list, p))
+       {
+               INIT_GLOB_LIST(list, p);
+               qfilename = shell_quote(p);
+               if (qfilename != NULL)
+               {
+                       length += strlen(qfilename) + 1;
+                       free(qfilename);
+               }
+       }
+       gfilename = (char *) ecalloc(length, sizeof(char));
+       for (SCAN_GLOB_LIST(list, p))
+       {
+               INIT_GLOB_LIST(list, p);
+               qfilename = shell_quote(p);
+               if (qfilename != NULL)
+               {
+                       sprintf(gfilename + strlen(gfilename), "%s ", qfilename);
+                       free(qfilename);
+               }
+       }
+       /*
+        * Overwrite the final trailing space with a null terminator.
+        */
+       *--p = '\0';
+       GLOB_LIST_DONE(list);
+}
+#else
+#ifdef DECL_GLOB_NAME
+{
+       /*
+        * The globbing function returns a single name, and
+        * is called multiple times to walk thru all names.
+        */
+       register char *p;
+       register int len;
+       register int n;
+       char *pathname;
+       char *qpathname;
+       DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle)
+       
+       GLOB_FIRST_NAME(filename, &fnd, handle);
+       if (GLOB_FIRST_FAILED(handle))
+       {
+               free(filename);
+               return (ofilename);
+       }
+
+       _splitpath(filename, drive, dir, fname, ext);
+       len = 100;
+       gfilename = (char *) ecalloc(len, sizeof(char));
+       p = gfilename;
+       do {
+               n = strlen(drive) + strlen(dir) + strlen(fnd.GLOB_NAME) + 1;
+               pathname = (char *) ecalloc(n, sizeof(char));
+               SNPRINTF3(pathname, n, "%s%s%s", drive, dir, fnd.GLOB_NAME);
+               qpathname = shell_quote(pathname);
+               free(pathname);
+               if (qpathname != NULL)
+               {
+                       n = strlen(qpathname);
+                       while (p - gfilename + n + 2 >= len)
+                       {
+                               /*
+                                * No room in current buffer.
+                                * Allocate a bigger one.
+                                */
+                               len *= 2;
+                               *p = '\0';
+                               p = (char *) ecalloc(len, sizeof(char));
+                               strcpy(p, gfilename);
+                               free(gfilename);
+                               gfilename = p;
+                               p = gfilename + strlen(gfilename);
+                       }
+                       strcpy(p, qpathname);
+                       free(qpathname);
+                       p += n;
+                       *p++ = ' ';
+               }
+       } while (GLOB_NEXT_NAME(handle, &fnd) == 0);
+
+       /*
+        * Overwrite the final trailing space with a null terminator.
+        */
+       *--p = '\0';
+       GLOB_NAME_DONE(handle);
+}
+#else
+#if HAVE_POPEN
+{
+       /*
+        * We get the shell to glob the filename for us by passing
+        * an "echo" command to the shell and reading its output.
+        */
+       FILE *fd;
+       char *s;
+       char *lessecho;
+       char *cmd;
+       char *esc;
+       int len;
+
+       esc = get_meta_escape();
+       if (strlen(esc) == 0)
+               esc = "-";
+       esc = shell_quote(esc);
+       if (esc == NULL)
+       {
+               free(filename);
+               return (ofilename);
+       }
+       lessecho = lgetenv("LESSECHO");
+       if (lessecho == NULL || *lessecho == '\0')
+               lessecho = "lessecho";
+       /*
+        * Invoke lessecho, and read its output (a globbed list of filenames).
+        */
+       len = strlen(lessecho) + strlen(ofilename) + (7*strlen(metachars())) + 24;
+       cmd = (char *) ecalloc(len, sizeof(char));
+       SNPRINTF4(cmd, len, "%s -p0x%x -d0x%x -e%s ", lessecho, openquote, closequote, esc);
+       free(esc);
+       for (s = metachars();  *s != '\0';  s++)
+               sprintf(cmd + strlen(cmd), "-n0x%x ", *s);
+       sprintf(cmd + strlen(cmd), "-- %s", ofilename);
+       fd = shellcmd(cmd);
+       free(cmd);
+       if (fd == NULL)
+       {
+               /*
+                * Cannot create the pipe.
+                * Just return the original (fexpanded) filename.
+                */
+               free(filename);
+               return (ofilename);
+       }
+       gfilename = readfd(fd);
+       pclose(fd);
+       if (*gfilename == '\0')
+       {
+               free(gfilename);
+               free(filename);
+               return (ofilename);
+       }
+}
+#else
+       /*
+        * No globbing functions at all.  Just use the fexpanded filename.
+        */
+       gfilename = save(filename);
+#endif
+#endif
+#endif
+       free(filename);
+       free(ofilename);
+       return (gfilename);
+}
+
+/*
+ * See if we should open a "replacement file" 
+ * instead of the file we're about to open.
+ */
+       public char *
+open_altfile(filename, pf, pfd)
+       char *filename;
+       int *pf;
+       void **pfd;
+{
+#if !HAVE_POPEN
+       return (NULL);
+#else
+       char *lessopen;
+       char *cmd;
+       int len;
+       FILE *fd;
+#if HAVE_FILENO
+       int returnfd = 0;
+#endif
+       
+       if (!use_lessopen || secure)
+               return (NULL);
+       ch_ungetchar(-1);
+       if ((lessopen = lgetenv("LESSOPEN")) == NULL)
+               return (NULL);
+       if (*lessopen == '|')
+       {
+               /*
+                * If LESSOPEN starts with a |, it indicates 
+                * a "pipe preprocessor".
+                */
+#if !HAVE_FILENO
+               error("LESSOPEN pipe is not supported", NULL_PARG);
+               return (NULL);
+#else
+               lessopen++;
+               returnfd = 1;
+#endif
+       }
+       if (*lessopen == '-') {
+               /*
+                * Lessopen preprocessor will accept "-" as a filename.
+                */
+               lessopen++;
+       } else {
+               if (strcmp(filename, "-") == 0)
+                       return (NULL);
+       }
+
+       len = strlen(lessopen) + strlen(filename) + 2;
+       cmd = (char *) ecalloc(len, sizeof(char));
+       SNPRINTF1(cmd, len, lessopen, filename);
+       fd = shellcmd(cmd);
+       free(cmd);
+       if (fd == NULL)
+       {
+               /*
+                * Cannot create the pipe.
+                */
+               return (NULL);
+       }
+#if HAVE_FILENO
+       if (returnfd)
+       {
+               int f;
+               char c;
+
+               /*
+                * Read one char to see if the pipe will produce any data.
+                * If it does, push the char back on the pipe.
+                */
+               f = fileno(fd);
+               SET_BINARY(f);
+               if (read(f, &c, 1) != 1)
+               {
+                       /*
+                        * Pipe is empty.  This means there is no alt file.
+                        */
+                       pclose(fd);
+                       return (NULL);
+               }
+               ch_ungetchar(c);
+               *pfd = (void *) fd;
+               *pf = f;
+               return (save("-"));
+       }
+#endif
+       cmd = readfd(fd);
+       pclose(fd);
+       if (*cmd == '\0')
+               /*
+                * Pipe is empty.  This means there is no alt file.
+                */
+               return (NULL);
+       return (cmd);
+#endif /* HAVE_POPEN */
+}
+
+/*
+ * Close a replacement file.
+ */
+       public void
+close_altfile(altfilename, filename, pipefd)
+       char *altfilename;
+       char *filename;
+       void *pipefd;
+{
+#if HAVE_POPEN
+       char *lessclose;
+       FILE *fd;
+       char *cmd;
+       int len;
+       
+       if (secure)
+               return;
+       if (pipefd != NULL)
+       {
+#if OS2
+               /*
+                * The pclose function of OS/2 emx sometimes fails.
+                * Send SIGINT to the piped process before closing it.
+                */
+               kill(((FILE*)pipefd)->_pid, SIGINT);
+#endif
+               pclose((FILE*) pipefd);
+       }
+       if ((lessclose = lgetenv("LESSCLOSE")) == NULL)
+               return;
+       len = strlen(lessclose) + strlen(filename) + strlen(altfilename) + 2;
+       cmd = (char *) ecalloc(len, sizeof(char));
+       SNPRINTF2(cmd, len, lessclose, filename, altfilename);
+       fd = shellcmd(cmd);
+       free(cmd);
+       if (fd != NULL)
+               pclose(fd);
+#endif
+}
+               
+/*
+ * Is the specified file a directory?
+ */
+       public int
+is_dir(filename)
+       char *filename;
+{
+       int isdir = 0;
+
+       filename = shell_unquote(filename);
+#if HAVE_STAT
+{
+       int r;
+       struct stat statbuf;
+
+       r = stat(filename, &statbuf);
+       isdir = (r >= 0 && S_ISDIR(statbuf.st_mode));
+}
+#else
+#ifdef _OSK
+{
+       register int f;
+
+       f = open(filename, S_IREAD | S_IFDIR);
+       if (f >= 0)
+               close(f);
+       isdir = (f >= 0);
+}
+#endif
+#endif
+       free(filename);
+       return (isdir);
+}
+
+/*
+ * Returns NULL if the file can be opened and
+ * is an ordinary file, otherwise an error message
+ * (if it cannot be opened or is a directory, etc.)
+ */
+       public char *
+bad_file(filename)
+       char *filename;
+{
+       register char *m = NULL;
+
+       filename = shell_unquote(filename);
+       if (!force_open && is_dir(filename))
+       {
+               static char is_a_dir[] = " is a directory";
+
+               m = (char *) ecalloc(strlen(filename) + sizeof(is_a_dir), 
+                       sizeof(char));
+               strcpy(m, filename);
+               strcat(m, is_a_dir);
+       } else
+       {
+#if HAVE_STAT
+               int r;
+               struct stat statbuf;
+
+               r = stat(filename, &statbuf);
+               if (r < 0)
+               {
+                       m = errno_message(filename);
+               } else if (force_open)
+               {
+                       m = NULL;
+               } else if (!S_ISREG(statbuf.st_mode))
+               {
+                       static char not_reg[] = " is not a regular file (use -f to see it)";
+                       m = (char *) ecalloc(strlen(filename) + sizeof(not_reg),
+                               sizeof(char));
+                       strcpy(m, filename);
+                       strcat(m, not_reg);
+               }
+#endif
+       }
+       free(filename);
+       return (m);
+}
+
+/*
+ * Return the size of a file, as cheaply as possible.
+ * In Unix, we can stat the file.
+ */
+       public POSITION
+filesize(f)
+       int f;
+{
+#if HAVE_STAT
+       struct stat statbuf;
+
+       if (fstat(f, &statbuf) >= 0)
+               return ((POSITION) statbuf.st_size);
+#else
+#ifdef _OSK
+       long size;
+
+       if ((size = (long) _gs_size(f)) >= 0)
+               return ((POSITION) size);
+#endif
+#endif
+       return (seek_filesize(f));
+}
+
+/*
+ * 
+ */
+       public char *
+shell_coption()
+{
+       return ("-c");
+}
+
+/*
+ * Return last component of a pathname.
+ */
+       public char *
+last_component(name)
+       char *name;
+{
+       char *slash;
+
+       for (slash = name + strlen(name);  slash > name; )
+       {
+               --slash;
+               if (*slash == *PATHNAME_SEP || *slash == '/')
+                       return (slash + 1);
+       }
+       return (name);
+}
+
diff --git a/thirdparty/less/forwback.c b/thirdparty/less/forwback.c
new file mode 100644 (file)
index 0000000..ebe422d
--- /dev/null
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Primitives for displaying the file on the screen,
+ * scrolling either forward or backward.
+ */
+
+#include "less.h"
+#include "position.h"
+
+public int screen_trashed;
+public int squished;
+public int no_back_scroll = 0;
+public int forw_prompt;
+
+extern int sigs;
+extern int top_scroll;
+extern int quiet;
+extern int sc_width, sc_height;
+extern int plusoption;
+extern int forw_scroll;
+extern int back_scroll;
+extern int ignore_eoi;
+extern int clear_bg;
+extern int final_attr;
+extern int oldbot;
+#if TAGS
+extern char *tagoption;
+#endif
+
+/*
+ * Sound the bell to indicate user is trying to move past end of file.
+ */
+       static void
+eof_bell()
+{
+       if (quiet == NOT_QUIET)
+               bell();
+       else
+               vbell();
+}
+
+/*
+ * Check to see if the end of file is currently displayed.
+ */
+       public int
+eof_displayed()
+{
+       POSITION pos;
+
+       if (ignore_eoi)
+               return (0);
+
+       if (ch_length() == NULL_POSITION)
+               /*
+                * If the file length is not known,
+                * we can't possibly be displaying EOF.
+                */
+               return (0);
+
+       /*
+        * If the bottom line is empty, we are at EOF.
+        * If the bottom line ends at the file length,
+        * we must be just at EOF.
+        */
+       pos = position(BOTTOM_PLUS_ONE);
+       return (pos == NULL_POSITION || pos == ch_length());
+}
+
+/*
+ * Check to see if the entire file is currently displayed.
+ */
+       public int
+entire_file_displayed()
+{
+       POSITION pos;
+
+       /* Make sure last line of file is displayed. */
+       if (!eof_displayed())
+               return (0);
+
+       /* Make sure first line of file is displayed. */
+       pos = position(0);
+       return (pos == NULL_POSITION || pos == 0);
+}
+
+/*
+ * If the screen is "squished", repaint it.
+ * "Squished" means the first displayed line is not at the top
+ * of the screen; this can happen when we display a short file
+ * for the first time.
+ */
+       public void
+squish_check()
+{
+       if (!squished)
+               return;
+       squished = 0;
+       repaint();
+}
+
+/*
+ * Display n lines, scrolling forward, 
+ * starting at position pos in the input file.
+ * "force" means display the n lines even if we hit end of file.
+ * "only_last" means display only the last screenful if n > screen size.
+ * "nblank" is the number of blank lines to draw before the first
+ *   real line.  If nblank > 0, the pos must be NULL_POSITION.
+ *   The first real line after the blanks will start at ch_zero().
+ */
+       public void
+forw(n, pos, force, only_last, nblank)
+       register int n;
+       POSITION pos;
+       int force;
+       int only_last;
+       int nblank;
+{
+       int eof = 0;
+       int nlines = 0;
+       int do_repaint;
+       static int first_time = 1;
+
+       squish_check();
+
+       /*
+        * do_repaint tells us not to display anything till the end, 
+        * then just repaint the entire screen.
+        * We repaint if we are supposed to display only the last 
+        * screenful and the request is for more than a screenful.
+        * Also if the request exceeds the forward scroll limit
+        * (but not if the request is for exactly a screenful, since
+        * repainting itself involves scrolling forward a screenful).
+        */
+       do_repaint = (only_last && n > sc_height-1) || 
+               (forw_scroll >= 0 && n > forw_scroll && n != sc_height-1);
+
+       if (!do_repaint)
+       {
+               if (top_scroll && n >= sc_height - 1 && pos != ch_length())
+               {
+                       /*
+                        * Start a new screen.
+                        * {{ This is not really desirable if we happen
+                        *    to hit eof in the middle of this screen,
+                        *    but we don't yet know if that will happen. }}
+                        */
+                       pos_clear();
+                       add_forw_pos(pos);
+                       force = 1;
+                       clear();
+                       home();
+               }
+
+               if (pos != position(BOTTOM_PLUS_ONE) || empty_screen())
+               {
+                       /*
+                        * This is not contiguous with what is
+                        * currently displayed.  Clear the screen image 
+                        * (position table) and start a new screen.
+                        */
+                       pos_clear();
+                       add_forw_pos(pos);
+                       force = 1;
+                       if (top_scroll)
+                       {
+                               clear();
+                               home();
+                       } else if (!first_time)
+                       {
+                               putstr("...skipping...\n");
+                       }
+               }
+       }
+
+       while (--n >= 0)
+       {
+               /*
+                * Read the next line of input.
+                */
+               if (nblank > 0)
+               {
+                       /*
+                        * Still drawing blanks; don't get a line 
+                        * from the file yet.
+                        * If this is the last blank line, get ready to
+                        * read a line starting at ch_zero() next time.
+                        */
+                       if (--nblank == 0)
+                               pos = ch_zero();
+               } else
+               {
+                       /* 
+                        * Get the next line from the file.
+                        */
+                       pos = forw_line(pos);
+                       if (pos == NULL_POSITION)
+                       {
+                               /*
+                                * End of file: stop here unless the top line 
+                                * is still empty, or "force" is true.
+                                * Even if force is true, stop when the last
+                                * line in the file reaches the top of screen.
+                                */
+                               eof = 1;
+                               if (!force && position(TOP) != NULL_POSITION)
+                                       break;
+                               if (!empty_lines(0, 0) && 
+                                   !empty_lines(1, 1) &&
+                                    empty_lines(2, sc_height-1))
+                                       break;
+                       }
+               }
+               /*
+                * Add the position of the next line to the position table.
+                * Display the current line on the screen.
+                */
+               add_forw_pos(pos);
+               nlines++;
+               if (do_repaint)
+                       continue;
+               /*
+                * If this is the first screen displayed and
+                * we hit an early EOF (i.e. before the requested
+                * number of lines), we "squish" the display down
+                * at the bottom of the screen.
+                * But don't do this if a + option or a -t option
+                * was given.  These options can cause us to
+                * start the display after the beginning of the file,
+                * and it is not appropriate to squish in that case.
+                */
+               if (first_time && pos == NULL_POSITION && !top_scroll && 
+#if TAGS
+                   tagoption == NULL &&
+#endif
+                   !plusoption)
+               {
+                       squished = 1;
+                       continue;
+               }
+               put_line();
+#if 0
+               /* {{ 
+                * Can't call clear_eol here.  The cursor might be at end of line
+                * on an ignaw terminal, so clear_eol would clear the last char
+                * of the current line instead of all of the next line.
+                * If we really need to do this on clear_bg terminals, we need
+                * to find a better way.
+                * }}
+                */
+               if (clear_bg && apply_at_specials(final_attr) != AT_NORMAL)
+               {
+                       /*
+                        * Writing the last character on the last line
+                        * of the display may have scrolled the screen.
+                        * If we were in standout mode, clear_bg terminals 
+                        * will fill the new line with the standout color.
+                        * Now we're in normal mode again, so clear the line.
+                        */
+                       clear_eol();
+               }
+#endif
+               forw_prompt = 1;
+       }
+
+       if (nlines == 0)
+               eof_bell();
+       else if (do_repaint)
+               repaint();
+       first_time = 0;
+       (void) currline(BOTTOM);
+}
+
+/*
+ * Display n lines, scrolling backward.
+ */
+       public void
+back(n, pos, force, only_last)
+       register int n;
+       POSITION pos;
+       int force;
+       int only_last;
+{
+       int nlines = 0;
+       int do_repaint;
+
+       squish_check();
+       do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
+       while (--n >= 0)
+       {
+               /*
+                * Get the previous line of input.
+                */
+               pos = back_line(pos);
+               if (pos == NULL_POSITION)
+               {
+                       /*
+                        * Beginning of file: stop here unless "force" is true.
+                        */
+                       if (!force)
+                               break;
+               }
+               /*
+                * Add the position of the previous line to the position table.
+                * Display the line on the screen.
+                */
+               add_back_pos(pos);
+               nlines++;
+               if (!do_repaint)
+               {
+                       home();
+                       add_line();
+                       put_line();
+               }
+       }
+
+       if (nlines == 0)
+               eof_bell();
+       else if (do_repaint)
+               repaint();
+       else if (!oldbot)
+               lower_left();
+       (void) currline(BOTTOM);
+}
+
+/*
+ * Display n more lines, forward.
+ * Start just after the line currently displayed at the bottom of the screen.
+ */
+       public void
+forward(n, force, only_last)
+       int n;
+       int force;
+       int only_last;
+{
+       POSITION pos;
+
+       if (get_quit_at_eof() && eof_displayed() && !(ch_getflags() & CH_HELPFILE))
+       {
+               /*
+                * If the -e flag is set and we're trying to go
+                * forward from end-of-file, go on to the next file.
+                */
+               if (edit_next(1))
+                       quit(QUIT_OK);
+               return;
+       }
+
+       pos = position(BOTTOM_PLUS_ONE);
+       if (pos == NULL_POSITION && (!force || empty_lines(2, sc_height-1)))
+       {
+               if (ignore_eoi)
+               {
+                       /*
+                        * ignore_eoi is to support A_F_FOREVER.
+                        * Back up until there is a line at the bottom
+                        * of the screen.
+                        */
+                       if (empty_screen())
+                               pos = ch_zero();
+                       else
+                       {
+                               do
+                               {
+                                       back(1, position(TOP), 1, 0);
+                                       pos = position(BOTTOM_PLUS_ONE);
+                               } while (pos == NULL_POSITION);
+                       }
+               } else
+               {
+                       eof_bell();
+                       return;
+               }
+       }
+       forw(n, pos, force, only_last, 0);
+}
+
+/*
+ * Display n more lines, backward.
+ * Start just before the line currently displayed at the top of the screen.
+ */
+       public void
+backward(n, force, only_last)
+       int n;
+       int force;
+       int only_last;
+{
+       POSITION pos;
+
+       pos = position(TOP);
+       if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0))
+       {
+               eof_bell();
+               return;   
+       }
+       back(n, pos, force, only_last);
+}
+
+/*
+ * Get the backwards scroll limit.
+ * Must call this function instead of just using the value of
+ * back_scroll, because the default case depends on sc_height and
+ * top_scroll, as well as back_scroll.
+ */
+       public int
+get_back_scroll()
+{
+       if (no_back_scroll)
+               return (0);
+       if (back_scroll >= 0)
+               return (back_scroll);
+       if (top_scroll)
+               return (sc_height - 2);
+       return (10000); /* infinity */
+}
diff --git a/thirdparty/less/funcs.h b/thirdparty/less/funcs.h
new file mode 100644 (file)
index 0000000..6595232
--- /dev/null
@@ -0,0 +1,291 @@
+       public char * save ();
+       public VOID_POINTER ecalloc ();
+       public char * skipsp ();
+       public int sprefix ();
+       public void quit ();
+       public void raw_mode ();
+       public void scrsize ();
+       public char * special_key_str ();
+       public void get_term ();
+       public void init ();
+       public void deinit ();
+       public void home ();
+       public void add_line ();
+       public void remove_top ();
+       public void win32_scroll_up ();
+       public void lower_left ();
+       public void line_left ();
+       public void check_winch ();
+       public void goto_line ();
+       public void vbell ();
+       public void bell ();
+       public void clear ();
+       public void clear_eol ();
+       public void clear_bot ();
+       public void at_enter ();
+       public void at_exit ();
+       public void at_switch ();
+       public int is_at_equiv ();
+       public int apply_at_specials ();
+       public void backspace ();
+       public void putbs ();
+       public char WIN32getch ();
+       public void WIN32setcolors ();
+       public void WIN32textout ();
+       public void match_brac ();
+       public void ch_ungetchar ();
+       public void end_logfile ();
+       public void sync_logfile ();
+       public int ch_seek ();
+       public int ch_end_seek ();
+       public int ch_beg_seek ();
+       public POSITION ch_length ();
+       public POSITION ch_tell ();
+       public int ch_forw_get ();
+       public int ch_back_get ();
+       public void ch_setbufspace ();
+       public void ch_flush ();
+       public int seekable ();
+       public void ch_init ();
+       public void ch_close ();
+       public int ch_getflags ();
+       public void ch_dump ();
+       public void init_charset ();
+       public int binary_char ();
+       public int control_char ();
+       public char * prchar ();
+       public char * prutfchar ();
+       public int utf_len ();
+       public int is_utf8_well_formed ();
+       public LWCHAR get_wchar ();
+       public void put_wchar ();
+       public LWCHAR step_char ();
+       public int is_composing_char ();
+       public int is_ubin_char ();
+       public int is_wide_char ();
+       public int is_combining_char ();
+       public void cmd_reset ();
+       public void clear_cmd ();
+       public void cmd_putstr ();
+       public int len_cmdbuf ();
+       public void set_mlist ();
+       public void cmd_addhist ();
+       public void cmd_accept ();
+       public int cmd_char ();
+       public LINENUM cmd_int ();
+       public char * get_cmdbuf ();
+       public char * cmd_lastpattern ();
+       public void init_cmdhist ();
+       public void save_cmdhist ();
+       public int in_mca ();
+       public void dispversion ();
+       public int getcc ();
+       public void ungetcc ();
+       public void ungetsc ();
+       public void commands ();
+       public int cvt_length ();
+       public int * cvt_alloc_chpos ();
+       public void cvt_text ();
+       public void init_cmds ();
+       public void add_fcmd_table ();
+       public void add_ecmd_table ();
+       public int fcmd_decode ();
+       public int ecmd_decode ();
+       public char * lgetenv ();
+       public int lesskey ();
+       public void add_hometable ();
+       public int editchar ();
+       public void init_textlist ();
+       public char * forw_textlist ();
+       public char * back_textlist ();
+       public int edit ();
+       public int edit_ifile ();
+       public int edit_list ();
+       public int edit_first ();
+       public int edit_last ();
+       public int edit_next ();
+       public int edit_prev ();
+       public int edit_index ();
+       public IFILE save_curr_ifile ();
+       public void unsave_ifile ();
+       public void reedit_ifile ();
+       public void reopen_curr_ifile ();
+       public int edit_stdin ();
+       public void cat_file ();
+       public void use_logfile ();
+       public char * shell_unquote ();
+       public char * get_meta_escape ();
+       public char * shell_quote ();
+       public char * homefile ();
+       public char * fexpand ();
+       public char * fcomplete ();
+       public int bin_file ();
+       public char * lglob ();
+       public char * open_altfile ();
+       public void close_altfile ();
+       public int is_dir ();
+       public char * bad_file ();
+       public POSITION filesize ();
+       public char * shell_coption ();
+       public char * last_component ();
+       public int eof_displayed ();
+       public int entire_file_displayed ();
+       public void squish_check ();
+       public void forw ();
+       public void back ();
+       public void forward ();
+       public void backward ();
+       public int get_back_scroll ();
+       public void del_ifile ();
+       public IFILE next_ifile ();
+       public IFILE prev_ifile ();
+       public IFILE getoff_ifile ();
+       public int nifile ();
+       public IFILE get_ifile ();
+       public char * get_filename ();
+       public int get_index ();
+       public void store_pos ();
+       public void get_pos ();
+       public void set_open ();
+       public int opened ();
+       public void hold_ifile ();
+       public int held_ifile ();
+       public void * get_filestate ();
+       public void set_filestate ();
+       public void if_dump ();
+       public POSITION forw_line ();
+       public POSITION back_line ();
+       public void set_attnpos ();
+       public void jump_forw ();
+       public void jump_back ();
+       public void repaint ();
+       public void jump_percent ();
+       public void jump_line_loc ();
+       public void jump_loc ();
+       public void init_line ();
+       public int is_ascii_char ();
+       public void prewind ();
+       public void plinenum ();
+       public void pshift_all ();
+       public int is_ansi_end ();
+       public int is_ansi_middle ();
+       public int pappend ();
+       public int pflushmbc ();
+       public void pdone ();
+       public void set_status_col ();
+       public int gline ();
+       public void null_line ();
+       public POSITION forw_raw_line ();
+       public POSITION back_raw_line ();
+       public void clr_linenum ();
+       public void add_lnum ();
+       public LINENUM find_linenum ();
+       public POSITION find_pos ();
+       public LINENUM currline ();
+       public void lsystem ();
+       public int pipe_mark ();
+       public int pipe_data ();
+       public void init_mark ();
+       public int badmark ();
+       public void setmark ();
+       public void lastmark ();
+       public void gomark ();
+       public POSITION markpos ();
+       public void unmark ();
+       public void opt_o ();
+       public void opt__O ();
+       public void opt_j ();
+       public void calc_jump_sline ();
+       public void opt_shift ();
+       public void calc_shift_count ();
+       public void opt_k ();
+       public void opt_t ();
+       public void opt__T ();
+       public void opt_p ();
+       public void opt__P ();
+       public void opt_b ();
+       public void opt_i ();
+       public void opt__V ();
+       public void opt_D ();
+       public void opt_x ();
+       public void opt_quote ();
+       public void opt_query ();
+       public int get_swindow ();
+       public char * propt ();
+       public void scan_option ();
+       public void toggle_option ();
+       public int opt_has_param ();
+       public char * opt_prompt ();
+       public int isoptpending ();
+       public void nopendopt ();
+       public int getnum ();
+       public long getfraction ();
+       public int get_quit_at_eof ();
+       public void init_option ();
+       public struct loption * findopt ();
+       public struct loption * findopt_name ();
+       public int iread ();
+       public void intread ();
+       public long get_time ();
+       public char * errno_message ();
+       public int percentage ();
+       public POSITION percent_pos ();
+       public int  os9_signal ();
+       public void put_line ();
+       public void flush ();
+       public int putchr ();
+       public void putstr ();
+       public void get_return ();
+       public void error ();
+       public void ierror ();
+       public int query ();
+       public int compile_pattern ();
+       public void uncompile_pattern ();
+       public int is_null_pattern ();
+       public int match_pattern ();
+       public POSITION position ();
+       public void add_forw_pos ();
+       public void add_back_pos ();
+       public void pos_clear ();
+       public void pos_init ();
+       public int onscreen ();
+       public int empty_screen ();
+       public int empty_lines ();
+       public void get_scrpos ();
+       public int adjsline ();
+       public void init_prompt ();
+       public char * pr_expand ();
+       public char * eq_message ();
+       public char * pr_string ();
+       public char * wait_message ();
+       public void init_search ();
+       public void repaint_hilite ();
+       public void clear_attn ();
+       public void undo_search ();
+       public void clr_hlist ();
+       public void clr_hilite ();
+       public void clr_filter ();
+       public int is_filtered ();
+       public int is_hilited ();
+       public void chg_caseless ();
+       public void chg_hilite ();
+       public int search ();
+       public void prep_hilite ();
+       public void set_filter_pattern ();
+       public int is_filtering ();
+       public RETSIGTYPE winch ();
+       public RETSIGTYPE winch ();
+       public void init_signals ();
+       public void psignals ();
+       public void cleantags ();
+       public int gettagtype ();
+       public void findtag ();
+       public POSITION tagsearch ();
+       public char * nexttag ();
+       public char * prevtag ();
+       public int ntags ();
+       public int curr_tag ();
+       public int edit_tagfile ();
+       public void open_getchr ();
+       public void close_getchr ();
+       public int getchr ();
diff --git a/thirdparty/less/help.c b/thirdparty/less/help.c
new file mode 100644 (file)
index 0000000..85b0a4a
--- /dev/null
@@ -0,0 +1,236 @@
+/* This file was generated by mkhelp from less.hlp */
+#include "less.h"
+constant char helpdata[] = {
+'\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','\b','S','U','\b','U','M','\b','M','M','\b','M','A','\b','A','R','\b','R','Y','\b','Y',' ','O','\b','O','F','\b','F',' ','L','\b','L','E','\b','E','S','\b','S','S','\b','S',' ','C','\b','C','O','\b','O','M','\b','M','M','\b','M','A','\b','A','N','\b','N','D','\b','D','S','\b','S','\n',
+'\n',
+' ',' ',' ',' ',' ',' ','C','o','m','m','a','n','d','s',' ','m','a','r','k','e','d',' ','w','i','t','h',' ','*',' ','m','a','y',' ','b','e',' ','p','r','e','c','e','d','e','d',' ','b','y',' ','a',' ','n','u','m','b','e','r',',',' ','_','\b','N','.','\n',
+' ',' ',' ',' ',' ',' ','N','o','t','e','s',' ','i','n',' ','p','a','r','e','n','t','h','e','s','e','s',' ','i','n','d','i','c','a','t','e',' ','t','h','e',' ','b','e','h','a','v','i','o','r',' ','i','f',' ','_','\b','N',' ','i','s',' ','g','i','v','e','n','.','\n',
+'\n',
+' ',' ','h',' ',' ','H',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','p','l','a','y',' ','t','h','i','s',' ','h','e','l','p','.','\n',
+' ',' ','q',' ',' ',':','q',' ',' ','Q',' ',' ',':','Q',' ',' ','Z','Z',' ',' ',' ',' ',' ','E','x','i','t','.','\n',
+' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n',
+'\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','M','\b','M','O','\b','O','V','\b','V','I','\b','I','N','\b','N','G','\b','G','\n',
+'\n',
+' ',' ','e',' ',' ','^','E',' ',' ','j',' ',' ','^','N',' ',' ','C','R',' ',' ','*',' ',' ','F','o','r','w','a','r','d',' ',' ','o','n','e',' ','l','i','n','e',' ',' ',' ','(','o','r',' ','_','\b','N',' ','l','i','n','e','s',')','.','\n',
+' ',' ','y',' ',' ','^','Y',' ',' ','k',' ',' ','^','K',' ',' ','^','P',' ',' ','*',' ',' ','B','a','c','k','w','a','r','d',' ','o','n','e',' ','l','i','n','e',' ',' ',' ','(','o','r',' ','_','\b','N',' ','l','i','n','e','s',')','.','\n',
+' ',' ','f',' ',' ','^','F',' ',' ','^','V',' ',' ','S','P','A','C','E',' ',' ','*',' ',' ','F','o','r','w','a','r','d',' ',' ','o','n','e',' ','w','i','n','d','o','w',' ','(','o','r',' ','_','\b','N',' ','l','i','n','e','s',')','.','\n',
+' ',' ','b',' ',' ','^','B',' ',' ','E','S','C','-','v',' ',' ',' ',' ',' ',' ','*',' ',' ','B','a','c','k','w','a','r','d',' ','o','n','e',' ','w','i','n','d','o','w',' ','(','o','r',' ','_','\b','N',' ','l','i','n','e','s',')','.','\n',
+' ',' ','z',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','F','o','r','w','a','r','d',' ',' ','o','n','e',' ','w','i','n','d','o','w',' ','(','a','n','d',' ','s','e','t',' ','w','i','n','d','o','w',' ','t','o',' ','_','\b','N',')','.','\n',
+' ',' ','w',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','B','a','c','k','w','a','r','d',' ','o','n','e',' ','w','i','n','d','o','w',' ','(','a','n','d',' ','s','e','t',' ','w','i','n','d','o','w',' ','t','o',' ','_','\b','N',')','.','\n',
+' ',' ','E','S','C','-','S','P','A','C','E',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','F','o','r','w','a','r','d',' ',' ','o','n','e',' ','w','i','n','d','o','w',',',' ','b','u','t',' ','d','o','n','\'','t',' ','s','t','o','p',' ','a','t',' ','e','n','d','-','o','f','-','f','i','l','e','.','\n',
+' ',' ','d',' ',' ','^','D',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','F','o','r','w','a','r','d',' ',' ','o','n','e',' ','h','a','l','f','-','w','i','n','d','o','w',' ','(','a','n','d',' ','s','e','t',' ','h','a','l','f','-','w','i','n','d','o','w',' ','t','o',' ','_','\b','N',')','.','\n',
+' ',' ','u',' ',' ','^','U',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','B','a','c','k','w','a','r','d',' ','o','n','e',' ','h','a','l','f','-','w','i','n','d','o','w',' ','(','a','n','d',' ','s','e','t',' ','h','a','l','f','-','w','i','n','d','o','w',' ','t','o',' ','_','\b','N',')','.','\n',
+' ',' ','E','S','C','-',')',' ',' ','R','i','g','h','t','A','r','r','o','w',' ','*',' ',' ','L','e','f','t',' ',' ','o','n','e',' ','h','a','l','f',' ','s','c','r','e','e','n',' ','w','i','d','t','h',' ','(','o','r',' ','_','\b','N',' ','p','o','s','i','t','i','o','n','s',')','.','\n',
+' ',' ','E','S','C','-','(',' ',' ','L','e','f','t','A','r','r','o','w',' ',' ','*',' ',' ','R','i','g','h','t',' ','o','n','e',' ','h','a','l','f',' ','s','c','r','e','e','n',' ','w','i','d','t','h',' ','(','o','r',' ','_','\b','N',' ','p','o','s','i','t','i','o','n','s',')','.','\n',
+' ',' ','F',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','F','o','r','w','a','r','d',' ','f','o','r','e','v','e','r',';',' ','l','i','k','e',' ','"','t','a','i','l',' ','-','f','"','.','\n',
+' ',' ','r',' ',' ','^','R',' ',' ','^','L',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','R','e','p','a','i','n','t',' ','s','c','r','e','e','n','.','\n',
+' ',' ','R',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','R','e','p','a','i','n','t',' ','s','c','r','e','e','n',',',' ','d','i','s','c','a','r','d','i','n','g',' ','b','u','f','f','e','r','e','d',' ','i','n','p','u','t','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','D','e','f','a','u','l','t',' ','"','w','i','n','d','o','w','"',' ','i','s',' ','t','h','e',' ','s','c','r','e','e','n',' ','h','e','i','g','h','t','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','D','e','f','a','u','l','t',' ','"','h','a','l','f','-','w','i','n','d','o','w','"',' ','i','s',' ','h','a','l','f',' ','o','f',' ','t','h','e',' ','s','c','r','e','e','n',' ','h','e','i','g','h','t','.','\n',
+' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n',
+'\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','\b','S','E','\b','E','A','\b','A','R','\b','R','C','\b','C','H','\b','H','I','\b','I','N','\b','N','G','\b','G','\n',
+'\n',
+' ',' ','/','_','\b','p','_','\b','a','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','n',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','S','e','a','r','c','h',' ','f','o','r','w','a','r','d',' ','f','o','r',' ','(','_','\b','N','-','t','h',')',' ','m','a','t','c','h','i','n','g',' ','l','i','n','e','.','\n',
+' ',' ','?','_','\b','p','_','\b','a','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','n',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','S','e','a','r','c','h',' ','b','a','c','k','w','a','r','d',' ','f','o','r',' ','(','_','\b','N','-','t','h',')',' ','m','a','t','c','h','i','n','g',' ','l','i','n','e','.','\n',
+' ',' ','n',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','R','e','p','e','a','t',' ','p','r','e','v','i','o','u','s',' ','s','e','a','r','c','h',' ','(','f','o','r',' ','_','\b','N','-','t','h',' ','o','c','c','u','r','r','e','n','c','e',')','.','\n',
+' ',' ','N',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','R','e','p','e','a','t',' ','p','r','e','v','i','o','u','s',' ','s','e','a','r','c','h',' ','i','n',' ','r','e','v','e','r','s','e',' ','d','i','r','e','c','t','i','o','n','.','\n',
+' ',' ','E','S','C','-','n',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','R','e','p','e','a','t',' ','p','r','e','v','i','o','u','s',' ','s','e','a','r','c','h',',',' ','s','p','a','n','n','i','n','g',' ','f','i','l','e','s','.','\n',
+' ',' ','E','S','C','-','N',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','R','e','p','e','a','t',' ','p','r','e','v','i','o','u','s',' ','s','e','a','r','c','h',',',' ','r','e','v','e','r','s','e',' ','d','i','r','.',' ','&',' ','s','p','a','n','n','i','n','g',' ','f','i','l','e','s','.','\n',
+' ',' ','E','S','C','-','u',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','U','n','d','o',' ','(','t','o','g','g','l','e',')',' ','s','e','a','r','c','h',' ','h','i','g','h','l','i','g','h','t','i','n','g','.','\n',
+' ',' ','&','_','\b','p','_','\b','a','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','n',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','D','i','s','p','l','a','y',' ','o','n','l','y',' ','m','a','t','c','h','i','n','g',' ','l','i','n','e','s','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','S','e','a','r','c','h',' ','p','a','t','t','e','r','n','s',' ','m','a','y',' ','b','e',' ','m','o','d','i','f','i','e','d',' ','b','y',' ','o','n','e',' ','o','r',' ','m','o','r','e',' ','o','f',':','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','^','N',' ','o','r',' ','!',' ',' ','S','e','a','r','c','h',' ','f','o','r',' ','N','O','N','-','m','a','t','c','h','i','n','g',' ','l','i','n','e','s','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','^','E',' ','o','r',' ','*',' ',' ','S','e','a','r','c','h',' ','m','u','l','t','i','p','l','e',' ','f','i','l','e','s',' ','(','p','a','s','s',' ','t','h','r','u',' ','E','N','D',' ','O','F',' ','F','I','L','E',')','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','^','F',' ','o','r',' ','@',' ',' ','S','t','a','r','t',' ','s','e','a','r','c','h',' ','a','t',' ','F','I','R','S','T',' ','f','i','l','e',' ','(','f','o','r',' ','/',')',' ','o','r',' ','l','a','s','t',' ','f','i','l','e',' ','(','f','o','r',' ','?',')','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','^','K',' ',' ',' ',' ',' ',' ',' ','H','i','g','h','l','i','g','h','t',' ','m','a','t','c','h','e','s',',',' ','b','u','t',' ','d','o','n','\'','t',' ','m','o','v','e',' ','(','K','E','E','P',' ','p','o','s','i','t','i','o','n',')','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','^','R',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','u','s','e',' ','R','E','G','U','L','A','R',' ','E','X','P','R','E','S','S','I','O','N','S','.','\n',
+' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n',
+'\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','J','\b','J','U','\b','U','M','\b','M','P','\b','P','I','\b','I','N','\b','N','G','\b','G','\n',
+'\n',
+' ',' ','g',' ',' ','<',' ',' ','E','S','C','-','<',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','G','o',' ','t','o',' ','f','i','r','s','t',' ','l','i','n','e',' ','i','n',' ','f','i','l','e',' ','(','o','r',' ','l','i','n','e',' ','_','\b','N',')','.','\n',
+' ',' ','G',' ',' ','>',' ',' ','E','S','C','-','>',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','G','o',' ','t','o',' ','l','a','s','t',' ','l','i','n','e',' ','i','n',' ','f','i','l','e',' ','(','o','r',' ','l','i','n','e',' ','_','\b','N',')','.','\n',
+' ',' ','p',' ',' ','%',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','G','o',' ','t','o',' ','b','e','g','i','n','n','i','n','g',' ','o','f',' ','f','i','l','e',' ','(','o','r',' ','_','\b','N',' ','p','e','r','c','e','n','t',' ','i','n','t','o',' ','f','i','l','e',')','.','\n',
+' ',' ','t',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','G','o',' ','t','o',' ','t','h','e',' ','(','_','\b','N','-','t','h',')',' ','n','e','x','t',' ','t','a','g','.','\n',
+' ',' ','T',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','G','o',' ','t','o',' ','t','h','e',' ','(','_','\b','N','-','t','h',')',' ','p','r','e','v','i','o','u','s',' ','t','a','g','.','\n',
+' ',' ','{',' ',' ','(',' ',' ','[',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','F','i','n','d',' ','c','l','o','s','e',' ','b','r','a','c','k','e','t',' ','}',' ',')',' ',']','.','\n',
+' ',' ','}',' ',' ',')',' ',' ',']',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','F','i','n','d',' ','o','p','e','n',' ','b','r','a','c','k','e','t',' ','{',' ','(',' ','[','.','\n',
+' ',' ','E','S','C','-','^','F',' ','_','\b','<','_','\b','c','_','\b','1','_','\b','>',' ','_','\b','<','_','\b','c','_','\b','2','_','\b','>',' ',' ','*',' ',' ','F','i','n','d',' ','c','l','o','s','e',' ','b','r','a','c','k','e','t',' ','_','\b','<','_','\b','c','_','\b','2','_','\b','>','.','\n',
+' ',' ','E','S','C','-','^','B',' ','_','\b','<','_','\b','c','_','\b','1','_','\b','>',' ','_','\b','<','_','\b','c','_','\b','2','_','\b','>',' ',' ','*',' ',' ','F','i','n','d',' ','o','p','e','n',' ','b','r','a','c','k','e','t',' ','_','\b','<','_','\b','c','_','\b','1','_','\b','>',' ','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','E','a','c','h',' ','"','f','i','n','d',' ','c','l','o','s','e',' ','b','r','a','c','k','e','t','"',' ','c','o','m','m','a','n','d',' ','g','o','e','s',' ','f','o','r','w','a','r','d',' ','t','o',' ','t','h','e',' ','c','l','o','s','e',' ','b','r','a','c','k','e','t',' ','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','m','a','t','c','h','i','n','g',' ','t','h','e',' ','(','_','\b','N','-','t','h',')',' ','o','p','e','n',' ','b','r','a','c','k','e','t',' ','i','n',' ','t','h','e',' ','t','o','p',' ','l','i','n','e','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','E','a','c','h',' ','"','f','i','n','d',' ','o','p','e','n',' ','b','r','a','c','k','e','t','"',' ','c','o','m','m','a','n','d',' ','g','o','e','s',' ','b','a','c','k','w','a','r','d',' ','t','o',' ','t','h','e',' ','o','p','e','n',' ','b','r','a','c','k','e','t',' ','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','m','a','t','c','h','i','n','g',' ','t','h','e',' ','(','_','\b','N','-','t','h',')',' ','c','l','o','s','e',' ','b','r','a','c','k','e','t',' ','i','n',' ','t','h','e',' ','b','o','t','t','o','m',' ','l','i','n','e','.','\n',
+'\n',
+' ',' ','m','_','\b','<','_','\b','l','_','\b','e','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','M','a','r','k',' ','t','h','e',' ','c','u','r','r','e','n','t',' ','p','o','s','i','t','i','o','n',' ','w','i','t','h',' ','<','l','e','t','t','e','r','>','.','\n',
+' ',' ','\'','_','\b','<','_','\b','l','_','\b','e','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','G','o',' ','t','o',' ','a',' ','p','r','e','v','i','o','u','s','l','y',' ','m','a','r','k','e','d',' ','p','o','s','i','t','i','o','n','.','\n',
+' ',' ','\'','\'',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','G','o',' ','t','o',' ','t','h','e',' ','p','r','e','v','i','o','u','s',' ','p','o','s','i','t','i','o','n','.','\n',
+' ',' ','^','X','^','X',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','a','m','e',' ','a','s',' ','\'','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','A',' ','m','a','r','k',' ','i','s',' ','a','n','y',' ','u','p','p','e','r','-','c','a','s','e',' ','o','r',' ','l','o','w','e','r','-','c','a','s','e',' ','l','e','t','t','e','r','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','C','e','r','t','a','i','n',' ','m','a','r','k','s',' ','a','r','e',' ','p','r','e','d','e','f','i','n','e','d',':','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','^',' ',' ','m','e','a','n','s',' ',' ','b','e','g','i','n','n','i','n','g',' ','o','f',' ','t','h','e',' ','f','i','l','e','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','$',' ',' ','m','e','a','n','s',' ',' ','e','n','d',' ','o','f',' ','t','h','e',' ','f','i','l','e','\n',
+' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n',
+'\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','C','\b','C','H','\b','H','A','\b','A','N','\b','N','G','\b','G','I','\b','I','N','\b','N','G','\b','G',' ','F','\b','F','I','\b','I','L','\b','L','E','\b','E','S','\b','S','\n',
+'\n',
+' ',' ',':','e',' ','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','x','a','m','i','n','e',' ','a',' ','n','e','w',' ','f','i','l','e','.','\n',
+' ',' ','^','X','^','V',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','a','m','e',' ','a','s',' ',':','e','.','\n',
+' ',' ',':','n',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','E','x','a','m','i','n','e',' ','t','h','e',' ','(','_','\b','N','-','t','h',')',' ','n','e','x','t',' ','f','i','l','e',' ','f','r','o','m',' ','t','h','e',' ','c','o','m','m','a','n','d',' ','l','i','n','e','.','\n',
+' ',' ',':','p',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','E','x','a','m','i','n','e',' ','t','h','e',' ','(','_','\b','N','-','t','h',')',' ','p','r','e','v','i','o','u','s',' ','f','i','l','e',' ','f','r','o','m',' ','t','h','e',' ','c','o','m','m','a','n','d',' ','l','i','n','e','.','\n',
+' ',' ',':','x',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','E','x','a','m','i','n','e',' ','t','h','e',' ','f','i','r','s','t',' ','(','o','r',' ','_','\b','N','-','t','h',')',' ','f','i','l','e',' ','f','r','o','m',' ','t','h','e',' ','c','o','m','m','a','n','d',' ','l','i','n','e','.','\n',
+' ',' ',':','d',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','e','l','e','t','e',' ','t','h','e',' ','c','u','r','r','e','n','t',' ','f','i','l','e',' ','f','r','o','m',' ','t','h','e',' ','c','o','m','m','a','n','d',' ','l','i','n','e',' ','l','i','s','t','.','\n',
+' ',' ','=',' ',' ','^','G',' ',' ',':','f',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','P','r','i','n','t',' ','c','u','r','r','e','n','t',' ','f','i','l','e',' ','n','a','m','e','.','\n',
+' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n',
+'\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','M','\b','M','I','\b','I','S','\b','S','C','\b','C','E','\b','E','L','\b','L','L','\b','L','A','\b','A','N','\b','N','E','\b','E','O','\b','O','U','\b','U','S','\b','S',' ','C','\b','C','O','\b','O','M','\b','M','M','\b','M','A','\b','A','N','\b','N','D','\b','D','S','\b','S','\n',
+'\n',
+' ',' ','-','_','\b','<','_','\b','f','_','\b','l','_','\b','a','_','\b','g','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','T','o','g','g','l','e',' ','a',' ','c','o','m','m','a','n','d',' ','l','i','n','e',' ','o','p','t','i','o','n',' ','[','s','e','e',' ','O','P','T','I','O','N','S',' ','b','e','l','o','w',']','.','\n',
+' ',' ','-','-','_','\b','<','_','\b','n','_','\b','a','_','\b','m','_','\b','e','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','T','o','g','g','l','e',' ','a',' ','c','o','m','m','a','n','d',' ','l','i','n','e',' ','o','p','t','i','o','n',',',' ','b','y',' ','n','a','m','e','.','\n',
+' ',' ','_','_','\b','<','_','\b','f','_','\b','l','_','\b','a','_','\b','g','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','p','l','a','y',' ','t','h','e',' ','s','e','t','t','i','n','g',' ','o','f',' ','a',' ','c','o','m','m','a','n','d',' ','l','i','n','e',' ','o','p','t','i','o','n','.','\n',
+' ',' ','_','_','_','\b','<','_','\b','n','_','\b','a','_','\b','m','_','\b','e','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','p','l','a','y',' ','t','h','e',' ','s','e','t','t','i','n','g',' ','o','f',' ','a','n',' ','o','p','t','i','o','n',',',' ','b','y',' ','n','a','m','e','.','\n',
+' ',' ','+','_','\b','c','_','\b','m','_','\b','d',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','x','e','c','u','t','e',' ','t','h','e',' ','l','e','s','s',' ','c','m','d',' ','e','a','c','h',' ','t','i','m','e',' ','a',' ','n','e','w',' ','f','i','l','e',' ','i','s',' ','e','x','a','m','i','n','e','d','.','\n',
+'\n',
+' ',' ','!','_','\b','c','_','\b','o','_','\b','m','_','\b','m','_','\b','a','_','\b','n','_','\b','d',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','x','e','c','u','t','e',' ','t','h','e',' ','s','h','e','l','l',' ','c','o','m','m','a','n','d',' ','w','i','t','h',' ','$','S','H','E','L','L','.','\n',
+' ',' ','|','X','\b','X','_','\b','c','_','\b','o','_','\b','m','_','\b','m','_','\b','a','_','\b','n','_','\b','d',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','P','i','p','e',' ','f','i','l','e',' ','b','e','t','w','e','e','n',' ','c','u','r','r','e','n','t',' ','p','o','s',' ','&',' ','m','a','r','k',' ','X','\b','X',' ','t','o',' ','s','h','e','l','l',' ','c','o','m','m','a','n','d','.','\n',
+' ',' ','v',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','d','i','t',' ','t','h','e',' ','c','u','r','r','e','n','t',' ','f','i','l','e',' ','w','i','t','h',' ','$','V','I','S','U','A','L',' ','o','r',' ','$','E','D','I','T','O','R','.','\n',
+' ',' ','V',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','P','r','i','n','t',' ','v','e','r','s','i','o','n',' ','n','u','m','b','e','r',' ','o','f',' ','"','l','e','s','s','"','.','\n',
+' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n',
+'\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','O','\b','O','P','\b','P','T','\b','T','I','\b','I','O','\b','O','N','\b','N','S','\b','S','\n',
+'\n',
+' ',' ',' ',' ',' ',' ',' ',' ','M','o','s','t',' ','o','p','t','i','o','n','s',' ','m','a','y',' ','b','e',' ','c','h','a','n','g','e','d',' ','e','i','t','h','e','r',' ','o','n',' ','t','h','e',' ','c','o','m','m','a','n','d',' ','l','i','n','e',',','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','o','r',' ','f','r','o','m',' ','w','i','t','h','i','n',' ','l','e','s','s',' ','b','y',' ','u','s','i','n','g',' ','t','h','e',' ','-',' ','o','r',' ','-','-',' ','c','o','m','m','a','n','d','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','O','p','t','i','o','n','s',' ','m','a','y',' ','b','e',' ','g','i','v','e','n',' ','i','n',' ','o','n','e',' ','o','f',' ','t','w','o',' ','f','o','r','m','s',':',' ','e','i','t','h','e','r',' ','a',' ','s','i','n','g','l','e','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','c','h','a','r','a','c','t','e','r',' ','p','r','e','c','e','d','e','d',' ','b','y',' ','a',' ','-',',',' ','o','r',' ','a',' ','n','a','m','e',' ','p','r','e','c','e','e','d','e','d',' ','b','y',' ','-','-','.','\n',
+'\n',
+' ',' ','-','?',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','h','e','l','p','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','p','l','a','y',' ','h','e','l','p',' ','(','f','r','o','m',' ','c','o','m','m','a','n','d',' ','l','i','n','e',')','.','\n',
+' ',' ','-','a',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','s','e','a','r','c','h','-','s','k','i','p','-','s','c','r','e','e','n','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','F','o','r','w','a','r','d',' ','s','e','a','r','c','h',' ','s','k','i','p','s',' ','c','u','r','r','e','n','t',' ','s','c','r','e','e','n','.','\n',
+' ',' ','-','A',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','S','E','A','R','C','H','-','S','K','I','P','-','S','C','R','E','E','N','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','F','o','r','w','a','r','d',' ','s','e','a','r','c','h',' ','a','l','w','a','y','s',' ','s','k','i','p','s',' ','t','a','r','g','e','t',' ','l','i','n','e','.','\n',
+' ',' ','-','b',' ','[','_','\b','N',']',' ',' ','.','.','.','.',' ',' ','-','-','b','u','f','f','e','r','s','=','[','_','\b','N',']','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','N','u','m','b','e','r',' ','o','f',' ','b','u','f','f','e','r','s','.','\n',
+' ',' ','-','B',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','a','u','t','o','-','b','u','f','f','e','r','s','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','a','u','t','o','m','a','t','i','c','a','l','l','y',' ','a','l','l','o','c','a','t','e',' ','b','u','f','f','e','r','s',' ','f','o','r',' ','p','i','p','e','s','.','\n',
+' ',' ','-','c',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','c','l','e','a','r','-','s','c','r','e','e','n','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','R','e','p','a','i','n','t',' ','b','y',' ','c','l','e','a','r','i','n','g',' ','r','a','t','h','e','r',' ','t','h','a','n',' ','s','c','r','o','l','l','i','n','g','.','\n',
+' ',' ','-','d',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','d','u','m','b','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','u','m','b',' ','t','e','r','m','i','n','a','l','.','\n',
+' ',' ','-','D',' ','[','_','\b','x','_','\b','n','_','\b','.','_','\b','n',']',' ',' ','.',' ',' ','-','-','c','o','l','o','r','=','_','\b','x','_','\b','n','_','\b','.','_','\b','n','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','t',' ','s','c','r','e','e','n',' ','c','o','l','o','r','s','.',' ','(','M','S','-','D','O','S',' ','o','n','l','y',')','\n',
+' ',' ','-','e',' ',' ','-','E',' ',' ','.','.','.','.',' ',' ','-','-','q','u','i','t','-','a','t','-','e','o','f',' ',' ','-','-','Q','U','I','T','-','A','T','-','E','O','F','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','Q','u','i','t',' ','a','t',' ','e','n','d',' ','o','f',' ','f','i','l','e','.','\n',
+' ',' ','-','f',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','f','o','r','c','e','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','F','o','r','c','e',' ','o','p','e','n',' ','n','o','n','-','r','e','g','u','l','a','r',' ','f','i','l','e','s','.','\n',
+' ',' ','-','F',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','q','u','i','t','-','i','f','-','o','n','e','-','s','c','r','e','e','n','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','Q','u','i','t',' ','i','f',' ','e','n','t','i','r','e',' ','f','i','l','e',' ','f','i','t','s',' ','o','n',' ','f','i','r','s','t',' ','s','c','r','e','e','n','.','\n',
+' ',' ','-','g',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','h','i','l','i','t','e','-','s','e','a','r','c','h','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','H','i','g','h','l','i','g','h','t',' ','o','n','l','y',' ','l','a','s','t',' ','m','a','t','c','h',' ','f','o','r',' ','s','e','a','r','c','h','e','s','.','\n',
+' ',' ','-','G',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','H','I','L','I','T','E','-','S','E','A','R','C','H','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','h','i','g','h','l','i','g','h','t',' ','a','n','y',' ','m','a','t','c','h','e','s',' ','f','o','r',' ','s','e','a','r','c','h','e','s','.','\n',
+' ',' ','-','h',' ','[','_','\b','N',']',' ',' ','.','.','.','.',' ',' ','-','-','m','a','x','-','b','a','c','k','-','s','c','r','o','l','l','=','[','_','\b','N',']','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','B','a','c','k','w','a','r','d',' ','s','c','r','o','l','l',' ','l','i','m','i','t','.','\n',
+' ',' ','-','i',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','i','g','n','o','r','e','-','c','a','s','e','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','I','g','n','o','r','e',' ','c','a','s','e',' ','i','n',' ','s','e','a','r','c','h','e','s',' ','t','h','a','t',' ','d','o',' ','n','o','t',' ','c','o','n','t','a','i','n',' ','u','p','p','e','r','c','a','s','e','.','\n',
+' ',' ','-','I',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','I','G','N','O','R','E','-','C','A','S','E','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','I','g','n','o','r','e',' ','c','a','s','e',' ','i','n',' ','a','l','l',' ','s','e','a','r','c','h','e','s','.','\n',
+' ',' ','-','j',' ','[','_','\b','N',']',' ',' ','.','.','.','.',' ',' ','-','-','j','u','m','p','-','t','a','r','g','e','t','=','[','_','\b','N',']','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','c','r','e','e','n',' ','p','o','s','i','t','i','o','n',' ','o','f',' ','t','a','r','g','e','t',' ','l','i','n','e','s','.','\n',
+' ',' ','-','J',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','s','t','a','t','u','s','-','c','o','l','u','m','n','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','p','l','a','y',' ','a',' ','s','t','a','t','u','s',' ','c','o','l','u','m','n',' ','a','t',' ','l','e','f','t',' ','e','d','g','e',' ','o','f',' ','s','c','r','e','e','n','.','\n',
+' ',' ','-','k',' ','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']',' ',' ','.',' ',' ','-','-','l','e','s','s','k','e','y','-','f','i','l','e','=','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','U','s','e',' ','a',' ','l','e','s','s','k','e','y',' ','f','i','l','e','.','\n',
+' ',' ','-','K',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','q','u','i','t','-','o','n','-','i','n','t','r','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','x','i','t',' ','l','e','s','s',' ','i','n',' ','r','e','s','p','o','n','s','e',' ','t','o',' ','c','t','r','l','-','C','.','\n',
+' ',' ','-','L',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','n','o','-','l','e','s','s','o','p','e','n','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','I','g','n','o','r','e',' ','t','h','e',' ','L','E','S','S','O','P','E','N',' ','e','n','v','i','r','o','n','m','e','n','t',' ','v','a','r','i','a','b','l','e','.','\n',
+' ',' ','-','m',' ',' ','-','M',' ',' ','.','.','.','.',' ',' ','-','-','l','o','n','g','-','p','r','o','m','p','t',' ',' ','-','-','L','O','N','G','-','P','R','O','M','P','T','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','t',' ','p','r','o','m','p','t',' ','s','t','y','l','e','.','\n',
+' ',' ','-','n',' ',' ','-','N',' ',' ','.','.','.','.',' ',' ','-','-','l','i','n','e','-','n','u','m','b','e','r','s',' ',' ','-','-','L','I','N','E','-','N','U','M','B','E','R','S','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','u','s','e',' ','l','i','n','e',' ','n','u','m','b','e','r','s','.','\n',
+' ',' ','-','o',' ','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']',' ',' ','.',' ',' ','-','-','l','o','g','-','f','i','l','e','=','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','C','o','p','y',' ','t','o',' ','l','o','g',' ','f','i','l','e',' ','(','s','t','a','n','d','a','r','d',' ','i','n','p','u','t',' ','o','n','l','y',')','.','\n',
+' ',' ','-','O',' ','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']',' ',' ','.',' ',' ','-','-','L','O','G','-','F','I','L','E','=','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','C','o','p','y',' ','t','o',' ','l','o','g',' ','f','i','l','e',' ','(','u','n','c','o','n','d','i','t','i','o','n','a','l','l','y',' ','o','v','e','r','w','r','i','t','e',')','.','\n',
+' ',' ','-','p',' ','[','_','\b','p','_','\b','a','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','n',']',' ',' ','-','-','p','a','t','t','e','r','n','=','[','_','\b','p','_','\b','a','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','n',']','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','t','a','r','t',' ','a','t',' ','p','a','t','t','e','r','n',' ','(','f','r','o','m',' ','c','o','m','m','a','n','d',' ','l','i','n','e',')','.','\n',
+' ',' ','-','P',' ','[','_','\b','p','_','\b','r','_','\b','o','_','\b','m','_','\b','p','_','\b','t',']',' ',' ',' ','-','-','p','r','o','m','p','t','=','[','_','\b','p','_','\b','r','_','\b','o','_','\b','m','_','\b','p','_','\b','t',']','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','e','f','i','n','e',' ','n','e','w',' ','p','r','o','m','p','t','.','\n',
+' ',' ','-','q',' ',' ','-','Q',' ',' ','.','.','.','.',' ',' ','-','-','q','u','i','e','t',' ',' ','-','-','Q','U','I','E','T',' ',' ','-','-','s','i','l','e','n','t',' ','-','-','S','I','L','E','N','T','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','Q','u','i','e','t',' ','t','h','e',' ','t','e','r','m','i','n','a','l',' ','b','e','l','l','.','\n',
+' ',' ','-','r',' ',' ','-','R',' ',' ','.','.','.','.',' ',' ','-','-','r','a','w','-','c','o','n','t','r','o','l','-','c','h','a','r','s',' ',' ','-','-','R','A','W','-','C','O','N','T','R','O','L','-','C','H','A','R','S','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','O','u','t','p','u','t',' ','"','r','a','w','"',' ','c','o','n','t','r','o','l',' ','c','h','a','r','a','c','t','e','r','s','.','\n',
+' ',' ','-','s',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','s','q','u','e','e','z','e','-','b','l','a','n','k','-','l','i','n','e','s','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','q','u','e','e','z','e',' ','m','u','l','t','i','p','l','e',' ','b','l','a','n','k',' ','l','i','n','e','s','.','\n',
+' ',' ','-','S',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','c','h','o','p','-','l','o','n','g','-','l','i','n','e','s','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','C','h','o','p',' ','l','o','n','g',' ','l','i','n','e','s','.','\n',
+' ',' ','-','t',' ','[','_','\b','t','_','\b','a','_','\b','g',']',' ',' ','.','.',' ',' ','-','-','t','a','g','=','[','_','\b','t','_','\b','a','_','\b','g',']','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','F','i','n','d',' ','a',' ','t','a','g','.','\n',
+' ',' ','-','T',' ','[','_','\b','t','_','\b','a','_','\b','g','_','\b','s','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']',' ','-','-','t','a','g','-','f','i','l','e','=','[','_','\b','t','_','\b','a','_','\b','g','_','\b','s','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','U','s','e',' ','a','n',' ','a','l','t','e','r','n','a','t','e',' ','t','a','g','s',' ','f','i','l','e','.','\n',
+' ',' ','-','u',' ',' ','-','U',' ',' ','.','.','.','.',' ',' ','-','-','u','n','d','e','r','l','i','n','e','-','s','p','e','c','i','a','l',' ',' ','-','-','U','N','D','E','R','L','I','N','E','-','S','P','E','C','I','A','L','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','C','h','a','n','g','e',' ','h','a','n','d','l','i','n','g',' ','o','f',' ','b','a','c','k','s','p','a','c','e','s','.','\n',
+' ',' ','-','V',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','v','e','r','s','i','o','n','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','p','l','a','y',' ','t','h','e',' ','v','e','r','s','i','o','n',' ','n','u','m','b','e','r',' ','o','f',' ','"','l','e','s','s','"','.','\n',
+' ',' ','-','w',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','h','i','l','i','t','e','-','u','n','r','e','a','d','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','H','i','g','h','l','i','g','h','t',' ','f','i','r','s','t',' ','n','e','w',' ','l','i','n','e',' ','a','f','t','e','r',' ','f','o','r','w','a','r','d','-','s','c','r','e','e','n','.','\n',
+' ',' ','-','W',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','H','I','L','I','T','E','-','U','N','R','E','A','D','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','H','i','g','h','l','i','g','h','t',' ','f','i','r','s','t',' ','n','e','w',' ','l','i','n','e',' ','a','f','t','e','r',' ','a','n','y',' ','f','o','r','w','a','r','d',' ','m','o','v','e','m','e','n','t','.','\n',
+' ',' ','-','x',' ','[','_','\b','N','[',',','.','.','.',']',']',' ',' ','-','-','t','a','b','s','=','[','_','\b','N','[',',','.','.','.',']',']','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','t',' ','t','a','b',' ','s','t','o','p','s','.','\n',
+' ',' ','-','X',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','n','o','-','i','n','i','t','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','u','s','e',' ','t','e','r','m','c','a','p',' ','i','n','i','t','/','d','e','i','n','i','t',' ','s','t','r','i','n','g','s','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','n','o','-','k','e','y','p','a','d','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','u','s','e',' ','t','e','r','m','c','a','p',' ','k','e','y','p','a','d',' ','i','n','i','t','/','d','e','i','n','i','t',' ','s','t','r','i','n','g','s','.','\n',
+' ',' ','-','y',' ','[','_','\b','N',']',' ',' ','.','.','.','.',' ',' ','-','-','m','a','x','-','f','o','r','w','-','s','c','r','o','l','l','=','[','_','\b','N',']','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','F','o','r','w','a','r','d',' ','s','c','r','o','l','l',' ','l','i','m','i','t','.','\n',
+' ',' ','-','z',' ','[','_','\b','N',']',' ',' ','.','.','.','.',' ',' ','-','-','w','i','n','d','o','w','=','[','_','\b','N',']','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','t',' ','s','i','z','e',' ','o','f',' ','w','i','n','d','o','w','.','\n',
+' ',' ','-','"',' ','[','_','\b','c','[','_','\b','c',']',']',' ',' ','.',' ',' ','-','-','q','u','o','t','e','s','=','[','_','\b','c','[','_','\b','c',']',']','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','t',' ','s','h','e','l','l',' ','q','u','o','t','e',' ','c','h','a','r','a','c','t','e','r','s','.','\n',
+' ',' ','-','~',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','t','i','l','d','e','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','d','i','s','p','l','a','y',' ','t','i','l','d','e','s',' ','a','f','t','e','r',' ','e','n','d',' ','o','f',' ','f','i','l','e','.','\n',
+' ',' ','-','#',' ','[','_','\b','N',']',' ',' ','.','.','.','.',' ',' ','-','-','s','h','i','f','t','=','[','_','\b','N',']','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','H','o','r','i','z','o','n','t','a','l',' ','s','c','r','o','l','l',' ','a','m','o','u','n','t',' ','(','0',' ','=',' ','o','n','e',' ','h','a','l','f',' ','s','c','r','e','e','n',' ','w','i','d','t','h',')','\n',
+' ',' ',' ',' ',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','n','o','-','k','e','y','p','a','d','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','s','e','n','d',' ','k','e','y','p','a','d',' ','i','n','i','t','/','d','e','i','n','i','t',' ','s','e','q','u','e','n','c','e','.','\n',
+' ',' ',' ',' ',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','f','o','l','l','o','w','-','n','a','m','e','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','T','h','e',' ','F',' ','c','o','m','m','a','n','d',' ','c','h','a','n','g','e','s',' ','f','i','l','e','s',' ','i','f',' ','t','h','e',' ','i','n','p','u','t',' ','f','i','l','e',' ','i','s',' ','r','e','n','a','m','e','d','.','\n',
+'\n',
+'\n',
+' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n',
+'\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','L','\b','L','I','\b','I','N','\b','N','E','\b','E',' ','E','\b','E','D','\b','D','I','\b','I','T','\b','T','I','\b','I','N','\b','N','G','\b','G','\n',
+'\n',
+' ',' ',' ',' ',' ',' ',' ',' ','T','h','e','s','e',' ','k','e','y','s',' ','c','a','n',' ','b','e',' ','u','s','e','d',' ','t','o',' ','e','d','i','t',' ','t','e','x','t',' ','b','e','i','n','g',' ','e','n','t','e','r','e','d',' ','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','o','n',' ','t','h','e',' ','"','c','o','m','m','a','n','d',' ','l','i','n','e','"',' ','a','t',' ','t','h','e',' ','b','o','t','t','o','m',' ','o','f',' ','t','h','e',' ','s','c','r','e','e','n','.','\n',
+'\n',
+' ','R','i','g','h','t','A','r','r','o','w',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','S','C','-','l',' ',' ',' ',' ',' ','M','o','v','e',' ','c','u','r','s','o','r',' ','r','i','g','h','t',' ','o','n','e',' ','c','h','a','r','a','c','t','e','r','.','\n',
+' ','L','e','f','t','A','r','r','o','w',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','S','C','-','h',' ',' ',' ',' ',' ','M','o','v','e',' ','c','u','r','s','o','r',' ','l','e','f','t',' ','o','n','e',' ','c','h','a','r','a','c','t','e','r','.','\n',
+' ','C','N','T','L','-','R','i','g','h','t','A','r','r','o','w',' ',' ','E','S','C','-','R','i','g','h','t','A','r','r','o','w',' ',' ','E','S','C','-','w',' ',' ',' ',' ',' ','M','o','v','e',' ','c','u','r','s','o','r',' ','r','i','g','h','t',' ','o','n','e',' ','w','o','r','d','.','\n',
+' ','C','N','T','L','-','L','e','f','t','A','r','r','o','w',' ',' ',' ','E','S','C','-','L','e','f','t','A','r','r','o','w',' ',' ',' ','E','S','C','-','b',' ',' ',' ',' ',' ','M','o','v','e',' ','c','u','r','s','o','r',' ','l','e','f','t',' ','o','n','e',' ','w','o','r','d','.','\n',
+' ','H','O','M','E',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','S','C','-','0',' ',' ',' ',' ',' ','M','o','v','e',' ','c','u','r','s','o','r',' ','t','o',' ','s','t','a','r','t',' ','o','f',' ','l','i','n','e','.','\n',
+' ','E','N','D',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','S','C','-','$',' ',' ',' ',' ',' ','M','o','v','e',' ','c','u','r','s','o','r',' ','t','o',' ','e','n','d',' ','o','f',' ','l','i','n','e','.','\n',
+' ','B','A','C','K','S','P','A','C','E',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','e','l','e','t','e',' ','c','h','a','r',' ','t','o',' ','l','e','f','t',' ','o','f',' ','c','u','r','s','o','r','.','\n',
+' ','D','E','L','E','T','E',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','S','C','-','x',' ',' ',' ',' ',' ','D','e','l','e','t','e',' ','c','h','a','r',' ','u','n','d','e','r',' ','c','u','r','s','o','r','.','\n',
+' ','C','N','T','L','-','B','A','C','K','S','P','A','C','E',' ',' ',' ','E','S','C','-','B','A','C','K','S','P','A','C','E',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','e','l','e','t','e',' ','w','o','r','d',' ','t','o',' ','l','e','f','t',' ','o','f',' ','c','u','r','s','o','r','.','\n',
+' ','C','N','T','L','-','D','E','L','E','T','E',' ',' ',' ',' ',' ',' ','E','S','C','-','D','E','L','E','T','E',' ',' ',' ',' ',' ',' ','E','S','C','-','X',' ',' ',' ',' ',' ','D','e','l','e','t','e',' ','w','o','r','d',' ','u','n','d','e','r',' ','c','u','r','s','o','r','.','\n',
+' ','C','N','T','L','-','U',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','S','C',' ','(','M','S','-','D','O','S',' ','o','n','l','y',')',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','e','l','e','t','e',' ','e','n','t','i','r','e',' ','l','i','n','e','.','\n',
+' ','U','p','A','r','r','o','w',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','S','C','-','k',' ',' ',' ',' ',' ','R','e','t','r','i','e','v','e',' ','p','r','e','v','i','o','u','s',' ','c','o','m','m','a','n','d',' ','l','i','n','e','.','\n',
+' ','D','o','w','n','A','r','r','o','w',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','S','C','-','j',' ',' ',' ',' ',' ','R','e','t','r','i','e','v','e',' ','n','e','x','t',' ','c','o','m','m','a','n','d',' ','l','i','n','e','.','\n',
+' ','T','A','B',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','C','o','m','p','l','e','t','e',' ','f','i','l','e','n','a','m','e',' ','&',' ','c','y','c','l','e','.','\n',
+' ','S','H','I','F','T','-','T','A','B',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','S','C','-','T','A','B',' ',' ',' ','C','o','m','p','l','e','t','e',' ','f','i','l','e','n','a','m','e',' ','&',' ','r','e','v','e','r','s','e',' ','c','y','c','l','e','.','\n',
+' ','C','N','T','L','-','L',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','C','o','m','p','l','e','t','e',' ','f','i','l','e','n','a','m','e',',',' ','l','i','s','t',' ','a','l','l','.','\n',
+'\n',
+'\n',
+ 0 };
+constant int size_helpdata = sizeof(helpdata) - 1;
diff --git a/thirdparty/less/ifile.c b/thirdparty/less/ifile.c
new file mode 100644 (file)
index 0000000..971e3b5
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * An IFILE represents an input file.
+ *
+ * It is actually a pointer to an ifile structure,
+ * but is opaque outside this module.
+ * Ifile structures are kept in a linked list in the order they 
+ * appear on the command line.
+ * Any new file which does not already appear in the list is
+ * inserted after the current file.
+ */
+
+#include "less.h"
+
+extern IFILE   curr_ifile;
+
+struct ifile {
+       struct ifile *h_next;           /* Links for command line list */
+       struct ifile *h_prev;
+       char *h_filename;               /* Name of the file */
+       void *h_filestate;              /* File state (used in ch.c) */
+       int h_index;                    /* Index within command line list */
+       int h_hold;                     /* Hold count */
+       char h_opened;                  /* Has this ifile been opened? */
+       struct scrpos h_scrpos;         /* Saved position within the file */
+};
+
+/*
+ * Convert an IFILE (external representation)
+ * to a struct file (internal representation), and vice versa.
+ */
+#define int_ifile(h)   ((struct ifile *)(h))
+#define ext_ifile(h)   ((IFILE)(h))
+
+/*
+ * Anchor for linked list.
+ */
+static struct ifile anchor = { &anchor, &anchor, NULL, NULL, 0, 0, '\0',
+                               { NULL_POSITION, 0 } };
+static int ifiles = 0;
+
+       static void
+incr_index(p, incr)
+       register struct ifile *p;
+       int incr;
+{
+       for (;  p != &anchor;  p = p->h_next)
+               p->h_index += incr;
+}
+
+/*
+ * Link an ifile into the ifile list.
+ */
+       static void
+link_ifile(p, prev)
+       struct ifile *p;
+       struct ifile *prev;
+{
+       /*
+        * Link into list.
+        */
+       if (prev == NULL)
+               prev = &anchor;
+       p->h_next = prev->h_next;
+       p->h_prev = prev;
+       prev->h_next->h_prev = p;
+       prev->h_next = p;
+       /*
+        * Calculate index for the new one,
+        * and adjust the indexes for subsequent ifiles in the list.
+        */
+       p->h_index = prev->h_index + 1;
+       incr_index(p->h_next, 1);
+       ifiles++;
+}
+       
+/*
+ * Unlink an ifile from the ifile list.
+ */
+       static void
+unlink_ifile(p)
+       struct ifile *p;
+{
+       p->h_next->h_prev = p->h_prev;
+       p->h_prev->h_next = p->h_next;
+       incr_index(p->h_next, -1);
+       ifiles--;
+}
+
+/*
+ * Allocate a new ifile structure and stick a filename in it.
+ * It should go after "prev" in the list
+ * (or at the beginning of the list if "prev" is NULL).
+ * Return a pointer to the new ifile structure.
+ */
+       static struct ifile *
+new_ifile(filename, prev)
+       char *filename;
+       struct ifile *prev;
+{
+       register struct ifile *p;
+
+       /*
+        * Allocate and initialize structure.
+        */
+       p = (struct ifile *) ecalloc(1, sizeof(struct ifile));
+       p->h_filename = save(filename);
+       p->h_scrpos.pos = NULL_POSITION;
+       p->h_opened = 0;
+       p->h_hold = 0;
+       p->h_filestate = NULL;
+       link_ifile(p, prev);
+       return (p);
+}
+
+/*
+ * Delete an existing ifile structure.
+ */
+       public void
+del_ifile(h)
+       IFILE h;
+{
+       register struct ifile *p;
+
+       if (h == NULL_IFILE)
+               return;
+       /*
+        * If the ifile we're deleting is the currently open ifile,
+        * move off it.
+        */
+       unmark(h);
+       if (h == curr_ifile)
+               curr_ifile = getoff_ifile(curr_ifile);
+       p = int_ifile(h);
+       unlink_ifile(p);
+       free(p->h_filename);
+       free(p);
+}
+
+/*
+ * Get the ifile after a given one in the list.
+ */
+       public IFILE
+next_ifile(h)
+       IFILE h;
+{
+       register struct ifile *p;
+
+       p = (h == NULL_IFILE) ? &anchor : int_ifile(h);
+       if (p->h_next == &anchor)
+               return (NULL_IFILE);
+       return (ext_ifile(p->h_next));
+}
+
+/*
+ * Get the ifile before a given one in the list.
+ */
+       public IFILE
+prev_ifile(h)
+       IFILE h;
+{
+       register struct ifile *p;
+
+       p = (h == NULL_IFILE) ? &anchor : int_ifile(h);
+       if (p->h_prev == &anchor)
+               return (NULL_IFILE);
+       return (ext_ifile(p->h_prev));
+}
+
+/*
+ * Return a different ifile from the given one.
+ */
+       public IFILE
+getoff_ifile(ifile)
+       IFILE ifile;
+{
+       IFILE newifile;
+       
+       if ((newifile = prev_ifile(ifile)) != NULL_IFILE)
+               return (newifile);
+       if ((newifile = next_ifile(ifile)) != NULL_IFILE)
+               return (newifile);
+       return (NULL_IFILE);
+}
+
+/*
+ * Return the number of ifiles.
+ */
+       public int
+nifile()
+{
+       return (ifiles);
+}
+
+/*
+ * Find an ifile structure, given a filename.
+ */
+       static struct ifile *
+find_ifile(filename)
+       char *filename;
+{
+       register struct ifile *p;
+
+       for (p = anchor.h_next;  p != &anchor;  p = p->h_next)
+               if (strcmp(filename, p->h_filename) == 0)
+                       return (p);
+       return (NULL);
+}
+
+/*
+ * Get the ifile associated with a filename.
+ * If the filename has not been seen before,
+ * insert the new ifile after "prev" in the list.
+ */
+       public IFILE
+get_ifile(filename, prev)
+       char *filename;
+       IFILE prev;
+{
+       register struct ifile *p;
+
+       if ((p = find_ifile(filename)) == NULL)
+               p = new_ifile(filename, int_ifile(prev));
+       return (ext_ifile(p));
+}
+
+/*
+ * Get the filename associated with a ifile.
+ */
+       public char *
+get_filename(ifile)
+       IFILE ifile;
+{
+       if (ifile == NULL)
+               return (NULL);
+       return (int_ifile(ifile)->h_filename);
+}
+
+/*
+ * Get the index of the file associated with a ifile.
+ */
+       public int
+get_index(ifile)
+       IFILE ifile;
+{
+       return (int_ifile(ifile)->h_index); 
+}
+
+/*
+ * Save the file position to be associated with a given file.
+ */
+       public void
+store_pos(ifile, scrpos)
+       IFILE ifile;
+       struct scrpos *scrpos;
+{
+       int_ifile(ifile)->h_scrpos = *scrpos;
+}
+
+/*
+ * Recall the file position associated with a file.
+ * If no position has been associated with the file, return NULL_POSITION.
+ */
+       public void
+get_pos(ifile, scrpos)
+       IFILE ifile;
+       struct scrpos *scrpos;
+{
+       *scrpos = int_ifile(ifile)->h_scrpos;
+}
+
+/*
+ * Mark the ifile as "opened".
+ */
+       public void
+set_open(ifile)
+       IFILE ifile;
+{
+       int_ifile(ifile)->h_opened = 1;
+}
+
+/*
+ * Return whether the ifile has been opened previously.
+ */
+       public int
+opened(ifile)
+       IFILE ifile;
+{
+       return (int_ifile(ifile)->h_opened);
+}
+
+       public void
+hold_ifile(ifile, incr)
+       IFILE ifile;
+       int incr;
+{
+       int_ifile(ifile)->h_hold += incr;
+}
+
+       public int
+held_ifile(ifile)
+       IFILE ifile;
+{
+       return (int_ifile(ifile)->h_hold);
+}
+
+       public void *
+get_filestate(ifile)
+       IFILE ifile;
+{
+       return (int_ifile(ifile)->h_filestate);
+}
+
+       public void
+set_filestate(ifile, filestate)
+       IFILE ifile;
+       void *filestate;
+{
+       int_ifile(ifile)->h_filestate = filestate;
+}
+
+#if 0
+       public void
+if_dump()
+{
+       register struct ifile *p;
+
+       for (p = anchor.h_next;  p != &anchor;  p = p->h_next)
+       {
+               printf("%x: %d. <%s> pos %d,%x\n", 
+                       p, p->h_index, p->h_filename, 
+                       p->h_scrpos.ln, p->h_scrpos.pos);
+               ch_dump(p->h_filestate);
+       }
+}
+#endif
diff --git a/thirdparty/less/input.c b/thirdparty/less/input.c
new file mode 100644 (file)
index 0000000..b82868b
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * High level routines dealing with getting lines of input 
+ * from the file being viewed.
+ *
+ * When we speak of "lines" here, we mean PRINTABLE lines;
+ * lines processed with respect to the screen width.
+ * We use the term "raw line" to refer to lines simply
+ * delimited by newlines; not processed with respect to screen width.
+ */
+
+#include "less.h"
+
+extern int squeeze;
+extern int chopline;
+extern int hshift;
+extern int quit_if_one_screen;
+extern int sigs;
+extern int ignore_eoi;
+extern int status_col;
+extern POSITION start_attnpos;
+extern POSITION end_attnpos;
+#if HILITE_SEARCH
+extern int hilite_search;
+extern int size_linebuf;
+#endif
+
+/*
+ * Get the next line.
+ * A "current" position is passed and a "new" position is returned.
+ * The current position is the position of the first character of
+ * a line.  The new position is the position of the first character
+ * of the NEXT line.  The line obtained is the line starting at curr_pos.
+ */
+       public POSITION
+forw_line(curr_pos)
+       POSITION curr_pos;
+{
+       POSITION base_pos;
+       POSITION new_pos;
+       register int c;
+       int blankline;
+       int endline;
+       int backchars;
+
+get_forw_line:
+       if (curr_pos == NULL_POSITION)
+       {
+               null_line();
+               return (NULL_POSITION);
+       }
+#if HILITE_SEARCH
+       if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
+               /*
+                * If we are ignoring EOI (command F), only prepare
+                * one line ahead, to avoid getting stuck waiting for
+                * slow data without displaying the data we already have.
+                * If we're not ignoring EOI, we *could* do the same, but
+                * for efficiency we prepare several lines ahead at once.
+                */
+               prep_hilite(curr_pos, curr_pos + 3*size_linebuf, 
+                               ignore_eoi ? 1 : -1);
+#endif
+       if (ch_seek(curr_pos))
+       {
+               null_line();
+               return (NULL_POSITION);
+       }
+
+       /*
+        * Step back to the beginning of the line.
+        */
+       base_pos = curr_pos;
+       for (;;)
+       {
+               if (ABORT_SIGS())
+               {
+                       null_line();
+                       return (NULL_POSITION);
+               }
+               c = ch_back_get();
+               if (c == EOI)
+                       break;
+               if (c == '\n')
+               {
+                       (void) ch_forw_get();
+                       break;
+               }
+               --base_pos;
+       }
+
+       /*
+        * Read forward again to the position we should start at.
+        */
+       prewind();
+       plinenum(base_pos);
+       (void) ch_seek(base_pos);
+       new_pos = base_pos;
+       while (new_pos < curr_pos)
+       {
+               if (ABORT_SIGS())
+               {
+                       null_line();
+                       return (NULL_POSITION);
+               }
+               c = ch_forw_get();
+               backchars = pappend(c, new_pos);
+               new_pos++;
+               if (backchars > 0)
+               {
+                       pshift_all();
+                       new_pos -= backchars;
+                       while (--backchars >= 0)
+                               (void) ch_back_get();
+               }
+       }
+       (void) pflushmbc();
+       pshift_all();
+
+       /*
+        * Read the first character to display.
+        */
+       c = ch_forw_get();
+       if (c == EOI)
+       {
+               null_line();
+               return (NULL_POSITION);
+       }
+       blankline = (c == '\n' || c == '\r');
+
+       /*
+        * Read each character in the line and append to the line buffer.
+        */
+       for (;;)
+       {
+               if (ABORT_SIGS())
+               {
+                       null_line();
+                       return (NULL_POSITION);
+               }
+               if (c == '\n' || c == EOI)
+               {
+                       /*
+                        * End of the line.
+                        */
+                       backchars = pflushmbc();
+                       new_pos = ch_tell();
+                       if (backchars > 0 && !chopline && hshift == 0)
+                       {
+                               new_pos -= backchars + 1;
+                               endline = FALSE;
+                       } else
+                               endline = TRUE;
+                       break;
+               }
+               if (c != '\r')
+                       blankline = 0;
+
+               /*
+                * Append the char to the line and get the next char.
+                */
+               backchars = pappend(c, ch_tell()-1);
+               if (backchars > 0)
+               {
+                       /*
+                        * The char won't fit in the line; the line
+                        * is too long to print in the screen width.
+                        * End the line here.
+                        */
+                       if (chopline || hshift > 0)
+                       {
+                               do
+                               {
+                                       if (ABORT_SIGS())
+                                       {
+                                               null_line();
+                                               return (NULL_POSITION);
+                                       }
+                                       c = ch_forw_get();
+                               } while (c != '\n' && c != EOI);
+                               new_pos = ch_tell();
+                               endline = TRUE;
+                               quit_if_one_screen = FALSE;
+                       } else
+                       {
+                               new_pos = ch_tell() - backchars;
+                               endline = FALSE;
+                       }
+                       break;
+               }
+               c = ch_forw_get();
+       }
+
+       pdone(endline, 1);
+
+#if HILITE_SEARCH
+       if (is_filtered(base_pos))
+       {
+               /*
+                * We don't want to display this line.
+                * Get the next line.
+                */
+               curr_pos = new_pos;
+               goto get_forw_line;
+       }
+
+       if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL))
+               set_status_col('*');
+#endif
+
+       if (squeeze && blankline)
+       {
+               /*
+                * This line is blank.
+                * Skip down to the last contiguous blank line
+                * and pretend it is the one which we are returning.
+                */
+               while ((c = ch_forw_get()) == '\n' || c == '\r')
+                       if (ABORT_SIGS())
+                       {
+                               null_line();
+                               return (NULL_POSITION);
+                       }
+               if (c != EOI)
+                       (void) ch_back_get();
+               new_pos = ch_tell();
+       }
+
+       return (new_pos);
+}
+
+/*
+ * Get the previous line.
+ * A "current" position is passed and a "new" position is returned.
+ * The current position is the position of the first character of
+ * a line.  The new position is the position of the first character
+ * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
+ */
+       public POSITION
+back_line(curr_pos)
+       POSITION curr_pos;
+{
+       POSITION new_pos, begin_new_pos, base_pos;
+       int c;
+       int endline;
+       int backchars;
+
+get_back_line:
+       if (curr_pos == NULL_POSITION || curr_pos <= ch_zero())
+       {
+               null_line();
+               return (NULL_POSITION);
+       }
+#if HILITE_SEARCH
+       if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
+               prep_hilite((curr_pos < 3*size_linebuf) ? 
+                               0 : curr_pos - 3*size_linebuf, curr_pos, -1);
+#endif
+       if (ch_seek(curr_pos-1))
+       {
+               null_line();
+               return (NULL_POSITION);
+       }
+
+       if (squeeze)
+       {
+               /*
+                * Find out if the "current" line was blank.
+                */
+               (void) ch_forw_get();    /* Skip the newline */
+               c = ch_forw_get();       /* First char of "current" line */
+               (void) ch_back_get();    /* Restore our position */
+               (void) ch_back_get();
+
+               if (c == '\n' || c == '\r')
+               {
+                       /*
+                        * The "current" line was blank.
+                        * Skip over any preceding blank lines,
+                        * since we skipped them in forw_line().
+                        */
+                       while ((c = ch_back_get()) == '\n' || c == '\r')
+                               if (ABORT_SIGS())
+                               {
+                                       null_line();
+                                       return (NULL_POSITION);
+                               }
+                       if (c == EOI)
+                       {
+                               null_line();
+                               return (NULL_POSITION);
+                       }
+                       (void) ch_forw_get();
+               }
+       }
+
+       /*
+        * Scan backwards until we hit the beginning of the line.
+        */
+       for (;;)
+       {
+               if (ABORT_SIGS())
+               {
+                       null_line();
+                       return (NULL_POSITION);
+               }
+               c = ch_back_get();
+               if (c == '\n')
+               {
+                       /*
+                        * This is the newline ending the previous line.
+                        * We have hit the beginning of the line.
+                        */
+                       base_pos = ch_tell() + 1;
+                       break;
+               }
+               if (c == EOI)
+               {
+                       /*
+                        * We have hit the beginning of the file.
+                        * This must be the first line in the file.
+                        * This must, of course, be the beginning of the line.
+                        */
+                       base_pos = ch_tell();
+                       break;
+               }
+       }
+
+       /*
+        * Now scan forwards from the beginning of this line.
+        * We keep discarding "printable lines" (based on screen width)
+        * until we reach the curr_pos.
+        *
+        * {{ This algorithm is pretty inefficient if the lines
+        *    are much longer than the screen width, 
+        *    but I don't know of any better way. }}
+        */
+       new_pos = base_pos;
+       if (ch_seek(new_pos))
+       {
+               null_line();
+               return (NULL_POSITION);
+       }
+       endline = FALSE;
+       prewind();
+       plinenum(new_pos);
+    loop:
+       begin_new_pos = new_pos;
+       (void) ch_seek(new_pos);
+
+       do
+       {
+               c = ch_forw_get();
+               if (c == EOI || ABORT_SIGS())
+               {
+                       null_line();
+                       return (NULL_POSITION);
+               }
+               new_pos++;
+               if (c == '\n')
+               {
+                       backchars = pflushmbc();
+                       if (backchars > 0 && !chopline && hshift == 0)
+                       {
+                               backchars++;
+                               goto shift;
+                       }
+                       endline = TRUE;
+                       break;
+               }
+               backchars = pappend(c, ch_tell()-1);
+               if (backchars > 0)
+               {
+                       /*
+                        * Got a full printable line, but we haven't
+                        * reached our curr_pos yet.  Discard the line
+                        * and start a new one.
+                        */
+                       if (chopline || hshift > 0)
+                       {
+                               endline = TRUE;
+                               quit_if_one_screen = FALSE;
+                               break;
+                       }
+               shift:
+                       pshift_all();
+                       while (backchars-- > 0)
+                       {
+                               (void) ch_back_get();
+                               new_pos--;
+                       }
+                       goto loop;
+               }
+       } while (new_pos < curr_pos);
+
+       pdone(endline, 0);
+
+#if HILITE_SEARCH
+       if (is_filtered(base_pos))
+       {
+               /*
+                * We don't want to display this line.
+                * Get the previous line.
+                */
+               curr_pos = begin_new_pos;
+               goto get_back_line;
+       }
+
+       if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL))
+               set_status_col('*');
+#endif
+
+       return (begin_new_pos);
+}
+
+/*
+ * Set attnpos.
+ */
+       public void
+set_attnpos(pos)
+       POSITION pos;
+{
+       int c;
+
+       if (pos != NULL_POSITION)
+       {
+               if (ch_seek(pos))
+                       return;
+               for (;;)
+               {
+                       c = ch_forw_get();
+                       if (c == EOI)
+                               return;
+                       if (c != '\n' && c != '\r')
+                               break;
+                       pos++;
+               }
+       }
+       start_attnpos = pos;
+       for (;;)
+       {
+               c = ch_forw_get();
+               pos++;
+               if (c == EOI || c == '\n' || c == '\r')
+                       break;
+       }
+       end_attnpos = pos;
+}
diff --git a/thirdparty/less/jump.c b/thirdparty/less/jump.c
new file mode 100644 (file)
index 0000000..d7ec770
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Routines which jump to a new location in the file.
+ */
+
+#include "less.h"
+#include "position.h"
+
+extern int jump_sline;
+extern int squished;
+extern int screen_trashed;
+extern int sc_width, sc_height;
+extern int show_attn;
+extern int top_scroll;
+
+/*
+ * Jump to the end of the file.
+ */
+       public void
+jump_forw()
+{
+       POSITION pos;
+       POSITION end_pos;
+
+       if (ch_end_seek())
+       {
+               error("Cannot seek to end of file", NULL_PARG);
+               return;
+       }
+       /* 
+        * Note; lastmark will be called later by jump_loc, but it fails
+        * because the position table has been cleared by pos_clear below.
+        * So call it here before calling pos_clear.
+        */
+       lastmark();
+       /*
+        * Position the last line in the file at the last screen line.
+        * Go back one line from the end of the file
+        * to get to the beginning of the last line.
+        */
+       pos_clear();
+       end_pos = ch_tell();
+       pos = back_line(end_pos);
+       if (pos == NULL_POSITION)
+               jump_loc((POSITION)0, sc_height-1);
+       else
+       {
+               jump_loc(pos, sc_height-1);
+               if (position(sc_height-1) != end_pos)
+                       repaint();
+       }
+}
+
+/*
+ * Jump to line n in the file.
+ */
+       public void
+jump_back(linenum)
+       LINENUM linenum;
+{
+       POSITION pos;
+       PARG parg;
+
+       /*
+        * Find the position of the specified line.
+        * If we can seek there, just jump to it.
+        * If we can't seek, but we're trying to go to line number 1,
+        * use ch_beg_seek() to get as close as we can.
+        */
+       pos = find_pos(linenum);
+       if (pos != NULL_POSITION && ch_seek(pos) == 0)
+       {
+               if (show_attn)
+                       set_attnpos(pos);
+               jump_loc(pos, jump_sline);
+       } else if (linenum <= 1 && ch_beg_seek() == 0)
+       {
+               jump_loc(ch_tell(), jump_sline);
+               error("Cannot seek to beginning of file", NULL_PARG);
+       } else
+       {
+               parg.p_linenum = linenum;
+               error("Cannot seek to line number %n", &parg);
+       }
+}
+
+/*
+ * Repaint the screen.
+ */
+       public void
+repaint()
+{
+       struct scrpos scrpos;
+       /*
+        * Start at the line currently at the top of the screen
+        * and redisplay the screen.
+        */
+       get_scrpos(&scrpos);
+       pos_clear();
+       jump_loc(scrpos.pos, scrpos.ln);
+}
+
+/*
+ * Jump to a specified percentage into the file.
+ */
+       public void
+jump_percent(percent, fraction)
+       int percent;
+       long fraction;
+{
+       POSITION pos, len;
+
+       /*
+        * Determine the position in the file
+        * (the specified percentage of the file's length).
+        */
+       if ((len = ch_length()) == NULL_POSITION)
+       {
+               ierror("Determining length of file", NULL_PARG);
+               ch_end_seek();
+       }
+       if ((len = ch_length()) == NULL_POSITION)
+       {
+               error("Don't know length of file", NULL_PARG);
+               return;
+       }
+       pos = percent_pos(len, percent, fraction);
+       if (pos >= len)
+               pos = len-1;
+
+       jump_line_loc(pos, jump_sline);
+}
+
+/*
+ * Jump to a specified position in the file.
+ * Like jump_loc, but the position need not be 
+ * the first character in a line.
+ */
+       public void
+jump_line_loc(pos, sline)
+       POSITION pos;
+       int sline;
+{
+       int c;
+
+       if (ch_seek(pos) == 0)
+       {
+               /*
+                * Back up to the beginning of the line.
+                */
+               while ((c = ch_back_get()) != '\n' && c != EOI)
+                       ;
+               if (c == '\n')
+                       (void) ch_forw_get();
+               pos = ch_tell();
+       }
+       if (show_attn)
+               set_attnpos(pos);
+       jump_loc(pos, sline);
+}
+
+/*
+ * Jump to a specified position in the file.
+ * The position must be the first character in a line.
+ * Place the target line on a specified line on the screen.
+ */
+       public void
+jump_loc(pos, sline)
+       POSITION pos;
+       int sline;
+{
+       register int nline;
+       POSITION tpos;
+       POSITION bpos;
+
+       /*
+        * Normalize sline.
+        */
+       sline = adjsline(sline);
+
+       if ((nline = onscreen(pos)) >= 0)
+       {
+               /*
+                * The line is currently displayed.  
+                * Just scroll there.
+                */
+               nline -= sline;
+               if (nline > 0)
+                       forw(nline, position(BOTTOM_PLUS_ONE), 1, 0, 0);
+               else
+                       back(-nline, position(TOP), 1, 0);
+#if HILITE_SEARCH
+               if (show_attn)
+                       repaint_hilite(1);
+#endif
+               return;
+       }
+
+       /*
+        * Line is not on screen.
+        * Seek to the desired location.
+        */
+       if (ch_seek(pos))
+       {
+               error("Cannot seek to that file position", NULL_PARG);
+               return;
+       }
+
+       /*
+        * See if the desired line is before or after 
+        * the currently displayed screen.
+        */
+       tpos = position(TOP);
+       bpos = position(BOTTOM_PLUS_ONE);
+       if (tpos == NULL_POSITION || pos >= tpos)
+       {
+               /*
+                * The desired line is after the current screen.
+                * Move back in the file far enough so that we can
+                * call forw() and put the desired line at the 
+                * sline-th line on the screen.
+                */
+               for (nline = 0;  nline < sline;  nline++)
+               {
+                       if (bpos != NULL_POSITION && pos <= bpos)
+                       {
+                               /*
+                                * Surprise!  The desired line is
+                                * close enough to the current screen
+                                * that we can just scroll there after all.
+                                */
+                               forw(sc_height-sline+nline-1, bpos, 1, 0, 0);
+#if HILITE_SEARCH
+                               if (show_attn)
+                                       repaint_hilite(1);
+#endif
+                               return;
+                       }
+                       pos = back_line(pos);
+                       if (pos == NULL_POSITION)
+                       {
+                               /*
+                                * Oops.  Ran into the beginning of the file.
+                                * Exit the loop here and rely on forw()
+                                * below to draw the required number of
+                                * blank lines at the top of the screen.
+                                */
+                               break;
+                       }
+               }
+               lastmark();
+               squished = 0;
+               screen_trashed = 0;
+               forw(sc_height-1, pos, 1, 0, sline-nline);
+       } else
+       {
+               /*
+                * The desired line is before the current screen.
+                * Move forward in the file far enough so that we
+                * can call back() and put the desired line at the 
+                * sline-th line on the screen.
+                */
+               for (nline = sline;  nline < sc_height - 1;  nline++)
+               {
+                       pos = forw_line(pos);
+                       if (pos == NULL_POSITION)
+                       {
+                               /*
+                                * Ran into end of file.
+                                * This shouldn't normally happen, 
+                                * but may if there is some kind of read error.
+                                */
+                               break;
+                       }
+                       if (pos >= tpos)
+                       {
+                               /* 
+                                * Surprise!  The desired line is
+                                * close enough to the current screen
+                                * that we can just scroll there after all.
+                                */
+                               back(nline+1, tpos, 1, 0);
+#if HILITE_SEARCH
+                               if (show_attn)
+                                       repaint_hilite(1);
+#endif
+                               return;
+                       }
+               }
+               lastmark();
+               if (!top_scroll)
+                       clear();
+               else
+                       home();
+               screen_trashed = 0;
+               add_back_pos(pos);
+               back(sc_height-1, pos, 1, 0);
+       }
+}
diff --git a/thirdparty/less/less.h b/thirdparty/less/less.h
new file mode 100644 (file)
index 0000000..d013345
--- /dev/null
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+#define NEWBOT 1
+
+/*
+ * Standard include file for "less".
+ */
+
+/*
+ * Defines for MSDOS_COMPILER.
+ */
+#define        MSOFTC          1       /* Microsoft C */
+#define        BORLANDC        2       /* Borland C */
+#define        WIN32C          3       /* Windows (Borland C or Microsoft C) */
+#define        DJGPPC          4       /* DJGPP C */
+
+/*
+ * Include the file of compile-time options.
+ * The <> make cc search for it in -I., not srcdir.
+ */
+#include <defines.h>
+
+#ifdef _SEQUENT_
+/*
+ * Kludge for Sequent Dynix systems that have sigsetmask, but
+ * it's not compatible with the way less calls it.
+ * {{ Do other systems need this? }}
+ */
+#undef HAVE_SIGSETMASK
+#endif
+
+/*
+ * Language details.
+ */
+#if HAVE_VOID
+#define        VOID_POINTER    void *
+#else
+#define        VOID_POINTER    char *
+#define        void  int
+#endif
+#if HAVE_CONST
+#define        constant        const
+#else
+#define        constant
+#endif
+
+#define        public          /* PUBLIC FUNCTION */
+
+/* Library function declarations */
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#if HAVE_WCTYPE_H
+#include <wctype.h>
+#endif
+#if HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+
+/* OS-specific includes */
+#ifdef _OSK
+#include <modes.h>
+#include <strings.h>
+#endif
+
+#ifdef __TANDEM
+#include <floss.h>
+#endif
+
+#if MSDOS_COMPILER==WIN32C || OS2
+#include <io.h>
+#endif
+
+#if MSDOS_COMPILER==DJGPPC
+#include <io.h>
+#include <sys/exceptn.h>
+#include <conio.h>
+#include <pc.h>
+#endif
+
+#if !HAVE_STDLIB_H
+char *getenv();
+off_t lseek();
+VOID_POINTER calloc();
+void free();
+#endif
+
+/*
+ * Simple lowercase test which can be used during option processing
+ * (before options are parsed which might tell us what charset to use).
+ */
+#define ASCII_IS_UPPER(c)      ((c) >= 'A' && (c) <= 'Z')
+#define ASCII_IS_LOWER(c)      ((c) >= 'a' && (c) <= 'z')
+#define        ASCII_TO_UPPER(c)       ((c) - 'a' + 'A')
+#define        ASCII_TO_LOWER(c)       ((c) - 'A' + 'a')
+
+#undef IS_UPPER
+#undef IS_LOWER
+#undef TO_UPPER
+#undef TO_LOWER
+#undef IS_SPACE
+#undef IS_DIGIT
+
+#if HAVE_WCTYPE
+#define        IS_UPPER(c)     iswupper(c)
+#define        IS_LOWER(c)     iswlower(c)
+#define        TO_UPPER(c)     towupper(c)
+#define        TO_LOWER(c)     towlower(c)
+#else
+#if HAVE_UPPER_LOWER
+#define        IS_UPPER(c)     isupper((unsigned char) (c))
+#define        IS_LOWER(c)     islower((unsigned char) (c))
+#define        TO_UPPER(c)     toupper((unsigned char) (c))
+#define        TO_LOWER(c)     tolower((unsigned char) (c))
+#else
+#define        IS_UPPER(c)     ASCII_IS_UPPER(c)
+#define        IS_LOWER(c)     ASCII_IS_LOWER(c)
+#define        TO_UPPER(c)     ASCII_TO_UPPER(c)
+#define        TO_LOWER(c)     ASCII_TO_LOWER(c)
+#endif
+#endif
+
+#ifdef isspace
+#define IS_SPACE(c)    isspace((unsigned char)(c))
+#else
+#define IS_SPACE(c)    ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' || (c) == '\f')
+#endif
+
+#ifdef isdigit
+#define IS_DIGIT(c)    isdigit((unsigned char)(c))
+#else
+#define IS_DIGIT(c)    ((c) >= '0' && (c) <= '9')
+#endif
+
+#define IS_CSI_START(c)        (((LWCHAR)(c)) == ESC || (((LWCHAR)(c)) == CSI))
+
+#ifndef NULL
+#define        NULL    0
+#endif
+
+#ifndef TRUE
+#define        TRUE            1
+#endif
+#ifndef FALSE
+#define        FALSE           0
+#endif
+
+#define        OPT_OFF         0
+#define        OPT_ON          1
+#define        OPT_ONPLUS      2
+
+#if !HAVE_MEMCPY
+#ifndef memcpy
+#define        memcpy(to,from,len)     bcopy((from),(to),(len))
+#endif
+#endif
+
+#if HAVE_SNPRINTF
+#define SNPRINTF1(str, size, fmt, v1)             snprintf((str), (size), (fmt), (v1))
+#define SNPRINTF2(str, size, fmt, v1, v2)         snprintf((str), (size), (fmt), (v1), (v2))
+#define SNPRINTF3(str, size, fmt, v1, v2, v3)     snprintf((str), (size), (fmt), (v1), (v2), (v3))
+#define SNPRINTF4(str, size, fmt, v1, v2, v3, v4) snprintf((str), (size), (fmt), (v1), (v2), (v3), (v4))
+#else
+/* Use unsafe sprintf if we don't have snprintf. */
+#define SNPRINTF1(str, size, fmt, v1)             sprintf((str), (fmt), (v1))
+#define SNPRINTF2(str, size, fmt, v1, v2)         sprintf((str), (fmt), (v1), (v2))
+#define SNPRINTF3(str, size, fmt, v1, v2, v3)     sprintf((str), (fmt), (v1), (v2), (v3))
+#define SNPRINTF4(str, size, fmt, v1, v2, v3, v4) sprintf((str), (fmt), (v1), (v2), (v3), (v4))
+#endif
+
+#define        BAD_LSEEK       ((off_t)-1)
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
+/*
+ * Upper bound on the string length of an integer converted to string.
+ * 302 / 1000 is ceil (log10 (2.0)).  Subtract 1 for the sign bit;
+ * add 1 for integer division truncation; add 1 more for a minus sign.
+ */
+#define INT_STRLEN_BOUND(t) ((sizeof(t) * CHAR_BIT - 1) * 302 / 1000 + 1 + 1)
+
+/*
+ * Special types and constants.
+ */
+typedef unsigned long LWCHAR;
+typedef off_t          POSITION;
+typedef off_t          LINENUM;
+#define MIN_LINENUM_WIDTH  7   /* Min printing width of a line number */
+#define MAX_UTF_CHAR_LEN   6   /* Max bytes in one UTF-8 char */
+
+#define        NULL_POSITION   ((POSITION)(-1))
+
+/*
+ * Flags for open()
+ */
+#if MSDOS_COMPILER || OS2
+#define        OPEN_READ       (O_RDONLY|O_BINARY)
+#else
+#ifdef _OSK
+#define        OPEN_READ       (S_IREAD)
+#else
+#ifdef O_RDONLY
+#define        OPEN_READ       (O_RDONLY)
+#else
+#define        OPEN_READ       (0)
+#endif
+#endif
+#endif
+
+#if defined(O_WRONLY) && defined(O_APPEND)
+#define        OPEN_APPEND     (O_APPEND|O_WRONLY)
+#else
+#ifdef _OSK
+#define OPEN_APPEND    (S_IWRITE)
+#else
+#define        OPEN_APPEND     (1)
+#endif
+#endif
+
+/*
+ * Set a file descriptor to binary mode.
+ */
+#if MSDOS_COMPILER==MSOFTC
+#define        SET_BINARY(f)   _setmode(f, _O_BINARY);
+#else
+#if MSDOS_COMPILER || OS2
+#define        SET_BINARY(f)   setmode(f, O_BINARY)
+#else
+#define        SET_BINARY(f)
+#endif
+#endif
+
+/*
+ * Does the shell treat "?" as a metacharacter?
+ */
+#if MSDOS_COMPILER || OS2 || _OSK
+#define        SHELL_META_QUEST 0
+#else
+#define        SHELL_META_QUEST 1
+#endif
+
+#define        SPACES_IN_FILENAMES 1
+
+/*
+ * An IFILE represents an input file.
+ */
+#define        IFILE           VOID_POINTER
+#define        NULL_IFILE      ((IFILE)NULL)
+
+/*
+ * The structure used to represent a "screen position".
+ * This consists of a file position, and a screen line number.
+ * The meaning is that the line starting at the given file
+ * position is displayed on the ln-th line of the screen.
+ * (Screen lines before ln are empty.)
+ */
+struct scrpos
+{
+       POSITION pos;
+       int ln;
+};
+
+typedef union parg
+{
+       char *p_string;
+       int p_int;
+       LINENUM p_linenum;
+} PARG;
+
+#define        NULL_PARG       ((PARG *)NULL)
+
+struct textlist
+{
+       char *string;
+       char *endstring;
+};
+
+#define        EOI             (-1)
+
+#define        READ_INTR       (-2)
+
+/* A fraction is represented by an int n; the fraction is n/NUM_FRAC_DENOM */
+#define NUM_FRAC_DENOM                 1000000
+#define NUM_LOG_FRAC_DENOM             6
+
+/* How quiet should we be? */
+#define        NOT_QUIET       0       /* Ring bell at eof and for errors */
+#define        LITTLE_QUIET    1       /* Ring bell only for errors */
+#define        VERY_QUIET      2       /* Never ring bell */
+
+/* How should we prompt? */
+#define        PR_SHORT        0       /* Prompt with colon */
+#define        PR_MEDIUM       1       /* Prompt with message */
+#define        PR_LONG         2       /* Prompt with longer message */
+
+/* How should we handle backspaces? */
+#define        BS_SPECIAL      0       /* Do special things for underlining and bold */
+#define        BS_NORMAL       1       /* \b treated as normal char; actually output */
+#define        BS_CONTROL      2       /* \b treated as control char; prints as ^H */
+
+/* How should we search? */
+#define        SRCH_FORW       (1 << 0)  /* Search forward from current position */
+#define        SRCH_BACK       (1 << 1)  /* Search backward from current position */
+#define SRCH_NO_MOVE    (1 << 2)  /* Highlight, but don't move */
+#define SRCH_FIND_ALL   (1 << 4)  /* Find and highlight all matches */
+#define SRCH_NO_MATCH   (1 << 8)  /* Search for non-matching lines */
+#define SRCH_PAST_EOF   (1 << 9)  /* Search past end-of-file, into next file */
+#define SRCH_FIRST_FILE (1 << 10) /* Search starting at the first file */
+#define SRCH_NO_REGEX   (1 << 12) /* Don't use regular expressions */
+#define SRCH_FILTER     (1 << 13) /* Search is for '&' (filter) command */
+#define SRCH_AFTER_TARGET (1 << 14) /* Start search after the target line */
+
+#define        SRCH_REVERSE(t) (((t) & SRCH_FORW) ? \
+                               (((t) & ~SRCH_FORW) | SRCH_BACK) : \
+                               (((t) & ~SRCH_BACK) | SRCH_FORW))
+
+/* */
+#define        NO_MCA          0
+#define        MCA_DONE        1
+#define        MCA_MORE        2
+
+#define        CC_OK           0       /* Char was accepted & processed */
+#define        CC_QUIT         1       /* Char was a request to abort current cmd */
+#define        CC_ERROR        2       /* Char could not be accepted due to error */
+#define        CC_PASS         3       /* Char was rejected (internal) */
+
+#define CF_QUIT_ON_ERASE 0001   /* Abort cmd if its entirely erased */
+
+/* Special char bit-flags used to tell put_line() to do something special */
+#define        AT_NORMAL       (0)
+#define        AT_UNDERLINE    (1 << 0)
+#define        AT_BOLD         (1 << 1)
+#define        AT_BLINK        (1 << 2)
+#define        AT_STANDOUT     (1 << 3)
+#define        AT_ANSI         (1 << 4)  /* Content-supplied "ANSI" escape sequence */
+#define        AT_BINARY       (1 << 5)  /* LESS*BINFMT representation */
+#define        AT_HILITE       (1 << 6)  /* Internal highlights (e.g., for search) */
+
+#if '0' == 240
+#define IS_EBCDIC_HOST 1
+#endif
+
+#if IS_EBCDIC_HOST
+/*
+ * Long definition for EBCDIC.
+ * Since the argument is usually a constant, this macro normally compiles
+ * into a constant.
+ */
+#define CONTROL(c) ( \
+       (c)=='[' ? '\047' : \
+       (c)=='a' ? '\001' : \
+       (c)=='b' ? '\002' : \
+       (c)=='c' ? '\003' : \
+       (c)=='d' ? '\067' : \
+       (c)=='e' ? '\055' : \
+       (c)=='f' ? '\056' : \
+       (c)=='g' ? '\057' : \
+       (c)=='h' ? '\026' : \
+       (c)=='i' ? '\005' : \
+       (c)=='j' ? '\025' : \
+       (c)=='k' ? '\013' : \
+       (c)=='l' ? '\014' : \
+       (c)=='m' ? '\015' : \
+       (c)=='n' ? '\016' : \
+       (c)=='o' ? '\017' : \
+       (c)=='p' ? '\020' : \
+       (c)=='q' ? '\021' : \
+       (c)=='r' ? '\022' : \
+       (c)=='s' ? '\023' : \
+       (c)=='t' ? '\074' : \
+       (c)=='u' ? '\075' : \
+       (c)=='v' ? '\062' : \
+       (c)=='w' ? '\046' : \
+       (c)=='x' ? '\030' : \
+       (c)=='y' ? '\031' : \
+       (c)=='z' ? '\077' : \
+       (c)=='A' ? '\001' : \
+       (c)=='B' ? '\002' : \
+       (c)=='C' ? '\003' : \
+       (c)=='D' ? '\067' : \
+       (c)=='E' ? '\055' : \
+       (c)=='F' ? '\056' : \
+       (c)=='G' ? '\057' : \
+       (c)=='H' ? '\026' : \
+       (c)=='I' ? '\005' : \
+       (c)=='J' ? '\025' : \
+       (c)=='K' ? '\013' : \
+       (c)=='L' ? '\014' : \
+       (c)=='M' ? '\015' : \
+       (c)=='N' ? '\016' : \
+       (c)=='O' ? '\017' : \
+       (c)=='P' ? '\020' : \
+       (c)=='Q' ? '\021' : \
+       (c)=='R' ? '\022' : \
+       (c)=='S' ? '\023' : \
+       (c)=='T' ? '\074' : \
+       (c)=='U' ? '\075' : \
+       (c)=='V' ? '\062' : \
+       (c)=='W' ? '\046' : \
+       (c)=='X' ? '\030' : \
+       (c)=='Y' ? '\031' : \
+       (c)=='Z' ? '\077' : \
+       (c)=='|' ? '\031' : \
+       (c)=='\\' ? '\034' : \
+       (c)=='^' ? '\036' : \
+       (c)&077)
+#else
+#define        CONTROL(c)      ((c)&037)
+#endif /* IS_EBCDIC_HOST */
+
+#define        ESC             CONTROL('[')
+#define        CSI             ((unsigned char)'\233')
+
+#if _OSK_MWC32
+#define        LSIGNAL(sig,func)       os9_signal(sig,func)
+#else
+#define        LSIGNAL(sig,func)       signal(sig,func)
+#endif
+
+#if HAVE_SIGPROCMASK
+#if HAVE_SIGSET_T
+#else
+#undef HAVE_SIGPROCMASK
+#endif
+#endif
+#if HAVE_SIGPROCMASK
+#if HAVE_SIGEMPTYSET
+#else
+#undef  sigemptyset
+#define sigemptyset(mp) *(mp) = 0
+#endif
+#endif
+
+#define        S_INTERRUPT     01
+#define        S_STOP          02
+#define S_WINCH                04
+#define        ABORT_SIGS()    (sigs & (S_INTERRUPT|S_STOP))
+
+#define        QUIT_OK         0
+#define        QUIT_ERROR      1
+#define        QUIT_INTERRUPT  2
+#define        QUIT_SAVED_STATUS (-1)
+
+#define FOLLOW_DESC     0
+#define FOLLOW_NAME     1
+
+/* filestate flags */
+#define        CH_CANSEEK      001
+#define        CH_KEEPOPEN     002
+#define        CH_POPENED      004
+#define        CH_HELPFILE     010
+
+#define        ch_zero()       ((POSITION)0)
+
+#define        FAKE_HELPFILE   "@/\\less/\\help/\\file/\\@"
+
+/* Flags for cvt_text */
+#define        CVT_TO_LC       01      /* Convert upper-case to lower-case */
+#define        CVT_BS          02      /* Do backspace processing */
+#define        CVT_CRLF        04      /* Remove CR after LF */
+#define        CVT_ANSI        010     /* Remove ANSI escape sequences */
+
+#include "funcs.h"
+
+/* Functions not included in funcs.h */
+void postoa();
+void linenumtoa();
+void inttoa();
diff --git a/thirdparty/less/less.hlp b/thirdparty/less/less.hlp
new file mode 100644 (file)
index 0000000..84d951f
--- /dev/null
@@ -0,0 +1,231 @@
+
+                   S\bSU\bUM\bMM\bMA\bAR\bRY\bY O\bOF\bF L\bLE\bES\bSS\bS C\bCO\bOM\bMM\bMA\bAN\bND\bDS\bS
+
+      Commands marked with * may be preceded by a number, _\bN.
+      Notes in parentheses indicate the behavior if _\bN is given.
+
+  h  H                 Display this help.
+  q  :q  Q  :Q  ZZ     Exit.
+ ---------------------------------------------------------------------------
+
+                           M\bMO\bOV\bVI\bIN\bNG\bG
+
+  e  ^E  j  ^N  CR  *  Forward  one line   (or _\bN lines).
+  y  ^Y  k  ^K  ^P  *  Backward one line   (or _\bN lines).
+  f  ^F  ^V  SPACE  *  Forward  one window (or _\bN lines).
+  b  ^B  ESC-v      *  Backward one window (or _\bN lines).
+  z                 *  Forward  one window (and set window to _\bN).
+  w                 *  Backward one window (and set window to _\bN).
+  ESC-SPACE         *  Forward  one window, but don't stop at end-of-file.
+  d  ^D             *  Forward  one half-window (and set half-window to _\bN).
+  u  ^U             *  Backward one half-window (and set half-window to _\bN).
+  ESC-)  RightArrow *  Left  one half screen width (or _\bN positions).
+  ESC-(  LeftArrow  *  Right one half screen width (or _\bN positions).
+  F                    Forward forever; like "tail -f".
+  r  ^R  ^L            Repaint screen.
+  R                    Repaint screen, discarding buffered input.
+        ---------------------------------------------------
+        Default "window" is the screen height.
+        Default "half-window" is half of the screen height.
+ ---------------------------------------------------------------------------
+
+                          S\bSE\bEA\bAR\bRC\bCH\bHI\bIN\bNG\bG
+
+  /_\bp_\ba_\bt_\bt_\be_\br_\bn          *  Search forward for (_\bN-th) matching line.
+  ?_\bp_\ba_\bt_\bt_\be_\br_\bn          *  Search backward for (_\bN-th) matching line.
+  n                 *  Repeat previous search (for _\bN-th occurrence).
+  N                 *  Repeat previous search in reverse direction.
+  ESC-n             *  Repeat previous search, spanning files.
+  ESC-N             *  Repeat previous search, reverse dir. & spanning files.
+  ESC-u                Undo (toggle) search highlighting.
+  &_\bp_\ba_\bt_\bt_\be_\br_\bn          *  Display only matching lines
+        ---------------------------------------------------
+        Search patterns may be modified by one or more of:
+        ^N or !  Search for NON-matching lines.
+        ^E or *  Search multiple files (pass thru END OF FILE).
+        ^F or @  Start search at FIRST file (for /) or last file (for ?).
+        ^K       Highlight matches, but don't move (KEEP position).
+        ^R       Don't use REGULAR EXPRESSIONS.
+ ---------------------------------------------------------------------------
+
+                           J\bJU\bUM\bMP\bPI\bIN\bNG\bG
+
+  g  <  ESC-<       *  Go to first line in file (or line _\bN).
+  G  >  ESC->       *  Go to last line in file (or line _\bN).
+  p  %              *  Go to beginning of file (or _\bN percent into file).
+  t                 *  Go to the (_\bN-th) next tag.
+  T                 *  Go to the (_\bN-th) previous tag.
+  {  (  [           *  Find close bracket } ) ].
+  }  )  ]           *  Find open bracket { ( [.
+  ESC-^F _\b<_\bc_\b1_\b> _\b<_\bc_\b2_\b>  *  Find close bracket _\b<_\bc_\b2_\b>.
+  ESC-^B _\b<_\bc_\b1_\b> _\b<_\bc_\b2_\b>  *  Find open bracket _\b<_\bc_\b1_\b
+        ---------------------------------------------------
+        Each "find close bracket" command goes forward to the close bracket 
+          matching the (_\bN-th) open bracket in the top line.
+        Each "find open bracket" command goes backward to the open bracket 
+          matching the (_\bN-th) close bracket in the bottom line.
+
+  m_\b<_\bl_\be_\bt_\bt_\be_\br_\b>            Mark the current position with <letter>.
+  '_\b<_\bl_\be_\bt_\bt_\be_\br_\b>            Go to a previously marked position.
+  ''                   Go to the previous position.
+  ^X^X                 Same as '.
+        ---------------------------------------------------
+        A mark is any upper-case or lower-case letter.
+        Certain marks are predefined:
+             ^  means  beginning of the file
+             $  means  end of the file
+ ---------------------------------------------------------------------------
+
+                        C\bCH\bHA\bAN\bNG\bGI\bIN\bNG\bG F\bFI\bIL\bLE\bES\bS
+
+  :e [_\bf_\bi_\bl_\be]            Examine a new file.
+  ^X^V                 Same as :e.
+  :n                *  Examine the (_\bN-th) next file from the command line.
+  :p                *  Examine the (_\bN-th) previous file from the command line.
+  :x                *  Examine the first (or _\bN-th) file from the command line.
+  :d                   Delete the current file from the command line list.
+  =  ^G  :f            Print current file name.
+ ---------------------------------------------------------------------------
+
+                    M\bMI\bIS\bSC\bCE\bEL\bLL\bLA\bAN\bNE\bEO\bOU\bUS\bS C\bCO\bOM\bMM\bMA\bAN\bND\bDS\bS
+
+  -_\b<_\bf_\bl_\ba_\bg_\b>              Toggle a command line option [see OPTIONS below].
+  --_\b<_\bn_\ba_\bm_\be_\b>             Toggle a command line option, by name.
+  __\b<_\bf_\bl_\ba_\bg_\b>              Display the setting of a command line option.
+  ___\b<_\bn_\ba_\bm_\be_\b>             Display the setting of an option, by name.
+  +_\bc_\bm_\bd                 Execute the less cmd each time a new file is examined.
+
+  !_\bc_\bo_\bm_\bm_\ba_\bn_\bd             Execute the shell command with $SHELL.
+  |X\bX_\bc_\bo_\bm_\bm_\ba_\bn_\bd            Pipe file between current pos & mark X\bX to shell command.
+  v                    Edit the current file with $VISUAL or $EDITOR.
+  V                    Print version number of "less".
+ ---------------------------------------------------------------------------
+
+                           O\bOP\bPT\bTI\bIO\bON\bNS\bS
+
+        Most options may be changed either on the command line,
+        or from within less by using the - or -- command.
+        Options may be given in one of two forms: either a single
+        character preceded by a -, or a name preceeded by --.
+
+  -?  ........  --help
+                  Display help (from command line).
+  -a  ........  --search-skip-screen
+                  Forward search skips current screen.
+  -A  ........  --SEARCH-SKIP-SCREEN
+                  Forward search always skips target line.
+  -b [_\bN]  ....  --buffers=[_\bN]
+                  Number of buffers.
+  -B  ........  --auto-buffers
+                  Don't automatically allocate buffers for pipes.
+  -c  ........  --clear-screen
+                  Repaint by clearing rather than scrolling.
+  -d  ........  --dumb
+                  Dumb terminal.
+  -D [_\bx_\bn_\b._\bn]  .  --color=_\bx_\bn_\b._\bn
+                  Set screen colors. (MS-DOS only)
+  -e  -E  ....  --quit-at-eof  --QUIT-AT-EOF
+                  Quit at end of file.
+  -f  ........  --force
+                  Force open non-regular files.
+  -F  ........  --quit-if-one-screen
+                  Quit if entire file fits on first screen.
+  -g  ........  --hilite-search
+                  Highlight only last match for searches.
+  -G  ........  --HILITE-SEARCH
+                  Don't highlight any matches for searches.
+  -h [_\bN]  ....  --max-back-scroll=[_\bN]
+                  Backward scroll limit.
+  -i  ........  --ignore-case
+                  Ignore case in searches that do not contain uppercase.
+  -I  ........  --IGNORE-CASE
+                  Ignore case in all searches.
+  -j [_\bN]  ....  --jump-target=[_\bN]
+                  Screen position of target lines.
+  -J  ........  --status-column
+                  Display a status column at left edge of screen.
+  -k [_\bf_\bi_\bl_\be]  .  --lesskey-file=[_\bf_\bi_\bl_\be]
+                  Use a lesskey file.
+  -K            --quit-on-intr
+                  Exit less in response to ctrl-C.
+  -L  ........  --no-lessopen
+                  Ignore the LESSOPEN environment variable.
+  -m  -M  ....  --long-prompt  --LONG-PROMPT
+                  Set prompt style.
+  -n  -N  ....  --line-numbers  --LINE-NUMBERS
+                  Don't use line numbers.
+  -o [_\bf_\bi_\bl_\be]  .  --log-file=[_\bf_\bi_\bl_\be]
+                  Copy to log file (standard input only).
+  -O [_\bf_\bi_\bl_\be]  .  --LOG-FILE=[_\bf_\bi_\bl_\be]
+                  Copy to log file (unconditionally overwrite).
+  -p [_\bp_\ba_\bt_\bt_\be_\br_\bn]  --pattern=[_\bp_\ba_\bt_\bt_\be_\br_\bn]
+                  Start at pattern (from command line).
+  -P [_\bp_\br_\bo_\bm_\bp_\bt]   --prompt=[_\bp_\br_\bo_\bm_\bp_\bt]
+                  Define new prompt.
+  -q  -Q  ....  --quiet  --QUIET  --silent --SILENT
+                  Quiet the terminal bell.
+  -r  -R  ....  --raw-control-chars  --RAW-CONTROL-CHARS
+                  Output "raw" control characters.
+  -s  ........  --squeeze-blank-lines
+                  Squeeze multiple blank lines.
+  -S  ........  --chop-long-lines
+                  Chop long lines.
+  -t [_\bt_\ba_\bg]  ..  --tag=[_\bt_\ba_\bg]
+                  Find a tag.
+  -T [_\bt_\ba_\bg_\bs_\bf_\bi_\bl_\be] --tag-file=[_\bt_\ba_\bg_\bs_\bf_\bi_\bl_\be]
+                  Use an alternate tags file.
+  -u  -U  ....  --underline-special  --UNDERLINE-SPECIAL
+                  Change handling of backspaces.
+  -V  ........  --version
+                  Display the version number of "less".
+  -w  ........  --hilite-unread
+                  Highlight first new line after forward-screen.
+  -W  ........  --HILITE-UNREAD
+                  Highlight first new line after any forward movement.
+  -x [_\bN[,...]]  --tabs=[_\bN[,...]]
+                  Set tab stops.
+  -X  ........  --no-init
+                  Don't use termcap init/deinit strings.
+                --no-keypad
+                  Don't use termcap keypad init/deinit strings.
+  -y [_\bN]  ....  --max-forw-scroll=[_\bN]
+                  Forward scroll limit.
+  -z [_\bN]  ....  --window=[_\bN]
+                  Set size of window.
+  -" [_\bc[_\bc]]  .  --quotes=[_\bc[_\bc]]
+                  Set shell quote characters.
+  -~  ........  --tilde
+                  Don't display tildes after end of file.
+  -# [_\bN]  ....  --shift=[_\bN]
+                  Horizontal scroll amount (0 = one half screen width)
+      ........  --no-keypad
+                  Don't send keypad init/deinit sequence.
+      ........  --follow-name
+                  The F command changes files if the input file is renamed.
+
+
+ ---------------------------------------------------------------------------
+
+                          L\bLI\bIN\bNE\bE E\bED\bDI\bIT\bTI\bIN\bNG\bG
+
+        These keys can be used to edit text being entered 
+        on the "command line" at the bottom of the screen.
+
+ RightArrow                       ESC-l     Move cursor right one character.
+ LeftArrow                        ESC-h     Move cursor left one character.
+ CNTL-RightArrow  ESC-RightArrow  ESC-w     Move cursor right one word.
+ CNTL-LeftArrow   ESC-LeftArrow   ESC-b     Move cursor left one word.
+ HOME                             ESC-0     Move cursor to start of line.
+ END                              ESC-$     Move cursor to end of line.
+ BACKSPACE                                  Delete char to left of cursor.
+ DELETE                           ESC-x     Delete char under cursor.
+ CNTL-BACKSPACE   ESC-BACKSPACE             Delete word to left of cursor.
+ CNTL-DELETE      ESC-DELETE      ESC-X     Delete word under cursor.
+ CNTL-U           ESC (MS-DOS only)         Delete entire line.
+ UpArrow                          ESC-k     Retrieve previous command line.
+ DownArrow                        ESC-j     Retrieve next command line.
+ TAB                                        Complete filename & cycle.
+ SHIFT-TAB                        ESC-TAB   Complete filename & reverse cycle.
+ CNTL-L                                     Complete filename, list all.
+
+
diff --git a/thirdparty/less/lessecho.c b/thirdparty/less/lessecho.c
new file mode 100644 (file)
index 0000000..6dcaf6c
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * lessecho [-ox] [-cx] [-pn] [-dn] [-a] file ...
+ * Simply echos its filename arguments on standard output.
+ * But any argument containing spaces is enclosed in quotes.
+ *
+ * -ox Specifies "x" to be the open quote character.
+ * -cx Specifies "x" to be the close quote character.
+ * -pn Specifies "n" to be the open quote character, as an integer.
+ * -dn Specifies "n" to be the close quote character, as an integer.
+ * -mx  Specifies "x" to be a metachar.
+ * -nn  Specifies "n" to be a metachar, as an integer.
+ * -ex  Specifies "x" to be the escape char for metachars.
+ * -fn  Specifies "x" to be the escape char for metachars, as an integer.
+ * -a  Specifies that all arguments are to be quoted.
+ *     The default is that only arguments containing spaces are quoted.
+ */
+
+#include "less.h"
+
+static char *version = "$Revision: 1.14 $";
+
+static int quote_all = 0;
+static char openquote = '"';
+static char closequote = '"';
+static char *meta_escape = "\\";
+static char meta_escape_buf[2];
+static char metachars[64] = "";
+static int num_metachars = 0;
+
+       static void
+pr_usage()
+{
+       fprintf(stderr,
+               "usage: lessecho [-ox] [-cx] [-pn] [-dn] [-mx] [-nn] [-ex] [-fn] [-a] file ...\n");
+}
+
+       static void
+pr_version()
+{
+       char *p;
+       char buf[10];
+       char *pbuf = buf;
+
+       for (p = version;  *p != ' ';  p++)
+               if (*p == '\0')
+                       return;
+       for (p++;  *p != '$' && *p != ' ' && *p != '\0';  p++)
+               *pbuf++ = *p;
+       *pbuf = '\0';
+       printf("%s\n", buf);
+}
+
+       static void
+pr_error(s)
+       char *s;
+{
+       fprintf(stderr, "%s\n", s);
+       exit(1);
+}
+
+       static long
+lstrtol(s, radix, pend)
+       char *s;
+       int radix;
+       char **pend;
+{
+       int v;
+       int neg = 0;
+       long n = 0;
+
+       /* Skip leading white space. */
+       while (*s == ' ' || *s == '\t')
+               s++;
+
+       /* Check for a leading + or -. */
+       if (*s == '-')
+       {
+               neg = 1;
+               s++;
+       } else if (*s == '+')
+       {
+               s++;
+       }
+
+       /* Determine radix if caller does not specify. */
+       if (radix == 0)
+       {
+               radix = 10;
+               if (*s == '0')
+               {
+                       switch (*++s)
+                       {
+                       case 'x':
+                               radix = 16;
+                               s++;
+                               break;
+                       default:
+                               radix = 8;
+                               break;
+                       }
+               }
+       }
+
+       /* Parse the digits of the number. */
+       for (;;)
+       {
+               if (*s >= '0' && *s <= '9')
+                       v = *s - '0';
+               else if (*s >= 'a' && *s <= 'f')
+                       v = *s - 'a' + 10;
+               else if (*s >= 'A' && *s <= 'F')
+                       v = *s - 'A' + 10;
+               else
+                       break;
+               if (v >= radix)
+                       break;
+               n = n * radix + v;
+               s++;
+       }
+
+       if (pend != NULL)
+       {
+               /* Skip trailing white space. */
+               while (*s == ' ' || *s == '\t')
+                       s++;
+               *pend = s;
+       }
+       if (neg)
+               return (-n);
+       return (n);
+}
+
+
+#if !HAVE_STRCHR
+       char *
+strchr(s, c)
+       char *s;
+       int c;
+{
+       for ( ;  *s != '\0';  s++)
+               if (*s == c)
+                       return (s);
+       if (c == '\0')
+               return (s);
+       return (NULL);
+}
+#endif
+
+       int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       char *arg;
+       char *s;
+       int no_more_options;
+
+       no_more_options = 0;
+       while (--argc > 0)
+       {
+               arg = *++argv;
+               if (*arg != '-' || no_more_options)
+                       break;
+               switch (*++arg)
+               {
+               case 'a':
+                       quote_all = 1;
+                       break;
+               case 'c':
+                       closequote = *++arg;
+                       break;
+               case 'd':
+                       closequote = lstrtol(++arg, 0, &s);
+                       if (s == arg)
+                               pr_error("Missing number after -d");
+                       break;
+               case 'e':
+                       if (strcmp(++arg, "-") == 0)
+                               meta_escape = "";
+                       else
+                               meta_escape = arg;
+                       break;
+               case 'f':
+                       meta_escape_buf[0] = lstrtol(++arg, 0, &s);
+                       meta_escape = meta_escape_buf;
+                       if (s == arg)
+                               pr_error("Missing number after -f");
+                       break;
+               case 'o':
+                       openquote = *++arg;
+                       break;
+               case 'p':
+                       openquote = lstrtol(++arg, 0, &s);
+                       if (s == arg)
+                               pr_error("Missing number after -p");
+                       break;
+               case 'm':
+                       metachars[num_metachars++] = *++arg;
+                       metachars[num_metachars] = '\0';
+                       break;
+               case 'n':
+                       metachars[num_metachars++] = lstrtol(++arg, 0, &s);
+                       if (s == arg)
+                               pr_error("Missing number after -n");
+                       metachars[num_metachars] = '\0';
+                       break;
+               case '?':
+                       pr_usage();
+                       return (0);
+               case '-':
+                       if (*++arg == '\0')
+                       {
+                               no_more_options = 1;
+                               break;
+                       }
+                       if (strcmp(arg, "version") == 0)
+                       {
+                               pr_version();
+                               return (0);
+                       }
+                       if (strcmp(arg, "help") == 0)
+                       {
+                               pr_usage();
+                               return (0);
+                       }
+                       pr_error("Invalid option after --");
+               default:
+                       pr_error("Invalid option letter");
+               }
+       }
+
+       while (argc-- > 0)
+       {
+               int has_meta = 0;
+               arg = *argv++;
+               for (s = arg;  *s != '\0';  s++)
+               {
+                       if (strchr(metachars, *s) != NULL)
+                       {
+                               has_meta = 1;
+                               break;
+                       }
+               }
+               if (quote_all || (has_meta && strlen(meta_escape) == 0))
+                       printf("%c%s%c", openquote, arg, closequote);
+               else 
+               {
+                       for (s = arg;  *s != '\0';  s++)
+                       {
+                               if (strchr(metachars, *s) != NULL)
+                                       printf("%s", meta_escape);
+                               printf("%c", *s);
+                       }
+               }
+               if (argc > 0)
+                       printf(" ");
+               else
+                       printf("\n");
+       }
+       return (0);
+}
diff --git a/thirdparty/less/lesskey.c b/thirdparty/less/lesskey.c
new file mode 100644 (file)
index 0000000..1ee2c40
--- /dev/null
@@ -0,0 +1,873 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ *     lesskey [-o output] [input]
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
+ *
+ *     Make a .less file.
+ *     If no input file is specified, standard input is used.
+ *     If no output file is specified, $HOME/.less is used.
+ *
+ *     The .less file is used to specify (to "less") user-defined
+ *     key bindings.  Basically any sequence of 1 to MAX_CMDLEN
+ *     keystrokes may be bound to an existing less function.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
+ *
+ *     The input file is an ascii file consisting of a 
+ *     sequence of lines of the form:
+ *             string <whitespace> action [chars] <newline>
+ *
+ *     "string" is a sequence of command characters which form
+ *             the new user-defined command.  The command
+ *             characters may be:
+ *             1. The actual character itself.
+ *             2. A character preceded by ^ to specify a
+ *                control character (e.g. ^X means control-X).
+ *             3. A backslash followed by one to three octal digits
+ *                to specify a character by its octal value.
+ *             4. A backslash followed by b, e, n, r or t
+ *                to specify \b, ESC, \n, \r or \t, respectively.
+ *             5. Any character (other than those mentioned above) preceded 
+ *                by a \ to specify the character itself (characters which
+ *                must be preceded by \ include ^, \, and whitespace.
+ *     "action" is the name of a "less" action, from the table below.
+ *     "chars" is an optional sequence of characters which is treated
+ *             as keyboard input after the command is executed.
+ *
+ *     Blank lines and lines which start with # are ignored, 
+ *     except for the special control lines:
+ *             #command        Signals the beginning of the command
+ *                             keys section.
+ *             #line-edit      Signals the beginning of the line-editing
+ *                             keys section.
+ *             #env            Signals the beginning of the environment
+ *                             variable section.
+ *             #stop           Stops command parsing in less;
+ *                             causes all default keys to be disabled.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
+ *
+ *     The output file is a non-ascii file, consisting of a header,
+ *     one or more sections, and a trailer.
+ *     Each section begins with a section header, a section length word
+ *     and the section data.  Normally there are three sections:
+ *             CMD_SECTION     Definition of command keys.
+ *             EDIT_SECTION    Definition of editing keys.
+ *             END_SECTION     A special section header, with no 
+ *                             length word or section data.
+ *
+ *     Section data consists of zero or more byte sequences of the form:
+ *             string <0> <action>
+ *     or
+ *             string <0> <action|A_EXTRA> chars <0>
+ *
+ *     "string" is the command string.
+ *     "<0>" is one null byte.
+ *     "<action>" is one byte containing the action code (the A_xxx value).
+ *     If action is ORed with A_EXTRA, the action byte is followed
+ *             by the null-terminated "chars" string.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
+ */
+
+#include "less.h"
+#include "lesskey.h"
+#include "cmd.h"
+
+struct cmdname
+{
+       char *cn_name;
+       int cn_action;
+};
+
+struct cmdname cmdnames[] = 
+{
+       { "back-bracket",         A_B_BRACKET },
+       { "back-line",            A_B_LINE },
+       { "back-line-force",      A_BF_LINE },
+       { "back-screen",          A_B_SCREEN },
+       { "back-scroll",          A_B_SCROLL },
+       { "back-search",          A_B_SEARCH },
+       { "back-window",          A_B_WINDOW },
+       { "debug",                A_DEBUG },
+       { "digit",                A_DIGIT },
+       { "display-flag",         A_DISP_OPTION },
+       { "display-option",       A_DISP_OPTION },
+       { "end",                  A_GOEND },
+       { "examine",              A_EXAMINE },
+       { "filter",               A_FILTER },
+       { "first-cmd",            A_FIRSTCMD },
+       { "firstcmd",             A_FIRSTCMD },
+       { "flush-repaint",        A_FREPAINT },
+       { "forw-bracket",         A_F_BRACKET },
+       { "forw-forever",         A_F_FOREVER },
+       { "forw-line",            A_F_LINE },
+       { "forw-line-force",      A_FF_LINE },
+       { "forw-screen",          A_F_SCREEN },
+       { "forw-screen-force",    A_FF_SCREEN },
+       { "forw-scroll",          A_F_SCROLL },
+       { "forw-search",          A_F_SEARCH },
+       { "forw-window",          A_F_WINDOW },
+       { "goto-end",             A_GOEND },
+       { "goto-line",            A_GOLINE },
+       { "goto-mark",            A_GOMARK },
+       { "help",                 A_HELP },
+       { "index-file",           A_INDEX_FILE },
+       { "invalid",              A_UINVALID },
+       { "left-scroll",          A_LSHIFT },
+       { "next-file",            A_NEXT_FILE },
+       { "next-tag",             A_NEXT_TAG },
+       { "noaction",             A_NOACTION },
+       { "percent",              A_PERCENT },
+       { "pipe",                 A_PIPE },
+       { "prev-file",            A_PREV_FILE },
+       { "prev-tag",             A_PREV_TAG },
+       { "quit",                 A_QUIT },
+       { "remove-file",          A_REMOVE_FILE },
+       { "repaint",              A_REPAINT },
+       { "repaint-flush",        A_FREPAINT },
+       { "repeat-search",        A_AGAIN_SEARCH },
+       { "repeat-search-all",    A_T_AGAIN_SEARCH },
+       { "reverse-search",       A_REVERSE_SEARCH },
+       { "reverse-search-all",   A_T_REVERSE_SEARCH },
+       { "right-scroll",         A_RSHIFT },
+       { "set-mark",             A_SETMARK },
+       { "shell",                A_SHELL },
+       { "status",               A_STAT },
+       { "toggle-flag",          A_OPT_TOGGLE },
+       { "toggle-option",        A_OPT_TOGGLE },
+       { "undo-hilite",          A_UNDO_SEARCH },
+       { "version",              A_VERSION },
+       { "visual",               A_VISUAL },
+       { NULL,   0 }
+};
+
+struct cmdname editnames[] = 
+{
+       { "back-complete",      EC_B_COMPLETE },
+       { "backspace",          EC_BACKSPACE },
+       { "delete",             EC_DELETE },
+       { "down",               EC_DOWN },
+       { "end",                EC_END },
+       { "expand",             EC_EXPAND },
+       { "forw-complete",      EC_F_COMPLETE },
+       { "home",               EC_HOME },
+       { "insert",             EC_INSERT },
+       { "invalid",            EC_UINVALID },
+       { "kill-line",          EC_LINEKILL },
+       { "abort",              EC_ABORT },
+       { "left",               EC_LEFT },
+       { "literal",            EC_LITERAL },
+       { "right",              EC_RIGHT },
+       { "up",                 EC_UP },
+       { "word-backspace",     EC_W_BACKSPACE },
+       { "word-delete",        EC_W_DELETE },
+       { "word-left",          EC_W_LEFT },
+       { "word-right",         EC_W_RIGHT },
+       { NULL, 0 }
+};
+
+struct table
+{
+       struct cmdname *names;
+       char *pbuffer;
+       char buffer[MAX_USERCMD];
+};
+
+struct table cmdtable;
+struct table edittable;
+struct table vartable;
+struct table *currtable = &cmdtable;
+
+char fileheader[] = {
+       C0_LESSKEY_MAGIC, 
+       C1_LESSKEY_MAGIC, 
+       C2_LESSKEY_MAGIC, 
+       C3_LESSKEY_MAGIC
+};
+char filetrailer[] = {
+       C0_END_LESSKEY_MAGIC, 
+       C1_END_LESSKEY_MAGIC, 
+       C2_END_LESSKEY_MAGIC
+};
+char cmdsection[1] =   { CMD_SECTION };
+char editsection[1] =  { EDIT_SECTION };
+char varsection[1] =   { VAR_SECTION };
+char endsection[1] =   { END_SECTION };
+
+char *infile = NULL;
+char *outfile = NULL ;
+
+int linenum;
+int errors;
+
+extern char version[];
+
+       void
+usage()
+{
+       fprintf(stderr, "usage: lesskey [-o output] [input]\n");
+       exit(1);
+}
+
+       char *
+mkpathname(dirname, filename)
+       char *dirname;
+       char *filename;
+{
+       char *pathname;
+
+       pathname = calloc(strlen(dirname) + strlen(filename) + 2, sizeof(char));
+       strcpy(pathname, dirname);
+       strcat(pathname, PATHNAME_SEP);
+       strcat(pathname, filename);
+       return (pathname);
+}
+
+/*
+ * Figure out the name of a default file (in the user's HOME directory).
+ */
+       char *
+homefile(filename)
+       char *filename;
+{
+       char *p;
+       char *pathname;
+
+       if ((p = getenv("HOME")) != NULL && *p != '\0')
+               pathname = mkpathname(p, filename);
+#if OS2
+       else if ((p = getenv("INIT")) != NULL && *p != '\0')
+               pathname = mkpathname(p, filename);
+#endif
+       else
+       {
+               fprintf(stderr, "cannot find $HOME - using current directory\n");
+               pathname = mkpathname(".", filename);
+       }
+       return (pathname);
+}
+
+/*
+ * Parse command line arguments.
+ */
+       void
+parse_args(argc, argv)
+       int argc;
+       char **argv;
+{
+       char *arg;
+
+       outfile = NULL;
+       while (--argc > 0)
+       {
+               arg = *++argv;
+               if (arg[0] != '-')
+                       /* Arg does not start with "-"; it's not an option. */
+                       break;
+               if (arg[1] == '\0')
+                       /* "-" means standard input. */
+                       break;
+               if (arg[1] == '-' && arg[2] == '\0')
+               {
+                       /* "--" means end of options. */
+                       argc--;
+                       argv++;
+                       break;
+               }
+               switch (arg[1])
+               {
+               case '-':
+                       if (strncmp(arg, "--output", 8) == 0)
+                       {
+                               if (arg[8] == '\0')
+                                       outfile = &arg[8];
+                               else if (arg[8] == '=')
+                                       outfile = &arg[9];
+                               else
+                                       usage();
+                               goto opt_o;
+                       }
+                       if (strcmp(arg, "--version") == 0)
+                       {
+                               goto opt_V;
+                       }
+                       usage();
+                       break;
+               case 'o':
+                       outfile = &argv[0][2];
+               opt_o:
+                       if (*outfile == '\0')
+                       {
+                               if (--argc <= 0)
+                                       usage();
+                               outfile = *(++argv);
+                       }
+                       break;
+               case 'V':
+               opt_V:
+                       printf("lesskey  version %s\n", version);
+                       exit(0);
+               default:
+                       usage();
+               }
+       }
+       if (argc > 1)
+               usage();
+       /*
+        * Open the input file, or use DEF_LESSKEYINFILE if none specified.
+        */
+       if (argc > 0)
+               infile = *argv;
+       else
+               infile = homefile(DEF_LESSKEYINFILE);
+}
+
+/*
+ * Initialize data structures.
+ */
+       void
+init_tables()
+{
+       cmdtable.names = cmdnames;
+       cmdtable.pbuffer = cmdtable.buffer;
+
+       edittable.names = editnames;
+       edittable.pbuffer = edittable.buffer;
+
+       vartable.names = NULL;
+       vartable.pbuffer = vartable.buffer;
+}
+
+/*
+ * Parse one character of a string.
+ */
+       char *
+tstr(pp, xlate)
+       char **pp;
+       int xlate;
+{
+       register char *p;
+       register char ch;
+       register int i;
+       static char buf[10];
+       static char tstr_control_k[] =
+               { SK_SPECIAL_KEY, SK_CONTROL_K, 6, 1, 1, 1, '\0' };
+
+       p = *pp;
+       switch (*p)
+       {
+       case '\\':
+               ++p;
+               switch (*p)
+               {
+               case '0': case '1': case '2': case '3':
+               case '4': case '5': case '6': case '7':
+                       /*
+                        * Parse an octal number.
+                        */
+                       ch = 0;
+                       i = 0;
+                       do
+                               ch = 8*ch + (*p - '0');
+                       while (*++p >= '0' && *p <= '7' && ++i < 3);
+                       *pp = p;
+                       if (xlate && ch == CONTROL('K'))
+                               return tstr_control_k;
+                       buf[0] = ch;
+                       buf[1] = '\0';
+                       return (buf);
+               case 'b':
+                       *pp = p+1;
+                       return ("\b");
+               case 'e':
+                       *pp = p+1;
+                       buf[0] = ESC;
+                       buf[1] = '\0';
+                       return (buf);
+               case 'n':
+                       *pp = p+1;
+                       return ("\n");
+               case 'r':
+                       *pp = p+1;
+                       return ("\r");
+               case 't':
+                       *pp = p+1;
+                       return ("\t");
+               case 'k':
+                       if (xlate)
+                       {
+                               switch (*++p)
+                               {
+                               case 'u': ch = SK_UP_ARROW; break;
+                               case 'd': ch = SK_DOWN_ARROW; break;
+                               case 'r': ch = SK_RIGHT_ARROW; break;
+                               case 'l': ch = SK_LEFT_ARROW; break;
+                               case 'U': ch = SK_PAGE_UP; break;
+                               case 'D': ch = SK_PAGE_DOWN; break;
+                               case 'h': ch = SK_HOME; break;
+                               case 'e': ch = SK_END; break;
+                               case 'x': ch = SK_DELETE; break;
+                               default:
+                                       error("illegal char after \\k");
+                                       *pp = p+1;
+                                       return ("");
+                               }
+                               *pp = p+1;
+                               buf[0] = SK_SPECIAL_KEY;
+                               buf[1] = ch;
+                               buf[2] = 6;
+                               buf[3] = 1;
+                               buf[4] = 1;
+                               buf[5] = 1;
+                               buf[6] = '\0';
+                               return (buf);
+                       }
+                       /* FALLTHRU */
+               default:
+                       /*
+                        * Backslash followed by any other char 
+                        * just means that char.
+                        */
+                       *pp = p+1;
+                       buf[0] = *p;
+                       buf[1] = '\0';
+                       if (xlate && buf[0] == CONTROL('K'))
+                               return tstr_control_k;
+                       return (buf);
+               }
+       case '^':
+               /*
+                * Carat means CONTROL.
+                */
+               *pp = p+2;
+               buf[0] = CONTROL(p[1]);
+               buf[1] = '\0';
+               if (buf[0] == CONTROL('K'))
+                       return tstr_control_k;
+               return (buf);
+       }
+       *pp = p+1;
+       buf[0] = *p;
+       buf[1] = '\0';
+       if (xlate && buf[0] == CONTROL('K'))
+               return tstr_control_k;
+       return (buf);
+}
+
+/*
+ * Skip leading spaces in a string.
+ */
+       public char *
+skipsp(s)
+       register char *s;
+{
+       while (*s == ' ' || *s == '\t') 
+               s++;
+       return (s);
+}
+
+/*
+ * Skip non-space characters in a string.
+ */
+       public char *
+skipnsp(s)
+       register char *s;
+{
+       while (*s != '\0' && *s != ' ' && *s != '\t')
+               s++;
+       return (s);
+}
+
+/*
+ * Clean up an input line:
+ * strip off the trailing newline & any trailing # comment.
+ */
+       char *
+clean_line(s)
+       char *s;
+{
+       register int i;
+
+       s = skipsp(s);
+       for (i = 0;  s[i] != '\n' && s[i] != '\r' && s[i] != '\0';  i++)
+               if (s[i] == '#' && (i == 0 || s[i-1] != '\\'))
+                       break;
+       s[i] = '\0';
+       return (s);
+}
+
+/*
+ * Add a byte to the output command table.
+ */
+       void
+add_cmd_char(c)
+       int c;
+{
+       if (currtable->pbuffer >= currtable->buffer + MAX_USERCMD)
+       {
+               error("too many commands");
+               exit(1);
+       }
+       *(currtable->pbuffer)++ = c;
+}
+
+/*
+ * Add a string to the output command table.
+ */
+       void
+add_cmd_str(s)
+       char *s;
+{
+       for ( ;  *s != '\0';  s++)
+               add_cmd_char(*s);
+}
+
+/*
+ * See if we have a special "control" line.
+ */
+       int
+control_line(s)
+       char *s;
+{
+#define        PREFIX(str,pat) (strncmp(str,pat,strlen(pat)) == 0)
+
+       if (PREFIX(s, "#line-edit"))
+       {
+               currtable = &edittable;
+               return (1);
+       }
+       if (PREFIX(s, "#command"))
+       {
+               currtable = &cmdtable;
+               return (1);
+       }
+       if (PREFIX(s, "#env"))
+       {
+               currtable = &vartable;
+               return (1);
+       }
+       if (PREFIX(s, "#stop"))
+       {
+               add_cmd_char('\0');
+               add_cmd_char(A_END_LIST);
+               return (1);
+       }
+       return (0);
+}
+
+/*
+ * Output some bytes.
+ */
+       void
+fputbytes(fd, buf, len)
+       FILE *fd;
+       char *buf;
+       int len;
+{
+       while (len-- > 0)
+       {
+               fwrite(buf, sizeof(char), 1, fd);
+               buf++;
+       }
+}
+
+/*
+ * Output an integer, in special KRADIX form.
+ */
+       void
+fputint(fd, val)
+       FILE *fd;
+       unsigned int val;
+{
+       char c;
+
+       if (val >= KRADIX*KRADIX)
+       {
+               fprintf(stderr, "error: integer too big (%d > %d)\n", 
+                       val, KRADIX*KRADIX);
+               exit(1);
+       }
+       c = val % KRADIX;
+       fwrite(&c, sizeof(char), 1, fd);
+       c = val / KRADIX;
+       fwrite(&c, sizeof(char), 1, fd);
+}
+
+/*
+ * Find an action, given the name of the action.
+ */
+       int
+findaction(actname)
+       char *actname;
+{
+       int i;
+
+       for (i = 0;  currtable->names[i].cn_name != NULL;  i++)
+               if (strcmp(currtable->names[i].cn_name, actname) == 0)
+                       return (currtable->names[i].cn_action);
+       error("unknown action");
+       return (A_INVALID);
+}
+
+       void
+error(s)
+       char *s;
+{
+       fprintf(stderr, "line %d: %s\n", linenum, s);
+       errors++;
+}
+
+
+       void
+parse_cmdline(p)
+       char *p;
+{
+       int cmdlen;
+       char *actname;
+       int action;
+       char *s;
+       char c;
+
+       /*
+        * Parse the command string and store it in the current table.
+        */
+       cmdlen = 0;
+       do
+       {
+               s = tstr(&p, 1);
+               cmdlen += strlen(s);
+               if (cmdlen > MAX_CMDLEN)
+                       error("command too long");
+               else
+                       add_cmd_str(s);
+       } while (*p != ' ' && *p != '\t' && *p != '\0');
+       /*
+        * Terminate the command string with a null byte.
+        */
+       add_cmd_char('\0');
+
+       /*
+        * Skip white space between the command string
+        * and the action name.
+        * Terminate the action name with a null byte.
+        */
+       p = skipsp(p);
+       if (*p == '\0')
+       {
+               error("missing action");
+               return;
+       }
+       actname = p;
+       p = skipnsp(p);
+       c = *p;
+       *p = '\0';
+
+       /*
+        * Parse the action name and store it in the current table.
+        */
+       action = findaction(actname);
+
+       /*
+        * See if an extra string follows the action name.
+        */
+       *p = c;
+       p = skipsp(p);
+       if (*p == '\0')
+       {
+               add_cmd_char(action);
+       } else
+       {
+               /*
+                * OR the special value A_EXTRA into the action byte.
+                * Put the extra string after the action byte.
+                */
+               add_cmd_char(action | A_EXTRA);
+               while (*p != '\0')
+                       add_cmd_str(tstr(&p, 0));
+               add_cmd_char('\0');
+       }
+}
+
+       void
+parse_varline(p)
+       char *p;
+{
+       char *s;
+
+       do
+       {
+               s = tstr(&p, 0);
+               add_cmd_str(s);
+       } while (*p != ' ' && *p != '\t' && *p != '=' && *p != '\0');
+       /*
+        * Terminate the variable name with a null byte.
+        */
+       add_cmd_char('\0');
+
+       p = skipsp(p);
+       if (*p++ != '=')
+       {
+               error("missing =");
+               return;
+       }
+
+       add_cmd_char(EV_OK|A_EXTRA);
+
+       p = skipsp(p);
+       while (*p != '\0')
+       {
+               s = tstr(&p, 0);
+               add_cmd_str(s);
+       }
+       add_cmd_char('\0');
+}
+
+/*
+ * Parse a line from the lesskey file.
+ */
+       void
+parse_line(line)
+       char *line;
+{
+       char *p;
+
+       /*
+        * See if it is a control line.
+        */
+       if (control_line(line))
+               return;
+       /*
+        * Skip leading white space.
+        * Replace the final newline with a null byte.
+        * Ignore blank lines and comments.
+        */
+       p = clean_line(line);
+       if (*p == '\0')
+               return;
+
+       if (currtable == &vartable)
+               parse_varline(p);
+       else
+               parse_cmdline(p);
+}
+
+       int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       FILE *desc;
+       FILE *out;
+       char line[1024];
+
+#ifdef WIN32
+       if (getenv("HOME") == NULL)
+       {
+               /*
+                * If there is no HOME environment variable,
+                * try the concatenation of HOMEDRIVE + HOMEPATH.
+                */
+               char *drive = getenv("HOMEDRIVE");
+               char *path  = getenv("HOMEPATH");
+               if (drive != NULL && path != NULL)
+               {
+                       char *env = (char *) calloc(strlen(drive) + 
+                                       strlen(path) + 6, sizeof(char));
+                       strcpy(env, "HOME=");
+                       strcat(env, drive);
+                       strcat(env, path);
+                       putenv(env);
+               }
+       }
+#endif /* WIN32 */
+
+       /*
+        * Process command line arguments.
+        */
+       parse_args(argc, argv);
+       init_tables();
+
+       /*
+        * Open the input file.
+        */
+       if (strcmp(infile, "-") == 0)
+               desc = stdin;
+       else if ((desc = fopen(infile, "r")) == NULL)
+       {
+#if HAVE_PERROR
+               perror(infile);
+#else
+               fprintf(stderr, "Cannot open %s\n", infile);
+#endif
+               usage();
+       }
+
+       /*
+        * Read and parse the input file, one line at a time.
+        */
+       errors = 0;
+       linenum = 0;
+       while (fgets(line, sizeof(line), desc) != NULL)
+       {
+               ++linenum;
+               parse_line(line);
+       }
+
+       /*
+        * Write the output file.
+        * If no output file was specified, use "$HOME/.less"
+        */
+       if (errors > 0)
+       {
+               fprintf(stderr, "%d errors; no output produced\n", errors);
+               exit(1);
+       }
+
+       if (outfile == NULL)
+               outfile = getenv("LESSKEY");
+       if (outfile == NULL)
+               outfile = homefile(LESSKEYFILE);
+       if ((out = fopen(outfile, "wb")) == NULL)
+       {
+#if HAVE_PERROR
+               perror(outfile);
+#else
+               fprintf(stderr, "Cannot open %s\n", outfile);
+#endif
+               exit(1);
+       }
+
+       /* File header */
+       fputbytes(out, fileheader, sizeof(fileheader));
+
+       /* Command key section */
+       fputbytes(out, cmdsection, sizeof(cmdsection));
+       fputint(out, cmdtable.pbuffer - cmdtable.buffer);
+       fputbytes(out, (char *)cmdtable.buffer, cmdtable.pbuffer-cmdtable.buffer);
+       /* Edit key section */
+       fputbytes(out, editsection, sizeof(editsection));
+       fputint(out, edittable.pbuffer - edittable.buffer);
+       fputbytes(out, (char *)edittable.buffer, edittable.pbuffer-edittable.buffer);
+
+       /* Environment variable section */
+       fputbytes(out, varsection, sizeof(varsection)); 
+       fputint(out, vartable.pbuffer - vartable.buffer);
+       fputbytes(out, (char *)vartable.buffer, vartable.pbuffer-vartable.buffer);
+
+       /* File trailer */
+       fputbytes(out, endsection, sizeof(endsection));
+       fputbytes(out, filetrailer, sizeof(filetrailer));
+       return (0);
+}
diff --git a/thirdparty/less/lesskey.h b/thirdparty/less/lesskey.h
new file mode 100644 (file)
index 0000000..9a457a9
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Format of a lesskey file:
+ *
+ *     LESSKEY_MAGIC (4 bytes)
+ *      sections...
+ *     END_LESSKEY_MAGIC (4 bytes)
+ *
+ * Each section is:
+ *
+ *     section_MAGIC (1 byte)
+ *     section_length (2 bytes)
+ *     key table (section_length bytes)
+ */
+#define        C0_LESSKEY_MAGIC        '\0'
+#define        C1_LESSKEY_MAGIC        'M'
+#define        C2_LESSKEY_MAGIC        '+'
+#define        C3_LESSKEY_MAGIC        'G'
+
+#define        CMD_SECTION             'c'
+#define        EDIT_SECTION            'e'
+#define        VAR_SECTION             'v'
+#define        END_SECTION             'x'
+
+#define        C0_END_LESSKEY_MAGIC    'E'
+#define        C1_END_LESSKEY_MAGIC    'n'
+#define        C2_END_LESSKEY_MAGIC    'd'
+
+/* */
+#define        KRADIX          64
diff --git a/thirdparty/less/lglob.h b/thirdparty/less/lglob.h
new file mode 100644 (file)
index 0000000..82b9543
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Macros to define the method of doing filename "globbing".
+ * There are three possible mechanisms:
+ *   1.        GLOB_LIST
+ *     This defines a function that returns a list of matching filenames.
+ *   2. GLOB_NAME
+ *     This defines a function that steps thru the list of matching
+ *     filenames, returning one name each time it is called.
+ *   3. GLOB_STRING
+ *     This defines a function that returns the complete list of
+ *     matching filenames as a single space-separated string.
+ */
+
+#if OS2
+
+#define        DECL_GLOB_LIST(list)            char **list;  char **pp;
+#define        GLOB_LIST(filename,list)        list = _fnexplode(filename)
+#define        GLOB_LIST_FAILED(list)          list == NULL
+#define        SCAN_GLOB_LIST(list,p)          pp = list;  *pp != NULL;  pp++
+#define        INIT_GLOB_LIST(list,p)          p = *pp
+#define        GLOB_LIST_DONE(list)            _fnexplodefree(list)
+
+#else
+#if MSDOS_COMPILER==DJGPPC
+
+#define        DECL_GLOB_LIST(list)            glob_t list;  int i;
+#define        GLOB_LIST(filename,list)        glob(filename,GLOB_NOCHECK,0,&list)
+#define        GLOB_LIST_FAILED(list)          0
+#define        SCAN_GLOB_LIST(list,p)          i = 0;  i < list.gl_pathc;  i++
+#define        INIT_GLOB_LIST(list,p)          p = list.gl_pathv[i]
+#define        GLOB_LIST_DONE(list)            globfree(&list)
+
+#else
+#if MSDOS_COMPILER==MSOFTC || MSDOS_COMPILER==BORLANDC
+
+#define        GLOB_FIRST_NAME(filename,fndp,h) h = _dos_findfirst(filename, ~_A_VOLID, fndp)
+#define        GLOB_FIRST_FAILED(handle)       ((handle) != 0)
+#define        GLOB_NEXT_NAME(handle,fndp)             _dos_findnext(fndp)
+#define        GLOB_NAME_DONE(handle)
+#define        GLOB_NAME                       name
+#define        DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle) \
+                                       struct find_t fnd;      \
+                                       char drive[_MAX_DRIVE]; \
+                                       char dir[_MAX_DIR];     \
+                                       char fname[_MAX_FNAME]; \
+                                       char ext[_MAX_EXT];     \
+                                       int handle;
+#else
+#if MSDOS_COMPILER==WIN32C && (defined(_MSC_VER) || defined(__MINGW2__))
+
+#define        GLOB_FIRST_NAME(filename,fndp,h) h = _findfirst(filename, fndp)
+#define        GLOB_FIRST_FAILED(handle)       ((handle) == -1)
+#define        GLOB_NEXT_NAME(handle,fndp)     _findnext(handle, fndp)
+#define        GLOB_NAME_DONE(handle)          _findclose(handle)
+#define        GLOB_NAME                       name
+#define        DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle) \
+                                       struct _finddata_t fnd; \
+                                       char drive[_MAX_DRIVE]; \
+                                       char dir[_MAX_DIR];     \
+                                       char fname[_MAX_FNAME]; \
+                                       char ext[_MAX_EXT];     \
+                                       long handle;
+
+#else
+#if MSDOS_COMPILER==WIN32C && defined(__BORLANDC__) /* Borland C for Windows */
+
+#define        GLOB_FIRST_NAME(filename,fndp,h) h = findfirst(filename, fndp, ~FA_LABEL)
+#define        GLOB_FIRST_FAILED(handle)       ((handle) != 0)
+#define        GLOB_NEXT_NAME(handle,fndp)     findnext(fndp)
+#define        GLOB_NAME_DONE(handle)
+#define        GLOB_NAME                       ff_name
+#define        DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle) \
+                                       struct ffblk fnd;       \
+                                       char drive[MAXDRIVE];   \
+                                       char dir[MAXDIR];       \
+                                       char fname[MAXFILE];    \
+                                       char ext[MAXEXT];       \
+                                       int handle;
+
+#endif
+#endif
+#endif
+#endif
+#endif
diff --git a/thirdparty/less/line.c b/thirdparty/less/line.c
new file mode 100644 (file)
index 0000000..798d4f2
--- /dev/null
@@ -0,0 +1,1244 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Routines to manipulate the "line buffer".
+ * The line buffer holds a line of output as it is being built
+ * in preparation for output to the screen.
+ */
+
+#include "less.h"
+#include "charset.h"
+
+static char *linebuf = NULL;   /* Buffer which holds the current output line */
+static char *attr = NULL;      /* Extension of linebuf to hold attributes */
+public int size_linebuf = 0;   /* Size of line buffer (and attr buffer) */
+
+static int cshift;             /* Current left-shift of output line buffer */
+public int hshift;             /* Desired left-shift of output line buffer */
+public int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */
+public int ntabstops = 1;      /* Number of tabstops */
+public int tabdefault = 8;     /* Default repeated tabstops */
+
+static int curr;               /* Index into linebuf */
+static int column;             /* Printable length, accounting for
+                                  backspaces, etc. */
+static int overstrike;         /* Next char should overstrike previous char */
+static int last_overstrike = AT_NORMAL;
+static int is_null_line;       /* There is no current line */
+static int lmargin;            /* Left margin */
+static char pendc;
+static POSITION pendpos;
+static char *end_ansi_chars;
+static char *mid_ansi_chars;
+
+static int attr_swidth();
+static int attr_ewidth();
+static int do_append();
+
+extern int sigs;
+extern int bs_mode;
+extern int linenums;
+extern int ctldisp;
+extern int twiddle;
+extern int binattr;
+extern int status_col;
+extern int auto_wrap, ignaw;
+extern int bo_s_width, bo_e_width;
+extern int ul_s_width, ul_e_width;
+extern int bl_s_width, bl_e_width;
+extern int so_s_width, so_e_width;
+extern int sc_width, sc_height;
+extern int utf_mode;
+extern POSITION start_attnpos;
+extern POSITION end_attnpos;
+
+static char mbc_buf[MAX_UTF_CHAR_LEN];
+static int mbc_buf_len = 0;
+static int mbc_buf_index = 0;
+static POSITION mbc_pos;
+
+/*
+ * Initialize from environment variables.
+ */
+       public void
+init_line()
+{
+       end_ansi_chars = lgetenv("LESSANSIENDCHARS");
+       if (end_ansi_chars == NULL || *end_ansi_chars == '\0')
+               end_ansi_chars = "m";
+
+       mid_ansi_chars = lgetenv("LESSANSIMIDCHARS");
+       if (mid_ansi_chars == NULL || *mid_ansi_chars == '\0')
+               mid_ansi_chars = "0123456789;[?!\"'#%()*+ ";
+
+       linebuf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
+       attr = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
+       size_linebuf = LINEBUF_SIZE;
+}
+
+/*
+ * Expand the line buffer.
+ */
+       static int
+expand_linebuf()
+{
+       /* Double the size of the line buffer. */
+       int new_size = size_linebuf * 2;
+
+       /* Just realloc to expand the buffer, if we can. */
+#if HAVE_REALLOC
+       char *new_buf = (char *) realloc(linebuf, new_size);
+       char *new_attr = (char *) realloc(attr, new_size);
+#else
+       char *new_buf = (char *) calloc(new_size, sizeof(char));
+       char *new_attr = (char *) calloc(new_size, sizeof(char));
+#endif
+       if (new_buf == NULL || new_attr == NULL)
+       {
+               if (new_attr != NULL)
+                       free(new_attr);
+               if (new_buf != NULL)
+                       free(new_buf);
+               return 1;
+       }
+#if HAVE_REALLOC
+       /*
+        * We realloc'd the buffers; they already have the old contents.
+        */
+       #if 0
+       memset(new_buf + size_linebuf, 0, new_size - size_linebuf);
+       memset(new_attr + size_linebuf, 0, new_size - size_linebuf);
+       #endif
+#else
+       /*
+        * We just calloc'd the buffers; copy the old contents.
+        */
+       memcpy(new_buf, linebuf, size_linebuf * sizeof(char));
+       memcpy(new_attr, attr, size_linebuf * sizeof(char));
+       free(attr);
+       free(linebuf);
+#endif
+       linebuf = new_buf;
+       attr = new_attr;
+       size_linebuf = new_size;
+       return 0;
+}
+
+/*
+ * Is a character ASCII?
+ */
+       public int
+is_ascii_char(ch)
+       LWCHAR ch;
+{
+       return (ch <= 0x7F);
+}
+
+/*
+ * Rewind the line buffer.
+ */
+       public void
+prewind()
+{
+       curr = 0;
+       column = 0;
+       cshift = 0;
+       overstrike = 0;
+       last_overstrike = AT_NORMAL;
+       mbc_buf_len = 0;
+       is_null_line = 0;
+       pendc = '\0';
+       lmargin = 0;
+       if (status_col)
+               lmargin += 1;
+}
+
+/*
+ * Insert the line number (of the given position) into the line buffer.
+ */
+       public void
+plinenum(pos)
+       POSITION pos;
+{
+       register LINENUM linenum = 0;
+       register int i;
+
+       if (linenums == OPT_ONPLUS)
+       {
+               /*
+                * Get the line number and put it in the current line.
+                * {{ Note: since find_linenum calls forw_raw_line,
+                *    it may seek in the input file, requiring the caller 
+                *    of plinenum to re-seek if necessary. }}
+                * {{ Since forw_raw_line modifies linebuf, we must
+                *    do this first, before storing anything in linebuf. }}
+                */
+               linenum = find_linenum(pos);
+       }
+
+       /*
+        * Display a status column if the -J option is set.
+        */
+       if (status_col)
+       {
+               linebuf[curr] = ' ';
+               if (start_attnpos != NULL_POSITION &&
+                   pos >= start_attnpos && pos < end_attnpos)
+                       attr[curr] = AT_NORMAL|AT_HILITE;
+               else
+                       attr[curr] = AT_NORMAL;
+               curr++;
+               column++;
+       }
+       /*
+        * Display the line number at the start of each line
+        * if the -N option is set.
+        */
+       if (linenums == OPT_ONPLUS)
+       {
+               char buf[INT_STRLEN_BOUND(pos) + 2];
+               int n;
+
+               linenumtoa(linenum, buf);
+               n = strlen(buf);
+               if (n < MIN_LINENUM_WIDTH)
+                       n = MIN_LINENUM_WIDTH;
+               sprintf(linebuf+curr, "%*s ", n, buf);
+               n++;  /* One space after the line number. */
+               for (i = 0; i < n; i++)
+                       attr[curr+i] = AT_NORMAL;
+               curr += n;
+               column += n;
+               lmargin += n;
+       }
+
+       /*
+        * Append enough spaces to bring us to the lmargin.
+        */
+       while (column < lmargin)
+       {
+               linebuf[curr] = ' ';
+               attr[curr++] = AT_NORMAL;
+               column++;
+       }
+}
+
+/*
+ * Shift the input line left.
+ * This means discarding N printable chars at the start of the buffer.
+ */
+       static void
+pshift(shift)
+       int shift;
+{
+       LWCHAR prev_ch = 0;
+       unsigned char c;
+       int shifted = 0;
+       int to;
+       int from;
+       int len;
+       int width;
+       int prev_attr;
+       int next_attr;
+
+       if (shift > column - lmargin)
+               shift = column - lmargin;
+       if (shift > curr - lmargin)
+               shift = curr - lmargin;
+
+       to = from = lmargin;
+       /*
+        * We keep on going when shifted == shift
+        * to get all combining chars.
+        */
+       while (shifted <= shift && from < curr)
+       {
+               c = linebuf[from];
+               if (ctldisp == OPT_ONPLUS && IS_CSI_START(c))
+               {
+                       /* Keep cumulative effect.  */
+                       linebuf[to] = c;
+                       attr[to++] = attr[from++];
+                       while (from < curr && linebuf[from])
+                       {
+                               linebuf[to] = linebuf[from];
+                               attr[to++] = attr[from];
+                               if (!is_ansi_middle(linebuf[from++]))
+                                       break;
+                       } 
+                       continue;
+               }
+
+               width = 0;
+
+               if (!IS_ASCII_OCTET(c) && utf_mode)
+               {
+                       /* Assumes well-formedness validation already done.  */
+                       LWCHAR ch;
+
+                       len = utf_len(c);
+                       if (from + len > curr)
+                               break;
+                       ch = get_wchar(linebuf + from);
+                       if (!is_composing_char(ch) && !is_combining_char(prev_ch, ch))
+                               width = is_wide_char(ch) ? 2 : 1;
+                       prev_ch = ch;
+               } else
+               {
+                       len = 1;
+                       if (c == '\b')
+                               /* XXX - Incorrect if several '\b' in a row.  */
+                               width = (utf_mode && is_wide_char(prev_ch)) ? -2 : -1;
+                       else if (!control_char(c))
+                               width = 1;
+                       prev_ch = 0;
+               }
+
+               if (width == 2 && shift - shifted == 1) {
+                       /* Should never happen when called by pshift_all().  */
+                       attr[to] = attr[from];
+                       /*
+                        * Assume a wide_char will never be the first half of a
+                        * combining_char pair, so reset prev_ch in case we're
+                        * followed by a '\b'.
+                        */
+                       prev_ch = linebuf[to++] = ' ';
+                       from += len;
+                       shifted++;
+                       continue;
+               }
+
+               /* Adjust width for magic cookies. */
+               prev_attr = (to > 0) ? attr[to-1] : AT_NORMAL;
+               next_attr = (from + len < curr) ? attr[from + len] : prev_attr;
+               if (!is_at_equiv(attr[from], prev_attr) && 
+                       !is_at_equiv(attr[from], next_attr))
+               {
+                       width += attr_swidth(attr[from]);
+                       if (from + len < curr)
+                               width += attr_ewidth(attr[from]);
+                       if (is_at_equiv(prev_attr, next_attr))
+                       {
+                               width += attr_ewidth(prev_attr);
+                               if (from + len < curr)
+                                       width += attr_swidth(next_attr);
+                       }
+               }
+
+               if (shift - shifted < width)
+                       break;
+               from += len;
+               shifted += width;
+               if (shifted < 0)
+                       shifted = 0;
+       }
+       while (from < curr)
+       {
+               linebuf[to] = linebuf[from];
+               attr[to++] = attr[from++];
+       }
+       curr = to;
+       column -= shifted;
+       cshift += shifted;
+}
+
+/*
+ *
+ */
+       public void
+pshift_all()
+{
+       pshift(column);
+}
+
+/*
+ * Return the printing width of the start (enter) sequence
+ * for a given character attribute.
+ */
+       static int
+attr_swidth(a)
+       int a;
+{
+       int w = 0;
+
+       a = apply_at_specials(a);
+
+       if (a & AT_UNDERLINE)
+               w += ul_s_width;
+       if (a & AT_BOLD)
+               w += bo_s_width;
+       if (a & AT_BLINK)
+               w += bl_s_width;
+       if (a & AT_STANDOUT)
+               w += so_s_width;
+
+       return w;
+}
+
+/*
+ * Return the printing width of the end (exit) sequence
+ * for a given character attribute.
+ */
+       static int
+attr_ewidth(a)
+       int a;
+{
+       int w = 0;
+
+       a = apply_at_specials(a);
+
+       if (a & AT_UNDERLINE)
+               w += ul_e_width;
+       if (a & AT_BOLD)
+               w += bo_e_width;
+       if (a & AT_BLINK)
+               w += bl_e_width;
+       if (a & AT_STANDOUT)
+               w += so_e_width;
+
+       return w;
+}
+
+/*
+ * Return the printing width of a given character and attribute,
+ * if the character were added to the current position in the line buffer.
+ * Adding a character with a given attribute may cause an enter or exit
+ * attribute sequence to be inserted, so this must be taken into account.
+ */
+       static int
+pwidth(ch, a, prev_ch)
+       LWCHAR ch;
+       int a;
+       LWCHAR prev_ch;
+{
+       int w;
+
+       if (ch == '\b')
+               /*
+                * Backspace moves backwards one or two positions.
+                * XXX - Incorrect if several '\b' in a row.
+                */
+               return (utf_mode && is_wide_char(prev_ch)) ? -2 : -1;
+
+       if (!utf_mode || is_ascii_char(ch))
+       {
+               if (control_char((char)ch))
+               {
+                       /*
+                        * Control characters do unpredictable things,
+                        * so we don't even try to guess; say it doesn't move.
+                        * This can only happen if the -r flag is in effect.
+                        */
+                       return (0);
+               }
+       } else
+       {
+               if (is_composing_char(ch) || is_combining_char(prev_ch, ch))
+               {
+                       /*
+                        * Composing and combining chars take up no space.
+                        *
+                        * Some terminals, upon failure to compose a
+                        * composing character with the character(s) that
+                        * precede(s) it will actually take up one column
+                        * for the composing character; there isn't much
+                        * we could do short of testing the (complex)
+                        * composition process ourselves and printing
+                        * a binary representation when it fails.
+                        */
+                       return (0);
+               }
+       }
+
+       /*
+        * Other characters take one or two columns,
+        * plus the width of any attribute enter/exit sequence.
+        */
+       w = 1;
+       if (is_wide_char(ch))
+               w++;
+       if (curr > 0 && !is_at_equiv(attr[curr-1], a))
+               w += attr_ewidth(attr[curr-1]);
+       if ((apply_at_specials(a) != AT_NORMAL) &&
+           (curr == 0 || !is_at_equiv(attr[curr-1], a)))
+               w += attr_swidth(a);
+       return (w);
+}
+
+/*
+ * Delete to the previous base character in the line buffer.
+ * Return 1 if one is found.
+ */
+       static int
+backc()
+{
+       LWCHAR prev_ch;
+       char *p = linebuf + curr;
+       LWCHAR ch = step_char(&p, -1, linebuf + lmargin);
+       int width;
+
+       /* This assumes that there is no '\b' in linebuf.  */
+       while (   curr > lmargin
+              && column > lmargin
+              && (!(attr[curr - 1] & (AT_ANSI|AT_BINARY))))
+       {
+               curr = p - linebuf;
+               prev_ch = step_char(&p, -1, linebuf + lmargin);
+               width = pwidth(ch, attr[curr], prev_ch);
+               column -= width;
+               if (width > 0)
+                       return 1;
+               ch = prev_ch;
+       }
+
+       return 0;
+}
+
+/*
+ * Are we currently within a recognized ANSI escape sequence?
+ */
+       static int
+in_ansi_esc_seq()
+{
+       char *p;
+
+       /*
+        * Search backwards for either an ESC (which means we ARE in a seq);
+        * or an end char (which means we're NOT in a seq).
+        */
+       for (p = &linebuf[curr];  p > linebuf; )
+       {
+               LWCHAR ch = step_char(&p, -1, linebuf);
+               if (IS_CSI_START(ch))
+                       return (1);
+               if (!is_ansi_middle(ch))
+                       return (0);
+       }
+       return (0);
+}
+
+/*
+ * Is a character the end of an ANSI escape sequence?
+ */
+       public int
+is_ansi_end(ch)
+       LWCHAR ch;
+{
+       if (!is_ascii_char(ch))
+               return (0);
+       return (strchr(end_ansi_chars, (char) ch) != NULL);
+}
+
+/*
+ *
+ */
+       public int
+is_ansi_middle(ch)
+       LWCHAR ch;
+{
+       if (!is_ascii_char(ch))
+               return (0);
+       if (is_ansi_end(ch))
+               return (0);
+       return (strchr(mid_ansi_chars, (char) ch) != NULL);
+}
+
+/*
+ * Append a character and attribute to the line buffer.
+ */
+#define        STORE_CHAR(ch,a,rep,pos) \
+       do { \
+               if (store_char((ch),(a),(rep),(pos))) return (1); \
+       } while (0)
+
+       static int
+store_char(ch, a, rep, pos)
+       LWCHAR ch;
+       int a;
+       char *rep;
+       POSITION pos;
+{
+       int w;
+       int replen;
+       char cs;
+
+       w = (a & (AT_UNDERLINE|AT_BOLD));       /* Pre-use w.  */
+       if (w != AT_NORMAL)
+               last_overstrike = w;
+
+#if HILITE_SEARCH
+       {
+               int matches;
+               if (is_hilited(pos, pos+1, 0, &matches))
+               {
+                       /*
+                        * This character should be highlighted.
+                        * Override the attribute passed in.
+                        */
+                       if (a != AT_ANSI)
+                               a |= AT_HILITE;
+               }
+       }
+#endif
+
+       if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq())
+       {
+               if (!is_ansi_end(ch) && !is_ansi_middle(ch)) {
+                       /* Remove whole unrecognized sequence.  */
+                       char *p = &linebuf[curr];
+                       LWCHAR bch;
+                       do {
+                               bch = step_char(&p, -1, linebuf);
+                       } while (p > linebuf && !IS_CSI_START(bch));
+                       curr = p - linebuf;
+                       return 0;
+               }
+               a = AT_ANSI;    /* Will force re-AT_'ing around it.  */
+               w = 0;
+       }
+       else if (ctldisp == OPT_ONPLUS && IS_CSI_START(ch))
+       {
+               a = AT_ANSI;    /* Will force re-AT_'ing around it.  */
+               w = 0;
+       }
+       else
+       {
+               char *p = &linebuf[curr];
+               LWCHAR prev_ch = step_char(&p, -1, linebuf);
+               w = pwidth(ch, a, prev_ch);
+       }
+
+       if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width)
+               /*
+                * Won't fit on screen.
+                */
+               return (1);
+
+       if (rep == NULL)
+       {
+               cs = (char) ch;
+               rep = &cs;
+               replen = 1;
+       } else
+       {
+               replen = utf_len(rep[0]);
+       }
+       if (curr + replen >= size_linebuf-6)
+       {
+               /*
+                * Won't fit in line buffer.
+                * Try to expand it.
+                */
+               if (expand_linebuf())
+                       return (1);
+       }
+
+       while (replen-- > 0)
+       {
+               linebuf[curr] = *rep++;
+               attr[curr] = a;
+               curr++;
+       }
+       column += w;
+       return (0);
+}
+
+/*
+ * Append a tab to the line buffer.
+ * Store spaces to represent the tab.
+ */
+#define        STORE_TAB(a,pos) \
+       do { if (store_tab((a),(pos))) return (1); } while (0)
+
+       static int
+store_tab(attr, pos)
+       int attr;
+       POSITION pos;
+{
+       int to_tab = column + cshift - lmargin;
+       int i;
+
+       if (ntabstops < 2 || to_tab >= tabstops[ntabstops-1])
+               to_tab = tabdefault -
+                    ((to_tab - tabstops[ntabstops-1]) % tabdefault);
+       else
+       {
+               for (i = ntabstops - 2;  i >= 0;  i--)
+                       if (to_tab >= tabstops[i])
+                               break;
+               to_tab = tabstops[i+1] - to_tab;
+       }
+
+       if (column + to_tab - 1 + pwidth(' ', attr, 0) + attr_ewidth(attr) > sc_width)
+               return 1;
+
+       do {
+               STORE_CHAR(' ', attr, " ", pos);
+       } while (--to_tab > 0);
+       return 0;
+}
+
+#define STORE_PRCHAR(c, pos) \
+       do { if (store_prchar((c), (pos))) return 1; } while (0)
+
+       static int
+store_prchar(c, pos)
+       char c;
+       POSITION pos;
+{
+       char *s;
+
+       /*
+        * Convert to printable representation.
+        */
+       s = prchar(c);
+
+       /*
+        * Make sure we can get the entire representation
+        * of the character on this line.
+        */
+       if (column + (int) strlen(s) - 1 +
+            pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width)
+               return 1;
+
+       for ( ;  *s != 0;  s++)
+               STORE_CHAR(*s, AT_BINARY, NULL, pos);
+
+       return 0;
+}
+
+       static int
+flush_mbc_buf(pos)
+       POSITION pos;
+{
+       int i;
+
+       for (i = 0; i < mbc_buf_index; i++)
+               if (store_prchar(mbc_buf[i], pos))
+                       return mbc_buf_index - i;
+
+       return 0;
+}
+
+/*
+ * Append a character to the line buffer.
+ * Expand tabs into spaces, handle underlining, boldfacing, etc.
+ * Returns 0 if ok, 1 if couldn't fit in buffer.
+ */
+       public int
+pappend(c, pos)
+       char c;
+       POSITION pos;
+{
+       int r;
+
+       if (pendc)
+       {
+               if (do_append(pendc, NULL, pendpos))
+                       /*
+                        * Oops.  We've probably lost the char which
+                        * was in pendc, since caller won't back up.
+                        */
+                       return (1);
+               pendc = '\0';
+       }
+
+       if (c == '\r' && bs_mode == BS_SPECIAL)
+       {
+               if (mbc_buf_len > 0)  /* utf_mode must be on. */
+               {
+                       /* Flush incomplete (truncated) sequence. */
+                       r = flush_mbc_buf(mbc_pos);
+                       mbc_buf_index = r + 1;
+                       mbc_buf_len = 0;
+                       if (r)
+                               return (mbc_buf_index);
+               }
+
+               /*
+                * Don't put the CR into the buffer until we see 
+                * the next char.  If the next char is a newline,
+                * discard the CR.
+                */
+               pendc = c;
+               pendpos = pos;
+               return (0);
+       }
+
+       if (!utf_mode)
+       {
+               r = do_append((LWCHAR) c, NULL, pos);
+       } else
+       {
+               /* Perform strict validation in all possible cases. */
+               if (mbc_buf_len == 0)
+               {
+               retry:
+                       mbc_buf_index = 1;
+                       *mbc_buf = c;
+                       if (IS_ASCII_OCTET(c))
+                               r = do_append((LWCHAR) c, NULL, pos);
+                       else if (IS_UTF8_LEAD(c))
+                       {
+                               mbc_buf_len = utf_len(c);
+                               mbc_pos = pos;
+                               return (0);
+                       } else
+                               /* UTF8_INVALID or stray UTF8_TRAIL */
+                               r = flush_mbc_buf(pos);
+               } else if (IS_UTF8_TRAIL(c))
+               {
+                       mbc_buf[mbc_buf_index++] = c;
+                       if (mbc_buf_index < mbc_buf_len)
+                               return (0);
+                       if (is_utf8_well_formed(mbc_buf))
+                               r = do_append(get_wchar(mbc_buf), mbc_buf, mbc_pos);
+                       else
+                               /* Complete, but not shortest form, sequence. */
+                               mbc_buf_index = r = flush_mbc_buf(mbc_pos);
+                       mbc_buf_len = 0;
+               } else
+               {
+                       /* Flush incomplete (truncated) sequence.  */
+                       r = flush_mbc_buf(mbc_pos);
+                       mbc_buf_index = r + 1;
+                       mbc_buf_len = 0;
+                       /* Handle new char.  */
+                       if (!r)
+                               goto retry;
+               }
+       }
+
+       /*
+        * If we need to shift the line, do it.
+        * But wait until we get to at least the middle of the screen,
+        * so shifting it doesn't affect the chars we're currently
+        * pappending.  (Bold & underline can get messed up otherwise.)
+        */
+       if (cshift < hshift && column > sc_width / 2)
+       {
+               linebuf[curr] = '\0';
+               pshift(hshift - cshift);
+       }
+       if (r)
+       {
+               /* How many chars should caller back up? */
+               r = (!utf_mode) ? 1 : mbc_buf_index;
+       }
+       return (r);
+}
+
+       static int
+do_append(ch, rep, pos)
+       LWCHAR ch;
+       char *rep;
+       POSITION pos;
+{
+       register int a;
+       LWCHAR prev_ch;
+
+       a = AT_NORMAL;
+
+       if (ch == '\b')
+       {
+               if (bs_mode == BS_CONTROL)
+                       goto do_control_char;
+
+               /*
+                * A better test is needed here so we don't
+                * backspace over part of the printed
+                * representation of a binary character.
+                */
+               if (   curr <= lmargin
+                   || column <= lmargin
+                   || (attr[curr - 1] & (AT_ANSI|AT_BINARY)))
+                       STORE_PRCHAR('\b', pos);
+               else if (bs_mode == BS_NORMAL)
+                       STORE_CHAR(ch, AT_NORMAL, NULL, pos);
+               else if (bs_mode == BS_SPECIAL)
+                       overstrike = backc();
+
+               return 0;
+       }
+
+       if (overstrike > 0)
+       {
+               /*
+                * Overstrike the character at the current position
+                * in the line buffer.  This will cause either 
+                * underline (if a "_" is overstruck), 
+                * bold (if an identical character is overstruck),
+                * or just deletion of the character in the buffer.
+                */
+               overstrike = utf_mode ? -1 : 0;
+               /* To be correct, this must be a base character.  */
+               prev_ch = get_wchar(linebuf + curr);
+               a = attr[curr];
+               if (ch == prev_ch)
+               {
+                       /*
+                        * Overstriking a char with itself means make it bold.
+                        * But overstriking an underscore with itself is
+                        * ambiguous.  It could mean make it bold, or
+                        * it could mean make it underlined.
+                        * Use the previous overstrike to resolve it.
+                        */
+                       if (ch == '_')
+                       {
+                               if ((a & (AT_BOLD|AT_UNDERLINE)) != AT_NORMAL)
+                                       a |= (AT_BOLD|AT_UNDERLINE);
+                               else if (last_overstrike != AT_NORMAL)
+                                       a |= last_overstrike;
+                               else
+                                       a |= AT_BOLD;
+                       } else
+                               a |= AT_BOLD;
+               } else if (ch == '_')
+               {
+                       a |= AT_UNDERLINE;
+                       ch = prev_ch;
+                       rep = linebuf + curr;
+               } else if (prev_ch == '_')
+               {
+                       a |= AT_UNDERLINE;
+               }
+               /* Else we replace prev_ch, but we keep its attributes.  */
+       } else if (overstrike < 0)
+       {
+               if (   is_composing_char(ch)
+                   || is_combining_char(get_wchar(linebuf + curr), ch))
+                       /* Continuation of the same overstrike.  */
+                       a = last_overstrike;
+               else
+                       overstrike = 0;
+       }
+
+       if (ch == '\t') 
+       {
+               /*
+                * Expand a tab into spaces.
+                */
+               switch (bs_mode)
+               {
+               case BS_CONTROL:
+                       goto do_control_char;
+               case BS_NORMAL:
+               case BS_SPECIAL:
+                       STORE_TAB(a, pos);
+                       break;
+               }
+       } else if ((!utf_mode || is_ascii_char(ch)) && control_char((char)ch))
+       {
+       do_control_char:
+               if (ctldisp == OPT_ON || (ctldisp == OPT_ONPLUS && IS_CSI_START(ch)))
+               {
+                       /*
+                        * Output as a normal character.
+                        */
+                       STORE_CHAR(ch, AT_NORMAL, rep, pos);
+               } else 
+               {
+                       STORE_PRCHAR((char) ch, pos);
+               }
+       } else if (utf_mode && ctldisp != OPT_ON && is_ubin_char(ch))
+       {
+               char *s;
+
+               s = prutfchar(ch);
+
+               if (column + (int) strlen(s) - 1 +
+                   pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width)
+                       return (1);
+
+               for ( ;  *s != 0;  s++)
+                       STORE_CHAR(*s, AT_BINARY, NULL, pos);
+       } else
+       {
+               STORE_CHAR(ch, a, rep, pos);
+       }
+       return (0);
+}
+
+/*
+ *
+ */
+       public int
+pflushmbc()
+{
+       int r = 0;
+
+       if (mbc_buf_len > 0)
+       {
+               /* Flush incomplete (truncated) sequence.  */
+               r = flush_mbc_buf(mbc_pos);
+               mbc_buf_len = 0;
+       }
+       return r;
+}
+
+/*
+ * Terminate the line in the line buffer.
+ */
+       public void
+pdone(endline, forw)
+       int endline;
+       int forw;
+{
+       (void) pflushmbc();
+
+       if (pendc && (pendc != '\r' || !endline))
+               /*
+                * If we had a pending character, put it in the buffer.
+                * But discard a pending CR if we are at end of line
+                * (that is, discard the CR in a CR/LF sequence).
+                */
+               (void) do_append(pendc, NULL, pendpos);
+
+       /*
+        * Make sure we've shifted the line, if we need to.
+        */
+       if (cshift < hshift)
+               pshift(hshift - cshift);
+
+       if (ctldisp == OPT_ONPLUS && is_ansi_end('m'))
+       {
+               /* Switch to normal attribute at end of line. */
+               char *p = "\033[m";
+               for ( ;  *p != '\0';  p++)
+               {
+                       linebuf[curr] = *p;
+                       attr[curr++] = AT_ANSI;
+               }
+       }
+
+       /*
+        * Add a newline if necessary,
+        * and append a '\0' to the end of the line.
+        * We output a newline if we're not at the right edge of the screen,
+        * or if the terminal doesn't auto wrap,
+        * or if this is really the end of the line AND the terminal ignores
+        * a newline at the right edge.
+        * (In the last case we don't want to output a newline if the terminal 
+        * doesn't ignore it since that would produce an extra blank line.
+        * But we do want to output a newline if the terminal ignores it in case
+        * the next line is blank.  In that case the single newline output for
+        * that blank line would be ignored!)
+        */
+       if (column < sc_width || !auto_wrap || (endline && ignaw) || ctldisp == OPT_ON)
+       {
+               linebuf[curr] = '\n';
+               attr[curr] = AT_NORMAL;
+               curr++;
+       } 
+       else if (ignaw && column >= sc_width && forw)
+       {
+               /*
+                * Terminals with "ignaw" don't wrap until they *really* need
+                * to, i.e. when the character *after* the last one to fit on a
+                * line is output. But they are too hard to deal with when they
+                * get in the state where a full screen width of characters
+                * have been output but the cursor is sitting on the right edge
+                * instead of at the start of the next line.
+                * So we nudge them into wrapping by outputting a space 
+                * character plus a backspace.  But do this only if moving 
+                * forward; if we're moving backward and drawing this line at
+                * the top of the screen, the space would overwrite the first
+                * char on the next line.  We don't need to do this "nudge" 
+                * at the top of the screen anyway.
+                */
+               linebuf[curr] = ' ';
+               attr[curr++] = AT_NORMAL;
+               linebuf[curr] = '\b'; 
+               attr[curr++] = AT_NORMAL;
+       }
+       linebuf[curr] = '\0';
+       attr[curr] = AT_NORMAL;
+}
+
+/*
+ *
+ */
+       public void
+set_status_col(c)
+       char c;
+{
+       linebuf[0] = c;
+       attr[0] = AT_NORMAL|AT_HILITE;
+}
+
+/*
+ * Get a character from the current line.
+ * Return the character as the function return value,
+ * and the character attribute in *ap.
+ */
+       public int
+gline(i, ap)
+       register int i;
+       register int *ap;
+{
+       if (is_null_line)
+       {
+               /*
+                * If there is no current line, we pretend the line is
+                * either "~" or "", depending on the "twiddle" flag.
+                */
+               if (twiddle)
+               {
+                       if (i == 0)
+                       {
+                               *ap = AT_BOLD;
+                               return '~';
+                       }
+                       --i;
+               }
+               /* Make sure we're back to AT_NORMAL before the '\n'.  */
+               *ap = AT_NORMAL;
+               return i ? '\0' : '\n';
+       }
+
+       *ap = attr[i];
+       return (linebuf[i] & 0xFF);
+}
+
+/*
+ * Indicate that there is no current line.
+ */
+       public void
+null_line()
+{
+       is_null_line = 1;
+       cshift = 0;
+}
+
+/*
+ * Analogous to forw_line(), but deals with "raw lines":
+ * lines which are not split for screen width.
+ * {{ This is supposed to be more efficient than forw_line(). }}
+ */
+       public POSITION
+forw_raw_line(curr_pos, linep, line_lenp)
+       POSITION curr_pos;
+       char **linep;
+       int *line_lenp;
+{
+       register int n;
+       register int c;
+       POSITION new_pos;
+
+       if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
+               (c = ch_forw_get()) == EOI)
+               return (NULL_POSITION);
+
+       n = 0;
+       for (;;)
+       {
+               if (c == '\n' || c == EOI || ABORT_SIGS())
+               {
+                       new_pos = ch_tell();
+                       break;
+               }
+               if (n >= size_linebuf-1)
+               {
+                       if (expand_linebuf())
+                       {
+                               /*
+                                * Overflowed the input buffer.
+                                * Pretend the line ended here.
+                                */
+                               new_pos = ch_tell() - 1;
+                               break;
+                       }
+               }
+               linebuf[n++] = c;
+               c = ch_forw_get();
+       }
+       linebuf[n] = '\0';
+       if (linep != NULL)
+               *linep = linebuf;
+       if (line_lenp != NULL)
+               *line_lenp = n;
+       return (new_pos);
+}
+
+/*
+ * Analogous to back_line(), but deals with "raw lines".
+ * {{ This is supposed to be more efficient than back_line(). }}
+ */
+       public POSITION
+back_raw_line(curr_pos, linep, line_lenp)
+       POSITION curr_pos;
+       char **linep;
+       int *line_lenp;
+{
+       register int n;
+       register int c;
+       POSITION new_pos;
+
+       if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() ||
+               ch_seek(curr_pos-1))
+               return (NULL_POSITION);
+
+       n = size_linebuf;
+       linebuf[--n] = '\0';
+       for (;;)
+       {
+               c = ch_back_get();
+               if (c == '\n' || ABORT_SIGS())
+               {
+                       /*
+                        * This is the newline ending the previous line.
+                        * We have hit the beginning of the line.
+                        */
+                       new_pos = ch_tell() + 1;
+                       break;
+               }
+               if (c == EOI)
+               {
+                       /*
+                        * We have hit the beginning of the file.
+                        * This must be the first line in the file.
+                        * This must, of course, be the beginning of the line.
+                        */
+                       new_pos = ch_zero();
+                       break;
+               }
+               if (n <= 0)
+               {
+                       int old_size_linebuf = size_linebuf;
+                       char *fm;
+                       char *to;
+                       if (expand_linebuf())
+                       {
+                               /*
+                                * Overflowed the input buffer.
+                                * Pretend the line ended here.
+                                */
+                               new_pos = ch_tell() + 1;
+                               break;
+                       }
+                       /*
+                        * Shift the data to the end of the new linebuf.
+                        */
+                       for (fm = linebuf + old_size_linebuf - 1,
+                             to = linebuf + size_linebuf - 1;
+                            fm >= linebuf;  fm--, to--)
+                               *to = *fm;
+                       n = size_linebuf - old_size_linebuf;
+               }
+               linebuf[--n] = c;
+       }
+       if (linep != NULL)
+               *linep = &linebuf[n];
+       if (line_lenp != NULL)
+               *line_lenp = size_linebuf - 1 - n;
+       return (new_pos);
+}
diff --git a/thirdparty/less/linenum.c b/thirdparty/less/linenum.c
new file mode 100644 (file)
index 0000000..4369f87
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Code to handle displaying line numbers.
+ *
+ * Finding the line number of a given file position is rather tricky.
+ * We don't want to just start at the beginning of the file and
+ * count newlines, because that is slow for large files (and also
+ * wouldn't work if we couldn't get to the start of the file; e.g.
+ * if input is a long pipe).
+ *
+ * So we use the function add_lnum to cache line numbers.
+ * We try to be very clever and keep only the more interesting
+ * line numbers when we run out of space in our table.  A line
+ * number is more interesting than another when it is far from
+ * other line numbers.   For example, we'd rather keep lines
+ * 100,200,300 than 100,101,300.  200 is more interesting than
+ * 101 because 101 can be derived very cheaply from 100, while
+ * 200 is more expensive to derive from 100.
+ *
+ * The function currline() returns the line number of a given
+ * position in the file.  As a side effect, it calls add_lnum
+ * to cache the line number.  Therefore currline is occasionally
+ * called to make sure we cache line numbers often enough.
+ */
+
+#include "less.h"
+
+/*
+ * Structure to keep track of a line number and the associated file position.
+ * A doubly-linked circular list of line numbers is kept ordered by line number.
+ */
+struct linenum_info
+{
+       struct linenum_info *next;      /* Link to next in the list */
+       struct linenum_info *prev;      /* Line to previous in the list */
+       POSITION pos;                   /* File position */
+       POSITION gap;                   /* Gap between prev and next */
+       LINENUM line;                   /* Line number */
+};
+/*
+ * "gap" needs some explanation: the gap of any particular line number
+ * is the distance between the previous one and the next one in the list.
+ * ("Distance" means difference in file position.)  In other words, the
+ * gap of a line number is the gap which would be introduced if this
+ * line number were deleted.  It is used to decide which one to replace
+ * when we have a new one to insert and the table is full.
+ */
+
+#define        NPOOL   200                     /* Size of line number pool */
+
+#define        LONGTIME        (2)             /* In seconds */
+
+static struct linenum_info anchor;     /* Anchor of the list */
+static struct linenum_info *freelist;  /* Anchor of the unused entries */
+static struct linenum_info pool[NPOOL];        /* The pool itself */
+static struct linenum_info *spare;             /* We always keep one spare entry */
+
+extern int linenums;
+extern int sigs;
+extern int sc_height;
+extern int screen_trashed;
+
+/*
+ * Initialize the line number structures.
+ */
+       public void
+clr_linenum()
+{
+       register struct linenum_info *p;
+
+       /*
+        * Put all the entries on the free list.
+        * Leave one for the "spare".
+        */
+       for (p = pool;  p < &pool[NPOOL-2];  p++)
+               p->next = p+1;
+       pool[NPOOL-2].next = NULL;
+       freelist = pool;
+
+       spare = &pool[NPOOL-1];
+
+       /*
+        * Initialize the anchor.
+        */
+       anchor.next = anchor.prev = &anchor;
+       anchor.gap = 0;
+       anchor.pos = (POSITION)0;
+       anchor.line = 1;
+}
+
+/*
+ * Calculate the gap for an entry.
+ */
+       static void
+calcgap(p)
+       register struct linenum_info *p;
+{
+       /*
+        * Don't bother to compute a gap for the anchor.
+        * Also don't compute a gap for the last one in the list.
+        * The gap for that last one should be considered infinite,
+        * but we never look at it anyway.
+        */
+       if (p == &anchor || p->next == &anchor)
+               return;
+       p->gap = p->next->pos - p->prev->pos;
+}
+
+/*
+ * Add a new line number to the cache.
+ * The specified position (pos) should be the file position of the
+ * FIRST character in the specified line.
+ */
+       public void
+add_lnum(linenum, pos)
+       LINENUM linenum;
+       POSITION pos;
+{
+       register struct linenum_info *p;
+       register struct linenum_info *new;
+       register struct linenum_info *nextp;
+       register struct linenum_info *prevp;
+       register POSITION mingap;
+
+       /*
+        * Find the proper place in the list for the new one.
+        * The entries are sorted by position.
+        */
+       for (p = anchor.next;  p != &anchor && p->pos < pos;  p = p->next)
+               if (p->line == linenum)
+                       /* We already have this one. */
+                       return;
+       nextp = p;
+       prevp = p->prev;
+
+       if (freelist != NULL)
+       {
+               /*
+                * We still have free (unused) entries.
+                * Use one of them.
+                */
+               new = freelist;
+               freelist = freelist->next;
+       } else
+       {
+               /*
+                * No free entries.
+                * Use the "spare" entry.
+                */
+               new = spare;
+               spare = NULL;
+       }
+
+       /*
+        * Fill in the fields of the new entry,
+        * and insert it into the proper place in the list.
+        */
+       new->next = nextp;
+       new->prev = prevp;
+       new->pos = pos;
+       new->line = linenum;
+
+       nextp->prev = new;
+       prevp->next = new;
+
+       /*
+        * Recalculate gaps for the new entry and the neighboring entries.
+        */
+       calcgap(new);
+       calcgap(nextp);
+       calcgap(prevp);
+
+       if (spare == NULL)
+       {
+               /*
+                * We have used the spare entry.
+                * Scan the list to find the one with the smallest
+                * gap, take it out and make it the spare.
+                * We should never remove the last one, so stop when
+                * we get to p->next == &anchor.  This also avoids
+                * looking at the gap of the last one, which is
+                * not computed by calcgap.
+                */
+               mingap = anchor.next->gap;
+               for (p = anchor.next;  p->next != &anchor;  p = p->next)
+               {
+                       if (p->gap <= mingap)
+                       {
+                               spare = p;
+                               mingap = p->gap;
+                       }
+               }
+               spare->next->prev = spare->prev;
+               spare->prev->next = spare->next;
+       }
+}
+
+/*
+ * If we get stuck in a long loop trying to figure out the
+ * line number, print a message to tell the user what we're doing.
+ */
+       static void
+longloopmessage()
+{
+       ierror("Calculating line numbers", NULL_PARG);
+}
+
+static int loopcount;
+#if HAVE_TIME
+static long startime;
+#endif
+
+       static void
+longish()
+{
+#if HAVE_TIME
+       if (loopcount >= 0 && ++loopcount > 100)
+       {
+               loopcount = 0;
+               if (get_time() >= startime + LONGTIME)
+               {
+                       longloopmessage();
+                       loopcount = -1;
+               }
+       }
+#else
+       if (loopcount >= 0 && ++loopcount > LONGLOOP)
+       {
+               longloopmessage();
+               loopcount = -1;
+       }
+#endif
+}
+
+/*
+ * Turn off line numbers because the user has interrupted
+ * a lengthy line number calculation.
+ */
+       static void
+abort_long()
+{
+       if (linenums == OPT_ONPLUS)
+               /*
+                * We were displaying line numbers, so need to repaint.
+                */
+               screen_trashed = 1;
+       linenums = 0;
+       error("Line numbers turned off", NULL_PARG);
+}
+
+/*
+ * Find the line number associated with a given position.
+ * Return 0 if we can't figure it out.
+ */
+       public LINENUM
+find_linenum(pos)
+       POSITION pos;
+{
+       register struct linenum_info *p;
+       register LINENUM linenum;
+       POSITION cpos;
+
+       if (!linenums)
+               /*
+                * We're not using line numbers.
+                */
+               return (0);
+       if (pos == NULL_POSITION)
+               /*
+                * Caller doesn't know what he's talking about.
+                */
+               return (0);
+       if (pos <= ch_zero())
+               /*
+                * Beginning of file is always line number 1.
+                */
+               return (1);
+
+       /*
+        * Find the entry nearest to the position we want.
+        */
+       for (p = anchor.next;  p != &anchor && p->pos < pos;  p = p->next)
+               continue;
+       if (p->pos == pos)
+               /* Found it exactly. */
+               return (p->line);
+
+       /*
+        * This is the (possibly) time-consuming part.
+        * We start at the line we just found and start
+        * reading the file forward or backward till we
+        * get to the place we want.
+        *
+        * First decide whether we should go forward from the 
+        * previous one or backwards from the next one.
+        * The decision is based on which way involves 
+        * traversing fewer bytes in the file.
+        */
+#if HAVE_TIME
+       startime = get_time();
+#endif
+       if (p == &anchor || pos - p->prev->pos < p->pos - pos)
+       {
+               /*
+                * Go forward.
+                */
+               p = p->prev;
+               if (ch_seek(p->pos))
+                       return (0);
+               loopcount = 0;
+               for (linenum = p->line, cpos = p->pos;  cpos < pos;  linenum++)
+               {
+                       /*
+                        * Allow a signal to abort this loop.
+                        */
+                       cpos = forw_raw_line(cpos, (char **)NULL, (int *)NULL);
+                       if (ABORT_SIGS()) {
+                               abort_long();
+                               return (0);
+                       }
+                       if (cpos == NULL_POSITION)
+                               return (0);
+                       longish();
+               }
+               /*
+                * We might as well cache it.
+                */
+               add_lnum(linenum, cpos);
+               /*
+                * If the given position is not at the start of a line,
+                * make sure we return the correct line number.
+                */
+               if (cpos > pos)
+                       linenum--;
+       } else
+       {
+               /*
+                * Go backward.
+                */
+               if (ch_seek(p->pos))
+                       return (0);
+               loopcount = 0;
+               for (linenum = p->line, cpos = p->pos;  cpos > pos;  linenum--)
+               {
+                       /*
+                        * Allow a signal to abort this loop.
+                        */
+                       cpos = back_raw_line(cpos, (char **)NULL, (int *)NULL);
+                       if (ABORT_SIGS()) {
+                               abort_long();
+                               return (0);
+                       }
+                       if (cpos == NULL_POSITION)
+                               return (0);
+                       longish();
+               }
+               /*
+                * We might as well cache it.
+                */
+               add_lnum(linenum, cpos);
+       }
+
+       return (linenum);
+}
+
+/*
+ * Find the position of a given line number.
+ * Return NULL_POSITION if we can't figure it out.
+ */
+       public POSITION
+find_pos(linenum)
+       LINENUM linenum;
+{
+       register struct linenum_info *p;
+       POSITION cpos;
+       LINENUM clinenum;
+
+       if (linenum <= 1)
+               /*
+                * Line number 1 is beginning of file.
+                */
+               return (ch_zero());
+
+       /*
+        * Find the entry nearest to the line number we want.
+        */
+       for (p = anchor.next;  p != &anchor && p->line < linenum;  p = p->next)
+               continue;
+       if (p->line == linenum)
+               /* Found it exactly. */
+               return (p->pos);
+
+       if (p == &anchor || linenum - p->prev->line < p->line - linenum)
+       {
+               /*
+                * Go forward.
+                */
+               p = p->prev;
+               if (ch_seek(p->pos))
+                       return (NULL_POSITION);
+               for (clinenum = p->line, cpos = p->pos;  clinenum < linenum;  clinenum++)
+               {
+                       /*
+                        * Allow a signal to abort this loop.
+                        */
+                       cpos = forw_raw_line(cpos, (char **)NULL, (int *)NULL);
+                       if (ABORT_SIGS())
+                               return (NULL_POSITION);
+                       if (cpos == NULL_POSITION)
+                               return (NULL_POSITION);
+               }
+       } else
+       {
+               /*
+                * Go backward.
+                */
+               if (ch_seek(p->pos))
+                       return (NULL_POSITION);
+               for (clinenum = p->line, cpos = p->pos;  clinenum > linenum;  clinenum--)
+               {
+                       /*
+                        * Allow a signal to abort this loop.
+                        */
+                       cpos = back_raw_line(cpos, (char **)NULL, (int *)NULL);
+                       if (ABORT_SIGS())
+                               return (NULL_POSITION);
+                       if (cpos == NULL_POSITION)
+                               return (NULL_POSITION);
+               }
+       }
+       /*
+        * We might as well cache it.
+        */
+       add_lnum(clinenum, cpos);
+       return (cpos);
+}
+
+/*
+ * Return the line number of the "current" line.
+ * The argument "where" tells which line is to be considered
+ * the "current" line (e.g. TOP, BOTTOM, MIDDLE, etc).
+ */
+       public LINENUM
+currline(where)
+       int where;
+{
+       POSITION pos;
+       POSITION len;
+       LINENUM linenum;
+
+       pos = position(where);
+       len = ch_length();
+       while (pos == NULL_POSITION && where >= 0 && where < sc_height)
+               pos = position(++where);
+       if (pos == NULL_POSITION)
+               pos = len;
+       linenum = find_linenum(pos);
+       if (pos == len)
+               linenum--;
+       return (linenum);
+}
diff --git a/thirdparty/less/lsystem.c b/thirdparty/less/lsystem.c
new file mode 100644 (file)
index 0000000..5ff0142
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Routines to execute other programs.
+ * Necessarily very OS dependent.
+ */
+
+#include "less.h"
+#include <signal.h>
+#include "position.h"
+
+#if MSDOS_COMPILER
+#include <dos.h>
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#include <direct.h>
+#define setdisk(n) _chdrive((n)+1)
+#else
+#include <dir.h>
+#endif
+#endif
+
+extern int screen_trashed;
+extern IFILE curr_ifile;
+
+
+#if HAVE_SYSTEM
+
+/*
+ * Pass the specified command to a shell to be executed.
+ * Like plain "system()", but handles resetting terminal modes, etc.
+ */
+       public void
+lsystem(cmd, donemsg)
+       char *cmd;
+       char *donemsg;
+{
+       register int inp;
+#if HAVE_SHELL
+       register char *shell;
+       register char *p;
+#endif
+       IFILE save_ifile;
+#if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C
+       char cwd[FILENAME_MAX+1];
+#endif
+
+       /*
+        * Print the command which is to be executed,
+        * unless the command starts with a "-".
+        */
+       if (cmd[0] == '-')
+               cmd++;
+       else
+       {
+               clear_bot();
+               putstr("!");
+               putstr(cmd);
+               putstr("\n");
+       }
+
+#if MSDOS_COMPILER
+#if MSDOS_COMPILER==WIN32C
+       if (*cmd == '\0')
+               cmd = getenv("COMSPEC");
+#else
+       /*
+        * Working directory is global on MSDOS.
+        * The child might change the working directory, so we
+        * must save and restore CWD across calls to "system",
+        * or else we won't find our file when we return and
+        * try to "reedit_ifile" it.
+        */
+       getcwd(cwd, FILENAME_MAX);
+#endif
+#endif
+
+       /*
+        * Close the current input file.
+        */
+       save_ifile = save_curr_ifile();
+       (void) edit_ifile(NULL_IFILE);
+
+       /*
+        * De-initialize the terminal and take out of raw mode.
+        */
+       deinit();
+       flush();        /* Make sure the deinit chars get out */
+       raw_mode(0);
+#if MSDOS_COMPILER==WIN32C
+       close_getchr();
+#endif
+
+       /*
+        * Restore signals to their defaults.
+        */
+       init_signals(0);
+
+#if HAVE_DUP
+       /*
+        * Force standard input to be the user's terminal
+        * (the normal standard input), even if less's standard input 
+        * is coming from a pipe.
+        */
+       inp = dup(0);
+       close(0);
+#if OS2
+       /* The __open() system call translates "/dev/tty" to "con". */
+       if (__open("/dev/tty", OPEN_READ) < 0)
+#else
+       if (open("/dev/tty", OPEN_READ) < 0)
+#endif
+               dup(inp);
+#endif
+
+       /*
+        * Pass the command to the system to be executed.
+        * If we have a SHELL environment variable, use
+        * <$SHELL -c "command"> instead of just <command>.
+        * If the command is empty, just invoke a shell.
+        */
+#if HAVE_SHELL
+       p = NULL;
+       if ((shell = lgetenv("SHELL")) != NULL && *shell != '\0')
+       {
+               if (*cmd == '\0')
+                       p = save(shell);
+               else
+               {
+                       char *esccmd = shell_quote(cmd);
+                       if (esccmd != NULL)
+                       {
+                               int len = strlen(shell) + strlen(esccmd) + 5;
+                               p = (char *) ecalloc(len, sizeof(char));
+                               SNPRINTF3(p, len, "%s %s %s", shell, shell_coption(), esccmd);
+                               free(esccmd);
+                       }
+               }
+       }
+       if (p == NULL)
+       {
+               if (*cmd == '\0')
+                       p = save("sh");
+               else
+                       p = save(cmd);
+       }
+       system(p);
+       free(p);
+#else
+#if MSDOS_COMPILER==DJGPPC
+       /*
+        * Make stdin of the child be in cooked mode.
+        */
+       setmode(0, O_TEXT);
+       /*
+        * We don't need to catch signals of the child (it
+        * also makes trouble with some DPMI servers).
+        */
+       __djgpp_exception_toggle();
+       system(cmd);
+       __djgpp_exception_toggle();
+#else
+       system(cmd);
+#endif
+#endif
+
+#if HAVE_DUP
+       /*
+        * Restore standard input, reset signals, raw mode, etc.
+        */
+       close(0);
+       dup(inp);
+       close(inp);
+#endif
+
+#if MSDOS_COMPILER==WIN32C
+       open_getchr();
+#endif
+       init_signals(1);
+       raw_mode(1);
+       if (donemsg != NULL)
+       {
+               putstr(donemsg);
+               putstr("  (press RETURN)");
+               get_return();
+               putchr('\n');
+               flush();
+       }
+       init();
+       screen_trashed = 1;
+
+#if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C
+       /*
+        * Restore the previous directory (possibly
+        * changed by the child program we just ran).
+        */
+       chdir(cwd);
+#if MSDOS_COMPILER != DJGPPC
+       /*
+        * Some versions of chdir() don't change to the drive
+        * which is part of CWD.  (DJGPP does this in chdir.)
+        */
+       if (cwd[1] == ':')
+       {
+               if (cwd[0] >= 'a' && cwd[0] <= 'z')
+                       setdisk(cwd[0] - 'a');
+               else if (cwd[0] >= 'A' && cwd[0] <= 'Z')
+                       setdisk(cwd[0] - 'A');
+       }
+#endif
+#endif
+
+       /*
+        * Reopen the current input file.
+        */
+       reedit_ifile(save_ifile);
+
+#if defined(SIGWINCH) || defined(SIGWIND)
+       /*
+        * Since we were ignoring window change signals while we executed
+        * the system command, we must assume the window changed.
+        * Warning: this leaves a signal pending (in "sigs"),
+        * so psignals() should be called soon after lsystem().
+        */
+       winch(0);
+#endif
+}
+
+#endif
+
+#if PIPEC
+
+/*
+ * Pipe a section of the input file into the given shell command.
+ * The section to be piped is the section "between" the current
+ * position and the position marked by the given letter.
+ *
+ * If the mark is after the current screen, the section between
+ * the top line displayed and the mark is piped.
+ * If the mark is before the current screen, the section between
+ * the mark and the bottom line displayed is piped.
+ * If the mark is on the current screen, or if the mark is ".",
+ * the whole current screen is piped.
+ */
+       public int
+pipe_mark(c, cmd)
+       int c;
+       char *cmd;
+{
+       POSITION mpos, tpos, bpos;
+
+       /*
+        * mpos = the marked position.
+        * tpos = top of screen.
+        * bpos = bottom of screen.
+        */
+       mpos = markpos(c);
+       if (mpos == NULL_POSITION)
+               return (-1);
+       tpos = position(TOP);
+       if (tpos == NULL_POSITION)
+               tpos = ch_zero();
+       bpos = position(BOTTOM);
+
+       if (c == '.') 
+               return (pipe_data(cmd, tpos, bpos));
+       else if (mpos <= tpos)
+               return (pipe_data(cmd, mpos, bpos));
+       else if (bpos == NULL_POSITION)
+               return (pipe_data(cmd, tpos, bpos));
+       else
+               return (pipe_data(cmd, tpos, mpos));
+}
+
+/*
+ * Create a pipe to the given shell command.
+ * Feed it the file contents between the positions spos and epos.
+ */
+       public int
+pipe_data(cmd, spos, epos)
+       char *cmd;
+       POSITION spos;
+       POSITION epos;
+{
+       register FILE *f;
+       register int c;
+#if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C
+       extern FILE *popen();
+#endif
+
+       /*
+        * This is structured much like lsystem().
+        * Since we're running a shell program, we must be careful
+        * to perform the necessary deinitialization before running
+        * the command, and reinitialization after it.
+        */
+       if (ch_seek(spos) != 0)
+       {
+               error("Cannot seek to start position", NULL_PARG);
+               return (-1);
+       }
+
+       if ((f = popen(cmd, "w")) == NULL)
+       {
+               error("Cannot create pipe", NULL_PARG);
+               return (-1);
+       }
+       clear_bot();
+       putstr("!");
+       putstr(cmd);
+       putstr("\n");
+
+       deinit();
+       flush();
+       raw_mode(0);
+       init_signals(0);
+#if MSDOS_COMPILER==WIN32C
+       close_getchr();
+#endif
+#ifdef SIGPIPE
+       LSIGNAL(SIGPIPE, SIG_IGN);
+#endif
+
+       c = EOI;
+       while (epos == NULL_POSITION || spos++ <= epos)
+       {
+               /*
+                * Read a character from the file and give it to the pipe.
+                */
+               c = ch_forw_get();
+               if (c == EOI)
+                       break;
+               if (putc(c, f) == EOF)
+                       break;
+       }
+
+       /*
+        * Finish up the last line.
+        */
+       while (c != '\n' && c != EOI ) 
+       {
+               c = ch_forw_get();
+               if (c == EOI)
+                       break;
+               if (putc(c, f) == EOF)
+                       break;
+       }
+
+       pclose(f);
+
+#ifdef SIGPIPE
+       LSIGNAL(SIGPIPE, SIG_DFL);
+#endif
+#if MSDOS_COMPILER==WIN32C
+       open_getchr();
+#endif
+       init_signals(1);
+       raw_mode(1);
+       init();
+       screen_trashed = 1;
+#if defined(SIGWINCH) || defined(SIGWIND)
+       /* {{ Probably don't need this here. }} */
+       winch(0);
+#endif
+       return (0);
+}
+
+#endif
diff --git a/thirdparty/less/main.c b/thirdparty/less/main.c
new file mode 100644 (file)
index 0000000..0af1762
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Entry point, initialization, miscellaneous routines.
+ */
+
+#include "less.h"
+#if MSDOS_COMPILER==WIN32C
+#include <windows.h>
+#endif
+
+public char *  every_first_cmd = NULL;
+public int     new_file;
+public int     is_tty;
+public IFILE   curr_ifile = NULL_IFILE;
+public IFILE   old_ifile = NULL_IFILE;
+public struct scrpos initial_scrpos;
+public int     any_display = FALSE;
+public POSITION        start_attnpos = NULL_POSITION;
+public POSITION        end_attnpos = NULL_POSITION;
+public int     wscroll;
+public char *  progname;
+public int     quitting;
+public int     secure;
+public int     dohelp;
+
+#if LOGFILE
+public int     logfile = -1;
+public int     force_logfile = FALSE;
+public char *  namelogfile = NULL;
+#endif
+
+#if EDITOR
+public char *  editor;
+public char *  editproto;
+#endif
+
+#if TAGS
+extern char *  tags;
+extern char *  tagoption;
+extern int     jump_sline;
+#endif
+
+#ifdef WIN32
+static char consoleTitle[256];
+#endif
+
+extern int     less_is_more;
+extern int     missing_cap;
+extern int     know_dumb;
+extern int     quit_if_one_screen;
+extern int     pr_type;
+
+
+/*
+ * Entry point.
+ */
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       IFILE ifile;
+       char *s;
+
+#ifdef __EMX__
+       _response(&argc, &argv);
+       _wildcard(&argc, &argv);
+#endif
+
+       progname = *argv++;
+       argc--;
+
+       secure = 0;
+       s = lgetenv("LESSSECURE");
+       if (s != NULL && *s != '\0')
+               secure = 1;
+
+#ifdef WIN32
+       if (getenv("HOME") == NULL)
+       {
+               /*
+                * If there is no HOME environment variable,
+                * try the concatenation of HOMEDRIVE + HOMEPATH.
+                */
+               char *drive = getenv("HOMEDRIVE");
+               char *path  = getenv("HOMEPATH");
+               if (drive != NULL && path != NULL)
+               {
+                       char *env = (char *) ecalloc(strlen(drive) + 
+                                       strlen(path) + 6, sizeof(char));
+                       strcpy(env, "HOME=");
+                       strcat(env, drive);
+                       strcat(env, path);
+                       putenv(env);
+               }
+       }
+       GetConsoleTitle(consoleTitle, sizeof(consoleTitle)/sizeof(char));
+#endif /* WIN32 */
+
+       /*
+        * Process command line arguments and LESS environment arguments.
+        * Command line arguments override environment arguments.
+        */
+       is_tty = isatty(1);
+       get_term();
+       init_cmds();
+       init_charset();
+       init_line();
+       init_cmdhist();
+       init_option();
+       init_search();
+
+       /*
+        * If the name of the executable program is "more",
+        * act like LESS_IS_MORE is set.
+        */
+       for (s = progname + strlen(progname);  s > progname;  s--)
+       {
+               if (s[-1] == PATHNAME_SEP[0])
+                       break;
+       }
+       if (strcmp(s, "more") == 0)
+               less_is_more = 1;
+
+       init_prompt();
+
+       s = lgetenv(less_is_more ? "MORE" : "LESS");
+       if (s != NULL)
+               scan_option(save(s));
+
+#define        isoptstring(s)  (((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0')
+       while (argc > 0 && (isoptstring(*argv) || isoptpending()))
+       {
+               s = *argv++;
+               argc--;
+               if (strcmp(s, "--") == 0)
+                       break;
+               scan_option(s);
+       }
+#undef isoptstring
+
+       if (isoptpending())
+       {
+               /*
+                * Last command line option was a flag requiring a
+                * following string, but there was no following string.
+                */
+               nopendopt();
+               quit(QUIT_OK);
+       }
+
+       if (less_is_more && get_quit_at_eof())
+               quit_if_one_screen = TRUE;
+
+#if EDITOR
+       editor = lgetenv("VISUAL");
+       if (editor == NULL || *editor == '\0')
+       {
+               editor = lgetenv("EDITOR");
+               if (editor == NULL || *editor == '\0')
+                       editor = EDIT_PGM;
+       }
+       editproto = lgetenv("LESSEDIT");
+       if (editproto == NULL || *editproto == '\0')
+               editproto = "%E ?lm+%lm. %f";
+#endif
+
+       /*
+        * Call get_ifile with all the command line filenames
+        * to "register" them with the ifile system.
+        */
+       ifile = NULL_IFILE;
+       if (dohelp)
+               ifile = get_ifile(FAKE_HELPFILE, ifile);
+       while (argc-- > 0)
+       {
+               char *filename;
+#if (MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC)
+               /*
+                * Because the "shell" doesn't expand filename patterns,
+                * treat each argument as a filename pattern rather than
+                * a single filename.  
+                * Expand the pattern and iterate over the expanded list.
+                */
+               struct textlist tlist;
+               char *gfilename;
+               
+               gfilename = lglob(*argv++);
+               init_textlist(&tlist, gfilename);
+               filename = NULL;
+               while ((filename = forw_textlist(&tlist, filename)) != NULL)
+               {
+                       (void) get_ifile(filename, ifile);
+                       ifile = prev_ifile(NULL_IFILE);
+               }
+               free(gfilename);
+#else
+               filename = shell_quote(*argv);
+               if (filename == NULL)
+                       filename = *argv;
+               argv++;
+               (void) get_ifile(filename, ifile);
+               ifile = prev_ifile(NULL_IFILE);
+#endif
+       }
+       /*
+        * Set up terminal, etc.
+        */
+       if (!is_tty)
+       {
+               /*
+                * Output is not a tty.
+                * Just copy the input file(s) to output.
+                */
+               SET_BINARY(1);
+               if (nifile() == 0)
+               {
+                       if (edit_stdin() == 0)
+                               cat_file();
+               } else if (edit_first() == 0)
+               {
+                       do {
+                               cat_file();
+                       } while (edit_next(1) == 0);
+               }
+               quit(QUIT_OK);
+       }
+
+       if (missing_cap && !know_dumb)
+               error("WARNING: terminal is not fully functional", NULL_PARG);
+       init_mark();
+       open_getchr();
+       raw_mode(1);
+       init_signals(1);
+
+       /*
+        * Select the first file to examine.
+        */
+#if TAGS
+       if (tagoption != NULL || strcmp(tags, "-") == 0)
+       {
+               /*
+                * A -t option was given.
+                * Verify that no filenames were also given.
+                * Edit the file selected by the "tags" search,
+                * and search for the proper line in the file.
+                */
+               if (nifile() > 0)
+               {
+                       error("No filenames allowed with -t option", NULL_PARG);
+                       quit(QUIT_ERROR);
+               }
+               findtag(tagoption);
+               if (edit_tagfile())  /* Edit file which contains the tag */
+                       quit(QUIT_ERROR);
+               /*
+                * Search for the line which contains the tag.
+                * Set up initial_scrpos so we display that line.
+                */
+               initial_scrpos.pos = tagsearch();
+               if (initial_scrpos.pos == NULL_POSITION)
+                       quit(QUIT_ERROR);
+               initial_scrpos.ln = jump_sline;
+       } else
+#endif
+       if (nifile() == 0)
+       {
+               if (edit_stdin())  /* Edit standard input */
+                       quit(QUIT_ERROR);
+       } else 
+       {
+               if (edit_first())  /* Edit first valid file in cmd line */
+                       quit(QUIT_ERROR);
+       }
+
+       init();
+       commands();
+       quit(QUIT_OK);
+       /*NOTREACHED*/
+       return (0);
+}
+
+/*
+ * Copy a string to a "safe" place
+ * (that is, to a buffer allocated by calloc).
+ */
+       public char *
+save(s)
+       char *s;
+{
+       register char *p;
+
+       p = (char *) ecalloc(strlen(s)+1, sizeof(char));
+       strcpy(p, s);
+       return (p);
+}
+
+/*
+ * Allocate memory.
+ * Like calloc(), but never returns an error (NULL).
+ */
+       public VOID_POINTER
+ecalloc(count, size)
+       int count;
+       unsigned int size;
+{
+       register VOID_POINTER p;
+
+       p = (VOID_POINTER) calloc(count, size);
+       if (p != NULL)
+               return (p);
+       error("Cannot allocate memory", NULL_PARG);
+       quit(QUIT_ERROR);
+       /*NOTREACHED*/
+       return (NULL);
+}
+
+/*
+ * Skip leading spaces in a string.
+ */
+       public char *
+skipsp(s)
+       register char *s;
+{
+       while (*s == ' ' || *s == '\t') 
+               s++;
+       return (s);
+}
+
+/*
+ * See how many characters of two strings are identical.
+ * If uppercase is true, the first string must begin with an uppercase
+ * character; the remainder of the first string may be either case.
+ */
+       public int
+sprefix(ps, s, uppercase)
+       char *ps;
+       char *s;
+       int uppercase;
+{
+       register int c;
+       register int sc;
+       register int len = 0;
+
+       for ( ;  *s != '\0';  s++, ps++)
+       {
+               c = *ps;
+               if (uppercase)
+               {
+                       if (len == 0 && ASCII_IS_LOWER(c))
+                               return (-1);
+                       if (ASCII_IS_UPPER(c))
+                               c = ASCII_TO_LOWER(c);
+               }
+               sc = *s;
+               if (len > 0 && ASCII_IS_UPPER(sc))
+                       sc = ASCII_TO_LOWER(sc);
+               if (c != sc)
+                       break;
+               len++;
+       }
+       return (len);
+}
+
+/*
+ * Exit the program.
+ */
+       public void
+quit(status)
+       int status;
+{
+       static int save_status;
+
+       /*
+        * Put cursor at bottom left corner, clear the line,
+        * reset the terminal modes, and exit.
+        */
+       if (status < 0)
+               status = save_status;
+       else
+               save_status = status;
+       quitting = 1;
+       edit((char*)NULL);
+       save_cmdhist();
+       if (any_display && is_tty)
+               clear_bot();
+       deinit();
+       flush();
+       raw_mode(0);
+#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
+       /* 
+        * If we don't close 2, we get some garbage from
+        * 2's buffer when it flushes automatically.
+        * I cannot track this one down  RB
+        * The same bug shows up if we use ^C^C to abort.
+        */
+       close(2);
+#endif
+#ifdef WIN32
+       SetConsoleTitle(consoleTitle);
+#endif
+       close_getchr();
+       exit(status);
+}
diff --git a/thirdparty/less/mark.c b/thirdparty/less/mark.c
new file mode 100644 (file)
index 0000000..585b412
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+#include "less.h"
+
+extern IFILE curr_ifile;
+extern int sc_height;
+extern int jump_sline;
+
+/*
+ * A mark is an ifile (input file) plus a position within the file.
+ */
+struct mark {
+       IFILE m_ifile;
+       struct scrpos m_scrpos;
+};
+
+/*
+ * The table of marks.
+ * Each mark is identified by a lowercase or uppercase letter.
+ * The final one is lmark, for the "last mark"; addressed by the apostrophe.
+ */
+#define        NMARKS          ((2*26)+1)      /* a-z, A-Z, lastmark */
+#define        LASTMARK        (NMARKS-1)
+static struct mark marks[NMARKS];
+
+/*
+ * Initialize the mark table to show no marks are set.
+ */
+       public void
+init_mark()
+{
+       int i;
+
+       for (i = 0;  i < NMARKS;  i++)
+               marks[i].m_scrpos.pos = NULL_POSITION;
+}
+
+/*
+ * See if a mark letter is valid (between a and z).
+ */
+       static struct mark *
+getumark(c)
+       int c;
+{
+       if (c >= 'a' && c <= 'z')
+               return (&marks[c-'a']);
+
+       if (c >= 'A' && c <= 'Z')
+               return (&marks[c-'A'+26]);
+
+       error("Invalid mark letter", NULL_PARG);
+       return (NULL);
+}
+
+/*
+ * Get the mark structure identified by a character.
+ * The mark struct may come either from the mark table
+ * or may be constructed on the fly for certain characters like ^, $.
+ */
+       static struct mark *
+getmark(c)
+       int c;
+{
+       register struct mark *m;
+       static struct mark sm;
+
+       switch (c)
+       {
+       case '^':
+               /*
+                * Beginning of the current file.
+                */
+               m = &sm;
+               m->m_scrpos.pos = ch_zero();
+               m->m_scrpos.ln = 0;
+               m->m_ifile = curr_ifile;
+               break;
+       case '$':
+               /*
+                * End of the current file.
+                */
+               if (ch_end_seek())
+               {
+                       error("Cannot seek to end of file", NULL_PARG);
+                       return (NULL);
+               }
+               m = &sm;
+               m->m_scrpos.pos = ch_tell();
+               m->m_scrpos.ln = sc_height-1;
+               m->m_ifile = curr_ifile;
+               break;
+       case '.':
+               /*
+                * Current position in the current file.
+                */
+               m = &sm;
+               get_scrpos(&m->m_scrpos);
+               m->m_ifile = curr_ifile;
+               break;
+       case '\'':
+               /*
+                * The "last mark".
+                */
+               m = &marks[LASTMARK];
+               break;
+       default:
+               /*
+                * Must be a user-defined mark.
+                */
+               m = getumark(c);
+               if (m == NULL)
+                       break;
+               if (m->m_scrpos.pos == NULL_POSITION)
+               {
+                       error("Mark not set", NULL_PARG);
+                       return (NULL);
+               }
+               break;
+       }
+       return (m);
+}
+
+/*
+ * Is a mark letter is invalid?
+ */
+       public int
+badmark(c)
+       int c;
+{
+       return (getmark(c) == NULL);
+}
+
+/*
+ * Set a user-defined mark.
+ */
+       public void
+setmark(c)
+       int c;
+{
+       register struct mark *m;
+       struct scrpos scrpos;
+
+       m = getumark(c);
+       if (m == NULL)
+               return;
+       get_scrpos(&scrpos);
+       m->m_scrpos = scrpos;
+       m->m_ifile = curr_ifile;
+}
+
+/*
+ * Set lmark (the mark named by the apostrophe).
+ */
+       public void
+lastmark()
+{
+       struct scrpos scrpos;
+
+       if (ch_getflags() & CH_HELPFILE)
+               return;
+       get_scrpos(&scrpos);
+       if (scrpos.pos == NULL_POSITION)
+               return;
+       marks[LASTMARK].m_scrpos = scrpos;
+       marks[LASTMARK].m_ifile = curr_ifile;
+}
+
+/*
+ * Go to a mark.
+ */
+       public void
+gomark(c)
+       int c;
+{
+       register struct mark *m;
+       struct scrpos scrpos;
+
+       m = getmark(c);
+       if (m == NULL)
+               return;
+
+       /*
+        * If we're trying to go to the lastmark and 
+        * it has not been set to anything yet,
+        * set it to the beginning of the current file.
+        */
+       if (m == &marks[LASTMARK] && m->m_scrpos.pos == NULL_POSITION)
+       {
+               m->m_ifile = curr_ifile;
+               m->m_scrpos.pos = ch_zero();
+               m->m_scrpos.ln = jump_sline;
+       }
+
+       /*
+        * If we're using lmark, we must save the screen position now,
+        * because if we call edit_ifile() below, lmark will change.
+        * (We save the screen position even if we're not using lmark.)
+        */
+       scrpos = m->m_scrpos;
+       if (m->m_ifile != curr_ifile)
+       {
+               /*
+                * Not in the current file; edit the correct file.
+                */
+               if (edit_ifile(m->m_ifile))
+                       return;
+       }
+
+       jump_loc(scrpos.pos, scrpos.ln);
+}
+
+/*
+ * Return the position associated with a given mark letter.
+ *
+ * We don't return which screen line the position 
+ * is associated with, but this doesn't matter much,
+ * because it's always the first non-blank line on the screen.
+ */
+       public POSITION
+markpos(c)
+       int c;
+{
+       register struct mark *m;
+
+       m = getmark(c);
+       if (m == NULL)
+               return (NULL_POSITION);
+
+       if (m->m_ifile != curr_ifile)
+       {
+               error("Mark not in current file", NULL_PARG);
+               return (NULL_POSITION);
+       }
+       return (m->m_scrpos.pos);
+}
+
+/*
+ * Clear the marks associated with a specified ifile.
+ */
+       public void
+unmark(ifile)
+       IFILE ifile;
+{
+       int i;
+
+       for (i = 0;  i < NMARKS;  i++)
+               if (marks[i].m_ifile == ifile)
+                       marks[i].m_scrpos.pos = NULL_POSITION;
+}
diff --git a/thirdparty/less/mkhelp.c b/thirdparty/less/mkhelp.c
new file mode 100644 (file)
index 0000000..4aa8cbd
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Silly little program to generate the help.c source file
+ * from the less.hlp text file.
+ * help.c just contains a char array whose contents are 
+ * the contents of less.hlp.
+ */
+
+#include <stdio.h>
+
+       int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       int ch;
+       int prevch;
+
+       printf("/* This file was generated by mkhelp from less.hlp */\n");
+       printf("#include \"less.h\"\n");
+       printf("constant char helpdata[] = {\n");
+       ch = 0;
+       while (prevch = ch, (ch = getchar()) != EOF)
+       {
+               switch (ch)
+               {
+               case '\'':
+                       printf("'\\'',");
+                       break;
+               case '\\':
+                       printf("'\\\\',");
+                       break;
+               case '\b':
+                       printf("'\\b',");
+                       break;
+               case '\t':
+                       printf("'\\t',");
+                       break;
+               case '\n':
+                       if (prevch != '\r') 
+                               printf("'\\n',\n");
+                       break;
+               case '\r':
+                       if (prevch != '\n') 
+                               printf("'\\n',\n");
+                       break;
+               default:
+                       if (ch >= ' ' && ch < 0x7f)
+                               printf("'%c',", ch);
+                       else
+                               printf("0x%02x,", ch);
+                       break;
+               }
+       }
+       /* Add an extra null char to avoid having a trailing comma. */
+       printf(" 0 };\n");
+       printf("constant int size_helpdata = sizeof(helpdata) - 1;\n");
+       return (0);
+}
diff --git a/thirdparty/less/optfunc.c b/thirdparty/less/optfunc.c
new file mode 100644 (file)
index 0000000..a0aa10a
--- /dev/null
@@ -0,0 +1,707 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Handling functions for command line options.
+ *
+ * Most options are handled by the generic code in option.c.
+ * But all string options, and a few non-string options, require
+ * special handling specific to the particular option.
+ * This special processing is done by the "handling functions" in this file.
+ *
+ * Each handling function is passed a "type" and, if it is a string
+ * option, the string which should be "assigned" to the option.
+ * The type may be one of:
+ *     INIT    The option is being initialized from the command line.
+ *     TOGGLE  The option is being changed from within the program.
+ *     QUERY   The setting of the option is merely being queried.
+ */
+
+#include "less.h"
+#include "option.h"
+
+extern int nbufs;
+extern int bufspace;
+extern int pr_type;
+extern int plusoption;
+extern int swindow;
+extern int sc_width;
+extern int sc_height;
+extern int secure;
+extern int dohelp;
+extern int any_display;
+extern char openquote;
+extern char closequote;
+extern char *prproto[];
+extern char *eqproto;
+extern char *hproto;
+extern char *wproto;
+extern IFILE curr_ifile;
+extern char version[];
+extern int jump_sline;
+extern int jump_sline_fraction;
+extern int shift_count;
+extern int shift_count_fraction;
+extern int less_is_more;
+#if LOGFILE
+extern char *namelogfile;
+extern int force_logfile;
+extern int logfile;
+#endif
+#if TAGS
+public char *tagoption = NULL;
+extern char *tags;
+#endif
+#if MSDOS_COMPILER
+extern int nm_fg_color, nm_bg_color;
+extern int bo_fg_color, bo_bg_color;
+extern int ul_fg_color, ul_bg_color;
+extern int so_fg_color, so_bg_color;
+extern int bl_fg_color, bl_bg_color;
+#endif
+
+
+#if LOGFILE
+/*
+ * Handler for -o option.
+ */
+       public void
+opt_o(type, s)
+       int type;
+       char *s;
+{
+       PARG parg;
+
+       if (secure)
+       {
+               error("log file support is not available", NULL_PARG);
+               return;
+       }
+       switch (type)
+       {
+       case INIT:
+               namelogfile = s;
+               break;
+       case TOGGLE:
+               if (ch_getflags() & CH_CANSEEK)
+               {
+                       error("Input is not a pipe", NULL_PARG);
+                       return;
+               }
+               if (logfile >= 0)
+               {
+                       error("Log file is already in use", NULL_PARG);
+                       return;
+               }
+               s = skipsp(s);
+               namelogfile = lglob(s);
+               use_logfile(namelogfile);
+               sync_logfile();
+               break;
+       case QUERY:
+               if (logfile < 0)
+                       error("No log file", NULL_PARG);
+               else
+               {
+                       parg.p_string = namelogfile;
+                       error("Log file \"%s\"", &parg);
+               }
+               break;
+       }
+}
+
+/*
+ * Handler for -O option.
+ */
+       public void
+opt__O(type, s)
+       int type;
+       char *s;
+{
+       force_logfile = TRUE;
+       opt_o(type, s);
+}
+#endif
+
+/*
+ * Handlers for -j option.
+ */
+       public void
+opt_j(type, s)
+       int type;
+       char *s;
+{
+       PARG parg;
+       char buf[16];
+       int len;
+       int err;
+
+       switch (type)
+       {
+       case INIT:
+       case TOGGLE:
+               if (*s == '.')
+               {
+                       s++;
+                       jump_sline_fraction = getfraction(&s, "j", &err);
+                       if (err)
+                               error("Invalid line fraction", NULL_PARG);
+                       else
+                               calc_jump_sline();
+               } else
+               {
+                       int sline = getnum(&s, "j", &err);
+                       if (err)
+                               error("Invalid line number", NULL_PARG);
+                       else
+                       {
+                               jump_sline = sline;
+                               jump_sline_fraction = -1;
+                       }
+               }
+               break;
+       case QUERY:
+               if (jump_sline_fraction < 0)
+               {
+                       parg.p_int =  jump_sline;
+                       error("Position target at screen line %d", &parg);
+               } else
+               {
+
+                       sprintf(buf, ".%06d", jump_sline_fraction);
+                       len = strlen(buf);
+                       while (len > 2 && buf[len-1] == '0')
+                               len--;
+                       buf[len] = '\0';
+                       parg.p_string = buf;
+                       error("Position target at screen position %s", &parg);
+               }
+               break;
+       }
+}
+
+       public void
+calc_jump_sline()
+{
+       if (jump_sline_fraction < 0)
+               return;
+       jump_sline = sc_height * jump_sline_fraction / NUM_FRAC_DENOM;
+}
+
+/*
+ * Handlers for -# option.
+ */
+       public void
+opt_shift(type, s)
+       int type;
+       char *s;
+{
+       PARG parg;
+       char buf[16];
+       int len;
+       int err;
+
+       switch (type)
+       {
+       case INIT:
+       case TOGGLE:
+               if (*s == '.')
+               {
+                       s++;
+                       shift_count_fraction = getfraction(&s, "#", &err);
+                       if (err)
+                               error("Invalid column fraction", NULL_PARG);
+                       else
+                               calc_shift_count();
+               } else
+               {
+                       int hs = getnum(&s, "#", &err);
+                       if (err)
+                               error("Invalid column number", NULL_PARG);
+                       else
+                       {
+                               shift_count = hs;
+                               shift_count_fraction = -1;
+                       }
+               }
+               break;
+       case QUERY:
+               if (shift_count_fraction < 0)
+               {
+                       parg.p_int = shift_count;
+                       error("Horizontal shift %d columns", &parg);
+               } else
+               {
+
+                       sprintf(buf, ".%06d", shift_count_fraction);
+                       len = strlen(buf);
+                       while (len > 2 && buf[len-1] == '0')
+                               len--;
+                       buf[len] = '\0';
+                       parg.p_string = buf;
+                       error("Horizontal shift %s of screen width", &parg);
+               }
+               break;
+       }
+}
+       public void
+calc_shift_count()
+{
+       if (shift_count_fraction < 0)
+               return;
+       shift_count = sc_width * shift_count_fraction / NUM_FRAC_DENOM;
+}
+
+#if USERFILE
+       public void
+opt_k(type, s)
+       int type;
+       char *s;
+{
+       PARG parg;
+
+       switch (type)
+       {
+       case INIT:
+               if (lesskey(s, 0))
+               {
+                       parg.p_string = s;
+                       error("Cannot use lesskey file \"%s\"", &parg);
+               }
+               break;
+       }
+}
+#endif
+
+#if TAGS
+/*
+ * Handler for -t option.
+ */
+       public void
+opt_t(type, s)
+       int type;
+       char *s;
+{
+       IFILE save_ifile;
+       POSITION pos;
+
+       switch (type)
+       {
+       case INIT:
+               tagoption = s;
+               /* Do the rest in main() */
+               break;
+       case TOGGLE:
+               if (secure)
+               {
+                       error("tags support is not available", NULL_PARG);
+                       break;
+               }
+               findtag(skipsp(s));
+               save_ifile = save_curr_ifile();
+               /*
+                * Try to open the file containing the tag
+                * and search for the tag in that file.
+                */
+               if (edit_tagfile() || (pos = tagsearch()) == NULL_POSITION)
+               {
+                       /* Failed: reopen the old file. */
+                       reedit_ifile(save_ifile);
+                       break;
+               }
+               unsave_ifile(save_ifile);
+               jump_loc(pos, jump_sline);
+               break;
+       }
+}
+
+/*
+ * Handler for -T option.
+ */
+       public void
+opt__T(type, s)
+       int type;
+       char *s;
+{
+       PARG parg;
+
+       switch (type)
+       {
+       case INIT:
+               tags = s;
+               break;
+       case TOGGLE:
+               s = skipsp(s);
+               tags = lglob(s);
+               break;
+       case QUERY:
+               parg.p_string = tags;
+               error("Tags file \"%s\"", &parg);
+               break;
+       }
+}
+#endif
+
+/*
+ * Handler for -p option.
+ */
+       public void
+opt_p(type, s)
+       int type;
+       register char *s;
+{
+       switch (type)
+       {
+       case INIT:
+               /*
+                * Unget a search command for the specified string.
+                * {{ This won't work if the "/" command is
+                *    changed or invalidated by a .lesskey file. }}
+                */
+               plusoption = TRUE;
+               ungetsc(s);
+               /*
+                * In "more" mode, the -p argument is a command,
+                * not a search string, so we don't need a slash.
+                */
+               if (!less_is_more)
+                       ungetsc("/");
+               break;
+       }
+}
+
+/*
+ * Handler for -P option.
+ */
+       public void
+opt__P(type, s)
+       int type;
+       register char *s;
+{
+       register char **proto;
+       PARG parg;
+
+       switch (type)
+       {
+       case INIT:
+       case TOGGLE:
+               /*
+                * Figure out which prototype string should be changed.
+                */
+               switch (*s)
+               {
+               case 's':  proto = &prproto[PR_SHORT];  s++;    break;
+               case 'm':  proto = &prproto[PR_MEDIUM]; s++;    break;
+               case 'M':  proto = &prproto[PR_LONG];   s++;    break;
+               case '=':  proto = &eqproto;            s++;    break;
+               case 'h':  proto = &hproto;             s++;    break;
+               case 'w':  proto = &wproto;             s++;    break;
+               default:   proto = &prproto[PR_SHORT];          break;
+               }
+               free(*proto);
+               *proto = save(s);
+               break;
+       case QUERY:
+               parg.p_string = prproto[pr_type];
+               error("%s", &parg);
+               break;
+       }
+}
+
+/*
+ * Handler for the -b option.
+ */
+       /*ARGSUSED*/
+       public void
+opt_b(type, s)
+       int type;
+       char *s;
+{
+       switch (type)
+       {
+       case INIT:
+       case TOGGLE:
+               /*
+                * Set the new number of buffers.
+                */
+               ch_setbufspace(bufspace);
+               break;
+       case QUERY:
+               break;
+       }
+}
+
+/*
+ * Handler for the -i option.
+ */
+       /*ARGSUSED*/
+       public void
+opt_i(type, s)
+       int type;
+       char *s;
+{
+       switch (type)
+       {
+       case TOGGLE:
+               chg_caseless();
+               break;
+       case QUERY:
+       case INIT:
+               break;
+       }
+}
+
+/*
+ * Handler for the -V option.
+ */
+       /*ARGSUSED*/
+       public void
+opt__V(type, s)
+       int type;
+       char *s;
+{
+       switch (type)
+       {
+       case TOGGLE:
+       case QUERY:
+               dispversion();
+               break;
+       case INIT:
+               /*
+                * Force output to stdout per GNU standard for --version output.
+                */
+               any_display = 1;
+               putstr("less ");
+               putstr(version);
+               putstr("\nCopyright (C) 1984-2009 Mark Nudelman\n\n");
+               putstr("less comes with NO WARRANTY, to the extent permitted by law.\n");
+               putstr("For information about the terms of redistribution,\n");
+               putstr("see the file named README in the less distribution.\n");
+               putstr("Homepage: http://www.greenwoodsoftware.com/less\n");
+               quit(QUIT_OK);
+               break;
+       }
+}
+
+#if MSDOS_COMPILER
+/*
+ * Parse an MSDOS color descriptor.
+ */
+       static void
+colordesc(s, fg_color, bg_color)
+       char *s;
+       int *fg_color;
+       int *bg_color;
+{
+       int fg, bg;
+       int err;
+       
+       fg = getnum(&s, "D", &err);
+       if (err)
+       {
+               error("Missing fg color in -D", NULL_PARG);
+               return;
+       }
+       if (*s != '.')
+               bg = nm_bg_color;
+       else
+       {
+               s++;
+               bg = getnum(&s, "D", &err);
+               if (err)
+               {
+                       error("Missing bg color in -D", NULL_PARG);
+                       return;
+               }
+       }
+       if (*s != '\0')
+               error("Extra characters at end of -D option", NULL_PARG);
+       *fg_color = fg;
+       *bg_color = bg;
+}
+
+/*
+ * Handler for the -D option.
+ */
+       /*ARGSUSED*/
+       public void
+opt_D(type, s)
+       int type;
+       char *s;
+{
+       switch (type)
+       {
+       case INIT:
+       case TOGGLE:
+               switch (*s++)
+               {
+               case 'n':
+                       colordesc(s, &nm_fg_color, &nm_bg_color);
+                       break;
+               case 'd':
+                       colordesc(s, &bo_fg_color, &bo_bg_color);
+                       break;
+               case 'u':
+                       colordesc(s, &ul_fg_color, &ul_bg_color);
+                       break;
+               case 'k':
+                       colordesc(s, &bl_fg_color, &bl_bg_color);
+                       break;
+               case 's':
+                       colordesc(s, &so_fg_color, &so_bg_color);
+                       break;
+               default:
+                       error("-D must be followed by n, d, u, k or s", NULL_PARG);
+                       break;
+               }
+               if (type == TOGGLE)
+               {
+                       at_enter(AT_STANDOUT);
+                       at_exit();
+               }
+               break;
+       case QUERY:
+               break;
+       }
+}
+#endif
+
+/*
+ * Handler for the -x option.
+ */
+       public void
+opt_x(type, s)
+       int type;
+       register char *s;
+{
+       extern int tabstops[];
+       extern int ntabstops;
+       extern int tabdefault;
+       char msg[60+(4*TABSTOP_MAX)];
+       int i;
+       PARG p;
+
+       switch (type)
+       {
+       case INIT:
+       case TOGGLE:
+               /* Start at 1 because tabstops[0] is always zero. */
+               for (i = 1;  i < TABSTOP_MAX;  )
+               {
+                       int n = 0;
+                       s = skipsp(s);
+                       while (*s >= '0' && *s <= '9')
+                               n = (10 * n) + (*s++ - '0');
+                       if (n > tabstops[i-1])
+                               tabstops[i++] = n;
+                       s = skipsp(s);
+                       if (*s++ != ',')
+                               break;
+               }
+               if (i < 2)
+                       return;
+               ntabstops = i;
+               tabdefault = tabstops[ntabstops-1] - tabstops[ntabstops-2];
+               break;
+       case QUERY:
+               strcpy(msg, "Tab stops ");
+               if (ntabstops > 2)
+               {
+                       for (i = 1;  i < ntabstops;  i++)
+                       {
+                               if (i > 1)
+                                       strcat(msg, ",");
+                               sprintf(msg+strlen(msg), "%d", tabstops[i]);
+                       }
+                       sprintf(msg+strlen(msg), " and then ");
+               }
+               sprintf(msg+strlen(msg), "every %d spaces",
+                       tabdefault);
+               p.p_string = msg;
+               error("%s", &p);
+               break;
+       }
+}
+
+
+/*
+ * Handler for the -" option.
+ */
+       public void
+opt_quote(type, s)
+       int type;
+       register char *s;
+{
+       char buf[3];
+       PARG parg;
+
+       switch (type)
+       {
+       case INIT:
+       case TOGGLE:
+               if (s[0] == '\0')
+               {
+                       openquote = closequote = '\0';
+                       break;
+               }
+               if (s[1] != '\0' && s[2] != '\0')
+               {
+                       error("-\" must be followed by 1 or 2 chars", NULL_PARG);
+                       return;
+               }
+               openquote = s[0];
+               if (s[1] == '\0')
+                       closequote = openquote;
+               else
+                       closequote = s[1];
+               break;
+       case QUERY:
+               buf[0] = openquote;
+               buf[1] = closequote;
+               buf[2] = '\0';
+               parg.p_string = buf;
+               error("quotes %s", &parg);
+               break;
+       }
+}
+
+/*
+ * "-?" means display a help message.
+ * If from the command line, exit immediately.
+ */
+       /*ARGSUSED*/
+       public void
+opt_query(type, s)
+       int type;
+       char *s;
+{
+       switch (type)
+       {
+       case QUERY:
+       case TOGGLE:
+               error("Use \"h\" for help", NULL_PARG);
+               break;
+       case INIT:
+               dohelp = 1;
+       }
+}
+
+/*
+ * Get the "screen window" size.
+ */
+       public int
+get_swindow()
+{
+       if (swindow > 0)
+               return (swindow);
+       return (sc_height + swindow);
+}
+
diff --git a/thirdparty/less/option.c b/thirdparty/less/option.c
new file mode 100644 (file)
index 0000000..acb8962
--- /dev/null
@@ -0,0 +1,702 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Process command line options.
+ *
+ * Each option is a single letter which controls a program variable.
+ * The options have defaults which may be changed via
+ * the command line option, toggled via the "-" command, 
+ * or queried via the "_" command.
+ */
+
+#include "less.h"
+#include "option.h"
+
+static struct loption *pendopt;
+public int plusoption = FALSE;
+
+static char *optstring();
+static int flip_triple();
+
+extern int screen_trashed;
+extern int less_is_more;
+extern int quit_at_eof;
+extern char *every_first_cmd;
+
+/*
+ * Return a printable description of an option.
+ */
+       static char *
+opt_desc(o)
+       struct loption *o;
+{
+       static char buf[OPTNAME_MAX + 10];
+       if (o->oletter == OLETTER_NONE)
+               SNPRINTF1(buf, sizeof(buf), "--%s", o->onames->oname);
+       else
+               SNPRINTF2(buf, sizeof(buf), "-%c (--%s)", o->oletter, o->onames->oname);
+       return (buf);
+}
+
+/*
+ * Return a string suitable for printing as the "name" of an option.
+ * For example, if the option letter is 'x', just return "-x".
+ */
+       public char *
+propt(c)
+       int c;
+{
+       static char buf[8];
+
+       sprintf(buf, "-%s", prchar(c));
+       return (buf);
+}
+
+/* 
+ * Scan an argument (either from the command line or from the 
+ * LESS environment variable) and process it.
+ */
+       public void
+scan_option(s)
+       char *s;
+{
+       register struct loption *o;
+       register int optc;
+       char *optname;
+       char *printopt;
+       char *str;
+       int set_default;
+       int lc;
+       int err;
+       PARG parg;
+
+       if (s == NULL)
+               return;
+
+       /*
+        * If we have a pending option which requires an argument,
+        * handle it now.
+        * This happens if the previous option was, for example, "-P"
+        * without a following string.  In that case, the current
+        * option is simply the argument for the previous option.
+        */
+       if (pendopt != NULL)
+       {
+               switch (pendopt->otype & OTYPE)
+               {
+               case STRING:
+                       (*pendopt->ofunc)(INIT, s);
+                       break;
+               case NUMBER:
+                       printopt = opt_desc(pendopt);
+                       *(pendopt->ovar) = getnum(&s, printopt, (int*)NULL);
+                       break;
+               }
+               pendopt = NULL;
+               return;
+       }
+
+       set_default = FALSE;
+       optname = NULL;
+
+       while (*s != '\0')
+       {
+               /*
+                * Check some special cases first.
+                */
+               switch (optc = *s++)
+               {
+               case ' ':
+               case '\t':
+               case END_OPTION_STRING:
+                       continue;
+               case '-':
+                       /*
+                        * "--" indicates an option name instead of a letter.
+                        */
+                       if (*s == '-')
+                       {
+                               optname = ++s;
+                               break;
+                       }
+                       /*
+                        * "-+" means set these options back to their defaults.
+                        * (They may have been set otherwise by previous 
+                        * options.)
+                        */
+                       set_default = (*s == '+');
+                       if (set_default)
+                               s++;
+                       continue;
+               case '+':
+                       /*
+                        * An option prefixed by a "+" is ungotten, so 
+                        * that it is interpreted as less commands 
+                        * processed at the start of the first input file.
+                        * "++" means process the commands at the start of
+                        * EVERY input file.
+                        */
+                       plusoption = TRUE;
+                       s = optstring(s, &str, propt('+'), NULL);
+                       if (*str == '+')
+                               every_first_cmd = save(++str);
+                       else
+                               ungetsc(str);
+                       continue;
+               case '0':  case '1':  case '2':  case '3':  case '4':
+               case '5':  case '6':  case '7':  case '8':  case '9':
+                       /*
+                        * Special "more" compatibility form "-<number>"
+                        * instead of -z<number> to set the scrolling 
+                        * window size.
+                        */
+                       s--;
+                       optc = 'z';
+                       break;
+               case 'n':
+                       if (less_is_more)
+                               optc = 'z';
+                       break;
+               }
+
+               /*
+                * Not a special case.
+                * Look up the option letter in the option table.
+                */
+               err = 0;
+               if (optname == NULL)
+               {
+                       printopt = propt(optc);
+                       lc = ASCII_IS_LOWER(optc);
+                       o = findopt(optc);
+               } else
+               {
+                       printopt = optname;
+                       lc = ASCII_IS_LOWER(optname[0]);
+                       o = findopt_name(&optname, NULL, &err);
+                       s = optname;
+                       optname = NULL;
+                       if (*s == '\0' || *s == ' ')
+                       {
+                               /*
+                                * The option name matches exactly.
+                                */
+                               ;
+                       } else if (*s == '=')
+                       {
+                               /*
+                                * The option name is followed by "=value".
+                                */
+                               if (o != NULL &&
+                                   (o->otype & OTYPE) != STRING &&
+                                   (o->otype & OTYPE) != NUMBER)
+                               {
+                                       parg.p_string = printopt;
+                                       error("The %s option should not be followed by =",
+                                               &parg);
+                                       quit(QUIT_ERROR);
+                               }
+                               s++;
+                       } else
+                       {
+                               /*
+                                * The specified name is longer than the
+                                * real option name.
+                                */
+                               o = NULL;
+                       }
+               }
+               if (o == NULL)
+               {
+                       parg.p_string = printopt;
+                       if (err == OPT_AMBIG)
+                               error("%s is an ambiguous abbreviation (\"less --help\" for help)",
+                                       &parg);
+                       else
+                               error("There is no %s option (\"less --help\" for help)",
+                                       &parg);
+                       quit(QUIT_ERROR);
+               }
+
+               str = NULL;
+               switch (o->otype & OTYPE)
+               {
+               case BOOL:
+                       if (set_default)
+                               *(o->ovar) = o->odefault;
+                       else
+                               *(o->ovar) = ! o->odefault;
+                       break;
+               case TRIPLE:
+                       if (set_default)
+                               *(o->ovar) = o->odefault;
+                       else
+                               *(o->ovar) = flip_triple(o->odefault, lc);
+                       break;
+               case STRING:
+                       if (*s == '\0')
+                       {
+                               /*
+                                * Set pendopt and return.
+                                * We will get the string next time
+                                * scan_option is called.
+                                */
+                               pendopt = o;
+                               return;
+                       }
+                       /*
+                        * Don't do anything here.
+                        * All processing of STRING options is done by 
+                        * the handling function.
+                        */
+                       while (*s == ' ')
+                               s++;
+                       s = optstring(s, &str, printopt, o->odesc[1]);
+                       break;
+               case NUMBER:
+                       if (*s == '\0')
+                       {
+                               pendopt = o;
+                               return;
+                       }
+                       *(o->ovar) = getnum(&s, printopt, (int*)NULL);
+                       break;
+               }
+               /*
+                * If the option has a handling function, call it.
+                */
+               if (o->ofunc != NULL)
+                       (*o->ofunc)(INIT, str);
+       }
+}
+
+/*
+ * Toggle command line flags from within the program.
+ * Used by the "-" and "_" commands.
+ * how_toggle may be:
+ *     OPT_NO_TOGGLE   just report the current setting, without changing it.
+ *     OPT_TOGGLE      invert the current setting
+ *     OPT_UNSET       set to the default value
+ *     OPT_SET         set to the inverse of the default value
+ */
+       public void
+toggle_option(o, lower, s, how_toggle)
+       struct loption *o;
+       int lower;
+       char *s;
+       int how_toggle;
+{
+       register int num;
+       int no_prompt;
+       int err;
+       PARG parg;
+
+       no_prompt = (how_toggle & OPT_NO_PROMPT);
+       how_toggle &= ~OPT_NO_PROMPT;
+
+       if (o == NULL)
+       {
+               error("No such option", NULL_PARG);
+               return;
+       }
+
+       if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE))
+       {
+               parg.p_string = opt_desc(o);
+               error("Cannot change the %s option", &parg);
+               return;
+       }
+
+       if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY))
+       {
+               parg.p_string = opt_desc(o);
+               error("Cannot query the %s option", &parg);
+               return;
+       } 
+
+       /*
+        * Check for something which appears to be a do_toggle
+        * (because the "-" command was used), but really is not.
+        * This could be a string option with no string, or
+        * a number option with no number.
+        */
+       switch (o->otype & OTYPE)
+       {
+       case STRING:
+       case NUMBER:
+               if (how_toggle == OPT_TOGGLE && *s == '\0')
+                       how_toggle = OPT_NO_TOGGLE;
+               break;
+       }
+
+#if HILITE_SEARCH
+       if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
+               repaint_hilite(0);
+#endif
+
+       /*
+        * Now actually toggle (change) the variable.
+        */
+       if (how_toggle != OPT_NO_TOGGLE)
+       {
+               switch (o->otype & OTYPE)
+               {
+               case BOOL:
+                       /*
+                        * Boolean.
+                        */
+                       switch (how_toggle)
+                       {
+                       case OPT_TOGGLE:
+                               *(o->ovar) = ! *(o->ovar);
+                               break;
+                       case OPT_UNSET:
+                               *(o->ovar) = o->odefault;
+                               break;
+                       case OPT_SET:
+                               *(o->ovar) = ! o->odefault;
+                               break;
+                       }
+                       break;
+               case TRIPLE:
+                       /*
+                        * Triple:
+                        *      If user gave the lower case letter, then switch 
+                        *      to 1 unless already 1, in which case make it 0.
+                        *      If user gave the upper case letter, then switch
+                        *      to 2 unless already 2, in which case make it 0.
+                        */
+                       switch (how_toggle)
+                       {
+                       case OPT_TOGGLE:
+                               *(o->ovar) = flip_triple(*(o->ovar), lower);
+                               break;
+                       case OPT_UNSET:
+                               *(o->ovar) = o->odefault;
+                               break;
+                       case OPT_SET:
+                               *(o->ovar) = flip_triple(o->odefault, lower);
+                               break;
+                       }
+                       break;
+               case STRING:
+                       /*
+                        * String: don't do anything here.
+                        *      The handling function will do everything.
+                        */
+                       switch (how_toggle)
+                       {
+                       case OPT_SET:
+                       case OPT_UNSET:
+                               error("Cannot use \"-+\" or \"--\" for a string option",
+                                       NULL_PARG);
+                               return;
+                       }
+                       break;
+               case NUMBER:
+                       /*
+                        * Number: set the variable to the given number.
+                        */
+                       switch (how_toggle)
+                       {
+                       case OPT_TOGGLE:
+                               num = getnum(&s, NULL, &err);
+                               if (!err)
+                                       *(o->ovar) = num;
+                               break;
+                       case OPT_UNSET:
+                               *(o->ovar) = o->odefault;
+                               break;
+                       case OPT_SET:
+                               error("Can't use \"-!\" for a numeric option",
+                                       NULL_PARG);
+                               return;
+                       }
+                       break;
+               }
+       }
+
+       /*
+        * Call the handling function for any special action 
+        * specific to this option.
+        */
+       if (o->ofunc != NULL)
+               (*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
+
+#if HILITE_SEARCH
+       if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
+               chg_hilite();
+#endif
+
+       if (!no_prompt)
+       {
+               /*
+                * Print a message describing the new setting.
+                */
+               switch (o->otype & OTYPE)
+               {
+               case BOOL:
+               case TRIPLE:
+                       /*
+                        * Print the odesc message.
+                        */
+                       error(o->odesc[*(o->ovar)], NULL_PARG);
+                       break;
+               case NUMBER:
+                       /*
+                        * The message is in odesc[1] and has a %d for 
+                        * the value of the variable.
+                        */
+                       parg.p_int = *(o->ovar);
+                       error(o->odesc[1], &parg);
+                       break;
+               case STRING:
+                       /*
+                        * Message was already printed by the handling function.
+                        */
+                       break;
+               }
+       }
+
+       if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT))
+               screen_trashed = TRUE;
+}
+
+/*
+ * "Toggle" a triple-valued option.
+ */
+       static int
+flip_triple(val, lc)
+       int val;
+       int lc;
+{
+       if (lc)
+               return ((val == OPT_ON) ? OPT_OFF : OPT_ON);
+       else
+               return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS);
+}
+
+/*
+ * Determine if an option takes a parameter.
+ */
+       public int
+opt_has_param(o)
+       struct loption *o;
+{
+       if (o == NULL)
+               return (0);
+       if (o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE))
+               return (0);
+       return (1);
+}
+
+/*
+ * Return the prompt to be used for a given option letter.
+ * Only string and number valued options have prompts.
+ */
+       public char *
+opt_prompt(o)
+       struct loption *o;
+{
+       if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
+               return ("?");
+       return (o->odesc[0]);
+}
+
+/*
+ * Return whether or not there is a string option pending;
+ * that is, if the previous option was a string-valued option letter 
+ * (like -P) without a following string.
+ * In that case, the current option is taken to be the string for
+ * the previous option.
+ */
+       public int
+isoptpending()
+{
+       return (pendopt != NULL);
+}
+
+/*
+ * Print error message about missing string.
+ */
+       static void
+nostring(printopt)
+       char *printopt;
+{
+       PARG parg;
+       parg.p_string = printopt;
+       error("Value is required after %s", &parg);
+}
+
+/*
+ * Print error message if a STRING type option is not followed by a string.
+ */
+       public void
+nopendopt()
+{
+       nostring(opt_desc(pendopt));
+}
+
+/*
+ * Scan to end of string or to an END_OPTION_STRING character.
+ * In the latter case, replace the char with a null char.
+ * Return a pointer to the remainder of the string, if any.
+ */
+       static char *
+optstring(s, p_str, printopt, validchars)
+       char *s;
+       char **p_str;
+       char *printopt;
+       char *validchars;
+{
+       register char *p;
+
+       if (*s == '\0')
+       {
+               nostring(printopt);
+               quit(QUIT_ERROR);
+       }
+       *p_str = s;
+       for (p = s;  *p != '\0';  p++)
+       {
+               if (*p == END_OPTION_STRING ||
+                   (validchars != NULL && strchr(validchars, *p) == NULL))
+               {
+                       switch (*p)
+                       {
+                       case END_OPTION_STRING:
+                       case ' ':  case '\t':  case '-':
+                               /* Replace the char with a null to terminate string. */
+                               *p++ = '\0';
+                               break;
+                       default:
+                               /* Cannot replace char; make a copy of the string. */
+                               *p_str = (char *) ecalloc(p-s+1, sizeof(char));
+                               strncpy(*p_str, s, p-s);
+                               (*p_str)[p-s] = '\0';
+                               break;
+                       }
+                       break;
+               }
+       }
+       return (p);
+}
+
+/*
+ */
+       static int
+num_error(printopt, errp)
+       char *printopt;
+       int *errp;
+{
+       PARG parg;
+
+       if (errp != NULL)
+       {
+               *errp = TRUE;
+               return (-1);
+       }
+       if (printopt != NULL)
+       {
+               parg.p_string = printopt;
+               error("Number is required after %s", &parg);
+       }
+       quit(QUIT_ERROR);
+       /* NOTREACHED */
+       return (-1);
+}
+
+/*
+ * Translate a string into a number.
+ * Like atoi(), but takes a pointer to a char *, and updates
+ * the char * to point after the translated number.
+ */
+       public int
+getnum(sp, printopt, errp)
+       char **sp;
+       char *printopt;
+       int *errp;
+{
+       register char *s;
+       register int n;
+       register int neg;
+
+       s = skipsp(*sp);
+       neg = FALSE;
+       if (*s == '-')
+       {
+               neg = TRUE;
+               s++;
+       }
+       if (*s < '0' || *s > '9')
+               return (num_error(printopt, errp));
+
+       n = 0;
+       while (*s >= '0' && *s <= '9')
+               n = 10 * n + *s++ - '0';
+       *sp = s;
+       if (errp != NULL)
+               *errp = FALSE;
+       if (neg)
+               n = -n;
+       return (n);
+}
+
+/*
+ * Translate a string into a fraction, represented by the part of a
+ * number which would follow a decimal point.
+ * The value of the fraction is returned as parts per NUM_FRAC_DENOM.
+ * That is, if "n" is returned, the fraction intended is n/NUM_FRAC_DENOM.
+ */
+       public long
+getfraction(sp, printopt, errp)
+       char **sp;
+       char *printopt;
+       int *errp;
+{
+       register char *s;
+       long frac = 0;
+       int fraclen = 0;
+
+       s = skipsp(*sp);
+       if (*s < '0' || *s > '9')
+               return (num_error(printopt, errp));
+
+       for ( ;  *s >= '0' && *s <= '9';  s++)
+       {
+               frac = (frac * 10) + (*s - '0');
+               fraclen++;
+       }
+       if (fraclen > NUM_LOG_FRAC_DENOM)
+               while (fraclen-- > NUM_LOG_FRAC_DENOM)
+                       frac /= 10;
+       else
+               while (fraclen++ < NUM_LOG_FRAC_DENOM)
+                       frac *= 10;
+       *sp = s;
+       if (errp != NULL)
+               *errp = FALSE;
+       return (frac);
+}
+
+
+/*
+ * Get the value of the -e flag.
+ */
+       public int
+get_quit_at_eof()
+{
+       if (!less_is_more)
+               return quit_at_eof;
+       /* When less_is_more is set, the -e flag semantics are different. */
+       return quit_at_eof ? OPT_ON : OPT_ONPLUS;
+}
diff --git a/thirdparty/less/option.h b/thirdparty/less/option.h
new file mode 100644 (file)
index 0000000..a32139e
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+#define        END_OPTION_STRING       ('$')
+
+/*
+ * Types of options.
+ */
+#define        BOOL            01      /* Boolean option: 0 or 1 */
+#define        TRIPLE          02      /* Triple-valued option: 0, 1 or 2 */
+#define        NUMBER          04      /* Numeric option */
+#define        STRING          010     /* String-valued option */
+#define        NOVAR           020     /* No associated variable */
+#define        REPAINT         040     /* Repaint screen after toggling option */
+#define        NO_TOGGLE       0100    /* Option cannot be toggled with "-" cmd */
+#define        HL_REPAINT      0200    /* Repaint hilites after toggling option */
+#define        NO_QUERY        0400    /* Option cannot be queried with "_" cmd */
+#define        INIT_HANDLER    01000   /* Call option handler function at startup */
+
+#define        OTYPE           (BOOL|TRIPLE|NUMBER|STRING|NOVAR)
+
+#define OLETTER_NONE    '\1'     /* Invalid option letter */
+
+/*
+ * Argument to a handling function tells what type of activity:
+ */
+#define        INIT    0       /* Initialization (from command line) */
+#define        QUERY   1       /* Query (from _ or - command) */
+#define        TOGGLE  2       /* Change value (from - command) */
+
+/* Flag to toggle_option to specify how to "toggle" */
+#define        OPT_NO_TOGGLE   0
+#define        OPT_TOGGLE      1
+#define        OPT_UNSET       2
+#define        OPT_SET         3
+#define OPT_NO_PROMPT  0100
+
+/* Error code from findopt_name */
+#define OPT_AMBIG       1
+
+struct optname
+{
+       char *oname;            /* Long (GNU-style) option name */
+       struct optname *onext;  /* List of synonymous option names */
+};
+
+#define OPTNAME_MAX    32      /* Max length of long option name */
+
+struct loption
+{
+       char oletter;           /* The controlling letter (a-z) */
+       struct optname *onames; /* Long (GNU-style) option name */
+       int otype;              /* Type of the option */
+       int odefault;           /* Default value */
+       int *ovar;              /* Pointer to the associated variable */
+       void (*ofunc)();        /* Pointer to special handling function */
+       char *odesc[3];         /* Description of each value */
+};
+
diff --git a/thirdparty/less/opttbl.c b/thirdparty/less/opttbl.c
new file mode 100644 (file)
index 0000000..63f6889
--- /dev/null
@@ -0,0 +1,599 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * The option table.
+ */
+
+#include "less.h"
+#include "option.h"
+
+/*
+ * Variables controlled by command line options.
+ */
+public int quiet;              /* Should we suppress the audible bell? */
+public int how_search;         /* Where should forward searches start? */
+public int top_scroll;         /* Repaint screen from top?
+                                  (alternative is scroll from bottom) */
+public int pr_type;            /* Type of prompt (short, medium, long) */
+public int bs_mode;            /* How to process backspaces */
+public int know_dumb;          /* Don't complain about dumb terminals */
+public int quit_at_eof;                /* Quit after hitting end of file twice */
+public int quit_if_one_screen; /* Quit if EOF on first screen */
+public int squeeze;            /* Squeeze multiple blank lines into one */
+public int tabstop;            /* Tab settings */
+public int back_scroll;                /* Repaint screen on backwards movement */
+public int forw_scroll;                /* Repaint screen on forward movement */
+public int caseless;           /* Do "caseless" searches */
+public int linenums;           /* Use line numbers */
+public int autobuf;            /* Automatically allocate buffers as needed */
+public int bufspace;           /* Max buffer space per file (K) */
+public int ctldisp;            /* Send control chars to screen untranslated */
+public int force_open;         /* Open the file even if not regular file */
+public int swindow;            /* Size of scrolling window */
+public int jump_sline;         /* Screen line of "jump target" */
+public long jump_sline_fraction = -1;
+public long shift_count_fraction = -1;
+public int chopline;           /* Truncate displayed lines at screen width */
+public int no_init;            /* Disable sending ti/te termcap strings */
+public int no_keypad;          /* Disable sending ks/ke termcap strings */
+public int twiddle;             /* Show tildes after EOF */
+public int show_attn;          /* Hilite first unread line */
+public int shift_count;                /* Number of positions to shift horizontally */
+public int status_col;         /* Display a status column */
+public int use_lessopen;       /* Use the LESSOPEN filter */
+public int quit_on_intr;       /* Quit on interrupt */
+public int follow_mode;                /* F cmd Follows file desc or file name? */
+public int oldbot;             /* Old bottom of screen behavior {{REMOVE}} */
+#if HILITE_SEARCH
+public int hilite_search;      /* Highlight matched search patterns? */
+#endif
+
+public int less_is_more = 0;   /* Make compatible with POSIX more */
+
+/*
+ * Long option names.
+ */
+static struct optname a_optname      = { "search-skip-screen",   NULL };
+static struct optname b_optname      = { "buffers",              NULL };
+static struct optname B__optname     = { "auto-buffers",         NULL };
+static struct optname c_optname      = { "clear-screen",         NULL };
+static struct optname d_optname      = { "dumb",                 NULL };
+#if MSDOS_COMPILER
+static struct optname D__optname     = { "color",                NULL };
+#endif
+static struct optname e_optname      = { "quit-at-eof",          NULL };
+static struct optname f_optname      = { "force",                NULL };
+static struct optname F__optname     = { "quit-if-one-screen",   NULL };
+#if HILITE_SEARCH
+static struct optname g_optname      = { "hilite-search",        NULL };
+#endif
+static struct optname h_optname      = { "max-back-scroll",      NULL };
+static struct optname i_optname      = { "ignore-case",          NULL };
+static struct optname j_optname      = { "jump-target",          NULL };
+static struct optname J__optname     = { "status-column",        NULL };
+#if USERFILE
+static struct optname k_optname      = { "lesskey-file",         NULL };
+#endif
+static struct optname K__optname     = { "quit-on-intr",         NULL };
+static struct optname L__optname     = { "no-lessopen",          NULL };
+static struct optname m_optname      = { "long-prompt",          NULL };
+static struct optname n_optname      = { "line-numbers",         NULL };
+#if LOGFILE
+static struct optname o_optname      = { "log-file",             NULL };
+static struct optname O__optname     = { "LOG-FILE",             NULL };
+#endif
+static struct optname p_optname      = { "pattern",              NULL };
+static struct optname P__optname     = { "prompt",               NULL };
+static struct optname q2_optname     = { "silent",               NULL };
+static struct optname q_optname      = { "quiet",                &q2_optname };
+static struct optname r_optname      = { "raw-control-chars",    NULL };
+static struct optname s_optname      = { "squeeze-blank-lines",  NULL };
+static struct optname S__optname     = { "chop-long-lines",      NULL };
+#if TAGS
+static struct optname t_optname      = { "tag",                  NULL };
+static struct optname T__optname     = { "tag-file",             NULL };
+#endif
+static struct optname u_optname      = { "underline-special",    NULL };
+static struct optname V__optname     = { "version",              NULL };
+static struct optname w_optname      = { "hilite-unread",        NULL };
+static struct optname x_optname      = { "tabs",                 NULL };
+static struct optname X__optname     = { "no-init",              NULL };
+static struct optname y_optname      = { "max-forw-scroll",      NULL };
+static struct optname z_optname      = { "window",               NULL };
+static struct optname quote_optname  = { "quotes",               NULL };
+static struct optname tilde_optname  = { "tilde",                NULL };
+static struct optname query_optname  = { "help",                 NULL };
+static struct optname pound_optname  = { "shift",                NULL };
+static struct optname keypad_optname = { "no-keypad",            NULL };
+static struct optname oldbot_optname = { "old-bot",              NULL };
+static struct optname follow_optname = { "follow-name",          NULL };
+
+
+/*
+ * Table of all options and their semantics.
+ *
+ * For BOOL and TRIPLE options, odesc[0], odesc[1], odesc[2] are
+ * the description of the option when set to 0, 1 or 2, respectively.
+ * For NUMBER options, odesc[0] is the prompt to use when entering
+ * a new value, and odesc[1] is the description, which should contain 
+ * one %d which is replaced by the value of the number.
+ * For STRING options, odesc[0] is the prompt to use when entering
+ * a new value, and odesc[1], if not NULL, is the set of characters
+ * that are valid in the string.
+ */
+static struct loption option[] =
+{
+       { 'a', &a_optname,
+               TRIPLE, OPT_ONPLUS, &how_search, NULL,
+               {
+                       "Search includes displayed screen",
+                       "Search skips displayed screen",
+                       "Search includes all of displayed screen"
+               }
+       },
+
+       { 'b', &b_optname,
+               NUMBER|INIT_HANDLER, 64, &bufspace, opt_b, 
+               {
+                       "Max buffer space per file (K): ",
+                       "Max buffer space per file: %dK",
+                       NULL
+               }
+       },
+       { 'B', &B__optname,
+               BOOL, OPT_ON, &autobuf, NULL,
+               {
+                       "Don't automatically allocate buffers",
+                       "Automatically allocate buffers when needed",
+                       NULL
+               }
+       },
+       { 'c', &c_optname,
+               TRIPLE, OPT_OFF, &top_scroll, NULL,
+               {
+                       "Repaint by scrolling from bottom of screen",
+                       "Repaint by painting from top of screen",
+                       "Repaint by painting from top of screen"
+               }
+       },
+       { 'd', &d_optname,
+               BOOL|NO_TOGGLE, OPT_OFF, &know_dumb, NULL,
+               {
+                       "Assume intelligent terminal",
+                       "Assume dumb terminal",
+                       NULL
+               }
+       },
+#if MSDOS_COMPILER
+       { 'D', &D__optname,
+               STRING|REPAINT|NO_QUERY, 0, NULL, opt_D,
+               {
+                       "color desc: ", 
+                       "Ddknsu0123456789.",
+                       NULL
+               }
+       },
+#endif
+       { 'e', &e_optname,
+               TRIPLE, OPT_OFF, &quit_at_eof, NULL,
+               {
+                       "Don't quit at end-of-file",
+                       "Quit at end-of-file",
+                       "Quit immediately at end-of-file"
+               }
+       },
+       { 'f', &f_optname,
+               BOOL, OPT_OFF, &force_open, NULL,
+               {
+                       "Open only regular files",
+                       "Open even non-regular files",
+                       NULL
+               }
+       },
+       { 'F', &F__optname,
+               BOOL, OPT_OFF, &quit_if_one_screen, NULL,
+               {
+                       "Don't quit if end-of-file on first screen",
+                       "Quit if end-of-file on first screen",
+                       NULL
+               }
+       },
+#if HILITE_SEARCH
+       { 'g', &g_optname,
+               TRIPLE|HL_REPAINT, OPT_ONPLUS, &hilite_search, NULL,
+               {
+                       "Don't highlight search matches",
+                       "Highlight matches for previous search only",
+                       "Highlight all matches for previous search pattern",
+               }
+       },
+#endif
+       { 'h', &h_optname,
+               NUMBER, -1, &back_scroll, NULL,
+               {
+                       "Backwards scroll limit: ",
+                       "Backwards scroll limit is %d lines",
+                       NULL
+               }
+       },
+       { 'i', &i_optname,
+               TRIPLE|HL_REPAINT, OPT_OFF, &caseless, opt_i,
+               {
+                       "Case is significant in searches",
+                       "Ignore case in searches",
+                       "Ignore case in searches and in patterns"
+               }
+       },
+       { 'j', &j_optname,
+               STRING, 0, NULL, opt_j,
+               {
+                       "Target line: ",
+                       "0123456789.-",
+                       NULL
+               }
+       },
+       { 'J', &J__optname,
+               BOOL|REPAINT, OPT_OFF, &status_col, NULL,
+               {
+                       "Don't display a status column",
+                       "Display a status column",
+                       NULL
+               }
+       },
+#if USERFILE
+       { 'k', &k_optname,
+               STRING|NO_TOGGLE|NO_QUERY, 0, NULL, opt_k,
+               { NULL, NULL, NULL }
+       },
+#endif
+       { 'K', &K__optname,
+               BOOL, OPT_OFF, &quit_on_intr, NULL,
+               {
+                       "Interrupt (ctrl-C) returns to prompt",
+                       "Interrupt (ctrl-C) exits less",
+                       NULL
+               }
+       },
+       { 'L', &L__optname,
+               BOOL, OPT_ON, &use_lessopen, NULL,
+               {
+                       "Don't use the LESSOPEN filter",
+                       "Use the LESSOPEN filter",
+                       NULL
+               }
+       },
+       { 'm', &m_optname,
+               TRIPLE, OPT_OFF, &pr_type, NULL,
+               {
+                       "Short prompt",
+                       "Medium prompt",
+                       "Long prompt"
+               }
+       },
+       { 'n', &n_optname,
+               TRIPLE|REPAINT, OPT_ON, &linenums, NULL,
+               {
+                       "Don't use line numbers",
+                       "Use line numbers",
+                       "Constantly display line numbers"
+               }
+       },
+#if LOGFILE
+       { 'o', &o_optname,
+               STRING, 0, NULL, opt_o,
+               { "log file: ", NULL, NULL }
+       },
+       { 'O', &O__optname,
+               STRING, 0, NULL, opt__O,
+               { "Log file: ", NULL, NULL }
+       },
+#endif
+       { 'p', &p_optname,
+               STRING|NO_TOGGLE|NO_QUERY, 0, NULL, opt_p,
+               { NULL, NULL, NULL }
+       },
+       { 'P', &P__optname,
+               STRING, 0, NULL, opt__P,
+               { "prompt: ", NULL, NULL }
+       },
+       { 'q', &q_optname,
+               TRIPLE, OPT_OFF, &quiet, NULL,
+               {
+                       "Ring the bell for errors AND at eof/bof",
+                       "Ring the bell for errors but not at eof/bof",
+                       "Never ring the bell"
+               }
+       },
+       { 'r', &r_optname,
+               TRIPLE|REPAINT, OPT_OFF, &ctldisp, NULL,
+               {
+                       "Display control characters as ^X",
+                       "Display control characters directly",
+                       "Display control characters directly, processing ANSI sequences"
+               }
+       },
+       { 's', &s_optname,
+               BOOL|REPAINT, OPT_OFF, &squeeze, NULL,
+               {
+                       "Display all blank lines",
+                       "Squeeze multiple blank lines",
+                       NULL
+               }
+       },
+       { 'S', &S__optname,
+               BOOL|REPAINT, OPT_OFF, &chopline, NULL,
+               {
+                       "Fold long lines",
+                       "Chop long lines",
+                       NULL
+               }
+       },
+#if TAGS
+       { 't', &t_optname,
+               STRING|NO_QUERY, 0, NULL, opt_t,
+               { "tag: ", NULL, NULL }
+       },
+       { 'T', &T__optname,
+               STRING, 0, NULL, opt__T,
+               { "tags file: ", NULL, NULL }
+       },
+#endif
+       { 'u', &u_optname,
+               TRIPLE|REPAINT, OPT_OFF, &bs_mode, NULL,
+               {
+                       "Display underlined text in underline mode",
+                       "Backspaces cause overstrike",
+                       "Print backspace as ^H"
+               }
+       },
+       { 'V', &V__optname,
+               NOVAR, 0, NULL, opt__V,
+               { NULL, NULL, NULL }
+       },
+       { 'w', &w_optname,
+               TRIPLE|REPAINT, OPT_OFF, &show_attn, NULL,
+               {
+                       "Don't highlight first unread line",
+                       "Highlight first unread line after forward-screen",
+                       "Highlight first unread line after any forward movement",
+               }
+       },
+       { 'x', &x_optname,
+               STRING|REPAINT, 0, NULL, opt_x,
+               {
+                       "Tab stops: ",
+                       "0123456789,",
+                       NULL
+               }
+       },
+       { 'X', &X__optname,
+               BOOL|NO_TOGGLE, OPT_OFF, &no_init, NULL,
+               {
+                       "Send init/deinit strings to terminal",
+                       "Don't use init/deinit strings",
+                       NULL
+               }
+       },
+       { 'y', &y_optname,
+               NUMBER, -1, &forw_scroll, NULL,
+               {
+                       "Forward scroll limit: ",
+                       "Forward scroll limit is %d lines",
+                       NULL
+               }
+       },
+       { 'z', &z_optname,
+               NUMBER, -1, &swindow, NULL,
+               {
+                       "Scroll window size: ",
+                       "Scroll window size is %d lines",
+                       NULL
+               }
+       },
+       { '"', &quote_optname,
+               STRING, 0, NULL, opt_quote,
+               { "quotes: ", NULL, NULL }
+       },
+       { '~', &tilde_optname,
+               BOOL|REPAINT, OPT_ON, &twiddle, NULL,
+               {
+                       "Don't show tildes after end of file",
+                       "Show tildes after end of file",
+                       NULL
+               }
+       },
+       { '?', &query_optname,
+               NOVAR, 0, NULL, opt_query,
+               { NULL, NULL, NULL }
+       },
+       { '#', &pound_optname,
+               STRING, 0, NULL, opt_shift,
+               {
+                       "Horizontal shift: ",
+                       "0123456789.",
+                       NULL
+               }
+       },
+       { OLETTER_NONE, &keypad_optname,
+               BOOL|NO_TOGGLE, OPT_OFF, &no_keypad, NULL,
+               {
+                       "Use keypad mode",
+                       "Don't use keypad mode",
+                       NULL
+               }
+       },
+       { OLETTER_NONE, &oldbot_optname,
+               BOOL, OPT_OFF, &oldbot, NULL,
+               {
+                       "Use new bottom of screen behavior",
+                       "Use old bottom of screen behavior",
+                       NULL
+               }
+       },
+       { OLETTER_NONE, &follow_optname,
+               BOOL, FOLLOW_DESC, &follow_mode, NULL,
+               {
+                       "F command follows file descriptor",
+                       "F command follows file name",
+                       NULL
+               }
+       },
+       { '\0', NULL, NOVAR, 0, NULL, NULL, { NULL, NULL, NULL } }
+};
+
+
+/*
+ * Initialize each option to its default value.
+ */
+       public void
+init_option()
+{
+       register struct loption *o;
+       char *p;
+
+       p = lgetenv("LESS_IS_MORE");
+       if (p != NULL && *p != '\0')
+               less_is_more = 1;
+
+       for (o = option;  o->oletter != '\0';  o++)
+       {
+               /*
+                * Set each variable to its default.
+                */
+               if (o->ovar != NULL)
+                       *(o->ovar) = o->odefault;
+               if (o->otype & INIT_HANDLER)
+                       (*(o->ofunc))(INIT, (char *) NULL);
+       }
+}
+
+/*
+ * Find an option in the option table, given its option letter.
+ */
+       public struct loption *
+findopt(c)
+       int c;
+{
+       register struct loption *o;
+
+       for (o = option;  o->oletter != '\0';  o++)
+       {
+               if (o->oletter == c)
+                       return (o);
+               if ((o->otype & TRIPLE) && ASCII_TO_UPPER(o->oletter) == c)
+                       return (o);
+       }
+       return (NULL);
+}
+
+/*
+ *
+ */
+       static int
+is_optchar(c)
+       char c;
+{
+       if (ASCII_IS_UPPER(c))
+               return 1;
+       if (ASCII_IS_LOWER(c))
+               return 1;
+       if (c == '-')
+               return 1;
+       return 0;
+}
+
+/*
+ * Find an option in the option table, given its option name.
+ * p_optname is the (possibly partial) name to look for, and
+ * is updated to point after the matched name.
+ * p_oname if non-NULL is set to point to the full option name.
+ */
+       public struct loption *
+findopt_name(p_optname, p_oname, p_err)
+       char **p_optname;
+       char **p_oname;
+       int *p_err;
+{
+       char *optname = *p_optname;
+       register struct loption *o;
+       register struct optname *oname;
+       register int len;
+       int uppercase;
+       struct loption *maxo = NULL;
+       struct optname *maxoname = NULL;
+       int maxlen = 0;
+       int ambig = 0;
+       int exact = 0;
+
+       /*
+        * Check all options.
+        */
+       for (o = option;  o->oletter != '\0';  o++)
+       {
+               /*
+                * Check all names for this option.
+                */
+               for (oname = o->onames;  oname != NULL;  oname = oname->onext)
+               {
+                       /* 
+                        * Try normal match first (uppercase == 0),
+                        * then, then if it's a TRIPLE option,
+                        * try uppercase match (uppercase == 1).
+                        */
+                       for (uppercase = 0;  uppercase <= 1;  uppercase++)
+                       {
+                               len = sprefix(optname, oname->oname, uppercase);
+                               if (len <= 0 || is_optchar(optname[len]))
+                               {
+                                       /*
+                                        * We didn't use all of the option name.
+                                        */
+                                       continue;
+                               }
+                               if (!exact && len == maxlen)
+                                       /*
+                                        * Already had a partial match,
+                                        * and now there's another one that
+                                        * matches the same length.
+                                        */
+                                       ambig = 1;
+                               else if (len > maxlen)
+                               {
+                                       /*
+                                        * Found a better match than
+                                        * the one we had.
+                                        */
+                                       maxo = o;
+                                       maxoname = oname;
+                                       maxlen = len;
+                                       ambig = 0;
+                                       exact = (len == (int)strlen(oname->oname));
+                               }
+                               if (!(o->otype & TRIPLE))
+                                       break;
+                       }
+               }
+       }
+       if (ambig)
+       {
+               /*
+                * Name matched more than one option.
+                */
+               if (p_err != NULL)
+                       *p_err = OPT_AMBIG;
+               return (NULL);
+       }
+       *p_optname = optname + maxlen;
+       if (p_oname != NULL)
+               *p_oname = maxoname == NULL ? NULL : maxoname->oname;
+       return (maxo);
+}
diff --git a/thirdparty/less/os.c b/thirdparty/less/os.c
new file mode 100644 (file)
index 0000000..dbb52fe
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Operating system dependent routines.
+ *
+ * Most of the stuff in here is based on Unix, but an attempt
+ * has been made to make things work on other operating systems.
+ * This will sometimes result in a loss of functionality, unless
+ * someone rewrites code specifically for the new operating system.
+ *
+ * The makefile provides defines to decide whether various
+ * Unix features are present.
+ */
+
+#include "less.h"
+#include <signal.h>
+#include <setjmp.h>
+#if HAVE_TIME_H
+#include <time.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_VALUES_H
+#include <values.h>
+#endif
+
+#if HAVE_TIME_T
+#define time_type      time_t
+#else
+#define        time_type       long
+#endif
+
+/*
+ * BSD setjmp() saves (and longjmp() restores) the signal mask.
+ * This costs a system call or two per setjmp(), so if possible we clear the
+ * signal mask with sigsetmask(), and use _setjmp()/_longjmp() instead.
+ * On other systems, setjmp() doesn't affect the signal mask and so
+ * _setjmp() does not exist; we just use setjmp().
+ */
+#if HAVE__SETJMP && HAVE_SIGSETMASK
+#define SET_JUMP       _setjmp
+#define LONG_JUMP      _longjmp
+#else
+#define SET_JUMP       setjmp
+#define LONG_JUMP      longjmp
+#endif
+
+public int reading;
+
+static jmp_buf read_label;
+
+extern int sigs;
+
+/*
+ * Like read() system call, but is deliberately interruptible.
+ * A call to intread() from a signal handler will interrupt
+ * any pending iread().
+ */
+       public int
+iread(fd, buf, len)
+       int fd;
+       char *buf;
+       unsigned int len;
+{
+       register int n;
+
+start:
+#if MSDOS_COMPILER==WIN32C
+       if (ABORT_SIGS())
+               return (READ_INTR);
+#else
+#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
+       if (kbhit())
+       {
+               int c;
+               
+               c = getch();
+               if (c == '\003')
+                       return (READ_INTR);
+               ungetch(c);
+       }
+#endif
+#endif
+       if (SET_JUMP(read_label))
+       {
+               /*
+                * We jumped here from intread.
+                */
+               reading = 0;
+#if HAVE_SIGPROCMASK
+               {
+                 sigset_t mask;
+                 sigemptyset(&mask);
+                 sigprocmask(SIG_SETMASK, &mask, NULL);
+               }
+#else
+#if HAVE_SIGSETMASK
+               sigsetmask(0);
+#else
+#ifdef _OSK
+               sigmask(~0);
+#endif
+#endif
+#endif
+               return (READ_INTR);
+       }
+
+       flush();
+       reading = 1;
+#if MSDOS_COMPILER==DJGPPC
+       if (isatty(fd))
+       {
+               /*
+                * Don't try reading from a TTY until a character is
+                * available, because that makes some background programs
+                * believe DOS is busy in a way that prevents those
+                * programs from working while "less" waits.
+                */
+               fd_set readfds;
+
+               FD_ZERO(&readfds);
+               FD_SET(fd, &readfds);
+               if (select(fd+1, &readfds, 0, 0, 0) == -1)
+                       return (-1);
+       }
+#endif
+       n = read(fd, buf, len);
+#if 1
+       /*
+        * This is a kludge to workaround a problem on some systems
+        * where terminating a remote tty connection causes read() to
+        * start returning 0 forever, instead of -1.
+        */
+       {
+               extern int ignore_eoi;
+               if (!ignore_eoi)
+               {
+                       static int consecutive_nulls = 0;
+                       if (n == 0)
+                               consecutive_nulls++;
+                       else
+                               consecutive_nulls = 0;
+                       if (consecutive_nulls > 20)
+                               quit(QUIT_ERROR);
+               }
+       }
+#endif
+       reading = 0;
+       if (n < 0)
+       {
+#if HAVE_ERRNO
+               /*
+                * Certain values of errno indicate we should just retry the read.
+                */
+#if MUST_DEFINE_ERRNO
+               extern int errno;
+#endif
+#ifdef EINTR
+               if (errno == EINTR)
+                       goto start;
+#endif
+#ifdef EAGAIN
+               if (errno == EAGAIN)
+                       goto start;
+#endif
+#endif
+               return (-1);
+       }
+       return (n);
+}
+
+/*
+ * Interrupt a pending iread().
+ */
+       public void
+intread()
+{
+       LONG_JUMP(read_label, 1);
+}
+
+/*
+ * Return the current time.
+ */
+#if HAVE_TIME
+       public long
+get_time()
+{
+       time_type t;
+
+       time(&t);
+       return (t);
+}
+#endif
+
+
+#if !HAVE_STRERROR
+/*
+ * Local version of strerror, if not available from the system.
+ */
+       static char *
+strerror(err)
+       int err;
+{
+#if HAVE_SYS_ERRLIST
+       static char buf[16];
+       extern char *sys_errlist[];
+       extern int sys_nerr;
+  
+       if (err < sys_nerr)
+               return sys_errlist[err];
+       sprintf(buf, "Error %d", err);
+       return buf;
+#else
+       return ("cannot open");
+#endif
+}
+#endif
+
+/*
+ * errno_message: Return an error message based on the value of "errno".
+ */
+       public char *
+errno_message(filename)
+       char *filename;
+{
+       register char *p;
+       register char *m;
+       int len;
+#if HAVE_ERRNO
+#if MUST_DEFINE_ERRNO
+       extern int errno;
+#endif
+       p = strerror(errno);
+#else
+       p = "cannot open";
+#endif
+       len = strlen(filename) + strlen(p) + 3;
+       m = (char *) ecalloc(len, sizeof(char));
+       SNPRINTF2(m, len, "%s: %s", filename, p);
+       return (m);
+}
+
+/* #define HAVE_FLOAT 0 */
+
+       static POSITION
+muldiv(val, num, den)
+       POSITION val, num, den;
+{
+#if HAVE_FLOAT
+       double v = (((double) val) * num) / den;
+       return ((POSITION) (v + 0.5));
+#else
+       POSITION v = ((POSITION) val) * num;
+
+       if (v / num == val)
+               /* No overflow */
+               return (POSITION) (v / den);
+       else
+               /* Above calculation overflows; 
+                * use a method that is less precise but won't overflow. */
+               return (POSITION) (val / (den / num));
+#endif
+}
+
+/*
+ * Return the ratio of two POSITIONS, as a percentage.
+ * {{ Assumes a POSITION is a long int. }}
+ */
+       public int
+percentage(num, den)
+       POSITION num, den;
+{
+       return (int) muldiv(num,  (POSITION) 100, den);
+}
+
+/*
+ * Return the specified percentage of a POSITION.
+ */
+       public POSITION
+percent_pos(pos, percent, fraction)
+       POSITION pos;
+       int percent;
+       long fraction;
+{
+       /* Change percent (parts per 100) to perden (parts per NUM_FRAC_DENOM). */
+       POSITION perden = (percent * (NUM_FRAC_DENOM / 100)) + (fraction / 100);
+
+       if (perden == 0)
+               return (0);
+       return (POSITION) muldiv(pos, perden, (POSITION) NUM_FRAC_DENOM);
+}
+
+#if !HAVE_STRCHR
+/*
+ * strchr is used by regexp.c.
+ */
+       char *
+strchr(s, c)
+       char *s;
+       int c;
+{
+       for ( ;  *s != '\0';  s++)
+               if (*s == c)
+                       return (s);
+       if (c == '\0')
+               return (s);
+       return (NULL);
+}
+#endif
+
+#if !HAVE_MEMCPY
+       VOID_POINTER
+memcpy(dst, src, len)
+       VOID_POINTER dst;
+       VOID_POINTER src;
+       int len;
+{
+       char *dstp = (char *) dst;
+       char *srcp = (char *) src;
+       int i;
+
+       for (i = 0;  i < len;  i++)
+               dstp[i] = srcp[i];
+       return (dst);
+}
+#endif
+
+#ifdef _OSK_MWC32
+
+/*
+ * This implements an ANSI-style intercept setup for Microware C 3.2
+ */
+       public int 
+os9_signal(type, handler)
+       int type;
+       RETSIGTYPE (*handler)();
+{
+       intercept(handler);
+}
+
+#include <sgstat.h>
+
+       int 
+isatty(f)
+       int f;
+{
+       struct sgbuf sgbuf;
+
+       if (_gs_opt(f, &sgbuf) < 0)
+               return -1;
+       return (sgbuf.sg_class == 0);
+}
+       
+#endif
diff --git a/thirdparty/less/output.c b/thirdparty/less/output.c
new file mode 100644 (file)
index 0000000..a4fdada
--- /dev/null
@@ -0,0 +1,601 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * High level routines dealing with the output to the screen.
+ */
+
+#include "less.h"
+#if MSDOS_COMPILER==WIN32C
+#include "windows.h"
+#endif
+
+public int errmsgs;    /* Count of messages displayed by error() */
+public int need_clr;
+public int final_attr;
+public int at_prompt;
+
+extern int sigs;
+extern int sc_width;
+extern int so_s_width, so_e_width;
+extern int screen_trashed;
+extern int any_display;
+extern int is_tty;
+extern int oldbot;
+
+#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
+extern int ctldisp;
+extern int nm_fg_color, nm_bg_color;
+extern int bo_fg_color, bo_bg_color;
+extern int ul_fg_color, ul_bg_color;
+extern int so_fg_color, so_bg_color;
+extern int bl_fg_color, bl_bg_color;
+#endif
+
+/*
+ * Display the line which is in the line buffer.
+ */
+       public void
+put_line()
+{
+       register int c;
+       register int i;
+       int a;
+
+       if (ABORT_SIGS())
+       {
+               /*
+                * Don't output if a signal is pending.
+                */
+               screen_trashed = 1;
+               return;
+       }
+
+       final_attr = AT_NORMAL;
+
+       for (i = 0;  (c = gline(i, &a)) != '\0';  i++)
+       {
+               at_switch(a);
+               final_attr = a;
+               if (c == '\b')
+                       putbs();
+               else
+                       putchr(c);
+       }
+
+       at_exit();
+}
+
+static char obuf[OUTBUF_SIZE];
+static char *ob = obuf;
+
+/*
+ * Flush buffered output.
+ *
+ * If we haven't displayed any file data yet,
+ * output messages on error output (file descriptor 2),
+ * otherwise output on standard output (file descriptor 1).
+ *
+ * This has the desirable effect of producing all
+ * error messages on error output if standard output
+ * is directed to a file.  It also does the same if
+ * we never produce any real output; for example, if
+ * the input file(s) cannot be opened.  If we do
+ * eventually produce output, code in edit() makes
+ * sure these messages can be seen before they are
+ * overwritten or scrolled away.
+ */
+       public void
+flush()
+{
+       register int n;
+       register int fd;
+
+       n = ob - obuf;
+       if (n == 0)
+               return;
+
+#if MSDOS_COMPILER==MSOFTC
+       if (is_tty && any_display)
+       {
+               *ob = '\0';
+               _outtext(obuf);
+               ob = obuf;
+               return;
+       }
+#else
+#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
+       if (is_tty && any_display)
+       {
+               *ob = '\0';
+               if (ctldisp != OPT_ONPLUS)
+                       WIN32textout(obuf, ob - obuf);
+               else
+               {
+                       /*
+                        * Look for SGR escape sequences, and convert them
+                        * to color commands.  Replace bold, underline,
+                        * and italic escapes into colors specified via
+                        * the -D command-line option.
+                        */
+                       char *anchor, *p, *p_next;
+                       unsigned char fg, bg;
+                       static unsigned char at;
+#if MSDOS_COMPILER==WIN32C
+                       /* Screen colors used by 3x and 4x SGR commands. */
+                       static unsigned char screen_color[] = {
+                               0, /* BLACK */
+                               FOREGROUND_RED,
+                               FOREGROUND_GREEN,
+                               FOREGROUND_RED|FOREGROUND_GREEN,
+                               FOREGROUND_BLUE, 
+                               FOREGROUND_BLUE|FOREGROUND_RED,
+                               FOREGROUND_BLUE|FOREGROUND_GREEN,
+                               FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED
+                       };
+#else
+                       static enum COLORS screen_color[] = {
+                               BLACK, RED, GREEN, BROWN,
+                               BLUE, MAGENTA, CYAN, LIGHTGRAY
+                       };
+#endif
+
+                       for (anchor = p_next = obuf;
+                            (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; )
+                       {
+                               p = p_next;
+                               if (p[1] == '[')  /* "ESC-[" sequence */
+                               {
+                                       if (p > anchor)
+                                       {
+                                               /*
+                                                * If some chars seen since
+                                                * the last escape sequence,
+                                                * write them out to the screen.
+                                                */
+                                               WIN32textout(anchor, p-anchor);
+                                               anchor = p;
+                                       }
+                                       p += 2;  /* Skip the "ESC-[" */
+                                       if (is_ansi_end(*p))
+                                       {
+                                               /*
+                                                * Handle null escape sequence
+                                                * "ESC[m", which restores
+                                                * the normal color.
+                                                */
+                                               p++;
+                                               anchor = p_next = p;
+                                               WIN32setcolors(nm_fg_color, nm_bg_color);
+                                               continue;
+                                       }
+                                       p_next = p;
+
+                                       /*
+                                        * Select foreground/background colors
+                                        * based on the escape sequence. 
+                                        */
+                                       fg = nm_fg_color;
+                                       bg = nm_bg_color;
+                                       while (!is_ansi_end(*p))
+                                       {
+                                               char *q;
+                                               long code = strtol(p, &q, 10);
+
+                                               if (*q == '\0')
+                                               {
+                                                       /*
+                                                        * Incomplete sequence.
+                                                        * Leave it unprocessed
+                                                        * in the buffer.
+                                                        */
+                                                       int slop = q - anchor;
+                                                       /* {{ strcpy args overlap! }} */
+                                                       strcpy(obuf, anchor);
+                                                       ob = &obuf[slop];
+                                                       return;
+                                               }
+
+                                               if (q == p ||
+                                                   code > 49 || code < 0 ||
+                                                   (!is_ansi_end(*q) && *q != ';'))
+                                               {
+                                                       p_next = q;
+                                                       break;
+                                               }
+                                               if (*q == ';')
+                                                       q++;
+
+                                               switch (code)
+                                               {
+                                               default:
+                                               /* case 0: all attrs off */
+                                                       fg = nm_fg_color;
+                                                       bg = nm_bg_color;
+                                                       at = 0;
+                                                       break;
+                                               case 1: /* bold on */
+                                                       at |= 1;
+                                                       break;
+                                               case 3: /* italic on */
+                                               case 7: /* inverse on */
+                                                       at |= 2;
+                                                       break;
+                                               case 4: /* underline on */
+                                                       at |= 4;
+                                                       break;
+                                               case 5: /* slow blink on */
+                                               case 6: /* fast blink on */
+                                                       at |= 8;
+                                                       break;
+                                               case 8: /* concealed on */
+                                                       fg = (bg & 7) | 8;
+                                                       break;
+                                               case 22: /* bold off */
+                                                       at &= ~1;
+                                                       break;
+                                               case 23: /* italic off */
+                                               case 27: /* inverse off */
+                                                       at &= ~2;
+                                                       break;
+                                               case 24: /* underline off */
+                                                       at &= ~4;
+                                                       break;
+                                               case 30: case 31: case 32:
+                                               case 33: case 34: case 35:
+                                               case 36: case 37:
+                                                       fg = (fg & 8) | (screen_color[code - 30]);
+                                                       break;
+                                               case 39: /* default fg */
+                                                       fg = nm_fg_color;
+                                                       break;
+                                               case 40: case 41: case 42:
+                                               case 43: case 44: case 45:
+                                               case 46: case 47:
+                                                       bg = (bg & 8) | (screen_color[code - 40]);
+                                                       break;
+                                               case 49: /* default fg */
+                                                       bg = nm_bg_color;
+                                                       break;
+                                               }
+                                               p = q;
+                                       }
+                                       if (!is_ansi_end(*p) || p == p_next)
+                                               break;
+                                       if (at & 1)
+                                       {
+                                                       fg = bo_fg_color;
+                                                       bg = bo_bg_color;
+                                       } else if (at & 2)
+                                       {
+                                                       fg = so_fg_color;
+                                                       bg = so_bg_color;
+                                       } else if (at & 4)
+                                       {
+                                                       fg = ul_fg_color;
+                                                       bg = ul_bg_color;
+                                       } else if (at & 8)
+                                       {
+                                                       fg = bl_fg_color;
+                                                       bg = bl_bg_color;
+                                       }
+                                       fg &= 0xf;
+                                       bg &= 0xf;
+                                       WIN32setcolors(fg, bg);
+                                       p_next = anchor = p + 1;
+                               } else
+                                       p_next++;
+                       }
+
+                       /* Output what's left in the buffer.  */
+                       WIN32textout(anchor, ob - anchor);
+               }
+               ob = obuf;
+               return;
+       }
+#endif
+#endif
+       fd = (any_display) ? 1 : 2;
+       if (write(fd, obuf, n) != n)
+               screen_trashed = 1;
+       ob = obuf;
+}
+
+/*
+ * Output a character.
+ */
+       public int
+putchr(c)
+       int c;
+{
+#if 0 /* fake UTF-8 output for testing */
+       extern int utf_mode;
+       if (utf_mode)
+       {
+               static char ubuf[MAX_UTF_CHAR_LEN];
+               static int ubuf_len = 0;
+               static int ubuf_index = 0;
+               if (ubuf_len == 0)
+               {
+                       ubuf_len = utf_len(c);
+                       ubuf_index = 0;
+               }
+               ubuf[ubuf_index++] = c;
+               if (ubuf_index < ubuf_len)
+                       return c;
+               c = get_wchar(ubuf) & 0xFF;
+               ubuf_len = 0;
+       }
+#endif
+       if (need_clr)
+       {
+               need_clr = 0;
+               clear_bot();
+       }
+#if MSDOS_COMPILER
+       if (c == '\n' && is_tty)
+       {
+               /* remove_top(1); */
+               putchr('\r');
+       }
+#else
+#ifdef _OSK
+       if (c == '\n' && is_tty)  /* In OS-9, '\n' == 0x0D */
+               putchr(0x0A);
+#endif
+#endif
+       /*
+        * Some versions of flush() write to *ob, so we must flush
+        * when we are still one char from the end of obuf.
+        */
+       if (ob >= &obuf[sizeof(obuf)-1])
+               flush();
+       *ob++ = c;
+       at_prompt = 0;
+       return (c);
+}
+
+/*
+ * Output a string.
+ */
+       public void
+putstr(s)
+       register char *s;
+{
+       while (*s != '\0')
+               putchr(*s++);
+}
+
+
+/*
+ * Convert an integral type to a string.
+ */
+#define TYPE_TO_A_FUNC(funcname, type) \
+void funcname(num, buf) \
+       type num; \
+       char *buf; \
+{ \
+       int neg = (num < 0); \
+       char tbuf[INT_STRLEN_BOUND(num)+2]; \
+       register char *s = tbuf + sizeof(tbuf); \
+       if (neg) num = -num; \
+       *--s = '\0'; \
+       do { \
+               *--s = (num % 10) + '0'; \
+       } while ((num /= 10) != 0); \
+       if (neg) *--s = '-'; \
+       strcpy(buf, s); \
+}
+
+TYPE_TO_A_FUNC(postoa, POSITION)
+TYPE_TO_A_FUNC(linenumtoa, LINENUM)
+TYPE_TO_A_FUNC(inttoa, int)
+
+/*
+ * Output an integer in a given radix.
+ */
+       static int
+iprint_int(num)
+       int num;
+{
+       char buf[INT_STRLEN_BOUND(num)];
+
+       inttoa(num, buf);
+       putstr(buf);
+       return (strlen(buf));
+}
+
+/*
+ * Output a line number in a given radix.
+ */
+       static int
+iprint_linenum(num)
+       LINENUM num;
+{
+       char buf[INT_STRLEN_BOUND(num)];
+
+       linenumtoa(num, buf);
+       putstr(buf);
+       return (strlen(buf));
+}
+
+/*
+ * This function implements printf-like functionality
+ * using a more portable argument list mechanism than printf's.
+ */
+       static int
+less_printf(fmt, parg)
+       register char *fmt;
+       PARG *parg;
+{
+       register char *s;
+       register int col;
+
+       col = 0;
+       while (*fmt != '\0')
+       {
+               if (*fmt != '%')
+               {
+                       putchr(*fmt++);
+                       col++;
+               } else
+               {
+                       ++fmt;
+                       switch (*fmt++)
+                       {
+                       case 's':
+                               s = parg->p_string;
+                               parg++;
+                               while (*s != '\0')
+                               {
+                                       putchr(*s++);
+                                       col++;
+                               }
+                               break;
+                       case 'd':
+                               col += iprint_int(parg->p_int);
+                               parg++;
+                               break;
+                       case 'n':
+                               col += iprint_linenum(parg->p_linenum);
+                               parg++;
+                               break;
+                       }
+               }
+       }
+       return (col);
+}
+
+/*
+ * Get a RETURN.
+ * If some other non-trivial char is pressed, unget it, so it will
+ * become the next command.
+ */
+       public void
+get_return()
+{
+       int c;
+
+#if ONLY_RETURN
+       while ((c = getchr()) != '\n' && c != '\r')
+               bell();
+#else
+       c = getchr();
+       if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
+               ungetcc(c);
+#endif
+}
+
+/*
+ * Output a message in the lower left corner of the screen
+ * and wait for carriage return.
+ */
+       public void
+error(fmt, parg)
+       char *fmt;
+       PARG *parg;
+{
+       int col = 0;
+       static char return_to_continue[] = "  (press RETURN)";
+
+       errmsgs++;
+
+       if (any_display && is_tty)
+       {
+               if (!oldbot)
+                       squish_check();
+               at_exit();
+               clear_bot();
+               at_enter(AT_STANDOUT);
+               col += so_s_width;
+       }
+
+       col += less_printf(fmt, parg);
+
+       if (!(any_display && is_tty))
+       {
+               putchr('\n');
+               return;
+       }
+
+       putstr(return_to_continue);
+       at_exit();
+       col += sizeof(return_to_continue) + so_e_width;
+
+       get_return();
+       lower_left();
+    clear_eol();
+
+       if (col >= sc_width)
+               /*
+                * Printing the message has probably scrolled the screen.
+                * {{ Unless the terminal doesn't have auto margins,
+                *    in which case we just hammered on the right margin. }}
+                */
+               screen_trashed = 1;
+
+       flush();
+}
+
+static char intr_to_abort[] = "... (interrupt to abort)";
+
+/*
+ * Output a message in the lower left corner of the screen
+ * and don't wait for carriage return.
+ * Usually used to warn that we are beginning a potentially
+ * time-consuming operation.
+ */
+       public void
+ierror(fmt, parg)
+       char *fmt;
+       PARG *parg;
+{
+       at_exit();
+       clear_bot();
+       at_enter(AT_STANDOUT);
+       (void) less_printf(fmt, parg);
+       putstr(intr_to_abort);
+       at_exit();
+       flush();
+       need_clr = 1;
+}
+
+/*
+ * Output a message in the lower left corner of the screen
+ * and return a single-character response.
+ */
+       public int
+query(fmt, parg)
+       char *fmt;
+       PARG *parg;
+{
+       register int c;
+       int col = 0;
+
+       if (any_display && is_tty)
+               clear_bot();
+
+       (void) less_printf(fmt, parg);
+       c = getchr();
+
+       if (!(any_display && is_tty))
+       {
+               putchr('\n');
+               return (c);
+       }
+
+       lower_left();
+       if (col >= sc_width)
+               screen_trashed = 1;
+       flush();
+
+       return (c);
+}
diff --git a/thirdparty/less/pattern.c b/thirdparty/less/pattern.c
new file mode 100644 (file)
index 0000000..ca349b6
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+/*
+ * Routines to do pattern matching.
+ */
+
+#include "less.h"
+#include "pattern.h"
+
+extern int caseless;
+
+/*
+ * Compile a search pattern, for future use by match_pattern.
+ */
+       static int
+compile_pattern2(pattern, search_type, comp_pattern)
+       char *pattern;
+       int search_type;
+       void **comp_pattern;
+{
+       if ((search_type & SRCH_NO_REGEX) == 0)
+       {
+#if HAVE_POSIX_REGCOMP
+               regex_t *comp = (regex_t *) ecalloc(1, sizeof(regex_t));
+               regex_t **pcomp = (regex_t **) comp_pattern;
+               if (regcomp(comp, pattern, REGCOMP_FLAG))
+               {
+                       free(comp);
+                       error("Invalid pattern", NULL_PARG);
+                       return (-1);
+               }
+               if (*pcomp != NULL)
+                       regfree(*pcomp);
+               *pcomp = comp;
+#endif
+#if HAVE_PCRE
+               pcre *comp;
+               pcre **pcomp = (pcre **) comp_pattern;
+               const char *errstring;
+               int erroffset;
+               PARG parg;
+               comp = pcre_compile(pattern, 0,
+                               &errstring, &erroffset, NULL);
+               if (comp == NULL)
+               {
+                       parg.p_string = (char *) errstring;
+                       error("%s", &parg);
+                       return (-1);
+               }
+               *pcomp = comp;
+#endif
+#if HAVE_RE_COMP
+               PARG parg;
+               int *pcomp = (int *) comp_pattern;
+               if ((parg.p_string = re_comp(pattern)) != NULL)
+               {
+                       error("%s", &parg);
+                       return (-1);
+               }
+               *pcomp = 1;
+#endif
+#if HAVE_REGCMP
+               char *comp;
+               char **pcomp = (char **) comp_pattern;
+               if ((comp = regcmp(pattern, 0)) == NULL)
+               {
+                       error("Invalid pattern", NULL_PARG);
+                       return (-1);
+               }
+               if (pcomp != NULL)
+                       free(*pcomp);
+               *pcomp = comp;
+#endif
+#if HAVE_V8_REGCOMP
+               struct regexp *comp;
+               struct regexp **pcomp = (struct regexp **) comp_pattern;
+               if ((comp = regcomp(pattern)) == NULL)
+               {
+                       /*
+                        * regcomp has already printed an error message 
+                        * via regerror().
+                        */
+                       return (-1);
+               }
+               if (*pcomp != NULL)
+                       free(*pcomp);
+               *pcomp = comp;
+#endif
+       }
+       return (0);
+}
+
+/*
+ * Like compile_pattern2, but convert the pattern to lowercase if necessary.
+ */
+       public int
+compile_pattern(pattern, search_type, comp_pattern)
+       char *pattern;
+       int search_type;
+       void **comp_pattern;
+{
+       char *cvt_pattern;
+       int result;
+
+       if (caseless != OPT_ONPLUS)
+               cvt_pattern = pattern;
+       else
+       {
+               cvt_pattern = (char*) ecalloc(1, cvt_length(strlen(pattern), CVT_TO_LC));
+               cvt_text(cvt_pattern, pattern, (int *)NULL, (int *)NULL, CVT_TO_LC);
+       }
+       result = compile_pattern2(cvt_pattern, search_type, comp_pattern);
+       if (cvt_pattern != pattern)
+               free(cvt_pattern);
+       return (result);
+}
+
+/*
+ * Forget that we have a compiled pattern.
+ */
+       public void
+uncompile_pattern(pattern)
+       void **pattern;
+{
+#if HAVE_POSIX_REGCOMP
+       regex_t **pcomp = (regex_t **) pattern;
+       if (*pcomp != NULL)
+               regfree(*pcomp);
+       *pcomp = NULL;
+#endif
+#if HAVE_PCRE
+       pcre **pcomp = (pcre **) pattern;
+       if (*pcomp != NULL)
+               pcre_free(*pcomp);
+       *pcomp = NULL;
+#endif
+#if HAVE_RE_COMP
+       int *pcomp = (int *) pattern;
+       *pcomp = 0;
+#endif
+#if HAVE_REGCMP
+       char **pcomp = (char **) pattern;
+       if (*pcomp != NULL)
+               free(*pcomp);
+       *pcomp = NULL;
+#endif
+#if HAVE_V8_REGCOMP
+       struct regexp **pcomp = (struct regexp **) pattern;
+       if (*pcomp != NULL)
+               free(*pcomp);
+       *pcomp = NULL;
+#endif
+}
+
+/*
+ * Is a compiled pattern null?
+ */
+       public int
+is_null_pattern(pattern)
+       void *pattern;
+{
+#if HAVE_POSIX_REGCOMP
+       return (pattern == NULL);
+#endif
+#if HAVE_PCRE
+       return (pattern == NULL);
+#endif
+#if HAVE_RE_COMP
+       return (pattern == 0);
+#endif
+#if HAVE_REGCMP
+       return (pattern == NULL);
+#endif
+#if HAVE_V8_REGCOMP
+       return (pattern == NULL);
+#endif
+#if NO_REGEX
+       return (search_pattern != NULL);
+#endif
+}
+
+/*
+ * Simple pattern matching function.
+ * It supports no metacharacters like *, etc.
+ */
+       static int
+match(pattern, pattern_len, buf, buf_len, pfound, pend)
+       char *pattern;
+       int pattern_len;
+       char *buf;
+       int buf_len;
+       char **pfound, **pend;
+{
+       register char *pp, *lp;
+       register char *pattern_end = pattern + pattern_len;
+       register char *buf_end = buf + buf_len;
+
+       for ( ;  buf < buf_end;  buf++)
+       {
+               for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
+                       if (pp == pattern_end || lp == buf_end)
+                               break;
+               if (pp == pattern_end)
+               {
+                       if (pfound != NULL)
+                               *pfound = buf;
+                       if (pend != NULL)
+                               *pend = lp;
+                       return (1);
+               }
+       }
+       return (0);
+}
+
+/*
+ * Perform a pattern match with the previously compiled pattern.
+ * Set sp and ep to the start and end of the matched string.
+ */
+       public int
+match_pattern(pattern, tpattern, line, line_len, sp, ep, notbol, search_type)
+       void *pattern;
+       char *tpattern;
+       char *line;
+       int line_len;
+       char **sp;
+       char **ep;
+       int notbol;
+       int search_type;
+{
+       int matched;
+#if HAVE_POSIX_REGCOMP
+       regex_t *spattern = (regex_t *) pattern;
+#endif
+#if HAVE_PCRE
+       pcre *spattern = (pcre *) pattern;
+#endif
+#if HAVE_RE_COMP
+       int spattern = (int) pattern;
+#endif
+#if HAVE_REGCMP
+       char *spattern = (char *) pattern;
+#endif
+#if HAVE_V8_REGCOMP
+       struct regexp *spattern = (struct regexp *) pattern;
+#endif
+
+       if (search_type & SRCH_NO_REGEX)
+               matched = match(tpattern, strlen(tpattern), line, line_len, sp, ep);
+       else
+       {
+#if HAVE_POSIX_REGCOMP
+       {
+               regmatch_t rm;
+               int flags = (notbol) ? REG_NOTBOL : 0;
+               matched = !regexec(spattern, line, 1, &rm, flags);
+               if (matched)
+               {
+#ifndef __WATCOMC__
+                       *sp = line + rm.rm_so;
+                       *ep = line + rm.rm_eo;
+#else
+                       *sp = rm.rm_sp;
+                       *ep = rm.rm_ep;
+#endif
+               }
+       }
+#endif
+#if HAVE_PCRE
+       {
+               int flags = (notbol) ? PCRE_NOTBOL : 0;
+               int ovector[3];
+               matched = pcre_exec(spattern, NULL, line, line_len,
+                       0, flags, ovector, 3) >= 0;
+               if (matched)
+               {
+                       *sp = line + ovector[0];
+                       *ep = line + ovector[1];
+               }
+       }
+#endif
+#if HAVE_RE_COMP
+       matched = (re_exec(line) == 1);
+       /*
+        * re_exec doesn't seem to provide a way to get the matched string.
+        */
+       *sp = *ep = NULL;
+#endif
+#if HAVE_REGCMP
+       *ep = regex(spattern, line);
+       matched = (*ep != NULL);
+       if (matched)
+               *sp = __loc1;
+#endif
+#if HAVE_V8_REGCOMP
+#if HAVE_REGEXEC2
+       matched = regexec2(spattern, line, notbol);
+#else
+       matched = regexec(spattern, line);
+#endif
+       if (matched)
+       {
+               *sp = spattern->startp[0];
+               *ep = spattern->endp[0];
+       }
+#endif
+#if NO_REGEX
+       matched = match(tpattern, strlen(tpattern), line, line_len, sp, ep);
+#endif
+       }
+       matched = (!(search_type & SRCH_NO_MATCH) && matched) ||
+                       ((search_type & SRCH_NO_MATCH) && !matched);
+       return (matched);
+}
+
diff --git a/thirdparty/less/pattern.h b/thirdparty/less/pattern.h
new file mode 100644 (file)
index 0000000..3b44e2d
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+#if HAVE_POSIX_REGCOMP
+#include <regex.h>
+#ifdef REG_EXTENDED
+#define        REGCOMP_FLAG    REG_EXTENDED
+#else
+#define        REGCOMP_FLAG    0
+#endif
+#define DEFINE_PATTERN(name)  regex_t *name
+#define CLEAR_PATTERN(name)   name = NULL
+#endif
+
+#if HAVE_PCRE
+#include <pcre.h>
+#define DEFINE_PATTERN(name)  pcre *name
+#define CLEAR_PATTERN(name)   name = NULL
+#endif
+
+#if HAVE_RE_COMP
+char *re_comp();
+int re_exec();
+#define DEFINE_PATTERN(name)  int name
+#define CLEAR_PATTERN(name)   name = 0
+#endif
+
+#if HAVE_REGCMP
+char *regcmp();
+char *regex();
+extern char *__loc1;
+#define DEFINE_PATTERN(name)  char *name
+#define CLEAR_PATTERN(name)   name = NULL
+#endif
+
+#if HAVE_V8_REGCOMP
+#include "regexp.h"
+#define DEFINE_PATTERN(name)  struct regexp *name
+#define CLEAR_PATTERN(name)   name = NULL
+#endif
+
diff --git a/thirdparty/less/pckeys.h b/thirdparty/less/pckeys.h
new file mode 100644 (file)
index 0000000..3708d85
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Definitions of keys on the PC.
+ * Special (non-ASCII) keys on the PC send a two-byte sequence,
+ * where the first byte is 0 and the second is as defined below.
+ */
+#define        PCK_SHIFT_TAB           '\017'
+#define        PCK_ALT_E               '\022'
+#define        PCK_CAPS_LOCK           '\072'
+#define        PCK_F1                  '\073'
+#define        PCK_NUM_LOCK            '\105'
+#define        PCK_HOME                '\107'
+#define        PCK_UP                  '\110'
+#define        PCK_PAGEUP              '\111'
+#define        PCK_LEFT                '\113'
+#define        PCK_RIGHT               '\115'
+#define        PCK_END                 '\117'
+#define        PCK_DOWN                '\120'
+#define        PCK_PAGEDOWN            '\121'
+#define        PCK_INSERT              '\122'
+#define        PCK_DELETE              '\123'
+#define        PCK_CTL_LEFT            '\163'
+#define        PCK_CTL_RIGHT           '\164'
+#define        PCK_CTL_DELETE          '\223'
diff --git a/thirdparty/less/position.c b/thirdparty/less/position.c
new file mode 100644 (file)
index 0000000..8c05c5d
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Routines dealing with the "position" table.
+ * This is a table which tells the position (in the input file) of the
+ * first char on each currently displayed line.
+ *
+ * {{ The position table is scrolled by moving all the entries.
+ *    Would be better to have a circular table 
+ *    and just change a couple of pointers. }}
+ */
+
+#include "less.h"
+#include "position.h"
+
+static POSITION *table = NULL; /* The position table */
+static int table_size;
+
+extern int sc_width, sc_height;
+
+/*
+ * Return the starting file position of a line displayed on the screen.
+ * The line may be specified as a line number relative to the top
+ * of the screen, but is usually one of these special cases:
+ *     the top (first) line on the screen
+ *     the second line on the screen
+ *     the bottom line on the screen
+ *     the line after the bottom line on the screen
+ */
+       public POSITION
+position(where)
+       int where;
+{
+       switch (where)
+       {
+       case BOTTOM:
+               where = sc_height - 2;
+               break;
+       case BOTTOM_PLUS_ONE:
+               where = sc_height - 1;
+               break;
+       case MIDDLE:
+               where = (sc_height - 1) / 2;
+       }
+       return (table[where]);
+}
+
+/*
+ * Add a new file position to the bottom of the position table.
+ */
+       public void
+add_forw_pos(pos)
+       POSITION pos;
+{
+       register int i;
+
+       /*
+        * Scroll the position table up.
+        */
+       for (i = 1;  i < sc_height;  i++)
+               table[i-1] = table[i];
+       table[sc_height - 1] = pos;
+}
+
+/*
+ * Add a new file position to the top of the position table.
+ */
+       public void
+add_back_pos(pos)
+       POSITION pos;
+{
+       register int i;
+
+       /*
+        * Scroll the position table down.
+        */
+       for (i = sc_height - 1;  i > 0;  i--)
+               table[i] = table[i-1];
+       table[0] = pos;
+}
+
+/*
+ * Initialize the position table, done whenever we clear the screen.
+ */
+       public void
+pos_clear()
+{
+       register int i;
+
+       for (i = 0;  i < sc_height;  i++)
+               table[i] = NULL_POSITION;
+}
+
+/*
+ * Allocate or reallocate the position table.
+ */
+       public void
+pos_init()
+{
+       struct scrpos scrpos;
+
+       if (sc_height <= table_size)
+               return;
+       /*
+        * If we already have a table, remember the first line in it
+        * before we free it, so we can copy that line to the new table.
+        */
+       if (table != NULL)
+       {
+               get_scrpos(&scrpos);
+               free((char*)table);
+       } else
+               scrpos.pos = NULL_POSITION;
+       table = (POSITION *) ecalloc(sc_height, sizeof(POSITION));
+       table_size = sc_height;
+       pos_clear();
+       if (scrpos.pos != NULL_POSITION)
+               table[scrpos.ln-1] = scrpos.pos;
+}
+
+/*
+ * See if the byte at a specified position is currently on the screen.
+ * Check the position table to see if the position falls within its range.
+ * Return the position table entry if found, -1 if not.
+ */
+       public int
+onscreen(pos)
+       POSITION pos;
+{
+       register int i;
+
+       if (pos < table[0])
+               return (-1);
+       for (i = 1;  i < sc_height;  i++)
+               if (pos < table[i])
+                       return (i-1);
+       return (-1);
+}
+
+/*
+ * See if the entire screen is empty.
+ */
+       public int
+empty_screen()
+{
+       return (empty_lines(0, sc_height-1));
+}
+
+       public int
+empty_lines(s, e)
+       int s;
+       int e;
+{
+       register int i;
+
+       for (i = s;  i <= e;  i++)
+               if (table[i] != NULL_POSITION)
+                       return (0);
+       return (1);
+}
+
+/*
+ * Get the current screen position.
+ * The screen position consists of both a file position and
+ * a screen line number where the file position is placed on the screen.
+ * Normally the screen line number is 0, but if we are positioned
+ * such that the top few lines are empty, we may have to set
+ * the screen line to a number > 0.
+ */
+       public void
+get_scrpos(scrpos)
+       struct scrpos *scrpos;
+{
+       register int i;
+
+       /*
+        * Find the first line on the screen which has something on it,
+        * and return the screen line number and the file position.
+        */
+       for (i = 0; i < sc_height;  i++)
+               if (table[i] != NULL_POSITION)
+               {
+                       scrpos->ln = i+1;
+                       scrpos->pos = table[i];
+                       return;
+               }
+       /*
+        * The screen is empty.
+        */
+       scrpos->pos = NULL_POSITION;
+}
+
+/*
+ * Adjust a screen line number to be a simple positive integer
+ * in the range { 0 .. sc_height-2 }.
+ * (The bottom line, sc_height-1, is reserved for prompts, etc.)
+ * The given "sline" may be in the range { 1 .. sc_height-1 }
+ * to refer to lines relative to the top of the screen (starting from 1),
+ * or it may be in { -1 .. -(sc_height-1) } to refer to lines
+ * relative to the bottom of the screen.
+ */
+       public int
+adjsline(sline)
+       int sline;
+{
+       /*
+        * Negative screen line number means
+        * relative to the bottom of the screen.
+        */
+       if (sline < 0)
+               sline += sc_height;
+       /*
+        * Can't be less than 1 or greater than sc_height-1.
+        */
+       if (sline <= 0)
+               sline = 1;
+       if (sline >= sc_height)
+               sline = sc_height - 1;
+       /*
+        * Return zero-based line number, not one-based.
+        */
+       return (sline-1);
+}
diff --git a/thirdparty/less/position.h b/thirdparty/less/position.h
new file mode 100644 (file)
index 0000000..146972c
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Include file for interfacing to position.c modules.
+ */
+#define        TOP             (0)
+#define        TOP_PLUS_ONE    (1)
+#define        BOTTOM          (-1)
+#define        BOTTOM_PLUS_ONE (-2)
+#define        MIDDLE          (-3)
diff --git a/thirdparty/less/prompt.c b/thirdparty/less/prompt.c
new file mode 100644 (file)
index 0000000..dce34f3
--- /dev/null
@@ -0,0 +1,587 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Prompting and other messages.
+ * There are three flavors of prompts, SHORT, MEDIUM and LONG,
+ * selected by the -m/-M options.
+ * There is also the "equals message", printed by the = command.
+ * A prompt is a message composed of various pieces, such as the 
+ * name of the file being viewed, the percentage into the file, etc.
+ */
+
+#include "less.h"
+#include "position.h"
+
+extern int pr_type;
+extern int new_file;
+extern int sc_width;
+extern int so_s_width, so_e_width;
+extern int linenums;
+extern int hshift;
+extern int sc_height;
+extern int jump_sline;
+extern int less_is_more;
+extern IFILE curr_ifile;
+#if EDITOR
+extern char *editor;
+extern char *editproto;
+#endif
+
+/*
+ * Prototypes for the three flavors of prompts.
+ * These strings are expanded by pr_expand().
+ */
+static constant char s_proto[] =
+  "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x..%t";
+static constant char m_proto[] =
+  "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t";
+static constant char M_proto[] =
+  "?f%f .?n?m(%T %i of %m) ..?ltlines %lt-%lb?L/%L. :byte %bB?s/%s. .?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t";
+static constant char e_proto[] =
+  "?f%f .?m(%T %i of %m) .?ltlines %lt-%lb?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t";
+static constant char h_proto[] =
+  "HELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done";
+static constant char w_proto[] =
+  "Waiting for data";
+static constant char more_proto[] =
+  "--More--(?eEND ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t)";
+
+public char *prproto[3];
+public char constant *eqproto = e_proto;
+public char constant *hproto = h_proto;
+public char constant *wproto = w_proto;
+
+static char message[PROMPT_SIZE];
+static char *mp;
+
+/*
+ * Initialize the prompt prototype strings.
+ */
+       public void
+init_prompt()
+{
+       prproto[0] = save(s_proto);
+       prproto[1] = save(less_is_more ? more_proto : m_proto);
+       prproto[2] = save(M_proto);
+       eqproto = save(e_proto);
+       hproto = save(h_proto);
+       wproto = save(w_proto);
+}
+
+/*
+ * Append a string to the end of the message.
+ */
+       static void
+ap_str(s)
+       char *s;
+{
+       int len;
+
+       len = strlen(s);
+       if (mp + len >= message + PROMPT_SIZE)
+               len = message + PROMPT_SIZE - mp - 1;
+       strncpy(mp, s, len);
+       mp += len;
+       *mp = '\0';
+}
+
+/*
+ * Append a character to the end of the message.
+ */
+       static void
+ap_char(c)
+       char c;
+{
+       char buf[2];
+
+       buf[0] = c;
+       buf[1] = '\0';
+       ap_str(buf);
+}
+
+/*
+ * Append a POSITION (as a decimal integer) to the end of the message.
+ */
+       static void
+ap_pos(pos)
+       POSITION pos;
+{
+       char buf[INT_STRLEN_BOUND(pos) + 2];
+
+       postoa(pos, buf);
+       ap_str(buf);
+}
+
+/*
+ * Append a line number to the end of the message.
+ */
+       static void
+ap_linenum(linenum)
+       LINENUM linenum;
+{
+       char buf[INT_STRLEN_BOUND(linenum) + 2];
+
+       linenumtoa(linenum, buf);
+       ap_str(buf);
+}
+
+/*
+ * Append an integer to the end of the message.
+ */
+       static void
+ap_int(num)
+       int num;
+{
+       char buf[INT_STRLEN_BOUND(num) + 2];
+
+       inttoa(num, buf);
+       ap_str(buf);
+}
+
+/*
+ * Append a question mark to the end of the message.
+ */
+       static void
+ap_quest()
+{
+       ap_str("?");
+}
+
+/*
+ * Return the "current" byte offset in the file.
+ */
+       static POSITION
+curr_byte(where)
+       int where;
+{
+       POSITION pos;
+
+       pos = position(where);
+       while (pos == NULL_POSITION && where >= 0 && where < sc_height-1)
+               pos = position(++where);
+       if (pos == NULL_POSITION)
+               pos = ch_length();
+       return (pos);
+}
+
+/*
+ * Return the value of a prototype conditional.
+ * A prototype string may include conditionals which consist of a 
+ * question mark followed by a single letter.
+ * Here we decode that letter and return the appropriate boolean value.
+ */
+       static int
+cond(c, where)
+       char c;
+       int where;
+{
+       POSITION len;
+
+       switch (c)
+       {
+       case 'a':       /* Anything in the message yet? */
+               return (mp > message);
+       case 'b':       /* Current byte offset known? */
+               return (curr_byte(where) != NULL_POSITION);
+       case 'c':
+               return (hshift != 0);
+       case 'e':       /* At end of file? */
+               return (eof_displayed());
+       case 'f':       /* Filename known? */
+               return (strcmp(get_filename(curr_ifile), "-") != 0);
+       case 'l':       /* Line number known? */
+       case 'd':       /* Same as l */
+               return (linenums);
+       case 'L':       /* Final line number known? */
+       case 'D':       /* Final page number known? */
+               return (linenums && ch_length() != NULL_POSITION);
+       case 'm':       /* More than one file? */
+#if TAGS
+               return (ntags() ? (ntags() > 1) : (nifile() > 1));
+#else
+               return (nifile() > 1);
+#endif
+       case 'n':       /* First prompt in a new file? */
+#if TAGS
+               return (ntags() ? 1 : new_file);
+#else
+               return (new_file);
+#endif
+       case 'p':       /* Percent into file (bytes) known? */
+               return (curr_byte(where) != NULL_POSITION && 
+                               ch_length() > 0);
+       case 'P':       /* Percent into file (lines) known? */
+               return (currline(where) != 0 &&
+                               (len = ch_length()) > 0 &&
+                               find_linenum(len) != 0);
+       case 's':       /* Size of file known? */
+       case 'B':
+               return (ch_length() != NULL_POSITION);
+       case 'x':       /* Is there a "next" file? */
+#if TAGS
+               if (ntags())
+                       return (0);
+#endif
+               return (next_ifile(curr_ifile) != NULL_IFILE);
+       }
+       return (0);
+}
+
+/*
+ * Decode a "percent" prototype character.
+ * A prototype string may include various "percent" escapes;
+ * that is, a percent sign followed by a single letter.
+ * Here we decode that letter and take the appropriate action,
+ * usually by appending something to the message being built.
+ */
+       static void
+protochar(c, where, iseditproto)
+       int c;
+       int where;
+       int iseditproto;
+{
+       POSITION pos;
+       POSITION len;
+       int n;
+       LINENUM linenum;
+       LINENUM last_linenum;
+       IFILE h;
+
+#undef  PAGE_NUM
+#define PAGE_NUM(linenum)  ((((linenum) - 1) / (sc_height - 1)) + 1)
+
+       switch (c)
+       {
+       case 'b':       /* Current byte offset */
+               pos = curr_byte(where);
+               if (pos != NULL_POSITION)
+                       ap_pos(pos);
+               else
+                       ap_quest();
+               break;
+       case 'c':
+               ap_int(hshift);
+               break;
+       case 'd':       /* Current page number */
+               linenum = currline(where);
+               if (linenum > 0 && sc_height > 1)
+                       ap_linenum(PAGE_NUM(linenum));
+               else
+                       ap_quest();
+               break;
+       case 'D':       /* Final page number */
+               /* Find the page number of the last byte in the file (len-1). */
+               len = ch_length();
+               if (len == NULL_POSITION)
+                       ap_quest();
+               else if (len == 0)
+                       /* An empty file has no pages. */
+                       ap_linenum(0);
+               else
+               {
+                       linenum = find_linenum(len - 1);
+                       if (linenum <= 0)
+                               ap_quest();
+                       else 
+                               ap_linenum(PAGE_NUM(linenum));
+               }
+               break;
+#if EDITOR
+       case 'E':       /* Editor name */
+               ap_str(editor);
+               break;
+#endif
+       case 'f':       /* File name */
+               ap_str(get_filename(curr_ifile));
+               break;
+       case 'F':       /* Last component of file name */
+               ap_str(last_component(get_filename(curr_ifile)));
+               break;
+       case 'i':       /* Index into list of files */
+#if TAGS
+               if (ntags())
+                       ap_int(curr_tag());
+               else
+#endif
+                       ap_int(get_index(curr_ifile));
+               break;
+       case 'l':       /* Current line number */
+               linenum = currline(where);
+               if (linenum != 0)
+                       ap_linenum(linenum);
+               else
+                       ap_quest();
+               break;
+       case 'L':       /* Final line number */
+               len = ch_length();
+               if (len == NULL_POSITION || len == ch_zero() ||
+                   (linenum = find_linenum(len)) <= 0)
+                       ap_quest();
+               else
+                       ap_linenum(linenum-1);
+               break;
+       case 'm':       /* Number of files */
+#if TAGS
+               n = ntags();
+               if (n)
+                       ap_int(n);
+               else
+#endif
+                       ap_int(nifile());
+               break;
+       case 'p':       /* Percent into file (bytes) */
+               pos = curr_byte(where);
+               len = ch_length();
+               if (pos != NULL_POSITION && len > 0)
+                       ap_int(percentage(pos,len));
+               else
+                       ap_quest();
+               break;
+       case 'P':       /* Percent into file (lines) */
+               linenum = currline(where);
+               if (linenum == 0 ||
+                   (len = ch_length()) == NULL_POSITION || len == ch_zero() ||
+                   (last_linenum = find_linenum(len)) <= 0)
+                       ap_quest();
+               else
+                       ap_int(percentage(linenum, last_linenum));
+               break;
+       case 's':       /* Size of file */
+       case 'B':
+               len = ch_length();
+               if (len != NULL_POSITION)
+                       ap_pos(len);
+               else
+                       ap_quest();
+               break;
+       case 't':       /* Truncate trailing spaces in the message */
+               while (mp > message && mp[-1] == ' ')
+                       mp--;
+               *mp = '\0';
+               break;
+       case 'T':       /* Type of list */
+#if TAGS
+               if (ntags())
+                       ap_str("tag");
+               else
+#endif
+                       ap_str("file");
+               break;
+       case 'x':       /* Name of next file */
+               h = next_ifile(curr_ifile);
+               if (h != NULL_IFILE)
+                       ap_str(get_filename(h));
+               else
+                       ap_quest();
+               break;
+       }
+}
+
+/*
+ * Skip a false conditional.
+ * When a false condition is found (either a false IF or the ELSE part 
+ * of a true IF), this routine scans the prototype string to decide
+ * where to resume parsing the string.
+ * We must keep track of nested IFs and skip them properly.
+ */
+       static char *
+skipcond(p)
+       register char *p;
+{
+       register int iflevel;
+
+       /*
+        * We came in here after processing a ? or :,
+        * so we start nested one level deep.
+        */
+       iflevel = 1;
+
+       for (;;) switch (*++p)
+       {
+       case '?':
+               /*
+                * Start of a nested IF.
+                */
+               iflevel++;
+               break;
+       case ':':
+               /*
+                * Else.
+                * If this matches the IF we came in here with,
+                * then we're done.
+                */
+               if (iflevel == 1)
+                       return (p);
+               break;
+       case '.':
+               /*
+                * Endif.
+                * If this matches the IF we came in here with,
+                * then we're done.
+                */
+               if (--iflevel == 0)
+                       return (p);
+               break;
+       case '\\':
+               /*
+                * Backslash escapes the next character.
+                */
+               ++p;
+               break;
+       case '\0':
+               /*
+                * Whoops.  Hit end of string.
+                * This is a malformed conditional, but just treat it
+                * as if all active conditionals ends here.
+                */
+               return (p-1);
+       }
+       /*NOTREACHED*/
+}
+
+/*
+ * Decode a char that represents a position on the screen.
+ */
+       static char *
+wherechar(p, wp)
+       char *p;
+       int *wp;
+{
+       switch (*p)
+       {
+       case 'b': case 'd': case 'l': case 'p': case 'P':
+               switch (*++p)
+               {
+               case 't':   *wp = TOP;                  break;
+               case 'm':   *wp = MIDDLE;               break;
+               case 'b':   *wp = BOTTOM;               break;
+               case 'B':   *wp = BOTTOM_PLUS_ONE;      break;
+               case 'j':   *wp = adjsline(jump_sline); break;
+               default:    *wp = TOP;  p--;            break;
+               }
+       }
+       return (p);
+}
+
+/*
+ * Construct a message based on a prototype string.
+ */
+       public char *
+pr_expand(proto, maxwidth)
+       char *proto;
+       int maxwidth;
+{
+       register char *p;
+       register int c;
+       int where;
+
+       mp = message;
+
+       if (*proto == '\0')
+               return ("");
+
+       for (p = proto;  *p != '\0';  p++)
+       {
+               switch (*p)
+               {
+               default:        /* Just put the character in the message */
+                       ap_char(*p);
+                       break;
+               case '\\':      /* Backslash escapes the next character */
+                       p++;
+                       ap_char(*p);
+                       break;
+               case '?':       /* Conditional (IF) */
+                       if ((c = *++p) == '\0')
+                               --p;
+                       else
+                       {
+                               where = 0;
+                               p = wherechar(p, &where);
+                               if (!cond(c, where))
+                                       p = skipcond(p);
+                       }
+                       break;
+               case ':':       /* ELSE */
+                       p = skipcond(p);
+                       break;
+               case '.':       /* ENDIF */
+                       break;
+               case '%':       /* Percent escape */
+                       if ((c = *++p) == '\0')
+                               --p;
+                       else
+                       {
+                               where = 0;
+                               p = wherechar(p, &where);
+                               protochar(c, where,
+#if EDITOR
+                                       (proto == editproto));
+#else
+                                       0);
+#endif
+
+                       }
+                       break;
+               }
+       }
+
+       if (mp == message)
+               return ("");
+       if (maxwidth > 0 && mp >= message + maxwidth)
+       {
+               /*
+                * Message is too long.
+                * Return just the final portion of it.
+                */
+               return (mp - maxwidth);
+       }
+       return (message);
+}
+
+/*
+ * Return a message suitable for printing by the "=" command.
+ */
+       public char *
+eq_message()
+{
+       return (pr_expand(eqproto, 0));
+}
+
+/*
+ * Return a prompt.
+ * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
+ * If we can't come up with an appropriate prompt, return NULL
+ * and the caller will prompt with a colon.
+ */
+       public char *
+pr_string()
+{
+       char *prompt;
+       int type;
+
+       type = (!less_is_more) ? pr_type : pr_type ? 0 : 1;
+       prompt = pr_expand((ch_getflags() & CH_HELPFILE) ?
+                               hproto : prproto[type],
+                       sc_width-so_s_width-so_e_width-2);
+       new_file = 0;
+       return (prompt);
+}
+
+/*
+ * Return a message suitable for printing while waiting in the F command.
+ */
+       public char *
+wait_message()
+{
+       return (pr_expand(wproto, sc_width-so_s_width-so_e_width-2));
+}
diff --git a/thirdparty/less/regexp.c b/thirdparty/less/regexp.c
new file mode 100644 (file)
index 0000000..77ab611
--- /dev/null
@@ -0,0 +1,1250 @@
+/*
+ * regcomp and regexec -- regsub and regerror are elsewhere
+ *
+ *     Copyright (c) 1986 by University of Toronto.
+ *     Written by Henry Spencer.  Not derived from licensed software.
+ *
+ *     Permission is granted to anyone to use this software for any
+ *     purpose on any computer system, and to redistribute it freely,
+ *     subject to the following restrictions:
+ *
+ *     1. The author is not responsible for the consequences of use of
+ *             this software, no matter how awful, even if they arise
+ *             from defects in it.
+ *
+ *     2. The origin of this software must not be misrepresented, either
+ *             by explicit claim or by omission.
+ *
+ *     3. Altered versions must be plainly marked as such, and must not
+ *             be misrepresented as being the original software.
+ *
+ * Beware that some of this code is subtly aware of the way operator
+ * precedence is structured in regular expressions.  Serious changes in
+ * regular-expression syntax might require a total rethink.
+ *
+ * *** NOTE: this code has been altered slightly for use in Tcl. ***
+ * Slightly modified by David MacKenzie to undo most of the changes for TCL.
+ * Added regexec2 with notbol parameter. -- 4/19/99 Mark Nudelman
+ */
+
+#include "less.h"
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#include "regexp.h"
+
+/*
+ * The "internal use only" fields in regexp.h are present to pass info from
+ * compile to execute that permits the execute phase to run lots faster on
+ * simple cases.  They are:
+ *
+ * regstart    char that must begin a match; '\0' if none obvious
+ * reganch     is the match anchored (at beginning-of-line only)?
+ * regmust     string (pointer into program) that match must include, or NULL
+ * regmlen     length of regmust string
+ *
+ * Regstart and reganch permit very fast decisions on suitable starting points
+ * for a match, cutting down the work a lot.  Regmust permits fast rejection
+ * of lines that cannot possibly match.  The regmust tests are costly enough
+ * that regcomp() supplies a regmust only if the r.e. contains something
+ * potentially expensive (at present, the only such thing detected is * or +
+ * at the start of the r.e., which can involve a lot of backup).  Regmlen is
+ * supplied because the test in regexec() needs it and regcomp() is
+ * computing it anyway.
+ */
+
+/*
+ * Structure for regexp "program".  This is essentially a linear encoding
+ * of a nondeterministic finite-state machine (aka syntax charts or
+ * "railroad normal form" in parsing technology).  Each node is an opcode
+ * plus a "next" pointer, possibly plus an operand.  "Next" pointers of
+ * all nodes except BRANCH implement concatenation; a "next" pointer with
+ * a BRANCH on both ends of it is connecting two alternatives.  (Here we
+ * have one of the subtle syntax dependencies:  an individual BRANCH (as
+ * opposed to a collection of them) is never concatenated with anything
+ * because of operator precedence.)  The operand of some types of node is
+ * a literal string; for others, it is a node leading into a sub-FSM.  In
+ * particular, the operand of a BRANCH node is the first node of the branch.
+ * (NB this is *not* a tree structure:  the tail of the branch connects
+ * to the thing following the set of BRANCHes.)  The opcodes are:
+ */
+
+/* definition  number  opnd?   meaning */
+#undef EOL
+#define        END     0       /* no   End of program. */
+#define        BOL     1       /* no   Match "" at beginning of line. */
+#define        EOL     2       /* no   Match "" at end of line. */
+#define        ANY     3       /* no   Match any one character. */
+#define        ANYOF   4       /* str  Match any character in this string. */
+#define        ANYBUT  5       /* str  Match any character not in this string. */
+#define        BRANCH  6       /* node Match this alternative, or the next... */
+#define        BACK    7       /* no   Match "", "next" ptr points backward. */
+#define        EXACTLY 8       /* str  Match this string. */
+#define        NOTHING 9       /* no   Match empty string. */
+#define        STAR    10      /* node Match this (simple) thing 0 or more times. */
+#define        PLUS    11      /* node Match this (simple) thing 1 or more times. */
+#define        OPEN    20      /* no   Mark this point in input as start of #n. */
+                       /*      OPEN+1 is number 1, etc. */
+#define        CLOSE   30      /* no   Analogous to OPEN. */
+
+/*
+ * Opcode notes:
+ *
+ * BRANCH      The set of branches constituting a single choice are hooked
+ *             together with their "next" pointers, since precedence prevents
+ *             anything being concatenated to any individual branch.  The
+ *             "next" pointer of the last BRANCH in a choice points to the
+ *             thing following the whole choice.  This is also where the
+ *             final "next" pointer of each individual branch points; each
+ *             branch starts with the operand node of a BRANCH node.
+ *
+ * BACK                Normal "next" pointers all implicitly point forward; BACK
+ *             exists to make loop structures possible.
+ *
+ * STAR,PLUS   '?', and complex '*' and '+', are implemented as circular
+ *             BRANCH structures using BACK.  Simple cases (one character
+ *             per match) are implemented with STAR and PLUS for speed
+ *             and to minimize recursive plunges.
+ *
+ * OPEN,CLOSE  ...are numbered at compile time.
+ */
+
+/*
+ * A node is one char of opcode followed by two chars of "next" pointer.
+ * "Next" pointers are stored as two 8-bit pieces, high order first.  The
+ * value is a positive offset from the opcode of the node containing it.
+ * An operand, if any, simply follows the node.  (Note that much of the
+ * code generation knows about this implicit relationship.)
+ *
+ * Using two bytes for the "next" pointer is vast overkill for most things,
+ * but allows patterns to get big without disasters.
+ */
+#define        OP(p)   (*(p))
+#define        NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
+#define        OPERAND(p)      ((p) + 3)
+
+/*
+ * See regmagic.h for one further detail of program structure.
+ */
+
+
+/*
+ * Utility definitions.
+ */
+#ifndef CHARBITS
+#define        UCHARAT(p)      ((int)*(unsigned char *)(p))
+#else
+#define        UCHARAT(p)      ((int)*(p)&CHARBITS)
+#endif
+
+#define        FAIL(m) { regerror(m); return(NULL); }
+#define        ISMULT(c)       ((c) == '*' || (c) == '+' || (c) == '?')
+#define        META    "^$.[()|?+*\\"
+
+/*
+ * Flags to be passed up and down.
+ */
+#define        HASWIDTH        01      /* Known never to match null string. */
+#define        SIMPLE          02      /* Simple enough to be STAR/PLUS operand. */
+#define        SPSTART         04      /* Starts with * or +. */
+#define        WORST           0       /* Worst case. */
+
+/*
+ * Global work variables for regcomp().
+ */
+static char *regparse;         /* Input-scan pointer. */
+static int regnpar;            /* () count. */
+static char regdummy;
+static char *regcode;          /* Code-emit pointer; &regdummy = don't. */
+static long regsize;           /* Code size. */
+
+/*
+ * The first byte of the regexp internal "program" is actually this magic
+ * number; the start node begins in the second byte.
+ */
+#define        MAGIC   0234
+
+
+/*
+ * Forward declarations for regcomp()'s friends.
+ */
+#ifndef STATIC
+#define        STATIC  static
+#endif
+STATIC char *reg();
+STATIC char *regbranch();
+STATIC char *regpiece();
+STATIC char *regatom();
+STATIC char *regnode();
+STATIC char *regnext();
+STATIC void regc();
+STATIC void reginsert();
+STATIC void regtail();
+STATIC void regoptail();
+#ifdef STRCSPN
+STATIC int strcspn();
+#endif
+
+/*
+ - regcomp - compile a regular expression into internal code
+ *
+ * We can't allocate space until we know how big the compiled form will be,
+ * but we can't compile it (and thus know how big it is) until we've got a
+ * place to put the code.  So we cheat:  we compile it twice, once with code
+ * generation turned off and size counting turned on, and once "for real".
+ * This also means that we don't allocate space until we are sure that the
+ * thing really will compile successfully, and we never have to move the
+ * code and thus invalidate pointers into it.  (Note that it has to be in
+ * one piece because free() must be able to free it all.)
+ *
+ * Beware that the optimization-preparation code in here knows about some
+ * of the structure of the compiled regexp.
+ */
+regexp *
+regcomp(exp)
+char *exp;
+{
+       register regexp *r;
+       register char *scan;
+       register char *longest;
+       register int len;
+       int flags;
+
+       if (exp == NULL)
+               FAIL("NULL argument");
+
+       /* First pass: determine size, legality. */
+       regparse = exp;
+       regnpar = 1;
+       regsize = 0L;
+       regcode = &regdummy;
+       regc(MAGIC);
+       if (reg(0, &flags) == NULL)
+               return(NULL);
+
+       /* Small enough for pointer-storage convention? */
+       if (regsize >= 32767L)          /* Probably could be 65535L. */
+               FAIL("regexp too big");
+
+       /* Allocate space. */
+       r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize);
+       if (r == NULL)
+               FAIL("out of space");
+
+       /* Second pass: emit code. */
+       regparse = exp;
+       regnpar = 1;
+       regcode = r->program;
+       regc(MAGIC);
+       if (reg(0, &flags) == NULL)
+               return(NULL);
+
+       /* Dig out information for optimizations. */
+       r->regstart = '\0';     /* Worst-case defaults. */
+       r->reganch = 0;
+       r->regmust = NULL;
+       r->regmlen = 0;
+       scan = r->program+1;                    /* First BRANCH. */
+       if (OP(regnext(scan)) == END) {         /* Only one top-level choice. */
+               scan = OPERAND(scan);
+
+               /* Starting-point info. */
+               if (OP(scan) == EXACTLY)
+                       r->regstart = *OPERAND(scan);
+               else if (OP(scan) == BOL)
+                       r->reganch++;
+
+               /*
+                * If there's something expensive in the r.e., find the
+                * longest literal string that must appear and make it the
+                * regmust.  Resolve ties in favor of later strings, since
+                * the regstart check works with the beginning of the r.e.
+                * and avoiding duplication strengthens checking.  Not a
+                * strong reason, but sufficient in the absence of others.
+                */
+               if (flags&SPSTART) {
+                       longest = NULL;
+                       len = 0;
+                       for (; scan != NULL; scan = regnext(scan))
+                               if (OP(scan) == EXACTLY && ((int) strlen(OPERAND(scan))) >= len) {
+                                       longest = OPERAND(scan);
+                                       len = strlen(OPERAND(scan));
+                               }
+                       r->regmust = longest;
+                       r->regmlen = len;
+               }
+       }
+
+       return(r);
+}
+
+/*
+ - reg - regular expression, i.e. main body or parenthesized thing
+ *
+ * Caller must absorb opening parenthesis.
+ *
+ * Combining parenthesis handling with the base level of regular expression
+ * is a trifle forced, but the need to tie the tails of the branches to what
+ * follows makes it hard to avoid.
+ */
+static char *
+reg(paren, flagp)
+int paren;                     /* Parenthesized? */
+int *flagp;
+{
+       register char *ret;
+       register char *br;
+       register char *ender;
+       register int parno = 0;
+       int flags;
+
+       *flagp = HASWIDTH;      /* Tentatively. */
+
+       /* Make an OPEN node, if parenthesized. */
+       if (paren) {
+               if (regnpar >= NSUBEXP)
+                       FAIL("too many ()");
+               parno = regnpar;
+               regnpar++;
+               ret = regnode(OPEN+parno);
+       } else
+               ret = NULL;
+
+       /* Pick up the branches, linking them together. */
+       br = regbranch(&flags);
+       if (br == NULL)
+               return(NULL);
+       if (ret != NULL)
+               regtail(ret, br);       /* OPEN -> first. */
+       else
+               ret = br;
+       if (!(flags&HASWIDTH))
+               *flagp &= ~HASWIDTH;
+       *flagp |= flags&SPSTART;
+       while (*regparse == '|') {
+               regparse++;
+               br = regbranch(&flags);
+               if (br == NULL)
+                       return(NULL);
+               regtail(ret, br);       /* BRANCH -> BRANCH. */
+               if (!(flags&HASWIDTH))
+                       *flagp &= ~HASWIDTH;
+               *flagp |= flags&SPSTART;
+       }
+
+       /* Make a closing node, and hook it on the end. */
+       ender = regnode((paren) ? CLOSE+parno : END);   
+       regtail(ret, ender);
+
+       /* Hook the tails of the branches to the closing node. */
+       for (br = ret; br != NULL; br = regnext(br))
+               regoptail(br, ender);
+
+       /* Check for proper termination. */
+       if (paren && *regparse++ != ')') {
+               FAIL("unmatched ()");
+       } else if (!paren && *regparse != '\0') {
+               if (*regparse == ')') {
+                       FAIL("unmatched ()");
+               } else
+                       FAIL("junk on end");    /* "Can't happen". */
+               /* NOTREACHED */
+       }
+
+       return(ret);
+}
+
+/*
+ - regbranch - one alternative of an | operator
+ *
+ * Implements the concatenation operator.
+ */
+static char *
+regbranch(flagp)
+int *flagp;
+{
+       register char *ret;
+       register char *chain;
+       register char *latest;
+       int flags;
+
+       *flagp = WORST;         /* Tentatively. */
+
+       ret = regnode(BRANCH);
+       chain = NULL;
+       while (*regparse != '\0' && *regparse != '|' && *regparse != ')') {
+               latest = regpiece(&flags);
+               if (latest == NULL)
+                       return(NULL);
+               *flagp |= flags&HASWIDTH;
+               if (chain == NULL)      /* First piece. */
+                       *flagp |= flags&SPSTART;
+               else
+                       regtail(chain, latest);
+               chain = latest;
+       }
+       if (chain == NULL)      /* Loop ran zero times. */
+               (void) regnode(NOTHING);
+
+       return(ret);
+}
+
+/*
+ - regpiece - something followed by possible [*+?]
+ *
+ * Note that the branching code sequences used for ? and the general cases
+ * of * and + are somewhat optimized:  they use the same NOTHING node as
+ * both the endmarker for their branch list and the body of the last branch.
+ * It might seem that this node could be dispensed with entirely, but the
+ * endmarker role is not redundant.
+ */
+static char *
+regpiece(flagp)
+int *flagp;
+{
+       register char *ret;
+       register char op;
+       register char *next;
+       int flags;
+
+       ret = regatom(&flags);
+       if (ret == NULL)
+               return(NULL);
+
+       op = *regparse;
+       if (!ISMULT(op)) {
+               *flagp = flags;
+               return(ret);
+       }
+
+       if (!(flags&HASWIDTH) && op != '?')
+               FAIL("*+ operand could be empty");
+       *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
+
+       if (op == '*' && (flags&SIMPLE))
+               reginsert(STAR, ret);
+       else if (op == '*') {
+               /* Emit x* as (x&|), where & means "self". */
+               reginsert(BRANCH, ret);                 /* Either x */
+               regoptail(ret, regnode(BACK));          /* and loop */
+               regoptail(ret, ret);                    /* back */
+               regtail(ret, regnode(BRANCH));          /* or */
+               regtail(ret, regnode(NOTHING));         /* null. */
+       } else if (op == '+' && (flags&SIMPLE))
+               reginsert(PLUS, ret);
+       else if (op == '+') {
+               /* Emit x+ as x(&|), where & means "self". */
+               next = regnode(BRANCH);                 /* Either */
+               regtail(ret, next);
+               regtail(regnode(BACK), ret);            /* loop back */
+               regtail(next, regnode(BRANCH));         /* or */
+               regtail(ret, regnode(NOTHING));         /* null. */
+       } else if (op == '?') {
+               /* Emit x? as (x|) */
+               reginsert(BRANCH, ret);                 /* Either x */
+               regtail(ret, regnode(BRANCH));          /* or */
+               next = regnode(NOTHING);                /* null. */
+               regtail(ret, next);
+               regoptail(ret, next);
+       }
+       regparse++;
+       if (ISMULT(*regparse))
+               FAIL("nested *?+");
+
+       return(ret);
+}
+
+/*
+ - regatom - the lowest level
+ *
+ * Optimization:  gobbles an entire sequence of ordinary characters so that
+ * it can turn them into a single node, which is smaller to store and
+ * faster to run.  Backslashed characters are exceptions, each becoming a
+ * separate node; the code is simpler that way and it's not worth fixing.
+ */
+static char *
+regatom(flagp)
+int *flagp;
+{
+       register char *ret;
+       int flags;
+
+       *flagp = WORST;         /* Tentatively. */
+
+       switch (*regparse++) {
+       case '^':
+               ret = regnode(BOL);
+               break;
+       case '$':
+               ret = regnode(EOL);
+               break;
+       case '.':
+               ret = regnode(ANY);
+               *flagp |= HASWIDTH|SIMPLE;
+               break;
+       case '[': {
+                       register int clss;
+                       register int classend;
+
+                       if (*regparse == '^') { /* Complement of range. */
+                               ret = regnode(ANYBUT);
+                               regparse++;
+                       } else
+                               ret = regnode(ANYOF);
+                       if (*regparse == ']' || *regparse == '-')
+                               regc(*regparse++);
+                       while (*regparse != '\0' && *regparse != ']') {
+                               if (*regparse == '-') {
+                                       regparse++;
+                                       if (*regparse == ']' || *regparse == '\0')
+                                               regc('-');
+                                       else {
+                                               clss = UCHARAT(regparse-2)+1;
+                                               classend = UCHARAT(regparse);
+                                               if (clss > classend+1)
+                                                       FAIL("invalid [] range");
+                                               for (; clss <= classend; clss++)
+                                                       regc(clss);
+                                               regparse++;
+                                       }
+                               } else
+                                       regc(*regparse++);
+                       }
+                       regc('\0');
+                       if (*regparse != ']')
+                               FAIL("unmatched []");
+                       regparse++;
+                       *flagp |= HASWIDTH|SIMPLE;
+               }
+               break;
+       case '(':
+               ret = reg(1, &flags);
+               if (ret == NULL)
+                       return(NULL);
+               *flagp |= flags&(HASWIDTH|SPSTART);
+               break;
+       case '\0':
+       case '|':
+       case ')':
+               FAIL("internal urp");   /* Supposed to be caught earlier. */
+               /* NOTREACHED */
+               break;
+       case '?':
+       case '+':
+       case '*':
+               FAIL("?+* follows nothing");
+               /* NOTREACHED */
+               break;
+       case '\\':
+               if (*regparse == '\0')
+                       FAIL("trailing \\");
+               ret = regnode(EXACTLY);
+               regc(*regparse++);
+               regc('\0');
+               *flagp |= HASWIDTH|SIMPLE;
+               break;
+       default: {
+                       register int len;
+                       register char ender;
+
+                       regparse--;
+                       len = strcspn(regparse, META);
+                       if (len <= 0)
+                               FAIL("internal disaster");
+                       ender = *(regparse+len);
+                       if (len > 1 && ISMULT(ender))
+                               len--;          /* Back off clear of ?+* operand. */
+                       *flagp |= HASWIDTH;
+                       if (len == 1)
+                               *flagp |= SIMPLE;
+                       ret = regnode(EXACTLY);
+                       while (len > 0) {
+                               regc(*regparse++);
+                               len--;
+                       }
+                       regc('\0');
+               }
+               break;
+       }
+
+       return(ret);
+}
+
+/*
+ - regnode - emit a node
+ */
+static char *                  /* Location. */
+regnode(op)
+char op;
+{
+       register char *ret;
+       register char *ptr;
+
+       ret = regcode;
+       if (ret == &regdummy) {
+               regsize += 3;
+               return(ret);
+       }
+
+       ptr = ret;
+       *ptr++ = op;
+       *ptr++ = '\0';          /* Null "next" pointer. */
+       *ptr++ = '\0';
+       regcode = ptr;
+
+       return(ret);
+}
+
+/*
+ - regc - emit (if appropriate) a byte of code
+ */
+static void
+regc(b)
+char b;
+{
+       if (regcode != &regdummy)
+               *regcode++ = b;
+       else
+               regsize++;
+}
+
+/*
+ - reginsert - insert an operator in front of already-emitted operand
+ *
+ * Means relocating the operand.
+ */
+static void
+reginsert(op, opnd)
+char op;
+char *opnd;
+{
+       register char *src;
+       register char *dst;
+       register char *place;
+
+       if (regcode == &regdummy) {
+               regsize += 3;
+               return;
+       }
+
+       src = regcode;
+       regcode += 3;
+       dst = regcode;
+       while (src > opnd)
+               *--dst = *--src;
+
+       place = opnd;           /* Op node, where operand used to be. */
+       *place++ = op;
+       *place++ = '\0';
+       *place++ = '\0';
+}
+
+/*
+ - regtail - set the next-pointer at the end of a node chain
+ */
+static void
+regtail(p, val)
+char *p;
+char *val;
+{
+       register char *scan;
+       register char *temp;
+       register int offset;
+
+       if (p == &regdummy)
+               return;
+
+       /* Find last node. */
+       scan = p;
+       for (;;) {
+               temp = regnext(scan);
+               if (temp == NULL)
+                       break;
+               scan = temp;
+       }
+
+       if (OP(scan) == BACK)
+               offset = scan - val;
+       else
+               offset = val - scan;
+       *(scan+1) = (offset>>8)&0377;
+       *(scan+2) = offset&0377;
+}
+
+/*
+ - regoptail - regtail on operand of first argument; nop if operandless
+ */
+static void
+regoptail(p, val)
+char *p;
+char *val;
+{
+       /* "Operandless" and "op != BRANCH" are synonymous in practice. */
+       if (p == NULL || p == &regdummy || OP(p) != BRANCH)
+               return;
+       regtail(OPERAND(p), val);
+}
+
+/*
+ * regexec and friends
+ */
+
+/*
+ * Global work variables for regexec().
+ */
+static char *reginput;         /* String-input pointer. */
+static char *regbol;           /* Beginning of input, for ^ check. */
+static char **regstartp;       /* Pointer to startp array. */
+static char **regendp;         /* Ditto for endp. */
+
+/*
+ * Forwards.
+ */
+STATIC int regtry();
+STATIC int regmatch();
+STATIC int regrepeat();
+
+#ifdef DEBUG
+int regnarrate = 0;
+void regdump();
+STATIC char *regprop();
+#endif
+
+/*
+ - regexec - match a regexp against a string
+ */
+int
+regexec2(prog, string, notbol)
+register regexp *prog;
+register char *string;
+int notbol;
+{
+       register char *s;
+
+       /* Be paranoid... */
+       if (prog == NULL || string == NULL) {
+               regerror("NULL parameter");
+               return(0);
+       }
+
+       /* Check validity of program. */
+       if (UCHARAT(prog->program) != MAGIC) {
+               regerror("corrupted program");
+               return(0);
+       }
+
+       /* If there is a "must appear" string, look for it. */
+       if (prog->regmust != NULL) {
+               s = string;
+               while ((s = strchr(s, prog->regmust[0])) != NULL) {
+                       if (strncmp(s, prog->regmust, prog->regmlen) == 0)
+                               break;  /* Found it. */
+                       s++;
+               }
+               if (s == NULL)  /* Not present. */
+                       return(0);
+       }
+
+       /* Mark beginning of line for ^ . */
+       if (notbol)
+               regbol = NULL;
+       else
+               regbol = string;
+
+       /* Simplest case:  anchored match need be tried only once. */
+       if (prog->reganch)
+               return(regtry(prog, string));
+
+       /* Messy cases:  unanchored match. */
+       s = string;
+       if (prog->regstart != '\0')
+               /* We know what char it must start with. */
+               while ((s = strchr(s, prog->regstart)) != NULL) {
+                       if (regtry(prog, s))
+                               return(1);
+                       s++;
+               }
+       else
+               /* We don't -- general case. */
+               do {
+                       if (regtry(prog, s))
+                               return(1);
+               } while (*s++ != '\0');
+
+       /* Failure. */
+       return(0);
+}
+
+int
+regexec(prog, string)
+register regexp *prog;
+register char *string;
+{
+       return regexec2(prog, string, 0);
+}
+
+/*
+ - regtry - try match at specific point
+ */
+static int                     /* 0 failure, 1 success */
+regtry(prog, string)
+regexp *prog;
+char *string;
+{
+       register int i;
+       register char **sp;
+       register char **ep;
+
+       reginput = string;
+       regstartp = prog->startp;
+       regendp = prog->endp;
+
+       sp = prog->startp;
+       ep = prog->endp;
+       for (i = NSUBEXP; i > 0; i--) {
+               *sp++ = NULL;
+               *ep++ = NULL;
+       }
+       if (regmatch(prog->program + 1)) {
+               prog->startp[0] = string;
+               prog->endp[0] = reginput;
+               return(1);
+       } else
+               return(0);
+}
+
+/*
+ - regmatch - main matching routine
+ *
+ * Conceptually the strategy is simple:  check to see whether the current
+ * node matches, call self recursively to see whether the rest matches,
+ * and then act accordingly.  In practice we make some effort to avoid
+ * recursion, in particular by going through "ordinary" nodes (that don't
+ * need to know whether the rest of the match failed) by a loop instead of
+ * by recursion.
+ */
+static int                     /* 0 failure, 1 success */
+regmatch(prog)
+char *prog;
+{
+       register char *scan;    /* Current node. */
+       char *next;             /* Next node. */
+
+       scan = prog;
+#ifdef DEBUG
+       if (scan != NULL && regnarrate)
+               fprintf(stderr, "%s(\n", regprop(scan));
+#endif
+       while (scan != NULL) {
+#ifdef DEBUG
+               if (regnarrate)
+                       fprintf(stderr, "%s...\n", regprop(scan));
+#endif
+               next = regnext(scan);
+
+               switch (OP(scan)) {
+               case BOL:
+                       if (reginput != regbol)
+                               return(0);
+                       break;
+               case EOL:
+                       if (*reginput != '\0')
+                               return(0);
+                       break;
+               case ANY:
+                       if (*reginput == '\0')
+                               return(0);
+                       reginput++;
+                       break;
+               case EXACTLY: {
+                               register int len;
+                               register char *opnd;
+
+                               opnd = OPERAND(scan);
+                               /* Inline the first character, for speed. */
+                               if (*opnd != *reginput)
+                                       return(0);
+                               len = strlen(opnd);
+                               if (len > 1 && strncmp(opnd, reginput, len) != 0)
+                                       return(0);
+                               reginput += len;
+                       }
+                       break;
+               case ANYOF:
+                       if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL)
+                               return(0);
+                       reginput++;
+                       break;
+               case ANYBUT:
+                       if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL)
+                               return(0);
+                       reginput++;
+                       break;
+               case NOTHING:
+                       break;
+               case BACK:
+                       break;
+               case OPEN+1:
+               case OPEN+2:
+               case OPEN+3:
+               case OPEN+4:
+               case OPEN+5:
+               case OPEN+6:
+               case OPEN+7:
+               case OPEN+8:
+               case OPEN+9: {
+                               register int no;
+                               register char *save;
+
+                               no = OP(scan) - OPEN;
+                               save = reginput;
+
+                               if (regmatch(next)) {
+                                       /*
+                                        * Don't set startp if some later
+                                        * invocation of the same parentheses
+                                        * already has.
+                                        */
+                                       if (regstartp[no] == NULL)
+                                               regstartp[no] = save;
+                                       return(1);
+                               } else
+                                       return(0);
+                       }
+                       /* NOTREACHED */
+                       break;
+               case CLOSE+1:
+               case CLOSE+2:
+               case CLOSE+3:
+               case CLOSE+4:
+               case CLOSE+5:
+               case CLOSE+6:
+               case CLOSE+7:
+               case CLOSE+8:
+               case CLOSE+9: {
+                               register int no;
+                               register char *save;
+
+                               no = OP(scan) - CLOSE;
+                               save = reginput;
+
+                               if (regmatch(next)) {
+                                       /*
+                                        * Don't set endp if some later
+                                        * invocation of the same parentheses
+                                        * already has.
+                                        */
+                                       if (regendp[no] == NULL)
+                                               regendp[no] = save;
+                                       return(1);
+                               } else
+                                       return(0);
+                       }
+                       /* NOTREACHED */
+                       break;
+               case BRANCH: {
+                               register char *save;
+
+                               if (OP(next) != BRANCH)         /* No choice. */
+                                       next = OPERAND(scan);   /* Avoid recursion. */
+                               else {
+                                       do {
+                                               save = reginput;
+                                               if (regmatch(OPERAND(scan)))
+                                                       return(1);
+                                               reginput = save;
+                                               scan = regnext(scan);
+                                       } while (scan != NULL && OP(scan) == BRANCH);
+                                       return(0);
+                                       /* NOTREACHED */
+                               }
+                       }
+                       /* NOTREACHED */
+                       break;
+               case STAR:
+               case PLUS: {
+                               register char nextch;
+                               register int no;
+                               register char *save;
+                               register int min;
+
+                               /*
+                                * Lookahead to avoid useless match attempts
+                                * when we know what character comes next.
+                                */
+                               nextch = '\0';
+                               if (OP(next) == EXACTLY)
+                                       nextch = *OPERAND(next);
+                               min = (OP(scan) == STAR) ? 0 : 1;
+                               save = reginput;
+                               no = regrepeat(OPERAND(scan));
+                               while (no >= min) {
+                                       /* If it could work, try it. */
+                                       if (nextch == '\0' || *reginput == nextch)
+                                               if (regmatch(next))
+                                                       return(1);
+                                       /* Couldn't or didn't -- back up. */
+                                       no--;
+                                       reginput = save + no;
+                               }
+                               return(0);
+                       }
+                       /* NOTREACHED */
+                       break;
+               case END:
+                       return(1);      /* Success! */
+                       /* NOTREACHED */
+                       break;
+               default:
+                       regerror("memory corruption");
+                       return(0);
+                       /* NOTREACHED */
+                       break;
+               }
+
+               scan = next;
+       }
+
+       /*
+        * We get here only if there's trouble -- normally "case END" is
+        * the terminating point.
+        */
+       regerror("corrupted pointers");
+       return(0);
+}
+
+/*
+ - regrepeat - repeatedly match something simple, report how many
+ */
+static int
+regrepeat(p)
+char *p;
+{
+       register int count = 0;
+       register char *scan;
+       register char *opnd;
+
+       scan = reginput;
+       opnd = OPERAND(p);
+       switch (OP(p)) {
+       case ANY:
+               count = strlen(scan);
+               scan += count;
+               break;
+       case EXACTLY:
+               while (*opnd == *scan) {
+                       count++;
+                       scan++;
+               }
+               break;
+       case ANYOF:
+               while (*scan != '\0' && strchr(opnd, *scan) != NULL) {
+                       count++;
+                       scan++;
+               }
+               break;
+       case ANYBUT:
+               while (*scan != '\0' && strchr(opnd, *scan) == NULL) {
+                       count++;
+                       scan++;
+               }
+               break;
+       default:                /* Oh dear.  Called inappropriately. */
+               regerror("internal foulup");
+               count = 0;      /* Best compromise. */
+               break;
+       }
+       reginput = scan;
+
+       return(count);
+}
+
+/*
+ - regnext - dig the "next" pointer out of a node
+ */
+static char *
+regnext(p)
+register char *p;
+{
+       register int offset;
+
+       if (p == &regdummy)
+               return(NULL);
+
+       offset = NEXT(p);
+       if (offset == 0)
+               return(NULL);
+
+       if (OP(p) == BACK)
+               return(p-offset);
+       else
+               return(p+offset);
+}
+
+#ifdef DEBUG
+
+STATIC char *regprop();
+
+/*
+ - regdump - dump a regexp onto stdout in vaguely comprehensible form
+ */
+void
+regdump(r)
+regexp *r;
+{
+       register char *s;
+       register char op = EXACTLY;     /* Arbitrary non-END op. */
+       register char *next;
+
+
+       s = r->program + 1;
+       while (op != END) {     /* While that wasn't END last time... */
+               op = OP(s);
+               printf("%2d%s", s-r->program, regprop(s));      /* Where, what. */
+               next = regnext(s);
+               if (next == NULL)               /* Next ptr. */
+                       printf("(0)");
+               else 
+                       printf("(%d)", (s-r->program)+(next-s));
+               s += 3;
+               if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
+                       /* Literal string, where present. */
+                       while (*s != '\0') {
+                               putchar(*s);
+                               s++;
+                       }
+                       s++;
+               }
+               putchar('\n');
+       }
+
+       /* Header fields of interest. */
+       if (r->regstart != '\0')
+               printf("start `%c' ", r->regstart);
+       if (r->reganch)
+               printf("anchored ");
+       if (r->regmust != NULL)
+               printf("must have \"%s\"", r->regmust);
+       printf("\n");
+}
+
+/*
+ - regprop - printable representation of opcode
+ */
+static char *
+regprop(op)
+char *op;
+{
+       register char *p;
+       static char buf[50];
+
+       (void) strcpy(buf, ":");
+
+       switch (OP(op)) {
+       case BOL:
+               p = "BOL";
+               break;
+       case EOL:
+               p = "EOL";
+               break;
+       case ANY:
+               p = "ANY";
+               break;
+       case ANYOF:
+               p = "ANYOF";
+               break;
+       case ANYBUT:
+               p = "ANYBUT";
+               break;
+       case BRANCH:
+               p = "BRANCH";
+               break;
+       case EXACTLY:
+               p = "EXACTLY";
+               break;
+       case NOTHING:
+               p = "NOTHING";
+               break;
+       case BACK:
+               p = "BACK";
+               break;
+       case END:
+               p = "END";
+               break;
+       case OPEN+1:
+       case OPEN+2:
+       case OPEN+3:
+       case OPEN+4:
+       case OPEN+5:
+       case OPEN+6:
+       case OPEN+7:
+       case OPEN+8:
+       case OPEN+9:
+               sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN);
+               p = NULL;
+               break;
+       case CLOSE+1:
+       case CLOSE+2:
+       case CLOSE+3:
+       case CLOSE+4:
+       case CLOSE+5:
+       case CLOSE+6:
+       case CLOSE+7:
+       case CLOSE+8:
+       case CLOSE+9:
+               sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE);
+               p = NULL;
+               break;
+       case STAR:
+               p = "STAR";
+               break;
+       case PLUS:
+               p = "PLUS";
+               break;
+       default:
+               regerror("corrupted opcode");
+               break;
+       }
+       if (p != NULL)
+               (void) strcat(buf, p);
+       return(buf);
+}
+#endif
+
+/*
+ * The following is provided for those people who do not have strcspn() in
+ * their C libraries.  They should get off their butts and do something
+ * about it; at least one public-domain implementation of those (highly
+ * useful) string routines has been published on Usenet.
+ */
+#ifdef STRCSPN
+/*
+ * strcspn - find length of initial segment of s1 consisting entirely
+ * of characters not from s2
+ */
+
+static int
+strcspn(s1, s2)
+char *s1;
+char *s2;
+{
+       register char *scan1;
+       register char *scan2;
+       register int count;
+
+       count = 0;
+       for (scan1 = s1; *scan1 != '\0'; scan1++) {
+               for (scan2 = s2; *scan2 != '\0';)       /* ++ moved down. */
+                       if (*scan1 == *scan2++)
+                               return(count);
+               count++;
+       }
+       return(count);
+}
+#endif
diff --git a/thirdparty/less/regexp.h b/thirdparty/less/regexp.h
new file mode 100644 (file)
index 0000000..bcef6d1
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Definitions etc. for regexp(3) routines.
+ *
+ * Caveat:  this is V8 regexp(3) [actually, a reimplementation thereof],
+ * not the System V one.
+ */
+
+#ifndef _REGEXP
+#define _REGEXP 1
+
+#define NSUBEXP  10
+typedef struct regexp {
+       char *startp[NSUBEXP];
+       char *endp[NSUBEXP];
+       char regstart;          /* Internal use only. */
+       char reganch;           /* Internal use only. */
+       char *regmust;          /* Internal use only. */
+       int regmlen;            /* Internal use only. */
+       char program[1];        /* Unwarranted chumminess with compiler. */
+} regexp;
+
+#if defined(__STDC__) || defined(__cplusplus)
+#   define _ANSI_ARGS_(x)       x
+#else
+#   define _ANSI_ARGS_(x)       ()
+#endif
+
+extern regexp *regcomp _ANSI_ARGS_((char *exp));
+extern int regexec _ANSI_ARGS_((regexp *prog, char *string));
+extern int regexec2 _ANSI_ARGS_((regexp *prog, char *string, int notbol));
+extern void regsub _ANSI_ARGS_((regexp *prog, char *source, char *dest));
+extern void regerror _ANSI_ARGS_((char *msg));
+
+#endif /* REGEXP */
diff --git a/thirdparty/less/screen.c b/thirdparty/less/screen.c
new file mode 100644 (file)
index 0000000..b8bc666
--- /dev/null
@@ -0,0 +1,2502 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Routines which deal with the characteristics of the terminal.
+ * Uses termcap to be as terminal-independent as possible.
+ */
+
+#include "less.h"
+#include "cmd.h"
+
+#if MSDOS_COMPILER
+#include "pckeys.h"
+#if MSDOS_COMPILER==MSOFTC
+#include <graph.h>
+#else
+#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
+#include <conio.h>
+#if MSDOS_COMPILER==DJGPPC
+#include <pc.h>
+extern int fd0;
+#endif
+#else
+#if MSDOS_COMPILER==WIN32C
+#include <windows.h>
+#endif
+#endif
+#endif
+#include <time.h>
+
+#else
+
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
+#include <termios.h>
+#else
+#if HAVE_TERMIO_H
+#include <termio.h>
+#else
+#if HAVE_SGSTAT_H
+#include <sgstat.h>
+#else
+#include <sgtty.h>
+#endif
+#endif
+#endif
+
+#if HAVE_TERMCAP_H
+#include <termcap.h>
+#endif
+#ifdef _OSK
+#include <signal.h>
+#endif
+#if OS2
+#include <sys/signal.h>
+#include "pckeys.h"
+#endif
+#if HAVE_SYS_STREAM_H
+#include <sys/stream.h>
+#endif
+#if HAVE_SYS_PTEM_H
+#include <sys/ptem.h>
+#endif
+
+#endif /* MSDOS_COMPILER */
+
+/*
+ * Check for broken termios package that forces you to manually
+ * set the line discipline.
+ */
+#ifdef __ultrix__
+#define MUST_SET_LINE_DISCIPLINE 1
+#else
+#define MUST_SET_LINE_DISCIPLINE 0
+#endif
+
+#if OS2
+#define        DEFAULT_TERM            "ansi"
+static char *windowid;
+#else
+#define        DEFAULT_TERM            "unknown"
+#endif
+
+#if MSDOS_COMPILER==MSOFTC
+static int videopages;
+static long msec_loops;
+static int flash_created = 0;
+#define        SETCOLORS(fg,bg)        { _settextcolor(fg); _setbkcolor(bg); }
+#endif
+
+#if MSDOS_COMPILER==BORLANDC
+static unsigned short *whitescreen;
+static int flash_created = 0;
+#endif
+#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
+#define _settextposition(y,x)   gotoxy(x,y)
+#define _clearscreen(m)         clrscr()
+#define _outtext(s)             cputs(s)
+#define        SETCOLORS(fg,bg)        { textcolor(fg); textbackground(bg); }
+extern int sc_height;
+#endif
+
+#if MSDOS_COMPILER==WIN32C
+struct keyRecord
+{
+       int ascii;
+       int scan;
+} currentKey;
+
+static int keyCount = 0;
+static WORD curr_attr;
+static int pending_scancode = 0;
+static WORD *whitescreen;
+
+static HANDLE con_out_save = INVALID_HANDLE_VALUE; /* previous console */
+static HANDLE con_out_ours = INVALID_HANDLE_VALUE; /* our own */
+HANDLE con_out = INVALID_HANDLE_VALUE;             /* current console */
+
+extern int quitting;
+static void win32_init_term();
+static void win32_deinit_term();
+
+#define FG_COLORS       (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY)
+#define BG_COLORS       (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY)
+#define        MAKEATTR(fg,bg)         ((WORD)((fg)|((bg)<<4)))
+#define        SETCOLORS(fg,bg)        { curr_attr = MAKEATTR(fg,bg); \
+                               if (SetConsoleTextAttribute(con_out, curr_attr) == 0) \
+                               error("SETCOLORS failed"); }
+#endif
+
+#if MSDOS_COMPILER
+public int nm_fg_color;                /* Color of normal text */
+public int nm_bg_color;
+public int bo_fg_color;                /* Color of bold text */
+public int bo_bg_color;
+public int ul_fg_color;                /* Color of underlined text */
+public int ul_bg_color;
+public int so_fg_color;                /* Color of standout text */
+public int so_bg_color;
+public int bl_fg_color;                /* Color of blinking text */
+public int bl_bg_color;
+static int sy_fg_color;                /* Color of system text (before less) */
+static int sy_bg_color;
+
+#else
+
+/*
+ * Strings passed to tputs() to do various terminal functions.
+ */
+static char
+       *sc_pad,                /* Pad string */
+       *sc_home,               /* Cursor home */
+       *sc_addline,            /* Add line, scroll down following lines */
+       *sc_lower_left,         /* Cursor to last line, first column */
+       *sc_return,             /* Cursor to beginning of current line */
+       *sc_move,               /* General cursor positioning */
+       *sc_clear,              /* Clear screen */
+       *sc_eol_clear,          /* Clear to end of line */
+       *sc_eos_clear,          /* Clear to end of screen */
+       *sc_s_in,               /* Enter standout (highlighted) mode */
+       *sc_s_out,              /* Exit standout mode */
+       *sc_u_in,               /* Enter underline mode */
+       *sc_u_out,              /* Exit underline mode */
+       *sc_b_in,               /* Enter bold mode */
+       *sc_b_out,              /* Exit bold mode */
+       *sc_bl_in,              /* Enter blink mode */
+       *sc_bl_out,             /* Exit blink mode */
+       *sc_visual_bell,        /* Visual bell (flash screen) sequence */
+       *sc_backspace,          /* Backspace cursor */
+       *sc_s_keypad,           /* Start keypad mode */
+       *sc_e_keypad,           /* End keypad mode */
+       *sc_init,               /* Startup terminal initialization */
+       *sc_deinit;             /* Exit terminal de-initialization */
+#endif
+
+static int init_done = 0;
+
+public int auto_wrap;          /* Terminal does \r\n when write past margin */
+public int ignaw;              /* Terminal ignores \n immediately after wrap */
+public int erase_char;         /* The user's erase char */
+public int erase2_char;                /* The user's other erase char */
+public int kill_char;          /* The user's line-kill char */
+public int werase_char;                /* The user's word-erase char */
+public int sc_width, sc_height;        /* Height & width of screen */
+public int bo_s_width, bo_e_width;     /* Printing width of boldface seq */
+public int ul_s_width, ul_e_width;     /* Printing width of underline seq */
+public int so_s_width, so_e_width;     /* Printing width of standout seq */
+public int bl_s_width, bl_e_width;     /* Printing width of blink seq */
+public int above_mem, below_mem;       /* Memory retained above/below screen */
+public int can_goto_line;              /* Can move cursor to any line */
+public int clear_bg;           /* Clear fills with background color */
+public int missing_cap = 0;    /* Some capability is missing */
+
+static int attrmode = AT_NORMAL;
+extern int binattr;
+
+#if !MSDOS_COMPILER
+static char *cheaper();
+static void tmodes();
+#endif
+
+/*
+ * These two variables are sometimes defined in,
+ * and needed by, the termcap library.
+ */
+#if MUST_DEFINE_OSPEED
+extern short ospeed;   /* Terminal output baud rate */
+extern char PC;                /* Pad character */
+#endif
+#ifdef _OSK
+short ospeed;
+char PC_, *UP, *BC;
+#endif
+
+extern int quiet;              /* If VERY_QUIET, use visual bell for bell */
+extern int no_back_scroll;
+extern int swindow;
+extern int no_init;
+extern int no_keypad;
+extern int sigs;
+extern int wscroll;
+extern int screen_trashed;
+extern int tty;
+extern int top_scroll;
+extern int oldbot;
+#if HILITE_SEARCH
+extern int hilite_search;
+#endif
+
+extern char *tgetstr();
+extern char *tgoto();
+
+
+/*
+ * Change terminal to "raw mode", or restore to "normal" mode.
+ * "Raw mode" means 
+ *     1. An outstanding read will complete on receipt of a single keystroke.
+ *     2. Input is not echoed.  
+ *     3. On output, \n is mapped to \r\n.
+ *     4. \t is NOT expanded into spaces.
+ *     5. Signal-causing characters such as ctrl-C (interrupt),
+ *        etc. are NOT disabled.
+ * It doesn't matter whether an input \n is mapped to \r, or vice versa.
+ */
+       public void
+raw_mode(on)
+       int on;
+{
+       static int curr_on = 0;
+
+       if (on == curr_on)
+               return;
+       erase2_char = '\b'; /* in case OS doesn't know about erase2 */
+#if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
+    {
+       struct termios s;
+       static struct termios save_term;
+       static int saved_term = 0;
+
+       if (on) 
+       {
+               /*
+                * Get terminal modes.
+                */
+               tcgetattr(tty, &s);
+
+               /*
+                * Save modes and set certain variables dependent on modes.
+                */
+               if (!saved_term)
+               {
+                       save_term = s;
+                       saved_term = 1;
+               }
+#if HAVE_OSPEED
+               switch (cfgetospeed(&s))
+               {
+#ifdef B0
+               case B0: ospeed = 0; break;
+#endif
+#ifdef B50
+               case B50: ospeed = 1; break;
+#endif
+#ifdef B75
+               case B75: ospeed = 2; break;
+#endif
+#ifdef B110
+               case B110: ospeed = 3; break;
+#endif
+#ifdef B134
+               case B134: ospeed = 4; break;
+#endif
+#ifdef B150
+               case B150: ospeed = 5; break;
+#endif
+#ifdef B200
+               case B200: ospeed = 6; break;
+#endif
+#ifdef B300
+               case B300: ospeed = 7; break;
+#endif
+#ifdef B600
+               case B600: ospeed = 8; break;
+#endif
+#ifdef B1200
+               case B1200: ospeed = 9; break;
+#endif
+#ifdef B1800
+               case B1800: ospeed = 10; break;
+#endif
+#ifdef B2400
+               case B2400: ospeed = 11; break;
+#endif
+#ifdef B4800
+               case B4800: ospeed = 12; break;
+#endif
+#ifdef B9600
+               case B9600: ospeed = 13; break;
+#endif
+#ifdef EXTA
+               case EXTA: ospeed = 14; break;
+#endif
+#ifdef EXTB
+               case EXTB: ospeed = 15; break;
+#endif
+#ifdef B57600
+               case B57600: ospeed = 16; break;
+#endif
+#ifdef B115200
+               case B115200: ospeed = 17; break;
+#endif
+               default: ;
+               }
+#endif
+               erase_char = s.c_cc[VERASE];
+#ifdef VERASE2
+               erase2_char = s.c_cc[VERASE2];
+#endif
+               kill_char = s.c_cc[VKILL];
+#ifdef VWERASE
+               werase_char = s.c_cc[VWERASE];
+#else
+               werase_char = CONTROL('W');
+#endif
+
+               /*
+                * Set the modes to the way we want them.
+                */
+               s.c_lflag &= ~(0
+#ifdef ICANON
+                       | ICANON
+#endif
+#ifdef ECHO
+                       | ECHO
+#endif
+#ifdef ECHOE
+                       | ECHOE
+#endif
+#ifdef ECHOK
+                       | ECHOK
+#endif
+#if ECHONL
+                       | ECHONL
+#endif
+               );
+
+               s.c_oflag |= (0
+#ifdef OXTABS
+                       | OXTABS
+#else
+#ifdef TAB3
+                       | TAB3
+#else
+#ifdef XTABS
+                       | XTABS
+#endif
+#endif
+#endif
+#ifdef OPOST
+                       | OPOST
+#endif
+#ifdef ONLCR
+                       | ONLCR
+#endif
+               );
+
+               s.c_oflag &= ~(0
+#ifdef ONOEOT
+                       | ONOEOT
+#endif
+#ifdef OCRNL
+                       | OCRNL
+#endif
+#ifdef ONOCR
+                       | ONOCR
+#endif
+#ifdef ONLRET
+                       | ONLRET
+#endif
+               );
+               s.c_cc[VMIN] = 1;
+               s.c_cc[VTIME] = 0;
+#ifdef VLNEXT
+               s.c_cc[VLNEXT] = 0;
+#endif
+#ifdef VDSUSP
+               s.c_cc[VDSUSP] = 0;
+#endif
+#if MUST_SET_LINE_DISCIPLINE
+               /*
+                * System's termios is broken; need to explicitly 
+                * request TERMIODISC line discipline.
+                */
+               s.c_line = TERMIODISC;
+#endif
+       } else
+       {
+               /*
+                * Restore saved modes.
+                */
+               s = save_term;
+       }
+#if HAVE_FSYNC
+       fsync(tty);
+#endif
+       tcsetattr(tty, TCSADRAIN, &s);
+#if MUST_SET_LINE_DISCIPLINE
+       if (!on)
+       {
+               /*
+                * Broken termios *ignores* any line discipline
+                * except TERMIODISC.  A different old line discipline
+                * is therefore not restored, yet.  Restore the old
+                * line discipline by hand.
+                */
+               ioctl(tty, TIOCSETD, &save_term.c_line);
+       }
+#endif
+    }
+#else
+#ifdef TCGETA
+    {
+       struct termio s;
+       static struct termio save_term;
+       static int saved_term = 0;
+
+       if (on)
+       {
+               /*
+                * Get terminal modes.
+                */
+               ioctl(tty, TCGETA, &s);
+
+               /*
+                * Save modes and set certain variables dependent on modes.
+                */
+               if (!saved_term)
+               {
+                       save_term = s;
+                       saved_term = 1;
+               }
+#if HAVE_OSPEED
+               ospeed = s.c_cflag & CBAUD;
+#endif
+               erase_char = s.c_cc[VERASE];
+               kill_char = s.c_cc[VKILL];
+#ifdef VWERASE
+               werase_char = s.c_cc[VWERASE];
+#else
+               werase_char = CONTROL('W');
+#endif
+
+               /*
+                * Set the modes to the way we want them.
+                */
+               s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
+               s.c_oflag |=  (OPOST|ONLCR|TAB3);
+               s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
+               s.c_cc[VMIN] = 1;
+               s.c_cc[VTIME] = 0;
+       } else
+       {
+               /*
+                * Restore saved modes.
+                */
+               s = save_term;
+       }
+       ioctl(tty, TCSETAW, &s);
+    }
+#else
+#ifdef TIOCGETP
+    {
+       struct sgttyb s;
+       static struct sgttyb save_term;
+       static int saved_term = 0;
+
+       if (on)
+       {
+               /*
+                * Get terminal modes.
+                */
+               ioctl(tty, TIOCGETP, &s);
+
+               /*
+                * Save modes and set certain variables dependent on modes.
+                */
+               if (!saved_term)
+               {
+                       save_term = s;
+                       saved_term = 1;
+               }
+#if HAVE_OSPEED
+               ospeed = s.sg_ospeed;
+#endif
+               erase_char = s.sg_erase;
+               kill_char = s.sg_kill;
+               werase_char = CONTROL('W');
+
+               /*
+                * Set the modes to the way we want them.
+                */
+               s.sg_flags |= CBREAK;
+               s.sg_flags &= ~(ECHO|XTABS);
+       } else
+       {
+               /*
+                * Restore saved modes.
+                */
+               s = save_term;
+       }
+       ioctl(tty, TIOCSETN, &s);
+    }
+#else
+#ifdef _OSK
+    {
+       struct sgbuf s;
+       static struct sgbuf save_term;
+       static int saved_term = 0;
+
+       if (on)
+       {
+               /*
+                * Get terminal modes.
+                */
+               _gs_opt(tty, &s);
+
+               /*
+                * Save modes and set certain variables dependent on modes.
+                */
+               if (!saved_term)
+               {
+                       save_term = s;
+                       saved_term = 1;
+               }
+               erase_char = s.sg_bspch;
+               kill_char = s.sg_dlnch;
+               werase_char = CONTROL('W');
+
+               /*
+                * Set the modes to the way we want them.
+                */
+               s.sg_echo = 0;
+               s.sg_eofch = 0;
+               s.sg_pause = 0;
+               s.sg_psch = 0;
+       } else
+       {
+               /*
+                * Restore saved modes.
+                */
+               s = save_term;
+       }
+       _ss_opt(tty, &s);
+    }
+#else
+       /* MS-DOS, Windows, or OS2 */
+#if OS2
+       /* OS2 */
+       LSIGNAL(SIGINT, SIG_IGN);
+#endif
+       erase_char = '\b';
+#if MSDOS_COMPILER==DJGPPC
+       kill_char = CONTROL('U');
+       /*
+        * So that when we shell out or run another program, its
+        * stdin is in cooked mode.  We do not switch stdin to binary 
+        * mode if fd0 is zero, since that means we were called before
+        * tty was reopened in open_getchr, in which case we would be
+        * changing the original stdin device outside less.
+        */
+       if (fd0 != 0)
+               setmode(0, on ? O_BINARY : O_TEXT);
+#else
+       kill_char = ESC;
+#endif
+       werase_char = CONTROL('W');
+#endif
+#endif
+#endif
+#endif
+       curr_on = on;
+}
+
+#if !MSDOS_COMPILER
+/*
+ * Some glue to prevent calling termcap functions if tgetent() failed.
+ */
+static int hardcopy;
+
+       static char *
+ltget_env(capname)
+       char *capname;
+{
+       char name[16];
+       char *s;
+
+       s = lgetenv("LESS_TERMCAP_DEBUG");
+       if (s != NULL && *s != '\0')
+       {
+               struct env { struct env *next; char *name; char *value; };
+               static struct env *envs = NULL;
+               struct env *p;
+               for (p = envs;  p != NULL;  p = p->next)
+                       if (strcmp(p->name, capname) == 0)
+                               return p->value;
+               p = (struct env *) ecalloc(1, sizeof(struct env));
+               p->name = save(capname);
+               p->value = (char *) ecalloc(strlen(capname)+3, sizeof(char));
+               sprintf(p->value, "<%s>", capname);
+               p->next = envs;
+               envs = p;
+               return p->value;
+       }
+       strcpy(name, "LESS_TERMCAP_");
+       strcat(name, capname);
+       return (lgetenv(name));
+}
+
+       static int
+ltgetflag(capname)
+       char *capname;
+{
+       char *s;
+
+       if ((s = ltget_env(capname)) != NULL)
+               return (*s != '\0' && *s != '0');
+       if (hardcopy)
+               return (0);
+       return (tgetflag(capname));
+}
+
+       static int
+ltgetnum(capname)
+       char *capname;
+{
+       char *s;
+
+       if ((s = ltget_env(capname)) != NULL)
+               return (atoi(s));
+       if (hardcopy)
+               return (-1);
+       return (tgetnum(capname));
+}
+
+       static char *
+ltgetstr(capname, pp)
+       char *capname;
+       char **pp;
+{
+       char *s;
+
+       if ((s = ltget_env(capname)) != NULL)
+               return (s);
+       if (hardcopy)
+               return (NULL);
+       return (tgetstr(capname, pp));
+}
+#endif /* MSDOS_COMPILER */
+
+/*
+ * Get size of the output screen.
+ */
+       public void
+scrsize()
+{
+       register char *s;
+       int sys_height;
+       int sys_width;
+#if !MSDOS_COMPILER
+       int n;
+#endif
+
+#define        DEF_SC_WIDTH    80
+#if MSDOS_COMPILER
+#define        DEF_SC_HEIGHT   25
+#else
+#define        DEF_SC_HEIGHT   24
+#endif
+
+
+       sys_width = sys_height = 0;
+
+#if MSDOS_COMPILER==MSOFTC
+       {
+               struct videoconfig w;
+               _getvideoconfig(&w);
+               sys_height = w.numtextrows;
+               sys_width = w.numtextcols;
+       }
+#else
+#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
+       {
+               struct text_info w;
+               gettextinfo(&w);
+               sys_height = w.screenheight;
+               sys_width = w.screenwidth;
+       }
+#else
+#if MSDOS_COMPILER==WIN32C
+       {
+               CONSOLE_SCREEN_BUFFER_INFO scr;
+               GetConsoleScreenBufferInfo(con_out, &scr);
+               sys_height = scr.srWindow.Bottom - scr.srWindow.Top + 1;
+               sys_width = scr.srWindow.Right - scr.srWindow.Left + 1;
+       }
+#else
+#if OS2
+       {
+               int s[2];
+               _scrsize(s);
+               sys_width = s[0];
+               sys_height = s[1];
+               /*
+                * When using terminal emulators for XFree86/OS2, the
+                * _scrsize function does not work well.
+                * Call the scrsize.exe program to get the window size.
+                */
+               windowid = getenv("WINDOWID");
+               if (windowid != NULL)
+               {
+                       FILE *fd = popen("scrsize", "rt");
+                       if (fd != NULL)
+                       {
+                               int w, h;
+                               fscanf(fd, "%i %i", &w, &h);
+                               if (w > 0 && h > 0)
+                               {
+                                       sys_width = w;
+                                       sys_height = h;
+                               }
+                               pclose(fd);
+                       }
+               }
+       }
+#else
+#ifdef TIOCGWINSZ
+       {
+               struct winsize w;
+               if (ioctl(2, TIOCGWINSZ, &w) == 0)
+               {
+                       if (w.ws_row > 0)
+                               sys_height = w.ws_row;
+                       if (w.ws_col > 0)
+                               sys_width = w.ws_col;
+               }
+       }
+#else
+#ifdef WIOCGETD
+       {
+               struct uwdata w;
+               if (ioctl(2, WIOCGETD, &w) == 0)
+               {
+                       if (w.uw_height > 0)
+                               sys_height = w.uw_height / w.uw_vs;
+                       if (w.uw_width > 0)
+                               sys_width = w.uw_width / w.uw_hs;
+               }
+       }
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+
+       if (sys_height > 0)
+               sc_height = sys_height;
+       else if ((s = lgetenv("LINES")) != NULL)
+               sc_height = atoi(s);
+#if !MSDOS_COMPILER
+       else if ((n = ltgetnum("li")) > 0)
+               sc_height = n;
+#endif
+       else
+               sc_height = DEF_SC_HEIGHT;
+
+       if (sys_width > 0)
+               sc_width = sys_width;
+       else if ((s = lgetenv("COLUMNS")) != NULL)
+               sc_width = atoi(s);
+#if !MSDOS_COMPILER
+       else if ((n = ltgetnum("co")) > 0)
+               sc_width = n;
+#endif
+       else
+               sc_width = DEF_SC_WIDTH;
+}
+
+#if MSDOS_COMPILER==MSOFTC
+/*
+ * Figure out how many empty loops it takes to delay a millisecond.
+ */
+       static void
+get_clock()
+{
+       clock_t start;
+       
+       /*
+        * Get synchronized at the start of a tick.
+        */
+       start = clock();
+       while (clock() == start)
+               ;
+       /*
+        * Now count loops till the next tick.
+        */
+       start = clock();
+       msec_loops = 0;
+       while (clock() == start)
+               msec_loops++;
+       /*
+        * Convert from (loops per clock) to (loops per millisecond).
+        */
+       msec_loops *= CLOCKS_PER_SEC;
+       msec_loops /= 1000;
+}
+
+/*
+ * Delay for a specified number of milliseconds.
+ */
+       static void
+dummy_func()
+{
+       static long delay_dummy = 0;
+       delay_dummy++;
+}
+
+       static void
+delay(msec)
+       int msec;
+{
+       long i;
+       
+       while (msec-- > 0)
+       {
+               for (i = 0;  i < msec_loops;  i++)
+               {
+                       /*
+                        * Make it look like we're doing something here,
+                        * so the optimizer doesn't remove the whole loop.
+                        */
+                       dummy_func();
+               }
+       }
+}
+#endif
+
+/*
+ * Return the characters actually input by a "special" key.
+ */
+       public char *
+special_key_str(key)
+       int key;
+{
+       static char tbuf[40];
+       char *s;
+#if MSDOS_COMPILER || OS2
+       static char k_right[]           = { '\340', PCK_RIGHT, 0 };
+       static char k_left[]            = { '\340', PCK_LEFT, 0  };
+       static char k_ctl_right[]       = { '\340', PCK_CTL_RIGHT, 0  };
+       static char k_ctl_left[]        = { '\340', PCK_CTL_LEFT, 0  };
+       static char k_insert[]          = { '\340', PCK_INSERT, 0  };
+       static char k_delete[]          = { '\340', PCK_DELETE, 0  };
+       static char k_ctl_delete[]      = { '\340', PCK_CTL_DELETE, 0  };
+       static char k_ctl_backspace[]   = { '\177', 0 };
+       static char k_home[]            = { '\340', PCK_HOME, 0 };
+       static char k_end[]             = { '\340', PCK_END, 0 };
+       static char k_up[]              = { '\340', PCK_UP, 0 };
+       static char k_down[]            = { '\340', PCK_DOWN, 0 };
+       static char k_backtab[]         = { '\340', PCK_SHIFT_TAB, 0 };
+       static char k_pagedown[]        = { '\340', PCK_PAGEDOWN, 0 };
+       static char k_pageup[]          = { '\340', PCK_PAGEUP, 0 };
+       static char k_f1[]              = { '\340', PCK_F1, 0 };
+#endif
+#if !MSDOS_COMPILER
+       char *sp = tbuf;
+#endif
+
+       switch (key)
+       {
+#if OS2
+       /*
+        * If windowid is not NULL, assume less is executed in 
+        * the XFree86 environment.
+        */
+       case SK_RIGHT_ARROW:
+               s = windowid ? ltgetstr("kr", &sp) : k_right;
+               break;
+       case SK_LEFT_ARROW:
+               s = windowid ? ltgetstr("kl", &sp) : k_left;
+               break;
+       case SK_UP_ARROW:
+               s = windowid ? ltgetstr("ku", &sp) : k_up;
+               break;
+       case SK_DOWN_ARROW:
+               s = windowid ? ltgetstr("kd", &sp) : k_down;
+               break;
+       case SK_PAGE_UP:
+               s = windowid ? ltgetstr("kP", &sp) : k_pageup;
+               break;
+       case SK_PAGE_DOWN:
+               s = windowid ? ltgetstr("kN", &sp) : k_pagedown;
+               break;
+       case SK_HOME:
+               s = windowid ? ltgetstr("kh", &sp) : k_home;
+               break;
+       case SK_END:
+               s = windowid ? ltgetstr("@7", &sp) : k_end;
+               break;
+       case SK_DELETE:
+               if (windowid)
+               {
+                       s = ltgetstr("kD", &sp);
+                       if (s == NULL)
+                       {
+                               tbuf[0] = '\177';
+                               tbuf[1] = '\0';
+                               s = tbuf;
+                       }
+               } else
+                       s = k_delete;
+               break;
+#endif
+#if MSDOS_COMPILER
+       case SK_RIGHT_ARROW:
+               s = k_right;
+               break;
+       case SK_LEFT_ARROW:
+               s = k_left;
+               break;
+       case SK_UP_ARROW:
+               s = k_up;
+               break;
+       case SK_DOWN_ARROW:
+               s = k_down;
+               break;
+       case SK_PAGE_UP:
+               s = k_pageup;
+               break;
+       case SK_PAGE_DOWN:
+               s = k_pagedown;
+               break;
+       case SK_HOME:
+               s = k_home;
+               break;
+       case SK_END:
+               s = k_end;
+               break;
+       case SK_DELETE:
+               s = k_delete;
+               break;
+#endif
+#if MSDOS_COMPILER || OS2
+       case SK_INSERT:
+               s = k_insert;
+               break;
+       case SK_CTL_LEFT_ARROW:
+               s = k_ctl_left;
+               break;
+       case SK_CTL_RIGHT_ARROW:
+               s = k_ctl_right;
+               break;
+       case SK_CTL_BACKSPACE:
+               s = k_ctl_backspace;
+               break;
+       case SK_CTL_DELETE:
+               s = k_ctl_delete;
+               break;
+       case SK_F1:
+               s = k_f1;
+               break;
+       case SK_BACKTAB:
+               s = k_backtab;
+               break;
+#else
+       case SK_RIGHT_ARROW:
+               s = ltgetstr("kr", &sp);
+               break;
+       case SK_LEFT_ARROW:
+               s = ltgetstr("kl", &sp);
+               break;
+       case SK_UP_ARROW:
+               s = ltgetstr("ku", &sp);
+               break;
+       case SK_DOWN_ARROW:
+               s = ltgetstr("kd", &sp);
+               break;
+       case SK_PAGE_UP:
+               s = ltgetstr("kP", &sp);
+               break;
+       case SK_PAGE_DOWN:
+               s = ltgetstr("kN", &sp);
+               break;
+       case SK_HOME:
+               s = ltgetstr("kh", &sp);
+               break;
+       case SK_END:
+               s = ltgetstr("@7", &sp);
+               break;
+       case SK_DELETE:
+               s = ltgetstr("kD", &sp);
+               if (s == NULL)
+               {
+                       tbuf[0] = '\177';
+                       tbuf[1] = '\0';
+                       s = tbuf;
+               }
+               break;
+#endif
+       case SK_CONTROL_K:
+               tbuf[0] = CONTROL('K');
+               tbuf[1] = '\0';
+               s = tbuf;
+               break;
+       default:
+               return (NULL);
+       }
+       return (s);
+}
+
+/*
+ * Get terminal capabilities via termcap.
+ */
+       public void
+get_term()
+{
+#if MSDOS_COMPILER
+       auto_wrap = 1;
+       ignaw = 0;
+       can_goto_line = 1;
+       clear_bg = 1;
+       /*
+        * Set up default colors.
+        * The xx_s_width and xx_e_width vars are already initialized to 0.
+        */
+#if MSDOS_COMPILER==MSOFTC
+       sy_bg_color = _getbkcolor();
+       sy_fg_color = _gettextcolor();
+       get_clock();
+#else
+#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
+    {
+       struct text_info w;
+       gettextinfo(&w);
+       sy_bg_color = (w.attribute >> 4) & 0x0F;
+       sy_fg_color = (w.attribute >> 0) & 0x0F;
+    }
+#else
+#if MSDOS_COMPILER==WIN32C
+    {
+       DWORD nread;
+       CONSOLE_SCREEN_BUFFER_INFO scr;
+
+       con_out_save = con_out = GetStdHandle(STD_OUTPUT_HANDLE);
+       /*
+        * Always open stdin in binary. Note this *must* be done
+        * before any file operations have been done on fd0.
+        */
+       SET_BINARY(0);
+       GetConsoleScreenBufferInfo(con_out, &scr);
+       ReadConsoleOutputAttribute(con_out, &curr_attr, 
+                                       1, scr.dwCursorPosition, &nread);
+       sy_bg_color = (curr_attr & BG_COLORS) >> 4; /* normalize */
+       sy_fg_color = curr_attr & FG_COLORS;
+    }
+#endif
+#endif
+#endif
+       nm_fg_color = sy_fg_color;
+       nm_bg_color = sy_bg_color;
+       bo_fg_color = 11;
+       bo_bg_color = 0;
+       ul_fg_color = 9;
+       ul_bg_color = 0;
+       so_fg_color = 15;
+       so_bg_color = 9;
+       bl_fg_color = 15;
+       bl_bg_color = 0;
+
+       /*
+        * Get size of the screen.
+        */
+       scrsize();
+       pos_init();
+
+
+#else /* !MSDOS_COMPILER */
+
+       char *sp;
+       register char *t1, *t2;
+       char *term;
+       char termbuf[TERMBUF_SIZE];
+
+       static char sbuf[TERMSBUF_SIZE];
+
+#if OS2
+       /*
+        * Make sure the termcap database is available.
+        */
+       sp = lgetenv("TERMCAP");
+       if (sp == NULL || *sp == '\0')
+       {
+               char *termcap;
+               if ((sp = homefile("termcap.dat")) != NULL)
+               {
+                       termcap = (char *) ecalloc(strlen(sp)+9, sizeof(char));
+                       sprintf(termcap, "TERMCAP=%s", sp);
+                       free(sp);
+                       putenv(termcap);
+               }
+       }
+#endif
+       /*
+        * Find out what kind of terminal this is.
+        */
+       if ((term = lgetenv("TERM")) == NULL)
+               term = DEFAULT_TERM;
+       hardcopy = 0;
+       if (tgetent(termbuf, term) != TGETENT_OK)
+               hardcopy = 1;
+       if (ltgetflag("hc"))
+               hardcopy = 1;
+
+       /*
+        * Get size of the screen.
+        */
+       scrsize();
+       pos_init();
+
+       auto_wrap = ltgetflag("am");
+       ignaw = ltgetflag("xn");
+       above_mem = ltgetflag("da");
+       below_mem = ltgetflag("db");
+       clear_bg = ltgetflag("ut");
+
+       /*
+        * Assumes termcap variable "sg" is the printing width of:
+        * the standout sequence, the end standout sequence,
+        * the underline sequence, the end underline sequence,
+        * the boldface sequence, and the end boldface sequence.
+        */
+       if ((so_s_width = ltgetnum("sg")) < 0)
+               so_s_width = 0;
+       so_e_width = so_s_width;
+
+       bo_s_width = bo_e_width = so_s_width;
+       ul_s_width = ul_e_width = so_s_width;
+       bl_s_width = bl_e_width = so_s_width;
+
+#if HILITE_SEARCH
+       if (so_s_width > 0 || so_e_width > 0)
+               /*
+                * Disable highlighting by default on magic cookie terminals.
+                * Turning on highlighting might change the displayed width
+                * of a line, causing the display to get messed up.
+                * The user can turn it back on with -g, 
+                * but she won't like the results.
+                */
+               hilite_search = 0;
+#endif
+
+       /*
+        * Get various string-valued capabilities.
+        */
+       sp = sbuf;
+
+#if HAVE_OSPEED
+       sc_pad = ltgetstr("pc", &sp);
+       if (sc_pad != NULL)
+               PC = *sc_pad;
+#endif
+
+       sc_s_keypad = ltgetstr("ks", &sp);
+       if (sc_s_keypad == NULL)
+               sc_s_keypad = "";
+       sc_e_keypad = ltgetstr("ke", &sp);
+       if (sc_e_keypad == NULL)
+               sc_e_keypad = "";
+               
+       sc_init = ltgetstr("ti", &sp);
+       if (sc_init == NULL)
+               sc_init = "";
+
+       sc_deinit= ltgetstr("te", &sp);
+       if (sc_deinit == NULL)
+               sc_deinit = "";
+
+       sc_eol_clear = ltgetstr("ce", &sp);
+       if (sc_eol_clear == NULL || *sc_eol_clear == '\0')
+       {
+               missing_cap = 1;
+               sc_eol_clear = "";
+       }
+
+       sc_eos_clear = ltgetstr("cd", &sp);
+       if (below_mem && (sc_eos_clear == NULL || *sc_eos_clear == '\0'))
+       {
+               missing_cap = 1;
+               sc_eos_clear = "";
+       }
+
+       sc_clear = ltgetstr("cl", &sp);
+       if (sc_clear == NULL || *sc_clear == '\0')
+       {
+               missing_cap = 1;
+               sc_clear = "\n\n";
+       }
+
+       sc_move = ltgetstr("cm", &sp);
+       if (sc_move == NULL || *sc_move == '\0')
+       {
+               /*
+                * This is not an error here, because we don't 
+                * always need sc_move.
+                * We need it only if we don't have home or lower-left.
+                */
+               sc_move = "";
+               can_goto_line = 0;
+       } else
+               can_goto_line = 1;
+
+       tmodes("so", "se", &sc_s_in, &sc_s_out, "", "", &sp);
+       tmodes("us", "ue", &sc_u_in, &sc_u_out, sc_s_in, sc_s_out, &sp);
+       tmodes("md", "me", &sc_b_in, &sc_b_out, sc_s_in, sc_s_out, &sp);
+       tmodes("mb", "me", &sc_bl_in, &sc_bl_out, sc_s_in, sc_s_out, &sp);
+
+       sc_visual_bell = ltgetstr("vb", &sp);
+       if (sc_visual_bell == NULL)
+               sc_visual_bell = "";
+
+       if (ltgetflag("bs"))
+               sc_backspace = "\b";
+       else
+       {
+               sc_backspace = ltgetstr("bc", &sp);
+               if (sc_backspace == NULL || *sc_backspace == '\0')
+                       sc_backspace = "\b";
+       }
+
+       /*
+        * Choose between using "ho" and "cm" ("home" and "cursor move")
+        * to move the cursor to the upper left corner of the screen.
+        */
+       t1 = ltgetstr("ho", &sp);
+       if (t1 == NULL)
+               t1 = "";
+       if (*sc_move == '\0')
+               t2 = "";
+       else
+       {
+               strcpy(sp, tgoto(sc_move, 0, 0));
+               t2 = sp;
+               sp += strlen(sp) + 1;
+       }
+       sc_home = cheaper(t1, t2, "|\b^");
+
+       /*
+        * Choose between using "ll" and "cm"  ("lower left" and "cursor move")
+        * to move the cursor to the lower left corner of the screen.
+        */
+       t1 = ltgetstr("ll", &sp);
+       if (t1 == NULL)
+               t1 = "";
+       if (*sc_move == '\0')
+               t2 = "";
+       else
+       {
+               strcpy(sp, tgoto(sc_move, 0, sc_height-1));
+               t2 = sp;
+               sp += strlen(sp) + 1;
+       }
+       sc_lower_left = cheaper(t1, t2, "\r");
+
+       /*
+        * Get carriage return string.
+        */
+       sc_return = ltgetstr("cr", &sp);
+       if (sc_return == NULL)
+               sc_return = "\r";
+
+       /*
+        * Choose between using "al" or "sr" ("add line" or "scroll reverse")
+        * to add a line at the top of the screen.
+        */
+       t1 = ltgetstr("al", &sp);
+       if (t1 == NULL)
+               t1 = "";
+       t2 = ltgetstr("sr", &sp);
+       if (t2 == NULL)
+               t2 = "";
+#if OS2
+       if (*t1 == '\0' && *t2 == '\0')
+               sc_addline = "";
+       else
+#endif
+       if (above_mem)
+               sc_addline = t1;
+       else
+               sc_addline = cheaper(t1, t2, "");
+       if (*sc_addline == '\0')
+       {
+               /*
+                * Force repaint on any backward movement.
+                */
+               no_back_scroll = 1;
+       }
+#endif /* MSDOS_COMPILER */
+}
+
+#if !MSDOS_COMPILER
+/*
+ * Return the cost of displaying a termcap string.
+ * We use the trick of calling tputs, but as a char printing function
+ * we give it inc_costcount, which just increments "costcount".
+ * This tells us how many chars would be printed by using this string.
+ * {{ Couldn't we just use strlen? }}
+ */
+static int costcount;
+
+/*ARGSUSED*/
+       static int
+inc_costcount(c)
+       int c;
+{
+       costcount++;
+       return (c);
+}
+
+       static int
+cost(t)
+       char *t;
+{
+       costcount = 0;
+       tputs(t, sc_height, inc_costcount);
+       return (costcount);
+}
+
+/*
+ * Return the "best" of the two given termcap strings.
+ * The best, if both exist, is the one with the lower 
+ * cost (see cost() function).
+ */
+       static char *
+cheaper(t1, t2, def)
+       char *t1, *t2;
+       char *def;
+{
+       if (*t1 == '\0' && *t2 == '\0')
+       {
+               missing_cap = 1;
+               return (def);
+       }
+       if (*t1 == '\0')
+               return (t2);
+       if (*t2 == '\0')
+               return (t1);
+       if (cost(t1) < cost(t2))
+               return (t1);
+       return (t2);
+}
+
+       static void
+tmodes(incap, outcap, instr, outstr, def_instr, def_outstr, spp)
+       char *incap;
+       char *outcap;
+       char **instr;
+       char **outstr;
+       char *def_instr;
+       char *def_outstr;
+       char **spp;
+{
+       *instr = ltgetstr(incap, spp);
+       if (*instr == NULL)
+       {
+               /* Use defaults. */
+               *instr = def_instr;
+               *outstr = def_outstr;
+               return;
+       }
+
+       *outstr = ltgetstr(outcap, spp);
+       if (*outstr == NULL)
+               /* No specific out capability; use "me". */
+               *outstr = ltgetstr("me", spp);
+       if (*outstr == NULL)
+               /* Don't even have "me"; use a null string. */
+               *outstr = "";
+}
+
+#endif /* MSDOS_COMPILER */
+
+
+/*
+ * Below are the functions which perform all the 
+ * terminal-specific screen manipulation.
+ */
+
+
+#if MSDOS_COMPILER
+
+#if MSDOS_COMPILER==WIN32C
+       static void
+_settextposition(int row, int col)
+{
+       COORD cpos;
+       CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+       GetConsoleScreenBufferInfo(con_out, &csbi);
+       cpos.X = csbi.srWindow.Left + (col - 1);
+       cpos.Y = csbi.srWindow.Top + (row - 1);
+       SetConsoleCursorPosition(con_out, cpos);
+}
+#endif
+
+/*
+ * Initialize the screen to the correct color at startup.
+ */
+       static void
+initcolor()
+{
+       SETCOLORS(nm_fg_color, nm_bg_color);
+#if 0
+       /*
+        * This clears the screen at startup.  This is different from
+        * the behavior of other versions of less.  Disable it for now.
+        */
+       char *blanks;
+       int row;
+       int col;
+       
+       /*
+        * Create a complete, blank screen using "normal" colors.
+        */
+       SETCOLORS(nm_fg_color, nm_bg_color);
+       blanks = (char *) ecalloc(width+1, sizeof(char));
+       for (col = 0;  col < sc_width;  col++)
+               blanks[col] = ' ';
+       blanks[sc_width] = '\0';
+       for (row = 0;  row < sc_height;  row++)
+               _outtext(blanks);
+       free(blanks);
+#endif
+}
+#endif
+
+#if MSDOS_COMPILER==WIN32C
+
+/*
+ * Termcap-like init with a private win32 console.
+ */
+       static void
+win32_init_term()
+{
+       CONSOLE_SCREEN_BUFFER_INFO scr;
+       COORD size;
+
+       if (con_out_save == INVALID_HANDLE_VALUE)
+               return;
+
+       GetConsoleScreenBufferInfo(con_out_save, &scr);
+
+       if (con_out_ours == INVALID_HANDLE_VALUE)
+       {
+               /*
+                * Create our own screen buffer, so that we
+                * may restore the original when done.
+                */
+               con_out_ours = CreateConsoleScreenBuffer(
+                       GENERIC_WRITE | GENERIC_READ,
+                       FILE_SHARE_WRITE | FILE_SHARE_READ,
+                       (LPSECURITY_ATTRIBUTES) NULL,
+                       CONSOLE_TEXTMODE_BUFFER,
+                       (LPVOID) NULL);
+       }
+
+       size.X = scr.srWindow.Right - scr.srWindow.Left + 1;
+       size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1;
+       SetConsoleScreenBufferSize(con_out_ours, size);
+       SetConsoleActiveScreenBuffer(con_out_ours);
+       con_out = con_out_ours;
+}
+
+/*
+ * Restore the startup console.
+ */
+static void
+win32_deinit_term()
+{
+       if (con_out_save == INVALID_HANDLE_VALUE)
+               return;
+       if (quitting)
+               (void) CloseHandle(con_out_ours);
+       SetConsoleActiveScreenBuffer(con_out_save);
+       con_out = con_out_save;
+}
+
+#endif
+
+/*
+ * Initialize terminal
+ */
+       public void
+init()
+{
+#if !MSDOS_COMPILER
+       if (!no_init)
+               tputs(sc_init, sc_height, putchr);
+       if (!no_keypad)
+               tputs(sc_s_keypad, sc_height, putchr);
+       if (top_scroll) 
+       {
+               int i;
+
+               /*
+                * This is nice to terminals with no alternate screen,
+                * but with saved scrolled-off-the-top lines.  This way,
+                * no previous line is lost, but we start with a whole
+                * screen to ourself.
+                */
+               for (i = 1; i < sc_height; i++)
+                       putchr('\n');
+       } else
+               line_left();
+#else
+#if MSDOS_COMPILER==WIN32C
+       if (!no_init)
+               win32_init_term();
+#endif
+       initcolor();
+       flush();
+#endif
+       init_done = 1;
+}
+
+/*
+ * Deinitialize terminal
+ */
+       public void
+deinit()
+{
+       if (!init_done)
+               return;
+#if !MSDOS_COMPILER
+       if (!no_keypad)
+               tputs(sc_e_keypad, sc_height, putchr);
+       if (!no_init)
+               tputs(sc_deinit, sc_height, putchr);
+#else
+       /* Restore system colors. */
+       SETCOLORS(sy_fg_color, sy_bg_color);
+#if MSDOS_COMPILER==WIN32C
+       if (!no_init)
+               win32_deinit_term();
+#else
+       /* Need clreol to make SETCOLORS take effect. */
+       clreol();
+#endif
+#endif
+       init_done = 0;
+}
+
+/*
+ * Home cursor (move to upper left corner of screen).
+ */
+       public void
+home()
+{
+#if !MSDOS_COMPILER
+       tputs(sc_home, 1, putchr);
+#else
+       flush();
+       _settextposition(1,1);
+#endif
+}
+
+/*
+ * Add a blank line (called with cursor at home).
+ * Should scroll the display down.
+ */
+       public void
+add_line()
+{
+#if !MSDOS_COMPILER
+       tputs(sc_addline, sc_height, putchr);
+#else
+       flush();
+#if MSDOS_COMPILER==MSOFTC
+       _scrolltextwindow(_GSCROLLDOWN);
+       _settextposition(1,1);
+#else
+#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
+       movetext(1,1, sc_width,sc_height-1, 1,2);
+       gotoxy(1,1);
+       clreol();
+#else
+#if MSDOS_COMPILER==WIN32C
+    {
+       CHAR_INFO fillchar;
+       SMALL_RECT rcSrc, rcClip;
+       COORD new_org;
+       CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+       GetConsoleScreenBufferInfo(con_out,&csbi);
+
+       /* The clip rectangle is the entire visible screen. */
+       rcClip.Left = csbi.srWindow.Left;
+       rcClip.Top = csbi.srWindow.Top;
+       rcClip.Right = csbi.srWindow.Right;
+       rcClip.Bottom = csbi.srWindow.Bottom;
+
+       /* The source rectangle is the visible screen minus the last line. */
+       rcSrc = rcClip;
+       rcSrc.Bottom--;
+
+       /* Move the top left corner of the source window down one row. */
+       new_org.X = rcSrc.Left;
+       new_org.Y = rcSrc.Top + 1;
+
+       /* Fill the right character and attributes. */
+       fillchar.Char.AsciiChar = ' ';
+       curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
+       fillchar.Attributes = curr_attr;
+       ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar);
+       _settextposition(1,1);
+    }
+#endif
+#endif
+#endif
+#endif
+}
+
+#if 0
+/*
+ * Remove the n topmost lines and scroll everything below it in the 
+ * window upward.  This is needed to stop leaking the topmost line 
+ * into the scrollback buffer when we go down-one-line (in WIN32).
+ */
+       public void
+remove_top(n)
+       int n;
+{
+#if MSDOS_COMPILER==WIN32C
+       SMALL_RECT rcSrc, rcClip;
+       CHAR_INFO fillchar;
+       COORD new_org;
+       CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */
+
+       if (n >= sc_height - 1)
+       {
+               clear();
+               home();
+               return;
+       }
+
+       flush();
+
+       GetConsoleScreenBufferInfo(con_out, &csbi);
+
+       /* Get the extent of all-visible-rows-but-the-last. */
+       rcSrc.Left    = csbi.srWindow.Left;
+       rcSrc.Top     = csbi.srWindow.Top + n;
+       rcSrc.Right   = csbi.srWindow.Right;
+       rcSrc.Bottom  = csbi.srWindow.Bottom;
+
+       /* Get the clip rectangle. */
+       rcClip.Left   = rcSrc.Left;
+       rcClip.Top    = csbi.srWindow.Top;
+       rcClip.Right  = rcSrc.Right;
+       rcClip.Bottom = rcSrc.Bottom ;
+
+       /* Move the source window up n rows. */
+       new_org.X = rcSrc.Left;
+       new_org.Y = rcSrc.Top - n;
+
+       /* Fill the right character and attributes. */
+       fillchar.Char.AsciiChar = ' ';
+       curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
+       fillchar.Attributes = curr_attr;
+
+       ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar);
+
+       /* Position cursor on first blank line. */
+       goto_line(sc_height - n - 1);
+#endif
+}
+#endif
+
+#if MSDOS_COMPILER==WIN32C
+/*
+ * Clear the screen.
+ */
+       static void
+win32_clear()
+{
+       /*
+        * This will clear only the currently visible rows of the NT
+        * console buffer, which means none of the precious scrollback
+        * rows are touched making for faster scrolling.  Note that, if
+        * the window has fewer columns than the console buffer (i.e.
+        * there is a horizontal scrollbar as well), the entire width
+        * of the visible rows will be cleared.
+        */
+       COORD topleft;
+       DWORD nchars;
+       DWORD winsz;
+       CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+       /* get the number of cells in the current buffer */
+       GetConsoleScreenBufferInfo(con_out, &csbi);
+       winsz = csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
+       topleft.X = 0;
+       topleft.Y = csbi.srWindow.Top;
+
+       curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
+       FillConsoleOutputCharacter(con_out, ' ', winsz, topleft, &nchars);
+       FillConsoleOutputAttribute(con_out, curr_attr, winsz, topleft, &nchars);
+}
+
+/*
+ * Remove the n topmost lines and scroll everything below it in the 
+ * window upward.
+ */
+       public void
+win32_scroll_up(n)
+       int n;
+{
+       SMALL_RECT rcSrc, rcClip;
+       CHAR_INFO fillchar;
+       COORD topleft;
+       COORD new_org;
+       DWORD nchars;
+       DWORD size;
+       CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+       if (n <= 0)
+               return;
+
+       if (n >= sc_height - 1)
+       {
+               win32_clear();
+               _settextposition(1,1);
+               return;
+       }
+
+       /* Get the extent of what will remain visible after scrolling. */
+       GetConsoleScreenBufferInfo(con_out, &csbi);
+       rcSrc.Left    = csbi.srWindow.Left;
+       rcSrc.Top     = csbi.srWindow.Top + n;
+       rcSrc.Right   = csbi.srWindow.Right;
+       rcSrc.Bottom  = csbi.srWindow.Bottom;
+
+       /* Get the clip rectangle. */
+       rcClip.Left   = rcSrc.Left;
+       rcClip.Top    = csbi.srWindow.Top;
+       rcClip.Right  = rcSrc.Right;
+       rcClip.Bottom = rcSrc.Bottom ;
+
+       /* Move the source text to the top of the screen. */
+       new_org.X = rcSrc.Left;
+       new_org.Y = rcClip.Top;
+
+       /* Fill the right character and attributes. */
+       fillchar.Char.AsciiChar = ' ';
+       fillchar.Attributes = MAKEATTR(nm_fg_color, nm_bg_color);
+
+       /* Scroll the window. */
+       SetConsoleTextAttribute(con_out, fillchar.Attributes);
+       ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar);
+
+       /* Clear remaining lines at bottom. */
+       topleft.X = csbi.dwCursorPosition.X;
+       topleft.Y = rcSrc.Bottom - n;
+       size = (n * csbi.dwSize.X) + (rcSrc.Right - topleft.X);
+       FillConsoleOutputCharacter(con_out, ' ', size, topleft,
+               &nchars);
+       FillConsoleOutputAttribute(con_out, fillchar.Attributes, size, topleft,
+               &nchars);
+       SetConsoleTextAttribute(con_out, curr_attr);
+
+       /* Move cursor n lines up from where it was. */
+       csbi.dwCursorPosition.Y -= n;
+       SetConsoleCursorPosition(con_out, csbi.dwCursorPosition);
+}
+#endif
+
+/*
+ * Move cursor to lower left corner of screen.
+ */
+       public void
+lower_left()
+{
+#if !MSDOS_COMPILER
+       tputs(sc_lower_left, 1, putchr);
+#else
+       flush();
+       _settextposition(sc_height, 1);
+#endif
+}
+
+/*
+ * Move cursor to left position of current line.
+ */
+       public void
+line_left()
+{
+#if !MSDOS_COMPILER
+       tputs(sc_return, 1, putchr);
+#else
+       int row;
+       flush();
+#if MSDOS_COMPILER==WIN32C
+       {
+               CONSOLE_SCREEN_BUFFER_INFO scr;
+               GetConsoleScreenBufferInfo(con_out, &scr);
+               row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1;
+       }
+#else
+#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
+               row = wherey();
+#else
+       {
+               struct rccoord tpos = _gettextposition();
+               row = tpos.row;
+       }
+#endif
+#endif
+       _settextposition(row, 1);
+#endif
+}
+
+/*
+ * Check if the console size has changed and reset internals 
+ * (in lieu of SIGWINCH for WIN32).
+ */
+       public void
+check_winch()
+{
+#if MSDOS_COMPILER==WIN32C
+       CONSOLE_SCREEN_BUFFER_INFO scr;
+       COORD size;
+
+       if (con_out == INVALID_HANDLE_VALUE)
+               return;
+       flush();
+       GetConsoleScreenBufferInfo(con_out, &scr);
+       size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1;
+       size.X = scr.srWindow.Right - scr.srWindow.Left + 1;
+       if (size.Y != sc_height || size.X != sc_width)
+       {
+               sc_height = size.Y;
+               sc_width = size.X;
+               if (!no_init && con_out_ours == con_out)
+                       SetConsoleScreenBufferSize(con_out, size);
+               pos_init();
+               wscroll = (sc_height + 1) / 2;
+               screen_trashed = 1;
+       }
+#endif
+}
+
+/*
+ * Goto a specific line on the screen.
+ */
+       public void
+goto_line(slinenum)
+       int slinenum;
+{
+#if !MSDOS_COMPILER
+       tputs(tgoto(sc_move, 0, slinenum), 1, putchr);
+#else
+       flush();
+       _settextposition(slinenum+1, 1);
+#endif
+}
+
+#if MSDOS_COMPILER==MSOFTC || MSDOS_COMPILER==BORLANDC
+/*
+ * Create an alternate screen which is all white.
+ * This screen is used to create a "flash" effect, by displaying it
+ * briefly and then switching back to the normal screen.
+ * {{ Yuck!  There must be a better way to get a visual bell. }}
+ */
+       static void
+create_flash()
+{
+#if MSDOS_COMPILER==MSOFTC
+       struct videoconfig w;
+       char *blanks;
+       int row, col;
+       
+       _getvideoconfig(&w);
+       videopages = w.numvideopages;
+       if (videopages < 2)
+       {
+               at_enter(AT_STANDOUT);
+               at_exit();
+       } else
+       {
+               _setactivepage(1);
+               at_enter(AT_STANDOUT);
+               blanks = (char *) ecalloc(w.numtextcols, sizeof(char));
+               for (col = 0;  col < w.numtextcols;  col++)
+                       blanks[col] = ' ';
+               for (row = w.numtextrows;  row > 0;  row--)
+                       _outmem(blanks, w.numtextcols);
+               _setactivepage(0);
+               _setvisualpage(0);
+               free(blanks);
+               at_exit();
+       }
+#else
+#if MSDOS_COMPILER==BORLANDC
+       register int n;
+
+       whitescreen = (unsigned short *) 
+               malloc(sc_width * sc_height * sizeof(short));
+       if (whitescreen == NULL)
+               return;
+       for (n = 0;  n < sc_width * sc_height;  n++)
+               whitescreen[n] = 0x7020;
+#else
+#if MSDOS_COMPILER==WIN32C
+       register int n;
+
+       whitescreen = (WORD *)
+               malloc(sc_height * sc_width * sizeof(WORD));
+       if (whitescreen == NULL)
+               return;
+       /* Invert the standard colors. */
+       for (n = 0;  n < sc_width * sc_height;  n++)
+               whitescreen[n] = (WORD)((nm_fg_color << 4) | nm_bg_color);
+#endif
+#endif
+#endif
+       flash_created = 1;
+}
+#endif /* MSDOS_COMPILER */
+
+/*
+ * Output the "visual bell", if there is one.
+ */
+       public void
+vbell()
+{
+#if !MSDOS_COMPILER
+       if (*sc_visual_bell == '\0')
+               return;
+       tputs(sc_visual_bell, sc_height, putchr);
+#else
+#if MSDOS_COMPILER==DJGPPC
+       ScreenVisualBell();
+#else
+#if MSDOS_COMPILER==MSOFTC
+       /*
+        * Create a flash screen on the second video page.
+        * Switch to that page, then switch back.
+        */
+       if (!flash_created)
+               create_flash();
+       if (videopages < 2)
+               return;
+       _setvisualpage(1);
+       delay(100);
+       _setvisualpage(0);
+#else
+#if MSDOS_COMPILER==BORLANDC
+       unsigned short *currscreen;
+
+       /*
+        * Get a copy of the current screen.
+        * Display the flash screen.
+        * Then restore the old screen.
+        */
+       if (!flash_created)
+               create_flash();
+       if (whitescreen == NULL)
+               return;
+       currscreen = (unsigned short *) 
+               malloc(sc_width * sc_height * sizeof(short));
+       if (currscreen == NULL) return;
+       gettext(1, 1, sc_width, sc_height, currscreen);
+       puttext(1, 1, sc_width, sc_height, whitescreen);
+       delay(100);
+       puttext(1, 1, sc_width, sc_height, currscreen);
+       free(currscreen);
+#else
+#if MSDOS_COMPILER==WIN32C
+       /* paint screen with an inverse color */
+       clear();
+
+       /* leave it displayed for 100 msec. */
+       Sleep(100);
+
+       /* restore with a redraw */
+       repaint();
+#endif
+#endif
+#endif
+#endif
+#endif
+}
+
+/*
+ * Make a noise.
+ */
+       static void
+beep()
+{
+#if !MSDOS_COMPILER
+       putchr(CONTROL('G'));
+#else
+#if MSDOS_COMPILER==WIN32C
+       MessageBeep(0);
+#else
+       write(1, "\7", 1);
+#endif
+#endif
+}
+
+/*
+ * Ring the terminal bell.
+ */
+       public void
+bell()
+{
+       if (quiet == VERY_QUIET)
+               vbell();
+       else
+               beep();
+}
+
+/*
+ * Clear the screen.
+ */
+       public void
+clear()
+{
+#if !MSDOS_COMPILER
+       tputs(sc_clear, sc_height, putchr);
+#else
+       flush();
+#if MSDOS_COMPILER==WIN32C
+       win32_clear();
+#else
+       _clearscreen(_GCLEARSCREEN);
+#endif
+#endif
+}
+
+/*
+ * Clear from the cursor to the end of the cursor's line.
+ * {{ This must not move the cursor. }}
+ */
+       public void
+clear_eol()
+{
+#if !MSDOS_COMPILER
+       tputs(sc_eol_clear, 1, putchr);
+#else
+#if MSDOS_COMPILER==MSOFTC
+       short top, left;
+       short bot, right;
+       struct rccoord tpos;
+       
+       flush();
+       /*
+        * Save current state.
+        */
+       tpos = _gettextposition();
+       _gettextwindow(&top, &left, &bot, &right);
+       /*
+        * Set a temporary window to the current line,
+        * from the cursor's position to the right edge of the screen.
+        * Then clear that window.
+        */
+       _settextwindow(tpos.row, tpos.col, tpos.row, sc_width);
+       _clearscreen(_GWINDOW);
+       /*
+        * Restore state.
+        */
+       _settextwindow(top, left, bot, right);
+       _settextposition(tpos.row, tpos.col);
+#else
+#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
+       flush();
+       clreol();
+#else
+#if MSDOS_COMPILER==WIN32C
+       DWORD           nchars;
+       COORD           cpos;
+       CONSOLE_SCREEN_BUFFER_INFO scr;
+
+       flush();
+       memset(&scr, 0, sizeof(scr));
+       GetConsoleScreenBufferInfo(con_out, &scr);
+       cpos.X = scr.dwCursorPosition.X;
+       cpos.Y = scr.dwCursorPosition.Y;
+       curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
+       FillConsoleOutputAttribute(con_out, curr_attr,
+               scr.dwSize.X - cpos.X, cpos, &nchars);
+       FillConsoleOutputCharacter(con_out, ' ',
+               scr.dwSize.X - cpos.X, cpos, &nchars);
+#endif
+#endif
+#endif
+#endif
+}
+
+/*
+ * Clear the current line.
+ * Clear the screen if there's off-screen memory below the display.
+ */
+       static void
+clear_eol_bot()
+{
+#if MSDOS_COMPILER
+       clear_eol();
+#else
+       if (below_mem)
+               tputs(sc_eos_clear, 1, putchr);
+       else
+               tputs(sc_eol_clear, 1, putchr);
+#endif
+}
+
+/*
+ * Clear the bottom line of the display.
+ * Leave the cursor at the beginning of the bottom line.
+ */
+       public void
+clear_bot()
+{
+       /*
+        * If we're in a non-normal attribute mode, temporarily exit
+        * the mode while we do the clear.  Some terminals fill the
+        * cleared area with the current attribute.
+        */
+       if (oldbot)
+               lower_left();
+       else
+               line_left();
+
+       if (attrmode == AT_NORMAL)
+               clear_eol_bot();
+       else
+       {
+               int saved_attrmode = attrmode;
+
+               at_exit();
+               clear_eol_bot();
+               at_enter(saved_attrmode);
+       }
+}
+
+       public void
+at_enter(attr)
+       int attr;
+{
+       attr = apply_at_specials(attr);
+
+#if !MSDOS_COMPILER
+       /* The one with the most priority is last.  */
+       if (attr & AT_UNDERLINE)
+               tputs(sc_u_in, 1, putchr);
+       if (attr & AT_BOLD)
+               tputs(sc_b_in, 1, putchr);
+       if (attr & AT_BLINK)
+               tputs(sc_bl_in, 1, putchr);
+       if (attr & AT_STANDOUT)
+               tputs(sc_s_in, 1, putchr);
+#else
+       flush();
+       /* The one with the most priority is first.  */
+       if (attr & AT_STANDOUT)
+       {
+               SETCOLORS(so_fg_color, so_bg_color);
+       } else if (attr & AT_BLINK)
+       {
+               SETCOLORS(bl_fg_color, bl_bg_color);
+       }
+       else if (attr & AT_BOLD)
+       {
+               SETCOLORS(bo_fg_color, bo_bg_color);
+       }
+       else if (attr & AT_UNDERLINE)
+       {
+               SETCOLORS(ul_fg_color, ul_bg_color);
+       }
+#endif
+
+       attrmode = attr;
+}
+
+       public void
+at_exit()
+{
+#if !MSDOS_COMPILER
+       /* Undo things in the reverse order we did them.  */
+       if (attrmode & AT_STANDOUT)
+               tputs(sc_s_out, 1, putchr);
+       if (attrmode & AT_BLINK)
+               tputs(sc_bl_out, 1, putchr);
+       if (attrmode & AT_BOLD)
+               tputs(sc_b_out, 1, putchr);
+       if (attrmode & AT_UNDERLINE)
+               tputs(sc_u_out, 1, putchr);
+#else
+       flush();
+       SETCOLORS(nm_fg_color, nm_bg_color);
+#endif
+
+       attrmode = AT_NORMAL;
+}
+
+       public void
+at_switch(attr)
+       int attr;
+{
+       int new_attrmode = apply_at_specials(attr);
+       int ignore_modes = AT_ANSI;
+
+       if ((new_attrmode & ~ignore_modes) != (attrmode & ~ignore_modes))
+       {
+               at_exit();
+               at_enter(attr);
+       }
+}
+
+       public int
+is_at_equiv(attr1, attr2)
+       int attr1;
+       int attr2;
+{
+       attr1 = apply_at_specials(attr1);
+       attr2 = apply_at_specials(attr2);
+
+       return (attr1 == attr2);
+}
+
+       public int
+apply_at_specials(attr)
+       int attr;
+{
+       if (attr & AT_BINARY)
+               attr |= binattr;
+       if (attr & AT_HILITE)
+               attr |= AT_STANDOUT;
+       attr &= ~(AT_BINARY|AT_HILITE);
+
+       return attr;
+}
+
+#if 0 /* No longer used */
+/*
+ * Erase the character to the left of the cursor 
+ * and move the cursor left.
+ */
+       public void
+backspace()
+{
+#if !MSDOS_COMPILER
+       /* 
+        * Erase the previous character by overstriking with a space.
+        */
+       tputs(sc_backspace, 1, putchr);
+       putchr(' ');
+       tputs(sc_backspace, 1, putchr);
+#else
+#if MSDOS_COMPILER==MSOFTC
+       struct rccoord tpos;
+       
+       flush();
+       tpos = _gettextposition();
+       if (tpos.col <= 1)
+               return;
+       _settextposition(tpos.row, tpos.col-1);
+       _outtext(" ");
+       _settextposition(tpos.row, tpos.col-1);
+#else
+#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
+       cputs("\b");
+#else
+#if MSDOS_COMPILER==WIN32C
+       COORD cpos;
+       DWORD cChars;
+       CONSOLE_SCREEN_BUFFER_INFO scr;
+
+       flush();
+       GetConsoleScreenBufferInfo(con_out, &scr);
+       cpos = scr.dwCursorPosition;
+       if (cpos.X <= 0)
+               return;
+       cpos.X--;
+       SetConsoleCursorPosition(con_out, cpos);
+       FillConsoleOutputCharacter(con_out, (TCHAR)' ', 1, cpos, &cChars);
+       SetConsoleCursorPosition(con_out, cpos);
+#endif
+#endif
+#endif
+#endif
+}
+#endif /* 0 */
+
+/*
+ * Output a plain backspace, without erasing the previous char.
+ */
+       public void
+putbs()
+{
+#if !MSDOS_COMPILER
+       tputs(sc_backspace, 1, putchr);
+#else
+       int row, col;
+
+       flush();
+       {
+#if MSDOS_COMPILER==MSOFTC
+               struct rccoord tpos;
+               tpos = _gettextposition();
+               row = tpos.row;
+               col = tpos.col;
+#else
+#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
+               row = wherey();
+               col = wherex();
+#else
+#if MSDOS_COMPILER==WIN32C
+               CONSOLE_SCREEN_BUFFER_INFO scr;
+               GetConsoleScreenBufferInfo(con_out, &scr);
+               row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1;
+               col = scr.dwCursorPosition.X - scr.srWindow.Left + 1;
+#endif
+#endif
+#endif
+       }
+       if (col <= 1)
+               return;
+       _settextposition(row, col-1);
+#endif /* MSDOS_COMPILER */
+}
+
+#if MSDOS_COMPILER==WIN32C
+/*
+ * Determine whether an input character is waiting to be read.
+ */
+       static int
+win32_kbhit(tty)
+       HANDLE tty;
+{
+       INPUT_RECORD ip;
+       DWORD read;
+
+       if (keyCount > 0)
+               return (TRUE);
+
+       currentKey.ascii = 0;
+       currentKey.scan = 0;
+
+       /*
+        * Wait for a real key-down event, but
+        * ignore SHIFT and CONTROL key events.
+        */
+       do
+       {
+               PeekConsoleInput(tty, &ip, 1, &read);
+               if (read == 0)
+                       return (FALSE);
+               ReadConsoleInput(tty, &ip, 1, &read);
+       } while (ip.EventType != KEY_EVENT ||
+               ip.Event.KeyEvent.bKeyDown != TRUE ||
+               ip.Event.KeyEvent.wVirtualScanCode == 0 ||
+               ip.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT ||
+               ip.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL ||
+               ip.Event.KeyEvent.wVirtualKeyCode == VK_MENU);
+               
+       currentKey.ascii = ip.Event.KeyEvent.uChar.AsciiChar;
+       currentKey.scan = ip.Event.KeyEvent.wVirtualScanCode;
+       keyCount = ip.Event.KeyEvent.wRepeatCount;
+
+       if (ip.Event.KeyEvent.dwControlKeyState & 
+               (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
+       {
+               switch (currentKey.scan)
+               {
+               case PCK_ALT_E:     /* letter 'E' */
+                       currentKey.ascii = 0;
+                       break;
+               }
+       } else if (ip.Event.KeyEvent.dwControlKeyState & 
+               (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
+       {
+               switch (currentKey.scan)
+               {
+               case PCK_RIGHT: /* right arrow */
+                       currentKey.scan = PCK_CTL_RIGHT;
+                       break;
+               case PCK_LEFT: /* left arrow */
+                       currentKey.scan = PCK_CTL_LEFT;
+                       break;
+               case PCK_DELETE: /* delete */
+                       currentKey.scan = PCK_CTL_DELETE;
+                       break;
+               }
+       }
+       return (TRUE);
+}
+
+/*
+ * Read a character from the keyboard.
+ */
+       public char
+WIN32getch(tty)
+       int tty;
+{
+       int ascii;
+
+       if (pending_scancode)
+       {
+               pending_scancode = 0;
+               return ((char)(currentKey.scan & 0x00FF));
+       }
+
+       while (win32_kbhit((HANDLE)tty) == FALSE)
+       {
+               Sleep(20);
+               if (ABORT_SIGS())
+                       return ('\003');
+               continue;
+       }
+       keyCount --;
+       ascii = currentKey.ascii;
+       /*
+        * On PC's, the extended keys return a 2 byte sequence beginning 
+        * with '00', so if the ascii code is 00, the next byte will be 
+        * the lsb of the scan code.
+        */
+       pending_scancode = (ascii == 0x00);
+       return ((char)ascii);
+}
+#endif
+
+#if MSDOS_COMPILER
+/*
+ */
+       public void
+WIN32setcolors(fg, bg)
+       int fg;
+       int bg;
+{
+       SETCOLORS(fg, bg);
+}
+
+/*
+ */
+       public void
+WIN32textout(text, len)
+       char *text;
+       int len;
+{
+#if MSDOS_COMPILER==WIN32C
+       DWORD written;
+       WriteConsole(con_out, text, len, &written, NULL);
+#else
+       char c = text[len];
+       text[len] = '\0';
+       cputs(text);
+       text[len] = c;
+#endif
+}
+#endif
diff --git a/thirdparty/less/scrsize.c b/thirdparty/less/scrsize.c
new file mode 100644 (file)
index 0000000..05d041e
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+/*
+ * This program is used to determine the screen dimensions on OS/2 systems.
+ * Adapted from code written by Kyosuke Tokoro (NBG01720@nifty.ne.jp).
+ */
+
+/*
+ * When I wrote this routine, I consulted some part of the source code 
+ * of the xwininfo utility by X Consortium.
+ *
+ * Copyright (c) 1987, X Consortium
+ *
+ * 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
+ * X CONSORTIUM 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.
+ *
+ * Except as contained in this notice, the name of the X Consortium shall not
+ * be used in advertising or otherwise to promote the sale, use or other
+ * dealings in this Software without prior written authorization from the X
+ * Consortium.
+ */
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+static int get_winsize(dpy, window, p_width, p_height)
+       Display *dpy;
+       Window window;
+       int *p_width;
+       int *p_height;
+{
+       XWindowAttributes win_attributes;
+       XSizeHints hints;
+       long longjunk;
+
+       if (!XGetWindowAttributes(dpy, window, &win_attributes))
+               return 1;
+       if (!XGetWMNormalHints(dpy, window, &hints, &longjunk))
+               return 1;
+       if (!(hints.flags & PResizeInc))
+               return 1;
+       if (hints.width_inc == 0 || hints.height_inc == 0)
+               return 1;
+       if (!(hints.flags & (PBaseSize|PMinSize)))
+               return 1;
+       if (hints.flags & PBaseSize)
+       {
+               win_attributes.width -= hints.base_width;
+               win_attributes.height -= hints.base_height;
+       } else
+       {
+               win_attributes.width -= hints.min_width;
+               win_attributes.height -= hints.min_height;
+       }
+       *p_width = win_attributes.width / hints.width_inc;
+       *p_height = win_attributes.height / hints.height_inc;
+       return 0;
+}
+
+int main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       char *cp;
+       Display *dpy;
+       int size[2];
+
+       _scrsize(size);
+       cp = getenv("WINDOWID");
+       if (cp != NULL)
+       {
+               dpy = XOpenDisplay(NULL);
+               if (dpy != NULL)
+               {
+                       get_winsize(dpy, (Window) atol(cp), &size[0], &size[1]);
+                       XCloseDisplay(dpy);
+               }
+       }
+       printf("%i %i\n", size[0], size[1]);
+       return (0);
+}
diff --git a/thirdparty/less/search.c b/thirdparty/less/search.c
new file mode 100644 (file)
index 0000000..143779e
--- /dev/null
@@ -0,0 +1,1211 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Routines to search a file for a pattern.
+ */
+
+#include "less.h"
+#include "pattern.h"
+#include "position.h"
+#include "charset.h"
+
+#define        MINPOS(a,b)     (((a) < (b)) ? (a) : (b))
+#define        MAXPOS(a,b)     (((a) > (b)) ? (a) : (b))
+
+extern int sigs;
+extern int how_search;
+extern int caseless;
+extern int linenums;
+extern int sc_height;
+extern int jump_sline;
+extern int bs_mode;
+extern int ctldisp;
+extern int status_col;
+extern void * constant ml_search;
+extern POSITION start_attnpos;
+extern POSITION end_attnpos;
+extern int utf_mode;
+extern int screen_trashed;
+#if HILITE_SEARCH
+extern int hilite_search;
+extern int size_linebuf;
+extern int squished;
+extern int can_goto_line;
+static int hide_hilite;
+static POSITION prep_startpos;
+static POSITION prep_endpos;
+static int is_caseless;
+static int is_ucase_pattern;
+
+struct hilite
+{
+       struct hilite *hl_next;
+       POSITION hl_startpos;
+       POSITION hl_endpos;
+};
+static struct hilite hilite_anchor = { NULL, NULL_POSITION, NULL_POSITION };
+static struct hilite filter_anchor = { NULL, NULL_POSITION, NULL_POSITION };
+#define        hl_first        hl_next
+#endif
+
+/*
+ * These are the static variables that represent the "remembered"
+ * search pattern and filter pattern.
+ */
+struct pattern_info {
+       DEFINE_PATTERN(compiled);
+       char* text;
+       int search_type;
+};
+       
+static struct pattern_info search_info;
+static struct pattern_info filter_info;
+
+/*
+ * Are there any uppercase letters in this string?
+ */
+       static int
+is_ucase(str)
+       char *str;
+{
+       char *str_end = str + strlen(str);
+       LWCHAR ch;
+
+       while (str < str_end)
+       {
+               ch = step_char(&str, +1, str_end);
+               if (IS_UPPER(ch))
+                       return (1);
+       }
+       return (0);
+}
+
+/*
+ * Compile and save a search pattern.
+ */
+       static int
+set_pattern(info, pattern, search_type)
+       struct pattern_info *info;
+       char *pattern;
+       int search_type;
+{
+       if (pattern == NULL)
+               CLEAR_PATTERN(search_info.compiled);
+       else if (compile_pattern(pattern, search_type, &info->compiled) < 0)
+               return -1;
+       /* Pattern compiled successfully; save the text too. */
+       if (info->text != NULL)
+               free(info->text);
+       info->text = NULL;
+       if (pattern != NULL)
+       {
+               info->text = (char *) ecalloc(1, strlen(pattern)+1);
+               strcpy(info->text, pattern);
+       }
+       info->search_type = search_type;
+
+       /*
+        * Ignore case if -I is set OR
+        * -i is set AND the pattern is all lowercase.
+        */
+       is_ucase_pattern = is_ucase(pattern);
+       if (is_ucase_pattern && caseless != OPT_ONPLUS)
+               is_caseless = 0;
+       else
+               is_caseless = caseless;
+       return 0;
+}
+
+/*
+ * Discard a saved pattern.
+ */
+       static void
+clear_pattern(info)
+       struct pattern_info *info;
+{
+       if (info->text != NULL)
+               free(info->text);
+       info->text = NULL;
+       uncompile_pattern(&info->compiled);
+}
+
+/*
+ * Initialize saved pattern to nothing.
+ */
+       static void
+init_pattern(info)
+       struct pattern_info *info;
+{
+       CLEAR_PATTERN(info->compiled);
+       info->text = NULL;
+       info->search_type = 0;
+}
+
+/*
+ * Initialize search variables.
+ */
+       public void
+init_search()
+{
+       init_pattern(&search_info);
+       init_pattern(&filter_info);
+}
+
+/*
+ * Determine which text conversions to perform before pattern matching.
+ */
+       static int
+get_cvt_ops()
+{
+       int ops = 0;
+       if (is_caseless || bs_mode == BS_SPECIAL)
+       {
+               if (is_caseless) 
+                       ops |= CVT_TO_LC;
+               if (bs_mode == BS_SPECIAL)
+                       ops |= CVT_BS;
+               if (bs_mode != BS_CONTROL)
+                       ops |= CVT_CRLF;
+       } else if (bs_mode != BS_CONTROL)
+       {
+               ops |= CVT_CRLF;
+       }
+       if (ctldisp == OPT_ONPLUS)
+               ops |= CVT_ANSI;
+       return (ops);
+}
+
+/*
+ * Is there a previous (remembered) search pattern?
+ */
+       static int
+prev_pattern(info)
+       struct pattern_info *info;
+{
+       if (info->search_type & SRCH_NO_REGEX)
+               return (info->text != NULL);
+       return (!is_null_pattern(info->compiled));
+}
+
+#if HILITE_SEARCH
+/*
+ * Repaint the hilites currently displayed on the screen.
+ * Repaint each line which contains highlighted text.
+ * If on==0, force all hilites off.
+ */
+       public void
+repaint_hilite(on)
+       int on;
+{
+       int slinenum;
+       POSITION pos;
+       POSITION epos;
+       int save_hide_hilite;
+
+       if (squished)
+               repaint();
+
+       save_hide_hilite = hide_hilite;
+       if (!on)
+       {
+               if (hide_hilite)
+                       return;
+               hide_hilite = 1;
+       }
+
+       if (!can_goto_line)
+       {
+               repaint();
+               hide_hilite = save_hide_hilite;
+               return;
+       }
+
+       for (slinenum = TOP;  slinenum < TOP + sc_height-1;  slinenum++)
+       {
+               pos = position(slinenum);
+               if (pos == NULL_POSITION)
+                       continue;
+               epos = position(slinenum+1);
+               (void) forw_line(pos);
+               goto_line(slinenum);
+               put_line();
+       }
+       lower_left();
+       hide_hilite = save_hide_hilite;
+}
+
+/*
+ * Clear the attn hilite.
+ */
+       public void
+clear_attn()
+{
+       int slinenum;
+       POSITION old_start_attnpos;
+       POSITION old_end_attnpos;
+       POSITION pos;
+       POSITION epos;
+       int moved = 0;
+
+       if (start_attnpos == NULL_POSITION)
+               return;
+       old_start_attnpos = start_attnpos;
+       old_end_attnpos = end_attnpos;
+       start_attnpos = end_attnpos = NULL_POSITION;
+
+       if (!can_goto_line)
+       {
+               repaint();
+               return;
+       }
+       if (squished)
+               repaint();
+
+       for (slinenum = TOP;  slinenum < TOP + sc_height-1;  slinenum++)
+       {
+               pos = position(slinenum);
+               if (pos == NULL_POSITION)
+                       continue;
+               epos = position(slinenum+1);
+               if (pos < old_end_attnpos &&
+                    (epos == NULL_POSITION || epos > old_start_attnpos))
+               {
+                       (void) forw_line(pos);
+                       goto_line(slinenum);
+                       put_line();
+                       moved = 1;
+               }
+       }
+       if (moved)
+               lower_left();
+}
+#endif
+
+/*
+ * Hide search string highlighting.
+ */
+       public void
+undo_search()
+{
+       if (!prev_pattern(&search_info))
+       {
+               error("No previous regular expression", NULL_PARG);
+               return;
+       }
+#if HILITE_SEARCH
+       hide_hilite = !hide_hilite;
+       repaint_hilite(1);
+#endif
+}
+
+#if HILITE_SEARCH
+/*
+ * Clear the hilite list.
+ */
+       public void
+clr_hlist(anchor)
+       struct hilite *anchor;
+{
+       struct hilite *hl;
+       struct hilite *nexthl;
+
+       for (hl = anchor->hl_first;  hl != NULL;  hl = nexthl)
+       {
+               nexthl = hl->hl_next;
+               free((void*)hl);
+       }
+       anchor->hl_first = NULL;
+       prep_startpos = prep_endpos = NULL_POSITION;
+}
+
+       public void
+clr_hilite()
+{
+       clr_hlist(&hilite_anchor);
+}
+
+       public void
+clr_filter()
+{
+       clr_hlist(&filter_anchor);
+}
+
+/*
+ * Should any characters in a specified range be highlighted?
+ */
+       static int
+is_hilited_range(pos, epos)
+       POSITION pos;
+       POSITION epos;
+{
+       struct hilite *hl;
+
+       /*
+        * Look at each highlight and see if any part of it falls in the range.
+        */
+       for (hl = hilite_anchor.hl_first;  hl != NULL;  hl = hl->hl_next)
+       {
+               if (hl->hl_endpos > pos &&
+                   (epos == NULL_POSITION || epos > hl->hl_startpos))
+                       return (1);
+       }
+       return (0);
+}
+
+/* 
+ * Is a line "filtered" -- that is, should it be hidden?
+ */
+       public int
+is_filtered(pos)
+       POSITION pos;
+{
+       struct hilite *hl;
+
+       if (ch_getflags() & CH_HELPFILE)
+               return (0);
+
+       /*
+        * Look at each filter and see if the start position
+        * equals the start position of the line.
+        */
+       for (hl = filter_anchor.hl_first;  hl != NULL;  hl = hl->hl_next)
+       {
+               if (hl->hl_startpos == pos)
+                       return (1);
+       }
+       return (0);
+}
+
+/*
+ * Should any characters in a specified range be highlighted?
+ * If nohide is nonzero, don't consider hide_hilite.
+ */
+       public int
+is_hilited(pos, epos, nohide, p_matches)
+       POSITION pos;
+       POSITION epos;
+       int nohide;
+       int *p_matches;
+{
+       int match;
+
+       if (p_matches != NULL)
+               *p_matches = 0;
+
+       if (!status_col &&
+           start_attnpos != NULL_POSITION && 
+           pos < end_attnpos &&
+            (epos == NULL_POSITION || epos > start_attnpos))
+               /*
+                * The attn line overlaps this range.
+                */
+               return (1);
+
+       match = is_hilited_range(pos, epos);
+       if (!match)
+               return (0);
+
+       if (p_matches != NULL)
+               /*
+                * Report matches, even if we're hiding highlights.
+                */
+               *p_matches = 1;
+
+       if (hilite_search == 0)
+               /*
+                * Not doing highlighting.
+                */
+               return (0);
+
+       if (!nohide && hide_hilite)
+               /*
+                * Highlighting is hidden.
+                */
+               return (0);
+
+       return (1);
+}
+
+/*
+ * Add a new hilite to a hilite list.
+ */
+       static void
+add_hilite(anchor, hl)
+       struct hilite *anchor;
+       struct hilite *hl;
+{
+       struct hilite *ihl;
+
+       /*
+        * Hilites are sorted in the list; find where new one belongs.
+        * Insert new one after ihl.
+        */
+       for (ihl = anchor;  ihl->hl_next != NULL;  ihl = ihl->hl_next)
+       {
+               if (ihl->hl_next->hl_startpos > hl->hl_startpos)
+                       break;
+       }
+
+       /*
+        * Truncate hilite so it doesn't overlap any existing ones
+        * above and below it.
+        */
+       if (ihl != anchor)
+               hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos);
+       if (ihl->hl_next != NULL)
+               hl->hl_endpos = MINPOS(hl->hl_endpos, ihl->hl_next->hl_startpos);
+       if (hl->hl_startpos >= hl->hl_endpos)
+       {
+               /*
+                * Hilite was truncated out of existence.
+                */
+               free(hl);
+               return;
+       }
+       hl->hl_next = ihl->hl_next;
+       ihl->hl_next = hl;
+}
+
+/*
+ * Make a hilite for each string in a physical line which matches 
+ * the current pattern.
+ * sp,ep delimit the first match already found.
+ */
+       static void
+hilite_line(linepos, line, line_len, chpos, sp, ep, cvt_ops)
+       POSITION linepos;
+       char *line;
+       int line_len;
+       int *chpos;
+       char *sp;
+       char *ep;
+       int cvt_ops;
+{
+       char *searchp;
+       char *line_end = line + line_len;
+       struct hilite *hl;
+
+       if (sp == NULL || ep == NULL)
+               return;
+       /*
+        * sp and ep delimit the first match in the line.
+        * Mark the corresponding file positions, then
+        * look for further matches and mark them.
+        * {{ This technique, of calling match_pattern on subsequent
+        *    substrings of the line, may mark more than is correct
+        *    if the pattern starts with "^".  This bug is fixed
+        *    for those regex functions that accept a notbol parameter
+        *    (currently POSIX, PCRE and V8-with-regexec2). }}
+        */
+       searchp = line;
+       do {
+               if (ep > sp)
+               {
+                       hl = (struct hilite *) ecalloc(1, sizeof(struct hilite));
+                       hl->hl_startpos = linepos + chpos[sp-line];
+                       hl->hl_endpos = linepos + chpos[ep-line];
+                       add_hilite(&hilite_anchor, hl);
+               }
+               /*
+                * If we matched more than zero characters,
+                * move to the first char after the string we matched.
+                * If we matched zero, just move to the next char.
+                */
+               if (ep > searchp)
+                       searchp = ep;
+               else if (searchp != line_end)
+                       searchp++;
+               else /* end of line */
+                       break;
+       } while (match_pattern(search_info.compiled, search_info.text,
+                       searchp, line_end - searchp, &sp, &ep, 1, search_info.search_type));
+}
+#endif
+
+/*
+ * Change the caseless-ness of searches.  
+ * Updates the internal search state to reflect a change in the -i flag.
+ */
+       public void
+chg_caseless()
+{
+       if (!is_ucase_pattern)
+               /*
+                * Pattern did not have uppercase.
+                * Just set the search caselessness to the global caselessness.
+                */
+               is_caseless = caseless;
+       else
+               /*
+                * Pattern did have uppercase.
+                * Discard the pattern; we can't change search caselessness now.
+                */
+               clear_pattern(&search_info);
+}
+
+#if HILITE_SEARCH
+/*
+ * Find matching text which is currently on screen and highlight it.
+ */
+       static void
+hilite_screen()
+{
+       struct scrpos scrpos;
+
+       get_scrpos(&scrpos);
+       if (scrpos.pos == NULL_POSITION)
+               return;
+       prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1);
+       repaint_hilite(1);
+}
+
+/*
+ * Change highlighting parameters.
+ */
+       public void
+chg_hilite()
+{
+       /*
+        * Erase any highlights currently on screen.
+        */
+       clr_hilite();
+       hide_hilite = 0;
+
+       if (hilite_search == OPT_ONPLUS)
+               /*
+                * Display highlights.
+                */
+               hilite_screen();
+}
+#endif
+
+/*
+ * Figure out where to start a search.
+ */
+       static POSITION
+search_pos(search_type)
+       int search_type;
+{
+       POSITION pos;
+       int linenum;
+
+       if (empty_screen())
+       {
+               /*
+                * Start at the beginning (or end) of the file.
+                * The empty_screen() case is mainly for 
+                * command line initiated searches;
+                * for example, "+/xyz" on the command line.
+                * Also for multi-file (SRCH_PAST_EOF) searches.
+                */
+               if (search_type & SRCH_FORW)
+               {
+                       pos = ch_zero();
+               } else
+               {
+                       pos = ch_length();
+                       if (pos == NULL_POSITION)
+                       {
+                               (void) ch_end_seek();
+                               pos = ch_length();
+                       }
+               }
+               linenum = 0;
+       } else 
+       {
+               int add_one = 0;
+
+               if (how_search == OPT_ON)
+               {
+                       /*
+                        * Search does not include current screen.
+                        */
+                       if (search_type & SRCH_FORW)
+                               linenum = BOTTOM_PLUS_ONE;
+                       else
+                               linenum = TOP;
+               } else if (how_search == OPT_ONPLUS && !(search_type & SRCH_AFTER_TARGET))
+               {
+                       /*
+                        * Search includes all of displayed screen.
+                        */
+                       if (search_type & SRCH_FORW)
+                               linenum = TOP;
+                       else
+                               linenum = BOTTOM_PLUS_ONE;
+               } else 
+               {
+                       /*
+                        * Search includes the part of current screen beyond the jump target.
+                        * It starts at the jump target (if searching backwards),
+                        * or at the jump target plus one (if forwards).
+                        */
+                       linenum = jump_sline;
+                       if (search_type & SRCH_FORW) 
+                           add_one = 1;
+               }
+               linenum = adjsline(linenum);
+               pos = position(linenum);
+               if (add_one)
+                       pos = forw_raw_line(pos, (char **)NULL, (int *)NULL);
+       }
+
+       /*
+        * If the line is empty, look around for a plausible starting place.
+        */
+       if (search_type & SRCH_FORW) 
+       {
+           while (pos == NULL_POSITION)
+           {
+               if (++linenum >= sc_height)
+                   break;
+               pos = position(linenum);
+           }
+       } else 
+       {
+           while (pos == NULL_POSITION)
+           {
+               if (--linenum < 0)
+                   break;
+               pos = position(linenum);
+           }
+       }
+       return (pos);
+}
+
+/*
+ * Search a subset of the file, specified by start/end position.
+ */
+       static int
+search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos)
+       POSITION pos;
+       POSITION endpos;
+       int search_type;
+       int matches;
+       int maxlines;
+       POSITION *plinepos;
+       POSITION *pendpos;
+{
+       char *line;
+       char *cline;
+       int line_len;
+       LINENUM linenum;
+       char *sp, *ep;
+       int line_match;
+       int cvt_ops;
+       int cvt_len;
+       int *chpos;
+       POSITION linepos, oldpos;
+
+       linenum = find_linenum(pos);
+       oldpos = pos;
+       for (;;)
+       {
+               /*
+                * Get lines until we find a matching one or until
+                * we hit end-of-file (or beginning-of-file if we're 
+                * going backwards), or until we hit the end position.
+                */
+               if (ABORT_SIGS())
+               {
+                       /*
+                        * A signal aborts the search.
+                        */
+                       return (-1);
+               }
+
+               if ((endpos != NULL_POSITION && pos >= endpos) || maxlines == 0)
+               {
+                       /*
+                        * Reached end position without a match.
+                        */
+                       if (pendpos != NULL)
+                               *pendpos = pos;
+                       return (matches);
+               }
+               if (maxlines > 0)
+                       maxlines--;
+
+               if (search_type & SRCH_FORW)
+               {
+                       /*
+                        * Read the next line, and save the 
+                        * starting position of that line in linepos.
+                        */
+                       linepos = pos;
+                       pos = forw_raw_line(pos, &line, &line_len);
+                       if (linenum != 0)
+                               linenum++;
+               } else
+               {
+                       /*
+                        * Read the previous line and save the
+                        * starting position of that line in linepos.
+                        */
+                       pos = back_raw_line(pos, &line, &line_len);
+                       linepos = pos;
+                       if (linenum != 0)
+                               linenum--;
+               }
+
+               if (pos == NULL_POSITION)
+               {
+                       /*
+                        * Reached EOF/BOF without a match.
+                        */
+                       if (pendpos != NULL)
+                               *pendpos = oldpos;
+                       return (matches);
+               }
+
+               /*
+                * If we're using line numbers, we might as well
+                * remember the information we have now (the position
+                * and line number of the current line).
+                * Don't do it for every line because it slows down
+                * the search.  Remember the line number only if
+                * we're "far" from the last place we remembered it.
+                */
+               if (linenums && abs((int)(pos - oldpos)) > 2048)
+                       add_lnum(linenum, pos);
+               oldpos = pos;
+
+               if (is_filtered(linepos))
+                       continue;
+
+               /*
+                * If it's a caseless search, convert the line to lowercase.
+                * If we're doing backspace processing, delete backspaces.
+                */
+               cvt_ops = get_cvt_ops();
+               cvt_len = cvt_length(line_len, cvt_ops);
+               cline = (char *) ecalloc(1, cvt_len);
+               chpos = cvt_alloc_chpos(cvt_len);
+               cvt_text(cline, line, chpos, &line_len, cvt_ops);
+
+#if HILITE_SEARCH
+               /*
+                * Check to see if the line matches the filter pattern.
+                * If so, add an entry to the filter list.
+                */
+               if ((search_type & SRCH_FIND_ALL) && prev_pattern(&filter_info)) {
+                       int line_filter = match_pattern(filter_info.compiled, filter_info.text,
+                               cline, line_len, &sp, &ep, 0, filter_info.search_type);
+                       if (line_filter)
+                       {
+                               struct hilite *hl = (struct hilite *)
+                                       ecalloc(1, sizeof(struct hilite));
+                               hl->hl_startpos = linepos;
+                               hl->hl_endpos = pos;
+                               add_hilite(&filter_anchor, hl);
+                       }
+               }
+#endif
+
+               /*
+                * Test the next line to see if we have a match.
+                * We are successful if we either want a match and got one,
+                * or if we want a non-match and got one.
+                */
+               if (prev_pattern(&search_info))
+               {
+                       line_match = match_pattern(search_info.compiled, search_info.text,
+                               cline, line_len, &sp, &ep, 0, search_type);
+                       if (line_match)
+                       {
+                               /*
+                                * Got a match.
+                                */
+                               if (search_type & SRCH_FIND_ALL)
+                               {
+#if HILITE_SEARCH
+                                       /*
+                                        * We are supposed to find all matches in the range.
+                                        * Just add the matches in this line to the 
+                                        * hilite list and keep searching.
+                                        */
+                                       hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops);
+#endif
+                               } else if (--matches <= 0)
+                               {
+                                       /*
+                                        * Found the one match we're looking for.
+                                        * Return it.
+                                        */
+#if HILITE_SEARCH
+                                       if (hilite_search == OPT_ON)
+                                       {
+                                               /*
+                                                * Clear the hilite list and add only
+                                                * the matches in this one line.
+                                                */
+                                               clr_hilite();
+                                               hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops);
+                                       }
+#endif
+                                       free(cline);
+                                       free(chpos);
+                                       if (plinepos != NULL)
+                                               *plinepos = linepos;
+                                       return (0);
+                               }
+                       }
+               }
+               free(cline);
+               free(chpos);
+       }
+}
+
+/*
+ * search for a pattern in history. If found, compile that pattern.
+ */
+       static int 
+hist_pattern(search_type) 
+       int search_type;
+{
+#if CMD_HISTORY
+       char *pattern;
+
+       set_mlist(ml_search, 0);
+       pattern = cmd_lastpattern();
+       if (pattern == NULL)
+               return (0);
+
+       if (set_pattern(&search_info, pattern, search_type) < 0)
+               return (0);
+
+#if HILITE_SEARCH
+       if (hilite_search == OPT_ONPLUS && !hide_hilite)
+               hilite_screen();
+#endif
+
+       return (1);
+#else /* CMD_HISTORY */
+       return (0);
+#endif /* CMD_HISTORY */
+}
+
+/*
+ * Search for the n-th occurrence of a specified pattern, 
+ * either forward or backward.
+ * Return the number of matches not yet found in this file
+ * (that is, n minus the number of matches found).
+ * Return -1 if the search should be aborted.
+ * Caller may continue the search in another file 
+ * if less than n matches are found in this file.
+ */
+       public int
+search(search_type, pattern, n)
+       int search_type;
+       char *pattern;
+       int n;
+{
+       POSITION pos;
+
+       if (pattern == NULL || *pattern == '\0')
+       {
+               /*
+                * A null pattern means use the previously compiled pattern.
+                */
+               search_type |= SRCH_AFTER_TARGET;
+               if (!prev_pattern(&search_info) && !hist_pattern(search_type))
+               {
+                       error("No previous regular expression", NULL_PARG);
+                       return (-1);
+               }
+               if ((search_type & SRCH_NO_REGEX) != 
+                     (search_info.search_type & SRCH_NO_REGEX))
+               {
+                       error("Please re-enter search pattern", NULL_PARG);
+                       return -1;
+               }
+#if HILITE_SEARCH
+               if (hilite_search == OPT_ON)
+               {
+                       /*
+                        * Erase the highlights currently on screen.
+                        * If the search fails, we'll redisplay them later.
+                        */
+                       repaint_hilite(0);
+               }
+               if (hilite_search == OPT_ONPLUS && hide_hilite)
+               {
+                       /*
+                        * Highlight any matches currently on screen,
+                        * before we actually start the search.
+                        */
+                       hide_hilite = 0;
+                       hilite_screen();
+               }
+               hide_hilite = 0;
+#endif
+       } else
+       {
+               /*
+                * Compile the pattern.
+                */
+               if (set_pattern(&search_info, pattern, search_type) < 0)
+                       return (-1);
+#if HILITE_SEARCH
+               if (hilite_search)
+               {
+                       /*
+                        * Erase the highlights currently on screen.
+                        * Also permanently delete them from the hilite list.
+                        */
+                       repaint_hilite(0);
+                       hide_hilite = 0;
+                       clr_hilite();
+               }
+               if (hilite_search == OPT_ONPLUS)
+               {
+                       /*
+                        * Highlight any matches currently on screen,
+                        * before we actually start the search.
+                        */
+                       hilite_screen();
+               }
+#endif
+       }
+
+       /*
+        * Figure out where to start the search.
+        */
+       pos = search_pos(search_type);
+       if (pos == NULL_POSITION)
+       {
+               /*
+                * Can't find anyplace to start searching from.
+                */
+               if (search_type & SRCH_PAST_EOF)
+                       return (n);
+               /* repaint(); -- why was this here? */
+               error("Nothing to search", NULL_PARG);
+               return (-1);
+       }
+
+       n = search_range(pos, NULL_POSITION, search_type, n, -1,
+                       &pos, (POSITION*)NULL);
+       if (n != 0)
+       {
+               /*
+                * Search was unsuccessful.
+                */
+#if HILITE_SEARCH
+               if (hilite_search == OPT_ON && n > 0)
+                       /*
+                        * Redisplay old hilites.
+                        */
+                       repaint_hilite(1);
+#endif
+               return (n);
+       }
+
+       if (!(search_type & SRCH_NO_MOVE))
+       {
+               /*
+                * Go to the matching line.
+                */
+               jump_loc(pos, jump_sline);
+       }
+
+#if HILITE_SEARCH
+       if (hilite_search == OPT_ON)
+               /*
+                * Display new hilites in the matching line.
+                */
+               repaint_hilite(1);
+#endif
+       return (0);
+}
+
+
+#if HILITE_SEARCH
+/*
+ * Prepare hilites in a given range of the file.
+ *
+ * The pair (prep_startpos,prep_endpos) delimits a contiguous region
+ * of the file that has been "prepared"; that is, scanned for matches for
+ * the current search pattern, and hilites have been created for such matches.
+ * If prep_startpos == NULL_POSITION, the prep region is empty.
+ * If prep_endpos == NULL_POSITION, the prep region extends to EOF.
+ * prep_hilite asks that the range (spos,epos) be covered by the prep region.
+ */
+       public void
+prep_hilite(spos, epos, maxlines)
+       POSITION spos;
+       POSITION epos;
+       int maxlines;
+{
+       POSITION nprep_startpos = prep_startpos;
+       POSITION nprep_endpos = prep_endpos;
+       POSITION new_epos;
+       POSITION max_epos;
+       int result;
+       int i;
+
+/*
+ * Search beyond where we're asked to search, so the prep region covers
+ * more than we need.  Do one big search instead of a bunch of small ones.
+ */
+#define        SEARCH_MORE (3*size_linebuf)
+
+       if (!prev_pattern(&search_info) && !is_filtering())
+               return;
+
+       /*
+        * If we're limited to a max number of lines, figure out the
+        * file position we should stop at.
+        */
+       if (maxlines < 0)
+               max_epos = NULL_POSITION;
+       else
+       {
+               max_epos = spos;
+               for (i = 0;  i < maxlines;  i++)
+                       max_epos = forw_raw_line(max_epos, (char **)NULL, (int *)NULL);
+       }
+
+       /*
+        * Find two ranges:
+        * The range that we need to search (spos,epos); and the range that
+        * the "prep" region will then cover (nprep_startpos,nprep_endpos).
+        */
+
+       if (prep_startpos == NULL_POSITION ||
+           (epos != NULL_POSITION && epos < prep_startpos) ||
+           spos > prep_endpos)
+       {
+               /*
+                * New range is not contiguous with old prep region.
+                * Discard the old prep region and start a new one.
+                */
+               clr_hilite();
+               clr_filter();
+               if (epos != NULL_POSITION)
+                       epos += SEARCH_MORE;
+               nprep_startpos = spos;
+       } else
+       {
+               /*
+                * New range partially or completely overlaps old prep region.
+                */
+               if (epos == NULL_POSITION)
+               {
+                       /*
+                        * New range goes to end of file.
+                        */
+                       ;
+               } else if (epos > prep_endpos)
+               {
+                       /*
+                        * New range ends after old prep region.
+                        * Extend prep region to end at end of new range.
+                        */
+                       epos += SEARCH_MORE;
+               } else /* (epos <= prep_endpos) */
+               {
+                       /*
+                        * New range ends within old prep region.
+                        * Truncate search to end at start of old prep region.
+                        */
+                       epos = prep_startpos;
+               }
+
+               if (spos < prep_startpos)
+               {
+                       /*
+                        * New range starts before old prep region.
+                        * Extend old prep region backwards to start at 
+                        * start of new range.
+                        */
+                       if (spos < SEARCH_MORE)
+                               spos = 0;
+                       else
+                               spos -= SEARCH_MORE;
+                       nprep_startpos = spos;
+               } else /* (spos >= prep_startpos) */
+               {
+                       /*
+                        * New range starts within or after old prep region.
+                        * Trim search to start at end of old prep region.
+                        */
+                       spos = prep_endpos;
+               }
+       }
+
+       if (epos != NULL_POSITION && max_epos != NULL_POSITION &&
+           epos > max_epos)
+               /*
+                * Don't go past the max position we're allowed.
+                */
+               epos = max_epos;
+
+       if (epos == NULL_POSITION || epos > spos)
+       {
+               int search_type = SRCH_FORW | SRCH_FIND_ALL;
+               search_type |= (search_info.search_type & SRCH_NO_REGEX);
+               result = search_range(spos, epos, search_type, 0,
+                               maxlines, (POSITION*)NULL, &new_epos);
+               if (result < 0)
+                       return;
+               if (prep_endpos == NULL_POSITION || new_epos > prep_endpos)
+                       nprep_endpos = new_epos;
+       }
+       prep_startpos = nprep_startpos;
+       prep_endpos = nprep_endpos;
+}
+
+/*
+ * Set the pattern to be used for line filtering.
+ */
+       public void
+set_filter_pattern(pattern, search_type)
+       char *pattern;
+       int search_type;
+{
+       clr_filter();
+       if (pattern == NULL || *pattern == '\0')
+               clear_pattern(&filter_info);
+       else
+               set_pattern(&filter_info, pattern, search_type);
+       screen_trashed = 1;
+}
+
+/*
+ * Is there a line filter in effect?
+ */
+       public int
+is_filtering()
+{
+       if (ch_getflags() & CH_HELPFILE)
+               return (0);
+       return prev_pattern(&filter_info);
+}
+#endif
+
+#if HAVE_V8_REGCOMP
+/*
+ * This function is called by the V8 regcomp to report 
+ * errors in regular expressions.
+ */
+       void 
+regerror(s) 
+       char *s; 
+{
+       PARG parg;
+
+       parg.p_string = s;
+       error("%s", &parg);
+}
+#endif
+
diff --git a/thirdparty/less/signal.c b/thirdparty/less/signal.c
new file mode 100644 (file)
index 0000000..0fbaf7e
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Routines dealing with signals.
+ *
+ * A signal usually merely causes a bit to be set in the "signals" word.
+ * At some convenient time, the mainline code checks to see if any
+ * signals need processing by calling psignal().
+ * If we happen to be reading from a file [in iread()] at the time
+ * the signal is received, we call intread to interrupt the iread.
+ */
+
+#include "less.h"
+#include <signal.h>
+
+/*
+ * "sigs" contains bits indicating signals which need to be processed.
+ */
+public int sigs;
+
+extern int sc_width, sc_height;
+extern int screen_trashed;
+extern int lnloop;
+extern int linenums;
+extern int wscroll;
+extern int reading;
+extern int quit_on_intr;
+extern long jump_sline_fraction;
+
+/*
+ * Interrupt signal handler.
+ */
+       /* ARGSUSED*/
+       static RETSIGTYPE
+u_interrupt(type)
+       int type;
+{
+       bell();
+#if OS2
+       LSIGNAL(SIGINT, SIG_ACK);
+#endif
+       LSIGNAL(SIGINT, u_interrupt);
+       sigs |= S_INTERRUPT;
+#if MSDOS_COMPILER==DJGPPC
+       /*
+        * If a keyboard has been hit, it must be Ctrl-C
+        * (as opposed to Ctrl-Break), so consume it.
+        * (Otherwise, Less will beep when it sees Ctrl-C from keyboard.)
+        */
+       if (kbhit())
+               getkey();
+#endif
+       if (reading)
+               intread(); /* May longjmp */
+}
+
+#ifdef SIGTSTP
+/*
+ * "Stop" (^Z) signal handler.
+ */
+       /* ARGSUSED*/
+       static RETSIGTYPE
+stop(type)
+       int type;
+{
+       LSIGNAL(SIGTSTP, stop);
+       sigs |= S_STOP;
+       if (reading)
+               intread();
+}
+#endif
+
+#ifdef SIGWINCH
+/*
+ * "Window" change handler
+ */
+       /* ARGSUSED*/
+       public RETSIGTYPE
+winch(type)
+       int type;
+{
+       LSIGNAL(SIGWINCH, winch);
+       sigs |= S_WINCH;
+       if (reading)
+               intread();
+}
+#else
+#ifdef SIGWIND
+/*
+ * "Window" change handler
+ */
+       /* ARGSUSED*/
+       public RETSIGTYPE
+winch(type)
+       int type;
+{
+       LSIGNAL(SIGWIND, winch);
+       sigs |= S_WINCH;
+       if (reading)
+               intread();
+}
+#endif
+#endif
+
+#if MSDOS_COMPILER==WIN32C
+/*
+ * Handle CTRL-C and CTRL-BREAK keys.
+ */
+#include "windows.h"
+
+       static BOOL WINAPI 
+wbreak_handler(dwCtrlType)
+       DWORD dwCtrlType;
+{
+       switch (dwCtrlType)
+       {
+       case CTRL_C_EVENT:
+       case CTRL_BREAK_EVENT:
+               sigs |= S_INTERRUPT;
+               return (TRUE);
+       default:
+               break;
+       }
+       return (FALSE);
+}
+#endif
+
+/*
+ * Set up the signal handlers.
+ */
+       public void
+init_signals(on)
+       int on;
+{
+       if (on)
+       {
+               /*
+                * Set signal handlers.
+                */
+               (void) LSIGNAL(SIGINT, u_interrupt);
+#if MSDOS_COMPILER==WIN32C
+               SetConsoleCtrlHandler(wbreak_handler, TRUE);
+#endif
+#ifdef SIGTSTP
+               (void) LSIGNAL(SIGTSTP, stop);
+#endif
+#ifdef SIGWINCH
+               (void) LSIGNAL(SIGWINCH, winch);
+#endif
+#ifdef SIGWIND
+               (void) LSIGNAL(SIGWIND, winch);
+#endif
+#ifdef SIGQUIT
+               (void) LSIGNAL(SIGQUIT, SIG_IGN);
+#endif
+       } else
+       {
+               /*
+                * Restore signals to defaults.
+                */
+               (void) LSIGNAL(SIGINT, SIG_DFL);
+#if MSDOS_COMPILER==WIN32C
+               SetConsoleCtrlHandler(wbreak_handler, FALSE);
+#endif
+#ifdef SIGTSTP
+               (void) LSIGNAL(SIGTSTP, SIG_DFL);
+#endif
+#ifdef SIGWINCH
+               (void) LSIGNAL(SIGWINCH, SIG_IGN);
+#endif
+#ifdef SIGWIND
+               (void) LSIGNAL(SIGWIND, SIG_IGN);
+#endif
+#ifdef SIGQUIT
+               (void) LSIGNAL(SIGQUIT, SIG_DFL);
+#endif
+       }
+}
+
+/*
+ * Process any signals we have received.
+ * A received signal cause a bit to be set in "sigs".
+ */
+       public void
+psignals()
+{
+       register int tsignals;
+
+       if ((tsignals = sigs) == 0)
+               return;
+       sigs = 0;
+
+#ifdef SIGTSTP
+       if (tsignals & S_STOP)
+       {
+               /*
+                * Clean up the terminal.
+                */
+#ifdef SIGTTOU
+               LSIGNAL(SIGTTOU, SIG_IGN);
+#endif
+               clear_bot();
+               deinit();
+               flush();
+               raw_mode(0);
+#ifdef SIGTTOU
+               LSIGNAL(SIGTTOU, SIG_DFL);
+#endif
+               LSIGNAL(SIGTSTP, SIG_DFL);
+               kill(getpid(), SIGTSTP);
+               /*
+                * ... Bye bye. ...
+                * Hopefully we'll be back later and resume here...
+                * Reset the terminal and arrange to repaint the
+                * screen when we get back to the main command loop.
+                */
+               LSIGNAL(SIGTSTP, stop);
+               raw_mode(1);
+               init();
+               screen_trashed = 1;
+               tsignals |= S_WINCH;
+       }
+#endif
+#ifdef S_WINCH
+       if (tsignals & S_WINCH)
+       {
+               int old_width, old_height;
+               /*
+                * Re-execute scrsize() to read the new window size.
+                */
+               old_width = sc_width;
+               old_height = sc_height;
+               get_term();
+               if (sc_width != old_width || sc_height != old_height)
+               {
+                       wscroll = (sc_height + 1) / 2;
+                       calc_jump_sline();
+                       calc_shift_count();
+                       screen_trashed = 1;
+               }
+       }
+#endif
+       if (tsignals & S_INTERRUPT)
+       {
+               if (quit_on_intr)
+                       quit(QUIT_INTERRUPT);
+       }
+}
diff --git a/thirdparty/less/tags.c b/thirdparty/less/tags.c
new file mode 100644 (file)
index 0000000..c00f9d4
--- /dev/null
@@ -0,0 +1,757 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+#include "less.h"
+
+#define        WHITESP(c)      ((c)==' ' || (c)=='\t')
+
+#if TAGS
+
+public char *tags = "tags";
+
+static int total;
+static int curseq;
+
+extern int linenums;
+extern int sigs;
+
+enum tag_result {
+       TAG_FOUND,
+       TAG_NOFILE,
+       TAG_NOTAG,
+       TAG_NOTYPE,
+       TAG_INTR
+};
+
+/*
+ * Tag type
+ */
+enum {
+       T_CTAGS,        /* 'tags': standard and extended format (ctags) */
+       T_CTAGS_X,      /* stdin: cross reference format (ctags) */
+       T_GTAGS,        /* 'GTAGS': function defenition (global) */
+       T_GRTAGS,       /* 'GRTAGS': function reference (global) */
+       T_GSYMS,        /* 'GSYMS': other symbols (global) */
+       T_GPATH         /* 'GPATH': path name (global) */
+};
+
+static enum tag_result findctag();
+static enum tag_result findgtag();
+static char *nextgtag();
+static char *prevgtag();
+static POSITION ctagsearch();
+static POSITION gtagsearch();
+static int getentry();
+
+/*
+ * The list of tags generated by the last findgtag() call.
+ *
+ * Use either pattern or line number.
+ * findgtag() always uses line number, so pattern is always NULL.
+ * findctag() uses either pattern (in which case line number is 0),
+ * or line number (in which case pattern is NULL).
+ */
+struct taglist {
+       struct tag *tl_first;
+       struct tag *tl_last;
+};
+#define TAG_END  ((struct tag *) &taglist)
+static struct taglist taglist = { TAG_END, TAG_END };
+struct tag {
+       struct tag *next, *prev; /* List links */
+       char *tag_file;         /* Source file containing the tag */
+       LINENUM tag_linenum;    /* Appropriate line number in source file */
+       char *tag_pattern;      /* Pattern used to find the tag */
+       char tag_endline;       /* True if the pattern includes '$' */
+};
+static struct tag *curtag;
+
+#define TAG_INS(tp) \
+       (tp)->next = TAG_END; \
+       (tp)->prev = taglist.tl_last; \
+       taglist.tl_last->next = (tp); \
+       taglist.tl_last = (tp);
+
+#define TAG_RM(tp) \
+       (tp)->next->prev = (tp)->prev; \
+       (tp)->prev->next = (tp)->next;
+
+/*
+ * Delete tag structures.
+ */
+       public void
+cleantags()
+{
+       register struct tag *tp;
+
+       /*
+        * Delete any existing tag list.
+        * {{ Ideally, we wouldn't do this until after we know that we
+        *    can load some other tag information. }}
+        */
+       while ((tp = taglist.tl_first) != TAG_END)
+       {
+               TAG_RM(tp);
+               free(tp);
+       }
+       curtag = NULL;
+       total = curseq = 0;
+}
+
+/*
+ * Create a new tag entry.
+ */
+       static struct tag *
+maketagent(name, file, linenum, pattern, endline)
+       char *name;
+       char *file;
+       LINENUM linenum;
+       char *pattern;
+       int endline;
+{
+       register struct tag *tp;
+
+       tp = (struct tag *) ecalloc(sizeof(struct tag), 1);
+       tp->tag_file = (char *) ecalloc(strlen(file) + 1, sizeof(char));
+       strcpy(tp->tag_file, file);
+       tp->tag_linenum = linenum;
+       tp->tag_endline = endline;
+       if (pattern == NULL)
+               tp->tag_pattern = NULL;
+       else
+       {
+               tp->tag_pattern = (char *) ecalloc(strlen(pattern) + 1, sizeof(char));
+               strcpy(tp->tag_pattern, pattern);
+       }
+       return (tp);
+}
+
+/*
+ * Get tag mode.
+ */
+       public int
+gettagtype()
+{
+       int f;
+
+       if (strcmp(tags, "GTAGS") == 0)
+               return T_GTAGS;
+       if (strcmp(tags, "GRTAGS") == 0)
+               return T_GRTAGS;
+       if (strcmp(tags, "GSYMS") == 0)
+               return T_GSYMS;
+       if (strcmp(tags, "GPATH") == 0)
+               return T_GPATH;
+       if (strcmp(tags, "-") == 0)
+               return T_CTAGS_X;
+       f = open(tags, OPEN_READ);
+       if (f >= 0)
+       {
+               close(f);
+               return T_CTAGS;
+       }
+       return T_GTAGS;
+}
+
+/*
+ * Find tags in tag file.
+ * Find a tag in the "tags" file.
+ * Sets "tag_file" to the name of the file containing the tag,
+ * and "tagpattern" to the search pattern which should be used
+ * to find the tag.
+ */
+       public void
+findtag(tag)
+       register char *tag;
+{
+       int type = gettagtype();
+       enum tag_result result;
+
+       if (type == T_CTAGS)
+               result = findctag(tag);
+       else
+               result = findgtag(tag, type);
+       switch (result)
+       {
+       case TAG_FOUND:
+       case TAG_INTR:
+               break;
+       case TAG_NOFILE:
+               error("No tags file", NULL_PARG);
+               break;
+       case TAG_NOTAG:
+               error("No such tag in tags file", NULL_PARG);
+               break;
+       case TAG_NOTYPE:
+               error("unknown tag type", NULL_PARG);
+               break;
+       }
+}
+
+/*
+ * Search for a tag.
+ */
+       public POSITION
+tagsearch()
+{
+       if (curtag == NULL)
+               return (NULL_POSITION);  /* No gtags loaded! */
+       if (curtag->tag_linenum != 0)
+               return gtagsearch();
+       else
+               return ctagsearch();
+}
+
+/*
+ * Go to the next tag.
+ */
+       public char *
+nexttag(n)
+       int n;
+{
+       char *tagfile = (char *) NULL;
+
+       while (n-- > 0)
+               tagfile = nextgtag();
+       return tagfile;
+}
+
+/*
+ * Go to the previous tag.
+ */
+       public char *
+prevtag(n)
+       int n;
+{
+       char *tagfile = (char *) NULL;
+
+       while (n-- > 0)
+               tagfile = prevgtag();
+       return tagfile;
+}
+
+/*
+ * Return the total number of tags.
+ */
+       public int
+ntags()
+{
+       return total;
+}
+
+/*
+ * Return the sequence number of current tag.
+ */
+       public int
+curr_tag()
+{
+       return curseq;
+}
+
+/*****************************************************************************
+ * ctags
+ */
+
+/*
+ * Find tags in the "tags" file.
+ * Sets curtag to the first tag entry.
+ */
+       static enum tag_result
+findctag(tag)
+       register char *tag;
+{
+       char *p;
+       register FILE *f;
+       register int taglen;
+       LINENUM taglinenum;
+       char *tagfile;
+       char *tagpattern;
+       int tagendline;
+       int search_char;
+       int err;
+       char tline[TAGLINE_SIZE];
+       struct tag *tp;
+
+       p = shell_unquote(tags);
+       f = fopen(p, "r");
+       free(p);
+       if (f == NULL)
+               return TAG_NOFILE;
+
+       cleantags();
+       total = 0;
+       taglen = strlen(tag);
+
+       /*
+        * Search the tags file for the desired tag.
+        */
+       while (fgets(tline, sizeof(tline), f) != NULL)
+       {
+               if (tline[0] == '!')
+                       /* Skip header of extended format. */
+                       continue;
+               if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
+                       continue;
+
+               /*
+                * Found it.
+                * The line contains the tag, the filename and the
+                * location in the file, separated by white space.
+                * The location is either a decimal line number, 
+                * or a search pattern surrounded by a pair of delimiters.
+                * Parse the line and extract these parts.
+                */
+               tagpattern = NULL;
+
+               /*
+                * Skip over the whitespace after the tag name.
+                */
+               p = skipsp(tline+taglen);
+               if (*p == '\0')
+                       /* File name is missing! */
+                       continue;
+
+               /*
+                * Save the file name.
+                * Skip over the whitespace after the file name.
+                */
+               tagfile = p;
+               while (!WHITESP(*p) && *p != '\0')
+                       p++;
+               *p++ = '\0';
+               p = skipsp(p);
+               if (*p == '\0')
+                       /* Pattern is missing! */
+                       continue;
+
+               /*
+                * First see if it is a line number. 
+                */
+               tagendline = 0;
+               taglinenum = getnum(&p, 0, &err);
+               if (err)
+               {
+                       /*
+                        * No, it must be a pattern.
+                        * Delete the initial "^" (if present) and 
+                        * the final "$" from the pattern.
+                        * Delete any backslash in the pattern.
+                        */
+                       taglinenum = 0;
+                       search_char = *p++;
+                       if (*p == '^')
+                               p++;
+                       tagpattern = p;
+                       while (*p != search_char && *p != '\0')
+                       {
+                               if (*p == '\\')
+                                       p++;
+                               p++;
+                       }
+                       tagendline = (p[-1] == '$');
+                       if (tagendline)
+                               p--;
+                       *p = '\0';
+               }
+               tp = maketagent(tag, tagfile, taglinenum, tagpattern, tagendline);
+               TAG_INS(tp);
+               total++;
+       }
+       fclose(f);
+       if (total == 0)
+               return TAG_NOTAG;
+       curtag = taglist.tl_first;
+       curseq = 1;
+       return TAG_FOUND;
+}
+
+/*
+ * Edit current tagged file.
+ */
+       public int
+edit_tagfile()
+{
+       if (curtag == NULL)
+               return (1);
+       return (edit(curtag->tag_file));
+}
+
+/*
+ * Search for a tag.
+ * This is a stripped-down version of search().
+ * We don't use search() for several reasons:
+ *   - We don't want to blow away any search string we may have saved.
+ *   - The various regular-expression functions (from different systems:
+ *     regcmp vs. re_comp) behave differently in the presence of 
+ *     parentheses (which are almost always found in a tag).
+ */
+       static POSITION
+ctagsearch()
+{
+       POSITION pos, linepos;
+       LINENUM linenum;
+       int len;
+       char *line;
+
+       pos = ch_zero();
+       linenum = find_linenum(pos);
+
+       for (;;)
+       {
+               /*
+                * Get lines until we find a matching one or 
+                * until we hit end-of-file.
+                */
+               if (ABORT_SIGS())
+                       return (NULL_POSITION);
+
+               /*
+                * Read the next line, and save the 
+                * starting position of that line in linepos.
+                */
+               linepos = pos;
+               pos = forw_raw_line(pos, &line, (int *)NULL);
+               if (linenum != 0)
+                       linenum++;
+
+               if (pos == NULL_POSITION)
+               {
+                       /*
+                        * We hit EOF without a match.
+                        */
+                       error("Tag not found", NULL_PARG);
+                       return (NULL_POSITION);
+               }
+
+               /*
+                * If we're using line numbers, we might as well
+                * remember the information we have now (the position
+                * and line number of the current line).
+                */
+               if (linenums)
+                       add_lnum(linenum, pos);
+
+               /*
+                * Test the line to see if we have a match.
+                * Use strncmp because the pattern may be
+                * truncated (in the tags file) if it is too long.
+                * If tagendline is set, make sure we match all
+                * the way to end of line (no extra chars after the match).
+                */
+               len = strlen(curtag->tag_pattern);
+               if (strncmp(curtag->tag_pattern, line, len) == 0 &&
+                   (!curtag->tag_endline || line[len] == '\0' || line[len] == '\r'))
+               {
+                       curtag->tag_linenum = find_linenum(linepos);
+                       break;
+               }
+       }
+
+       return (linepos);
+}
+
+/*******************************************************************************
+ * gtags
+ */
+
+/*
+ * Find tags in the GLOBAL's tag file.
+ * The findgtag() will try and load information about the requested tag.
+ * It does this by calling "global -x tag" and storing the parsed output
+ * for future use by gtagsearch().
+ * Sets curtag to the first tag entry.
+ */
+       static enum tag_result
+findgtag(tag, type)
+       char *tag;              /* tag to load */
+       int type;               /* tags type */
+{
+       char buf[256];
+       FILE *fp;
+       struct tag *tp;
+
+       if (type != T_CTAGS_X && tag == NULL)
+               return TAG_NOFILE;
+
+       cleantags();
+       total = 0;
+
+       /*
+        * If type == T_CTAGS_X then read ctags's -x format from stdin
+        * else execute global(1) and read from it.
+        */
+       if (type == T_CTAGS_X)
+       {
+               fp = stdin;
+               /* Set tag default because we cannot read stdin again. */
+               tags = "tags";
+       } else
+       {
+#if !HAVE_POPEN
+               return TAG_NOFILE;
+#else
+               char *command;
+               char *flag;
+               char *qtag;
+               char *cmd = lgetenv("LESSGLOBALTAGS");
+
+               if (cmd == NULL || *cmd == '\0')
+                       return TAG_NOFILE;
+               /* Get suitable flag value for global(1). */
+               switch (type)
+               {
+               case T_GTAGS:
+                       flag = "" ;
+                       break;
+               case T_GRTAGS:
+                       flag = "r";
+                       break;
+               case T_GSYMS:
+                       flag = "s";
+                       break;
+               case T_GPATH:
+                       flag = "P";
+                       break;
+               default:
+                       return TAG_NOTYPE;
+               }
+
+               /* Get our data from global(1). */
+               qtag = shell_quote(tag);
+               if (qtag == NULL)
+                       qtag = tag;
+               command = (char *) ecalloc(strlen(cmd) + strlen(flag) +
+                               strlen(qtag) + 5, sizeof(char));
+               sprintf(command, "%s -x%s %s", cmd, flag, qtag);
+               if (qtag != tag)
+                       free(qtag);
+               fp = popen(command, "r");
+               free(command);
+#endif
+       }
+       if (fp != NULL)
+       {
+               while (fgets(buf, sizeof(buf), fp))
+               {
+                       char *name, *file, *line;
+                       int len;
+
+                       if (sigs)
+                       {
+#if HAVE_POPEN
+                               if (fp != stdin)
+                                       pclose(fp);
+#endif
+                               return TAG_INTR;
+                       }
+                       len = strlen(buf);
+                       if (len > 0 && buf[len-1] == '\n')
+                               buf[len-1] = '\0';
+                       else
+                       {
+                               int c;
+                               do {
+                                       c = fgetc(fp);
+                               } while (c != '\n' && c != EOF);
+                       }
+
+                       if (getentry(buf, &name, &file, &line))
+                       {
+                               /*
+                                * Couldn't parse this line for some reason.
+                                * We'll just pretend it never happened.
+                                */
+                               break;
+                       }
+
+                       /* Make new entry and add to list. */
+                       tp = maketagent(name, file, (LINENUM) atoi(line), NULL, 0);
+                       TAG_INS(tp);
+                       total++;
+               }
+               if (fp != stdin)
+               {
+                       if (pclose(fp))
+                       {
+                               curtag = NULL;
+                               total = curseq = 0;
+                               return TAG_NOFILE;
+                       }
+               }
+       }
+
+       /* Check to see if we found anything. */
+       tp = taglist.tl_first;
+       if (tp == TAG_END)
+               return TAG_NOTAG;
+       curtag = tp;
+       curseq = 1;
+       return TAG_FOUND;
+}
+
+static int circular = 0;       /* 1: circular tag structure */
+
+/*
+ * Return the filename required for the next gtag in the queue that was setup
+ * by findgtag().  The next call to gtagsearch() will try to position at the
+ * appropriate tag.
+ */
+       static char *
+nextgtag()
+{
+       struct tag *tp;
+
+       if (curtag == NULL)
+               /* No tag loaded */
+               return NULL;
+
+       tp = curtag->next;
+       if (tp == TAG_END)
+       {
+               if (!circular)
+                       return NULL;
+               /* Wrapped around to the head of the queue */
+               curtag = taglist.tl_first;
+               curseq = 1;
+       } else
+       {
+               curtag = tp;
+               curseq++;
+       }
+       return (curtag->tag_file);
+}
+
+/*
+ * Return the filename required for the previous gtag in the queue that was
+ * setup by findgtat().  The next call to gtagsearch() will try to position
+ * at the appropriate tag.
+ */
+       static char *
+prevgtag()
+{
+       struct tag *tp;
+
+       if (curtag == NULL)
+               /* No tag loaded */
+               return NULL;
+
+       tp = curtag->prev;
+       if (tp == TAG_END)
+       {
+               if (!circular)
+                       return NULL;
+               /* Wrapped around to the tail of the queue */
+               curtag = taglist.tl_last;
+               curseq = total;
+       } else
+       {
+               curtag = tp;
+               curseq--;
+       }
+       return (curtag->tag_file);
+}
+
+/*
+ * Position the current file at at what is hopefully the tag that was chosen
+ * using either findtag() or one of nextgtag() and prevgtag().  Returns -1
+ * if it was unable to position at the tag, 0 if successful.
+ */
+       static POSITION
+gtagsearch()
+{
+       if (curtag == NULL)
+               return (NULL_POSITION);  /* No gtags loaded! */
+       return (find_pos(curtag->tag_linenum));
+}
+
+/*
+ * The getentry() parses both standard and extended ctags -x format.
+ *
+ * [standard format]
+ * <tag>   <lineno>  <file>         <image>
+ * +------------------------------------------------
+ * |main     30      main.c         main(argc, argv)
+ * |func     21      subr.c         func(arg)
+ *
+ * The following commands write this format.
+ *     o Traditinal Ctags with -x option
+ *     o Global with -x option
+ *             See <http://www.gnu.org/software/global/global.html>
+ *
+ * [extended format]
+ * <tag>   <type>  <lineno>   <file>        <image>
+ * +----------------------------------------------------------
+ * |main     function 30      main.c         main(argc, argv)
+ * |func     function 21      subr.c         func(arg)
+ *
+ * The following commands write this format.
+ *     o Exuberant Ctags with -x option
+ *             See <http://ctags.sourceforge.net>
+ *
+ * Returns 0 on success, -1 on error.
+ * The tag, file, and line will each be NUL-terminated pointers
+ * into buf.
+ */
+       static int
+getentry(buf, tag, file, line)
+       char *buf;      /* standard or extended ctags -x format data */
+       char **tag;     /* name of the tag we actually found */
+       char **file;    /* file in which to find this tag */
+       char **line;    /* line number of file where this tag is found */
+{
+       char *p = buf;
+
+       for (*tag = p;  *p && !IS_SPACE(*p);  p++)      /* tag name */
+               ;
+       if (*p == 0)
+               return (-1);
+       *p++ = 0;
+       for ( ;  *p && IS_SPACE(*p);  p++)              /* (skip blanks) */
+               ;
+       if (*p == 0)
+               return (-1);
+       /*
+        * If the second part begin with other than digit,
+        * it is assumed tag type. Skip it.
+        */
+       if (!IS_DIGIT(*p))
+       {
+               for ( ;  *p && !IS_SPACE(*p);  p++)     /* (skip tag type) */
+                       ;
+               for (;  *p && IS_SPACE(*p);  p++)       /* (skip blanks) */
+                       ;
+       }
+       if (!IS_DIGIT(*p))
+               return (-1);
+       *line = p;                                      /* line number */
+       for (*line = p;  *p && !IS_SPACE(*p);  p++)
+               ;
+       if (*p == 0)
+               return (-1);
+       *p++ = 0;
+       for ( ; *p && IS_SPACE(*p);  p++)               /* (skip blanks) */
+               ;
+       if (*p == 0)
+               return (-1);
+       *file = p;                                      /* file name */
+       for (*file = p;  *p && !IS_SPACE(*p);  p++)
+               ;
+       if (*p == 0)
+               return (-1);
+       *p = 0;
+
+       /* value check */
+       if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0)
+               return (0);
+       return (-1);
+}
+  
+#endif
diff --git a/thirdparty/less/ttyin.c b/thirdparty/less/ttyin.c
new file mode 100644 (file)
index 0000000..00f2c9e
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+ * Routines dealing with getting input from the keyboard (i.e. from the user).
+ */
+
+#include "less.h"
+#if OS2
+#include "cmd.h"
+#include "pckeys.h"
+#endif
+#if MSDOS_COMPILER==WIN32C
+#include "windows.h"
+extern char WIN32getch();
+static DWORD console_mode;
+#endif
+
+public int tty;
+extern int sigs;
+extern int utf_mode;
+
+/*
+ * Open keyboard for input.
+ */
+       public void
+open_getchr()
+{
+#if MSDOS_COMPILER==WIN32C
+       /* Need this to let child processes inherit our console handle */
+       SECURITY_ATTRIBUTES sa;
+       memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
+       sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+       sa.bInheritHandle = TRUE;
+       tty = (int) CreateFile("CONIN$", GENERIC_READ,
+                       FILE_SHARE_READ, &sa, 
+                       OPEN_EXISTING, 0L, NULL);
+       GetConsoleMode((HANDLE)tty, &console_mode);
+       /* Make sure we get Ctrl+C events. */
+       SetConsoleMode((HANDLE)tty, ENABLE_PROCESSED_INPUT);
+#else
+#if MSDOS_COMPILER
+       extern int fd0;
+       /*
+        * Open a new handle to CON: in binary mode 
+        * for unbuffered keyboard read.
+        */
+        fd0 = dup(0);
+        close(0);
+        tty = open("CON", OPEN_READ);
+#if MSDOS_COMPILER==DJGPPC
+       /*
+        * Setting stdin to binary causes Ctrl-C to not
+        * raise SIGINT.  We must undo that side-effect.
+        */
+       (void) __djgpp_set_ctrl_c(1);
+#endif
+#else
+       /*
+        * Try /dev/tty.
+        * If that doesn't work, use file descriptor 2,
+        * which in Unix is usually attached to the screen,
+        * but also usually lets you read from the keyboard.
+        */
+#if OS2
+       /* The __open() system call translates "/dev/tty" to "con". */
+       tty = __open("/dev/tty", OPEN_READ);
+#else
+       tty = open("/dev/tty", OPEN_READ);
+#endif
+       if (tty < 0)
+               tty = 2;
+#endif
+#endif
+}
+
+/*
+ * Close the keyboard.
+ */
+       public void
+close_getchr()
+{
+#if MSDOS_COMPILER==WIN32C
+       SetConsoleMode((HANDLE)tty, console_mode);
+       CloseHandle((HANDLE)tty);
+#endif
+}
+
+/*
+ * Get a character from the keyboard.
+ */
+       public int
+getchr()
+{
+       char c;
+       int result;
+
+       do
+       {
+#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
+               /*
+                * In raw read, we don't see ^C so look here for it.
+                */
+               flush();
+#if MSDOS_COMPILER==WIN32C
+               if (ABORT_SIGS())
+                       return (READ_INTR);
+               c = WIN32getch(tty);
+#else
+               c = getch();
+#endif
+               result = 1;
+               if (c == '\003')
+                       return (READ_INTR);
+#else
+               result = iread(tty, &c, sizeof(char));
+               if (result == READ_INTR)
+                       return (READ_INTR);
+               if (result < 0)
+               {
+                       /*
+                        * Don't call error() here,
+                        * because error calls getchr!
+                        */
+                       quit(QUIT_ERROR);
+               }
+#endif
+#if 0 /* allow entering arbitrary hex chars for testing */
+               /* ctrl-A followed by two hex chars makes a byte */
+       {
+               int hex_in = 0;
+               int hex_value = 0;
+               if (c == CONTROL('A'))
+               {
+                       hex_in = 2;
+                       result = 0;
+                       continue;
+               }
+               if (hex_in > 0)
+               {
+                       int v;
+                       if (c >= '0' && c <= '9')
+                               v = c - '0';
+                       else if (c >= 'a' && c <= 'f')
+                               v = c - 'a' + 10;
+                       else if (c >= 'A' && c <= 'F')
+                               v = c - 'A' + 10;
+                       else
+                               hex_in = 0;
+                       hex_value = (hex_value << 4) | v;
+                       if (--hex_in > 0)
+                       {
+                               result = 0;
+                               continue;
+                       }
+                       c = hex_value;
+               }
+       }
+#endif
+               /*
+                * Various parts of the program cannot handle
+                * an input character of '\0'.
+                * If a '\0' was actually typed, convert it to '\340' here.
+                */
+               if (c == '\0')
+                       c = '\340';
+       } while (result != 1);
+
+       return (c & 0xFF);
+}
diff --git a/thirdparty/less/version.c b/thirdparty/less/version.c
new file mode 100644 (file)
index 0000000..dc5bb0c
--- /dev/null
@@ -0,0 +1,748 @@
+/*
+ * Copyright (C) 1984-2011  Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information about less, or for information on how to 
+ * contact the author, see the README file.
+ */
+
+
+/*
+----------------------- CHANGE HISTORY --------------------------
+
+       1/29/84  Allowed use on standard input
+       2/1/84   Added E, N, P commands
+       4/17/84  Added '=' command, 'stop' signal handling
+       4/20/84  Added line folding
+v2     4/27/84  Fixed '=' command to use BOTTOM_PLUS_ONE,
+                instead of TOP, added 'p' & 'v' commands
+v3     5/3/84   Added -m and -t options, '-' command
+v4     5/3/84   Added LESS environment variable
+v5     5/3/84   New comments, fixed '-' command slightly
+v6     5/15/84  Added -Q, visual bell
+v7     5/24/84  Fixed jump_back(n) bug: n should count real
+                lines, not folded lines.  Also allow number on G command.
+v8     5/30/84  Re-do -q and -Q commands
+v9     9/25/84  Added "+<cmd>" argument
+v10    10/10/84 Fixed bug in -b<n> argument processing
+v11    10/18/84 Made error() ring bell if \n not entered.
+-----------------------------------------------------------------
+v12    2/13/85  Reorganized signal handling and made portable to 4.2bsd.
+v13    2/16/85  Reword error message for '-' command.
+v14    2/22/85  Added -bf and -bp variants of -b.
+v15    2/25/85  Miscellaneous changes.
+v16    3/13/85  Added -u flag for backspace processing.
+v17    4/13/85  Added j and k commands, changed -t default.
+v18    4/20/85  Rewrote signal handling code.
+v19    5/2/85   Got rid of "verbose" eq_message().
+                Made search() scroll in some cases.
+v20    5/21/85  Fixed screen.c ioctls for System V.
+v21    5/23/85  Fixed some first_cmd bugs.
+v22    5/24/85  Added support for no RECOMP nor REGCMP.
+v23    5/25/85  Miscellanous changes and prettying up.
+                Posted to USENET.
+-----------------------------------------------------------------
+v24    6/3/85   Added ti,te terminal init & de-init.       
+                (Thanks to Mike Kersenbrock)
+v25    6/8/85   Added -U flag, standout mode underlining.
+v26    6/9/85   Added -M flag.
+                Use underline termcap (us) if it exists.
+v27    6/15/85  Renamed some variables to make unique in
+                6 chars.  Minor fix to -m.
+v28    6/28/85  Fixed right margin bug.
+v29    6/28/85  Incorporated M.Rose's changes to signal.c
+v30    6/29/85  Fixed stupid bug in argument processing.
+v31    7/15/85  Added -p flag, changed repaint algorithm.  
+                Added kludge for magic cookie terminals.
+v32    7/16/85  Added cat_file if output not a tty.
+v33    7/23/85  Added -e flag and EDITOR.
+v34    7/26/85  Added -s flag.
+v35    7/27/85  Rewrote option handling; added option.c.
+v36    7/29/85  Fixed -e flag to work if not last file.
+v37    8/10/85  Added -x flag.
+v38    8/19/85  Changed prompting; created prompt.c.
+v39    8/24/85  (Not -p) does not initially clear screen.
+v40    8/26/85  Added "skipping" indicator in forw().
+                Posted to USENET.
+-----------------------------------------------------------------
+v41    9/17/85  ONLY_RETURN, control char commands,
+                faster search, other minor fixes.
+v42    9/25/85  Added ++ command line syntax;
+                ch_fsize for pipes.
+v43    10/15/85 Added -h flag, changed prim.c algorithms.
+v44    10/16/85 Made END print in all cases of eof;
+                ignore SIGTTOU after receiv ing SIGTSTP.
+v45    10/16/85 Never print backspaces unless -u.
+v46    10/24/85 Backwards scroll in jump_loc.
+v47    10/30/85 Fixed bug in edit(): *first_cmd==0
+v48    11/16/85 Use TIOCSETN instead of TIOCSETP.
+                Added marks (m and ' commands).
+                Posted to USENET.
+-----------------------------------------------------------------
+v49    1/9/86   Fixed bug: signal didn't clear mcc.
+v50    1/15/86  Added ' (quote) to gomark.
+v51    1/16/86  Added + cmd, fixed problem if first_cmd
+                fails, made g cmd sort of "work" on pipes
+                ev en if bof is no longer buffered.
+v52    1/17/86  Made short files work better.
+v53    1/20/86  Added -P option.
+v54    1/20/86  Changed help to use HELPFILE.
+v55    1/23/86  Messages work better if not tty output.
+v56    1/24/86  Added -l option.
+v57    1/31/86  Fixed -l to get confirmation before
+                ov erwriting an existing file.
+v58    8/28/86  Added filename globbing.
+v59    9/15/86  Fixed some bugs with very long filenames.
+v60    9/26/86  Incorporated changes from Leith (Casey)
+                Leedom for boldface and -z option.
+v61    9/26/86  Got rid of annoying repaints after ! cmd.
+                Posted to USENET.
+-----------------------------------------------------------------
+v62    12/23/86 Added is_directory(); change -z default to
+                -1 instead of 24; cat-and-exit if -e and
+                file is less than a screenful.
+v63    1/8/87   Fixed bug in cat-and-exit if > 1 file.
+v64    1/12/87  Changed puts/putstr, putc/putchr,
+                getc/getchr to av oid name conflict with
+                stdio functions.
+v65    1/26/87  Allowed '-' command to change NUMBER
+                v alued options (thanks to Gary Puckering)
+v66    2/13/87  Fixed bug: prepaint should use force=1.
+v67    2/24/87  Added !! and % expansion to ! command.
+v68    2/25/87  Added SIGWINCH and TIOCGWINSZ support;
+                changed is_directory to bad_file.
+                (thanks to J. Robert Ward)
+v69    2/25/87  Added SIGWIND and WIOCGETD (for Unix PC).
+v70    3/13/87  Changed help cmd from 'h' to 'H'; better
+                error msgs in bad_file, errno_message.
+v71    5/11/87  Changed -p to -c, made triple -c/-C
+                for clear-eol like more's -c.
+v72    6/26/87  Added -E, -L, use $SHELL in lsystem().
+                (thanks to Stev e Spearman)
+v73    6/26/87  Allow Examine "#" for previous file.
+                Posted to USENET 8/25/87.
+-----------------------------------------------------------------
+v74    9/18/87  Fix conflict in EOF symbol with stdio.h,
+                Make os.c more portable to BSD.
+v75    9/23/87  Fix problems in get_term (thanks to 
+                Paul Eggert); new backwards scrolling in
+                jump_loc (thanks to Marion Hakanson).
+v76    9/23/87  Added -i flag; allow single "!" to
+                inv oke a shell (thanks to Franco Barber).
+v77    9/24/87  Added -n flag and line number support.
+v78    9/25/87  Fixed problem with prompts longer than
+                the screen width.
+v79    9/29/87  Added the _ command.
+v80    10/6/87  Allow signal to break out of linenum scan.
+v81    10/6/87  Allow -b to be changed from within less.
+v82    10/7/87  Add cmd_decode to use a table for key
+                binding (thanks to Dav id Nason).
+v83    10/9/87  Allow .less file for user-defined keys.
+v84    10/11/87 Fix -e/-E problems (thanks to Felix Lee).
+v85    10/15/87 Search now keeps track of line numbers.
+v86    10/20/87 Added -B option and autobuf; fixed
+                "pipe error" bug.
+v87    3/1/88   Fix bug re BSD signals while reading file.
+v88    3/12/88  Use new format for -P option (thanks to
+                der Mouse), allow "+-c" without message,
+                fix bug re BSD hangup.
+v89    3/18/88  Turn off line numbers if linenum scan
+                is interrupted.
+v90    3/30/88  Allow -P from within less.
+v91    3/30/88  Added tags file support (new -t option)
+                (thanks to Brian Campbell).
+v92    4/4/88   Added -+option syntax.
+v93    4/11/88  Add support for slow input (thanks to
+                Joe Orost & apologies for taking almost
+                3 years to get this in!)
+v94    4/11/88  Redo reading/signal stuff.
+v95    4/20/88  Repaint screen better after signal.
+v96    4/21/88  Add /! and ?! commands.
+v97    5/17/88  Allow -l/-L from within less.
+                Eliminate some static arrays (use calloc).
+                Posted to USENET.
+-----------------------------------------------------------------
+v98    10/14/88 Fix incorrect calloc call; uninitialized
+                var in exec_mca; core dump on unknown TERM.
+                Make v cmd work if past last line of file.
+                Fix some signal bugs.
+v99    10/29/88 Allow space between -X and string,
+                when X is a string-valued option.
+v100   1/5/89   Fix globbing bug when $SHELL not set;
+                allow spaces after -t command.
+v101   1/6/89   Fix problem with long (truncated) lines
+                in tags file (thanks to Neil Dixon).
+v102   1/6/89   Fix bug with E# when no prev file;
+                allow spaces after -l command.
+v103   3/14/89  Add -N, -f and -? options.  Add z and w
+                commands.  Add %L for prompt strings.
+v104   3/16/89  Added EDITPROTO.
+v105   3/20/89  Fix bug in find_linenum which cached
+                incorrectly on long lines.
+v106   3/31/89  Added -k option and multiple lesskey      
+                files.
+v107   4/27/89  Add 8-bit char support and -g option.
+                Split option code into 3 files.
+v108   5/5/89   Allocate position table dynamically       
+                (thanks to Paul Eggert); change % command
+                from "percent" to vi-style brace finder.
+v109   5/10/89  Added ESC-% command, split prim.c.
+v110   5/24/89  Fixed bug in + option; fixed repaint bug
+                under Sun windows (thanks to Paul Eggert).
+v111   5/25/89  Generalized # and % expansion; use 
+                calloc for some error messages.
+v112   5/30/89  Get rid of ESC-%, add {}()[] commands.
+v113   5/31/89  Optimize lseeks (thanks to Paul Eggert).
+v114   7/25/89  Added ESC-/ and ESC-/! commands.
+v115   7/26/89  Added ESC-n command.
+v116   7/31/89  Added find_pos to optimize g command.
+v117   8/1/89   Change -f option to -r.
+v118   8/2/89   Save positions for all previous files,
+                not just the immediately previous one.
+v119   8/7/89   Save marks across file boundaries.
+                Add file handle stuff.
+v120   8/11/89  Add :ta command.
+v121   8/16/89  Add -f option.
+v122   8/30/89  Fix performance with many buffers.
+v123   8/31/89  Verbose prompts for string options.
+                Posted beta to USENET.
+-----------------------------------------------------------------
+v124   9/18/89  Reorganize search commands,
+                N = rev, ESC-n = span, add ESC-N.
+v125   9/18/89  Fix tab bug (thanks to Alex Liu).
+                Fix EOF bug when both -w and -c.
+v126   10/25/89 Add -j option.
+v127   10/27/89 Fix problems with blank lines before BOF.
+v128   10/27/89 Add %bj, etc. to prompt strings.
+v129   11/3/89  Add -+,-- commands; add set-option and
+                unset-option to lesskey.
+v130   11/6/89  Generalize A_EXTRA to string, remove
+                set-option, unset-option from lesskey.
+v131   11/7/89  Changed name of EDITPROTO to LESSEDIT.
+v132   11/8/89  Allow editing of command prefix.
+v133   11/16/89 Add -y option (thanks to Jeff Sullivan).
+v134   12/1/89  Glob filenames in the -l command.
+v135   12/5/89  Combined {}()[] commands into one, and
+                added ESC-^F and ESC-^B commands.
+v136   1/20/90  Added -S, -R flags.  Added | command.
+                Added warning for binary files. (thanks
+                to Richard Brittain and J. Sullivan).
+v137   1/21/90  Rewrote horrible pappend code.
+                Added * notation for hi-bit chars.
+v138   1/24/90  Fix magic cookie terminal handling.
+                Get rid of "cleanup" loop in ch_get.
+v139   1/27/90  Added MSDOS support.  (many thanks
+                to Richard Brittain).
+v140   2/7/90   Editing a new file adds it to the
+                command line list.
+v141   2/8/90   Add edit_list for editing >1 file.
+v142   2/10/90  Add :x command.
+v143   2/11/90  Add * and @ modifies to search cmds.
+                Change ESC-/ cmd from /@* to / *.
+v144   3/1/90   Messed around with ch_zero; 
+                no real change.
+v145   3/2/90   Added -R and -v/-V for MSDOS;
+                renamed FILENAME to avoid conflict.
+v146   3/5/90   Pull cmdbuf functions out of command.c
+v147   3/7/90   Implement ?@; fix multi-file edit bugs.
+v148   3/29/90  Fixed bug in :e<file> then :e#.
+v149   4/3/90   Change error,ierror,query to use PARG.
+v150   4/6/90   Add LESS_CHARSET, LESS_CHARDEF.
+v151   4/13/90  Remove -g option; clean up ispipe.
+v152   4/14/90  lsystem() closes input file, for
+                editors which require exclusive open.
+v153   4/18/90  Fix bug if SHELL unset; 
+                fix bug in overstrike control char.
+v154   4/25/90  Output to fd 2 via buffer.
+v155   4/30/90  Ignore -i if uppercase in pattern
+                (thanks to Michael Rendell.)
+v156   5/3/90   Remove scroll limits in forw() & back();
+                causes problems with -c.
+v157   5/4/90   Forward search starts at next real line
+                (not screen line) after jump target.
+v158   6/14/90  Added F command.
+v159   7/29/90  Fix bug in exiting: output not flushed.
+v160   7/29/90  Clear screen before initial output w/ -c.
+v161   7/29/90  Add -T flag.
+v162   8/14/90  Fix bug with +F on command line.
+v163   8/21/90  Added LESSBINFMT variable.
+v164   9/5/90   Added -p, LINES, COLUMNS and
+                unset mark ' == BOF, for 1003.2 D5.
+v165   9/6/90   At EOF with -c set, don't display empty
+                screen when try to page forward.
+v166   9/6/90   Fix G when final line in file wraps.
+v167   9/11/90  Translate CR/LF -> LF for 1003.2.
+v168   9/13/90  Return to curr file if "tag not found".
+v169   12/12/90 G goes to EOF even if file has grown.
+v170   1/17/91  Add optimization for BSD _setjmp;
+                fix #include ioctl.h TERMIO problem.
+                (thanks to Paul Eggert)
+                Posted to USENET.
+-----------------------------------------------------------------
+v171   3/6/91   Fix -? bug in get_filename.
+v172   3/15/91  Fix G bug in empty file.
+                Fix bug with ?\n and -i and uppercase
+                pattern at EOF!
+                (thanks to Paul Eggert)
+v173   3/17/91  Change N cmd to not permanently change
+                direction. (thanks to Brian Matthews)
+v174   3/18/91  Fix bug with namelogfile not getting
+                cleared when change files.
+v175   3/18/91  Fix bug with ++cmd on command line.
+                (thanks to Jim Meyering)
+v176   4/2/91   Change | to not force current screen,
+                include marked line, start/end from
+                top of screen.  Improve search speed.
+                (thanks to Don Mears)
+v177   4/2/91   Add LESSHELP variable.
+                Fix bug with F command with -e.
+                Try /dev/tty for input before using fd 2.
+                Patches posted to USENET  4/2/91.
+-----------------------------------------------------------------
+v178   4/8/91   Fixed bug in globbing logfile name.
+                (thanks to Jim Meyering)
+v179   4/9/91   Allow negative -z for screen-relative.
+v180   4/9/91   Clear to eos rather than eol if "db";
+                don't use "sr" if "da".
+                (thanks to Tor Lillqvist)
+v181   4/18/91  Fixed bug with "negative" chars 80 - FF.
+                (thanks to Benny Sander Hofmann)
+v182   5/16/91  Fixed bug with attribute at EOL.
+                (thanks to Brian Matthews)
+v183   6/1/91   Rewrite linstall to do smart config.
+v184   7/11/91  Process \b in searches based on -u
+                rather than -i.
+v185   7/11/91  -Pxxx sets short prompt; assume SIGWINCH
+                after a SIGSTOP. (thanks to Ken Laprade)
+-----------------------------------------------------------------
+v186   4/20/92  Port to MS-DOS (Microsoft C).
+v187   4/23/92  Added -D option & TAB_COMPLETE_FILENAME.
+v188   4/28/92  Added command line editing features.
+v189   12/8/92  Fix mem overrun in anscreen.c:init; 
+                fix edit_list to recover from bin file.
+v190   2/13/93  Make TAB enter one filename at a time;
+                create ^L with old TAB functionality.
+v191   3/10/93  Defer creating "flash" page for MS-DOS.
+v192   9/6/93   Add BACK-TAB.
+v193   9/17/93  Simplify binary_file handling.
+v194   1/4/94   Add rudiments of alt_filename handling.
+v195   1/11/94  Port back to Unix; support keypad.
+-----------------------------------------------------------------
+v196   6/7/94   Fix bug with bad filename; fix IFILE
+                type problem. (thanks to David MacKenzie)
+v197   6/7/94   Fix bug with .less tables inserted wrong.
+v198   6/23/94  Use autoconf installation technology.
+                (thanks to David MacKenzie)
+v199   6/29/94  Fix MS-DOS build (thanks to Tim Wiegman).
+v200   7/25/94  Clean up copyright, minor fixes.
+        Posted to prep.ai.mit.edu
+-----------------------------------------------------------------
+v201   7/27/94  Check for no memcpy; add casts to calloc;
+                look for regcmp in libgen.a.
+                (thanks to Kaveh Ghazi).
+v202   7/28/94  Fix bug in edit_next/edit_prev with 
+                non-existant files.
+v203   8/2/94   Fix a variety of configuration bugs on
+                various systems. (thanks to Sakai
+                Kiyotaka, Harald Koenig, Bjorn Brox,
+                Teemu Rantanen, and Thorsten Lockert)
+v204   8/3/94   Use strerror if available.
+                (thanks to J.T. Conklin)
+v205   8/5/94   Fix bug in finding "me" termcap entry.
+                (thanks to Andreas Stolcke)
+8/10/94         v205+: Change BUFSIZ to LBUFSIZE to avoid name
+                conflict with stdio.h.
+                Posted to prep.ai.mit.edu
+-----------------------------------------------------------------
+v206   8/10/94  Use initial_scrpos for -t to avoid
+                displaying first page before init().
+                (thanks to Dominique Petitpierre)
+v207   8/12/94  Fix bug if stdout is not tty.
+v208   8/16/94  Fix bug in close_altfile if goto err1
+                in edit_ifile. (Thanks to M.J. Hewitt)
+v209   8/16/94  Change scroll to wscroll to avoid 
+                conflict with library function.
+v210   8/16/94  Fix bug with bold on 8 bit chars.
+                (thanks to Vitor Duarte)
+v211   8/16/94  Don't quit on EOI in jump_loc / forw.
+v212   8/18/94  Use time_t if available.
+v213   8/20/94  Allow ospeed to be defined in termcap.h.
+v214   8/20/94  Added HILITE_SEARCH, -F, ESC-u cmd.
+                (thanks to Paul Lew and Bob Byrnes)
+v215   8/23/94  Fix -i toggle behavior.
+v216   8/23/94  Process BS in all searches, not only -u.
+v217   8/24/94  Added -X flag.
+v218   8/24/94  Reimplement undo_search.
+v219   8/24/94  Find tags marked with line number
+                instead of pattern.
+v220   8/24/94  Stay at same position after SIG_WINCH.
+v221   8/24/94  Fix bug in file percentage in big file.
+v222   8/25/94  Do better if can't reopen current file.
+v223   8/27/94  Support setlocale.
+                (thanks to Robert Joop)
+v224   8/29/94  Revert v216: process BS in search
+                only if -u.
+v225   9/6/94   Rewrite undo_search again: toggle.
+v226   9/15/94  Configuration fixes. 
+                (thanks to David MacKenzie)
+v227   9/19/94  Fixed strerror config problem.
+                Posted to prep.ai.mit.edu
+-----------------------------------------------------------------
+v228   9/21/94  Fix bug in signals: repeated calls to
+                get_editkeys overflowed st_edittable.
+v229   9/21/94  Fix "Nothing to search" error if -a
+                and SRCH_PAST_EOF.
+v230   9/21/94  Don't print extra error msg in search
+                after regerror().
+v231   9/22/94  Fix hilite bug if search matches 0 chars.
+                (thanks to John Polstra)
+v232   9/23/94  Deal with weird systems that have 
+                termios.h but not tcgetattr().
+                Posted to prep.ai.mit.edu
+-----------------------------------------------------------------
+v233   9/26/94  Use get_term() instead of pos_init() in
+                psignals to re-get lower_left termcap.
+                (Thanks to John Malecki)
+v234   9/26/94  Make MIDDLE closer to middle of screen.
+v235   9/27/94  Use local strchr if system doesn't have.
+v236   9/28/94  Don't use libucb; use libterm if 
+                libtermcap & libcurses doesn't work.
+                (Fix for Solaris; thanks to Frank Kaefer)
+v237   9/30/94  Use system isupper() etc if provided.
+                Posted to prep.ai.mit.edu
+-----------------------------------------------------------------
+v238   10/6/94  Make binary non-blinking if LESSBINFMT
+                is set to a string without a *.
+v239   10/7/94  Don't let delimit_word run back past
+                beginning of cmdbuf.
+v240   10/10/94 Don't write into termcap buffer.
+                (Thanks to Benoit Speckel)
+v241   10/13/94 New lesskey file format.
+                Don't expand filenames in search command.
+v242   10/14/94 Allow lesskey specification of "literal".
+v243   10/14/94 Add #stop command to lesskey.
+v244   10/16/94 Add -f flag to lesskey.
+v245   10/25/94 Allow TAB_COMPLETE_FILENAME to be undefd.
+v246   10/27/94 Move help file to /usr/local/share.
+v247   10/27/94 Add -V option.
+v248   11/5/94  Add -V option to lesskey.
+v249   11/5/94  Remove -f flag from lesskey; default
+                input file is ~/.lesskey.in, not stdin.
+v250   11/7/94  Lesskey input file "-" means stdin.
+v251   11/9/94  Convert cfgetospeed result to ospeed.
+                (Thanks to Andrew Chernov)
+v252   11/16/94 Change default lesskey input file from 
+                .lesskey.in to .lesskey.
+                Posted to prep.ai.mit.edu
+-----------------------------------------------------------------
+v253   11/21/94 Fix bug when tags file has a backslash.
+v254   12/6/94  Fix -k option.
+v255   12/8/94  Add #define EXAMINE to disable :e etc.
+v256   12/10/94 Change highlighting: only highlite search
+                results (but now it is reliable).
+v257   12/10/94 Add goto_line and repaint_highlight
+                to optimize highlight repaints.
+v258   12/12/94 Fixup in hilite_line if BS_SPECIAL.
+v259   12/12/94 Convert to autoconf 2.0.
+v260   12/13/94 Add SECURE define.
+v261   12/14/94 Use system WERASE char as EC_W_BACKSPACE.
+v262   12/16/94 Add -g/-G flag and screen_hilite.
+v263   12/20/94 Reimplement/optimize -G flag behavior.
+v264   12/23/94 Allow EXTRA string after line-edit cmd
+                in lesskey file.
+v265   12/24/94 Add LESSOPEN=|cmd syntax.
+v266   12/26/94 Add -I flag.
+v267   12/28/94 Formalize the four-byte header emitted
+                by a LESSOPEN pipe.
+v268   12/28/94 Get rid of four-byte header.
+v269   1/2/95   Close alt file before open new one.
+                Avoids multiple popen().
+v270   1/3/95   Use VISUAL; use S_ISDIR/S_ISREG; fix
+                config problem with Solaris POSIX regcomp.
+v271   1/4/95   Don't quit on read error.
+v272   1/5/95   Get rid of -L.
+v273   1/6/95   Fix ch_ungetchar bug; don't call
+                LESSOPEN on a pipe.
+v274   1/6/95   Ported to OS/2 (thanks to Kai Uwe Rommel)
+v275   1/18/95  Fix bug if toggle -G at EOF.
+v276   1/30/95  Fix OS/2 version.
+v277   1/31/95  Add "next" charset; don't display ^X 
+                for X > 128.
+v278   2/14/95  Change default for -G.
+                Posted to prep.ai.mit.edu
+-----------------------------------------------------------------
+v279   2/22/95  Add GNU options --help, --version.
+                Minor config fixes.
+v280   2/24/95  Clean up calls to glob(); don't set #
+                if we can't open the new file.
+v281   2/24/95  Repeat search should turn on hilites.
+v282   3/2/95   Minor fixes.
+v283   3/2/95   Fix homefile; make OS2 look in $HOME.
+v284   3/2/95   Error if "v" on LESSOPENed file;
+                "%" figures out file size on pipe.
+v285   3/7/95   Don't set # in lsystem; 
+                lesskey try $HOME first.
+v286   3/7/95   Reformat change history (too much free time?).
+v287   3/8/95   Fix hilite bug if overstrike multiple chars.
+v288   3/8/95   Allow lesskey to override get_editkey keys.
+v289   3/9/95   Fix adj_hilite bug when line gets processed by
+                hilite_line more than once.
+v290   3/9/95   Make configure automatically.  Fix Sequent problem
+                with incompatible sigsetmask().
+                Posted to prep.ai.mit.edu
+-----------------------------------------------------------------
+v291   3/21/95  Add #env to lesskey.  Fix MS-DOS build.
+                Posted to simtel.
+-----------------------------------------------------------------
+v292   4/24/95  Add MS-DOS support for Borland C.
+                Fix arrow keys in MS-DOS versions.
+v293   4/28/95  Add auto-versioning stuff to make dist.
+v294   5/12/95  Fix Borland build.
+v295   1/20/96  Fix search on squished file; add /@@.
+v296   1/23/96  Allow cmdbuf larger than screen width.
+v297   1/24/96  Don't call termcap if tgetent fails; 
+                add #defines for buffers.
+v298   1/24/96  Change @@ to ^K.  
+                Add alternate search modifiers ^N, ^F, ^E.
+v299   1/25/96  Fix percent overflow in jump_percent (thanks to Brent Wiese);
+                don't send "ti" after shell command till RETURN pressed.
+v300   1/25/96  Change -U to print tabs as ^I.
+v301   1/30/96  Make hilites work in cmd F output.
+v302   1/31/96  Fix cmd F to notice window-change signals.
+v303   1/31/96  Add ESC-SPACE command.
+v304   2/1/96   Add ^R search modifier; add LESSSECURE.
+v305   2/2/96   Workaround Linux /proc kernel bug; add LESSKEY.
+v306   3/16/96  Minor fixes.
+v307   3/25/96  Allow cmd line arg "--"; fix DOS & OS/2 defines.h.
+v308   4/4/96   Port to OS-9 (thanks to Boisy Pitre); fix -d.
+v309   4/9/96   Fix OS-9 version; fix tags bug with "$".
+v310   4/10/96  Get rid of HELPFILE.
+v311   4/22/96  Add Windows32 support; merge doscreen.c into screen.c.
+v312   4/24/96  Don't quit after "cannot reopen" error.
+v313   4/25/96  Added horizontal scrolling.
+v314   4/26/96  Modified -e to quit on reaching end of a squished file.
+v315   4/26/96  Fix "!;TAB" bug.
+v316   5/2/96   Make "|a" when (a < curr screen) go to end of curr screen.
+v317   5/14/96  Various fixes for the MS-DOS and OS/2 builds.
+                Added ## and %% handling for filenames
+v318   5/29/96  Port to OS-9 Microware compiler; minor fixes 
+                (thanks to Martin Gregorie).
+v319   7/8/96   Fix Windows port (thanks to Jeff Paquette).
+v320   7/11/96  Final fixes for Windows port.
+v321   7/18/96  Minor fixes.
+                Posted to Web page.
+-----------------------------------------------------------------
+v322   8/13/96  Fix bug in shell escape from help file; add support for 
+                Microsoft Visual C under Windows; numerous small fixes.
+v323   8/19/96  Fixes for Windows version (thanks to Simon Munton);
+                fix for Linux library weirdness (thanks to Jim Diamond);
+                port to DJGPP (thanks to Eli Zaretskii).
+v324   8/21/96  Add support for spaces in filenames (thanks to Simon Munton).
+v325   8/21/96  Add lessecho, for spaces in filenames under Unix.
+v326   8/27/96  Fix DJGPP version.
+v327   9/1/96   Reorganize lglob, make spaces in filenames work better in Unix.
+v328   10/7/96  Append / to directory name in filename completion.
+                Fix MS-DOS and OS-9 versions.
+v329   10/11/96 Fix more MS-DOS bugs; add LESSSEPARATOR; add -" option.
+                Add LESSMETACHARS, LESSMETAESCAPE.
+v330   10/21/96 Minor fixes.
+                Posted to Web page.
+-----------------------------------------------------------------
+v331   4/22/97  Various Windows fixes (thanks to Gurusamy Sarathy).
+v332   4/22/97  Enter filenames from cmd line into edit history.
+                Posted to Web page.
+-----------------------------------------------------------------
+v333    3/4/99  Changed -w to highlite new line after forward movement.
+v334    3/9/99  Avoid overflowing prompt buffer; add %d and %D.
+v335   3/20/99  Add EBCDIC support (thanks to Thomas Dorner).
+                Use HOMEDRIVE/HOMEPATH on Windows (thanks to Preston Bannister).
+                Posted to Web page.
+-----------------------------------------------------------------
+v336    4/8/99  Fix installation bugs.
+v337    4/9/99  Fix another installation bug.
+                Posted to Web page.
+-----------------------------------------------------------------
+v338   4/13/99  Add support for long option names.
+v339   4/18/99  Add \k, long option names to lesskey.  Add -^P.  Add :d.
+v340   4/21/99  Add regexec2.  Fix Windows build.
+                Posted to Web page.
+-----------------------------------------------------------------
+v341    5/6/99  Add -F option; %c & ?c prompt escapes.
+                (Thanks to Michele Maltoni)
+v342   7/22/99  Add system-wide lesskey file; allow GPL or Less License.
+v343   9/23/99  Support UTF-8 (Thanks to Robert Brady).
+                Add %P and ?P in prompts.
+v344  10/27/99  -w highlights target line of g and p commands.
+v345  10/29/99  Make -R pass thru ESC but not other control chars.
+                Posted to Web page.
+-----------------------------------------------------------------
+v346   11/4/99  Fix bugs in long option processing; R cmd should clear hilites.
+                Posted to Web page.
+-----------------------------------------------------------------
+v347  12/13/99  Fixes for DJGPP version (thanks to Eli Zaretskii).
+v348  12/28/99  Fix deleting file with marks (thanks to Dimitar Jekov).
+                Fix color problem in DJGPP version (thanks to Eli Zaretskii).
+v349   1/24/00  Fix minor DJGPP bugs; check environment vars for UTF-8;
+                add --with-editor (thanks to Eli, Markus Kuhn, Thomas Schoepf).
+v350   3/1/00   Fix clear-while-standout bug.
+v351   3/5/00   Change -M and = prompts to show top & bottom line number.
+                Posted to Web page.
+-----------------------------------------------------------------
+v352   3/8/00   Fix scan_option NULL dereference.
+-----------------------------------------------------------------
+v353   3/20/00  Fix SECURE compile bug, allow space after numeric option.
+v354   3/23/00  Add support for PCRE; add --with-regex configure option.
+-----------------------------------------------------------------
+v355   6/28/00  Add -# option (thanks to Andy Levinson).
+v356   7/5/00   Add -J option.
+v357   7/6/00   Support sigprocmask.
+-----------------------------------------------------------------
+v358   7/8/00   Fix problems with #stop in lesskey file.
+                Posted to Web page.
+-----------------------------------------------------------------
+v359  9/10/00   Fixes for Win32 display problems (thanks to Maurizio Vairani).
+v360  1/17/01   Move sysless to etc.
+v361  12/4/01   Add IBM-1047 charset & EBCDIC fixes (thanks to Thomas Dorner).
+                Fix 32 bit dependencies (thanks to Paul Eggert).
+                Fix UTF-8 overstriking (thanks to Robert Brady).
+v362  12/4/01   Make status column show search targets.
+v363  12/6/01   Add --no-keypad option.
+                Add variable width tabstops (thanks to Peter Samuelson).
+v364 12/10/01   Better handling of very long lines in input;
+                Fix horizontal shifting of colored text.
+v365 12/11/01   Fix overstriking of tabs;
+                Add support for global(1) and multiple tag matches
+                (thanks to Shigio Yamaguchi and Tim Vanderhoek).
+v366 12/11/01   Fixes for OS/2 (thanks to Kyosuke Tokoro).
+v367 12/13/01   Allow -D and -x options to terminate without dollar sign;
+                Right/left arrow when entering N are shift cmds, not line edit.
+v368 12/18/01   Update lesskey commands.
+v370 12/23/01   Fix tags error messages.
+                Posted to Web page.
+-----------------------------------------------------------------
+v371 12/26/01   Fix new_file bug; use popen in Windows version;
+                fix some compiler warnings.
+v372 12/29/01   Make -b be in units of 1K.
+v373  1/14/02   Improve handling of filenames containing shell metachars.
+v374   2/7/02   Fix memory leak; fix bug in -x argument parsing.
+v375   4/7/02   Fix searching for SGR sequences; fix SECURE build;
+                add SGR support to DJGPP version (thanks to Eli Zaretskii).
+v376  6/10/02   Fix bug in overstriking mulitbyte UTF-8 characters
+                (thanks to Jungshik Shin).
+                Posted to Web page.
+-----------------------------------------------------------------
+v377  9/10/02   Fix bug in Windows version when file contains CR;
+                fix bug in search highlights with -R;
+                make initial buffer limit really be 64K not unlimited.
+v378  9/30/02   Misc bug fixes and compiler warning cleanup.
+                Posted to Web page.
+-----------------------------------------------------------------
+v379 11/23/02   Add -L option; fix bug with ctrl-K in lesskey files;
+                improve UTF-8 overstriking and underscore overstriking;
+                fix minor man page problems; change to autoconf 2.54.
+v380 11/24/02   Make LINENUM same as POSITION.
+v381 11/28/02   Make -N use 7 columns for line number if possible.
+-----------------------------------------------------------------
+v382   2/3/04   Remove copyrighted code.
+-----------------------------------------------------------------
+v383  2/16/04   Add history file; add -K option; improve UTF-8 handling;
+                fix some signed char bugs (thanks to Christian Biere);
+                fix some upper/lower case bugs (thanks to Bjoern Jacke);
+                add erase2 char (thanks to David Lawrence);
+                add windows charset (thanks to Dimitar Zhekov).
+v384  2/20/04   Improvements in UTF-8 handling.
+v385  2/23/04   Fix UTF-8 output bug.
+-----------------------------------------------------------------
+v386  9/13/05   Improvements to UTF-8 shift & color (thanks to Charles Levert);
+                protect against invalid LESSOPEN and LESSCLOSE values.
+v387  9/14/05   Update Charles Levert's UTF-8 patch.
+v388  9/14/05   Change history behavior; change most sprintf calls to snprintf.
+v389  9/14/05   Fix copy & paste with long lines; improve performance of 
+                expand_linebuf; fix crash in init_mlist; 
+v390  9/15/05   Show search matches in status column even if -G is set.
+-----------------------------------------------------------------
+v391  9/17/05   Fix bugs.
+v392  10/14/05  Fix line wrapping bug.
+v393  10/19/05  Allow multiple attributes per char; fix bold+underline bug
+                (thanks again to Charles Levert).
+v394  11/8/05   Fix prompt bug; fix compile problem in Windows build.
+-----------------------------------------------------------------
+v395  1/12/07   Update Unicode tables (thanks to Charles Levert);
+                don't chmod if LESSHISTFILE = /dev/null;
+                make -f work for directories; support DESTDIR in Makefile;
+                fix sigset_t detection in configure; 
+                make "t" cmd traverse tags in correct order
+v396  1/13/07   Add compatibility with POSIX more.
+v397  3/21/07   Allow decimal point in number for % command;
+                Allow decimal point in number for -j option;
+                Allow n command to fetch last search pattern from history
+                (thanks to arno).
+v398  3/22/07   Don't rewrite history file if not necessary;
+                fix bug when filenames contain "$".
+v399  3/22/07   Don't move to bottom of screen at startup;
+                don't output extraneous newlines.
+v400  3/23/07   Allow search to find pattern after null byte (PCRE and no-regex)
+                (thanks to Michael Constant).
+-----------------------------------------------------------------
+v401  3/24/07   Minor documentation fixes.
+v402  3/30/07   Fix autoconf bug when memcpy etc are inline;
+                fix bug in terminating number following -j option.
+v403  5/25/07   Fix Windows build.
+v404  6/5/07    Fix display bug with F command and long lines.
+v405  6/17/07   Fix display bug when using -w option.
+v406  6/17/07   Fix secure build.
+v407  8/16/07   Fix bugs; support CSI chars.
+v408  10/1/07   Fix bug in -i with non-ASCII chars.
+v409  10/12/07  Fix crash when viewing text with invalid UTF-8 sequences.
+v411  11/6/07   Fix case-insensitive searching with non-ASCII text.
+v412  11/6/07   Use symbolic SEEK constants.
+v413  11/6/07   Fix search highlight bug with non-ASCII text.
+v414  11/6/07   Fix display bug with no-wrap terminals.
+v415  11/14/07  Add --follow-name option.
+v416  11/22/07  Fix crash when searching text with invalid UTF-8 sequences.
+v417  12/31/07  Don't support single-char CSI in UTF-8 mode;
+                fix bug with -R and invalid CSI sequences;
+                fix bug searching text with SGR sequences with -r;
+                emulate SGR sequences in WIN32 build.
+v418  12/31/07  Clean up.
+-----------------------------------------------------------------
+v419  1/16/08   Make CSI char 0x9B work in UTF-8 mode (thanks to Colin Watson).
+v420  2/24/08   Add & command; fix -F option; fix '' after G.
+v421  2/24/08   Ignore filtered lines when searching.
+v422  3/2/08    Output CR at startup.
+v423  5/27/08   Clean up.
+v424  6/16/08   Fix compile bug with pcre; don't filter help file.
+v425  7/14/08   Fix non-ANSI code in list handling in ch.c.
+v426  10/27/08  Fix ignaw terminal handling (thanks to Per Hedeland);
+                fix binary file detection in UTF-8 mode.
+v427  3/16/09   A few Win32 fixes (thanks to Jason Hood).
+v428  3/30/09   Add "|-" syntax to LESSOPEN.
+v429  4/10/09   Fix search highlighting bug with underlined text.
+-----------------------------------------------------------------
+v430  4/22/09   Don't pass "-" to non-pipe LESSOPEN unless it starts with "-".
+v431  4/29/09   Fix highlight bug when match is at end of line.
+v432  6/27/09   Better fix for highlight bugs;
+                fix new problems with ignaw terminals.
+v433  6/28/09   Cleanup search code.
+v434  6/29/09   More cleanup.
+v435  7/04/09   Fix bugs with non-regex filtering.
+v436  7/05/09   Fix memory leak.
+-----------------------------------------------------------------
+v437  7/14/09   Fix bug in handling some long option names;
+                make percentage calculation more accurate.
+v438  12/29/10  Fix bugs with -i/-I and & filtering; 
+                exit with status 2 on ctrl-C with -K.
+v439  12/31/10  Add -A option.
+v440  1/5/11    Fix bug displaying prompt after = command.
+v441  1/21/11   Fix semi-infinite loop if no newlines in file;
+                make new -A behavior the default.
+-----------------------------------------------------------------
+v442  3/2/11    Fix search bug.
+                Add ctrl-G line edit command.
+v443  4/9/11    Fix Windows build.
+v444  6/8/11    Fix ungetc bug; remove vestiges of obsolete -l option.
+*/
+
+char version[] = "444";