Import of less-403.
authorPeter Avalos <pavalos@dragonflybsd.org>
Sun, 3 Jun 2007 04:18:02 +0000 (04:18 +0000)
committerPeter Avalos <pavalos@dragonflybsd.org>
Sun, 3 Jun 2007 04:18:02 +0000 (04:18 +0000)
48 files changed:
contrib/less-403/COPYING [new file with mode: 0755]
contrib/less-403/LICENSE [new file with mode: 0755]
contrib/less-403/NEWS [new file with mode: 0644]
contrib/less-403/brac.c [new file with mode: 0755]
contrib/less-403/ch.c [new file with mode: 0755]
contrib/less-403/charset.c [new file with mode: 0755]
contrib/less-403/charset.h [new file with mode: 0644]
contrib/less-403/cmd.h [new file with mode: 0755]
contrib/less-403/cmdbuf.c [new file with mode: 0755]
contrib/less-403/command.c [new file with mode: 0755]
contrib/less-403/decode.c [new file with mode: 0755]
contrib/less-403/edit.c [new file with mode: 0755]
contrib/less-403/filename.c [new file with mode: 0755]
contrib/less-403/forwback.c [new file with mode: 0755]
contrib/less-403/funcs.h [new file with mode: 0644]
contrib/less-403/help.c [new file with mode: 0644]
contrib/less-403/ifile.c [new file with mode: 0755]
contrib/less-403/input.c [new file with mode: 0755]
contrib/less-403/jump.c [new file with mode: 0755]
contrib/less-403/less.h [new file with mode: 0755]
contrib/less-403/less.nro [new file with mode: 0644]
contrib/less-403/lessecho.c [new file with mode: 0755]
contrib/less-403/lessecho.nro [new file with mode: 0644]
contrib/less-403/lesskey.c [new file with mode: 0755]
contrib/less-403/lesskey.h [new file with mode: 0755]
contrib/less-403/lesskey.nro [new file with mode: 0644]
contrib/less-403/lglob.h [new file with mode: 0755]
contrib/less-403/line.c [new file with mode: 0755]
contrib/less-403/linenum.c [new file with mode: 0755]
contrib/less-403/lsystem.c [new file with mode: 0755]
contrib/less-403/main.c [new file with mode: 0755]
contrib/less-403/mark.c [new file with mode: 0755]
contrib/less-403/optfunc.c [new file with mode: 0755]
contrib/less-403/option.c [new file with mode: 0755]
contrib/less-403/option.h [new file with mode: 0755]
contrib/less-403/opttbl.c [new file with mode: 0755]
contrib/less-403/os.c [new file with mode: 0755]
contrib/less-403/output.c [new file with mode: 0755]
contrib/less-403/pckeys.h [new file with mode: 0755]
contrib/less-403/position.c [new file with mode: 0755]
contrib/less-403/position.h [new file with mode: 0755]
contrib/less-403/prompt.c [new file with mode: 0755]
contrib/less-403/screen.c [new file with mode: 0755]
contrib/less-403/search.c [new file with mode: 0755]
contrib/less-403/signal.c [new file with mode: 0755]
contrib/less-403/tags.c [new file with mode: 0755]
contrib/less-403/ttyin.c [new file with mode: 0755]
contrib/less-403/version.c [new file with mode: 0755]

diff --git a/contrib/less-403/COPYING b/contrib/less-403/COPYING
new file mode 100755 (executable)
index 0000000..d104746
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/contrib/less-403/LICENSE b/contrib/less-403/LICENSE
new file mode 100755 (executable)
index 0000000..7e4887b
--- /dev/null
@@ -0,0 +1,27 @@
+                          Less License
+                          ------------
+
+Less
+Copyright (C) 1984-2005  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/contrib/less-403/NEWS b/contrib/less-403/NEWS
new file mode 100644 (file)
index 0000000..ddd3137
--- /dev/null
@@ -0,0 +1,685 @@
+
+                     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 394 and 403
+
+* 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/contrib/less-403/brac.c b/contrib/less-403/brac.c
new file mode 100755 (executable)
index 0000000..20c7353
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 1984-2007  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/contrib/less-403/ch.c b/contrib/less-403/ch.c
new file mode 100755 (executable)
index 0000000..61bc83a
--- /dev/null
@@ -0,0 +1,841 @@
+/*
+ * Copyright (C) 1984-2007  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
+
+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".
+ */
+#define        LBUFSIZE        8192
+struct buf {
+       struct buf *next, *prev;
+       struct buf *hnext, *hprev;
+       BLOCKNUM block;
+       unsigned int datasize;
+       unsigned char data[LBUFSIZE];
+};
+
+struct buflist {
+       /* -- Following members must match struct buf */
+       struct buf *buf_next, *buf_prev;
+       struct buf *buf_hnext, *buf_hprev;
+};
+
+/*
+ * 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 buf *buf_next, *buf_prev;
+       struct buflist hashtbl[BUFHASH_SIZE];
+       int file;
+       int flags;
+       POSITION fpos;
+       int nbufs;
+       BLOCKNUM block;
+       unsigned int offset;
+       POSITION fsize;
+};
+
+#define        ch_bufhead      thisfile->buf_next
+#define        ch_buftail      thisfile->buf_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    ((struct buf *)&thisfile->buf_next)
+#define        END_OF_HCHAIN(h) ((struct buf *)&thisfile->hashtbl[h])
+#define BUFHASH(blk)   ((blk) & (BUFHASH_SIZE-1))
+
+#define        FOR_BUFS_IN_CHAIN(h,bp) \
+       for (bp = thisfile->hashtbl[h].buf_hnext;  \
+            bp != END_OF_HCHAIN(h);  bp = bp->hnext)
+
+#define        HASH_RM(bp) \
+       (bp)->hnext->hprev = (bp)->hprev; \
+       (bp)->hprev->hnext = (bp)->hnext;
+
+#define        HASH_INS(bp,h) \
+       (bp)->hnext = thisfile->hashtbl[h].buf_hnext; \
+       (bp)->hprev = END_OF_HCHAIN(h); \
+       thisfile->hashtbl[h].buf_hnext->hprev = (bp); \
+       thisfile->hashtbl[h].buf_hnext = (bp);
+
+static struct filestate *thisfile;
+static int ch_ungotchar = -1;
+static int maxbufs = -1;
+
+extern int autobuf;
+extern int sigs;
+extern int secure;
+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.
+ * ch_get() is a macro which is more efficient to call
+ * than fch_get (the function), in the usual case 
+ * that the block desired is at the head of the chain.
+ */
+#define        ch_get()   ((ch_block == ch_bufhead->block && \
+                    ch_offset < ch_bufhead->datasize) ? \
+                       ch_bufhead->data[ch_offset] : fch_get())
+       int
+fch_get()
+{
+       register struct buf *bp;
+       register int n;
+       register int slept;
+       register int h;
+       POSITION pos;
+       POSITION len;
+
+       slept = FALSE;
+
+       /*
+        * Look for a buffer holding the desired block.
+        */
+       h = BUFHASH(ch_block);
+       FOR_BUFS_IN_CHAIN(h, bp)
+       {
+               if (bp->block == ch_block)
+               {
+                       if (ch_offset >= bp->datasize)
+                               /*
+                                * Need more data in this buffer.
+                                */
+                               goto read_more;
+                       goto found;
+               }
+       }
+       /*
+        * 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 || 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;
+       }
+       bp = ch_buftail;
+       HASH_RM(bp); /* Remove from old hash chain. */
+       bp->block = ch_block;
+       bp->datasize = 0;
+       HASH_INS(bp, 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, 0) == 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 (sigs)
+                       return (EOI);
+       }
+
+    found:
+       if (ch_bufhead != bp)
+       {
+               /*
+                * Move the buffer to the head of the buffer chain.
+                * This orders the buffer chain, most- to least-recently used.
+                */
+               bp->next->prev = bp->prev;
+               bp->prev->next = bp->next;
+               bp->next = ch_bufhead;
+               bp->prev = END_OF_CHAIN;
+               ch_bufhead->prev = bp;
+               ch_bufhead = bp;
+
+               /*
+                * Move to head of hash chain too.
+                */
+               HASH_RM(bp);
+               HASH_INS(bp, 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;
+       int warned = FALSE;
+       BLOCKNUM block;
+       BLOCKNUM nblocks;
+
+       nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE;
+       for (block = 0;  block < nblocks;  block++)
+       {
+               for (bp = ch_bufhead;  ;  bp = bp->next)
+               {
+                       if (bp == END_OF_CHAIN)
+                       {
+                               if (!warned)
+                               {
+                                       error("Warning: log file is incomplete",
+                                               NULL_PARG);
+                                       warned = TRUE;
+                               }
+                               break;
+                       }
+                       if (bp->block == block)
+                       {
+                               write(logfile, (char *) bp->data, bp->datasize);
+                               break;
+                       }
+               }
+       }
+}
+
+#endif
+
+/*
+ * Determine if a specific block is currently in one of the buffers.
+ */
+       static int
+buffered(block)
+       BLOCKNUM block;
+{
+       register struct buf *bp;
+       register int h;
+
+       h = BUFHASH(block);
+       FOR_BUFS_IN_CHAIN(h, bp)
+       {
+               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;
+
+       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 (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 buf *bp, *firstbp;
+
+       /*
+        * 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.
+        */
+       firstbp = bp = ch_bufhead;
+       if (bp == END_OF_CHAIN)
+               return (1);
+       while ((bp = bp->next) != END_OF_CHAIN)
+               if (bp->block < firstbp->block)
+                       firstbp = bp;
+       ch_block = firstbp->block;
+       ch_offset = 0;
+       return (0);
+}
+
+/*
+ * Return the length of the file, if known.
+ */
+       public POSITION
+ch_length()
+{
+       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()
+{
+       return (ch_block * LBUFSIZE) + ch_offset;
+}
+
+/*
+ * Get the current char and post-increment the read pointer.
+ */
+       public int
+ch_forw_get()
+{
+       register int c;
+
+       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 (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 buf *bp;
+
+       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 (bp = ch_bufhead;  bp != END_OF_CHAIN;  bp = bp->next)
+               bp->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, 0) == 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;
+
+       /*
+        * 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;
+       bp->next = END_OF_CHAIN;
+       bp->prev = ch_buftail;
+       ch_buftail->next = bp;
+       ch_buftail = bp;
+       HASH_INS(bp, 0);
+       return (0);
+}
+
+/*
+ *
+ */
+       static void
+init_hashtbl()
+{
+       register int h;
+
+       for (h = 0;  h < BUFHASH_SIZE;  h++)
+       {
+               thisfile->hashtbl[h].buf_hnext = END_OF_HCHAIN(h);
+               thisfile->hashtbl[h].buf_hprev = END_OF_HCHAIN(h);
+       }
+}
+
+/*
+ * Delete all buffers for this file.
+ */
+       static void
+ch_delbufs()
+{
+       register struct buf *bp;
+
+       while (ch_bufhead != END_OF_CHAIN)
+       {
+               bp = ch_bufhead;
+               bp->next->prev = bp->prev;;
+               bp->prev->next = bp->next;
+               free(bp);
+       }
+       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, 0) != 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->buf_next = thisfile->buf_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 (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()
+{
+       return (ch_flags);
+}
+
+#if 0
+       public void
+ch_dump(struct filestate *fs)
+{
+       struct buf *bp;
+       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 (bp = fs->buf_next; bp != (struct buf *)fs;  bp = bp->next)
+       {
+               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/contrib/less-403/charset.c b/contrib/less-403/charset.c
new file mode 100755 (executable)
index 0000000..3ee76bc
--- /dev/null
@@ -0,0 +1,1109 @@
+/*
+ * Copyright (C) 1984-2007  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)
+       unsigned char c;
+{
+       c &= 0377;
+       return (chardef[c] & IS_BINARY_CHAR);
+}
+
+/*
+ * Is a given character a "control" character?
+ */
+       public int
+control_char(c)
+       int 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)
+       int 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", 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", 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:
+               return (LWCHAR)
+                       (p[0] & 0xFF);
+       case 2:
+               return (LWCHAR) (
+                       ((p[0] & 0x1F) << 6) |
+                       (p[1] & 0x3F));
+       case 3:
+               return (LWCHAR) (
+                       ((p[0] & 0x0F) << 12) |
+                       ((p[1] & 0x3F) << 6) |
+                       (p[2] & 0x3F));
+       case 4:
+               return (LWCHAR) (
+                       ((p[0] & 0x07) << 18) |
+                       ((p[1] & 0x3F) << 12) | 
+                       ((p[2] & 0x3F) << 6) | 
+                       (p[3] & 0x3F));
+       case 5:
+               return (LWCHAR) (
+                       ((p[0] & 0x03) << 24) |
+                       ((p[1] & 0x3F) << 18) | 
+                       ((p[2] & 0x3F) << 12) | 
+                       ((p[3] & 0x3F) << 6) | 
+                       (p[4] & 0x3F));
+       case 6:
+               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));
+       }
+}
+
+/*
+ * 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;
+       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)
+       {
+               if (p + utf_len(*p) > limit)
+                       ch = 0;
+               else
+               {
+                       ch = get_wchar(p);
+                       p++;
+                       while (IS_UTF8_TRAIL(*p))
+                               p++;
+               }
+       } 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,  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/contrib/less-403/charset.h b/contrib/less-403/charset.h
new file mode 100644 (file)
index 0000000..548033e
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2007  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/contrib/less-403/cmd.h b/contrib/less-403/cmd.h
new file mode 100755 (executable)
index 0000000..298d367
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 1984-2007  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_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_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/contrib/less-403/cmdbuf.c b/contrib/less-403/cmdbuf.c
new file mode 100755 (executable)
index 0000000..6effb60
--- /dev/null
@@ -0,0 +1,1474 @@
+/*
+ * Copyright (C) 1984-2007  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;
+{
+       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;
+}
+
+#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_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);
+}
+
+/*
+ * 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);
+}
+
+#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;
+#if SHELL_ESCAPE || PIPEC
+               else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0)
+                       ml = &mlist_shell;
+#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;
+
+       filename = histfile_name();
+       if (filename == NULL)
+               return;
+       if (!mlist_search.modified && !mlist_shell.modified)
+               return;
+       f = fopen(filename, "w");
+       free(filename);
+       if (f == NULL)
+               return;
+#if HAVE_FCHMOD
+       /* Make history file readable only by owner. */
+       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/contrib/less-403/command.c b/contrib/less-403/command.c
new file mode 100755 (executable)
index 0000000..da0f3ea
--- /dev/null
@@ -0,0 +1,1657 @@
+/*
+ * Copyright (C) 1984-2007  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 hit_eof;
+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;
+
+static char ungot[UNGOT_SIZE];
+static char *ungotp = NULL;
+#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 char optchar;
+static int optflag;
+static int optgetname;
+static POSITION bottompos;
+static int save_hshift;
+#if PIPEC
+static char pipec;
+#endif
+
+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()
+{
+       clear_attn();
+       line_left();
+       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 (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 (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;
+       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(optchar, cbuf, optflag);
+               optchar = '\0';
+               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
+       }
+}
+
+/*
+ * Add a character to a multi-character command.
+ */
+       static int
+mca_char(c)
+       int c;
+{
+       char *p;
+       int flag;
+       char buf[3];
+       PARG parg;
+
+       switch (mca)
+       {
+       case 0:
+               /*
+                * 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.
+                        * Treat as a normal command character.
+                        */
+                       number = cmd_int(&fraction);
+                       mca = 0;
+                       cmd_accept();
+                       return (NO_MCA);
+               }
+               break;
+
+       case A_OPT_TOGGLE:
+               /*
+                * Special case for the TOGGLE_OPTION command.
+                * If the option letter which was entered is a
+                * single-char option, execute the command immediately,
+                * so user doesn't have to hit RETURN.
+                * If the first char is + or -, this indicates
+                * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.
+                * "--" begins inputting a long option name.
+                */
+               if (optchar == '\0' && len_cmdbuf() == 0)
+               {
+                       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);
+                               }
+                       }
+               }
+               if (optgetname)
+               {
+                       /*
+                        * We're getting a long option name.
+                        * See if we've matched an option name yet.
+                        * If so, display the complete name and stop 
+                        * accepting chars until user hits RETURN.
+                        */
+                       struct loption *o;
+                       char *oname;
+                       int lc;
+
+                       if (c == '\n' || c == '\r')
+                       {
+                               /*
+                                * When the user hits RETURN, make sure
+                                * we've matched an option name, then
+                                * pretend he just entered the equivalent
+                                * option letter.
+                                */
+                               if (optchar == '\0')
+                               {
+                                       parg.p_string = get_cmdbuf();
+                                       error("There is no --%s option", &parg);
+                                       return (MCA_DONE);
+                               }
+                               optgetname = FALSE;
+                               cmd_reset();
+                               c = optchar;
+                       } else
+                       {
+                               if (optchar != '\0')
+                               {
+                                       /*
+                                        * Already have a match for the name.
+                                        * Don't accept anything but erase/kill.
+                                        */
+                                       if (c == erase_char ||
+                                           c == erase2_char ||
+                                           c == kill_char)
+                                               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();
+                               lc = ASCII_IS_LOWER(p[0]);
+                               o = findopt_name(&p, &oname, NULL);
+                               if (o != NULL)
+                               {
+                                       /*
+                                        * Got a match.
+                                        * Remember the option letter and
+                                        * display the full option name.
+                                        */
+                                       optchar = o->oletter;
+                                       if (!lc && ASCII_IS_LOWER(optchar))
+                                               optchar = ASCII_TO_UPPER(optchar);
+                                       cmd_reset();
+                                       mca_opt_toggle();
+                                       for (p = oname;  *p != '\0';  p++)
+                                       {
+                                               c = *p;
+                                               if (!lc && ASCII_IS_LOWER(c))
+                                                       c = ASCII_TO_UPPER(c);
+                                               if (cmd_char(c) != CC_OK)
+                                                       return (MCA_DONE);
+                                       }
+                               }
+                               return (MCA_MORE);
+                       }
+               } else
+               {
+                       if (c == erase_char || c == erase2_char || c == kill_char)
+                               break;
+                       if (optchar != '\0')
+                               /* We already have the option letter. */
+                               break;
+               }
+
+               optchar = c;
+               if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE ||
+                   single_char_option(c))
+               {
+                       toggle_option(c, "", optflag);
+                       return (MCA_DONE);
+               }
+               /*
+                * Display a prompt appropriate for the option letter.
+                */
+               if ((p = opt_prompt(c)) == NULL)
+               {
+                       buf[0] = '-';
+                       buf[1] = c;
+                       buf[2] = '\0';
+                       p = buf;
+               }
+               start_mca(A_OPT_TOGGLE, p, (void*)NULL, 0);
+               return (MCA_MORE);
+
+       case A_F_SEARCH:
+       case A_B_SEARCH:
+               /*
+                * Special case for search commands.
+                * 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)
+                       /*
+                        * Only works for the first char of the pattern.
+                        */
+                       break;
+
+               flag = 0;
+               switch (c)
+               {
+               case CONTROL('E'): /* ignore END of file */
+               case '*':
+                       flag = SRCH_PAST_EOF;
+                       break;
+               case CONTROL('F'): /* FIRST file */
+               case '@':
+                       flag = SRCH_FIRST_FILE;
+                       break;
+               case CONTROL('K'): /* KEEP position */
+                       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);
+               }
+               break;
+       }
+
+       /*
+        * Any other multicharacter 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);
+}
+
+/*
+ * 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;
+               save_top_scroll = top_scroll;
+               top_scroll = 1;
+               repaint();
+               top_scroll = save_top_scroll;
+       }
+}
+
+/*
+ * Display the appropriate prompt.
+ */
+       static void
+prompt()
+{
+       register char *p;
+
+       if (ungotp != NULL && ungotp > ungot)
+       {
+               /*
+                * 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
+        * (or -F is set and this is the first prompt), then quit.
+        * {{ Relying on "first prompt" to detect a single-screen file
+        * fails if +G is used, for example. }}
+        */
+       if ((get_quit_at_eof() == OPT_ONPLUS || quit_if_one_screen) &&
+           hit_eof && !(ch_getflags() & CH_HELPFILE) && 
+           next_ifile(curr_ifile) == NULL_IFILE)
+               quit(QUIT_OK);
+       quit_if_one_screen = FALSE;
+#if 0 /* This doesn't work well because some "te"s clear the screen. */
+       /*
+        * If the -e flag is set and we've hit EOF on the last file,
+        * and the file is squished (shorter than the screen), quit.
+        */
+       if (get_quit_at_eof() && squished &&
+           next_ifile(curr_ifile) == NULL_IFILE)
+               quit(QUIT_OK);
+#endif
+
+#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 (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 (ungotp == NULL)
+               /*
+                * Normal case: no ungotten chars, so get one from the user.
+                */
+               return (getchr());
+
+       if (ungotp > ungot)
+               /*
+                * Return the next ungotten char.
+                */
+               return (*--ungotp);
+
+       /*
+        * We have just run out of ungotten chars.
+        */
+       ungotp = NULL;
+       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());
+       }
+}
+
+/*
+ * "Unget" a command character.
+ * The next getcc() will return this character.
+ */
+       public void
+ungetcc(c)
+       int c;
+{
+       if (ungotp == NULL)
+               ungotp = ungot;
+       if (ungotp >= ungot + sizeof(ungot))
+       {
+               error("ungetcc overflow", NULL_PARG);
+               quit(QUIT_ERROR);
+       }
+       *ungotp++ = c;
+}
+
+/*
+ * 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;
+               optchar = '\0';
+
+               /*
+                * 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;
+                       hit_eof = 0;
+                       while (!sigs)
+                               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!
+                        */
+                       if (ch_getflags() & CH_CANSEEK)
+                       {
+                               ch_flush();
+                               clr_linenum();
+#if HILITE_SEARCH
+                               clr_hilite();
+#endif
+                       }
+                       /* 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_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() && hit_eof && 
+                                   !(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;
+                       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/contrib/less-403/decode.c b/contrib/less-403/decode.c
new file mode 100755 (executable)
index 0000000..3007473
--- /dev/null
@@ -0,0 +1,839 @@
+/*
+ * Copyright (C) 1984-2007  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,
+       '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 */
+};
+
+/*
+ * 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, 0) == 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/contrib/less-403/edit.c b/contrib/less-403/edit.c
new file mode 100755 (executable)
index 0000000..908f29e
--- /dev/null
@@ -0,0 +1,787 @@
+/*
+ * Copyright (C) 1984-2007  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"
+
+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
+
+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;
+}
+
+/*
+ * 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;
+                       }
+               }
+       }
+       free(qopen_filename);
+
+       /*
+        * 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 (every_first_cmd != NULL)
+                       ungetsc(every_first_cmd);
+       }
+
+       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);
+}
+
+/*
+ * 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, 2) == 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/contrib/less-403/filename.c b/contrib/less-403/filename.c
new file mode 100755 (executable)
index 0000000..8e0823e
--- /dev/null
@@ -0,0 +1,1048 @@
+/*
+ * Copyright (C) 1984-2007  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.
<