diff --git a/Makefile b/Makefile
index 6d18ce053..8debc39d9 100644
--- a/Makefile
+++ b/Makefile
@@ -224,6 +224,7 @@ include dsp/BUILD.mk # │
include third_party/stb/BUILD.mk # │
include third_party/mbedtls/BUILD.mk # │
include third_party/ncurses/BUILD.mk # │
+include third_party/readline/BUILD.mk # │
include third_party/libcxx/BUILD.mk # │
include third_party/pcre/BUILD.mk # │
include third_party/less/BUILD.mk # │
diff --git a/third_party/BUILD.mk b/third_party/BUILD.mk
index eed3b36bf..58a00b7fa 100644
--- a/third_party/BUILD.mk
+++ b/third_party/BUILD.mk
@@ -29,6 +29,7 @@ o/$(MODE)/third_party: \
o/$(MODE)/third_party/puff \
o/$(MODE)/third_party/python \
o/$(MODE)/third_party/quickjs \
+ o/$(MODE)/third_party/readline \
o/$(MODE)/third_party/regex \
o/$(MODE)/third_party/sed \
o/$(MODE)/third_party/smallz4 \
diff --git a/third_party/readline/BUILD.mk b/third_party/readline/BUILD.mk
new file mode 100644
index 000000000..8ad717af4
--- /dev/null
+++ b/third_party/readline/BUILD.mk
@@ -0,0 +1,71 @@
+#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
+#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
+
+PKGS += THIRD_PARTY_READLINE
+
+THIRD_PARTY_READLINE_ARTIFACTS += THIRD_PARTY_READLINE_A
+THIRD_PARTY_READLINE = $(THIRD_PARTY_READLINE_A_DEPS) $(THIRD_PARTY_READLINE_A)
+THIRD_PARTY_READLINE_A = o/$(MODE)/third_party/readline/readline.a
+THIRD_PARTY_READLINE_A_FILES := $(wildcard third_party/readline/*)
+THIRD_PARTY_READLINE_A_HDRS = $(filter %.h,$(THIRD_PARTY_READLINE_A_FILES))
+THIRD_PARTY_READLINE_A_INCS = $(filter %.inc,$(THIRD_PARTY_READLINE_A_FILES))
+THIRD_PARTY_READLINE_A_SRCS = $(filter %.c,$(THIRD_PARTY_READLINE_A_FILES))
+THIRD_PARTY_READLINE_A_OBJS = $(THIRD_PARTY_READLINE_A_SRCS:%.c=o/$(MODE)/%.o)
+THIRD_PARTY_READLINE_A_CHECKS = $(THIRD_PARTY_READLINE_A).pkg
+
+THIRD_PARTY_READLINE_A_DIRECTDEPS = \
+ LIBC_CALLS \
+ LIBC_FMT \
+ LIBC_INTRIN \
+ LIBC_MEM \
+ LIBC_NEXGEN32E \
+ LIBC_PROC \
+ LIBC_RUNTIME \
+ LIBC_STDIO \
+ LIBC_STR \
+ LIBC_SYSV \
+ THIRD_PARTY_MUSL \
+ THIRD_PARTY_NCURSES
+
+THIRD_PARTY_READLINE_A_DEPS := \
+ $(call uniq,$(foreach x,$(THIRD_PARTY_READLINE_A_DIRECTDEPS),$($(x))))
+
+$(THIRD_PARTY_READLINE_A): \
+ third_party/readline/ \
+ $(THIRD_PARTY_READLINE_A).pkg \
+ $(THIRD_PARTY_READLINE_A_OBJS)
+
+$(THIRD_PARTY_READLINE_A).pkg: \
+ $(THIRD_PARTY_READLINE_A_OBJS) \
+ $(foreach x,$(THIRD_PARTY_READLINE_A_DIRECTDEPS),$($(x)_A).pkg)
+
+$(THIRD_PARTY_READLINE_A_OBJS): private \
+ CPPFLAGS += \
+ -DHAVE_CONFIG_H \
+ -DRL_LIBRARY_VERSION=\"8.2\" \
+ -DBRACKETED_PASTE_DEFAULT=1
+
+$(THIRD_PARTY_READLINE_A_OBJS): private \
+ CFLAGS += \
+ -Wno-unused-but-set-variable \
+ -Wno-stringop-truncation \
+ -Wno-maybe-uninitialized \
+ -Wno-unused-variable \
+ -Wno-unused-label \
+ -Wno-parentheses \
+ -fportcosmo
+
+THIRD_PARTY_READLINE_BINS = $(THIRD_PARTY_READLINE_COMS) $(THIRD_PARTY_READLINE_COMS:%=%.dbg)
+THIRD_PARTY_READLINE_LIBS = $(foreach x,$(THIRD_PARTY_READLINE_ARTIFACTS),$($(x)))
+THIRD_PARTY_READLINE_SRCS = $(foreach x,$(THIRD_PARTY_READLINE_ARTIFACTS),$($(x)_SRCS))
+THIRD_PARTY_READLINE_HDRS = $(foreach x,$(THIRD_PARTY_READLINE_ARTIFACTS),$($(x)_HDRS))
+THIRD_PARTY_READLINE_INCS = $(foreach x,$(THIRD_PARTY_READLINE_ARTIFACTS),$($(x)_INCS))
+THIRD_PARTY_READLINE_CHECKS = $(foreach x,$(THIRD_PARTY_READLINE_ARTIFACTS),$($(x)_CHECKS))
+THIRD_PARTY_READLINE_OBJS = $(foreach x,$(THIRD_PARTY_READLINE_ARTIFACTS),$($(x)_OBJS))
+$(THIRD_PARTY_READLINE_A_OBJS): $(BUILD_FILES) third_party/readline/BUILD.mk
+
+.PHONY: o/$(MODE)/third_party/readline
+o/$(MODE)/third_party/readline: \
+ $(THIRD_PARTY_READLINE_A) \
+ $(THIRD_PARTY_READLINE_BINS) \
+ $(THIRD_PARTY_READLINE_CHECKS)
diff --git a/third_party/readline/LICENSE b/third_party/readline/LICENSE
new file mode 100644
index 000000000..94a9ed024
--- /dev/null
+++ b/third_party/readline/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/third_party/readline/README.cosmo b/third_party/readline/README.cosmo
new file mode 100644
index 000000000..05e3acc4e
--- /dev/null
+++ b/third_party/readline/README.cosmo
@@ -0,0 +1,15 @@
+DESCRIPTION
+
+ readline is a teletypewriter line editing library
+
+LICENSE
+
+ GPL v3
+
+ORIGIN
+
+ https://ftp.gnu.org/gnu/readline/readline-8.2.tar.gz
+
+LOCAL CHANGES
+
+ - Favor FIONREAD in _rl_input_available()
diff --git a/third_party/readline/ansi_stdlib.h b/third_party/readline/ansi_stdlib.h
new file mode 100644
index 000000000..7dc2ee0cf
--- /dev/null
+++ b/third_party/readline/ansi_stdlib.h
@@ -0,0 +1,54 @@
+/* ansi_stdlib.h -- An ANSI Standard stdlib.h. */
+/* A minimal stdlib.h containing extern declarations for those functions
+ that bash uses. */
+
+/* Copyright (C) 1993 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash 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 Bash. If not, see .
+*/
+
+#if !defined (_STDLIB_H_)
+#define _STDLIB_H_ 1
+
+/* String conversion functions. */
+extern int atoi ();
+
+extern double atof ();
+extern double strtod ();
+
+/* Memory allocation functions. */
+/* Generic pointer type. */
+#ifndef PTR_T
+
+#if defined (__STDC__)
+# define PTR_T void *
+#else
+# define PTR_T char *
+#endif
+
+#endif /* PTR_T */
+
+extern PTR_T malloc ();
+extern PTR_T realloc ();
+extern void free ();
+
+/* Other miscellaneous functions. */
+extern void abort ();
+extern void exit ();
+extern char *getenv ();
+extern void qsort ();
+
+#endif /* _STDLIB_H */
diff --git a/third_party/readline/bind.c b/third_party/readline/bind.c
new file mode 100644
index 000000000..fb7d10523
--- /dev/null
+++ b/third_party/readline/bind.c
@@ -0,0 +1,3082 @@
+/* bind.c -- key binding and startup file support for the readline library. */
+
+/* Copyright (C) 1987-2022 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (__TANDEM)
+# include
+#endif
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+#include
+#include
+#if defined (HAVE_SYS_FILE_H)
+# include
+#endif /* HAVE_SYS_FILE_H */
+
+#if defined (HAVE_UNISTD_H)
+# include
+#endif /* HAVE_UNISTD_H */
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#include
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+#include "posixstat.h"
+
+/* System-specific feature definitions and include files. */
+#include "rldefs.h"
+
+/* Some standard library routines. */
+#include "readline.h"
+#include "history.h"
+
+#include "rlprivate.h"
+#include "rlshell.h"
+#include "xmalloc.h"
+
+#if !defined (strchr) && !defined (__STDC__)
+extern char *strchr (), *strrchr ();
+#endif /* !strchr && !__STDC__ */
+
+/* Variables exported by this file. */
+Keymap rl_binding_keymap;
+
+static int _rl_skip_to_delim (char *, int, int);
+
+#if defined (USE_VARARGS) && defined (PREFER_STDARG)
+static void _rl_init_file_error (const char *, ...) __attribute__((__format__ (printf, 1, 2)));
+#else
+static void _rl_init_file_error ();
+#endif
+
+static rl_command_func_t *_rl_function_of_keyseq_internal (const char *, size_t, Keymap, int *);
+
+static char *_rl_read_file (char *, size_t *);
+static int _rl_read_init_file (const char *, int);
+static int glean_key_from_name (char *);
+
+static int find_boolean_var (const char *);
+static int find_string_var (const char *);
+
+static const char *boolean_varname (int);
+static const char *string_varname (int);
+
+static char *_rl_get_string_variable_value (const char *);
+static int substring_member_of_array (const char *, const char * const *);
+
+static int _rl_get_keymap_by_name (const char *);
+static int _rl_get_keymap_by_map (Keymap);
+
+static int currently_reading_init_file;
+
+/* used only in this file */
+static int _rl_prefer_visible_bell = 1;
+
+#define OP_EQ 1
+#define OP_NE 2
+#define OP_GT 3
+#define OP_GE 4
+#define OP_LT 5
+#define OP_LE 6
+
+#define OPSTART(c) ((c) == '=' || (c) == '!' || (c) == '<' || (c) == '>')
+#define CMPSTART(c) ((c) == '=' || (c) == '!')
+
+/* **************************************************************** */
+/* */
+/* Binding keys */
+/* */
+/* **************************************************************** */
+
+/* rl_add_defun (char *name, rl_command_func_t *function, int key)
+ Add NAME to the list of named functions. Make FUNCTION be the function
+ that gets called. If KEY is not -1, then bind it. */
+int
+rl_add_defun (const char *name, rl_command_func_t *function, int key)
+{
+ if (key != -1)
+ rl_bind_key (key, function);
+ rl_add_funmap_entry (name, function);
+ return 0;
+}
+
+/* Bind KEY to FUNCTION. Returns non-zero if KEY is out of range. */
+int
+rl_bind_key (int key, rl_command_func_t *function)
+{
+ char keyseq[4];
+ int l;
+
+ if (key < 0 || key > largest_char)
+ return (key);
+
+ /* Want to make this a multi-character key sequence with an ESC prefix */
+ if (META_CHAR (key) && _rl_convert_meta_chars_to_ascii)
+ {
+ if (_rl_keymap[ESC].type == ISKMAP)
+ {
+ Keymap escmap;
+
+ escmap = FUNCTION_TO_KEYMAP (_rl_keymap, ESC);
+ key = UNMETA (key);
+ escmap[key].type = ISFUNC;
+ escmap[key].function = function;
+ return (0);
+ }
+
+ /* Otherwise, let's just let rl_generic_bind handle the key sequence.
+ We start it off with ESC here and let the code below add the rest
+ of the sequence. */
+ keyseq[0] = ESC;
+ l = 1;
+ key = UNMETA(key);
+ goto bind_keyseq;
+ }
+
+ /* If it's bound to a function or macro, just overwrite. Otherwise we have
+ to treat it as a key sequence so rl_generic_bind handles shadow keymaps
+ for us. If we are binding '\' or \C-@ (NUL) make sure to escape it so
+ it makes it through the call to rl_translate_keyseq. */
+ if (_rl_keymap[key].type != ISKMAP)
+ {
+ if (_rl_keymap[key].type == ISMACR)
+ xfree ((char *)_rl_keymap[key].function);
+ _rl_keymap[key].type = ISFUNC;
+ _rl_keymap[key].function = function;
+ }
+ else
+ {
+ l = 0;
+bind_keyseq:
+ if (key == '\\')
+ {
+ keyseq[l++] = '\\';
+ keyseq[l++] = '\\';
+ }
+ else if (key == '\0')
+ {
+ keyseq[l++] = '\\';
+ keyseq[l++] = '0';
+ }
+ else
+ keyseq[l++] = key;
+ keyseq[l] = '\0';
+ rl_bind_keyseq (keyseq, function);
+ }
+ rl_binding_keymap = _rl_keymap;
+ return (0);
+}
+
+/* Bind KEY to FUNCTION in MAP. Returns non-zero in case of invalid
+ KEY. */
+int
+rl_bind_key_in_map (int key, rl_command_func_t *function, Keymap map)
+{
+ int result;
+ Keymap oldmap;
+
+ oldmap = _rl_keymap;
+ _rl_keymap = map;
+ result = rl_bind_key (key, function);
+ _rl_keymap = oldmap;
+ return (result);
+}
+
+/* Bind key sequence KEYSEQ to DEFAULT_FUNC if KEYSEQ is unbound. Right
+ now, this is always used to attempt to bind the arrow keys. */
+int
+rl_bind_key_if_unbound_in_map (int key, rl_command_func_t *default_func, Keymap kmap)
+{
+ char *keyseq;
+
+ keyseq = rl_untranslate_keyseq ((unsigned char)key);
+ return (rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, kmap));
+}
+
+int
+rl_bind_key_if_unbound (int key, rl_command_func_t *default_func)
+{
+ char *keyseq;
+
+ keyseq = rl_untranslate_keyseq ((unsigned char)key);
+ return (rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, _rl_keymap));
+}
+
+/* Make KEY do nothing in the currently selected keymap.
+ Returns non-zero in case of error. This is not the same as self-insert;
+ this makes it a dead key. */
+int
+rl_unbind_key (int key)
+{
+ return (rl_bind_key (key, (rl_command_func_t *)NULL));
+}
+
+/* Make KEY do nothing in MAP. Returns non-zero in case of error. */
+int
+rl_unbind_key_in_map (int key, Keymap map)
+{
+ return (rl_bind_key_in_map (key, (rl_command_func_t *)NULL, map));
+}
+
+/* Unbind all keys bound to FUNCTION in MAP. */
+int
+rl_unbind_function_in_map (rl_command_func_t *func, Keymap map)
+{
+ register int i, rval;
+
+ for (i = rval = 0; i < KEYMAP_SIZE; i++)
+ {
+ if (map[i].type == ISFUNC && map[i].function == func)
+ {
+ map[i].function = (rl_command_func_t *)NULL;
+ rval = 1;
+ }
+ else if (map[i].type == ISKMAP) /* TAG:readline-8.1 */
+ {
+ int r;
+ r = rl_unbind_function_in_map (func, FUNCTION_TO_KEYMAP (map, i));
+ if (r == 1)
+ rval = 1;
+ }
+ }
+ return rval;
+}
+
+/* Unbind all keys bound to COMMAND, which is a bindable command name, in MAP */
+int
+rl_unbind_command_in_map (const char *command, Keymap map)
+{
+ rl_command_func_t *func;
+
+ func = rl_named_function (command);
+ if (func == 0)
+ return 0;
+ return (rl_unbind_function_in_map (func, map));
+}
+
+/* Bind the key sequence represented by the string KEYSEQ to
+ FUNCTION, starting in the current keymap. This makes new
+ keymaps as necessary. */
+int
+rl_bind_keyseq (const char *keyseq, rl_command_func_t *function)
+{
+ return (rl_generic_bind (ISFUNC, keyseq, (char *)function, _rl_keymap));
+}
+
+/* Bind the key sequence represented by the string KEYSEQ to
+ FUNCTION. This makes new keymaps as necessary. The initial
+ place to do bindings is in MAP. */
+int
+rl_bind_keyseq_in_map (const char *keyseq, rl_command_func_t *function, Keymap map)
+{
+ return (rl_generic_bind (ISFUNC, keyseq, (char *)function, map));
+}
+
+/* Backwards compatibility; equivalent to rl_bind_keyseq_in_map() */
+int
+rl_set_key (const char *keyseq, rl_command_func_t *function, Keymap map)
+{
+ return (rl_generic_bind (ISFUNC, keyseq, (char *)function, map));
+}
+
+/* Bind key sequence KEYSEQ to DEFAULT_FUNC if KEYSEQ is unbound. Right
+ now, this is always used to attempt to bind the arrow keys, hence the
+ check for rl_vi_movement_mode. */
+int
+rl_bind_keyseq_if_unbound_in_map (const char *keyseq, rl_command_func_t *default_func, Keymap kmap)
+{
+ rl_command_func_t *func;
+ char *keys;
+ int keys_len;
+
+ if (keyseq)
+ {
+ /* Handle key sequences that require translations and `raw' ones that
+ don't. This might be a problem with backslashes. */
+ keys = (char *)xmalloc (1 + (2 * strlen (keyseq)));
+ if (rl_translate_keyseq (keyseq, keys, &keys_len))
+ {
+ xfree (keys);
+ return -1;
+ }
+ func = rl_function_of_keyseq_len (keys, keys_len, kmap, (int *)NULL);
+ xfree (keys);
+#if defined (VI_MODE)
+ if (!func || func == rl_do_lowercase_version || func == rl_vi_movement_mode)
+#else
+ if (!func || func == rl_do_lowercase_version)
+#endif
+ return (rl_bind_keyseq_in_map (keyseq, default_func, kmap));
+ else
+ return 1;
+ }
+ return 0;
+}
+
+int
+rl_bind_keyseq_if_unbound (const char *keyseq, rl_command_func_t *default_func)
+{
+ return (rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, _rl_keymap));
+}
+
+/* Bind the key sequence represented by the string KEYSEQ to
+ the string of characters MACRO. This makes new keymaps as
+ necessary. The initial place to do bindings is in MAP. */
+int
+rl_macro_bind (const char *keyseq, const char *macro, Keymap map)
+{
+ char *macro_keys;
+ int macro_keys_len;
+
+ macro_keys = (char *)xmalloc ((2 * strlen (macro)) + 1);
+
+ if (rl_translate_keyseq (macro, macro_keys, ¯o_keys_len))
+ {
+ xfree (macro_keys);
+ return -1;
+ }
+ rl_generic_bind (ISMACR, keyseq, macro_keys, map);
+ return 0;
+}
+
+/* Bind the key sequence represented by the string KEYSEQ to
+ the arbitrary pointer DATA. TYPE says what kind of data is
+ pointed to by DATA, right now this can be a function (ISFUNC),
+ a macro (ISMACR), or a keymap (ISKMAP). This makes new keymaps
+ as necessary. The initial place to do bindings is in MAP. */
+int
+rl_generic_bind (int type, const char *keyseq, char *data, Keymap map)
+{
+ char *keys;
+ int keys_len, prevkey, ic;
+ register int i;
+ KEYMAP_ENTRY k;
+ Keymap prevmap;
+
+ k.function = 0;
+
+ /* If no keys to bind to, exit right away. */
+ if (keyseq == 0 || *keyseq == 0)
+ {
+ if (type == ISMACR)
+ xfree (data);
+ return -1;
+ }
+
+ keys = (char *)xmalloc (1 + (2 * strlen (keyseq)));
+
+ /* Translate the ASCII representation of KEYSEQ into an array of
+ characters. Stuff the characters into KEYS, and the length of
+ KEYS into KEYS_LEN. */
+ if (rl_translate_keyseq (keyseq, keys, &keys_len))
+ {
+ xfree (keys);
+ return -1;
+ }
+
+ prevmap = map;
+ prevkey = keys[0];
+
+ /* Bind keys, making new keymaps as necessary. */
+ for (i = 0; i < keys_len; i++)
+ {
+ unsigned char uc = keys[i];
+
+ if (i > 0)
+ prevkey = ic;
+
+ ic = uc;
+ if (ic < 0 || ic >= KEYMAP_SIZE)
+ {
+ xfree (keys);
+ return -1;
+ }
+
+ /* We now rely on rl_translate_keyseq to do this conversion, so this
+ check is superfluous. */
+#if 0
+ if (META_CHAR (ic) && _rl_convert_meta_chars_to_ascii)
+ {
+ ic = UNMETA (ic);
+ if (map[ESC].type == ISKMAP)
+ {
+ prevmap = map;
+ map = FUNCTION_TO_KEYMAP (map, ESC);
+ }
+ }
+#endif
+
+ if ((i + 1) < keys_len)
+ {
+ if (map[ic].type != ISKMAP)
+ {
+ /* We allow subsequences of keys. If a keymap is being
+ created that will `shadow' an existing function or macro
+ key binding, we save that keybinding into the ANYOTHERKEY
+ index in the new map. The dispatch code will look there
+ to find the function to execute if the subsequence is not
+ matched. ANYOTHERKEY was chosen to be greater than
+ UCHAR_MAX. */
+ k = map[ic];
+
+ map[ic].type = ISKMAP;
+ map[ic].function = KEYMAP_TO_FUNCTION (rl_make_bare_keymap());
+ }
+ prevmap = map;
+ map = FUNCTION_TO_KEYMAP (map, ic);
+ /* The dispatch code will return this function if no matching
+ key sequence is found in the keymap. This (with a little
+ help from the dispatch code in readline.c) allows `a' to be
+ mapped to something, `abc' to be mapped to something else,
+ and the function bound to `a' to be executed when the user
+ types `abx', leaving `bx' in the input queue. */
+ if (k.function && ((k.type == ISFUNC && k.function != rl_do_lowercase_version) || k.type == ISMACR))
+ {
+ map[ANYOTHERKEY] = k;
+ k.function = 0;
+ }
+ }
+ else
+ {
+ if (map[ic].type == ISKMAP)
+ {
+ prevmap = map;
+ map = FUNCTION_TO_KEYMAP (map, ic);
+ ic = ANYOTHERKEY;
+ /* If we're trying to override a keymap with a null function
+ (e.g., trying to unbind it), we can't use a null pointer
+ here because that's indistinguishable from having not been
+ overridden. We use a special bindable function that does
+ nothing. */
+ if (type == ISFUNC && data == 0)
+ data = (char *)_rl_null_function;
+ }
+ if (map[ic].type == ISMACR)
+ xfree ((char *)map[ic].function);
+
+ map[ic].function = KEYMAP_TO_FUNCTION (data);
+ map[ic].type = type;
+ }
+
+ rl_binding_keymap = map;
+
+ }
+
+ /* If we unbound a key (type == ISFUNC, data == 0), and the prev keymap
+ points to the keymap where we unbound the key (sanity check), and the
+ current binding keymap is empty (rl_empty_keymap() returns non-zero),
+ and the binding keymap has ANYOTHERKEY set with type == ISFUNC
+ (overridden function), delete the now-empty keymap, take the previously-
+ overridden function and remove the override. */
+ /* Right now, this only works one level back. */
+ if (type == ISFUNC && data == 0 &&
+ prevmap[prevkey].type == ISKMAP &&
+ (FUNCTION_TO_KEYMAP(prevmap, prevkey) == rl_binding_keymap) &&
+ rl_binding_keymap[ANYOTHERKEY].type == ISFUNC &&
+ rl_empty_keymap (rl_binding_keymap))
+ {
+ prevmap[prevkey].type = rl_binding_keymap[ANYOTHERKEY].type;
+ prevmap[prevkey].function = rl_binding_keymap[ANYOTHERKEY].function;
+ rl_discard_keymap (rl_binding_keymap);
+ rl_binding_keymap = prevmap;
+ }
+
+ xfree (keys);
+ return 0;
+}
+
+/* Translate the ASCII representation of SEQ, stuffing the values into ARRAY,
+ an array of characters. LEN gets the final length of ARRAY. Return
+ non-zero if there was an error parsing SEQ. */
+int
+rl_translate_keyseq (const char *seq, char *array, int *len)
+{
+ register int i, l, temp;
+ int has_control, has_meta;
+ unsigned char c;
+
+ has_control = 0;
+ has_meta = 0;
+
+ /* When there are incomplete prefixes \C- or \M- (has_control || has_meta)
+ without base character at the end of SEQ, they are processed as the
+ prefixes for '\0'.
+ */
+ for (i = l = 0; (c = seq[i]) || has_control || has_meta; i++)
+ {
+ /* Only backslashes followed by a non-null character are handled
+ specially. Trailing backslash (backslash followed by '\0') is
+ processed as a normal character.
+ */
+ if (c == '\\' && seq[i + 1] != '\0')
+ {
+ c = seq[++i];
+
+ /* Handle \C- and \M- prefixes. */
+ if (c == 'C' && seq[i + 1] == '-')
+ {
+ i++;
+ has_control = 1;
+ continue;
+ }
+ else if (c == 'M' && seq[i + 1] == '-')
+ {
+ i++;
+ has_meta = 1;
+ continue;
+ }
+
+ /* Translate other backslash-escaped characters. These are the
+ same escape sequences that bash's `echo' and `printf' builtins
+ handle, with the addition of \d -> RUBOUT. A backslash
+ preceding a character that is not special is stripped. */
+ switch (c)
+ {
+ case 'a':
+ c = '\007';
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ case 'd':
+ c = RUBOUT; /* readline-specific */
+ break;
+ case 'e':
+ c = ESC;
+ break;
+ case 'f':
+ c = '\f';
+ break;
+ case 'n':
+ c = NEWLINE;
+ break;
+ case 'r':
+ c = RETURN;
+ break;
+ case 't':
+ c = TAB;
+ break;
+ case 'v':
+ c = 0x0B;
+ break;
+ case '\\':
+ c = '\\';
+ break;
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ i++;
+ for (temp = 2, c -= '0'; ISOCTAL ((unsigned char)seq[i]) && temp--; i++)
+ c = (c * 8) + OCTVALUE (seq[i]);
+ i--; /* auto-increment in for loop */
+ c &= largest_char;
+ break;
+ case 'x':
+ i++;
+ for (temp = 2, c = 0; ISXDIGIT ((unsigned char)seq[i]) && temp--; i++)
+ c = (c * 16) + HEXVALUE (seq[i]);
+ if (temp == 2)
+ c = 'x';
+ i--; /* auto-increment in for loop */
+ c &= largest_char;
+ break;
+ default: /* backslashes before non-special chars just add the char */
+ c &= largest_char;
+ break; /* the backslash is stripped */
+ }
+ }
+
+ /* Process \C- and \M- flags */
+ if (has_control)
+ {
+ /* Special treatment for C-? */
+ c = (c == '?') ? RUBOUT : CTRL (_rl_to_upper (c));
+ has_control = 0;
+ }
+ if (has_meta)
+ {
+ c = META (c);
+ has_meta = 0;
+ }
+
+ /* If convert-meta is turned on, convert a meta char to a key sequence */
+ if (META_CHAR (c) && _rl_convert_meta_chars_to_ascii)
+ {
+ array[l++] = ESC; /* ESC is meta-prefix */
+ array[l++] = UNMETA (c);
+ }
+ else
+ array[l++] = (c);
+
+ /* Null characters may be processed for incomplete prefixes at the end of
+ sequence */
+ if (seq[i] == '\0')
+ break;
+ }
+
+ *len = l;
+ array[l] = '\0';
+ return (0);
+}
+
+static int
+_rl_isescape (int c)
+{
+ switch (c)
+ {
+ case '\007':
+ case '\b':
+ case '\f':
+ case '\n':
+ case '\r':
+ case TAB:
+ case 0x0b: return (1);
+ default: return (0);
+ }
+}
+
+static int
+_rl_escchar (int c)
+{
+ switch (c)
+ {
+ case '\007': return ('a');
+ case '\b': return ('b');
+ case '\f': return ('f');
+ case '\n': return ('n');
+ case '\r': return ('r');
+ case TAB: return ('t');
+ case 0x0b: return ('v');
+ default: return (c);
+ }
+}
+
+char *
+rl_untranslate_keyseq (int seq)
+{
+ static char kseq[16];
+ int i, c;
+
+ i = 0;
+ c = seq;
+ if (META_CHAR (c))
+ {
+ kseq[i++] = '\\';
+ kseq[i++] = 'M';
+ kseq[i++] = '-';
+ c = UNMETA (c);
+ }
+ else if (c == ESC)
+ {
+ kseq[i++] = '\\';
+ c = 'e';
+ }
+ else if (CTRL_CHAR (c))
+ {
+ kseq[i++] = '\\';
+ kseq[i++] = 'C';
+ kseq[i++] = '-';
+ c = _rl_to_lower (UNCTRL (c));
+ }
+ else if (c == RUBOUT)
+ {
+ kseq[i++] = '\\';
+ kseq[i++] = 'C';
+ kseq[i++] = '-';
+ c = '?';
+ }
+
+ if (c == ESC)
+ {
+ kseq[i++] = '\\';
+ c = 'e';
+ }
+ else if (c == '\\' || c == '"')
+ {
+ kseq[i++] = '\\';
+ }
+
+ kseq[i++] = (unsigned char) c;
+ kseq[i] = '\0';
+ return kseq;
+}
+
+char *
+_rl_untranslate_macro_value (char *seq, int use_escapes)
+{
+ char *ret, *r, *s;
+ int c;
+
+ r = ret = (char *)xmalloc (7 * strlen (seq) + 1);
+ for (s = seq; *s; s++)
+ {
+ c = *s;
+ if (META_CHAR (c))
+ {
+ *r++ = '\\';
+ *r++ = 'M';
+ *r++ = '-';
+ c = UNMETA (c);
+ }
+ else if (c == ESC)
+ {
+ *r++ = '\\';
+ c = 'e';
+ }
+ else if (CTRL_CHAR (c))
+ {
+ *r++ = '\\';
+ if (use_escapes && _rl_isescape (c))
+ c = _rl_escchar (c);
+ else
+ {
+ *r++ = 'C';
+ *r++ = '-';
+ c = _rl_to_lower (UNCTRL (c));
+ }
+ }
+ else if (c == RUBOUT)
+ {
+ *r++ = '\\';
+ *r++ = 'C';
+ *r++ = '-';
+ c = '?';
+ }
+
+ if (c == ESC)
+ {
+ *r++ = '\\';
+ c = 'e';
+ }
+ else if (c == '\\' || c == '"')
+ *r++ = '\\';
+
+ *r++ = (unsigned char)c;
+ }
+ *r = '\0';
+ return ret;
+}
+
+/* Return a pointer to the function that STRING represents.
+ If STRING doesn't have a matching function, then a NULL pointer
+ is returned. The string match is case-insensitive. */
+rl_command_func_t *
+rl_named_function (const char *string)
+{
+ register int i;
+
+ rl_initialize_funmap ();
+
+ for (i = 0; funmap[i]; i++)
+ if (_rl_stricmp (funmap[i]->name, string) == 0)
+ return (funmap[i]->function);
+ return ((rl_command_func_t *)NULL);
+}
+
+/* Return the function (or macro) definition which would be invoked via
+ KEYSEQ if executed in MAP. If MAP is NULL, then the current keymap is
+ used. TYPE, if non-NULL, is a pointer to an int which will receive the
+ type of the object pointed to. One of ISFUNC (function), ISKMAP (keymap),
+ or ISMACR (macro). */
+static rl_command_func_t *
+_rl_function_of_keyseq_internal (const char *keyseq, size_t len, Keymap map, int *type)
+{
+ register int i;
+
+ if (map == 0)
+ map = _rl_keymap;
+
+ for (i = 0; keyseq && i < len; i++)
+ {
+ unsigned char ic = keyseq[i];
+
+ if (META_CHAR (ic) && _rl_convert_meta_chars_to_ascii)
+ {
+ if (map[ESC].type == ISKMAP)
+ {
+ map = FUNCTION_TO_KEYMAP (map, ESC);
+ ic = UNMETA (ic);
+ }
+ /* XXX - should we just return NULL here, since this obviously
+ doesn't match? */
+ else
+ {
+ if (type)
+ *type = map[ESC].type;
+
+ return (map[ESC].function);
+ }
+ }
+
+ if (map[ic].type == ISKMAP)
+ {
+ /* If this is the last key in the key sequence, return the
+ map. */
+ if (i + 1 == len)
+ {
+ if (type)
+ *type = ISKMAP;
+
+ return (map[ic].function);
+ }
+ else
+ map = FUNCTION_TO_KEYMAP (map, ic);
+ }
+ /* If we're not at the end of the key sequence, and the current key
+ is bound to something other than a keymap, then the entire key
+ sequence is not bound. */
+ else if (map[ic].type != ISKMAP && i+1 < len)
+ return ((rl_command_func_t *)NULL);
+ else /* map[ic].type != ISKMAP && i+1 == len */
+ {
+ if (type)
+ *type = map[ic].type;
+
+ return (map[ic].function);
+ }
+ }
+ return ((rl_command_func_t *) NULL);
+}
+
+rl_command_func_t *
+rl_function_of_keyseq (const char *keyseq, Keymap map, int *type)
+{
+ return _rl_function_of_keyseq_internal (keyseq, strlen (keyseq), map, type);
+}
+
+rl_command_func_t *
+rl_function_of_keyseq_len (const char *keyseq, size_t len, Keymap map, int *type)
+{
+ return _rl_function_of_keyseq_internal (keyseq, len, map, type);
+}
+
+/* Assuming there is a numeric argument at the beginning of KEYSEQ (the
+ caller is responsible for checking), return the index of the portion of
+ the key sequence following the numeric argument. If there's no numeric
+ argument (?), or if KEYSEQ consists solely of a numeric argument (?),
+ return -1. */
+int
+rl_trim_arg_from_keyseq (const char *keyseq, size_t len, Keymap map)
+{
+ register int i, j, parsing_digits;
+ unsigned char ic;
+ Keymap map0;
+
+ if (map == 0)
+ map = _rl_keymap;
+ map0 = map;
+
+ /* The digits following the initial one (e.g., the binding to digit-argument)
+ or the optional `-' in a binding to digit-argument or universal-argument
+ are not added to rl_executing_keyseq. This is basically everything read by
+ rl_digit_loop. The parsing_digits logic is here in case they ever are. */
+ for (i = j = parsing_digits = 0; keyseq && i < len; i++)
+ {
+ ic = keyseq[i];
+
+ if (parsing_digits)
+ {
+ if (_rl_digit_p (ic))
+ {
+ j = i + 1;
+ continue;
+ }
+ parsing_digits = 0;
+ }
+
+ if (map[ic].type == ISKMAP)
+ {
+ if (i + 1 == len)
+ return -1;
+ map = FUNCTION_TO_KEYMAP (map, ic);
+ continue;
+ }
+ if (map[ic].type == ISFUNC)
+ {
+#if defined (VI_MODE)
+ if (map[ic].function != rl_digit_argument && map[ic].function != rl_universal_argument && map[ic].function != rl_vi_arg_digit)
+#else
+ if (map[ic].function != rl_digit_argument && map[ic].function != rl_universal_argument)
+#endif
+ return (j);
+
+ /* We don't bother with a keyseq that is only a numeric argument */
+ if (i + 1 == len)
+ return -1;
+
+ parsing_digits = 1;
+
+ /* This logic should be identical to rl_digit_loop */
+ /* We accept M-- as equivalent to M--1, C-u- as equivalent to C-u-1
+ but set parsing_digits to 2 to note that we saw `-' */
+ if (map[ic].function == rl_universal_argument && (i + 1 == '-'))
+ {
+ i++;
+ parsing_digits = 2;
+ }
+ if (map[ic].function == rl_digit_argument && ic == '-')
+ {
+ parsing_digits = 2;
+ }
+
+ map = map0;
+ j = i + 1;
+ }
+ }
+
+ /* If we're still parsing digits by the time we get here, we don't allow a
+ key sequence that consists solely of a numeric argument */
+ return -1;
+}
+
+/* The last key bindings file read. */
+static char *last_readline_init_file = (char *)NULL;
+
+/* The file we're currently reading key bindings from. */
+static const char *current_readline_init_file;
+static int current_readline_init_include_level;
+static int current_readline_init_lineno;
+
+/* Read FILENAME into a locally-allocated buffer and return the buffer.
+ The size of the buffer is returned in *SIZEP. Returns NULL if any
+ errors were encountered. */
+static char *
+_rl_read_file (char *filename, size_t *sizep)
+{
+ struct stat finfo;
+ size_t file_size;
+ char *buffer;
+ int i, file;
+
+ file = -1;
+ if (((file = open (filename, O_RDONLY, 0666)) < 0) || (fstat (file, &finfo) < 0))
+ {
+ if (file >= 0)
+ close (file);
+ return ((char *)NULL);
+ }
+
+ file_size = (size_t)finfo.st_size;
+
+ /* check for overflow on very large files */
+ if (file_size != finfo.st_size || file_size + 1 < file_size)
+ {
+ if (file >= 0)
+ close (file);
+#if defined (EFBIG)
+ errno = EFBIG;
+#endif
+ return ((char *)NULL);
+ }
+
+ /* Read the file into BUFFER. */
+ buffer = (char *)xmalloc (file_size + 1);
+ i = read (file, buffer, file_size);
+ close (file);
+
+ if (i < 0)
+ {
+ xfree (buffer);
+ return ((char *)NULL);
+ }
+
+ RL_CHECK_SIGNALS ();
+
+ buffer[i] = '\0';
+ if (sizep)
+ *sizep = i;
+
+ return (buffer);
+}
+
+/* Re-read the current keybindings file. */
+int
+rl_re_read_init_file (int count, int ignore)
+{
+ int r;
+ r = rl_read_init_file ((const char *)NULL);
+ rl_set_keymap_from_edit_mode ();
+ return r;
+}
+
+/* Do key bindings from a file. If FILENAME is NULL it defaults
+ to the first non-null filename from this list:
+ 1. the filename used for the previous call
+ 2. the value of the shell variable `INPUTRC'
+ 3. ~/.inputrc
+ 4. /etc/inputrc
+ If the file existed and could be opened and read, 0 is returned,
+ otherwise errno is returned. */
+int
+rl_read_init_file (const char *filename)
+{
+ /* Default the filename. */
+ if (filename == 0)
+ filename = last_readline_init_file;
+ if (filename == 0)
+ filename = sh_get_env_value ("INPUTRC");
+ if (filename == 0 || *filename == 0)
+ {
+ filename = DEFAULT_INPUTRC;
+ /* Try to read DEFAULT_INPUTRC; fall back to SYS_INPUTRC on failure */
+ if (_rl_read_init_file (filename, 0) == 0)
+ return 0;
+ filename = SYS_INPUTRC;
+ }
+
+#if defined (__MSDOS__)
+ if (_rl_read_init_file (filename, 0) == 0)
+ return 0;
+ filename = "~/_inputrc";
+#endif
+ return (_rl_read_init_file (filename, 0));
+}
+
+static int
+_rl_read_init_file (const char *filename, int include_level)
+{
+ register int i;
+ char *buffer, *openname, *line, *end;
+ size_t file_size;
+
+ current_readline_init_file = filename;
+ current_readline_init_include_level = include_level;
+
+ openname = tilde_expand (filename);
+ buffer = _rl_read_file (openname, &file_size);
+ xfree (openname);
+
+ RL_CHECK_SIGNALS ();
+ if (buffer == 0)
+ return (errno);
+
+ if (include_level == 0 && filename != last_readline_init_file)
+ {
+ FREE (last_readline_init_file);
+ last_readline_init_file = savestring (filename);
+ }
+
+ currently_reading_init_file = 1;
+
+ /* Loop over the lines in the file. Lines that start with `#' are
+ comments; all other lines are commands for readline initialization. */
+ current_readline_init_lineno = 1;
+ line = buffer;
+ end = buffer + file_size;
+ while (line < end)
+ {
+ /* Find the end of this line. */
+ for (i = 0; line + i != end && line[i] != '\n'; i++);
+
+#if defined (__CYGWIN__)
+ /* ``Be liberal in what you accept.'' */
+ if (line[i] == '\n' && line[i-1] == '\r')
+ line[i - 1] = '\0';
+#endif
+
+ /* Mark end of line. */
+ line[i] = '\0';
+
+ /* Skip leading whitespace. */
+ while (*line && whitespace (*line))
+ {
+ line++;
+ i--;
+ }
+
+ /* If the line is not a comment, then parse it. */
+ if (*line && *line != '#')
+ rl_parse_and_bind (line);
+
+ /* Move to the next line. */
+ line += i + 1;
+ current_readline_init_lineno++;
+ }
+
+ xfree (buffer);
+ currently_reading_init_file = 0;
+ return (0);
+}
+
+static void
+#if defined (PREFER_STDARG)
+_rl_init_file_error (const char *format, ...)
+#else
+_rl_init_file_error (va_alist)
+ va_dcl
+#endif
+{
+ va_list args;
+#if defined (PREFER_VARARGS)
+ char *format;
+#endif
+
+#if defined (PREFER_STDARG)
+ va_start (args, format);
+#else
+ va_start (args);
+ format = va_arg (args, char *);
+#endif
+
+ fprintf (stderr, "readline: ");
+ if (currently_reading_init_file)
+ fprintf (stderr, "%s: line %d: ", current_readline_init_file,
+ current_readline_init_lineno);
+
+ vfprintf (stderr, format, args);
+ fprintf (stderr, "\n");
+ fflush (stderr);
+
+ va_end (args);
+}
+
+/* **************************************************************** */
+/* */
+/* Parser Helper Functions */
+/* */
+/* **************************************************************** */
+
+static int
+parse_comparison_op (s, indp)
+ const char *s;
+ int *indp;
+{
+ int i, peekc, op;
+
+ if (OPSTART (s[*indp]) == 0)
+ return -1;
+ i = *indp;
+ peekc = s[i] ? s[i+1] : 0;
+ op = -1;
+
+ if (s[i] == '=')
+ {
+ op = OP_EQ;
+ if (peekc == '=')
+ i++;
+ i++;
+ }
+ else if (s[i] == '!' && peekc == '=')
+ {
+ op = OP_NE;
+ i += 2;
+ }
+ else if (s[i] == '<' && peekc == '=')
+ {
+ op = OP_LE;
+ i += 2;
+ }
+ else if (s[i] == '>' && peekc == '=')
+ {
+ op = OP_GE;
+ i += 2;
+ }
+ else if (s[i] == '<')
+ {
+ op = OP_LT;
+ i += 1;
+ }
+ else if (s[i] == '>')
+ {
+ op = OP_GT;
+ i += 1;
+ }
+
+ *indp = i;
+ return op;
+}
+
+/* **************************************************************** */
+/* */
+/* Parser Directives */
+/* */
+/* **************************************************************** */
+
+typedef int _rl_parser_func_t (char *);
+
+/* Things that mean `Control'. */
+const char * const _rl_possible_control_prefixes[] = {
+ "Control-", "C-", "CTRL-", (const char *)NULL
+};
+
+const char * const _rl_possible_meta_prefixes[] = {
+ "Meta", "M-", (const char *)NULL
+};
+
+/* Forward declarations */
+static int parser_if (char *);
+static int parser_else (char *);
+static int parser_endif (char *);
+static int parser_include (char *);
+
+/* Conditionals. */
+
+/* Calling programs set this to have their argv[0]. */
+const char *rl_readline_name = "other";
+
+/* Stack of previous values of parsing_conditionalized_out. */
+static unsigned char *if_stack = (unsigned char *)NULL;
+static int if_stack_depth;
+static int if_stack_size;
+
+/* Push _rl_parsing_conditionalized_out, and set parser state based
+ on ARGS. */
+static int
+parser_if (char *args)
+{
+ int i, llen, boolvar, strvar;
+
+ boolvar = strvar = -1;
+
+ /* Push parser state. */
+ if (if_stack_depth + 1 >= if_stack_size)
+ {
+ if (!if_stack)
+ if_stack = (unsigned char *)xmalloc (if_stack_size = 20);
+ else
+ if_stack = (unsigned char *)xrealloc (if_stack, if_stack_size += 20);
+ }
+ if_stack[if_stack_depth++] = _rl_parsing_conditionalized_out;
+
+ /* If parsing is turned off, then nothing can turn it back on except
+ for finding the matching endif. In that case, return right now. */
+ if (_rl_parsing_conditionalized_out)
+ return 0;
+
+ llen = strlen (args);
+
+ /* Isolate first argument. */
+ for (i = 0; args[i] && !whitespace (args[i]); i++);
+
+ if (args[i])
+ args[i++] = '\0';
+
+ /* Handle "$if term=foo" and "$if mode=emacs" constructs. If this
+ isn't term=foo, or mode=emacs, then check to see if the first
+ word in ARGS is the same as the value stored in rl_readline_name. */
+ if (rl_terminal_name && _rl_strnicmp (args, "term=", 5) == 0)
+ {
+ char *tem, *tname;
+
+ /* Terminals like "aaa-60" are equivalent to "aaa". */
+ tname = savestring (rl_terminal_name);
+ tem = strchr (tname, '-');
+ if (tem)
+ *tem = '\0';
+
+ /* Test the `long' and `short' forms of the terminal name so that
+ if someone has a `sun-cmd' and does not want to have bindings
+ that will be executed if the terminal is a `sun', they can put
+ `$if term=sun-cmd' into their .inputrc. */
+ _rl_parsing_conditionalized_out = _rl_stricmp (args + 5, tname) &&
+ _rl_stricmp (args + 5, rl_terminal_name);
+ xfree (tname);
+ }
+#if defined (VI_MODE)
+ else if (_rl_strnicmp (args, "mode=", 5) == 0)
+ {
+ int mode;
+
+ if (_rl_stricmp (args + 5, "emacs") == 0)
+ mode = emacs_mode;
+ else if (_rl_stricmp (args + 5, "vi") == 0)
+ mode = vi_mode;
+ else
+ mode = no_mode;
+
+ _rl_parsing_conditionalized_out = mode != rl_editing_mode;
+ }
+#endif /* VI_MODE */
+ else if (_rl_strnicmp (args, "version", 7) == 0)
+ {
+ int rlversion, versionarg, op, previ, major, minor, opresult;
+
+ _rl_parsing_conditionalized_out = 1;
+ rlversion = RL_VERSION_MAJOR*10 + RL_VERSION_MINOR;
+ /* if "version" is separated from the operator by whitespace, or the
+ operand is separated from the operator by whitespace, restore it.
+ We're more liberal with allowed whitespace for this variable. */
+ if (i > 0 && i <= llen && args[i-1] == '\0')
+ args[i-1] = ' ';
+ args[llen] = '\0'; /* just in case */
+ for (i = 7; whitespace (args[i]); i++)
+ ;
+ if (OPSTART(args[i]) == 0)
+ {
+ _rl_init_file_error ("comparison operator expected, found `%s'", args[i] ? args + i : "end-of-line");
+ return 0;
+ }
+ previ = i;
+ op = parse_comparison_op (args, &i);
+ if (op <= 0)
+ {
+ _rl_init_file_error ("comparison operator expected, found `%s'", args+previ);
+ return 0;
+ }
+ for ( ; args[i] && whitespace (args[i]); i++)
+ ;
+ if (args[i] == 0 || _rl_digit_p (args[i]) == 0)
+ {
+ _rl_init_file_error ("numeric argument expected, found `%s'", args+i);
+ return 0;
+ }
+ major = minor = 0;
+ previ = i;
+ for ( ; args[i] && _rl_digit_p (args[i]); i++)
+ major = major*10 + _rl_digit_value (args[i]);
+ if (args[i] == '.')
+ {
+ if (args[i + 1] && _rl_digit_p (args [i + 1]) == 0)
+ {
+ _rl_init_file_error ("numeric argument expected, found `%s'", args+previ);
+ return 0;
+ }
+ for (++i; args[i] && _rl_digit_p (args[i]); i++)
+ minor = minor*10 + _rl_digit_value (args[i]);
+ }
+ /* optional - check for trailing garbage on the line, allow whitespace
+ and a trailing comment */
+ previ = i;
+ for ( ; args[i] && whitespace (args[i]); i++)
+ ;
+ if (args[i] && args[i] != '#')
+ {
+ _rl_init_file_error ("trailing garbage on line: `%s'", args+previ);
+ return 0;
+ }
+ versionarg = major*10 + minor;
+
+ switch (op)
+ {
+ case OP_EQ:
+ opresult = rlversion == versionarg;
+ break;
+ case OP_NE:
+ opresult = rlversion != versionarg;
+ break;
+ case OP_GT:
+ opresult = rlversion > versionarg;
+ break;
+ case OP_GE:
+ opresult = rlversion >= versionarg;
+ break;
+ case OP_LT:
+ opresult = rlversion < versionarg;
+ break;
+ case OP_LE:
+ opresult = rlversion <= versionarg;
+ break;
+ }
+ _rl_parsing_conditionalized_out = 1 - opresult;
+ }
+ /* Check to see if the first word in ARGS is the same as the
+ value stored in rl_readline_name. */
+ else if (_rl_stricmp (args, rl_readline_name) == 0)
+ _rl_parsing_conditionalized_out = 0;
+ else if ((boolvar = find_boolean_var (args)) >= 0 || (strvar = find_string_var (args)) >= 0)
+ {
+ int op, previ;
+ size_t vlen;
+ const char *vname;
+ char *valuearg, *vval, prevc;
+
+ _rl_parsing_conditionalized_out = 1;
+ vname = (boolvar >= 0) ? boolean_varname (boolvar) : string_varname (strvar);
+ vlen = strlen (vname);
+ if (i > 0 && i <= llen && args[i-1] == '\0')
+ args[i-1] = ' ';
+ args[llen] = '\0'; /* just in case */
+ for (i = vlen; whitespace (args[i]); i++)
+ ;
+ if (CMPSTART(args[i]) == 0)
+ {
+ _rl_init_file_error ("equality comparison operator expected, found `%s'", args[i] ? args + i : "end-of-line");
+ return 0;
+ }
+ previ = i;
+ op = parse_comparison_op (args, &i);
+ if (op != OP_EQ && op != OP_NE)
+ {
+ _rl_init_file_error ("equality comparison operator expected, found `%s'", args+previ);
+ return 0;
+ }
+ for ( ; args[i] && whitespace (args[i]); i++)
+ ;
+ if (args[i] == 0)
+ {
+ _rl_init_file_error ("argument expected, found `%s'", args+i);
+ return 0;
+ }
+ previ = i;
+ valuearg = args + i;
+ for ( ; args[i] && whitespace (args[i]) == 0; i++)
+ ;
+ prevc = args[i];
+ args[i] = '\0'; /* null-terminate valuearg */
+ vval = rl_variable_value (vname);
+ if (op == OP_EQ)
+ _rl_parsing_conditionalized_out = _rl_stricmp (vval, valuearg) != 0;
+ else if (op == OP_NE)
+ _rl_parsing_conditionalized_out = _rl_stricmp (vval, valuearg) == 0;
+ args[i] = prevc;
+ }
+ else
+ _rl_parsing_conditionalized_out = 1;
+ return 0;
+}
+
+/* Invert the current parser state if there is anything on the stack. */
+static int
+parser_else (char *args)
+{
+ register int i;
+
+ if (if_stack_depth == 0)
+ {
+ _rl_init_file_error ("$else found without matching $if");
+ return 0;
+ }
+
+#if 0
+ /* Check the previous (n - 1) levels of the stack to make sure that
+ we haven't previously turned off parsing. */
+ for (i = 0; i < if_stack_depth - 1; i++)
+#else
+ /* Check the previous (n) levels of the stack to make sure that
+ we haven't previously turned off parsing. */
+ for (i = 0; i < if_stack_depth; i++)
+#endif
+ if (if_stack[i] == 1)
+ return 0;
+
+ /* Invert the state of parsing if at top level. */
+ _rl_parsing_conditionalized_out = !_rl_parsing_conditionalized_out;
+ return 0;
+}
+
+/* Terminate a conditional, popping the value of
+ _rl_parsing_conditionalized_out from the stack. */
+static int
+parser_endif (char *args)
+{
+ if (if_stack_depth)
+ _rl_parsing_conditionalized_out = if_stack[--if_stack_depth];
+ else
+ _rl_init_file_error ("$endif without matching $if");
+ return 0;
+}
+
+static int
+parser_include (char *args)
+{
+ const char *old_init_file;
+ char *e;
+ int old_line_number, old_include_level, r;
+
+ if (_rl_parsing_conditionalized_out)
+ return (0);
+
+ old_init_file = current_readline_init_file;
+ old_line_number = current_readline_init_lineno;
+ old_include_level = current_readline_init_include_level;
+
+ e = strchr (args, '\n');
+ if (e)
+ *e = '\0';
+ r = _rl_read_init_file ((const char *)args, old_include_level + 1);
+
+ current_readline_init_file = old_init_file;
+ current_readline_init_lineno = old_line_number;
+ current_readline_init_include_level = old_include_level;
+
+ return r;
+}
+
+/* Associate textual names with actual functions. */
+static const struct {
+ const char * const name;
+ _rl_parser_func_t *function;
+} parser_directives [] = {
+ { "if", parser_if },
+ { "endif", parser_endif },
+ { "else", parser_else },
+ { "include", parser_include },
+ { (char *)0x0, (_rl_parser_func_t *)0x0 }
+};
+
+/* Handle a parser directive. STATEMENT is the line of the directive
+ without any leading `$'. */
+static int
+handle_parser_directive (char *statement)
+{
+ register int i;
+ char *directive, *args;
+
+ /* Isolate the actual directive. */
+
+ /* Skip whitespace. */
+ for (i = 0; whitespace (statement[i]); i++);
+
+ directive = &statement[i];
+
+ for (; statement[i] && !whitespace (statement[i]); i++);
+
+ if (statement[i])
+ statement[i++] = '\0';
+
+ for (; statement[i] && whitespace (statement[i]); i++);
+
+ args = &statement[i];
+
+ /* Lookup the command, and act on it. */
+ for (i = 0; parser_directives[i].name; i++)
+ if (_rl_stricmp (directive, parser_directives[i].name) == 0)
+ {
+ (*parser_directives[i].function) (args);
+ return (0);
+ }
+
+ /* display an error message about the unknown parser directive */
+ _rl_init_file_error ("%s: unknown parser directive", directive);
+ return (1);
+}
+
+/* Start at STRING[START] and look for DELIM. Return I where STRING[I] ==
+ DELIM or STRING[I] == 0. DELIM is usually a double quote. */
+static int
+_rl_skip_to_delim (char *string, int start, int delim)
+{
+ int i, c, passc;
+
+ for (i = start,passc = 0; c = string[i]; i++)
+ {
+ if (passc)
+ {
+ passc = 0;
+ if (c == 0)
+ break;
+ continue;
+ }
+
+ if (c == '\\')
+ {
+ passc = 1;
+ continue;
+ }
+
+ if (c == delim)
+ break;
+ }
+
+ return i;
+}
+
+/* Read the binding command from STRING and perform it.
+ A key binding command looks like: Keyname: function-name\0,
+ a variable binding command looks like: set variable value.
+ A new-style keybinding looks like "\C-x\C-x": exchange-point-and-mark. */
+int
+rl_parse_and_bind (char *string)
+{
+ char *funname, *kname;
+ register int c, i;
+ int key, equivalency, foundmod, foundsep;
+
+ while (string && whitespace (*string))
+ string++;
+
+ if (string == 0 || *string == 0 || *string == '#')
+ return 0;
+
+ /* If this is a parser directive, act on it. */
+ if (*string == '$')
+ {
+ handle_parser_directive (&string[1]);
+ return 0;
+ }
+
+ /* If we aren't supposed to be parsing right now, then we're done. */
+ if (_rl_parsing_conditionalized_out)
+ return 0;
+
+ i = 0;
+ /* If this keyname is a complex key expression surrounded by quotes,
+ advance to after the matching close quote. This code allows the
+ backslash to quote characters in the key expression. */
+ if (*string == '"')
+ {
+ i = _rl_skip_to_delim (string, 1, '"');
+
+ /* If we didn't find a closing quote, abort the line. */
+ if (string[i] == '\0')
+ {
+ _rl_init_file_error ("%s: no closing `\"' in key binding", string);
+ return 1;
+ }
+ else
+ i++; /* skip past closing double quote */
+ }
+
+ /* Advance to the colon (:) or whitespace which separates the two objects. */
+ for (; (c = string[i]) && c != ':' && c != ' ' && c != '\t'; i++ );
+
+ if (i == 0)
+ {
+ _rl_init_file_error ("`%s': invalid key binding: missing key sequence", string);
+ return 1;
+ }
+
+ equivalency = (c == ':' && string[i + 1] == '=');
+
+ foundsep = c != 0;
+
+ /* Mark the end of the command (or keyname). */
+ if (string[i])
+ string[i++] = '\0';
+
+ /* If doing assignment, skip the '=' sign as well. */
+ if (equivalency)
+ string[i++] = '\0';
+
+ /* If this is a command to set a variable, then do that. */
+ if (_rl_stricmp (string, "set") == 0)
+ {
+ char *var, *value, *e;
+ int s;
+
+ var = string + i;
+ /* Make VAR point to start of variable name. */
+ while (*var && whitespace (*var)) var++;
+
+ /* Make VALUE point to start of value string. */
+ value = var;
+ while (*value && whitespace (*value) == 0) value++;
+ if (*value)
+ *value++ = '\0';
+ while (*value && whitespace (*value)) value++;
+
+ /* Strip trailing whitespace from values of boolean variables. */
+ if (find_boolean_var (var) >= 0)
+ {
+ /* just read a whitespace-delimited word or empty string */
+ for (e = value; *e && whitespace (*e) == 0; e++)
+ ;
+ if (e > value)
+ *e = '\0'; /* cut off everything trailing */
+ }
+ else if ((i = find_string_var (var)) >= 0)
+ {
+ /* Allow quoted strings in variable values */
+ if (*value == '"')
+ {
+ i = _rl_skip_to_delim (value, 1, *value);
+ value[i] = '\0';
+ value++; /* skip past the quote */
+ }
+ else
+ {
+ /* remove trailing whitespace */
+ e = value + strlen (value) - 1;
+ while (e >= value && whitespace (*e))
+ e--;
+ e++; /* skip back to whitespace or EOS */
+
+ if (*e && e >= value)
+ *e = '\0';
+ }
+ }
+ else
+ {
+ /* avoid calling rl_variable_bind just to find this out */
+ _rl_init_file_error ("%s: unknown variable name", var);
+ return 1;
+ }
+
+ rl_variable_bind (var, value);
+ return 0;
+ }
+
+ /* Skip any whitespace between keyname and funname. */
+ for (; string[i] && whitespace (string[i]); i++);
+ funname = &string[i];
+
+ /* Now isolate funname.
+ For straight function names just look for whitespace, since
+ that will signify the end of the string. But this could be a
+ macro definition. In that case, the string is quoted, so skip
+ to the matching delimiter. We allow the backslash to quote the
+ delimiter characters in the macro body. */
+ /* This code exists to allow whitespace in macro expansions, which
+ would otherwise be gobbled up by the next `for' loop.*/
+ /* XXX - it may be desirable to allow backslash quoting only if " is
+ the quoted string delimiter, like the shell. */
+ if (*funname == '\'' || *funname == '"')
+ {
+ i = _rl_skip_to_delim (string, i+1, *funname);
+ if (string[i])
+ i++;
+ else
+ {
+ _rl_init_file_error ("`%s': missing closing quote for macro", funname);
+ return 1;
+ }
+ }
+
+ /* Advance to the end of the string. */
+ for (; string[i] && whitespace (string[i]) == 0; i++);
+
+ /* No extra whitespace at the end of the string. */
+ string[i] = '\0';
+
+ /* Handle equivalency bindings here. Make the left-hand side be exactly
+ whatever the right-hand evaluates to, including keymaps. */
+ if (equivalency)
+ {
+ return 0;
+ }
+
+ if (foundsep == 0)
+ {
+ _rl_init_file_error ("%s: no key sequence terminator", string);
+ return 1;
+ }
+
+ /* If this is a new-style key-binding, then do the binding with
+ rl_bind_keyseq (). Otherwise, let the older code deal with it. */
+ if (*string == '"')
+ {
+ char *seq;
+ register int j, k, passc;
+
+ seq = (char *)xmalloc (1 + strlen (string));
+ for (j = 1, k = passc = 0; string[j]; j++)
+ {
+ /* Allow backslash to quote characters, but leave them in place.
+ This allows a string to end with a backslash quoting another
+ backslash, or with a backslash quoting a double quote. The
+ backslashes are left in place for rl_translate_keyseq (). */
+ if (passc || (string[j] == '\\'))
+ {
+ seq[k++] = string[j];
+ passc = !passc;
+ continue;
+ }
+
+ if (string[j] == '"')
+ break;
+
+ seq[k++] = string[j];
+ }
+ seq[k] = '\0';
+
+ /* Binding macro? */
+ if (*funname == '\'' || *funname == '"')
+ {
+ j = strlen (funname);
+
+ /* Remove the delimiting quotes from each end of FUNNAME. */
+ if (j && funname[j - 1] == *funname)
+ funname[j - 1] = '\0';
+
+ rl_macro_bind (seq, &funname[1], _rl_keymap);
+ }
+ else
+ rl_bind_keyseq (seq, rl_named_function (funname));
+
+ xfree (seq);
+ return 0;
+ }
+
+ /* Get the actual character we want to deal with. */
+ kname = strrchr (string, '-');
+ if (kname == 0)
+ kname = string;
+ else
+ kname++;
+
+ key = glean_key_from_name (kname);
+
+ /* Add in control and meta bits. */
+ foundmod = 0;
+ if (substring_member_of_array (string, _rl_possible_control_prefixes))
+ {
+ key = CTRL (_rl_to_upper (key));
+ foundmod = 1;
+ }
+
+ if (substring_member_of_array (string, _rl_possible_meta_prefixes))
+ {
+ key = META (key);
+ foundmod = 1;
+ }
+
+ if (foundmod == 0 && kname != string)
+ {
+ _rl_init_file_error ("%s: unknown key modifier", string);
+ return 1;
+ }
+
+ /* Temporary. Handle old-style keyname with macro-binding. */
+ if (*funname == '\'' || *funname == '"')
+ {
+ char useq[2];
+ int fl = strlen (funname);
+
+ useq[0] = key; useq[1] = '\0';
+ if (fl && funname[fl - 1] == *funname)
+ funname[fl - 1] = '\0';
+
+ rl_macro_bind (useq, &funname[1], _rl_keymap);
+ }
+#if defined (PREFIX_META_HACK)
+ /* Ugly, but working hack to keep prefix-meta around. */
+ else if (_rl_stricmp (funname, "prefix-meta") == 0)
+ {
+ char seq[2];
+
+ seq[0] = key;
+ seq[1] = '\0';
+ rl_generic_bind (ISKMAP, seq, (char *)emacs_meta_keymap, _rl_keymap);
+ }
+#endif /* PREFIX_META_HACK */
+ else
+ rl_bind_key (key, rl_named_function (funname));
+
+ return 0;
+}
+
+/* Simple structure for boolean readline variables (i.e., those that can
+ have one of two values; either "On" or 1 for truth, or "Off" or 0 for
+ false. */
+
+#define V_SPECIAL 0x1
+
+static const struct {
+ const char * const name;
+ int *value;
+ int flags;
+} boolean_varlist [] = {
+ { "bind-tty-special-chars", &_rl_bind_stty_chars, 0 },
+ { "blink-matching-paren", &rl_blink_matching_paren, V_SPECIAL },
+ { "byte-oriented", &rl_byte_oriented, 0 },
+#if defined (COLOR_SUPPORT)
+ { "colored-completion-prefix",&_rl_colored_completion_prefix, 0 },
+ { "colored-stats", &_rl_colored_stats, 0 },
+#endif
+ { "completion-ignore-case", &_rl_completion_case_fold, 0 },
+ { "completion-map-case", &_rl_completion_case_map, 0 },
+ { "convert-meta", &_rl_convert_meta_chars_to_ascii, 0 },
+ { "disable-completion", &rl_inhibit_completion, 0 },
+ { "echo-control-characters", &_rl_echo_control_chars, 0 },
+ { "enable-active-region", &_rl_enable_active_region, 0 },
+ { "enable-bracketed-paste", &_rl_enable_bracketed_paste, V_SPECIAL },
+ { "enable-keypad", &_rl_enable_keypad, 0 },
+ { "enable-meta-key", &_rl_enable_meta, 0 },
+ { "expand-tilde", &rl_complete_with_tilde_expansion, 0 },
+ { "history-preserve-point", &_rl_history_preserve_point, 0 },
+ { "horizontal-scroll-mode", &_rl_horizontal_scroll_mode, 0 },
+ { "input-meta", &_rl_meta_flag, 0 },
+ { "mark-directories", &_rl_complete_mark_directories, 0 },
+ { "mark-modified-lines", &_rl_mark_modified_lines, 0 },
+ { "mark-symlinked-directories", &_rl_complete_mark_symlink_dirs, 0 },
+ { "match-hidden-files", &_rl_match_hidden_files, 0 },
+ { "menu-complete-display-prefix", &_rl_menu_complete_prefix_first, 0 },
+ { "meta-flag", &_rl_meta_flag, 0 },
+ { "output-meta", &_rl_output_meta_chars, 0 },
+ { "page-completions", &_rl_page_completions, 0 },
+ { "prefer-visible-bell", &_rl_prefer_visible_bell, V_SPECIAL },
+ { "print-completions-horizontally", &_rl_print_completions_horizontally, 0 },
+ { "revert-all-at-newline", &_rl_revert_all_at_newline, 0 },
+ { "show-all-if-ambiguous", &_rl_complete_show_all, 0 },
+ { "show-all-if-unmodified", &_rl_complete_show_unmodified, 0 },
+ { "show-mode-in-prompt", &_rl_show_mode_in_prompt, 0 },
+ { "skip-completed-text", &_rl_skip_completed_text, 0 },
+#if defined (VISIBLE_STATS)
+ { "visible-stats", &rl_visible_stats, 0 },
+#endif /* VISIBLE_STATS */
+ { (char *)NULL, (int *)NULL, 0 }
+};
+
+static int
+find_boolean_var (const char *name)
+{
+ register int i;
+
+ for (i = 0; boolean_varlist[i].name; i++)
+ if (_rl_stricmp (name, boolean_varlist[i].name) == 0)
+ return i;
+ return -1;
+}
+
+static const char *
+boolean_varname (int i)
+{
+ return ((i >= 0) ? boolean_varlist[i].name : (char *)NULL);
+}
+
+/* Hooks for handling special boolean variables, where a
+ function needs to be called or another variable needs
+ to be changed when they're changed. */
+static void
+hack_special_boolean_var (int i)
+{
+ const char *name;
+
+ name = boolean_varlist[i].name;
+
+ if (_rl_stricmp (name, "blink-matching-paren") == 0)
+ _rl_enable_paren_matching (rl_blink_matching_paren);
+ else if (_rl_stricmp (name, "prefer-visible-bell") == 0)
+ {
+ if (_rl_prefer_visible_bell)
+ _rl_bell_preference = VISIBLE_BELL;
+ else
+ _rl_bell_preference = AUDIBLE_BELL;
+ }
+ else if (_rl_stricmp (name, "show-mode-in-prompt") == 0)
+ _rl_reset_prompt ();
+ else if (_rl_stricmp (name, "enable-bracketed-paste") == 0)
+ _rl_enable_active_region = _rl_enable_bracketed_paste;
+}
+
+typedef int _rl_sv_func_t (const char *);
+
+/* These *must* correspond to the array indices for the appropriate
+ string variable. (Though they're not used right now.) */
+#define V_BELLSTYLE 0
+#define V_COMBEGIN 1
+#define V_EDITMODE 2
+#define V_ISRCHTERM 3
+#define V_KEYMAP 4
+
+#define V_STRING 1
+#define V_INT 2
+
+/* Forward declarations */
+static int sv_region_start_color (const char *);
+static int sv_region_end_color (const char *);
+static int sv_bell_style (const char *);
+static int sv_combegin (const char *);
+static int sv_dispprefix (const char *);
+static int sv_compquery (const char *);
+static int sv_compwidth (const char *);
+static int sv_editmode (const char *);
+static int sv_emacs_modestr (const char *);
+static int sv_histsize (const char *);
+static int sv_isrchterm (const char *);
+static int sv_keymap (const char *);
+static int sv_seqtimeout (const char *);
+static int sv_viins_modestr (const char *);
+static int sv_vicmd_modestr (const char *);
+
+static const struct {
+ const char * const name;
+ int flags;
+ _rl_sv_func_t *set_func;
+} string_varlist[] = {
+ { "active-region-end-color", V_STRING, sv_region_end_color },
+ { "active-region-start-color", V_STRING, sv_region_start_color },
+ { "bell-style", V_STRING, sv_bell_style },
+ { "comment-begin", V_STRING, sv_combegin },
+ { "completion-display-width", V_INT, sv_compwidth },
+ { "completion-prefix-display-length", V_INT, sv_dispprefix },
+ { "completion-query-items", V_INT, sv_compquery },
+ { "editing-mode", V_STRING, sv_editmode },
+ { "emacs-mode-string", V_STRING, sv_emacs_modestr },
+ { "history-size", V_INT, sv_histsize },
+ { "isearch-terminators", V_STRING, sv_isrchterm },
+ { "keymap", V_STRING, sv_keymap },
+ { "keyseq-timeout", V_INT, sv_seqtimeout },
+ { "vi-cmd-mode-string", V_STRING, sv_vicmd_modestr },
+ { "vi-ins-mode-string", V_STRING, sv_viins_modestr },
+ { (char *)NULL, 0, (_rl_sv_func_t *)0 }
+};
+
+static int
+find_string_var (const char *name)
+{
+ register int i;
+
+ for (i = 0; string_varlist[i].name; i++)
+ if (_rl_stricmp (name, string_varlist[i].name) == 0)
+ return i;
+ return -1;
+}
+
+static const char *
+string_varname (int i)
+{
+ return ((i >= 0) ? string_varlist[i].name : (char *)NULL);
+}
+
+/* A boolean value that can appear in a `set variable' command is true if
+ the value is null or empty, `on' (case-insensitive), or "1". All other
+ values result in 0 (false). */
+static int
+bool_to_int (const char *value)
+{
+ return (value == 0 || *value == '\0' ||
+ (_rl_stricmp (value, "on") == 0) ||
+ (value[0] == '1' && value[1] == '\0'));
+}
+
+char *
+rl_variable_value (const char *name)
+{
+ register int i;
+
+ /* Check for simple variables first. */
+ i = find_boolean_var (name);
+ if (i >= 0)
+ return (*boolean_varlist[i].value ? "on" : "off");
+
+ i = find_string_var (name);
+ if (i >= 0)
+ return (_rl_get_string_variable_value (string_varlist[i].name));
+
+ /* Unknown variable names return NULL. */
+ return (char *)NULL;
+}
+
+int
+rl_variable_bind (const char *name, const char *value)
+{
+ register int i;
+ int v;
+
+ /* Check for simple variables first. */
+ i = find_boolean_var (name);
+ if (i >= 0)
+ {
+ *boolean_varlist[i].value = bool_to_int (value);
+ if (boolean_varlist[i].flags & V_SPECIAL)
+ hack_special_boolean_var (i);
+ return 0;
+ }
+
+ i = find_string_var (name);
+
+ /* For the time being, string names without a handler function are simply
+ ignored. */
+ if (i < 0 || string_varlist[i].set_func == 0)
+ {
+ if (i < 0)
+ _rl_init_file_error ("%s: unknown variable name", name);
+ return 0;
+ }
+
+ v = (*string_varlist[i].set_func) (value);
+ if (v != 0)
+ _rl_init_file_error ("%s: could not set value to `%s'", name, value);
+ return v;
+}
+
+static int
+sv_editmode (const char *value)
+{
+ if (_rl_strnicmp (value, "vi", 2) == 0)
+ {
+#if defined (VI_MODE)
+ _rl_keymap = vi_insertion_keymap;
+ rl_editing_mode = vi_mode;
+#endif /* VI_MODE */
+ return 0;
+ }
+ else if (_rl_strnicmp (value, "emacs", 5) == 0)
+ {
+ _rl_keymap = emacs_standard_keymap;
+ rl_editing_mode = emacs_mode;
+ return 0;
+ }
+ return 1;
+}
+
+static int
+sv_combegin (const char *value)
+{
+ if (value && *value)
+ {
+ FREE (_rl_comment_begin);
+ _rl_comment_begin = savestring (value);
+ return 0;
+ }
+ return 1;
+}
+
+static int
+sv_dispprefix (const char *value)
+{
+ int nval = 0;
+
+ if (value && *value)
+ {
+ nval = atoi (value);
+ if (nval < 0)
+ nval = 0;
+ }
+ _rl_completion_prefix_display_length = nval;
+ return 0;
+}
+
+static int
+sv_compquery (const char *value)
+{
+ int nval = 100;
+
+ if (value && *value)
+ {
+ nval = atoi (value);
+ if (nval < 0)
+ nval = 0;
+ }
+ rl_completion_query_items = nval;
+ return 0;
+}
+
+static int
+sv_compwidth (const char *value)
+{
+ int nval = -1;
+
+ if (value && *value)
+ nval = atoi (value);
+
+ _rl_completion_columns = nval;
+ return 0;
+}
+
+static int
+sv_histsize (const char *value)
+{
+ int nval;
+
+ nval = 500;
+ if (value && *value)
+ {
+ nval = atoi (value);
+ if (nval < 0)
+ {
+ unstifle_history ();
+ return 0;
+ }
+ }
+ stifle_history (nval);
+ return 0;
+}
+
+static int
+sv_keymap (const char *value)
+{
+ Keymap kmap;
+
+ kmap = rl_get_keymap_by_name (value);
+ if (kmap)
+ {
+ rl_set_keymap (kmap);
+ return 0;
+ }
+ return 1;
+}
+
+static int
+sv_seqtimeout (const char *value)
+{
+ int nval;
+
+ nval = 0;
+ if (value && *value)
+ {
+ nval = atoi (value);
+ if (nval < 0)
+ nval = 0;
+ }
+ _rl_keyseq_timeout = nval;
+ return 0;
+}
+
+static int
+sv_region_start_color (const char *value)
+{
+ return (_rl_reset_region_color (0, value));
+}
+
+static int
+sv_region_end_color (const char *value)
+{
+ return (_rl_reset_region_color (1, value));
+}
+
+static int
+sv_bell_style (const char *value)
+{
+ if (value == 0 || *value == '\0')
+ _rl_bell_preference = AUDIBLE_BELL;
+ else if (_rl_stricmp (value, "none") == 0 || _rl_stricmp (value, "off") == 0)
+ _rl_bell_preference = NO_BELL;
+ else if (_rl_stricmp (value, "audible") == 0 || _rl_stricmp (value, "on") == 0)
+ _rl_bell_preference = AUDIBLE_BELL;
+ else if (_rl_stricmp (value, "visible") == 0)
+ _rl_bell_preference = VISIBLE_BELL;
+ else
+ return 1;
+ return 0;
+}
+
+static int
+sv_isrchterm (const char *value)
+{
+ int beg, end, delim;
+ char *v;
+
+ if (value == 0)
+ return 1;
+
+ /* Isolate the value and translate it into a character string. */
+ v = savestring (value);
+ FREE (_rl_isearch_terminators);
+ if (v[0] == '"' || v[0] == '\'')
+ {
+ delim = v[0];
+ for (beg = end = 1; v[end] && v[end] != delim; end++)
+ ;
+ }
+ else
+ {
+ for (beg = end = 0; v[end] && whitespace (v[end]) == 0; end++)
+ ;
+ }
+
+ v[end] = '\0';
+
+ /* The value starts at v + beg. Translate it into a character string. */
+ _rl_isearch_terminators = (char *)xmalloc (2 * strlen (v) + 1);
+ rl_translate_keyseq (v + beg, _rl_isearch_terminators, &end);
+ _rl_isearch_terminators[end] = '\0';
+
+ xfree (v);
+ return 0;
+}
+
+extern char *_rl_emacs_mode_str;
+
+static int
+sv_emacs_modestr (const char *value)
+{
+ if (value && *value)
+ {
+ FREE (_rl_emacs_mode_str);
+ _rl_emacs_mode_str = (char *)xmalloc (2 * strlen (value) + 1);
+ rl_translate_keyseq (value, _rl_emacs_mode_str, &_rl_emacs_modestr_len);
+ _rl_emacs_mode_str[_rl_emacs_modestr_len] = '\0';
+ return 0;
+ }
+ else if (value)
+ {
+ FREE (_rl_emacs_mode_str);
+ _rl_emacs_mode_str = (char *)xmalloc (1);
+ _rl_emacs_mode_str[_rl_emacs_modestr_len = 0] = '\0';
+ return 0;
+ }
+ else if (value == 0)
+ {
+ FREE (_rl_emacs_mode_str);
+ _rl_emacs_mode_str = 0; /* prompt_modestr does the right thing */
+ _rl_emacs_modestr_len = 0;
+ return 0;
+ }
+ return 1;
+}
+
+static int
+sv_viins_modestr (const char *value)
+{
+ if (value && *value)
+ {
+ FREE (_rl_vi_ins_mode_str);
+ _rl_vi_ins_mode_str = (char *)xmalloc (2 * strlen (value) + 1);
+ rl_translate_keyseq (value, _rl_vi_ins_mode_str, &_rl_vi_ins_modestr_len);
+ _rl_vi_ins_mode_str[_rl_vi_ins_modestr_len] = '\0';
+ return 0;
+ }
+ else if (value)
+ {
+ FREE (_rl_vi_ins_mode_str);
+ _rl_vi_ins_mode_str = (char *)xmalloc (1);
+ _rl_vi_ins_mode_str[_rl_vi_ins_modestr_len = 0] = '\0';
+ return 0;
+ }
+ else if (value == 0)
+ {
+ FREE (_rl_vi_ins_mode_str);
+ _rl_vi_ins_mode_str = 0; /* prompt_modestr does the right thing */
+ _rl_vi_ins_modestr_len = 0;
+ return 0;
+ }
+ return 1;
+}
+
+static int
+sv_vicmd_modestr (const char *value)
+{
+ if (value && *value)
+ {
+ FREE (_rl_vi_cmd_mode_str);
+ _rl_vi_cmd_mode_str = (char *)xmalloc (2 * strlen (value) + 1);
+ rl_translate_keyseq (value, _rl_vi_cmd_mode_str, &_rl_vi_cmd_modestr_len);
+ _rl_vi_cmd_mode_str[_rl_vi_cmd_modestr_len] = '\0';
+ return 0;
+ }
+ else if (value)
+ {
+ FREE (_rl_vi_cmd_mode_str);
+ _rl_vi_cmd_mode_str = (char *)xmalloc (1);
+ _rl_vi_cmd_mode_str[_rl_vi_cmd_modestr_len = 0] = '\0';
+ return 0;
+ }
+ else if (value == 0)
+ {
+ FREE (_rl_vi_cmd_mode_str);
+ _rl_vi_cmd_mode_str = 0; /* prompt_modestr does the right thing */
+ _rl_vi_cmd_modestr_len = 0;
+ return 0;
+ }
+ return 1;
+}
+
+/* Return the character which matches NAME.
+ For example, `Space' returns ' '. */
+
+typedef struct {
+ const char * const name;
+ int value;
+} assoc_list;
+
+static const assoc_list name_key_alist[] = {
+ { "DEL", 0x7f },
+ { "ESC", '\033' },
+ { "Escape", '\033' },
+ { "LFD", '\n' },
+ { "Newline", '\n' },
+ { "RET", '\r' },
+ { "Return", '\r' },
+ { "Rubout", 0x7f },
+ { "SPC", ' ' },
+ { "Space", ' ' },
+ { "Tab", 0x09 },
+ { (char *)0x0, 0 }
+};
+
+static int
+glean_key_from_name (char *name)
+{
+ register int i;
+
+ for (i = 0; name_key_alist[i].name; i++)
+ if (_rl_stricmp (name, name_key_alist[i].name) == 0)
+ return (name_key_alist[i].value);
+
+ return (*(unsigned char *)name); /* XXX was return (*name) */
+}
+
+/* Auxiliary functions to manage keymaps. */
+struct name_and_keymap {
+ char *name;
+ Keymap map;
+};
+
+static struct name_and_keymap builtin_keymap_names[] = {
+ { "emacs", emacs_standard_keymap },
+ { "emacs-standard", emacs_standard_keymap },
+ { "emacs-meta", emacs_meta_keymap },
+ { "emacs-ctlx", emacs_ctlx_keymap },
+#if defined (VI_MODE)
+ { "vi", vi_movement_keymap },
+ { "vi-move", vi_movement_keymap },
+ { "vi-command", vi_movement_keymap },
+ { "vi-insert", vi_insertion_keymap },
+#endif /* VI_MODE */
+ { (char *)0x0, (Keymap)0x0 }
+};
+
+/* -1 for NULL entry */
+#define NUM_BUILTIN_KEYMAPS (sizeof (builtin_keymap_names) / sizeof (builtin_keymap_names[0]) - 1)
+
+static struct name_and_keymap *keymap_names = builtin_keymap_names;
+
+static int
+_rl_get_keymap_by_name (const char *name)
+{
+ register int i;
+
+ for (i = 0; keymap_names[i].name; i++)
+ if (_rl_stricmp (name, keymap_names[i].name) == 0)
+ return (i);
+ return -1;
+}
+
+Keymap
+rl_get_keymap_by_name (const char *name)
+{
+ int i;
+
+ i = _rl_get_keymap_by_name (name);
+ return ((i >= 0) ? keymap_names[i].map : (Keymap) NULL);
+}
+
+static int
+_rl_get_keymap_by_map (Keymap map)
+{
+ register int i;
+
+ for (i = 0; keymap_names[i].name; i++)
+ if (map == keymap_names[i].map)
+ return (i);
+ return -1;
+}
+
+char *
+rl_get_keymap_name (Keymap map)
+{
+ int i;
+
+ i = _rl_get_keymap_by_map (map);
+ return ((i >= 0) ? keymap_names[i].name : (char *)NULL);
+}
+
+int
+rl_set_keymap_name (const char *name, Keymap map)
+{
+ int i, ni, mi;
+
+ /* First check whether or not we're trying to rename a builtin keymap */
+ mi = _rl_get_keymap_by_map (map);
+ if (mi >= 0 && mi < NUM_BUILTIN_KEYMAPS)
+ return -1;
+
+ /* Then reject attempts to set one of the builtin names to a new map */
+ ni = _rl_get_keymap_by_name (name);
+ if (ni >= 0 && ni < NUM_BUILTIN_KEYMAPS)
+ return -1;
+
+ /* Renaming a keymap we already added */
+ if (mi >= 0) /* XXX - could be >= NUM_BUILTIN_KEYMAPS */
+ {
+ xfree (keymap_names[mi].name);
+ keymap_names[mi].name = savestring (name);
+ return mi;
+ }
+
+ /* Associating new keymap with existing name */
+ if (ni >= 0)
+ {
+ keymap_names[ni].map = map;
+ return ni;
+ }
+
+ for (i = 0; keymap_names[i].name; i++)
+ ;
+
+ if (keymap_names == builtin_keymap_names)
+ {
+ keymap_names = xmalloc ((i + 2) * sizeof (struct name_and_keymap));
+ memcpy (keymap_names, builtin_keymap_names, i * sizeof (struct name_and_keymap));
+ }
+ else
+ keymap_names = xrealloc (keymap_names, (i + 2) * sizeof (struct name_and_keymap));
+
+ keymap_names[i].name = savestring (name);
+ keymap_names[i].map = map;
+
+ keymap_names[i+1].name = NULL;
+ keymap_names[i+1].map = NULL;
+
+ return i;
+}
+
+void
+rl_set_keymap (Keymap map)
+{
+ if (map)
+ _rl_keymap = map;
+}
+
+Keymap
+rl_get_keymap (void)
+{
+ return (_rl_keymap);
+}
+
+void
+rl_set_keymap_from_edit_mode (void)
+{
+ if (rl_editing_mode == emacs_mode)
+ _rl_keymap = emacs_standard_keymap;
+#if defined (VI_MODE)
+ else if (rl_editing_mode == vi_mode)
+ _rl_keymap = vi_insertion_keymap;
+#endif /* VI_MODE */
+}
+
+char *
+rl_get_keymap_name_from_edit_mode (void)
+{
+ if (rl_editing_mode == emacs_mode)
+ return "emacs";
+#if defined (VI_MODE)
+ else if (rl_editing_mode == vi_mode)
+ return "vi";
+#endif /* VI_MODE */
+ else
+ return "none";
+}
+
+/* **************************************************************** */
+/* */
+/* Key Binding and Function Information */
+/* */
+/* **************************************************************** */
+
+/* Each of the following functions produces information about the
+ state of keybindings and functions known to Readline. The info
+ is always printed to rl_outstream, and in such a way that it can
+ be read back in (i.e., passed to rl_parse_and_bind ()). */
+
+/* Print the names of functions known to Readline. */
+void
+rl_list_funmap_names (void)
+{
+ register int i;
+ const char **funmap_names;
+
+ funmap_names = rl_funmap_names ();
+
+ if (!funmap_names)
+ return;
+
+ for (i = 0; funmap_names[i]; i++)
+ fprintf (rl_outstream, "%s\n", funmap_names[i]);
+
+ xfree (funmap_names);
+}
+
+static char *
+_rl_get_keyname (int key)
+{
+ char *keyname;
+ int i, c;
+
+ keyname = (char *)xmalloc (8);
+
+ c = key;
+ /* Since this is going to be used to write out keysequence-function
+ pairs for possible inclusion in an inputrc file, we don't want to
+ do any special meta processing on KEY. */
+
+#if 1
+ /* XXX - Experimental */
+ /* We might want to do this, but the old version of the code did not. */
+
+ /* If this is an escape character, we don't want to do any more processing.
+ Just add the special ESC key sequence and return. */
+ if (c == ESC)
+ {
+ keyname[0] = '\\';
+ keyname[1] = 'e';
+ keyname[2] = '\0';
+ return keyname;
+ }
+#endif
+
+ /* RUBOUT is translated directly into \C-? */
+ if (key == RUBOUT)
+ {
+ keyname[0] = '\\';
+ keyname[1] = 'C';
+ keyname[2] = '-';
+ keyname[3] = '?';
+ keyname[4] = '\0';
+ return keyname;
+ }
+
+ i = 0;
+ /* Now add special prefixes needed for control characters. This can
+ potentially change C. */
+ if (CTRL_CHAR (c))
+ {
+ keyname[i++] = '\\';
+ keyname[i++] = 'C';
+ keyname[i++] = '-';
+ c = _rl_to_lower (UNCTRL (c));
+ }
+
+ /* XXX experimental code. Turn the characters that are not ASCII or
+ ISO Latin 1 (128 - 159) into octal escape sequences (\200 - \237).
+ This changes C. */
+ if (c >= 128 && c <= 159)
+ {
+ keyname[i++] = '\\';
+ keyname[i++] = '2';
+ c -= 128;
+ keyname[i++] = (c / 8) + '0';
+ c = (c % 8) + '0';
+ }
+ /* These characters are valid UTF-8; convert them into octal escape
+ sequences as well. This changes C. */
+ else if (c >= 160)
+ {
+ keyname[i++] = '\\';
+ keyname[i++] = '0' + ((((unsigned char)c) >> 6) & 0x07);
+ keyname[i++] = '0' + ((((unsigned char)c) >> 3) & 0x07);
+ c = (c % 8) + '0';
+ }
+
+ /* Now, if the character needs to be quoted with a backslash, do that. */
+ if (c == '\\' || c == '"')
+ keyname[i++] = '\\';
+
+ /* Now add the key, terminate the string, and return it. */
+ keyname[i++] = (char) c;
+ keyname[i] = '\0';
+
+ return keyname;
+}
+
+/* Return a NULL terminated array of strings which represent the key
+ sequences that are used to invoke FUNCTION in MAP. */
+char **
+rl_invoking_keyseqs_in_map (rl_command_func_t *function, Keymap map)
+{
+ register int key;
+ char **result;
+ int result_index, result_size;
+
+ result = (char **)NULL;
+ result_index = result_size = 0;
+
+ for (key = 0; key < KEYMAP_SIZE; key++)
+ {
+ switch (map[key].type)
+ {
+ case ISMACR:
+ /* Macros match, if, and only if, the pointers are identical.
+ Thus, they are treated exactly like functions in here. */
+ case ISFUNC:
+ /* If the function in the keymap is the one we are looking for,
+ then add the current KEY to the list of invoking keys. */
+ if (map[key].function == function)
+ {
+ char *keyname;
+
+ keyname = _rl_get_keyname (key);
+
+ if (result_index + 2 > result_size)
+ {
+ result_size += 10;
+ result = (char **)xrealloc (result, result_size * sizeof (char *));
+ }
+
+ result[result_index++] = keyname;
+ result[result_index] = (char *)NULL;
+ }
+ break;
+
+ case ISKMAP:
+ {
+ char **seqs;
+ register int i;
+
+ /* Find the list of keyseqs in this map which have FUNCTION as
+ their target. Add the key sequences found to RESULT. */
+ if (map[key].function)
+ seqs =
+ rl_invoking_keyseqs_in_map (function, FUNCTION_TO_KEYMAP (map, key));
+ else
+ break;
+
+ if (seqs == 0)
+ break;
+
+ for (i = 0; seqs[i]; i++)
+ {
+ char *keyname = (char *)xmalloc (6 + strlen (seqs[i]));
+
+ if (key == ESC)
+ {
+ /* If ESC is the meta prefix and we're converting chars
+ with the eighth bit set to ESC-prefixed sequences, then
+ we can use \M-. Otherwise we need to use the sequence
+ for ESC. */
+ if (_rl_convert_meta_chars_to_ascii && map[ESC].type == ISKMAP)
+ sprintf (keyname, "\\M-");
+ else
+ sprintf (keyname, "\\e");
+ }
+ else
+ {
+ int c = key, l = 0;
+ if (CTRL_CHAR (c) || c == RUBOUT)
+ {
+ keyname[l++] = '\\';
+ keyname[l++] = 'C';
+ keyname[l++] = '-';
+ c = (c == RUBOUT) ? '?' : _rl_to_lower (UNCTRL (c));
+ }
+
+ if (c == '\\' || c == '"')
+ keyname[l++] = '\\';
+
+ keyname[l++] = (char) c;
+ keyname[l++] = '\0';
+ }
+
+ strcat (keyname, seqs[i]);
+ xfree (seqs[i]);
+
+ if (result_index + 2 > result_size)
+ {
+ result_size += 10;
+ result = (char **)xrealloc (result, result_size * sizeof (char *));
+ }
+
+ result[result_index++] = keyname;
+ result[result_index] = (char *)NULL;
+ }
+
+ xfree (seqs);
+ }
+ break;
+ }
+ }
+ return (result);
+}
+
+/* Return a NULL terminated array of strings which represent the key
+ sequences that can be used to invoke FUNCTION using the current keymap. */
+char **
+rl_invoking_keyseqs (rl_command_func_t *function)
+{
+ return (rl_invoking_keyseqs_in_map (function, _rl_keymap));
+}
+
+/* Print all of the functions and their bindings to rl_outstream. If
+ PRINT_READABLY is non-zero, then print the output in such a way
+ that it can be read back in. */
+void
+rl_function_dumper (int print_readably)
+{
+ register int i;
+ const char **names;
+ const char *name;
+
+ names = rl_funmap_names ();
+
+ fprintf (rl_outstream, "\n");
+
+ for (i = 0; name = names[i]; i++)
+ {
+ rl_command_func_t *function;
+ char **invokers;
+
+ function = rl_named_function (name);
+ invokers = rl_invoking_keyseqs_in_map (function, _rl_keymap);
+
+ if (print_readably)
+ {
+ if (!invokers)
+ fprintf (rl_outstream, "# %s (not bound)\n", name);
+ else
+ {
+ register int j;
+
+ for (j = 0; invokers[j]; j++)
+ {
+ fprintf (rl_outstream, "\"%s\": %s\n",
+ invokers[j], name);
+ xfree (invokers[j]);
+ }
+
+ xfree (invokers);
+ }
+ }
+ else
+ {
+ if (!invokers)
+ fprintf (rl_outstream, "%s is not bound to any keys\n",
+ name);
+ else
+ {
+ register int j;
+
+ fprintf (rl_outstream, "%s can be found on ", name);
+
+ for (j = 0; invokers[j] && j < 5; j++)
+ {
+ fprintf (rl_outstream, "\"%s\"%s", invokers[j],
+ invokers[j + 1] ? ", " : ".\n");
+ }
+
+ if (j == 5 && invokers[j])
+ fprintf (rl_outstream, "...\n");
+
+ for (j = 0; invokers[j]; j++)
+ xfree (invokers[j]);
+
+ xfree (invokers);
+ }
+ }
+ }
+
+ xfree (names);
+}
+
+/* Print all of the current functions and their bindings to
+ rl_outstream. If an explicit argument is given, then print
+ the output in such a way that it can be read back in. */
+int
+rl_dump_functions (int count, int key)
+{
+ if (rl_dispatching)
+ fprintf (rl_outstream, "\r\n");
+ rl_function_dumper (rl_explicit_arg);
+ rl_on_new_line ();
+ return (0);
+}
+
+static void
+_rl_macro_dumper_internal (int print_readably, Keymap map, char *prefix)
+{
+ register int key;
+ char *keyname, *out;
+ int prefix_len;
+
+ for (key = 0; key < KEYMAP_SIZE; key++)
+ {
+ switch (map[key].type)
+ {
+ case ISMACR:
+ keyname = _rl_get_keyname (key);
+ out = _rl_untranslate_macro_value ((char *)map[key].function, 0);
+
+ if (print_readably)
+ fprintf (rl_outstream, "\"%s%s\": \"%s\"\n", prefix ? prefix : "",
+ keyname,
+ out ? out : "");
+ else
+ fprintf (rl_outstream, "%s%s outputs %s\n", prefix ? prefix : "",
+ keyname,
+ out ? out : "");
+ xfree (keyname);
+ xfree (out);
+ break;
+ case ISFUNC:
+ break;
+ case ISKMAP:
+ prefix_len = prefix ? strlen (prefix) : 0;
+ if (key == ESC)
+ {
+ keyname = (char *)xmalloc (3 + prefix_len);
+ if (prefix)
+ strcpy (keyname, prefix);
+ keyname[prefix_len] = '\\';
+ keyname[prefix_len + 1] = 'e';
+ keyname[prefix_len + 2] = '\0';
+ }
+ else
+ {
+ keyname = _rl_get_keyname (key);
+ if (prefix)
+ {
+ out = (char *)xmalloc (strlen (keyname) + prefix_len + 1);
+ strcpy (out, prefix);
+ strcpy (out + prefix_len, keyname);
+ xfree (keyname);
+ keyname = out;
+ }
+ }
+
+ _rl_macro_dumper_internal (print_readably, FUNCTION_TO_KEYMAP (map, key), keyname);
+ xfree (keyname);
+ break;
+ }
+ }
+}
+
+void
+rl_macro_dumper (int print_readably)
+{
+ _rl_macro_dumper_internal (print_readably, _rl_keymap, (char *)NULL);
+}
+
+int
+rl_dump_macros (int count, int key)
+{
+ if (rl_dispatching)
+ fprintf (rl_outstream, "\r\n");
+ rl_macro_dumper (rl_explicit_arg);
+ rl_on_new_line ();
+ return (0);
+}
+
+static char *
+_rl_get_string_variable_value (const char *name)
+{
+ static char numbuf[32];
+ char *ret;
+
+ if (_rl_stricmp (name, "bell-style") == 0)
+ {
+ switch (_rl_bell_preference)
+ {
+ case NO_BELL:
+ return "none";
+ case VISIBLE_BELL:
+ return "visible";
+ case AUDIBLE_BELL:
+ default:
+ return "audible";
+ }
+ }
+ else if (_rl_stricmp (name, "comment-begin") == 0)
+ return (_rl_comment_begin ? _rl_comment_begin : RL_COMMENT_BEGIN_DEFAULT);
+ else if (_rl_stricmp (name, "completion-display-width") == 0)
+ {
+ sprintf (numbuf, "%d", _rl_completion_columns);
+ return (numbuf);
+ }
+ else if (_rl_stricmp (name, "completion-prefix-display-length") == 0)
+ {
+ sprintf (numbuf, "%d", _rl_completion_prefix_display_length);
+ return (numbuf);
+ }
+ else if (_rl_stricmp (name, "completion-query-items") == 0)
+ {
+ sprintf (numbuf, "%d", rl_completion_query_items);
+ return (numbuf);
+ }
+ else if (_rl_stricmp (name, "editing-mode") == 0)
+ return (rl_get_keymap_name_from_edit_mode ());
+ else if (_rl_stricmp (name, "history-size") == 0)
+ {
+ sprintf (numbuf, "%d", history_is_stifled() ? history_max_entries : 0);
+ return (numbuf);
+ }
+ else if (_rl_stricmp (name, "isearch-terminators") == 0)
+ {
+ if (_rl_isearch_terminators == 0)
+ return 0;
+ ret = _rl_untranslate_macro_value (_rl_isearch_terminators, 0);
+ if (ret)
+ {
+ strncpy (numbuf, ret, sizeof (numbuf) - 1);
+ xfree (ret);
+ numbuf[sizeof(numbuf) - 1] = '\0';
+ }
+ else
+ numbuf[0] = '\0';
+ return numbuf;
+ }
+ else if (_rl_stricmp (name, "keymap") == 0)
+ {
+ ret = rl_get_keymap_name (_rl_keymap);
+ if (ret == 0)
+ ret = rl_get_keymap_name_from_edit_mode ();
+ return (ret ? ret : "none");
+ }
+ else if (_rl_stricmp (name, "keyseq-timeout") == 0)
+ {
+ sprintf (numbuf, "%d", _rl_keyseq_timeout);
+ return (numbuf);
+ }
+ else if (_rl_stricmp (name, "emacs-mode-string") == 0)
+ return (_rl_emacs_mode_str ? _rl_emacs_mode_str : RL_EMACS_MODESTR_DEFAULT);
+ else if (_rl_stricmp (name, "vi-cmd-mode-string") == 0)
+ return (_rl_vi_cmd_mode_str ? _rl_vi_cmd_mode_str : RL_VI_CMD_MODESTR_DEFAULT);
+ else if (_rl_stricmp (name, "vi-ins-mode-string") == 0)
+ return (_rl_vi_ins_mode_str ? _rl_vi_ins_mode_str : RL_VI_INS_MODESTR_DEFAULT);
+ else
+ return (0);
+}
+
+void
+rl_variable_dumper (int print_readably)
+{
+ int i;
+ char *v;
+
+ for (i = 0; boolean_varlist[i].name; i++)
+ {
+ if (print_readably)
+ fprintf (rl_outstream, "set %s %s\n", boolean_varlist[i].name,
+ *boolean_varlist[i].value ? "on" : "off");
+ else
+ fprintf (rl_outstream, "%s is set to `%s'\n", boolean_varlist[i].name,
+ *boolean_varlist[i].value ? "on" : "off");
+ }
+
+ for (i = 0; string_varlist[i].name; i++)
+ {
+ v = _rl_get_string_variable_value (string_varlist[i].name);
+ if (v == 0) /* _rl_isearch_terminators can be NULL */
+ continue;
+ if (print_readably)
+ fprintf (rl_outstream, "set %s %s\n", string_varlist[i].name, v);
+ else
+ fprintf (rl_outstream, "%s is set to `%s'\n", string_varlist[i].name, v);
+ }
+}
+
+/* Print all of the current variables and their values to
+ rl_outstream. If an explicit argument is given, then print
+ the output in such a way that it can be read back in. */
+int
+rl_dump_variables (int count, int key)
+{
+ if (rl_dispatching)
+ fprintf (rl_outstream, "\r\n");
+ rl_variable_dumper (rl_explicit_arg);
+ rl_on_new_line ();
+ return (0);
+}
+
+/* Return non-zero if any members of ARRAY are a substring in STRING. */
+static int
+substring_member_of_array (const char *string, const char * const *array)
+{
+ while (*array)
+ {
+ if (_rl_strindex (string, *array))
+ return (1);
+ array++;
+ }
+ return (0);
+}
diff --git a/third_party/readline/callback.c b/third_party/readline/callback.c
new file mode 100644
index 000000000..0efda03e7
--- /dev/null
+++ b/third_party/readline/callback.c
@@ -0,0 +1,378 @@
+/* callback.c -- functions to use readline as an X `callback' mechanism. */
+
+/* Copyright (C) 1987-2022 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include "rlconf.h"
+
+#if defined (READLINE_CALLBACKS)
+
+#include
+
+#ifdef HAVE_STDLIB_H
+# include
+#else
+# include "ansi_stdlib.h"
+#endif
+
+#include
+
+/* System-specific feature definitions and include files. */
+#include "rldefs.h"
+#include "readline.h"
+#include "rlprivate.h"
+#include "xmalloc.h"
+
+/* Private data for callback registration functions. See comments in
+ rl_callback_read_char for more details. */
+_rl_callback_func_t *_rl_callback_func = 0;
+_rl_callback_generic_arg *_rl_callback_data = 0;
+
+/* Applications can set this to non-zero to have readline's signal handlers
+ installed during the entire duration of reading a complete line, as in
+ readline-6.2. This should be used with care, because it can result in
+ readline receiving signals and not handling them until it's called again
+ via rl_callback_read_char, thereby stealing them from the application.
+ By default, signal handlers are only active while readline is active. */
+int rl_persistent_signal_handlers = 0;
+
+/* **************************************************************** */
+/* */
+/* Callback Readline Functions */
+/* */
+/* **************************************************************** */
+
+/* Allow using readline in situations where a program may have multiple
+ things to handle at once, and dispatches them via select(). Call
+ rl_callback_handler_install() with the prompt and a function to call
+ whenever a complete line of input is ready. The user must then
+ call rl_callback_read_char() every time some input is available, and
+ rl_callback_read_char() will call the user's function with the complete
+ text read in at each end of line. The terminal is kept prepped
+ all the time, except during calls to the user's function. Signal
+ handlers are only installed when the application calls back into
+ readline, so readline doesn't `steal' signals from the application. */
+
+rl_vcpfunc_t *rl_linefunc; /* user callback function */
+static int in_handler; /* terminal_prepped and signals set? */
+
+/* Make sure the terminal is set up, initialize readline, and prompt. */
+static void
+_rl_callback_newline (void)
+{
+ rl_initialize ();
+
+ if (in_handler == 0)
+ {
+ in_handler = 1;
+
+ if (rl_prep_term_function)
+ (*rl_prep_term_function) (_rl_meta_flag);
+
+#if defined (HANDLE_SIGNALS)
+ if (rl_persistent_signal_handlers)
+ rl_set_signals ();
+#endif
+ }
+
+ readline_internal_setup ();
+ RL_CHECK_SIGNALS ();
+}
+
+/* Install a readline handler, set up the terminal, and issue the prompt. */
+void
+rl_callback_handler_install (const char *prompt, rl_vcpfunc_t *linefunc)
+{
+ rl_set_prompt (prompt);
+ RL_SETSTATE (RL_STATE_CALLBACK);
+ rl_linefunc = linefunc;
+ _rl_callback_newline ();
+}
+
+#if defined (HANDLE_SIGNALS)
+#define CALLBACK_READ_RETURN() \
+ do { \
+ if (rl_persistent_signal_handlers == 0) \
+ rl_clear_signals (); \
+ return; \
+ } while (0)
+#else
+#define CALLBACK_READ_RETURN() return
+#endif
+
+/* Read one character, and dispatch to the handler if it ends the line. */
+void
+rl_callback_read_char (void)
+{
+ char *line;
+ int eof, jcode;
+ static procenv_t olevel;
+
+ if (rl_linefunc == NULL)
+ {
+ _rl_errmsg ("readline_callback_read_char() called with no handler!");
+ abort ();
+ }
+
+ eof = 0;
+
+ memcpy ((void *)olevel, (void *)_rl_top_level, sizeof (procenv_t));
+#if defined (HAVE_POSIX_SIGSETJMP)
+ jcode = sigsetjmp (_rl_top_level, 0);
+#else
+ jcode = setjmp (_rl_top_level);
+#endif
+ if (jcode)
+ {
+ (*rl_redisplay_function) ();
+ _rl_want_redisplay = 0;
+ memcpy ((void *)_rl_top_level, (void *)olevel, sizeof (procenv_t));
+
+ /* If we longjmped because of a timeout, handle it here. */
+ if (RL_ISSTATE (RL_STATE_TIMEOUT))
+ {
+ RL_SETSTATE (RL_STATE_DONE);
+ rl_done = 1;
+ }
+
+ CALLBACK_READ_RETURN ();
+ }
+
+#if defined (HANDLE_SIGNALS)
+ /* Install signal handlers only when readline has control. */
+ if (rl_persistent_signal_handlers == 0)
+ rl_set_signals ();
+#endif
+
+ do
+ {
+ RL_CHECK_SIGNALS ();
+ if (RL_ISSTATE (RL_STATE_ISEARCH))
+ {
+ eof = _rl_isearch_callback (_rl_iscxt);
+ if (eof == 0 && (RL_ISSTATE (RL_STATE_ISEARCH) == 0) && RL_ISSTATE (RL_STATE_INPUTPENDING))
+ rl_callback_read_char ();
+
+ CALLBACK_READ_RETURN ();
+ }
+ else if (RL_ISSTATE (RL_STATE_NSEARCH))
+ {
+ eof = _rl_nsearch_callback (_rl_nscxt);
+
+ CALLBACK_READ_RETURN ();
+ }
+#if defined (VI_MODE)
+ /* States that can occur while in state VIMOTION have to be checked
+ before RL_STATE_VIMOTION */
+ else if (RL_ISSTATE (RL_STATE_CHARSEARCH))
+ {
+ int k;
+
+ k = _rl_callback_data->i2;
+
+ eof = (*_rl_callback_func) (_rl_callback_data);
+ /* If the function `deregisters' itself, make sure the data is
+ cleaned up. */
+ if (_rl_callback_func == 0) /* XXX - just sanity check */
+ {
+ if (_rl_callback_data)
+ {
+ _rl_callback_data_dispose (_rl_callback_data);
+ _rl_callback_data = 0;
+ }
+ }
+
+ /* Messy case where vi motion command can be char search */
+ if (RL_ISSTATE (RL_STATE_VIMOTION))
+ {
+ _rl_vi_domove_motion_cleanup (k, _rl_vimvcxt);
+ _rl_internal_char_cleanup ();
+ CALLBACK_READ_RETURN ();
+ }
+
+ _rl_internal_char_cleanup ();
+ }
+ else if (RL_ISSTATE (RL_STATE_VIMOTION))
+ {
+ eof = _rl_vi_domove_callback (_rl_vimvcxt);
+ /* Should handle everything, including cleanup, numeric arguments,
+ and turning off RL_STATE_VIMOTION */
+ if (RL_ISSTATE (RL_STATE_NUMERICARG) == 0)
+ _rl_internal_char_cleanup ();
+
+ CALLBACK_READ_RETURN ();
+ }
+#endif
+ else if (RL_ISSTATE (RL_STATE_NUMERICARG))
+ {
+ eof = _rl_arg_callback (_rl_argcxt);
+ if (eof == 0 && (RL_ISSTATE (RL_STATE_NUMERICARG) == 0) && RL_ISSTATE (RL_STATE_INPUTPENDING))
+ rl_callback_read_char ();
+ /* XXX - this should handle _rl_last_command_was_kill better */
+ else if (RL_ISSTATE (RL_STATE_NUMERICARG) == 0)
+ _rl_internal_char_cleanup ();
+
+ CALLBACK_READ_RETURN ();
+ }
+ else if (RL_ISSTATE (RL_STATE_MULTIKEY))
+ {
+ eof = _rl_dispatch_callback (_rl_kscxt); /* For now */
+ while ((eof == -1 || eof == -2) && RL_ISSTATE (RL_STATE_MULTIKEY) && _rl_kscxt && (_rl_kscxt->flags & KSEQ_DISPATCHED))
+ eof = _rl_dispatch_callback (_rl_kscxt);
+ if (RL_ISSTATE (RL_STATE_MULTIKEY) == 0)
+ {
+ _rl_internal_char_cleanup ();
+ _rl_want_redisplay = 1;
+ }
+ }
+ else if (_rl_callback_func)
+ {
+ /* This allows functions that simply need to read an additional
+ character (like quoted-insert) to register a function to be
+ called when input is available. _rl_callback_data is a
+ pointer to a struct that has the argument count originally
+ passed to the registering function and space for any additional
+ parameters. */
+ eof = (*_rl_callback_func) (_rl_callback_data);
+ /* If the function `deregisters' itself, make sure the data is
+ cleaned up. */
+ if (_rl_callback_func == 0)
+ {
+ if (_rl_callback_data)
+ {
+ _rl_callback_data_dispose (_rl_callback_data);
+ _rl_callback_data = 0;
+ }
+ _rl_internal_char_cleanup ();
+ }
+ }
+ else
+ eof = readline_internal_char ();
+
+ RL_CHECK_SIGNALS ();
+ if (rl_done == 0 && _rl_want_redisplay)
+ {
+ (*rl_redisplay_function) ();
+ _rl_want_redisplay = 0;
+ }
+
+ /* Make sure application hooks can see whether we saw EOF. */
+ if (eof > 0)
+ {
+ rl_eof_found = eof;
+ RL_SETSTATE(RL_STATE_EOF);
+ }
+
+ if (rl_done)
+ {
+ line = readline_internal_teardown (eof);
+
+ if (rl_deprep_term_function)
+ (*rl_deprep_term_function) ();
+#if defined (HANDLE_SIGNALS)
+ rl_clear_signals ();
+#endif
+ in_handler = 0;
+ if (rl_linefunc) /* just in case */
+ (*rl_linefunc) (line);
+
+ /* If the user did not clear out the line, do it for him. */
+ if (rl_line_buffer[0])
+ _rl_init_line_state ();
+
+ /* Redisplay the prompt if readline_handler_{install,remove}
+ not called. */
+ if (in_handler == 0 && rl_linefunc)
+ _rl_callback_newline ();
+ }
+ }
+ while (rl_pending_input || _rl_pushed_input_available () || RL_ISSTATE (RL_STATE_MACROINPUT));
+
+ CALLBACK_READ_RETURN ();
+}
+
+/* Remove the handler, and make sure the terminal is in its normal state. */
+void
+rl_callback_handler_remove (void)
+{
+ rl_linefunc = NULL;
+ RL_UNSETSTATE (RL_STATE_CALLBACK);
+ RL_CHECK_SIGNALS ();
+ if (in_handler)
+ {
+ in_handler = 0;
+ if (rl_deprep_term_function)
+ (*rl_deprep_term_function) ();
+#if defined (HANDLE_SIGNALS)
+ rl_clear_signals ();
+#endif
+ }
+}
+
+_rl_callback_generic_arg *
+_rl_callback_data_alloc (int count)
+{
+ _rl_callback_generic_arg *arg;
+
+ arg = (_rl_callback_generic_arg *)xmalloc (sizeof (_rl_callback_generic_arg));
+ arg->count = count;
+
+ arg->i1 = arg->i2 = 0;
+
+ return arg;
+}
+
+void
+_rl_callback_data_dispose (_rl_callback_generic_arg *arg)
+{
+ xfree (arg);
+}
+
+/* Make sure that this agrees with cases in rl_callback_read_char */
+void
+rl_callback_sigcleanup (void)
+{
+ if (RL_ISSTATE (RL_STATE_CALLBACK) == 0)
+ return;
+
+ if (RL_ISSTATE (RL_STATE_ISEARCH))
+ _rl_isearch_cleanup (_rl_iscxt, 0);
+ else if (RL_ISSTATE (RL_STATE_NSEARCH))
+ _rl_nsearch_cleanup (_rl_nscxt, 0);
+ else if (RL_ISSTATE (RL_STATE_VIMOTION))
+ RL_UNSETSTATE (RL_STATE_VIMOTION);
+ else if (RL_ISSTATE (RL_STATE_NUMERICARG))
+ {
+ _rl_argcxt = 0;
+ RL_UNSETSTATE (RL_STATE_NUMERICARG);
+ }
+ else if (RL_ISSTATE (RL_STATE_MULTIKEY))
+ RL_UNSETSTATE (RL_STATE_MULTIKEY);
+ if (RL_ISSTATE (RL_STATE_CHARSEARCH))
+ RL_UNSETSTATE (RL_STATE_CHARSEARCH);
+
+ _rl_callback_func = 0;
+}
+#endif
diff --git a/third_party/readline/chardefs.h b/third_party/readline/chardefs.h
new file mode 100644
index 000000000..24a25f1f1
--- /dev/null
+++ b/third_party/readline/chardefs.h
@@ -0,0 +1,165 @@
+/* chardefs.h -- Character definitions for readline. */
+
+/* Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#ifndef _CHARDEFS_H_
+#define _CHARDEFS_H_
+
+#include
+
+#if defined (HAVE_CONFIG_H)
+# if defined (HAVE_STRING_H)
+# include
+# endif /* HAVE_STRING_H */
+# if defined (HAVE_STRINGS_H)
+# include
+# endif /* HAVE_STRINGS_H */
+#else
+# include
+#endif /* !HAVE_CONFIG_H */
+
+#ifndef whitespace
+#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
+#endif
+
+#ifdef CTRL
+# undef CTRL
+#endif
+#ifdef UNCTRL
+# undef UNCTRL
+#endif
+
+/* Some character stuff. */
+#define control_character_threshold 0x020 /* Smaller than this is control. */
+#define control_character_mask 0x1f /* 0x20 - 1 */
+#define meta_character_threshold 0x07f /* Larger than this is Meta. */
+#define control_character_bit 0x40 /* 0x000000, must be off. */
+#define meta_character_bit 0x080 /* x0000000, must be on. */
+#define largest_char 255 /* Largest character value. */
+
+#define CTRL_CHAR(c) ((c) < control_character_threshold && (((c) & 0x80) == 0))
+#define META_CHAR(c) ((c) > meta_character_threshold && (c) <= largest_char)
+
+#define CTRL(c) ((c) & control_character_mask)
+#define META(c) ((c) | meta_character_bit)
+
+#define UNMETA(c) ((c) & (~meta_character_bit))
+#define UNCTRL(c) _rl_to_upper(((c)|control_character_bit))
+
+#ifndef UCHAR_MAX
+# define UCHAR_MAX 255
+#endif
+#ifndef CHAR_MAX
+# define CHAR_MAX 127
+#endif
+
+/* use this as a proxy for C89 */
+#if defined (HAVE_STDLIB_H) && defined (HAVE_STRING_H)
+# define IN_CTYPE_DOMAIN(c) 1
+# define NON_NEGATIVE(c) 1
+#else
+# define IN_CTYPE_DOMAIN(c) ((c) >= 0 && (c) <= CHAR_MAX)
+# define NON_NEGATIVE(c) ((unsigned char)(c) == (c))
+#endif
+
+#if !defined (isxdigit) && !defined (HAVE_ISXDIGIT) && !defined (__cplusplus)
+# define isxdigit(c) (isdigit((unsigned char)(c)) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
+#endif
+
+/* Some systems define these; we want our definitions. */
+#undef ISPRINT
+
+/* Beware: these only work with single-byte ASCII characters. */
+
+#define ISALNUM(c) (IN_CTYPE_DOMAIN (c) && isalnum ((unsigned char)c))
+#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha ((unsigned char)c))
+#define ISDIGIT(c) (IN_CTYPE_DOMAIN (c) && isdigit ((unsigned char)c))
+#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower ((unsigned char)c))
+#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint ((unsigned char)c))
+#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper ((unsigned char)c))
+#define ISXDIGIT(c) (IN_CTYPE_DOMAIN (c) && isxdigit ((unsigned char)c))
+
+#define _rl_lowercase_p(c) (NON_NEGATIVE(c) && ISLOWER(c))
+#define _rl_uppercase_p(c) (NON_NEGATIVE(c) && ISUPPER(c))
+#define _rl_digit_p(c) ((c) >= '0' && (c) <= '9')
+
+#define _rl_alphabetic_p(c) (NON_NEGATIVE(c) && ISALNUM(c))
+#define _rl_pure_alphabetic(c) (NON_NEGATIVE(c) && ISALPHA(c))
+
+#ifndef _rl_to_upper
+# define _rl_to_upper(c) (_rl_lowercase_p(c) ? toupper((unsigned char)(c)) : (c))
+# define _rl_to_lower(c) (_rl_uppercase_p(c) ? tolower((unsigned char)(c)) : (c))
+#endif
+
+#ifndef _rl_digit_value
+# define _rl_digit_value(x) ((x) - '0')
+#endif
+
+#ifndef _rl_isident
+# define _rl_isident(c) (ISALNUM(c) || (c) == '_')
+#endif
+
+#ifndef ISOCTAL
+# define ISOCTAL(c) ((c) >= '0' && (c) <= '7')
+#endif
+#define OCTVALUE(c) ((c) - '0')
+
+#define HEXVALUE(c) \
+ (((c) >= 'a' && (c) <= 'f') \
+ ? (c)-'a'+10 \
+ : (c) >= 'A' && (c) <= 'F' ? (c)-'A'+10 : (c)-'0')
+
+#ifndef NEWLINE
+#define NEWLINE '\n'
+#endif
+
+#ifndef RETURN
+#define RETURN CTRL('M')
+#endif
+
+#ifndef RUBOUT
+#define RUBOUT 0x7f
+#endif
+
+#ifndef TAB
+#define TAB '\t'
+#endif
+
+#ifdef ABORT_CHAR
+#undef ABORT_CHAR
+#endif
+#define ABORT_CHAR CTRL('G')
+
+#ifdef PAGE
+#undef PAGE
+#endif
+#define PAGE CTRL('L')
+
+#ifdef SPACE
+#undef SPACE
+#endif
+#define SPACE ' ' /* XXX - was 0x20 */
+
+#ifdef ESC
+#undef ESC
+#endif
+#define ESC CTRL('[')
+
+#endif /* _CHARDEFS_H_ */
diff --git a/third_party/readline/colors.c b/third_party/readline/colors.c
new file mode 100644
index 000000000..d18880e48
--- /dev/null
+++ b/third_party/readline/colors.c
@@ -0,0 +1,320 @@
+/* `dir', `vdir' and `ls' directory listing programs for GNU.
+
+ Modified by Chet Ramey for Readline.
+
+ Copyright (C) 1985, 1988, 1990-1991, 1995-2021
+ Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see . */
+
+/* Written by Richard Stallman and David MacKenzie. */
+
+/* Color support by Peter Anvin and Dennis
+ Flaherty based on original patches by
+ Greg Lee . */
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include "rlconf.h"
+
+#if defined __TANDEM
+# define _XOPEN_SOURCE_EXTENDED 1
+# define _TANDEM_SOURCE 1
+# include
+# include
+#endif
+
+#include
+
+#include "posixstat.h" // stat related macros (S_ISREG, ...)
+#include // S_ISUID
+
+#ifndef S_ISDIR
+# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+
+// strlen()
+#if defined (HAVE_STRING_H)
+# include
+#else /* !HAVE_STRING_H */
+# include
+#endif /* !HAVE_STRING_H */
+
+// abort()
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#include "readline.h"
+#include "rldefs.h"
+
+#ifdef COLOR_SUPPORT
+
+#include "xmalloc.h"
+#include "colors.h"
+
+static bool is_colored (enum indicator_no type);
+static void restore_default_color (void);
+
+#define RL_COLOR_PREFIX_EXTENSION "readline-colored-completion-prefix"
+
+COLOR_EXT_TYPE *_rl_color_ext_list = 0;
+
+/* Output a color indicator (which may contain nulls). */
+void
+_rl_put_indicator (const struct bin_str *ind)
+{
+ fwrite (ind->string, ind->len, 1, rl_outstream);
+}
+
+static bool
+is_colored (enum indicator_no colored_filetype)
+{
+ size_t len = _rl_color_indicator[colored_filetype].len;
+ char const *s = _rl_color_indicator[colored_filetype].string;
+ return ! (len == 0
+ || (len == 1 && strncmp (s, "0", 1) == 0)
+ || (len == 2 && strncmp (s, "00", 2) == 0));
+}
+
+static void
+restore_default_color (void)
+{
+ _rl_put_indicator (&_rl_color_indicator[C_LEFT]);
+ _rl_put_indicator (&_rl_color_indicator[C_RIGHT]);
+}
+
+void
+_rl_set_normal_color (void)
+{
+ if (is_colored (C_NORM))
+ {
+ _rl_put_indicator (&_rl_color_indicator[C_LEFT]);
+ _rl_put_indicator (&_rl_color_indicator[C_NORM]);
+ _rl_put_indicator (&_rl_color_indicator[C_RIGHT]);
+ }
+}
+
+static struct bin_str *
+_rl_custom_readline_prefix (void)
+{
+ size_t len;
+ COLOR_EXT_TYPE *ext;
+
+ len = strlen (RL_COLOR_PREFIX_EXTENSION);
+ for (ext = _rl_color_ext_list; ext; ext = ext->next)
+ if (ext->ext.len == len && STREQN (ext->ext.string, RL_COLOR_PREFIX_EXTENSION, len))
+ return (&ext->seq);
+ return (NULL);
+}
+
+bool
+_rl_print_prefix_color (void)
+{
+ struct bin_str *s;
+
+ /* What do we want to use for the prefix? Let's try cyan first, see colors.h */
+ s = _rl_custom_readline_prefix ();
+ if (s == 0)
+ s = &_rl_color_indicator[C_PREFIX];
+ if (s->string != NULL)
+ {
+ if (is_colored (C_NORM))
+ restore_default_color ();
+ _rl_put_indicator (&_rl_color_indicator[C_LEFT]);
+ _rl_put_indicator (s);
+ _rl_put_indicator (&_rl_color_indicator[C_RIGHT]);
+ return 0;
+ }
+ else
+ return 1;
+}
+
+/* Returns whether any color sequence was printed. */
+bool
+_rl_print_color_indicator (const char *f)
+{
+ enum indicator_no colored_filetype;
+ COLOR_EXT_TYPE *ext; /* Color extension */
+ size_t len; /* Length of name */
+
+ const char* name;
+ char *filename;
+ struct stat astat, linkstat;
+ mode_t mode;
+ int linkok; /* 1 == ok, 0 == dangling symlink, -1 == missing */
+ int stat_ok;
+
+ name = f;
+
+ /* This should already have undergone tilde expansion */
+ filename = 0;
+ if (rl_filename_stat_hook)
+ {
+ filename = savestring (f);
+ (*rl_filename_stat_hook) (&filename);
+ name = filename;
+ }
+
+#if defined (HAVE_LSTAT)
+ stat_ok = lstat(name, &astat);
+#else
+ stat_ok = stat(name, &astat);
+#endif
+ if (stat_ok == 0)
+ {
+ mode = astat.st_mode;
+#if defined (HAVE_LSTAT)
+ if (S_ISLNK (mode))
+ {
+ linkok = stat (name, &linkstat) == 0;
+ if (linkok && strncmp (_rl_color_indicator[C_LINK].string, "target", 6) == 0)
+ mode = linkstat.st_mode;
+ }
+ else
+#endif
+ linkok = 1;
+ }
+ else
+ linkok = -1;
+
+ /* Is this a nonexistent file? If so, linkok == -1. */
+
+ if (linkok == -1 && _rl_color_indicator[C_MISSING].string != NULL)
+ colored_filetype = C_MISSING;
+ else if (linkok == 0 && _rl_color_indicator[C_ORPHAN].string != NULL)
+ colored_filetype = C_ORPHAN; /* dangling symlink */
+ else if(stat_ok != 0)
+ {
+ static enum indicator_no filetype_indicator[] = FILETYPE_INDICATORS;
+ colored_filetype = filetype_indicator[normal]; //f->filetype];
+ }
+ else
+ {
+ if (S_ISREG (mode))
+ {
+ colored_filetype = C_FILE;
+
+#if defined (S_ISUID)
+ if ((mode & S_ISUID) != 0 && is_colored (C_SETUID))
+ colored_filetype = C_SETUID;
+ else
+#endif
+#if defined (S_ISGID)
+ if ((mode & S_ISGID) != 0 && is_colored (C_SETGID))
+ colored_filetype = C_SETGID;
+ else
+#endif
+ if (is_colored (C_CAP) && 0) //f->has_capability)
+ colored_filetype = C_CAP;
+ else if ((mode & S_IXUGO) != 0 && is_colored (C_EXEC))
+ colored_filetype = C_EXEC;
+ else if ((1 < astat.st_nlink) && is_colored (C_MULTIHARDLINK))
+ colored_filetype = C_MULTIHARDLINK;
+ }
+ else if (S_ISDIR (mode))
+ {
+ colored_filetype = C_DIR;
+
+#if defined (S_ISVTX)
+ if ((mode & S_ISVTX) && (mode & S_IWOTH)
+ && is_colored (C_STICKY_OTHER_WRITABLE))
+ colored_filetype = C_STICKY_OTHER_WRITABLE;
+ else
+#endif
+ if ((mode & S_IWOTH) != 0 && is_colored (C_OTHER_WRITABLE))
+ colored_filetype = C_OTHER_WRITABLE;
+#if defined (S_ISVTX)
+ else if ((mode & S_ISVTX) != 0 && is_colored (C_STICKY))
+ colored_filetype = C_STICKY;
+#endif
+ }
+#if defined (S_ISLNK)
+ else if (S_ISLNK (mode))
+ colored_filetype = C_LINK;
+#endif
+ else if (S_ISFIFO (mode))
+ colored_filetype = C_FIFO;
+#if defined (S_ISSOCK)
+ else if (S_ISSOCK (mode))
+ colored_filetype = C_SOCK;
+#endif
+#if defined (S_ISBLK)
+ else if (S_ISBLK (mode))
+ colored_filetype = C_BLK;
+#endif
+ else if (S_ISCHR (mode))
+ colored_filetype = C_CHR;
+ else
+ {
+ /* Classify a file of some other type as C_ORPHAN. */
+ colored_filetype = C_ORPHAN;
+ }
+ }
+
+ /* Check the file's suffix only if still classified as C_FILE. */
+ ext = NULL;
+ if (colored_filetype == C_FILE)
+ {
+ /* Test if NAME has a recognized suffix. */
+ len = strlen (name);
+ name += len; /* Pointer to final \0. */
+ for (ext = _rl_color_ext_list; ext != NULL; ext = ext->next)
+ {
+ if (ext->ext.len <= len
+ && strncmp (name - ext->ext.len, ext->ext.string,
+ ext->ext.len) == 0)
+ break;
+ }
+ }
+
+ free (filename); /* NULL or savestring return value */
+
+ {
+ const struct bin_str *const s
+ = ext ? &(ext->seq) : &_rl_color_indicator[colored_filetype];
+ if (s->string != NULL)
+ {
+ /* Need to reset so not dealing with attribute combinations */
+ if (is_colored (C_NORM))
+ restore_default_color ();
+ _rl_put_indicator (&_rl_color_indicator[C_LEFT]);
+ _rl_put_indicator (s);
+ _rl_put_indicator (&_rl_color_indicator[C_RIGHT]);
+ return 0;
+ }
+ else
+ return 1;
+ }
+}
+
+void
+_rl_prep_non_filename_text (void)
+{
+ if (_rl_color_indicator[C_END].string != NULL)
+ _rl_put_indicator (&_rl_color_indicator[C_END]);
+ else
+ {
+ _rl_put_indicator (&_rl_color_indicator[C_LEFT]);
+ _rl_put_indicator (&_rl_color_indicator[C_RESET]);
+ _rl_put_indicator (&_rl_color_indicator[C_RIGHT]);
+ }
+}
+#endif /* COLOR_SUPPORT */
diff --git a/third_party/readline/colors.h b/third_party/readline/colors.h
new file mode 100644
index 000000000..6561ad90c
--- /dev/null
+++ b/third_party/readline/colors.h
@@ -0,0 +1,126 @@
+/* `dir', `vdir' and `ls' directory listing programs for GNU.
+
+ Modified by Chet Ramey for Readline.
+
+ Copyright (C) 1985, 1988, 1990-1991, 1995-2010, 2012, 2015
+ Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see . */
+
+/* Written by Richard Stallman and David MacKenzie. */
+
+/* Color support by Peter Anvin and Dennis
+ Flaherty based on original patches by
+ Greg Lee . */
+
+#ifndef _COLORS_H_
+#define _COLORS_H_
+
+#include // size_t
+
+#if defined(__TANDEM) && defined(HAVE_STDBOOL_H) && (__STDC_VERSION__ < 199901L)
+typedef int _Bool;
+#endif
+
+#if defined (HAVE_STDBOOL_H)
+# include // bool
+#else
+typedef int _rl_bool_t;
+
+#ifdef bool
+# undef bool
+#endif
+#define bool _rl_bool_t
+
+#ifndef true
+# define true 1
+# define false 0
+#endif
+
+#endif /* !HAVE_STDBOOL_H */
+
+/* Null is a valid character in a color indicator (think about Epson
+ printers, for example) so we have to use a length/buffer string
+ type. */
+struct bin_str
+ {
+ size_t len;
+ const char *string;
+ };
+
+/* file type indicators (dir, sock, fifo, ...)
+ Default value is initialized in parse-colors.c.
+ It is then modified from the values of $LS_COLORS. */
+extern struct bin_str _rl_color_indicator[];
+
+/* The LS_COLORS variable is in a termcap-like format. */
+typedef struct _color_ext_type
+ {
+ struct bin_str ext; /* The extension we're looking for */
+ struct bin_str seq; /* The sequence to output when we do */
+ struct _color_ext_type *next; /* Next in list */
+ } COLOR_EXT_TYPE;
+
+/* file extensions indicators (.txt, .log, .jpg, ...)
+ Values are taken from $LS_COLORS in rl_parse_colors(). */
+extern COLOR_EXT_TYPE *_rl_color_ext_list;
+
+#define FILETYPE_INDICATORS \
+ { \
+ C_ORPHAN, C_FIFO, C_CHR, C_DIR, C_BLK, C_FILE, \
+ C_LINK, C_SOCK, C_FILE, C_DIR \
+ }
+
+/* Whether we used any colors in the output so far. If so, we will
+ need to restore the default color later. If not, we will need to
+ call prep_non_filename_text before using color for the first time. */
+
+enum indicator_no
+ {
+ C_LEFT, C_RIGHT, C_END, C_RESET, C_NORM, C_FILE, C_DIR, C_LINK,
+ C_FIFO, C_SOCK,
+ C_BLK, C_CHR, C_MISSING, C_ORPHAN, C_EXEC, C_DOOR, C_SETUID, C_SETGID,
+ C_STICKY, C_OTHER_WRITABLE, C_STICKY_OTHER_WRITABLE, C_CAP, C_MULTIHARDLINK,
+ C_CLR_TO_EOL
+ };
+
+
+#if !S_IXUGO
+# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
+#endif
+
+enum filetype
+ {
+ unknown,
+ fifo,
+ chardev,
+ directory,
+ blockdev,
+ normal,
+ symbolic_link,
+ sock,
+ whiteout,
+ arg_directory
+ };
+
+/* Prefix color, currently same as socket */
+#define C_PREFIX C_SOCK
+
+extern void _rl_put_indicator (const struct bin_str *ind);
+extern void _rl_set_normal_color (void);
+extern bool _rl_print_prefix_color (void);
+extern bool _rl_print_color_indicator (const char *f);
+extern void _rl_prep_non_filename_text (void);
+
+#endif /* !_COLORS_H_ */
diff --git a/third_party/readline/compat.c b/third_party/readline/compat.c
new file mode 100644
index 000000000..bc3a043c5
--- /dev/null
+++ b/third_party/readline/compat.c
@@ -0,0 +1,106 @@
+/* compat.c -- backwards compatibility functions. */
+
+/* Copyright (C) 2000-2021 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+
+#include "rlstdc.h"
+#include "rltypedefs.h"
+
+extern void rl_free_undo_list (void);
+extern int rl_maybe_save_line (void);
+extern int rl_maybe_unsave_line (void);
+extern int rl_maybe_replace_line (void);
+
+extern int rl_crlf (void);
+extern int rl_ding (void);
+extern int rl_alphabetic (int);
+
+extern char **rl_completion_matches (const char *, rl_compentry_func_t *);
+extern char *rl_username_completion_function (const char *, int);
+extern char *rl_filename_completion_function (const char *, int);
+
+/* Provide backwards-compatible entry points for old function names. */
+
+void
+free_undo_list (void)
+{
+ rl_free_undo_list ();
+}
+
+int
+maybe_replace_line (void)
+{
+ return rl_maybe_replace_line ();
+}
+
+int
+maybe_save_line (void)
+{
+ return rl_maybe_save_line ();
+}
+
+int
+maybe_unsave_line (void)
+{
+ return rl_maybe_unsave_line ();
+}
+
+int
+ding (void)
+{
+ return rl_ding ();
+}
+
+int
+crlf (void)
+{
+ return rl_crlf ();
+}
+
+int
+alphabetic (int c)
+{
+ return rl_alphabetic (c);
+}
+
+char **
+completion_matches (const char *s, rl_compentry_func_t *f)
+{
+ return rl_completion_matches (s, f);
+}
+
+char *
+username_completion_function (const char *s, int i)
+{
+ return rl_username_completion_function (s, i);
+}
+
+char *
+filename_completion_function (const char *s, int i)
+{
+ return rl_filename_completion_function (s, i);
+}
diff --git a/third_party/readline/complete.c b/third_party/readline/complete.c
new file mode 100644
index 000000000..99c579211
--- /dev/null
+++ b/third_party/readline/complete.c
@@ -0,0 +1,2992 @@
+/* complete.c -- filename completion for readline. */
+
+/* Copyright (C) 1987-2021 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (__TANDEM)
+# define _XOPEN_SOURCE_EXTENDED 1
+#endif
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+#if defined (__TANDEM)
+# include
+#endif
+#include
+#if defined (HAVE_SYS_FILE_H)
+# include
+#endif
+
+#include
+
+#if defined (HAVE_UNISTD_H)
+# include
+#endif /* HAVE_UNISTD_H */
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#include
+
+#include
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+#if defined (HAVE_PWD_H)
+#include
+#endif
+
+#include "posixdir.h"
+#include "posixstat.h"
+
+/* System-specific feature definitions and include files. */
+#include "rldefs.h"
+#include "rlmbutil.h"
+
+/* Some standard library routines. */
+#include "readline.h"
+#include "xmalloc.h"
+#include "rlprivate.h"
+
+#if defined (COLOR_SUPPORT)
+# include "colors.h"
+#endif
+
+#ifdef __STDC__
+typedef int QSFUNC (const void *, const void *);
+#else
+typedef int QSFUNC ();
+#endif
+
+#ifdef HAVE_LSTAT
+# define LSTAT lstat
+#else
+# define LSTAT stat
+#endif
+
+/* Unix version of a hidden file. Could be different on other systems. */
+#define HIDDEN_FILE(fname) ((fname)[0] == '.')
+
+/* Most systems don't declare getpwent in if _POSIX_SOURCE is
+ defined. */
+#if defined (HAVE_GETPWENT) && (!defined (HAVE_GETPW_DECLS) || defined (_POSIX_SOURCE))
+extern struct passwd *getpwent (void);
+#endif /* HAVE_GETPWENT && (!HAVE_GETPW_DECLS || _POSIX_SOURCE) */
+
+/* If non-zero, then this is the address of a function to call when
+ completing a word would normally display the list of possible matches.
+ This function is called instead of actually doing the display.
+ It takes three arguments: (char **matches, int num_matches, int max_length)
+ where MATCHES is the array of strings that matched, NUM_MATCHES is the
+ number of strings in that array, and MAX_LENGTH is the length of the
+ longest string in that array. */
+rl_compdisp_func_t *rl_completion_display_matches_hook = (rl_compdisp_func_t *)NULL;
+
+#if defined (VISIBLE_STATS) || defined (COLOR_SUPPORT)
+# if !defined (X_OK)
+# define X_OK 1
+# endif
+#endif
+
+#if defined (VISIBLE_STATS)
+static int stat_char (char *);
+#endif
+
+#if defined (COLOR_SUPPORT)
+static int colored_stat_start (const char *);
+static void colored_stat_end (void);
+static int colored_prefix_start (void);
+static void colored_prefix_end (void);
+#endif
+
+static int path_isdir (const char *);
+
+static char *rl_quote_filename (char *, int, char *);
+
+static void _rl_complete_sigcleanup (int, void *);
+
+static void set_completion_defaults (int);
+static int get_y_or_n (int);
+static int _rl_internal_pager (int);
+static char *printable_part (char *);
+static int fnwidth (const char *);
+static int fnprint (const char *, int, const char *);
+static int print_filename (char *, char *, int);
+
+static char **gen_completion_matches (char *, int, int, rl_compentry_func_t *, int, int);
+
+static char **remove_duplicate_matches (char **);
+static void insert_match (char *, int, int, char *);
+static int append_to_match (char *, int, int, int);
+static void insert_all_matches (char **, int, char *);
+static int complete_fncmp (const char *, int, const char *, int);
+static void display_matches (char **);
+static int compute_lcd_of_matches (char **, int, const char *);
+static int postprocess_matches (char ***, int);
+static int compare_match (char *, const char *);
+static int complete_get_screenwidth (void);
+
+static char *make_quoted_replacement (char *, int, char *);
+
+/* **************************************************************** */
+/* */
+/* Completion matching, from readline's point of view. */
+/* */
+/* **************************************************************** */
+
+/* Variables known only to the readline library. */
+
+/* If non-zero, non-unique completions always show the list of matches. */
+int _rl_complete_show_all = 0;
+
+/* If non-zero, non-unique completions show the list of matches, unless it
+ is not possible to do partial completion and modify the line. */
+int _rl_complete_show_unmodified = 0;
+
+/* If non-zero, completed directory names have a slash appended. */
+int _rl_complete_mark_directories = 1;
+
+/* If non-zero, the symlinked directory completion behavior introduced in
+ readline-4.2a is disabled, and symlinks that point to directories have
+ a slash appended (subject to the value of _rl_complete_mark_directories).
+ This is user-settable via the mark-symlinked-directories variable. */
+int _rl_complete_mark_symlink_dirs = 0;
+
+/* If non-zero, completions are printed horizontally in alphabetical order,
+ like `ls -x'. */
+int _rl_print_completions_horizontally;
+
+/* Non-zero means that case is not significant in filename completion. */
+#if (defined (__MSDOS__) && !defined (__DJGPP__)) || (defined (_WIN32) && !defined (__CYGWIN__))
+int _rl_completion_case_fold = 1;
+#else
+int _rl_completion_case_fold = 0;
+#endif
+
+/* Non-zero means that `-' and `_' are equivalent when comparing filenames
+ for completion. */
+int _rl_completion_case_map = 0;
+
+/* If zero, don't match hidden files (filenames beginning with a `.' on
+ Unix) when doing filename completion. */
+int _rl_match_hidden_files = 1;
+
+/* Length in characters of a common prefix replaced with an ellipsis (`...')
+ when displaying completion matches. Matches whose printable portion has
+ more than this number of displaying characters in common will have the common
+ display prefix replaced with an ellipsis. */
+int _rl_completion_prefix_display_length = 0;
+
+/* The readline-private number of screen columns to use when displaying
+ matches. If < 0 or > _rl_screenwidth, it is ignored. */
+int _rl_completion_columns = -1;
+
+#if defined (COLOR_SUPPORT)
+/* Non-zero means to use colors to indicate file type when listing possible
+ completions. The colors used are taken from $LS_COLORS, if set. */
+int _rl_colored_stats = 0;
+
+/* Non-zero means to use a color (currently magenta) to indicate the common
+ prefix of a set of possible word completions. */
+int _rl_colored_completion_prefix = 0;
+#endif
+
+/* If non-zero, when completing in the middle of a word, don't insert
+ characters from the match that match characters following point in
+ the word. This means, for instance, completing when the cursor is
+ after the `e' in `Makefile' won't result in `Makefilefile'. */
+int _rl_skip_completed_text = 0;
+
+/* If non-zero, menu completion displays the common prefix first in the
+ cycle of possible completions instead of the last. */
+int _rl_menu_complete_prefix_first = 0;
+
+/* Global variables available to applications using readline. */
+
+#if defined (VISIBLE_STATS)
+/* Non-zero means add an additional character to each filename displayed
+ during listing completion iff rl_filename_completion_desired which helps
+ to indicate the type of file being listed. */
+int rl_visible_stats = 0;
+#endif /* VISIBLE_STATS */
+
+/* If non-zero, then this is the address of a function to call when
+ completing on a directory name. The function is called with
+ the address of a string (the current directory name) as an arg. */
+rl_icppfunc_t *rl_directory_completion_hook = (rl_icppfunc_t *)NULL;
+
+rl_icppfunc_t *rl_directory_rewrite_hook = (rl_icppfunc_t *)NULL;
+
+rl_icppfunc_t *rl_filename_stat_hook = (rl_icppfunc_t *)NULL;
+
+/* If non-zero, this is the address of a function to call when reading
+ directory entries from the filesystem for completion and comparing
+ them to the partial word to be completed. The function should
+ either return its first argument (if no conversion takes place) or
+ newly-allocated memory. This can, for instance, convert filenames
+ between character sets for comparison against what's typed at the
+ keyboard. The returned value is what is added to the list of
+ matches. The second argument is the length of the filename to be
+ converted. */
+rl_dequote_func_t *rl_filename_rewrite_hook = (rl_dequote_func_t *)NULL;
+
+/* Non-zero means readline completion functions perform tilde expansion. */
+int rl_complete_with_tilde_expansion = 0;
+
+/* Pointer to the generator function for completion_matches ().
+ NULL means to use rl_filename_completion_function (), the default filename
+ completer. */
+rl_compentry_func_t *rl_completion_entry_function = (rl_compentry_func_t *)NULL;
+
+/* Pointer to generator function for rl_menu_complete (). NULL means to use
+ *rl_completion_entry_function (see above). */
+rl_compentry_func_t *rl_menu_completion_entry_function = (rl_compentry_func_t *)NULL;
+
+/* Pointer to alternative function to create matches.
+ Function is called with TEXT, START, and END.
+ START and END are indices in RL_LINE_BUFFER saying what the boundaries
+ of TEXT are.
+ If this function exists and returns NULL then call the value of
+ rl_completion_entry_function to try to match, otherwise use the
+ array of strings returned. */
+rl_completion_func_t *rl_attempted_completion_function = (rl_completion_func_t *)NULL;
+
+/* Non-zero means to suppress normal filename completion after the
+ user-specified completion function has been called. */
+int rl_attempted_completion_over = 0;
+
+/* Set to a character indicating the type of completion being performed
+ by rl_complete_internal, available for use by application completion
+ functions. */
+int rl_completion_type = 0;
+
+/* Up to this many items will be displayed in response to a
+ possible-completions call. After that, we ask the user if
+ she is sure she wants to see them all. A negative value means
+ don't ask. */
+int rl_completion_query_items = 100;
+
+int _rl_page_completions = 1;
+
+/* The basic list of characters that signal a break between words for the
+ completer routine. The contents of this variable is what breaks words
+ in the shell, i.e. " \t\n\"\\'`@$><=" */
+const char *rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{("; /* }) */
+
+/* List of basic quoting characters. */
+const char *rl_basic_quote_characters = "\"'";
+
+/* The list of characters that signal a break between words for
+ rl_complete_internal. The default list is the contents of
+ rl_basic_word_break_characters. */
+const char *rl_completer_word_break_characters = 0;
+
+/* Hook function to allow an application to set the completion word
+ break characters before readline breaks up the line. Allows
+ position-dependent word break characters. */
+rl_cpvfunc_t *rl_completion_word_break_hook = (rl_cpvfunc_t *)NULL;
+
+/* List of characters which can be used to quote a substring of the line.
+ Completion occurs on the entire substring, and within the substring
+ rl_completer_word_break_characters are treated as any other character,
+ unless they also appear within this list. */
+const char *rl_completer_quote_characters = (const char *)NULL;
+
+/* List of characters that should be quoted in filenames by the completer. */
+const char *rl_filename_quote_characters = (const char *)NULL;
+
+/* List of characters that are word break characters, but should be left
+ in TEXT when it is passed to the completion function. The shell uses
+ this to help determine what kind of completing to do. */
+const char *rl_special_prefixes = (const char *)NULL;
+
+/* If non-zero, then disallow duplicates in the matches. */
+int rl_ignore_completion_duplicates = 1;
+
+/* Non-zero means that the results of the matches are to be treated
+ as filenames. This is ALWAYS zero on entry, and can only be changed
+ within a completion entry finder function. */
+int rl_filename_completion_desired = 0;
+
+/* Non-zero means that the results of the matches are to be quoted using
+ double quotes (or an application-specific quoting mechanism) if the
+ filename contains any characters in rl_filename_quote_chars. This is
+ ALWAYS non-zero on entry, and can only be changed within a completion
+ entry finder function. */
+int rl_filename_quoting_desired = 1;
+
+/* This function, if defined, is called by the completer when real
+ filename completion is done, after all the matching names have been
+ generated. It is passed a (char**) known as matches in the code below.
+ It consists of a NULL-terminated array of pointers to potential
+ matching strings. The 1st element (matches[0]) is the maximal
+ substring that is common to all matches. This function can re-arrange
+ the list of matches as required, but all elements of the array must be
+ free()'d if they are deleted. The main intent of this function is
+ to implement FIGNORE a la SunOS csh. */
+rl_compignore_func_t *rl_ignore_some_completions_function = (rl_compignore_func_t *)NULL;
+
+/* Set to a function to quote a filename in an application-specific fashion.
+ Called with the text to quote, the type of match found (single or multiple)
+ and a pointer to the quoting character to be used, which the function can
+ reset if desired. */
+rl_quote_func_t *rl_filename_quoting_function = rl_quote_filename;
+
+/* Function to call to remove quoting characters from a filename. Called
+ before completion is attempted, so the embedded quotes do not interfere
+ with matching names in the file system. Readline doesn't do anything
+ with this; it's set only by applications. */
+rl_dequote_func_t *rl_filename_dequoting_function = (rl_dequote_func_t *)NULL;
+
+/* Function to call to decide whether or not a word break character is
+ quoted. If a character is quoted, it does not break words for the
+ completer. */
+rl_linebuf_func_t *rl_char_is_quoted_p = (rl_linebuf_func_t *)NULL;
+
+/* If non-zero, the completion functions don't append anything except a
+ possible closing quote. This is set to 0 by rl_complete_internal and
+ may be changed by an application-specific completion function. */
+int rl_completion_suppress_append = 0;
+
+/* Character appended to completed words when at the end of the line. The
+ default is a space. */
+int rl_completion_append_character = ' ';
+
+/* If non-zero, the completion functions don't append any closing quote.
+ This is set to 0 by rl_complete_internal and may be changed by an
+ application-specific completion function. */
+int rl_completion_suppress_quote = 0;
+
+/* Set to any quote character readline thinks it finds before any application
+ completion function is called. */
+int rl_completion_quote_character;
+
+/* Set to a non-zero value if readline found quoting anywhere in the word to
+ be completed; set before any application completion function is called. */
+int rl_completion_found_quote;
+
+/* If non-zero, a slash will be appended to completed filenames that are
+ symbolic links to directory names, subject to the value of the
+ mark-directories variable (which is user-settable). This exists so
+ that application completion functions can override the user's preference
+ (set via the mark-symlinked-directories variable) if appropriate.
+ It's set to the value of _rl_complete_mark_symlink_dirs in
+ rl_complete_internal before any application-specific completion
+ function is called, so without that function doing anything, the user's
+ preferences are honored. */
+int rl_completion_mark_symlink_dirs;
+
+/* If non-zero, inhibit completion (temporarily). */
+int rl_inhibit_completion;
+
+/* Set to the last key used to invoke one of the completion functions */
+int rl_completion_invoking_key;
+
+/* If non-zero, sort the completion matches. On by default. */
+int rl_sort_completion_matches = 1;
+
+/* Variables local to this file. */
+
+/* Local variable states what happened during the last completion attempt. */
+static int completion_changed_buffer;
+static int last_completion_failed = 0;
+
+/* The result of the query to the user about displaying completion matches */
+static int completion_y_or_n;
+
+static int _rl_complete_display_matches_interrupt = 0;
+
+/*************************************/
+/* */
+/* Bindable completion functions */
+/* */
+/*************************************/
+
+/* Complete the word at or before point. You have supplied the function
+ that does the initial simple matching selection algorithm (see
+ rl_completion_matches ()). The default is to do filename completion. */
+int
+rl_complete (int ignore, int invoking_key)
+{
+ rl_completion_invoking_key = invoking_key;
+
+ if (rl_inhibit_completion)
+ return (_rl_insert_char (ignore, invoking_key));
+#if 0
+ else if (rl_last_func == rl_complete && completion_changed_buffer == 0 && last_completion_failed == 0)
+#else
+ else if (rl_last_func == rl_complete && completion_changed_buffer == 0)
+#endif
+ return (rl_complete_internal ('?'));
+ else if (_rl_complete_show_all)
+ return (rl_complete_internal ('!'));
+ else if (_rl_complete_show_unmodified)
+ return (rl_complete_internal ('@'));
+ else
+ return (rl_complete_internal (TAB));
+}
+
+/* List the possible completions. See description of rl_complete (). */
+int
+rl_possible_completions (int ignore, int invoking_key)
+{
+ rl_completion_invoking_key = invoking_key;
+ return (rl_complete_internal ('?'));
+}
+
+int
+rl_insert_completions (int ignore, int invoking_key)
+{
+ rl_completion_invoking_key = invoking_key;
+ return (rl_complete_internal ('*'));
+}
+
+/* Return the correct value to pass to rl_complete_internal performing
+ the same tests as rl_complete. This allows consecutive calls to an
+ application's completion function to list possible completions and for
+ an application-specific completion function to honor the
+ show-all-if-ambiguous readline variable. */
+int
+rl_completion_mode (rl_command_func_t *cfunc)
+{
+ if (rl_last_func == cfunc && !completion_changed_buffer)
+ return '?';
+ else if (_rl_complete_show_all)
+ return '!';
+ else if (_rl_complete_show_unmodified)
+ return '@';
+ else
+ return TAB;
+}
+
+/************************************/
+/* */
+/* Completion utility functions */
+/* */
+/************************************/
+
+/* Reset public readline state on a signal or other event. */
+void
+_rl_reset_completion_state (void)
+{
+ rl_completion_found_quote = 0;
+ rl_completion_quote_character = 0;
+}
+
+static void
+_rl_complete_sigcleanup (int sig, void *ptr)
+{
+ if (sig == SIGINT) /* XXX - for now */
+ {
+ _rl_free_match_list ((char **)ptr);
+ _rl_complete_display_matches_interrupt = 1;
+ }
+}
+
+/* Set default values for readline word completion. These are the variables
+ that application completion functions can change or inspect. */
+static void
+set_completion_defaults (int what_to_do)
+{
+ /* Only the completion entry function can change these. */
+ rl_filename_completion_desired = 0;
+ rl_filename_quoting_desired = 1;
+ rl_completion_type = what_to_do;
+ rl_completion_suppress_append = rl_completion_suppress_quote = 0;
+ rl_completion_append_character = ' ';
+
+ /* The completion entry function may optionally change this. */
+ rl_completion_mark_symlink_dirs = _rl_complete_mark_symlink_dirs;
+
+ /* Reset private state. */
+ _rl_complete_display_matches_interrupt = 0;
+}
+
+/* The user must press "y" or "n". Non-zero return means "y" pressed. */
+static int
+get_y_or_n (int for_pager)
+{
+ int c;
+
+ /* For now, disable pager in callback mode, until we later convert to state
+ driven functions. Have to wait until next major version to add new
+ state definition, since it will change value of RL_STATE_DONE. */
+#if defined (READLINE_CALLBACKS)
+ if (RL_ISSTATE (RL_STATE_CALLBACK))
+ return 1;
+#endif
+
+ for (;;)
+ {
+ RL_SETSTATE(RL_STATE_MOREINPUT);
+ c = rl_read_key ();
+ RL_UNSETSTATE(RL_STATE_MOREINPUT);
+
+ if (c == 'y' || c == 'Y' || c == ' ')
+ return (1);
+ if (c == 'n' || c == 'N' || c == RUBOUT)
+ return (0);
+ if (c == ABORT_CHAR || c < 0)
+ _rl_abort_internal ();
+ if (for_pager && (c == NEWLINE || c == RETURN))
+ return (2);
+ if (for_pager && (c == 'q' || c == 'Q'))
+ return (0);
+ rl_ding ();
+ }
+}
+
+static int
+_rl_internal_pager (int lines)
+{
+ int i;
+
+ fprintf (rl_outstream, "--More--");
+ fflush (rl_outstream);
+ i = get_y_or_n (1);
+ _rl_erase_entire_line ();
+ if (i == 0)
+ return -1;
+ else if (i == 2)
+ return (lines - 1);
+ else
+ return 0;
+}
+
+static int
+path_isdir (const char *filename)
+{
+ struct stat finfo;
+
+ return (stat (filename, &finfo) == 0 && S_ISDIR (finfo.st_mode));
+}
+
+#if defined (VISIBLE_STATS)
+/* Return the character which best describes FILENAME.
+ `@' for symbolic links
+ `/' for directories
+ `*' for executables
+ `=' for sockets
+ `|' for FIFOs
+ `%' for character special devices
+ `#' for block special devices */
+static int
+stat_char (char *filename)
+{
+ struct stat finfo;
+ int character, r;
+ char *f;
+ const char *fn;
+
+ /* Short-circuit a //server on cygwin, since that will always behave as
+ a directory. */
+#if __CYGWIN__
+ if (filename[0] == '/' && filename[1] == '/' && strchr (filename+2, '/') == 0)
+ return '/';
+#endif
+
+ f = 0;
+ if (rl_filename_stat_hook)
+ {
+ f = savestring (filename);
+ (*rl_filename_stat_hook) (&f);
+ fn = f;
+ }
+ else
+ fn = filename;
+
+#if defined (HAVE_LSTAT) && defined (S_ISLNK)
+ r = lstat (fn, &finfo);
+#else
+ r = stat (fn, &finfo);
+#endif
+
+ if (r == -1)
+ {
+ xfree (f);
+ return (0);
+ }
+
+ character = 0;
+ if (S_ISDIR (finfo.st_mode))
+ character = '/';
+#if defined (S_ISCHR)
+ else if (S_ISCHR (finfo.st_mode))
+ character = '%';
+#endif /* S_ISCHR */
+#if defined (S_ISBLK)
+ else if (S_ISBLK (finfo.st_mode))
+ character = '#';
+#endif /* S_ISBLK */
+#if defined (S_ISLNK)
+ else if (S_ISLNK (finfo.st_mode))
+ character = '@';
+#endif /* S_ISLNK */
+#if defined (S_ISSOCK)
+ else if (S_ISSOCK (finfo.st_mode))
+ character = '=';
+#endif /* S_ISSOCK */
+#if defined (S_ISFIFO)
+ else if (S_ISFIFO (finfo.st_mode))
+ character = '|';
+#endif
+ else if (S_ISREG (finfo.st_mode))
+ {
+#if defined (_WIN32) && !defined (__CYGWIN__)
+ char *ext;
+
+ /* Windows doesn't do access and X_OK; check file extension instead */
+ ext = strrchr (fn, '.');
+ if (ext && (_rl_stricmp (ext, ".exe") == 0 ||
+ _rl_stricmp (ext, ".cmd") == 0 ||
+ _rl_stricmp (ext, ".bat") == 0 ||
+ _rl_stricmp (ext, ".com") == 0))
+ character = '*';
+#else
+ if (access (filename, X_OK) == 0)
+ character = '*';
+#endif
+ }
+
+ xfree (f);
+ return (character);
+}
+#endif /* VISIBLE_STATS */
+
+#if defined (COLOR_SUPPORT)
+static int
+colored_stat_start (const char *filename)
+{
+ _rl_set_normal_color ();
+ return (_rl_print_color_indicator (filename));
+}
+
+static void
+colored_stat_end (void)
+{
+ _rl_prep_non_filename_text ();
+ _rl_put_indicator (&_rl_color_indicator[C_CLR_TO_EOL]);
+}
+
+static int
+colored_prefix_start (void)
+{
+ _rl_set_normal_color ();
+ return (_rl_print_prefix_color ());
+}
+
+static void
+colored_prefix_end (void)
+{
+ colored_stat_end (); /* for now */
+}
+#endif
+
+/* Return the portion of PATHNAME that should be output when listing
+ possible completions. If we are hacking filename completion, we
+ are only interested in the basename, the portion following the
+ final slash. Otherwise, we return what we were passed. Since
+ printing empty strings is not very informative, if we're doing
+ filename completion, and the basename is the empty string, we look
+ for the previous slash and return the portion following that. If
+ there's no previous slash, we just return what we were passed. */
+static char *
+printable_part (char *pathname)
+{
+ char *temp, *x;
+
+ if (rl_filename_completion_desired == 0) /* don't need to do anything */
+ return (pathname);
+
+ temp = strrchr (pathname, '/');
+#if defined (__MSDOS__) || defined (_WIN32)
+ if (temp == 0 && ISALPHA ((unsigned char)pathname[0]) && pathname[1] == ':')
+ temp = pathname + 1;
+#endif
+
+ if (temp == 0 || *temp == '\0')
+ return (pathname);
+ else if (temp[1] == 0 && temp == pathname)
+ return (pathname);
+ /* If the basename is NULL, we might have a pathname like '/usr/src/'.
+ Look for a previous slash and, if one is found, return the portion
+ following that slash. If there's no previous slash, just return the
+ pathname we were passed. */
+ else if (temp[1] == '\0')
+ {
+ for (x = temp - 1; x > pathname; x--)
+ if (*x == '/')
+ break;
+ return ((*x == '/') ? x + 1 : pathname);
+ }
+ else
+ return ++temp;
+}
+
+/* Compute width of STRING when displayed on screen by print_filename */
+static int
+fnwidth (const char *string)
+{
+ int width, pos;
+#if defined (HANDLE_MULTIBYTE)
+ mbstate_t ps;
+ int left, w;
+ size_t clen;
+ WCHAR_T wc;
+
+ left = strlen (string) + 1;
+ memset (&ps, 0, sizeof (mbstate_t));
+#endif
+
+ width = pos = 0;
+ while (string[pos])
+ {
+ if (CTRL_CHAR (string[pos]) || string[pos] == RUBOUT)
+ {
+ width += 2;
+ pos++;
+ }
+ else
+ {
+#if defined (HANDLE_MULTIBYTE)
+ clen = MBRTOWC (&wc, string + pos, left - pos, &ps);
+ if (MB_INVALIDCH (clen))
+ {
+ width++;
+ pos++;
+ memset (&ps, 0, sizeof (mbstate_t));
+ }
+ else if (MB_NULLWCH (clen))
+ break;
+ else
+ {
+ pos += clen;
+ w = WCWIDTH (wc);
+ width += (w >= 0) ? w : 1;
+ }
+#else
+ width++;
+ pos++;
+#endif
+ }
+ }
+
+ return width;
+}
+
+#define ELLIPSIS_LEN 3
+
+static int
+fnprint (const char *to_print, int prefix_bytes, const char *real_pathname)
+{
+ int printed_len, w;
+ const char *s;
+ int common_prefix_len, print_len;
+#if defined (HANDLE_MULTIBYTE)
+ mbstate_t ps;
+ const char *end;
+ size_t tlen;
+ int width;
+ WCHAR_T wc;
+
+ print_len = strlen (to_print);
+ end = to_print + print_len + 1;
+ memset (&ps, 0, sizeof (mbstate_t));
+#else
+ print_len = strlen (to_print);
+#endif
+
+ printed_len = common_prefix_len = 0;
+
+ /* Don't print only the ellipsis if the common prefix is one of the
+ possible completions. Only cut off prefix_bytes if we're going to be
+ printing the ellipsis, which takes precedence over coloring the
+ completion prefix (see print_filename() below). */
+ if (_rl_completion_prefix_display_length > 0 && prefix_bytes >= print_len)
+ prefix_bytes = 0;
+
+#if defined (COLOR_SUPPORT)
+ if (_rl_colored_stats && (prefix_bytes == 0 || _rl_colored_completion_prefix <= 0))
+ colored_stat_start (real_pathname);
+#endif
+
+ if (prefix_bytes && _rl_completion_prefix_display_length > 0 &&
+ prefix_bytes > _rl_completion_prefix_display_length)
+ {
+ char ellipsis;
+
+ ellipsis = (to_print[prefix_bytes] == '.') ? '_' : '.';
+ for (w = 0; w < ELLIPSIS_LEN; w++)
+ putc (ellipsis, rl_outstream);
+ printed_len = ELLIPSIS_LEN;
+ }
+#if defined (COLOR_SUPPORT)
+ else if (prefix_bytes && _rl_colored_completion_prefix > 0)
+ {
+ common_prefix_len = prefix_bytes;
+ prefix_bytes = 0;
+ /* XXX - print color indicator start here */
+ colored_prefix_start ();
+ }
+#endif
+
+ s = to_print + prefix_bytes;
+ while (*s)
+ {
+ if (CTRL_CHAR (*s))
+ {
+ putc ('^', rl_outstream);
+ putc (UNCTRL (*s), rl_outstream);
+ printed_len += 2;
+ s++;
+#if defined (HANDLE_MULTIBYTE)
+ memset (&ps, 0, sizeof (mbstate_t));
+#endif
+ }
+ else if (*s == RUBOUT)
+ {
+ putc ('^', rl_outstream);
+ putc ('?', rl_outstream);
+ printed_len += 2;
+ s++;
+#if defined (HANDLE_MULTIBYTE)
+ memset (&ps, 0, sizeof (mbstate_t));
+#endif
+ }
+ else
+ {
+#if defined (HANDLE_MULTIBYTE)
+ tlen = MBRTOWC (&wc, s, end - s, &ps);
+ if (MB_INVALIDCH (tlen))
+ {
+ tlen = 1;
+ width = 1;
+ memset (&ps, 0, sizeof (mbstate_t));
+ }
+ else if (MB_NULLWCH (tlen))
+ break;
+ else
+ {
+ w = WCWIDTH (wc);
+ width = (w >= 0) ? w : 1;
+ }
+ fwrite (s, 1, tlen, rl_outstream);
+ s += tlen;
+ printed_len += width;
+#else
+ putc (*s, rl_outstream);
+ s++;
+ printed_len++;
+#endif
+ }
+ if (common_prefix_len > 0 && (s - to_print) >= common_prefix_len)
+ {
+#if defined (COLOR_SUPPORT)
+ /* printed bytes = s - to_print */
+ /* printed bytes should never be > but check for paranoia's sake */
+ colored_prefix_end ();
+ if (_rl_colored_stats)
+ colored_stat_start (real_pathname); /* XXX - experiment */
+#endif
+ common_prefix_len = 0;
+ }
+ }
+
+#if defined (COLOR_SUPPORT)
+ /* XXX - unconditional for now */
+ if (_rl_colored_stats)
+ colored_stat_end ();
+#endif
+
+ return printed_len;
+}
+
+/* Output TO_PRINT to rl_outstream. If VISIBLE_STATS is defined and we
+ are using it, check for and output a single character for `special'
+ filenames. Return the number of characters we output. */
+
+static int
+print_filename (char *to_print, char *full_pathname, int prefix_bytes)
+{
+ int printed_len, extension_char, slen, tlen;
+ char *s, c, *new_full_pathname, *dn;
+
+ extension_char = 0;
+#if defined (COLOR_SUPPORT)
+ /* Defer printing if we want to prefix with a color indicator */
+ if (_rl_colored_stats == 0 || rl_filename_completion_desired == 0)
+#endif
+ printed_len = fnprint (to_print, prefix_bytes, to_print);
+
+ if (rl_filename_completion_desired && (
+#if defined (VISIBLE_STATS)
+ rl_visible_stats ||
+#endif
+#if defined (COLOR_SUPPORT)
+ _rl_colored_stats ||
+#endif
+ _rl_complete_mark_directories))
+ {
+ /* If to_print != full_pathname, to_print is the basename of the
+ path passed. In this case, we try to expand the directory
+ name before checking for the stat character. */
+ if (to_print != full_pathname)
+ {
+ /* Terminate the directory name. */
+ c = to_print[-1];
+ to_print[-1] = '\0';
+
+ /* If setting the last slash in full_pathname to a NUL results in
+ full_pathname being the empty string, we are trying to complete
+ files in the root directory. If we pass a null string to the
+ bash directory completion hook, for example, it will expand it
+ to the current directory. We just want the `/'. */
+ if (full_pathname == 0 || *full_pathname == 0)
+ dn = "/";
+ else if (full_pathname[0] != '/')
+ dn = full_pathname;
+ else if (full_pathname[1] == 0)
+ dn = "//"; /* restore trailing slash to `//' */
+ else if (full_pathname[1] == '/' && full_pathname[2] == 0)
+ dn = "/"; /* don't turn /// into // */
+ else
+ dn = full_pathname;
+ s = tilde_expand (dn);
+ if (rl_directory_completion_hook)
+ (*rl_directory_completion_hook) (&s);
+
+ slen = strlen (s);
+ tlen = strlen (to_print);
+ new_full_pathname = (char *)xmalloc (slen + tlen + 2);
+ strcpy (new_full_pathname, s);
+ if (s[slen - 1] == '/')
+ slen--;
+ else
+ new_full_pathname[slen] = '/';
+ strcpy (new_full_pathname + slen + 1, to_print);
+
+#if defined (VISIBLE_STATS)
+ if (rl_visible_stats)
+ extension_char = stat_char (new_full_pathname);
+ else
+#endif
+ if (_rl_complete_mark_directories)
+ {
+ dn = 0;
+ if (rl_directory_completion_hook == 0 && rl_filename_stat_hook)
+ {
+ dn = savestring (new_full_pathname);
+ (*rl_filename_stat_hook) (&dn);
+ xfree (new_full_pathname);
+ new_full_pathname = dn;
+ }
+ if (path_isdir (new_full_pathname))
+ extension_char = '/';
+ }
+
+ /* Move colored-stats code inside fnprint() */
+#if defined (COLOR_SUPPORT)
+ if (_rl_colored_stats)
+ printed_len = fnprint (to_print, prefix_bytes, new_full_pathname);
+#endif
+
+ xfree (new_full_pathname);
+ to_print[-1] = c;
+ }
+ else
+ {
+ s = tilde_expand (full_pathname);
+#if defined (VISIBLE_STATS)
+ if (rl_visible_stats)
+ extension_char = stat_char (s);
+ else
+#endif
+ if (_rl_complete_mark_directories && path_isdir (s))
+ extension_char = '/';
+
+ /* Move colored-stats code inside fnprint() */
+#if defined (COLOR_SUPPORT)
+ if (_rl_colored_stats)
+ printed_len = fnprint (to_print, prefix_bytes, s);
+#endif
+ }
+
+ xfree (s);
+ if (extension_char)
+ {
+ putc (extension_char, rl_outstream);
+ printed_len++;
+ }
+ }
+
+ return printed_len;
+}
+
+static char *
+rl_quote_filename (char *s, int rtype, char *qcp)
+{
+ char *r;
+
+ r = (char *)xmalloc (strlen (s) + 2);
+ *r = *rl_completer_quote_characters;
+ strcpy (r + 1, s);
+ if (qcp)
+ *qcp = *rl_completer_quote_characters;
+ return r;
+}
+
+/* Find the bounds of the current word for completion purposes, and leave
+ rl_point set to the end of the word. This function skips quoted
+ substrings (characters between matched pairs of characters in
+ rl_completer_quote_characters). First we try to find an unclosed
+ quoted substring on which to do matching. If one is not found, we use
+ the word break characters to find the boundaries of the current word.
+ We call an application-specific function to decide whether or not a
+ particular word break character is quoted; if that function returns a
+ non-zero result, the character does not break a word. This function
+ returns the opening quote character if we found an unclosed quoted
+ substring, '\0' otherwise. FP, if non-null, is set to a value saying
+ which (shell-like) quote characters we found (single quote, double
+ quote, or backslash) anywhere in the string. DP, if non-null, is set to
+ the value of the delimiter character that caused a word break. */
+
+char
+_rl_find_completion_word (int *fp, int *dp)
+{
+ int scan, end, found_quote, delimiter, pass_next, isbrk;
+ char quote_char;
+ const char *brkchars;
+
+ end = rl_point;
+ found_quote = delimiter = 0;
+ quote_char = '\0';
+
+ brkchars = 0;
+ if (rl_completion_word_break_hook)
+ brkchars = (*rl_completion_word_break_hook) ();
+ if (brkchars == 0)
+ brkchars = rl_completer_word_break_characters;
+
+ if (rl_completer_quote_characters)
+ {
+ /* We have a list of characters which can be used in pairs to
+ quote substrings for the completer. Try to find the start
+ of an unclosed quoted substring. */
+ /* FOUND_QUOTE is set so we know what kind of quotes we found. */
+ for (scan = pass_next = 0; scan < end; scan = MB_NEXTCHAR (rl_line_buffer, scan, 1, MB_FIND_ANY))
+ {
+ if (pass_next)
+ {
+ pass_next = 0;
+ continue;
+ }
+
+ /* Shell-like semantics for single quotes -- don't allow backslash
+ to quote anything in single quotes, especially not the closing
+ quote. If you don't like this, take out the check on the value
+ of quote_char. */
+ if (quote_char != '\'' && rl_line_buffer[scan] == '\\')
+ {
+ pass_next = 1;
+ found_quote |= RL_QF_BACKSLASH;
+ continue;
+ }
+
+ if (quote_char != '\0')
+ {
+ /* Ignore everything until the matching close quote char. */
+ if (rl_line_buffer[scan] == quote_char)
+ {
+ /* Found matching close. Abandon this substring. */
+ quote_char = '\0';
+ rl_point = end;
+ }
+ }
+ else if (strchr (rl_completer_quote_characters, rl_line_buffer[scan]))
+ {
+ /* Found start of a quoted substring. */
+ quote_char = rl_line_buffer[scan];
+ rl_point = scan + 1;
+ /* Shell-like quoting conventions. */
+ if (quote_char == '\'')
+ found_quote |= RL_QF_SINGLE_QUOTE;
+ else if (quote_char == '"')
+ found_quote |= RL_QF_DOUBLE_QUOTE;
+ else
+ found_quote |= RL_QF_OTHER_QUOTE;
+ }
+ }
+ }
+
+ if (rl_point == end && quote_char == '\0')
+ {
+ /* We didn't find an unclosed quoted substring upon which to do
+ completion, so use the word break characters to find the
+ substring on which to complete. */
+ while (rl_point = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_ANY))
+ {
+ scan = rl_line_buffer[rl_point];
+
+ if (strchr (brkchars, scan) == 0)
+ continue;
+
+ /* Call the application-specific function to tell us whether
+ this word break character is quoted and should be skipped. */
+ if (rl_char_is_quoted_p && found_quote &&
+ (*rl_char_is_quoted_p) (rl_line_buffer, rl_point))
+ continue;
+
+ /* Convoluted code, but it avoids an n^2 algorithm with calls
+ to char_is_quoted. */
+ break;
+ }
+ }
+
+ /* If we are at an unquoted word break, then advance past it. */
+ scan = rl_line_buffer[rl_point];
+
+ /* If there is an application-specific function to say whether or not
+ a character is quoted and we found a quote character, let that
+ function decide whether or not a character is a word break, even
+ if it is found in rl_completer_word_break_characters. Don't bother
+ if we're at the end of the line, though. */
+ if (scan)
+ {
+ if (rl_char_is_quoted_p)
+ isbrk = (found_quote == 0 ||
+ (*rl_char_is_quoted_p) (rl_line_buffer, rl_point) == 0) &&
+ strchr (brkchars, scan) != 0;
+ else
+ isbrk = strchr (brkchars, scan) != 0;
+
+ if (isbrk)
+ {
+ /* If the character that caused the word break was a quoting
+ character, then remember it as the delimiter. */
+ if (rl_basic_quote_characters &&
+ strchr (rl_basic_quote_characters, scan) &&
+ (end - rl_point) > 1)
+ delimiter = scan;
+
+ /* If the character isn't needed to determine something special
+ about what kind of completion to perform, then advance past it. */
+ if (rl_special_prefixes == 0 || strchr (rl_special_prefixes, scan) == 0)
+ rl_point++;
+ }
+ }
+
+ if (fp)
+ *fp = found_quote;
+ if (dp)
+ *dp = delimiter;
+
+ return (quote_char);
+}
+
+static char **
+gen_completion_matches (char *text, int start, int end, rl_compentry_func_t *our_func, int found_quote, int quote_char)
+{
+ char **matches;
+
+ rl_completion_found_quote = found_quote;
+ rl_completion_quote_character = quote_char;
+
+ /* If the user wants to TRY to complete, but then wants to give
+ up and use the default completion function, they set the
+ variable rl_attempted_completion_function. */
+ if (rl_attempted_completion_function)
+ {
+ matches = (*rl_attempted_completion_function) (text, start, end);
+ if (RL_SIG_RECEIVED())
+ {
+ _rl_free_match_list (matches);
+ matches = 0;
+ RL_CHECK_SIGNALS ();
+ }
+
+ if (matches || rl_attempted_completion_over)
+ {
+ rl_attempted_completion_over = 0;
+ return (matches);
+ }
+ }
+
+ /* XXX -- filename dequoting moved into rl_filename_completion_function */
+
+ /* rl_completion_matches will check for signals as well to avoid a long
+ delay while reading a directory. */
+ matches = rl_completion_matches (text, our_func);
+ if (RL_SIG_RECEIVED())
+ {
+ _rl_free_match_list (matches);
+ matches = 0;
+ RL_CHECK_SIGNALS ();
+ }
+ return matches;
+}
+
+/* Filter out duplicates in MATCHES. This frees up the strings in
+ MATCHES. */
+static char **
+remove_duplicate_matches (char **matches)
+{
+ char *lowest_common;
+ int i, j, newlen;
+ char dead_slot;
+ char **temp_array;
+
+ /* Sort the items. */
+ for (i = 0; matches[i]; i++)
+ ;
+
+ /* Sort the array without matches[0], since we need it to
+ stay in place no matter what. */
+ if (i && rl_sort_completion_matches)
+ qsort (matches+1, i-1, sizeof (char *), (QSFUNC *)_rl_qsort_string_compare);
+
+ /* Remember the lowest common denominator for it may be unique. */
+ lowest_common = savestring (matches[0]);
+
+ for (i = newlen = 0; matches[i + 1]; i++)
+ {
+ if (strcmp (matches[i], matches[i + 1]) == 0)
+ {
+ xfree (matches[i]);
+ matches[i] = (char *)&dead_slot;
+ }
+ else
+ newlen++;
+ }
+
+ /* We have marked all the dead slots with (char *)&dead_slot.
+ Copy all the non-dead entries into a new array. */
+ temp_array = (char **)xmalloc ((3 + newlen) * sizeof (char *));
+ for (i = j = 1; matches[i]; i++)
+ {
+ if (matches[i] != (char *)&dead_slot)
+ temp_array[j++] = matches[i];
+ }
+ temp_array[j] = (char *)NULL;
+
+ if (matches[0] != (char *)&dead_slot)
+ xfree (matches[0]);
+
+ /* Place the lowest common denominator back in [0]. */
+ temp_array[0] = lowest_common;
+
+ /* If there is one string left, and it is identical to the
+ lowest common denominator, then the LCD is the string to
+ insert. */
+ if (j == 2 && strcmp (temp_array[0], temp_array[1]) == 0)
+ {
+ xfree (temp_array[1]);
+ temp_array[1] = (char *)NULL;
+ }
+ return (temp_array);
+}
+
+/* Find the common prefix of the list of matches, and put it into
+ matches[0]. */
+static int
+compute_lcd_of_matches (char **match_list, int matches, const char *text)
+{
+ register int i, c1, c2, si;
+ int low; /* Count of max-matched characters. */
+ int lx;
+ char *dtext; /* dequoted TEXT, if needed */
+#if defined (HANDLE_MULTIBYTE)
+ int v;
+ size_t v1, v2;
+ mbstate_t ps1, ps2;
+ WCHAR_T wc1, wc2;
+#endif
+
+ /* If only one match, just use that. Otherwise, compare each
+ member of the list with the next, finding out where they
+ stop matching. */
+ if (matches == 1)
+ {
+ match_list[0] = match_list[1];
+ match_list[1] = (char *)NULL;
+ return 1;
+ }
+
+ for (i = 1, low = 100000; i < matches; i++)
+ {
+#if defined (HANDLE_MULTIBYTE)
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ {
+ memset (&ps1, 0, sizeof (mbstate_t));
+ memset (&ps2, 0, sizeof (mbstate_t));
+ }
+#endif
+ for (si = 0; (c1 = match_list[i][si]) && (c2 = match_list[i + 1][si]); si++)
+ {
+ if (_rl_completion_case_fold)
+ {
+ c1 = _rl_to_lower (c1);
+ c2 = _rl_to_lower (c2);
+ }
+#if defined (HANDLE_MULTIBYTE)
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ {
+ v1 = MBRTOWC (&wc1, match_list[i]+si, strlen (match_list[i]+si), &ps1);
+ v2 = MBRTOWC (&wc2, match_list[i+1]+si, strlen (match_list[i+1]+si), &ps2);
+ if (MB_INVALIDCH (v1) || MB_INVALIDCH (v2))
+ {
+ if (c1 != c2) /* do byte comparison */
+ break;
+ continue;
+ }
+ if (_rl_completion_case_fold)
+ {
+ wc1 = towlower (wc1);
+ wc2 = towlower (wc2);
+ }
+ if (wc1 != wc2)
+ break;
+ else if (v1 > 1)
+ si += v1 - 1;
+ }
+ else
+#endif
+ if (c1 != c2)
+ break;
+ }
+
+ if (low > si)
+ low = si;
+ }
+
+ /* If there were multiple matches, but none matched up to even the
+ first character, and the user typed something, use that as the
+ value of matches[0]. */
+ if (low == 0 && text && *text)
+ {
+ match_list[0] = (char *)xmalloc (strlen (text) + 1);
+ strcpy (match_list[0], text);
+ }
+ else
+ {
+ match_list[0] = (char *)xmalloc (low + 1);
+
+ /* XXX - this might need changes in the presence of multibyte chars */
+
+ /* If we are ignoring case, try to preserve the case of the string
+ the user typed in the face of multiple matches differing in case. */
+ if (_rl_completion_case_fold)
+ {
+ /* We're making an assumption here:
+ IF we're completing filenames AND
+ the application has defined a filename dequoting function AND
+ we found a quote character AND
+ the application has requested filename quoting
+ THEN
+ we assume that TEXT was dequoted before checking against
+ the file system and needs to be dequoted here before we
+ check against the list of matches
+ FI */
+ dtext = (char *)NULL;
+ if (rl_filename_completion_desired &&
+ rl_filename_dequoting_function &&
+ rl_completion_found_quote &&
+ rl_filename_quoting_desired)
+ {
+ dtext = (*rl_filename_dequoting_function) ((char *)text, rl_completion_quote_character);
+ text = dtext;
+ }
+
+ /* sort the list to get consistent answers. */
+ if (rl_sort_completion_matches)
+ qsort (match_list+1, matches, sizeof(char *), (QSFUNC *)_rl_qsort_string_compare);
+
+ si = strlen (text);
+ lx = (si <= low) ? si : low; /* check shorter of text and matches */
+ /* Try to preserve the case of what the user typed in the presence of
+ multiple matches: check each match for something that matches
+ what the user typed taking case into account; use it up to common
+ length of matches if one is found. If not, just use first match. */
+ for (i = 1; i <= matches; i++)
+ if (strncmp (match_list[i], text, lx) == 0)
+ {
+ strncpy (match_list[0], match_list[i], low);
+ break;
+ }
+ /* no casematch, use first entry */
+ if (i > matches)
+ strncpy (match_list[0], match_list[1], low);
+
+ FREE (dtext);
+ }
+ else
+ strncpy (match_list[0], match_list[1], low);
+
+ match_list[0][low] = '\0';
+ }
+
+ return matches;
+}
+
+static int
+postprocess_matches (char ***matchesp, int matching_filenames)
+{
+ char *t, **matches, **temp_matches;
+ int nmatch, i;
+
+ matches = *matchesp;
+
+ if (matches == 0)
+ return 0;
+
+ /* It seems to me that in all the cases we handle we would like
+ to ignore duplicate possibilities. Scan for the text to
+ insert being identical to the other completions. */
+ if (rl_ignore_completion_duplicates)
+ {
+ temp_matches = remove_duplicate_matches (matches);
+ xfree (matches);
+ matches = temp_matches;
+ }
+
+ /* If we are matching filenames, then here is our chance to
+ do clever processing by re-examining the list. Call the
+ ignore function with the array as a parameter. It can
+ munge the array, deleting matches as it desires. */
+ if (rl_ignore_some_completions_function && matching_filenames)
+ {
+ for (nmatch = 1; matches[nmatch]; nmatch++)
+ ;
+ (void)(*rl_ignore_some_completions_function) (matches);
+ if (matches == 0 || matches[0] == 0)
+ {
+ FREE (matches);
+ *matchesp = (char **)0;
+ return 0;
+ }
+ else
+ {
+ /* If we removed some matches, recompute the common prefix. */
+ for (i = 1; matches[i]; i++)
+ ;
+ if (i > 1 && i < nmatch)
+ {
+ t = matches[0];
+ compute_lcd_of_matches (matches, i - 1, t);
+ FREE (t);
+ }
+ }
+ }
+
+ *matchesp = matches;
+ return (1);
+}
+
+static int
+complete_get_screenwidth (void)
+{
+ int cols;
+ char *envcols;
+
+ cols = _rl_completion_columns;
+ if (cols >= 0 && cols <= _rl_screenwidth)
+ return cols;
+ envcols = getenv ("COLUMNS");
+ if (envcols && *envcols)
+ cols = atoi (envcols);
+ if (cols >= 0 && cols <= _rl_screenwidth)
+ return cols;
+ return _rl_screenwidth;
+}
+
+/* A convenience function for displaying a list of strings in
+ columnar format on readline's output stream. MATCHES is the list
+ of strings, in argv format, LEN is the number of strings in MATCHES,
+ and MAX is the length of the longest string in MATCHES. */
+void
+rl_display_match_list (char **matches, int len, int max)
+{
+ int count, limit, printed_len, lines, cols;
+ int i, j, k, l, common_length, sind;
+ char *temp, *t;
+
+ /* Find the length of the prefix common to all items: length as displayed
+ characters (common_length) and as a byte index into the matches (sind) */
+ common_length = sind = 0;
+ if (_rl_completion_prefix_display_length > 0)
+ {
+ t = printable_part (matches[0]);
+ /* check again in case of /usr/src/ */
+ temp = rl_filename_completion_desired ? strrchr (t, '/') : 0;
+ common_length = temp ? fnwidth (temp) : fnwidth (t);
+ sind = temp ? strlen (temp) : strlen (t);
+ if (common_length > max || sind > max)
+ common_length = sind = 0;
+
+ if (common_length > _rl_completion_prefix_display_length && common_length > ELLIPSIS_LEN)
+ max -= common_length - ELLIPSIS_LEN;
+ else if (_rl_colored_completion_prefix <= 0)
+ common_length = sind = 0;
+ }
+#if defined (COLOR_SUPPORT)
+ else if (_rl_colored_completion_prefix > 0)
+ {
+ t = printable_part (matches[0]);
+ temp = rl_filename_completion_desired ? strrchr (t, '/') : 0;
+ common_length = temp ? fnwidth (temp) : fnwidth (t);
+ sind = temp ? RL_STRLEN (temp+1) : RL_STRLEN (t); /* want portion after final slash */
+ if (common_length > max || sind > max)
+ common_length = sind = 0;
+ }
+#endif
+
+ /* How many items of MAX length can we fit in the screen window? */
+ cols = complete_get_screenwidth ();
+ max += 2;
+ limit = cols / max;
+ if (limit != 1 && (limit * max == cols))
+ limit--;
+
+ /* If cols == 0, limit will end up -1 */
+ if (cols < _rl_screenwidth && limit < 0)
+ limit = 1;
+
+ /* Avoid a possible floating exception. If max > cols,
+ limit will be 0 and a divide-by-zero fault will result. */
+ if (limit == 0)
+ limit = 1;
+
+ /* How many iterations of the printing loop? */
+ count = (len + (limit - 1)) / limit;
+
+ /* Watch out for special case. If LEN is less than LIMIT, then
+ just do the inner printing loop.
+ 0 < len <= limit implies count = 1. */
+
+ /* Sort the items if they are not already sorted. */
+ if (rl_ignore_completion_duplicates == 0 && rl_sort_completion_matches)
+ qsort (matches + 1, len, sizeof (char *), (QSFUNC *)_rl_qsort_string_compare);
+
+ rl_crlf ();
+
+ lines = 0;
+ if (_rl_print_completions_horizontally == 0)
+ {
+ /* Print the sorted items, up-and-down alphabetically, like ls. */
+ for (i = 1; i <= count; i++)
+ {
+ for (j = 0, l = i; j < limit; j++)
+ {
+ if (l > len || matches[l] == 0)
+ break;
+ else
+ {
+ temp = printable_part (matches[l]);
+ printed_len = print_filename (temp, matches[l], sind);
+
+ if (j + 1 < limit)
+ {
+ if (max <= printed_len)
+ putc (' ', rl_outstream);
+ else
+ for (k = 0; k < max - printed_len; k++)
+ putc (' ', rl_outstream);
+ }
+ }
+ l += count;
+ }
+ rl_crlf ();
+#if defined (SIGWINCH)
+ if (RL_SIG_RECEIVED () && RL_SIGWINCH_RECEIVED() == 0)
+#else
+ if (RL_SIG_RECEIVED ())
+#endif
+ return;
+ lines++;
+ if (_rl_page_completions && lines >= (_rl_screenheight - 1) && i < count)
+ {
+ lines = _rl_internal_pager (lines);
+ if (lines < 0)
+ return;
+ }
+ }
+ }
+ else
+ {
+ /* Print the sorted items, across alphabetically, like ls -x. */
+ for (i = 1; matches[i]; i++)
+ {
+ temp = printable_part (matches[i]);
+ printed_len = print_filename (temp, matches[i], sind);
+ /* Have we reached the end of this line? */
+#if defined (SIGWINCH)
+ if (RL_SIG_RECEIVED () && RL_SIGWINCH_RECEIVED() == 0)
+#else
+ if (RL_SIG_RECEIVED ())
+#endif
+ return;
+ if (matches[i+1])
+ {
+ if (limit == 1 || (i && (limit > 1) && (i % limit) == 0))
+ {
+ rl_crlf ();
+ lines++;
+ if (_rl_page_completions && lines >= _rl_screenheight - 1)
+ {
+ lines = _rl_internal_pager (lines);
+ if (lines < 0)
+ return;
+ }
+ }
+ else if (max <= printed_len)
+ putc (' ', rl_outstream);
+ else
+ for (k = 0; k < max - printed_len; k++)
+ putc (' ', rl_outstream);
+ }
+ }
+ rl_crlf ();
+ }
+}
+
+/* Display MATCHES, a list of matching filenames in argv format. This
+ handles the simple case -- a single match -- first. If there is more
+ than one match, we compute the number of strings in the list and the
+ length of the longest string, which will be needed by the display
+ function. If the application wants to handle displaying the list of
+ matches itself, it sets RL_COMPLETION_DISPLAY_MATCHES_HOOK to the
+ address of a function, and we just call it. If we're handling the
+ display ourselves, we just call rl_display_match_list. We also check
+ that the list of matches doesn't exceed the user-settable threshold,
+ and ask the user if he wants to see the list if there are more matches
+ than RL_COMPLETION_QUERY_ITEMS. */
+static void
+display_matches (char **matches)
+{
+ int len, max, i;
+ char *temp;
+
+ /* Move to the last visible line of a possibly-multiple-line command. */
+ _rl_move_vert (_rl_vis_botlin);
+
+ /* Handle simple case first. What if there is only one answer? */
+ if (matches[1] == 0)
+ {
+ temp = printable_part (matches[0]);
+ rl_crlf ();
+ print_filename (temp, matches[0], 0);
+ rl_crlf ();
+
+ rl_forced_update_display ();
+ rl_display_fixed = 1;
+
+ return;
+ }
+
+ /* There is more than one answer. Find out how many there are,
+ and find the maximum printed length of a single entry. */
+ for (max = 0, i = 1; matches[i]; i++)
+ {
+ temp = printable_part (matches[i]);
+ len = fnwidth (temp);
+
+ if (len > max)
+ max = len;
+ }
+
+ len = i - 1;
+
+ /* If the caller has defined a display hook, then call that now. */
+ if (rl_completion_display_matches_hook)
+ {
+ (*rl_completion_display_matches_hook) (matches, len, max);
+ return;
+ }
+
+ /* If there are many items, then ask the user if she really wants to
+ see them all. */
+ if (rl_completion_query_items > 0 && len >= rl_completion_query_items)
+ {
+ rl_crlf ();
+ fprintf (rl_outstream, "Display all %d possibilities? (y or n)", len);
+ fflush (rl_outstream);
+ if ((completion_y_or_n = get_y_or_n (0)) == 0)
+ {
+ rl_crlf ();
+
+ rl_forced_update_display ();
+ rl_display_fixed = 1;
+
+ return;
+ }
+ }
+
+ rl_display_match_list (matches, len, max);
+
+ rl_forced_update_display ();
+ rl_display_fixed = 1;
+}
+
+/* qc == pointer to quoting character, if any */
+static char *
+make_quoted_replacement (char *match, int mtype, char *qc)
+{
+ int should_quote, do_replace;
+ char *replacement;
+
+ /* If we are doing completion on quoted substrings, and any matches
+ contain any of the completer_word_break_characters, then auto-
+ matically prepend the substring with a quote character (just pick
+ the first one from the list of such) if it does not already begin
+ with a quote string. FIXME: Need to remove any such automatically
+ inserted quote character when it no longer is necessary, such as
+ if we change the string we are completing on and the new set of
+ matches don't require a quoted substring. */
+ replacement = match;
+
+ should_quote = match && rl_completer_quote_characters &&
+ rl_filename_completion_desired &&
+ rl_filename_quoting_desired;
+
+ if (should_quote)
+ should_quote = should_quote && (!qc || !*qc ||
+ (rl_completer_quote_characters && strchr (rl_completer_quote_characters, *qc)));
+
+ if (should_quote)
+ {
+ /* If there is a single match, see if we need to quote it.
+ This also checks whether the common prefix of several
+ matches needs to be quoted. */
+ should_quote = rl_filename_quote_characters
+ ? (_rl_strpbrk (match, rl_filename_quote_characters) != 0)
+ : 0;
+
+ do_replace = should_quote ? mtype : NO_MATCH;
+ /* Quote the replacement, since we found an embedded
+ word break character in a potential match. */
+ if (do_replace != NO_MATCH && rl_filename_quoting_function)
+ replacement = (*rl_filename_quoting_function) (match, do_replace, qc);
+ }
+ return (replacement);
+}
+
+static void
+insert_match (char *match, int start, int mtype, char *qc)
+{
+ char *replacement, *r;
+ char oqc;
+ int end, rlen;
+
+ oqc = qc ? *qc : '\0';
+ replacement = make_quoted_replacement (match, mtype, qc);
+
+ /* Now insert the match. */
+ if (replacement)
+ {
+ rlen = strlen (replacement);
+ /* Don't double an opening quote character. */
+ if (qc && *qc && start && rl_line_buffer[start - 1] == *qc &&
+ replacement[0] == *qc)
+ start--;
+ /* If make_quoted_replacement changed the quoting character, remove
+ the opening quote and insert the (fully-quoted) replacement. */
+ else if (qc && (*qc != oqc) && start && rl_line_buffer[start - 1] == oqc &&
+ replacement[0] != oqc)
+ start--;
+ end = rl_point - 1;
+ /* Don't double a closing quote character */
+ if (qc && *qc && end && rl_line_buffer[rl_point] == *qc && replacement[rlen - 1] == *qc)
+ end++;
+ if (_rl_skip_completed_text)
+ {
+ r = replacement;
+ while (start < rl_end && *r && rl_line_buffer[start] == *r)
+ {
+ start++;
+ r++;
+ }
+ if (start <= end || *r)
+ _rl_replace_text (r, start, end);
+ rl_point = start + strlen (r);
+ }
+ else
+ _rl_replace_text (replacement, start, end);
+ if (replacement != match)
+ xfree (replacement);
+ }
+}
+
+/* Append any necessary closing quote and a separator character to the
+ just-inserted match. If the user has specified that directories
+ should be marked by a trailing `/', append one of those instead. The
+ default trailing character is a space. Returns the number of characters
+ appended. If NONTRIVIAL_MATCH is set, we test for a symlink (if the OS
+ has them) and don't add a suffix for a symlink to a directory. A
+ nontrivial match is one that actually adds to the word being completed.
+ The variable rl_completion_mark_symlink_dirs controls this behavior
+ (it's initially set to the what the user has chosen, indicated by the
+ value of _rl_complete_mark_symlink_dirs, but may be modified by an
+ application's completion function). */
+static int
+append_to_match (char *text, int delimiter, int quote_char, int nontrivial_match)
+{
+ char temp_string[4], *filename, *fn;
+ int temp_string_index, s;
+ struct stat finfo;
+
+ temp_string_index = 0;
+ if (quote_char && rl_point && rl_completion_suppress_quote == 0 &&
+ rl_line_buffer[rl_point - 1] != quote_char)
+ temp_string[temp_string_index++] = quote_char;
+
+ if (delimiter)
+ temp_string[temp_string_index++] = delimiter;
+ else if (rl_completion_suppress_append == 0 && rl_completion_append_character)
+ temp_string[temp_string_index++] = rl_completion_append_character;
+
+ temp_string[temp_string_index++] = '\0';
+
+ if (rl_filename_completion_desired)
+ {
+ filename = tilde_expand (text);
+ if (rl_filename_stat_hook)
+ {
+ fn = savestring (filename);
+ (*rl_filename_stat_hook) (&fn);
+ xfree (filename);
+ filename = fn;
+ }
+ s = (nontrivial_match && rl_completion_mark_symlink_dirs == 0)
+ ? LSTAT (filename, &finfo)
+ : stat (filename, &finfo);
+ if (s == 0 && S_ISDIR (finfo.st_mode))
+ {
+ if (_rl_complete_mark_directories /* && rl_completion_suppress_append == 0 */)
+ {
+ /* This is clumsy. Avoid putting in a double slash if point
+ is at the end of the line and the previous character is a
+ slash. */
+ if (rl_point && rl_line_buffer[rl_point] == '\0' && rl_line_buffer[rl_point - 1] == '/')
+ ;
+ else if (rl_line_buffer[rl_point] != '/')
+ rl_insert_text ("/");
+ }
+ }
+#ifdef S_ISLNK
+ /* Don't add anything if the filename is a symlink and resolves to a
+ directory. */
+ else if (s == 0 && S_ISLNK (finfo.st_mode) && path_isdir (filename))
+ ;
+#endif
+ else
+ {
+ if (rl_point == rl_end && temp_string_index)
+ rl_insert_text (temp_string);
+ }
+ xfree (filename);
+ }
+ else
+ {
+ if (rl_point == rl_end && temp_string_index)
+ rl_insert_text (temp_string);
+ }
+
+ return (temp_string_index);
+}
+
+static void
+insert_all_matches (char **matches, int point, char *qc)
+{
+ int i;
+ char *rp;
+
+ rl_begin_undo_group ();
+ /* remove any opening quote character; make_quoted_replacement will add
+ it back. */
+ if (qc && *qc && point && rl_line_buffer[point - 1] == *qc)
+ point--;
+ rl_delete_text (point, rl_point);
+ rl_point = point;
+
+ if (matches[1])
+ {
+ for (i = 1; matches[i]; i++)
+ {
+ rp = make_quoted_replacement (matches[i], SINGLE_MATCH, qc);
+ rl_insert_text (rp);
+ rl_insert_text (" ");
+ if (rp != matches[i])
+ xfree (rp);
+ }
+ }
+ else
+ {
+ rp = make_quoted_replacement (matches[0], SINGLE_MATCH, qc);
+ rl_insert_text (rp);
+ rl_insert_text (" ");
+ if (rp != matches[0])
+ xfree (rp);
+ }
+ rl_end_undo_group ();
+}
+
+void
+_rl_free_match_list (char **matches)
+{
+ register int i;
+
+ if (matches == 0)
+ return;
+
+ for (i = 0; matches[i]; i++)
+ xfree (matches[i]);
+ xfree (matches);
+}
+
+/* Compare a possibly-quoted filename TEXT from the line buffer and a possible
+ MATCH that is the product of filename completion, which acts on the dequoted
+ text. */
+static int
+compare_match (char *text, const char *match)
+{
+ char *temp;
+ int r;
+
+ if (rl_filename_completion_desired && rl_filename_quoting_desired &&
+ rl_completion_found_quote && rl_filename_dequoting_function)
+ {
+ temp = (*rl_filename_dequoting_function) (text, rl_completion_quote_character);
+ r = strcmp (temp, match);
+ xfree (temp);
+ return r;
+ }
+ return (strcmp (text, match));
+}
+
+/* Complete the word at or before point.
+ WHAT_TO_DO says what to do with the completion.
+ `?' means list the possible completions.
+ TAB means do standard completion.
+ `*' means insert all of the possible completions.
+ `!' means to do standard completion, and list all possible completions if
+ there is more than one.
+ `@' means to do standard completion, and list all possible completions if
+ there is more than one and partial completion is not possible. */
+int
+rl_complete_internal (int what_to_do)
+{
+ char **matches;
+ rl_compentry_func_t *our_func;
+ int start, end, delimiter, found_quote, i, nontrivial_lcd;
+ char *text, *saved_line_buffer;
+ char quote_char;
+ int tlen, mlen, saved_last_completion_failed;
+
+ RL_SETSTATE(RL_STATE_COMPLETING);
+
+ saved_last_completion_failed = last_completion_failed;
+
+ set_completion_defaults (what_to_do);
+
+ saved_line_buffer = rl_line_buffer ? savestring (rl_line_buffer) : (char *)NULL;
+ our_func = rl_completion_entry_function
+ ? rl_completion_entry_function
+ : rl_filename_completion_function;
+ /* We now look backwards for the start of a filename/variable word. */
+ end = rl_point;
+ found_quote = delimiter = 0;
+ quote_char = '\0';
+
+ if (rl_point)
+ /* This (possibly) changes rl_point. If it returns a non-zero char,
+ we know we have an open quote. */
+ quote_char = _rl_find_completion_word (&found_quote, &delimiter);
+
+ start = rl_point;
+ rl_point = end;
+
+ text = rl_copy_text (start, end);
+ matches = gen_completion_matches (text, start, end, our_func, found_quote, quote_char);
+ /* nontrivial_lcd is set if the common prefix adds something to the word
+ being completed. */
+ nontrivial_lcd = matches && compare_match (text, matches[0]) != 0;
+ if (what_to_do == '!' || what_to_do == '@')
+ tlen = strlen (text);
+ xfree (text);
+
+ if (matches == 0)
+ {
+ rl_ding ();
+ FREE (saved_line_buffer);
+ completion_changed_buffer = 0;
+ last_completion_failed = 1;
+ RL_UNSETSTATE(RL_STATE_COMPLETING);
+ _rl_reset_completion_state ();
+ return (0);
+ }
+
+ /* If we are matching filenames, the attempted completion function will
+ have set rl_filename_completion_desired to a non-zero value. The basic
+ rl_filename_completion_function does this. */
+ i = rl_filename_completion_desired;
+
+ if (postprocess_matches (&matches, i) == 0)
+ {
+ rl_ding ();
+ FREE (saved_line_buffer);
+ completion_changed_buffer = 0;
+ last_completion_failed = 1;
+ RL_UNSETSTATE(RL_STATE_COMPLETING);
+ _rl_reset_completion_state ();
+ return (0);
+ }
+
+ if (matches && matches[0] && *matches[0])
+ last_completion_failed = 0;
+
+ switch (what_to_do)
+ {
+ case TAB:
+ case '!':
+ case '@':
+ /* Insert the first match with proper quoting. */
+ if (what_to_do == TAB)
+ {
+ if (*matches[0])
+ insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char);
+ }
+ else if (*matches[0] && matches[1] == 0)
+ /* should we perform the check only if there are multiple matches? */
+ insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char);
+ else if (*matches[0]) /* what_to_do != TAB && multiple matches */
+ {
+ mlen = *matches[0] ? strlen (matches[0]) : 0;
+ if (mlen >= tlen)
+ insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char);
+ }
+
+ /* If there are more matches, ring the bell to indicate.
+ If we are in vi mode, Posix.2 says to not ring the bell.
+ If the `show-all-if-ambiguous' variable is set, display
+ all the matches immediately. Otherwise, if this was the
+ only match, and we are hacking files, check the file to
+ see if it was a directory. If so, and the `mark-directories'
+ variable is set, add a '/' to the name. If not, and we
+ are at the end of the line, then add a space. */
+ if (matches[1])
+ {
+ if (what_to_do == '!')
+ {
+ display_matches (matches);
+ break;
+ }
+ else if (what_to_do == '@')
+ {
+ if (nontrivial_lcd == 0)
+ display_matches (matches);
+ break;
+ }
+ else if (rl_editing_mode != vi_mode)
+ rl_ding (); /* There are other matches remaining. */
+ }
+ else
+ append_to_match (matches[0], delimiter, quote_char, nontrivial_lcd);
+
+ break;
+
+ case '*':
+ insert_all_matches (matches, start, "e_char);
+ break;
+
+ case '?':
+ /* Let's try to insert a single match here if the last completion failed
+ but this attempt returned a single match. */
+ if (saved_last_completion_failed && matches[0] && *matches[0] && matches[1] == 0)
+ {
+ insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char);
+ append_to_match (matches[0], delimiter, quote_char, nontrivial_lcd);
+ break;
+ }
+
+ if (rl_completion_display_matches_hook == 0)
+ {
+ _rl_sigcleanup = _rl_complete_sigcleanup;
+ _rl_sigcleanarg = matches;
+ _rl_complete_display_matches_interrupt = 0;
+ }
+ display_matches (matches);
+ if (_rl_complete_display_matches_interrupt)
+ {
+ matches = 0; /* already freed by rl_complete_sigcleanup */
+ _rl_complete_display_matches_interrupt = 0;
+ if (rl_signal_event_hook)
+ (*rl_signal_event_hook) (); /* XXX */
+ }
+ _rl_sigcleanup = 0;
+ _rl_sigcleanarg = 0;
+ break;
+
+ default:
+ _rl_ttymsg ("bad value %d for what_to_do in rl_complete", what_to_do);
+ rl_ding ();
+ FREE (saved_line_buffer);
+ RL_UNSETSTATE(RL_STATE_COMPLETING);
+ _rl_free_match_list (matches);
+ _rl_reset_completion_state ();
+ return 1;
+ }
+
+ _rl_free_match_list (matches);
+
+ /* Check to see if the line has changed through all of this manipulation. */
+ if (saved_line_buffer)
+ {
+ completion_changed_buffer = strcmp (rl_line_buffer, saved_line_buffer) != 0;
+ xfree (saved_line_buffer);
+ }
+
+ RL_UNSETSTATE(RL_STATE_COMPLETING);
+ _rl_reset_completion_state ();
+
+ RL_CHECK_SIGNALS ();
+ return 0;
+}
+
+/***************************************************************/
+/* */
+/* Application-callable completion match generator functions */
+/* */
+/***************************************************************/
+
+/* Return an array of (char *) which is a list of completions for TEXT.
+ If there are no completions, return a NULL pointer.
+ The first entry in the returned array is the substitution for TEXT.
+ The remaining entries are the possible completions.
+ The array is terminated with a NULL pointer.
+
+ ENTRY_FUNCTION is a function of two args, and returns a (char *).
+ The first argument is TEXT.
+ The second is a state argument; it should be zero on the first call, and
+ non-zero on subsequent calls. It returns a NULL pointer to the caller
+ when there are no more matches.
+ */
+char **
+rl_completion_matches (const char *text, rl_compentry_func_t *entry_function)
+{
+ register int i;
+
+ /* Number of slots in match_list. */
+ int match_list_size;
+
+ /* The list of matches. */
+ char **match_list;
+
+ /* Number of matches actually found. */
+ int matches;
+
+ /* Temporary string binder. */
+ char *string;
+
+ matches = 0;
+ match_list_size = 10;
+ match_list = (char **)xmalloc ((match_list_size + 1) * sizeof (char *));
+ match_list[1] = (char *)NULL;
+
+ while (string = (*entry_function) (text, matches))
+ {
+ if (RL_SIG_RECEIVED ())
+ {
+ /* Start at 1 because we don't set matches[0] in this function.
+ Only free the list members if we're building match list from
+ rl_filename_completion_function, since we know that doesn't
+ free the strings it returns. */
+ if (entry_function == rl_filename_completion_function)
+ {
+ for (i = 1; match_list[i]; i++)
+ xfree (match_list[i]);
+ }
+ xfree (match_list);
+ match_list = 0;
+ match_list_size = 0;
+ matches = 0;
+ RL_CHECK_SIGNALS ();
+ }
+
+ if (matches + 1 >= match_list_size)
+ match_list = (char **)xrealloc
+ (match_list, ((match_list_size += 10) + 1) * sizeof (char *));
+
+ if (match_list == 0)
+ return (match_list);
+
+ match_list[++matches] = string;
+ match_list[matches + 1] = (char *)NULL;
+ }
+
+ /* If there were any matches, then look through them finding out the
+ lowest common denominator. That then becomes match_list[0]. */
+ if (matches)
+ compute_lcd_of_matches (match_list, matches, text);
+ else /* There were no matches. */
+ {
+ xfree (match_list);
+ match_list = (char **)NULL;
+ }
+ return (match_list);
+}
+
+/* A completion function for usernames.
+ TEXT contains a partial username preceded by a random
+ character (usually `~'). */
+char *
+rl_username_completion_function (const char *text, int state)
+{
+#if defined (__WIN32__) || defined (__OPENNT)
+ return (char *)NULL;
+#else /* !__WIN32__ && !__OPENNT) */
+ static char *username = (char *)NULL;
+ static struct passwd *entry;
+ static int namelen, first_char, first_char_loc;
+ char *value;
+
+ if (state == 0)
+ {
+ FREE (username);
+
+ first_char = *text;
+ first_char_loc = first_char == '~';
+
+ username = savestring (&text[first_char_loc]);
+ namelen = strlen (username);
+#if defined (HAVE_GETPWENT)
+ setpwent ();
+#endif
+ }
+
+#if defined (HAVE_GETPWENT)
+ while (entry = getpwent ())
+ {
+ /* Null usernames should result in all users as possible completions. */
+ if (namelen == 0 || (STREQN (username, entry->pw_name, namelen)))
+ break;
+ }
+#endif
+
+ if (entry == 0)
+ {
+#if defined (HAVE_GETPWENT)
+ endpwent ();
+#endif
+ return ((char *)NULL);
+ }
+ else
+ {
+ value = (char *)xmalloc (2 + strlen (entry->pw_name));
+
+ *value = *text;
+
+ strcpy (value + first_char_loc, entry->pw_name);
+
+ if (first_char == '~')
+ rl_filename_completion_desired = 1;
+
+ return (value);
+ }
+#endif /* !__WIN32__ && !__OPENNT */
+}
+
+/* Return non-zero if CONVFN matches FILENAME up to the length of FILENAME
+ (FILENAME_LEN). If _rl_completion_case_fold is set, compare without
+ regard to the alphabetic case of characters. If
+ _rl_completion_case_map is set, make `-' and `_' equivalent. CONVFN is
+ the possibly-converted directory entry; FILENAME is what the user typed. */
+static int
+complete_fncmp (const char *convfn, int convlen, const char *filename, int filename_len)
+{
+ register char *s1, *s2;
+ int d, len;
+#if defined (HANDLE_MULTIBYTE)
+ size_t v1, v2;
+ mbstate_t ps1, ps2;
+ WCHAR_T wc1, wc2;
+#endif
+
+#if defined (HANDLE_MULTIBYTE)
+ memset (&ps1, 0, sizeof (mbstate_t));
+ memset (&ps2, 0, sizeof (mbstate_t));
+#endif
+
+ if (filename_len == 0)
+ return 1;
+ if (convlen < filename_len)
+ return 0;
+
+ len = filename_len;
+ s1 = (char *)convfn;
+ s2 = (char *)filename;
+
+ /* Otherwise, if these match up to the length of filename, then
+ it is a match. */
+ if (_rl_completion_case_fold && _rl_completion_case_map)
+ {
+ /* Case-insensitive comparison treating _ and - as equivalent */
+#if defined (HANDLE_MULTIBYTE)
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ {
+ do
+ {
+ v1 = MBRTOWC (&wc1, s1, convlen, &ps1);
+ v2 = MBRTOWC (&wc2, s2, filename_len, &ps2);
+ if (v1 == 0 && v2 == 0)
+ return 1;
+ else if (MB_INVALIDCH (v1) || MB_INVALIDCH (v2))
+ {
+ if (*s1 != *s2) /* do byte comparison */
+ return 0;
+ else if ((*s1 == '-' || *s1 == '_') && (*s2 == '-' || *s2 == '_'))
+ return 0;
+ s1++; s2++; len--;
+ continue;
+ }
+ wc1 = towlower (wc1);
+ wc2 = towlower (wc2);
+ s1 += v1;
+ s2 += v1;
+ len -= v1;
+ if ((wc1 == L'-' || wc1 == L'_') && (wc2 == L'-' || wc2 == L'_'))
+ continue;
+ if (wc1 != wc2)
+ return 0;
+ }
+ while (len != 0);
+ }
+ else
+#endif
+ {
+ do
+ {
+ d = _rl_to_lower (*s1) - _rl_to_lower (*s2);
+ /* *s1 == [-_] && *s2 == [-_] */
+ if ((*s1 == '-' || *s1 == '_') && (*s2 == '-' || *s2 == '_'))
+ d = 0;
+ if (d != 0)
+ return 0;
+ s1++; s2++; /* already checked convlen >= filename_len */
+ }
+ while (--len != 0);
+ }
+
+ return 1;
+ }
+ else if (_rl_completion_case_fold)
+ {
+#if defined (HANDLE_MULTIBYTE)
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ {
+ do
+ {
+ v1 = MBRTOWC (&wc1, s1, convlen, &ps1);
+ v2 = MBRTOWC (&wc2, s2, filename_len, &ps2);
+ if (v1 == 0 && v2 == 0)
+ return 1;
+ else if (MB_INVALIDCH (v1) || MB_INVALIDCH (v2))
+ {
+ if (*s1 != *s2) /* do byte comparison */
+ return 0;
+ s1++; s2++; len--;
+ continue;
+ }
+ wc1 = towlower (wc1);
+ wc2 = towlower (wc2);
+ if (wc1 != wc2)
+ return 0;
+ s1 += v1;
+ s2 += v1;
+ len -= v1;
+ }
+ while (len != 0);
+ return 1;
+ }
+ else
+#endif
+ if ((_rl_to_lower (convfn[0]) == _rl_to_lower (filename[0])) &&
+ (convlen >= filename_len) &&
+ (_rl_strnicmp (filename, convfn, filename_len) == 0))
+ return 1;
+ }
+ else
+ {
+ if ((convfn[0] == filename[0]) &&
+ (convlen >= filename_len) &&
+ (strncmp (filename, convfn, filename_len) == 0))
+ return 1;
+ }
+ return 0;
+}
+
+/* Okay, now we write the entry_function for filename completion. In the
+ general case. Note that completion in the shell is a little different
+ because of all the pathnames that must be followed when looking up the
+ completion for a command. */
+char *
+rl_filename_completion_function (const char *text, int state)
+{
+ static DIR *directory = (DIR *)NULL;
+ static char *filename = (char *)NULL;
+ static char *dirname = (char *)NULL;
+ static char *users_dirname = (char *)NULL;
+ static int filename_len;
+ char *temp, *dentry, *convfn;
+ int dirlen, dentlen, convlen;
+ int tilde_dirname;
+ struct dirent *entry;
+
+ /* If we don't have any state, then do some initialization. */
+ if (state == 0)
+ {
+ /* If we were interrupted before closing the directory or reading
+ all of its contents, close it. */
+ if (directory)
+ {
+ closedir (directory);
+ directory = (DIR *)NULL;
+ }
+ FREE (dirname);
+ FREE (filename);
+ FREE (users_dirname);
+
+ filename = savestring (text);
+ if (*text == 0)
+ text = ".";
+ dirname = savestring (text);
+
+ temp = strrchr (dirname, '/');
+
+#if defined (__MSDOS__) || defined (_WIN32)
+ /* special hack for //X/... */
+ if (dirname[0] == '/' && dirname[1] == '/' && ISALPHA ((unsigned char)dirname[2]) && dirname[3] == '/')
+ temp = strrchr (dirname + 3, '/');
+#endif
+
+ if (temp)
+ {
+ strcpy (filename, ++temp);
+ *temp = '\0';
+ }
+#if defined (__MSDOS__) || (defined (_WIN32) && !defined (__CYGWIN__))
+ /* searches from current directory on the drive */
+ else if (ISALPHA ((unsigned char)dirname[0]) && dirname[1] == ':')
+ {
+ strcpy (filename, dirname + 2);
+ dirname[2] = '\0';
+ }
+#endif
+ else
+ {
+ dirname[0] = '.';
+ dirname[1] = '\0';
+ }
+
+ /* We aren't done yet. We also support the "~user" syntax. */
+
+ /* Save the version of the directory that the user typed, dequoting
+ it if necessary. */
+ if (rl_completion_found_quote && rl_filename_dequoting_function)
+ users_dirname = (*rl_filename_dequoting_function) (dirname, rl_completion_quote_character);
+ else
+ users_dirname = savestring (dirname);
+
+ tilde_dirname = 0;
+ if (*dirname == '~')
+ {
+ temp = tilde_expand (dirname);
+ xfree (dirname);
+ dirname = temp;
+ tilde_dirname = 1;
+ }
+
+ /* We have saved the possibly-dequoted version of the directory name
+ the user typed. Now transform the directory name we're going to
+ pass to opendir(2). The directory rewrite hook modifies only the
+ directory name; the directory completion hook modifies both the
+ directory name passed to opendir(2) and the version the user
+ typed. Both the directory completion and rewrite hooks should perform
+ any necessary dequoting. The hook functions return 1 if they modify
+ the directory name argument. If either hook returns 0, it should
+ not modify the directory name pointer passed as an argument. */
+ if (rl_directory_rewrite_hook)
+ (*rl_directory_rewrite_hook) (&dirname);
+ else if (rl_directory_completion_hook && (*rl_directory_completion_hook) (&dirname))
+ {
+ xfree (users_dirname);
+ users_dirname = savestring (dirname);
+ }
+ else if (tilde_dirname == 0 && rl_completion_found_quote && rl_filename_dequoting_function)
+ {
+ /* delete single and double quotes */
+ xfree (dirname);
+ dirname = savestring (users_dirname);
+ }
+ directory = opendir (dirname);
+
+ /* Now dequote a non-null filename. FILENAME will not be NULL, but may
+ be empty. */
+ if (*filename && rl_completion_found_quote && rl_filename_dequoting_function)
+ {
+ /* delete single and double quotes */
+ temp = (*rl_filename_dequoting_function) (filename, rl_completion_quote_character);
+ xfree (filename);
+ filename = temp;
+ }
+ filename_len = strlen (filename);
+
+ rl_filename_completion_desired = 1;
+ }
+
+ /* At this point we should entertain the possibility of hacking wildcarded
+ filenames, like /usr/man/man/te. If the directory name
+ contains globbing characters, then build an array of directories, and
+ then map over that list while completing. */
+ /* *** UNIMPLEMENTED *** */
+
+ /* Now that we have some state, we can read the directory. */
+
+ entry = (struct dirent *)NULL;
+ while (directory && (entry = readdir (directory)))
+ {
+ convfn = dentry = entry->d_name;
+ convlen = dentlen = D_NAMLEN (entry);
+
+ if (rl_filename_rewrite_hook)
+ {
+ convfn = (*rl_filename_rewrite_hook) (dentry, dentlen);
+ convlen = (convfn == dentry) ? dentlen : strlen (convfn);
+ }
+
+ /* Special case for no filename. If the user has disabled the
+ `match-hidden-files' variable, skip filenames beginning with `.'.
+ All other entries except "." and ".." match. */
+ if (filename_len == 0)
+ {
+ if (_rl_match_hidden_files == 0 && HIDDEN_FILE (convfn))
+ continue;
+
+ if (convfn[0] != '.' ||
+ (convfn[1] && (convfn[1] != '.' || convfn[2])))
+ break;
+ }
+ else
+ {
+ if (complete_fncmp (convfn, convlen, filename, filename_len))
+ break;
+ }
+ }
+
+ if (entry == 0)
+ {
+ if (directory)
+ {
+ closedir (directory);
+ directory = (DIR *)NULL;
+ }
+ if (dirname)
+ {
+ xfree (dirname);
+ dirname = (char *)NULL;
+ }
+ if (filename)
+ {
+ xfree (filename);
+ filename = (char *)NULL;
+ }
+ if (users_dirname)
+ {
+ xfree (users_dirname);
+ users_dirname = (char *)NULL;
+ }
+
+ return (char *)NULL;
+ }
+ else
+ {
+ /* dirname && (strcmp (dirname, ".") != 0) */
+ if (dirname && (dirname[0] != '.' || dirname[1]))
+ {
+ if (rl_complete_with_tilde_expansion && *users_dirname == '~')
+ {
+ dirlen = strlen (dirname);
+ temp = (char *)xmalloc (2 + dirlen + D_NAMLEN (entry));
+ strcpy (temp, dirname);
+ /* Canonicalization cuts off any final slash present. We
+ may need to add it back. */
+ if (dirname[dirlen - 1] != '/')
+ {
+ temp[dirlen++] = '/';
+ temp[dirlen] = '\0';
+ }
+ }
+ else
+ {
+ dirlen = strlen (users_dirname);
+ temp = (char *)xmalloc (2 + dirlen + D_NAMLEN (entry));
+ strcpy (temp, users_dirname);
+ /* Make sure that temp has a trailing slash here. */
+ if (users_dirname[dirlen - 1] != '/')
+ temp[dirlen++] = '/';
+ }
+
+ strcpy (temp + dirlen, convfn);
+ }
+ else
+ temp = savestring (convfn);
+
+ if (convfn != dentry)
+ xfree (convfn);
+
+ return (temp);
+ }
+}
+
+/* An initial implementation of a menu completion function a la tcsh. The
+ first time (if the last readline command was not rl_old_menu_complete), we
+ generate the list of matches. This code is very similar to the code in
+ rl_complete_internal -- there should be a way to combine the two. Then,
+ for each item in the list of matches, we insert the match in an undoable
+ fashion, with the appropriate character appended (this happens on the
+ second and subsequent consecutive calls to rl_old_menu_complete). When we
+ hit the end of the match list, we restore the original unmatched text,
+ ring the bell, and reset the counter to zero. */
+int
+rl_old_menu_complete (int count, int invoking_key)
+{
+ rl_compentry_func_t *our_func;
+ int matching_filenames, found_quote;
+
+ static char *orig_text;
+ static char **matches = (char **)0;
+ static int match_list_index = 0;
+ static int match_list_size = 0;
+ static int orig_start, orig_end;
+ static char quote_char;
+ static int delimiter;
+
+ /* The first time through, we generate the list of matches and set things
+ up to insert them. */
+ if (rl_last_func != rl_old_menu_complete)
+ {
+ /* Clean up from previous call, if any. */
+ FREE (orig_text);
+ if (matches)
+ _rl_free_match_list (matches);
+
+ match_list_index = match_list_size = 0;
+ matches = (char **)NULL;
+
+ rl_completion_invoking_key = invoking_key;
+
+ RL_SETSTATE(RL_STATE_COMPLETING);
+
+ /* Only the completion entry function can change these. */
+ set_completion_defaults ('%');
+
+ our_func = rl_menu_completion_entry_function;
+ if (our_func == 0)
+ our_func = rl_completion_entry_function
+ ? rl_completion_entry_function
+ : rl_filename_completion_function;
+
+ /* We now look backwards for the start of a filename/variable word. */
+ orig_end = rl_point;
+ found_quote = delimiter = 0;
+ quote_char = '\0';
+
+ if (rl_point)
+ /* This (possibly) changes rl_point. If it returns a non-zero char,
+ we know we have an open quote. */
+ quote_char = _rl_find_completion_word (&found_quote, &delimiter);
+
+ orig_start = rl_point;
+ rl_point = orig_end;
+
+ orig_text = rl_copy_text (orig_start, orig_end);
+ matches = gen_completion_matches (orig_text, orig_start, orig_end,
+ our_func, found_quote, quote_char);
+
+ /* If we are matching filenames, the attempted completion function will
+ have set rl_filename_completion_desired to a non-zero value. The basic
+ rl_filename_completion_function does this. */
+ matching_filenames = rl_filename_completion_desired;
+
+ if (matches == 0 || postprocess_matches (&matches, matching_filenames) == 0)
+ {
+ rl_ding ();
+ FREE (matches);
+ matches = (char **)0;
+ FREE (orig_text);
+ orig_text = (char *)0;
+ completion_changed_buffer = 0;
+ RL_UNSETSTATE(RL_STATE_COMPLETING);
+ return (0);
+ }
+
+ RL_UNSETSTATE(RL_STATE_COMPLETING);
+
+ for (match_list_size = 0; matches[match_list_size]; match_list_size++)
+ ;
+ /* matches[0] is lcd if match_list_size > 1, but the circular buffer
+ code below should take care of it. */
+
+ if (match_list_size > 1 && _rl_complete_show_all)
+ display_matches (matches);
+ }
+
+ /* Now we have the list of matches. Replace the text between
+ rl_line_buffer[orig_start] and rl_line_buffer[rl_point] with
+ matches[match_list_index], and add any necessary closing char. */
+
+ if (matches == 0 || match_list_size == 0)
+ {
+ rl_ding ();
+ FREE (matches);
+ matches = (char **)0;
+ completion_changed_buffer = 0;
+ return (0);
+ }
+
+ match_list_index += count;
+ if (match_list_index < 0)
+ {
+ while (match_list_index < 0)
+ match_list_index += match_list_size;
+ }
+ else
+ match_list_index %= match_list_size;
+
+ if (match_list_index == 0 && match_list_size > 1)
+ {
+ rl_ding ();
+ insert_match (orig_text, orig_start, MULT_MATCH, "e_char);
+ }
+ else
+ {
+ insert_match (matches[match_list_index], orig_start, SINGLE_MATCH, "e_char);
+ append_to_match (matches[match_list_index], delimiter, quote_char,
+ compare_match (orig_text, matches[match_list_index]));
+ }
+
+ completion_changed_buffer = 1;
+ return (0);
+}
+
+/* The current version of menu completion.
+ The differences between this function and the original are:
+
+1. It honors the maximum number of completions variable (completion-query-items)
+2. It appends to the word as usual if there is only one match
+3. It displays the common prefix if there is one, and makes it the first menu
+ choice if the menu-complete-display-prefix option is enabled
+*/
+
+int
+rl_menu_complete (int count, int ignore)
+{
+ rl_compentry_func_t *our_func;
+ int matching_filenames, found_quote;
+
+ static char *orig_text;
+ static char **matches = (char **)0;
+ static int match_list_index = 0;
+ static int match_list_size = 0;
+ static int nontrivial_lcd = 0;
+ static int full_completion = 0; /* set to 1 if menu completion should reinitialize on next call */
+ static int orig_start, orig_end;
+ static char quote_char;
+ static int delimiter, cstate;
+
+ /* The first time through, we generate the list of matches and set things
+ up to insert them. */
+ if ((rl_last_func != rl_menu_complete && rl_last_func != rl_backward_menu_complete) || full_completion)
+ {
+ /* Clean up from previous call, if any. */
+ FREE (orig_text);
+ if (matches)
+ _rl_free_match_list (matches);
+
+ match_list_index = match_list_size = 0;
+ matches = (char **)NULL;
+
+ full_completion = 0;
+
+ RL_SETSTATE(RL_STATE_COMPLETING);
+
+ /* Only the completion entry function can change these. */
+ set_completion_defaults ('%');
+
+ our_func = rl_menu_completion_entry_function;
+ if (our_func == 0)
+ our_func = rl_completion_entry_function
+ ? rl_completion_entry_function
+ : rl_filename_completion_function;
+
+ /* We now look backwards for the start of a filename/variable word. */
+ orig_end = rl_point;
+ found_quote = delimiter = 0;
+ quote_char = '\0';
+
+ if (rl_point)
+ /* This (possibly) changes rl_point. If it returns a non-zero char,
+ we know we have an open quote. */
+ quote_char = _rl_find_completion_word (&found_quote, &delimiter);
+
+ orig_start = rl_point;
+ rl_point = orig_end;
+
+ orig_text = rl_copy_text (orig_start, orig_end);
+ matches = gen_completion_matches (orig_text, orig_start, orig_end,
+ our_func, found_quote, quote_char);
+
+ nontrivial_lcd = matches && compare_match (orig_text, matches[0]) != 0;
+
+ /* If we are matching filenames, the attempted completion function will
+ have set rl_filename_completion_desired to a non-zero value. The basic
+ rl_filename_completion_function does this. */
+ matching_filenames = rl_filename_completion_desired;
+
+ if (matches == 0 || postprocess_matches (&matches, matching_filenames) == 0)
+ {
+ rl_ding ();
+ FREE (matches);
+ matches = (char **)0;
+ FREE (orig_text);
+ orig_text = (char *)0;
+ completion_changed_buffer = 0;
+ RL_UNSETSTATE(RL_STATE_COMPLETING);
+ return (0);
+ }
+
+ RL_UNSETSTATE(RL_STATE_COMPLETING);
+
+ for (match_list_size = 0; matches[match_list_size]; match_list_size++)
+ ;
+
+ if (match_list_size == 0)
+ {
+ rl_ding ();
+ FREE (matches);
+ matches = (char **)0;
+ match_list_index = 0;
+ completion_changed_buffer = 0;
+ return (0);
+ }
+
+ /* matches[0] is lcd if match_list_size > 1, but the circular buffer
+ code below should take care of it. */
+ if (*matches[0])
+ {
+ insert_match (matches[0], orig_start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char);
+ orig_end = orig_start + strlen (matches[0]);
+ completion_changed_buffer = STREQ (orig_text, matches[0]) == 0;
+ }
+
+ if (match_list_size > 1 && _rl_complete_show_all)
+ {
+ display_matches (matches);
+ /* If there are so many matches that the user has to be asked
+ whether or not he wants to see the matches, menu completion
+ is unwieldy. */
+ if (rl_completion_query_items > 0 && match_list_size >= rl_completion_query_items)
+ {
+ rl_ding ();
+ FREE (matches);
+ matches = (char **)0;
+ full_completion = 1;
+ return (0);
+ }
+ else if (_rl_menu_complete_prefix_first)
+ {
+ rl_ding ();
+ return (0);
+ }
+ }
+ else if (match_list_size <= 1)
+ {
+ append_to_match (matches[0], delimiter, quote_char, nontrivial_lcd);
+ full_completion = 1;
+ return (0);
+ }
+ else if (_rl_menu_complete_prefix_first && match_list_size > 1)
+ {
+ rl_ding ();
+ return (0);
+ }
+ }
+
+ /* Now we have the list of matches. Replace the text between
+ rl_line_buffer[orig_start] and rl_line_buffer[rl_point] with
+ matches[match_list_index], and add any necessary closing char. */
+
+ if (matches == 0 || match_list_size == 0)
+ {
+ rl_ding ();
+ FREE (matches);
+ matches = (char **)0;
+ completion_changed_buffer = 0;
+ return (0);
+ }
+
+ match_list_index += count;
+ if (match_list_index < 0)
+ {
+ while (match_list_index < 0)
+ match_list_index += match_list_size;
+ }
+ else
+ match_list_index %= match_list_size;
+
+ if (match_list_index == 0 && match_list_size > 1)
+ {
+ rl_ding ();
+ insert_match (matches[0], orig_start, MULT_MATCH, "e_char);
+ }
+ else
+ {
+ insert_match (matches[match_list_index], orig_start, SINGLE_MATCH, "e_char);
+ append_to_match (matches[match_list_index], delimiter, quote_char,
+ compare_match (orig_text, matches[match_list_index]));
+ }
+
+ completion_changed_buffer = 1;
+ return (0);
+}
+
+int
+rl_backward_menu_complete (int count, int key)
+{
+ /* Positive arguments to backward-menu-complete translate into negative
+ arguments for menu-complete, and vice versa. */
+ return (rl_menu_complete (-count, key));
+}
diff --git a/third_party/readline/config.h b/third_party/readline/config.h
new file mode 100644
index 000000000..8a83f6613
--- /dev/null
+++ b/third_party/readline/config.h
@@ -0,0 +1,312 @@
+/* config.h. Generated from config.h.in by configure. */
+/* config.h.in. Maintained by hand. */
+
+/* Template definitions for autoconf */
+
+/* These are set by AC_USE_SYSTEM_EXTENSIONS */
+#define __EXTENSIONS__ 1
+#define _ALL_SOURCE 1
+#define _GNU_SOURCE 1
+/* #undef _POSIX_SOURCE */
+/* #undef _POSIX_1_SOURCE */
+#define _POSIX_PTHREAD_SEMANTICS 1
+#define _TANDEM_SOURCE 1
+/* #undef _MINIX */
+
+/* Define NO_MULTIBYTE_SUPPORT to not compile in support for multibyte
+ characters, even if the OS supports them. */
+/* #undef NO_MULTIBYTE_SUPPORT */
+
+/* #undef _FILE_OFFSET_BITS */
+
+/* Characteristics of the compiler. */
+/* #undef inline */
+
+/* #undef sig_atomic_t */
+
+/* #undef size_t */
+
+/* #undef ssize_t */
+
+/* #undef const */
+
+/* #undef volatile */
+
+#define PROTOTYPES 1
+#define __PROTOTYPES 1
+
+/* #undef __CHAR_UNSIGNED__ */
+
+/* Define if the `S_IS*' macros in do not work properly. */
+/* #undef STAT_MACROS_BROKEN */
+
+/* Define if you have the chown function. */
+#define HAVE_CHOWN 1
+
+/* Define if you have the fcntl function. */
+#define HAVE_FCNTL 1
+
+/* Define if you have the fnmatch function. */
+#define HAVE_FNMATCH 1
+
+/* Define if you have the getpwent function. */
+#define HAVE_GETPWENT 1
+
+/* Define if you have the getpwnam function. */
+#define HAVE_GETPWNAM 1
+
+/* Define if you have the getpwuid function. */
+#define HAVE_GETPWUID 1
+
+/* Define if you have the gettimeofday function. */
+#define HAVE_GETTIMEOFDAY 1
+
+/* Define if you have the isascii function. */
+#define HAVE_ISASCII 1
+
+/* Define if you have the iswctype function. */
+#define HAVE_ISWCTYPE 1
+
+/* Define if you have the iswlower function. */
+#define HAVE_ISWLOWER 1
+
+/* Define if you have the iswupper function. */
+#define HAVE_ISWUPPER 1
+
+/* Define if you have the isxdigit function. */
+#define HAVE_ISXDIGIT 1
+
+/* Define if you have the kill function. */
+#define HAVE_KILL 1
+
+/* Define if you have the lstat function. */
+#define HAVE_LSTAT 1
+
+/* Define if you have the mbrlen function. */
+#define HAVE_MBRLEN 1
+
+/* Define if you have the mbrtowc function. */
+#define HAVE_MBRTOWC 1
+
+/* Define if you have the mbsrtowcs function. */
+#define HAVE_MBSRTOWCS 1
+
+/* Define if you have the memmove function. */
+#define HAVE_MEMMOVE 1
+
+/* Define if you have the pselect function. */
+#define HAVE_PSELECT 1
+
+/* Define if you have the putenv function. */
+#define HAVE_PUTENV 1
+
+/* Define if you have the readlink function. */
+#define HAVE_READLINK 1
+
+/* Define if you have the select function. */
+#define HAVE_SELECT 1
+
+/* Define if you have the setenv function. */
+#define HAVE_SETENV 1
+
+/* Define if you have the setitimer function. */
+#define HAVE_SETITIMER 1
+
+/* Define if you have the setlocale function. */
+#define HAVE_SETLOCALE 1
+
+/* Define if you have the strcasecmp function. */
+#define HAVE_STRCASECMP 1
+
+/* Define if you have the strcoll function. */
+#define HAVE_STRCOLL 1
+
+/* #undef STRCOLL_BROKEN */
+
+/* Define if you have the strpbrk function. */
+#define HAVE_STRPBRK 1
+
+/* Define if you have the sysconf function. */
+#define HAVE_SYSCONF 1
+
+/* Define if you have the tcgetattr function. */
+#define HAVE_TCGETATTR 1
+
+/* Define if you have the towlower function. */
+#define HAVE_TOWLOWER 1
+
+/* Define if you have the towupper function. */
+#define HAVE_TOWUPPER 1
+
+/* Define if you have the vsnprintf function. */
+#define HAVE_VSNPRINTF 1
+
+/* Define if you have the wcrtomb function. */
+#define HAVE_WCRTOMB 1
+
+/* Define if you have the wcscoll function. */
+#define HAVE_WCSCOLL 1
+
+/* Define if you have the wctype function. */
+#define HAVE_WCTYPE 1
+
+/* Define if you have the wcwidth function. */
+#define HAVE_WCWIDTH 1
+
+/* and whether it works */
+/* #undef WCWIDTH_BROKEN */
+
+/* Define if you have the header file. */
+#define HAVE_DIRENT_H 1
+
+/* Define if you have the header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define if you have the header file. */
+#define HAVE_LANGINFO_H 1
+
+/* Define if you have the header file. */
+/* #undef HAVE_LIBAUDIT_H */
+
+/* Define if you have the header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define if you have the header file. */
+#define HAVE_LOCALE_H 1
+
+/* Define if you have the header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define if you have the header file. */
+/* #undef HAVE_NDIR_H */
+
+/* Define if you have the "third_party/ncurses/termcap.h" header file. */
+#define HAVE_NCURSES_TERMCAP_H 1
+
+/* Define if you have the header file. */
+#define HAVE_PWD_H 1
+
+/* Define if you have the header file. */
+#define HAVE_STDARG_H 1
+
+/* Define if you have the header file. */
+#define HAVE_STDBOOL_H 1
+
+/* Define if you have the header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define if you have the header file. */
+#define HAVE_STRING_H 1
+
+/* Define if you have the header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define if you have the header file. */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define if you have the header file. */
+#define HAVE_SYS_FILE_H 1
+
+/* Define if you have the header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define if you have the header file. */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define if you have the header file. */
+/* #undef HAVE_SYS_PTE_H */
+
+/* Define if you have the header file. */
+/* #undef HAVE_SYS_PTEM_H */
+
+/* Define if you have the header file. */
+#define HAVE_SYS_SELECT_H 1
+
+/* Define if you have the header file. */
+/* #undef HAVE_SYS_STREAM_H */
+
+/* Define if you have the header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define if you have the header file. */
+/* #undef HAVE_TERMCAP_H */
+
+/* Define if you have the header file. */
+/* #undef HAVE_TERMIO_H */
+
+/* Define if you have the header file. */
+#define HAVE_TERMIOS_H 1
+
+/* Define if you have the header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define if you have the header file. */
+/* #undef HAVE_VARARGS_H */
+
+/* Define if you have the header file. */
+#define HAVE_WCHAR_H 1
+
+/* Define if you have the header file. */
+#define HAVE_WCTYPE_H 1
+
+#define HAVE_MBSTATE_T 1
+
+/* Define if you have wchar_t in . */
+#define HAVE_WCHAR_T 1
+
+/* Define if you have wctype_t in . */
+#define HAVE_WCTYPE_T 1
+
+/* Define if you have wint_t in . */
+#define HAVE_WINT_T 1
+
+/* Define if you have and nl_langinfo(CODESET). */
+#define HAVE_LANGINFO_CODESET 1
+
+/* Define if you have and it defines AUDIT_USER_TTY */
+#define HAVE_DECL_AUDIT_USER_TTY 0
+
+/* Definitions pulled in from aclocal.m4. */
+/* #undef GWINSZ_IN_SYS_IOCTL */
+
+#define STRUCT_WINSIZE_IN_SYS_IOCTL 1
+
+/* #undef STRUCT_WINSIZE_IN_TERMIOS */
+
+/* #undef TIOCSTAT_IN_SYS_IOCTL */
+
+#define FIONREAD_IN_SYS_IOCTL 1
+
+#define SPEED_T_IN_SYS_TYPES 1
+
+#define HAVE_GETPW_DECLS 1
+
+#define HAVE_STRUCT_DIRENT_D_INO 1
+
+/* #undef HAVE_STRUCT_DIRENT_D_FILENO */
+
+/* #undef HAVE_STRUCT_DIRENT_D_NAMLEN */
+
+#define HAVE_TIMEVAL 1
+
+/* #undef HAVE_BSD_SIGNALS */
+
+#define HAVE_POSIX_SIGNALS 1
+
+/* #undef HAVE_USG_SIGHOLD */
+
+/* #undef MUST_REINSTALL_SIGHANDLERS */
+
+#define HAVE_POSIX_SIGSETJMP 1
+
+/* #undef CTYPE_NON_ASCII */
+
+/* modify settings or make new ones based on what autoconf tells us. */
+
+/* Ultrix botches type-ahead when switching from canonical to
+ non-canonical mode, at least through version 4.3 */
+#if !defined (HAVE_TERMIOS_H) || !defined (HAVE_TCGETATTR) || defined (ultrix)
+# define TERMIOS_MISSING
+#endif
+
+/* VARARGS defines moved to rlstdc.h */
diff --git a/third_party/readline/display.c b/third_party/readline/display.c
new file mode 100644
index 000000000..62ad356fe
--- /dev/null
+++ b/third_party/readline/display.c
@@ -0,0 +1,3583 @@
+/* display.c -- readline redisplay facility. */
+
+/* Copyright (C) 1987-2022 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+
+#if defined (HAVE_UNISTD_H)
+# include
+#endif /* HAVE_UNISTD_H */
+
+#include "posixstat.h"
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#include
+
+#ifdef __MSDOS__
+# include
+#endif
+
+/* System-specific feature definitions and include files. */
+#include "rldefs.h"
+#include "rlmbutil.h"
+
+/* Termcap library stuff. */
+#include "tcap.h"
+
+/* Some standard library routines. */
+#include "readline.h"
+#include "history.h"
+
+#include "rlprivate.h"
+#include "xmalloc.h"
+
+#if !defined (strchr) && !defined (__STDC__)
+extern char *strchr (), *strrchr ();
+#endif /* !strchr && !__STDC__ */
+
+static void putc_face (int, int, char *);
+static void puts_face (const char *, const char *, int);
+static void norm_face (char *, int);
+
+static void update_line (char *, char *, char *, char *, int, int, int, int);
+static void space_to_eol (int);
+static void delete_chars (int);
+static void insert_some_chars (char *, int, int);
+static void open_some_spaces (int);
+static void cr (void);
+static void redraw_prompt (char *);
+static void _rl_move_cursor_relative (int, const char *, const char *);
+
+/* Values for FLAGS */
+#define PMT_MULTILINE 0x01
+
+static char *expand_prompt (char *, int, int *, int *, int *, int *);
+
+#define DEFAULT_LINE_BUFFER_SIZE 1024
+
+/* State of visible and invisible lines. */
+struct line_state
+ {
+ char *line;
+ char *lface;
+ int *lbreaks;
+ int lbsize;
+#if defined (HANDLE_MULTIBYTE)
+ int wbsize;
+ int *wrapped_line;
+#endif
+ };
+
+/* The line display buffers. One is the line currently displayed on
+ the screen. The other is the line about to be displayed. */
+static struct line_state line_state_array[2];
+static struct line_state *line_state_visible = &line_state_array[0];
+static struct line_state *line_state_invisible = &line_state_array[1];
+static int line_structures_initialized = 0;
+
+/* Backwards-compatible names. */
+#define inv_lbreaks (line_state_invisible->lbreaks)
+#define inv_lbsize (line_state_invisible->lbsize)
+#define vis_lbreaks (line_state_visible->lbreaks)
+#define vis_lbsize (line_state_visible->lbsize)
+
+#define visible_line (line_state_visible->line)
+#define vis_face (line_state_visible->lface)
+#define invisible_line (line_state_invisible->line)
+#define inv_face (line_state_invisible->lface)
+
+#if defined (HANDLE_MULTIBYTE)
+static int _rl_col_width (const char *, int, int, int);
+#else
+# define _rl_col_width(l, s, e, f) (((e) <= (s)) ? 0 : (e) - (s))
+#endif
+
+/* Heuristic used to decide whether it is faster to move from CUR to NEW
+ by backing up or outputting a carriage return and moving forward. CUR
+ and NEW are either both buffer positions or absolute screen positions. */
+#define CR_FASTER(new, cur) (((new) + 1) < ((cur) - (new)))
+
+/* _rl_last_c_pos is an absolute cursor position in multibyte locales and a
+ buffer index in others. This macro is used when deciding whether the
+ current cursor position is in the middle of a prompt string containing
+ invisible characters. XXX - might need to take `modmark' into account. */
+/* XXX - only valid when tested against _rl_last_c_pos; buffer indices need
+ to use prompt_last_invisible directly. */
+#define PROMPT_ENDING_INDEX \
+ ((MB_CUR_MAX > 1 && rl_byte_oriented == 0) ? prompt_physical_chars : prompt_last_invisible+1)
+
+#define FACE_NORMAL '0'
+#define FACE_STANDOUT '1'
+#define FACE_INVALID ((char)1)
+
+/* **************************************************************** */
+/* */
+/* Display stuff */
+/* */
+/* **************************************************************** */
+
+/* This is the stuff that is hard for me. I never seem to write good
+ display routines in C. Let's see how I do this time. */
+
+/* (PWP) Well... Good for a simple line updater, but totally ignores
+ the problems of input lines longer than the screen width.
+
+ update_line and the code that calls it makes a multiple line,
+ automatically wrapping line update. Careful attention needs
+ to be paid to the vertical position variables. */
+
+/* Keep two buffers; one which reflects the current contents of the
+ screen, and the other to draw what we think the new contents should
+ be. Then compare the buffers, and make whatever changes to the
+ screen itself that we should. Finally, make the buffer that we
+ just drew into be the one which reflects the current contents of the
+ screen, and place the cursor where it belongs.
+
+ Commands that want to can fix the display themselves, and then let
+ this function know that the display has been fixed by setting the
+ RL_DISPLAY_FIXED variable. This is good for efficiency. */
+
+/* Application-specific redisplay function. */
+rl_voidfunc_t *rl_redisplay_function = rl_redisplay;
+
+/* Global variables declared here. */
+/* What YOU turn on when you have handled all redisplay yourself. */
+int rl_display_fixed = 0;
+
+/* The stuff that gets printed out before the actual text of the line.
+ This is usually pointing to rl_prompt. */
+char *rl_display_prompt = (char *)NULL;
+
+/* Variables used to include the editing mode in the prompt. */
+char *_rl_emacs_mode_str;
+int _rl_emacs_modestr_len;
+
+char *_rl_vi_ins_mode_str;
+int _rl_vi_ins_modestr_len;
+
+char *_rl_vi_cmd_mode_str;
+int _rl_vi_cmd_modestr_len;
+
+/* Pseudo-global variables declared here. */
+
+/* Hints for other parts of readline to give to the display engine. */
+int _rl_suppress_redisplay = 0;
+int _rl_want_redisplay = 0;
+
+/* The visible cursor position. If you print some text, adjust this. */
+/* NOTE: _rl_last_c_pos is used as a buffer index when not in a locale
+ supporting multibyte characters, and an absolute cursor position when
+ in such a locale. This is an artifact of the donated multibyte support.
+ Care must be taken when modifying its value. */
+int _rl_last_c_pos = 0;
+int _rl_last_v_pos = 0;
+
+/* Number of physical lines consumed by the current line buffer currently
+ on screen minus 1. */
+int _rl_vis_botlin = 0;
+
+static int _rl_quick_redisplay = 0;
+
+/* This is a hint update_line gives to rl_redisplay that it has adjusted the
+ value of _rl_last_c_pos *and* taken the presence of any invisible chars in
+ the prompt into account. rl_redisplay notes this and does not do the
+ adjustment itself. */
+static int cpos_adjusted;
+
+/* The index into the line buffer corresponding to the cursor position */
+static int cpos_buffer_position;
+
+/* A flag to note when we're displaying the first line of the prompt */
+static int displaying_prompt_first_line;
+/* The number of multibyte characters in the prompt, if any */
+static int prompt_multibyte_chars;
+
+static int _rl_inv_botlin = 0;
+
+/* Variables used only in this file. */
+/* The last left edge of text that was displayed. This is used when
+ doing horizontal scrolling. It shifts in thirds of a screenwidth. */
+static int last_lmargin;
+
+/* A buffer for `modeline' messages. */
+static char *msg_buf = 0;
+static int msg_bufsiz = 0;
+
+/* Non-zero forces the redisplay even if we thought it was unnecessary. */
+static int forced_display;
+
+/* Default and initial buffer size. Can grow. */
+static int line_size = 0;
+
+/* Set to a non-zero value if horizontal scrolling has been enabled
+ automatically because the terminal was resized to height 1. */
+static int horizontal_scrolling_autoset = 0; /* explicit initialization */
+
+/* Variables to keep track of the expanded prompt string, which may
+ include invisible characters. */
+
+static char *local_prompt, *local_prompt_prefix;
+static int local_prompt_len;
+static int prompt_prefix_length;
+/* Number of chars in the buffer that contribute to visible chars on the screen.
+ This might be different from the number of physical chars in the presence
+ of multibyte characters */
+static int prompt_visible_length;
+
+/* The number of invisible characters in the line currently being
+ displayed on the screen. */
+static int visible_wrap_offset;
+
+/* The number of invisible characters in the prompt string. Static so it
+ can be shared between rl_redisplay and update_line */
+static int wrap_offset;
+
+/* The index of the last invisible character in the prompt string. */
+static int prompt_last_invisible;
+
+/* The length (buffer offset) of the first line of the last (possibly
+ multi-line) buffer displayed on the screen. */
+static int visible_first_line_len;
+
+/* Number of invisible characters on the first physical line of the prompt.
+ Only valid when the number of physical characters in the prompt exceeds
+ (or is equal to) _rl_screenwidth. */
+static int prompt_invis_chars_first_line;
+
+static int prompt_last_screen_line;
+
+static int prompt_physical_chars;
+
+/* An array of indexes into the prompt string where we will break physical
+ screen lines. It's easier to compute in expand_prompt and use later in
+ rl_redisplay instead of having rl_redisplay try to guess about invisible
+ characters in the prompt or use heuristics about where they are. */
+static int *local_prompt_newlines;
+
+/* set to a non-zero value by rl_redisplay if we are marking modified history
+ lines and the current line is so marked. */
+static int modmark;
+
+static int line_totbytes;
+
+/* Variables to save and restore prompt and display information. */
+
+/* These are getting numerous enough that it's time to create a struct. */
+
+static char *saved_local_prompt;
+static char *saved_local_prefix;
+static int *saved_local_prompt_newlines;
+
+static int saved_last_invisible;
+static int saved_visible_length;
+static int saved_prefix_length;
+static int saved_local_length;
+static int saved_invis_chars_first_line;
+static int saved_physical_chars;
+
+/* Return a string indicating the editing mode, for use in the prompt. */
+
+static char *
+prompt_modestr (int *lenp)
+{
+ if (rl_editing_mode == emacs_mode)
+ {
+ if (lenp)
+ *lenp = _rl_emacs_mode_str ? _rl_emacs_modestr_len : RL_EMACS_MODESTR_DEFLEN;
+ return _rl_emacs_mode_str ? _rl_emacs_mode_str : RL_EMACS_MODESTR_DEFAULT;
+ }
+ else if (_rl_keymap == vi_insertion_keymap)
+ {
+ if (lenp)
+ *lenp = _rl_vi_ins_mode_str ? _rl_vi_ins_modestr_len : RL_VI_INS_MODESTR_DEFLEN;
+ return _rl_vi_ins_mode_str ? _rl_vi_ins_mode_str : RL_VI_INS_MODESTR_DEFAULT; /* vi insert mode */
+ }
+ else
+ {
+ if (lenp)
+ *lenp = _rl_vi_cmd_mode_str ? _rl_vi_cmd_modestr_len : RL_VI_CMD_MODESTR_DEFLEN;
+ return _rl_vi_cmd_mode_str ? _rl_vi_cmd_mode_str : RL_VI_CMD_MODESTR_DEFAULT; /* vi command mode */
+ }
+}
+
+/* Expand the prompt string S and return the number of visible
+ characters in *LP, if LP is not null. This is currently more-or-less
+ a placeholder for expansion. LIP, if non-null is a place to store the
+ index of the last invisible character in the returned string. NIFLP,
+ if non-zero, is a place to store the number of invisible characters in
+ the first prompt line. The previous are used as byte counts -- indexes
+ into a character buffer. *VLP gets the number of physical characters in
+ the expanded prompt (visible length) */
+
+/* Current implementation:
+ \001 (^A) start non-visible characters
+ \002 (^B) end non-visible characters
+ all characters except \001 and \002 (following a \001) are copied to
+ the returned string; all characters except those between \001 and
+ \002 are assumed to be `visible'. */
+
+/* Possible values for FLAGS:
+ PMT_MULTILINE caller indicates that this is part of a multiline prompt
+*/
+
+/* This approximates the number of lines the prompt will take when displayed */
+#define APPROX_DIV(n, d) (((n) < (d)) ? 1 : ((n) / (d)) + 1)
+
+static char *
+expand_prompt (char *pmt, int flags, int *lp, int *lip, int *niflp, int *vlp)
+{
+ char *r, *ret, *p, *igstart, *nprompt, *ms;
+ int l, rl, last, ignoring, ninvis, invfl, invflset, ind, pind, physchars;
+ int mlen, newlines, newlines_guess, bound, can_add_invis;
+ int mb_cur_max;
+
+ /* We only expand the mode string for the last line of a multiline prompt
+ (a prompt with embedded newlines). */
+ ms = (((pmt == rl_prompt) ^ (flags & PMT_MULTILINE)) && _rl_show_mode_in_prompt) ? prompt_modestr (&mlen) : 0;
+ if (ms)
+ {
+ l = strlen (pmt);
+ nprompt = (char *)xmalloc (l + mlen + 1);
+ memcpy (nprompt, ms, mlen);
+ strcpy (nprompt + mlen, pmt);
+ }
+ else
+ nprompt = pmt;
+
+ can_add_invis = 0;
+ mb_cur_max = MB_CUR_MAX;
+
+ if (_rl_screenwidth == 0)
+ _rl_get_screen_size (0, 0); /* avoid division by zero */
+
+ /* Short-circuit if we can. We can do this if we are treating the prompt as
+ a sequence of bytes and there are no invisible characters in the prompt
+ to deal with. Since we populate local_prompt_newlines, we have to run
+ through the rest of the function if this prompt looks like it's going to
+ be longer than one screen line. */
+ if ((mb_cur_max <= 1 || rl_byte_oriented) && strchr (nprompt, RL_PROMPT_START_IGNORE) == 0)
+ {
+ l = strlen (nprompt);
+ if (l < (_rl_screenwidth > 0 ? _rl_screenwidth : 80))
+ {
+ r = (nprompt == pmt) ? savestring (pmt) : nprompt;
+ if (lp)
+ *lp = l;
+ if (lip)
+ *lip = 0;
+ if (niflp)
+ *niflp = 0;
+ if (vlp)
+ *vlp = l;
+
+ local_prompt_newlines = (int *) xrealloc (local_prompt_newlines, sizeof (int) * 2);
+ local_prompt_newlines[0] = 0;
+ local_prompt_newlines[1] = -1;
+
+ return r;
+ }
+ }
+
+ l = strlen (nprompt); /* XXX */
+ r = ret = (char *)xmalloc (l + 1);
+
+ /* Guess at how many screen lines the prompt will take to size the array that
+ keeps track of where the line wraps happen */
+ newlines_guess = (_rl_screenwidth > 0) ? APPROX_DIV(l, _rl_screenwidth) : APPROX_DIV(l, 80);
+ local_prompt_newlines = (int *) xrealloc (local_prompt_newlines, sizeof (int) * (newlines_guess + 1));
+ local_prompt_newlines[newlines = 0] = 0;
+ for (rl = 1; rl <= newlines_guess; rl++)
+ local_prompt_newlines[rl] = -1;
+
+ rl = physchars = 0; /* mode string now part of nprompt */
+ invfl = 0; /* invisible chars in first line of prompt */
+ invflset = 0; /* we only want to set invfl once */
+ igstart = 0; /* we're not ignoring any characters yet */
+
+ for (ignoring = last = ninvis = 0, p = nprompt; p && *p; p++)
+ {
+ /* This code strips the invisible character string markers
+ RL_PROMPT_START_IGNORE and RL_PROMPT_END_IGNORE */
+ if (ignoring == 0 && *p == RL_PROMPT_START_IGNORE) /* XXX - check ignoring? */
+ {
+ ignoring = 1;
+ igstart = p;
+ continue;
+ }
+ else if (ignoring && *p == RL_PROMPT_END_IGNORE)
+ {
+ ignoring = 0;
+ /* If we have a run of invisible characters, adjust local_prompt_newlines
+ to add them, since update_line expects them to be counted before
+ wrapping the line. */
+ if (can_add_invis)
+ {
+ local_prompt_newlines[newlines] = r - ret;
+ /* If we're adding to the number of invisible characters on the
+ first line of the prompt, but we've already set the number of
+ invisible characters on that line, we need to adjust the
+ counter. */
+ if (invflset && newlines == 1)
+ invfl = ninvis;
+ }
+ if (p != (igstart + 1))
+ last = r - ret - 1;
+ continue;
+ }
+ else
+ {
+#if defined (HANDLE_MULTIBYTE)
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ {
+ pind = p - nprompt;
+ ind = _rl_find_next_mbchar (nprompt, pind, 1, MB_FIND_NONZERO);
+ l = ind - pind;
+ while (l--)
+ *r++ = *p++;
+ if (!ignoring)
+ {
+ /* rl ends up being assigned to prompt_visible_length,
+ which is the number of characters in the buffer that
+ contribute to characters on the screen, which might
+ not be the same as the number of physical characters
+ on the screen in the presence of multibyte characters */
+ rl += ind - pind;
+ physchars += _rl_col_width (nprompt, pind, ind, 0);
+ }
+ else
+ ninvis += ind - pind;
+ p--; /* compensate for later increment */
+ }
+ else
+#endif
+ {
+ *r++ = *p;
+ if (!ignoring)
+ {
+ rl++; /* visible length byte counter */
+ physchars++;
+ }
+ else
+ ninvis++; /* invisible chars byte counter */
+ }
+
+ if (invflset == 0 && physchars >= _rl_screenwidth)
+ {
+ invfl = ninvis;
+ invflset = 1;
+ }
+
+ if (physchars >= (bound = (newlines + 1) * _rl_screenwidth) && local_prompt_newlines[newlines+1] == -1)
+ {
+ int new;
+ if (physchars > bound) /* should rarely happen */
+ {
+#if defined (HANDLE_MULTIBYTE)
+ *r = '\0'; /* need null-termination for strlen */
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ new = _rl_find_prev_mbchar (ret, r - ret, MB_FIND_ANY);
+ else
+#endif
+ new = r - ret - (physchars - bound); /* XXX */
+ }
+ else
+ new = r - ret;
+ local_prompt_newlines[++newlines] = new;
+ }
+
+ /* What if a physical character of width >= 2 is split? There is
+ code that wraps before the physical screen width if the character
+ width would exceed it, but it needs to be checked against this
+ code and local_prompt_newlines[]. */
+ if (ignoring == 0)
+ can_add_invis = (physchars == bound);
+ }
+ }
+
+ if (rl <= _rl_screenwidth)
+ invfl = ninvis;
+
+ *r = '\0';
+ if (lp)
+ *lp = rl;
+ if (lip)
+ *lip = last;
+ if (niflp)
+ *niflp = invfl;
+ if (vlp)
+ *vlp = physchars;
+
+ if (nprompt != pmt)
+ xfree (nprompt);
+
+ return ret;
+}
+
+/* Just strip out RL_PROMPT_START_IGNORE and RL_PROMPT_END_IGNORE from
+ PMT and return the rest of PMT. */
+char *
+_rl_strip_prompt (char *pmt)
+{
+ char *ret;
+
+ ret = expand_prompt (pmt, 0, (int *)NULL, (int *)NULL, (int *)NULL, (int *)NULL);
+ return ret;
+}
+
+void
+_rl_reset_prompt (void)
+{
+ rl_visible_prompt_length = rl_expand_prompt (rl_prompt);
+}
+
+/*
+ * Expand the prompt string into the various display components, if
+ * necessary.
+ *
+ * local_prompt = expanded last line of string in rl_display_prompt
+ * (portion after the final newline)
+ * local_prompt_prefix = portion before last newline of rl_display_prompt,
+ * expanded via expand_prompt
+ * prompt_visible_length = number of visible characters in local_prompt
+ * prompt_prefix_length = number of visible characters in local_prompt_prefix
+ *
+ * It also tries to keep track of the number of invisible characters in the
+ * prompt string, and where they are.
+ *
+ * This function is called once per call to readline(). It may also be
+ * called arbitrarily to expand the primary prompt.
+ *
+ * The return value is the number of visible characters on the last line
+ * of the (possibly multi-line) prompt. In this case, multi-line means
+ * there are embedded newlines in the prompt string itself, not that the
+ * number of physical characters exceeds the screen width and the prompt
+ * wraps.
+ */
+int
+rl_expand_prompt (char *prompt)
+{
+ char *p, *t;
+ int c;
+
+ /* Clear out any saved values. */
+ FREE (local_prompt);
+ FREE (local_prompt_prefix);
+
+ local_prompt = local_prompt_prefix = (char *)0;
+ local_prompt_len = 0;
+ prompt_last_invisible = prompt_invis_chars_first_line = 0;
+ prompt_visible_length = prompt_physical_chars = 0;
+
+ if (prompt == 0 || *prompt == 0)
+ return (0);
+
+ p = strrchr (prompt, '\n');
+ if (p == 0)
+ {
+ /* The prompt is only one logical line, though it might wrap. */
+ local_prompt = expand_prompt (prompt, 0, &prompt_visible_length,
+ &prompt_last_invisible,
+ &prompt_invis_chars_first_line,
+ &prompt_physical_chars);
+ local_prompt_prefix = (char *)0;
+ local_prompt_len = local_prompt ? strlen (local_prompt) : 0;
+ return (prompt_visible_length);
+ }
+ else
+ {
+ /* The prompt spans multiple lines. */
+ t = ++p;
+ c = *t; *t = '\0';
+ /* The portion of the prompt string up to and including the
+ final newline is now null-terminated. */
+ local_prompt_prefix = expand_prompt (prompt, PMT_MULTILINE,
+ &prompt_prefix_length,
+ (int *)NULL,
+ (int *)NULL,
+ (int *)NULL);
+ *t = c;
+
+ local_prompt = expand_prompt (p, PMT_MULTILINE,
+ &prompt_visible_length,
+ &prompt_last_invisible,
+ &prompt_invis_chars_first_line,
+ &prompt_physical_chars);
+ local_prompt_len = local_prompt ? strlen (local_prompt) : 0;
+ return (prompt_prefix_length);
+ }
+}
+
+/* Allocate the various line structures, making sure they can hold MINSIZE
+ bytes. If the existing line size can accommodate MINSIZE bytes, don't do
+ anything. */
+static void
+realloc_line (int minsize)
+{
+ int minimum_size;
+ int newsize, delta;
+
+ minimum_size = DEFAULT_LINE_BUFFER_SIZE;
+ if (minsize < minimum_size)
+ minsize = minimum_size;
+ if (minsize <= _rl_screenwidth) /* XXX - for gdb */
+ minsize = _rl_screenwidth + 1;
+ if (line_size >= minsize)
+ return;
+
+ newsize = minimum_size;
+ while (newsize < minsize)
+ newsize *= 2;
+
+ visible_line = (char *)xrealloc (visible_line, newsize);
+ vis_face = (char *)xrealloc (vis_face, newsize);
+
+ invisible_line = (char *)xrealloc (invisible_line, newsize);
+ inv_face = (char *)xrealloc (inv_face, newsize);
+
+ delta = newsize - line_size;
+ memset (visible_line + line_size, 0, delta);
+ memset (vis_face + line_size, FACE_NORMAL, delta);
+ memset (invisible_line + line_size, 1, delta);
+ memset (inv_face + line_size, FACE_INVALID, delta);
+
+ line_size = newsize;
+}
+
+/* Initialize the VISIBLE_LINE and INVISIBLE_LINE arrays, and their associated
+ arrays of line break markers. MINSIZE is the minimum size of VISIBLE_LINE
+ and INVISIBLE_LINE; if it is greater than LINE_SIZE, LINE_SIZE is
+ increased. If the lines have already been allocated, this ensures that
+ they can hold at least MINSIZE characters. */
+static void
+init_line_structures (int minsize)
+{
+ if (invisible_line == 0) /* initialize it */
+ {
+ if (line_size > minsize)
+ minsize = line_size;
+ }
+ realloc_line (minsize);
+
+ if (vis_lbreaks == 0)
+ {
+ /* should be enough. */
+ inv_lbsize = vis_lbsize = 256;
+
+#if defined (HANDLE_MULTIBYTE)
+ line_state_visible->wbsize = vis_lbsize;
+ line_state_visible->wrapped_line = (int *)xmalloc (line_state_visible->wbsize * sizeof (int));
+
+ line_state_invisible->wbsize = inv_lbsize;
+ line_state_invisible->wrapped_line = (int *)xmalloc (line_state_invisible->wbsize * sizeof (int));
+#endif
+
+ inv_lbreaks = (int *)xmalloc (inv_lbsize * sizeof (int));
+ vis_lbreaks = (int *)xmalloc (vis_lbsize * sizeof (int));
+ inv_lbreaks[0] = vis_lbreaks[0] = 0;
+ }
+
+ line_structures_initialized = 1;
+}
+
+/* Convenience functions to add chars to the invisible line that update the
+ face information at the same time. */
+static void /* XXX - change this */
+invis_addc (int *outp, char c, char face)
+{
+ realloc_line (*outp + 1);
+ invisible_line[*outp] = c;
+ inv_face[*outp] = face;
+ *outp += 1;
+}
+
+static void
+invis_adds (int *outp, const char *str, int n, char face)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ invis_addc (outp, str[i], face);
+}
+
+static void
+invis_nul (int *outp)
+{
+ invis_addc (outp, '\0', 0);
+ *outp -= 1;
+}
+
+static void
+set_active_region (int *beg, int *end)
+{
+ if (rl_point >= 0 && rl_point <= rl_end && rl_mark >= 0 && rl_mark <= rl_end)
+ {
+ *beg = (rl_mark < rl_point) ? rl_mark : rl_point;
+ *end = (rl_mark < rl_point) ? rl_point : rl_mark;
+ }
+}
+
+/* Do whatever tests are necessary and tell update_line that it can do a
+ quick, dumb redisplay on the assumption that there are so many
+ differences between the old and new lines that it would be a waste to
+ compute all the differences.
+ Right now, it just sets _rl_quick_redisplay if the current visible line
+ is a single line (so we don't have to move vertically or mess with line
+ wrapping). */
+void
+_rl_optimize_redisplay (void)
+{
+ if (_rl_vis_botlin == 0)
+ _rl_quick_redisplay = 1;
+}
+
+/* Basic redisplay algorithm. See comments inline. */
+void
+rl_redisplay (void)
+{
+ int in, out, c, linenum, cursor_linenum;
+ int inv_botlin, lb_botlin, lb_linenum, o_cpos;
+ int newlines, lpos, temp, n0, num, prompt_lines_estimate;
+ char *prompt_this_line;
+ char cur_face;
+ int hl_begin, hl_end;
+ int mb_cur_max = MB_CUR_MAX;
+#if defined (HANDLE_MULTIBYTE)
+ WCHAR_T wc;
+ size_t wc_bytes;
+ int wc_width;
+ mbstate_t ps;
+ int _rl_wrapped_multicolumn = 0;
+#endif
+
+ if (_rl_echoing_p == 0)
+ return;
+
+ /* Block keyboard interrupts because this function manipulates global
+ data structures. */
+ _rl_block_sigint ();
+ RL_SETSTATE (RL_STATE_REDISPLAYING);
+
+ cur_face = FACE_NORMAL;
+ /* Can turn this into an array for multiple highlighted objects in addition
+ to the region */
+ hl_begin = hl_end = -1;
+
+ if (rl_mark_active_p ())
+ set_active_region (&hl_begin, &hl_end);
+
+ if (!rl_display_prompt)
+ rl_display_prompt = "";
+
+ if (line_structures_initialized == 0)
+ {
+ init_line_structures (0);
+ rl_on_new_line ();
+ }
+ else if (line_size <= _rl_screenwidth)
+ init_line_structures (_rl_screenwidth + 1);
+
+ /* Enable horizontal scrolling automatically for terminals of height 1
+ where wrapping lines doesn't work. Disable it as soon as the terminal
+ height is increased again if it was automatically enabled. */
+ if (_rl_screenheight <= 1)
+ {
+ if (_rl_horizontal_scroll_mode == 0)
+ horizontal_scrolling_autoset = 1;
+ _rl_horizontal_scroll_mode = 1;
+ }
+ else if (horizontal_scrolling_autoset)
+ _rl_horizontal_scroll_mode = 0;
+
+ /* Draw the line into the buffer. */
+ cpos_buffer_position = -1;
+
+ prompt_multibyte_chars = prompt_visible_length - prompt_physical_chars;
+
+ out = inv_botlin = 0;
+
+ /* Mark the line as modified or not. We only do this for history
+ lines. */
+ modmark = 0;
+ if (_rl_mark_modified_lines && current_history () && rl_undo_list)
+ {
+ invis_addc (&out, '*', cur_face);
+ invis_nul (&out);
+ modmark = 1;
+ }
+
+ /* If someone thought that the redisplay was handled, but the currently
+ visible line has a different modification state than the one about
+ to become visible, then correct the caller's misconception. */
+ if (visible_line[0] != invisible_line[0])
+ rl_display_fixed = 0;
+
+ /* If the prompt to be displayed is the `primary' readline prompt (the
+ one passed to readline()), use the values we have already expanded.
+ If not, use what's already in rl_display_prompt. WRAP_OFFSET is the
+ number of non-visible characters (bytes) in the prompt string. */
+ /* This is where we output the characters in the prompt before the last
+ newline, if any. If there aren't any embedded newlines, we don't
+ write anything. Copy the last line of the prompt string into the line in
+ any case */
+ if (rl_display_prompt == rl_prompt || local_prompt)
+ {
+ if (local_prompt_prefix && forced_display)
+ _rl_output_some_chars (local_prompt_prefix, strlen (local_prompt_prefix));
+
+ if (local_prompt_len > 0)
+ invis_adds (&out, local_prompt, local_prompt_len, cur_face);
+ invis_nul (&out);
+ wrap_offset = local_prompt_len - prompt_visible_length;
+ }
+ else
+ {
+ int pmtlen;
+ prompt_this_line = strrchr (rl_display_prompt, '\n');
+ if (!prompt_this_line)
+ prompt_this_line = rl_display_prompt;
+ else
+ {
+ prompt_this_line++;
+ pmtlen = prompt_this_line - rl_display_prompt; /* temp var */
+ if (forced_display)
+ {
+ _rl_output_some_chars (rl_display_prompt, pmtlen);
+ /* Make sure we are at column zero even after a newline,
+ regardless of the state of terminal output processing. */
+ if (pmtlen < 2 || prompt_this_line[-2] != '\r')
+ cr ();
+ }
+ }
+
+ prompt_physical_chars = pmtlen = strlen (prompt_this_line); /* XXX */
+ invis_adds (&out, prompt_this_line, pmtlen, cur_face);
+ invis_nul (&out);
+ wrap_offset = prompt_invis_chars_first_line = 0;
+ }
+
+#if defined (HANDLE_MULTIBYTE)
+#define CHECK_INV_LBREAKS() \
+ do { \
+ if (newlines >= (inv_lbsize - 2)) \
+ { \
+ inv_lbsize *= 2; \
+ inv_lbreaks = (int *)xrealloc (inv_lbreaks, inv_lbsize * sizeof (int)); \
+ } \
+ if (newlines >= (line_state_invisible->wbsize - 2)) \
+ { \
+ line_state_invisible->wbsize *= 2; \
+ line_state_invisible->wrapped_line = (int *)xrealloc (line_state_invisible->wrapped_line, line_state_invisible->wbsize * sizeof(int)); \
+ } \
+ } while (0)
+#else
+#define CHECK_INV_LBREAKS() \
+ do { \
+ if (newlines >= (inv_lbsize - 2)) \
+ { \
+ inv_lbsize *= 2; \
+ inv_lbreaks = (int *)xrealloc (inv_lbreaks, inv_lbsize * sizeof (int)); \
+ } \
+ } while (0)
+#endif /* !HANDLE_MULTIBYTE */
+
+#if defined (HANDLE_MULTIBYTE)
+#define CHECK_LPOS() \
+ do { \
+ lpos++; \
+ if (lpos >= _rl_screenwidth) \
+ { \
+ if (newlines >= (inv_lbsize - 2)) \
+ { \
+ inv_lbsize *= 2; \
+ inv_lbreaks = (int *)xrealloc (inv_lbreaks, inv_lbsize * sizeof (int)); \
+ } \
+ inv_lbreaks[++newlines] = out; \
+ if (newlines >= (line_state_invisible->wbsize - 2)) \
+ { \
+ line_state_invisible->wbsize *= 2; \
+ line_state_invisible->wrapped_line = (int *)xrealloc (line_state_invisible->wrapped_line, line_state_invisible->wbsize * sizeof(int)); \
+ } \
+ line_state_invisible->wrapped_line[newlines] = _rl_wrapped_multicolumn; \
+ lpos = 0; \
+ } \
+ } while (0)
+#else
+#define CHECK_LPOS() \
+ do { \
+ lpos++; \
+ if (lpos >= _rl_screenwidth) \
+ { \
+ if (newlines >= (inv_lbsize - 2)) \
+ { \
+ inv_lbsize *= 2; \
+ inv_lbreaks = (int *)xrealloc (inv_lbreaks, inv_lbsize * sizeof (int)); \
+ } \
+ inv_lbreaks[++newlines] = out; \
+ lpos = 0; \
+ } \
+ } while (0)
+#endif
+
+ /* inv_lbreaks[i] is where line i starts in the buffer. */
+ inv_lbreaks[newlines = 0] = 0;
+ /* lpos is a physical cursor position, so it needs to be adjusted by the
+ number of invisible characters in the prompt, per line. We compute
+ the line breaks in the prompt string in expand_prompt, taking invisible
+ characters into account, and if lpos exceeds the screen width, we copy
+ the data in the loop below. */
+ lpos = prompt_physical_chars + modmark;
+
+#if defined (HANDLE_MULTIBYTE)
+ memset (line_state_invisible->wrapped_line, 0, line_state_invisible->wbsize * sizeof (int));
+ num = 0;
+#endif
+
+ /* prompt_invis_chars_first_line is the number of invisible characters (bytes)
+ in the first physical line of the prompt.
+ wrap_offset - prompt_invis_chars_first_line is usually the number of
+ invis chars on the second (or, more generally, last) line. */
+
+ /* This is zero-based, used to set the newlines */
+ prompt_lines_estimate = lpos / _rl_screenwidth;
+
+ /* what if lpos is already >= _rl_screenwidth before we start drawing the
+ contents of the command line? */
+ if (lpos >= _rl_screenwidth)
+ {
+ temp = 0;
+
+ /* first copy the linebreaks array we computed in expand_prompt */
+ while (local_prompt_newlines[newlines+1] != -1)
+ {
+ temp = local_prompt_newlines[newlines+1];
+ inv_lbreaks[++newlines] = temp;
+ }
+
+ /* Now set lpos from the last newline */
+ if (mb_cur_max > 1 && rl_byte_oriented == 0 && prompt_multibyte_chars > 0)
+ lpos = _rl_col_width (local_prompt, temp, local_prompt_len, 1) - (wrap_offset - prompt_invis_chars_first_line);
+ else
+ lpos -= (_rl_screenwidth * newlines);
+ }
+
+ prompt_last_screen_line = newlines;
+
+ /* Draw the rest of the line (after the prompt) into invisible_line, keeping
+ track of where the cursor is (cpos_buffer_position), the number of the
+ line containing the cursor (lb_linenum), the last line number (lb_botlin
+ and inv_botlin).
+ It maintains an array of line breaks for display (inv_lbreaks).
+ This handles expanding tabs for display and displaying meta characters. */
+ lb_linenum = 0;
+#if defined (HANDLE_MULTIBYTE)
+ in = 0;
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ {
+ memset (&ps, 0, sizeof (mbstate_t));
+ if (_rl_utf8locale && UTF8_SINGLEBYTE(rl_line_buffer[0]))
+ {
+ wc = (WCHAR_T)rl_line_buffer[0];
+ wc_bytes = 1;
+ }
+ else
+ wc_bytes = MBRTOWC (&wc, rl_line_buffer, rl_end, &ps);
+ }
+ else
+ wc_bytes = 1;
+ while (in < rl_end)
+#else
+ for (in = 0; in < rl_end; in++)
+#endif
+ {
+ if (in == hl_begin)
+ cur_face = FACE_STANDOUT;
+ else if (in == hl_end)
+ cur_face = FACE_NORMAL;
+
+ c = (unsigned char)rl_line_buffer[in];
+
+#if defined (HANDLE_MULTIBYTE)
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ {
+ if (MB_INVALIDCH (wc_bytes))
+ {
+ /* Byte sequence is invalid or shortened. Assume that the
+ first byte represents a character. */
+ wc_bytes = 1;
+ /* Assume that a character occupies a single column. */
+ wc_width = 1;
+ memset (&ps, 0, sizeof (mbstate_t));
+ }
+ else if (MB_NULLWCH (wc_bytes))
+ break; /* Found '\0' */
+ else
+ {
+ temp = WCWIDTH (wc);
+ wc_width = (temp >= 0) ? temp : 1;
+ }
+ }
+#endif
+
+ if (in == rl_point)
+ {
+ cpos_buffer_position = out;
+ lb_linenum = newlines;
+ }
+
+#if defined (HANDLE_MULTIBYTE)
+ if (META_CHAR (c) && _rl_output_meta_chars == 0) /* XXX - clean up */
+#else
+ if (META_CHAR (c))
+#endif
+ {
+ if (_rl_output_meta_chars == 0)
+ {
+ char obuf[5];
+ int olen;
+
+ olen = sprintf (obuf, "\\%o", c);
+
+ if (lpos + olen >= _rl_screenwidth)
+ {
+ temp = _rl_screenwidth - lpos;
+ CHECK_INV_LBREAKS ();
+ inv_lbreaks[++newlines] = out + temp;
+#if defined (HANDLE_MULTIBYTE)
+ line_state_invisible->wrapped_line[newlines] = _rl_wrapped_multicolumn;
+#endif
+ lpos = olen - temp;
+ }
+ else
+ lpos += olen;
+
+ for (temp = 0; temp < olen; temp++)
+ {
+ invis_addc (&out, obuf[temp], cur_face);
+ CHECK_LPOS ();
+ }
+ }
+ else
+ {
+ invis_addc (&out, c, cur_face);
+ CHECK_LPOS();
+ }
+ }
+#if defined (DISPLAY_TABS)
+ else if (c == '\t')
+ {
+ register int newout;
+
+ newout = out + 8 - lpos % 8;
+ temp = newout - out;
+ if (lpos + temp >= _rl_screenwidth)
+ {
+ register int temp2;
+ temp2 = _rl_screenwidth - lpos;
+ CHECK_INV_LBREAKS ();
+ inv_lbreaks[++newlines] = out + temp2;
+#if defined (HANDLE_MULTIBYTE)
+ line_state_invisible->wrapped_line[newlines] = _rl_wrapped_multicolumn;
+#endif
+ lpos = temp - temp2;
+ while (out < newout)
+ invis_addc (&out, ' ', cur_face);
+ }
+ else
+ {
+ while (out < newout)
+ invis_addc (&out, ' ', cur_face);
+ lpos += temp;
+ }
+ }
+#endif
+ else if (c == '\n' && _rl_horizontal_scroll_mode == 0 && _rl_term_up && *_rl_term_up)
+ {
+ invis_addc (&out, '\0', cur_face);
+ CHECK_INV_LBREAKS ();
+ inv_lbreaks[++newlines] = out;
+#if defined (HANDLE_MULTIBYTE)
+ line_state_invisible->wrapped_line[newlines] = _rl_wrapped_multicolumn;
+#endif
+ lpos = 0;
+ }
+ else if (CTRL_CHAR (c) || c == RUBOUT)
+ {
+ invis_addc (&out, '^', cur_face);
+ CHECK_LPOS();
+ invis_addc (&out, CTRL_CHAR (c) ? UNCTRL (c) : '?', cur_face);
+ CHECK_LPOS();
+ }
+ else
+ {
+#if defined (HANDLE_MULTIBYTE)
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ {
+ register int i;
+
+ _rl_wrapped_multicolumn = 0;
+
+ if (_rl_screenwidth < lpos + wc_width)
+ for (i = lpos; i < _rl_screenwidth; i++)
+ {
+ /* The space will be removed in update_line() */
+ invis_addc (&out, ' ', cur_face);
+ _rl_wrapped_multicolumn++;
+ CHECK_LPOS();
+ }
+ if (in == rl_point)
+ {
+ cpos_buffer_position = out;
+ lb_linenum = newlines;
+ }
+ for (i = in; i < in+wc_bytes; i++)
+ invis_addc (&out, rl_line_buffer[i], cur_face);
+ for (i = 0; i < wc_width; i++)
+ CHECK_LPOS();
+ }
+ else
+ {
+ invis_addc (&out, c, cur_face);
+ CHECK_LPOS();
+ }
+#else
+ invis_addc (&out, c, cur_face);
+ CHECK_LPOS();
+#endif
+ }
+
+#if defined (HANDLE_MULTIBYTE)
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ {
+ in += wc_bytes;
+ if (_rl_utf8locale && UTF8_SINGLEBYTE(rl_line_buffer[in]))
+ {
+ wc = (WCHAR_T)rl_line_buffer[in];
+ wc_bytes = 1;
+ memset (&ps, 0, sizeof (mbstate_t)); /* re-init state */
+ }
+ else
+ wc_bytes = MBRTOWC (&wc, rl_line_buffer + in, rl_end - in, &ps);
+ }
+ else
+ in++;
+#endif
+ }
+ invis_nul (&out);
+ line_totbytes = out;
+ if (cpos_buffer_position < 0)
+ {
+ cpos_buffer_position = out;
+ lb_linenum = newlines;
+ }
+
+ /* If we are switching from one line to multiple wrapped lines, we don't
+ want to do a dumb update (or we want to make it smarter). */
+ if (_rl_quick_redisplay && newlines > 0)
+ _rl_quick_redisplay = 0;
+
+ inv_botlin = lb_botlin = _rl_inv_botlin = newlines;
+ CHECK_INV_LBREAKS ();
+ inv_lbreaks[newlines+1] = out;
+#if defined (HANDLE_MULTIBYTE)
+ /* This should be 0 anyway */
+ line_state_invisible->wrapped_line[newlines+1] = _rl_wrapped_multicolumn;
+#endif
+ cursor_linenum = lb_linenum;
+
+ /* CPOS_BUFFER_POSITION == position in buffer where cursor should be placed.
+ CURSOR_LINENUM == line number where the cursor should be placed. */
+
+ /* PWP: now is when things get a bit hairy. The visible and invisible
+ line buffers are really multiple lines, which would wrap every
+ (screenwidth - 1) characters. Go through each in turn, finding
+ the changed region and updating it. The line order is top to bottom. */
+
+ /* If we can move the cursor up and down, then use multiple lines,
+ otherwise, let long lines display in a single terminal line, and
+ horizontally scroll it. */
+ displaying_prompt_first_line = 1;
+ if (_rl_horizontal_scroll_mode == 0 && _rl_term_up && *_rl_term_up)
+ {
+ int nleft, pos, changed_screen_line, tx;
+
+ if (!rl_display_fixed || forced_display)
+ {
+ forced_display = 0;
+
+ /* If we have more than a screenful of material to display, then
+ only display a screenful. We should display the last screen,
+ not the first. */
+ if (out >= _rl_screenchars)
+ {
+#if defined (HANDLE_MULTIBYTE)
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ out = _rl_find_prev_mbchar (invisible_line, _rl_screenchars, MB_FIND_ANY);
+ else
+#endif
+ out = _rl_screenchars - 1;
+ }
+
+ /* The first line is at character position 0 in the buffer. The
+ second and subsequent lines start at inv_lbreaks[N], offset by
+ OFFSET (which has already been calculated above). */
+
+#define INVIS_FIRST() (prompt_physical_chars > _rl_screenwidth ? prompt_invis_chars_first_line : wrap_offset)
+#define WRAP_OFFSET(line, offset) ((line == 0) \
+ ? (offset ? INVIS_FIRST() : 0) \
+ : ((line == prompt_last_screen_line) ? wrap_offset-prompt_invis_chars_first_line : 0))
+#define W_OFFSET(line, offset) ((line) == 0 ? offset : 0)
+#define VIS_LLEN(l) ((l) > _rl_vis_botlin ? 0 : (vis_lbreaks[l+1] - vis_lbreaks[l]))
+#define INV_LLEN(l) (inv_lbreaks[l+1] - inv_lbreaks[l])
+#define VIS_CHARS(line) (visible_line + vis_lbreaks[line])
+#define VIS_FACE(line) (vis_face + vis_lbreaks[line])
+#define VIS_LINE(line) ((line) > _rl_vis_botlin) ? "" : VIS_CHARS(line)
+#define VIS_LINE_FACE(line) ((line) > _rl_vis_botlin) ? "" : VIS_FACE(line)
+#define INV_LINE(line) (invisible_line + inv_lbreaks[line])
+#define INV_LINE_FACE(line) (inv_face + inv_lbreaks[line])
+
+#define OLD_CPOS_IN_PROMPT() (cpos_adjusted == 0 && \
+ _rl_last_c_pos != o_cpos && \
+ _rl_last_c_pos > wrap_offset && \
+ o_cpos < prompt_last_invisible)
+
+
+ /* We don't want to highlight anything that's going to be off the top
+ of the display; if the current line takes up more than an entire
+ screen, just mark the lines that won't be displayed as having a
+ `normal' face.
+ It's imperfect, but better than display corruption. */
+ if (rl_mark_active_p () && inv_botlin > _rl_screenheight)
+ {
+ int extra;
+
+ extra = inv_botlin - _rl_screenheight;
+ for (linenum = 0; linenum <= extra; linenum++)
+ norm_face (INV_LINE_FACE(linenum), INV_LLEN (linenum));
+ }
+
+ /* For each line in the buffer, do the updating display. */
+ for (linenum = 0; linenum <= inv_botlin; linenum++)
+ {
+ /* This can lead us astray if we execute a program that changes
+ the locale from a non-multibyte to a multibyte one. */
+ o_cpos = _rl_last_c_pos;
+ cpos_adjusted = 0;
+ update_line (VIS_LINE(linenum), VIS_LINE_FACE(linenum),
+ INV_LINE(linenum), INV_LINE_FACE(linenum),
+ linenum,
+ VIS_LLEN(linenum), INV_LLEN(linenum), inv_botlin);
+
+ /* update_line potentially changes _rl_last_c_pos, but doesn't
+ take invisible characters into account, since _rl_last_c_pos
+ is an absolute cursor position in a multibyte locale. We
+ choose to (mostly) compensate for that here, rather than
+ change update_line itself. There are several cases in which
+ update_line adjusts _rl_last_c_pos itself (so it can pass
+ _rl_move_cursor_relative accurate values); it communicates
+ this back by setting cpos_adjusted. If we assume that
+ _rl_last_c_pos is correct (an absolute cursor position) each
+ time update_line is called, then we can assume in our
+ calculations that o_cpos does not need to be adjusted by
+ wrap_offset. */
+ if (linenum == 0 && (mb_cur_max > 1 && rl_byte_oriented == 0) && OLD_CPOS_IN_PROMPT())
+ _rl_last_c_pos -= prompt_invis_chars_first_line; /* XXX - was wrap_offset */
+ else if (cpos_adjusted == 0 &&
+ linenum == prompt_last_screen_line &&
+ prompt_physical_chars > _rl_screenwidth &&
+ (mb_cur_max > 1 && rl_byte_oriented == 0) &&
+ _rl_last_c_pos != o_cpos &&
+ _rl_last_c_pos > (prompt_last_invisible - _rl_screenwidth - prompt_invis_chars_first_line)) /* XXX - rethink this last one */
+ /* This assumes that all the invisible characters are split
+ between the first and last lines of the prompt, if the
+ prompt consumes more than two lines. It's usually right */
+ /* XXX - not sure this is ever executed */
+ _rl_last_c_pos -= (wrap_offset-prompt_invis_chars_first_line);
+
+ /* If this is the line with the prompt, we might need to
+ compensate for invisible characters in the new line. Do
+ this only if there is not more than one new line (which
+ implies that we completely overwrite the old visible line)
+ and the new line is shorter than the old. Make sure we are
+ at the end of the new line before clearing. */
+ if (linenum == 0 &&
+ inv_botlin == 0 && _rl_last_c_pos == out &&
+ (wrap_offset > visible_wrap_offset) &&
+ (_rl_last_c_pos < visible_first_line_len))
+ {
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ nleft = _rl_screenwidth - _rl_last_c_pos;
+ else
+ nleft = _rl_screenwidth + wrap_offset - _rl_last_c_pos;
+ if (nleft)
+ _rl_clear_to_eol (nleft);
+ }
+#if 0
+ /* This segment is intended to handle the case where the old
+ visible prompt has invisible characters and the new line
+ to be displayed needs to clear the rest of the old characters
+ out (e.g., when printing the i-search prompt): in general,
+ the case of the new line being shorter than the old. We need
+ to be at the end of the new line and the old line needs to be
+ longer than the current cursor position. It's not perfect,
+ since it uses the byte length of the first line, but this will
+ at worst result in some extra clear-to-end-of-lines. We can't
+ use the prompt length variables because they may not
+ correspond to the visible line (see printing the i-search
+ prompt above). The tests for differing numbers of invisible
+ characters may not matter and can probably be removed. */
+ else if (linenum == 0 &&
+ linenum == prompt_last_screen_line &&
+ _rl_last_c_pos == out &&
+ _rl_last_c_pos < visible_first_line_len &&
+ visible_wrap_offset &&
+ visible_wrap_offset != wrap_offset)
+ {
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ nleft = _rl_screenwidth - _rl_last_c_pos;
+ else
+ nleft = _rl_screenwidth + wrap_offset - _rl_last_c_pos;
+ if (nleft)
+ _rl_clear_to_eol (nleft);
+ }
+#endif
+
+ /* Since the new first line is now visible, save its length. */
+ if (linenum == 0)
+ visible_first_line_len = (inv_botlin > 0) ? inv_lbreaks[1] : out - wrap_offset;
+ }
+
+ /* We may have deleted some lines. If so, clear the left over
+ blank ones at the bottom out. */
+ if (_rl_vis_botlin > inv_botlin)
+ {
+ char *tt;
+ for (; linenum <= _rl_vis_botlin; linenum++)
+ {
+ tt = VIS_CHARS (linenum);
+ _rl_move_vert (linenum);
+ _rl_move_cursor_relative (0, tt, VIS_FACE(linenum));
+ _rl_clear_to_eol
+ ((linenum == _rl_vis_botlin) ? strlen (tt) : _rl_screenwidth);
+ }
+ }
+ _rl_vis_botlin = inv_botlin;
+
+ /* CHANGED_SCREEN_LINE is set to 1 if we have moved to a
+ different screen line during this redisplay. */
+ changed_screen_line = _rl_last_v_pos != cursor_linenum;
+ if (changed_screen_line)
+ {
+ _rl_move_vert (cursor_linenum);
+ /* If we moved up to the line with the prompt using _rl_term_up,
+ the physical cursor position on the screen stays the same,
+ but the buffer position needs to be adjusted to account
+ for invisible characters. */
+ if ((mb_cur_max == 1 || rl_byte_oriented) && cursor_linenum == 0 && wrap_offset)
+ _rl_last_c_pos += wrap_offset;
+ }
+
+ /* Now we move the cursor to where it needs to be. First, make
+ sure we are on the correct line (cursor_linenum). */
+
+ /* We have to reprint the prompt if it contains invisible
+ characters, since it's not generally OK to just reprint
+ the characters from the current cursor position. But we
+ only need to reprint it if the cursor is before the last
+ invisible character in the prompt string. */
+ /* XXX - why not use local_prompt_len? */
+ nleft = prompt_visible_length + wrap_offset;
+ if (cursor_linenum == 0 && wrap_offset > 0 && _rl_last_c_pos > 0 &&
+ _rl_last_c_pos < PROMPT_ENDING_INDEX && local_prompt)
+ {
+ _rl_cr ();
+ if (modmark)
+ _rl_output_some_chars ("*", 1);
+
+ _rl_output_some_chars (local_prompt, nleft);
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ _rl_last_c_pos = _rl_col_width (local_prompt, 0, nleft, 1) - wrap_offset + modmark;
+ else
+ _rl_last_c_pos = nleft + modmark;
+ }
+
+ /* Where on that line? And where does that line start
+ in the buffer? */
+ pos = inv_lbreaks[cursor_linenum];
+ /* nleft == number of characters (bytes) in the line buffer between
+ the start of the line and the desired cursor position. */
+ nleft = cpos_buffer_position - pos;
+
+ /* NLEFT is now a number of characters in a buffer. When in a
+ multibyte locale, however, _rl_last_c_pos is an absolute cursor
+ position that doesn't take invisible characters in the prompt
+ into account. We use a fudge factor to compensate. */
+
+ /* Since _rl_backspace() doesn't know about invisible characters in
+ the prompt, and there's no good way to tell it, we compensate for
+ those characters here and call _rl_backspace() directly if
+ necessary */
+ if (wrap_offset && cursor_linenum == 0 && nleft < _rl_last_c_pos)
+ {
+ /* TX == new physical cursor position in multibyte locale. */
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ tx = _rl_col_width (&visible_line[pos], 0, nleft, 1) - visible_wrap_offset;
+ else
+ tx = nleft;
+ if (tx >= 0 && _rl_last_c_pos > tx)
+ {
+ _rl_backspace (_rl_last_c_pos - tx); /* XXX */
+ _rl_last_c_pos = tx;
+ }
+ }
+
+ /* We need to note that in a multibyte locale we are dealing with
+ _rl_last_c_pos as an absolute cursor position, but moving to a
+ point specified by a buffer position (NLEFT) that doesn't take
+ invisible characters into account. */
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ _rl_move_cursor_relative (nleft, &invisible_line[pos], &inv_face[pos]);
+ else if (nleft != _rl_last_c_pos)
+ _rl_move_cursor_relative (nleft, &invisible_line[pos], &inv_face[pos]);
+ }
+ }
+ else /* Do horizontal scrolling. Much simpler */
+ {
+#define M_OFFSET(margin, offset) ((margin) == 0 ? offset : 0)
+ int lmargin, ndisp, nleft, phys_c_pos, t;
+
+ /* Always at top line. */
+ _rl_last_v_pos = 0;
+
+ /* Compute where in the buffer the displayed line should start. This
+ will be LMARGIN. */
+
+ /* The number of characters that will be displayed before the cursor. */
+ ndisp = cpos_buffer_position - wrap_offset;
+ nleft = prompt_visible_length + wrap_offset;
+ /* Where the new cursor position will be on the screen. This can be
+ longer than SCREENWIDTH; if it is, lmargin will be adjusted. */
+ phys_c_pos = cpos_buffer_position - (last_lmargin ? last_lmargin : wrap_offset);
+ t = _rl_screenwidth / 3;
+
+ /* If the number of characters had already exceeded the screenwidth,
+ last_lmargin will be > 0. */
+
+ /* If the number of characters to be displayed is more than the screen
+ width, compute the starting offset so that the cursor is about
+ two-thirds of the way across the screen. */
+ if (phys_c_pos > _rl_screenwidth - 2)
+ {
+ lmargin = cpos_buffer_position - (2 * t);
+ if (lmargin < 0)
+ lmargin = 0;
+ /* If the left margin would be in the middle of a prompt with
+ invisible characters, don't display the prompt at all. */
+ if (wrap_offset && lmargin > 0 && lmargin < nleft)
+ lmargin = nleft;
+ }
+ else if (ndisp < _rl_screenwidth - 2) /* XXX - was -1 */
+ lmargin = 0;
+ else if (phys_c_pos < 1)
+ {
+ /* If we are moving back towards the beginning of the line and
+ the last margin is no longer correct, compute a new one. */
+ lmargin = ((cpos_buffer_position - 1) / t) * t; /* XXX */
+ if (wrap_offset && lmargin > 0 && lmargin < nleft)
+ lmargin = nleft;
+ }
+ else
+ lmargin = last_lmargin;
+
+ displaying_prompt_first_line = lmargin < nleft;
+
+ /* If the first character on the screen isn't the first character
+ in the display line, indicate this with a special character. */
+ if (lmargin > 0)
+ invisible_line[lmargin] = '<';
+
+ /* If SCREENWIDTH characters starting at LMARGIN do not encompass
+ the whole line, indicate that with a special character at the
+ right edge of the screen. If LMARGIN is 0, we need to take the
+ wrap offset into account. */
+ t = lmargin + M_OFFSET (lmargin, wrap_offset) + _rl_screenwidth;
+ if (t > 0 && t < out)
+ invisible_line[t - 1] = '>';
+
+ if (rl_display_fixed == 0 || forced_display || lmargin != last_lmargin)
+ {
+ forced_display = 0;
+ o_cpos = _rl_last_c_pos;
+ cpos_adjusted = 0;
+ update_line (&visible_line[last_lmargin], &vis_face[last_lmargin],
+ &invisible_line[lmargin], &inv_face[lmargin],
+ 0,
+ _rl_screenwidth + visible_wrap_offset,
+ _rl_screenwidth + (lmargin ? 0 : wrap_offset),
+ 0);
+
+ if ((mb_cur_max > 1 && rl_byte_oriented == 0) &&
+ displaying_prompt_first_line && OLD_CPOS_IN_PROMPT())
+ _rl_last_c_pos -= prompt_invis_chars_first_line; /* XXX - was wrap_offset */
+
+ /* If the visible new line is shorter than the old, but the number
+ of invisible characters is greater, and we are at the end of
+ the new line, we need to clear to eol. */
+ t = _rl_last_c_pos - M_OFFSET (lmargin, wrap_offset);
+ if ((M_OFFSET (lmargin, wrap_offset) > visible_wrap_offset) &&
+ (_rl_last_c_pos == out) && displaying_prompt_first_line &&
+ t < visible_first_line_len)
+ {
+ nleft = _rl_screenwidth - t;
+ _rl_clear_to_eol (nleft);
+ }
+ visible_first_line_len = out - lmargin - M_OFFSET (lmargin, wrap_offset);
+ if (visible_first_line_len > _rl_screenwidth)
+ visible_first_line_len = _rl_screenwidth;
+
+ _rl_move_cursor_relative (cpos_buffer_position - lmargin, &invisible_line[lmargin], &inv_face[lmargin]);
+ last_lmargin = lmargin;
+ }
+ }
+ fflush (rl_outstream);
+
+ /* Swap visible and non-visible lines. */
+ {
+ struct line_state *vtemp = line_state_visible;
+
+ line_state_visible = line_state_invisible;
+ line_state_invisible = vtemp;
+
+ rl_display_fixed = 0;
+ /* If we are displaying on a single line, and last_lmargin is > 0, we
+ are not displaying any invisible characters, so set visible_wrap_offset
+ to 0. */
+ if (_rl_horizontal_scroll_mode && last_lmargin)
+ visible_wrap_offset = 0;
+ else
+ visible_wrap_offset = wrap_offset;
+
+ _rl_quick_redisplay = 0;
+ }
+
+ RL_UNSETSTATE (RL_STATE_REDISPLAYING);
+ _rl_release_sigint ();
+}
+
+static void
+putc_face (int c, int face, char *cur_face)
+{
+ char cf;
+ cf = *cur_face;
+ if (cf != face)
+ {
+ if (cf != FACE_NORMAL && cf != FACE_STANDOUT)
+ return;
+ if (face != FACE_NORMAL && face != FACE_STANDOUT)
+ return;
+ if (face == FACE_STANDOUT && cf == FACE_NORMAL)
+ _rl_region_color_on ();
+ if (face == FACE_NORMAL && cf == FACE_STANDOUT)
+ _rl_region_color_off ();
+ *cur_face = face;
+ }
+ if (c != EOF)
+ putc (c, rl_outstream);
+}
+
+static void
+puts_face (const char *str, const char *face, int n)
+{
+ int i;
+ char cur_face;
+
+ for (cur_face = FACE_NORMAL, i = 0; i < n; i++)
+ putc_face ((unsigned char) str[i], face[i], &cur_face);
+ putc_face (EOF, FACE_NORMAL, &cur_face);
+}
+
+static void
+norm_face (char *face, int n)
+{
+ memset (face, FACE_NORMAL, n);
+}
+
+#define ADJUST_CPOS(x) do { _rl_last_c_pos -= (x) ; cpos_adjusted = 1; } while (0)
+
+/* PWP: update_line() is based on finding the middle difference of each
+ line on the screen; vis:
+
+ /old first difference
+ /beginning of line | /old last same /old EOL
+ v v v v
+old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as
+new: eddie> Oh, my little buggy says to me, as lurgid as
+ ^ ^ ^ ^
+ \beginning of line | \new last same \new end of line
+ \new first difference
+
+ All are character pointers for the sake of speed. Special cases for
+ no differences, as well as for end of line additions must be handled.
+
+ Could be made even smarter, but this works well enough */
+static void
+update_line (char *old, char *old_face, char *new, char *new_face, int current_line, int omax, int nmax, int inv_botlin)
+{
+ char *ofd, *ols, *oe, *nfd, *nls, *ne;
+ char *ofdf, *nfdf, *olsf, *nlsf;
+ int temp, lendiff, wsatend, od, nd, twidth, o_cpos;
+ int current_invis_chars;
+ int col_lendiff, col_temp;
+ int bytes_to_insert;
+ int mb_cur_max = MB_CUR_MAX;
+#if defined (HANDLE_MULTIBYTE)
+ mbstate_t ps_new, ps_old;
+ int new_offset, old_offset;
+#endif
+
+ /* If we're at the right edge of a terminal that supports xn, we're
+ ready to wrap around, so do so. This fixes problems with knowing
+ the exact cursor position and cut-and-paste with certain terminal
+ emulators. In this calculation, TEMP is the physical screen
+ position of the cursor. */
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ temp = _rl_last_c_pos;
+ else
+ temp = _rl_last_c_pos - WRAP_OFFSET (_rl_last_v_pos, visible_wrap_offset);
+ if (temp == _rl_screenwidth && _rl_term_autowrap && !_rl_horizontal_scroll_mode
+ && _rl_last_v_pos == current_line - 1)
+ {
+ /* We're going to wrap around by writing the first character of NEW to
+ the screen and dealing with changes to what's visible by modifying
+ OLD to match it. Complicated by the presence of multi-width
+ characters at the end of the line or beginning of the new one. */
+ /* old is always somewhere in visible_line; new is always somewhere in
+ invisible_line. These should always be null-terminated. */
+#if defined (HANDLE_MULTIBYTE)
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ {
+ WCHAR_T wc;
+ mbstate_t ps;
+ int oldwidth, newwidth;
+ int oldbytes, newbytes;
+ size_t ret;
+
+ /* This fixes only double-column characters, but if the wrapped
+ character consumes more than three columns, spaces will be
+ inserted in the string buffer. */
+ /* XXX remember that we are working on the invisible line right now;
+ we don't swap visible and invisible until just before rl_redisplay
+ returns */
+ /* This will remove the extra placeholder space we added with
+ _rl_wrapped_multicolumn */
+ if (current_line < line_state_invisible->wbsize && line_state_invisible->wrapped_line[current_line] > 0)
+ _rl_clear_to_eol (line_state_invisible->wrapped_line[current_line]);
+
+ /* 1. how many screen positions does first char in old consume? */
+ memset (&ps, 0, sizeof (mbstate_t));
+ ret = MBRTOWC (&wc, old, mb_cur_max, &ps);
+ oldbytes = ret;
+ if (MB_INVALIDCH (ret))
+ {
+ oldwidth = 1;
+ oldbytes = 1;
+ }
+ else if (MB_NULLWCH (ret))
+ oldwidth = 0;
+ else
+ oldwidth = WCWIDTH (wc);
+ if (oldwidth < 0)
+ oldwidth = 1;
+
+ /* 2. how many screen positions does the first char in new consume? */
+ memset (&ps, 0, sizeof (mbstate_t));
+ ret = MBRTOWC (&wc, new, mb_cur_max, &ps);
+ newbytes = ret;
+ if (MB_INVALIDCH (ret))
+ {
+ newwidth = 1;
+ newbytes = 1;
+ }
+ else if (MB_NULLWCH (ret))
+ newwidth = 0;
+ else
+ newwidth = WCWIDTH (wc);
+ if (newwidth < 0)
+ newwidth = 1;
+
+ /* 3. if the new width is less than the old width, we need to keep
+ going in new until we have consumed at least that many screen
+ positions, and figure out how many bytes that will take */
+ while (newbytes < nmax && newwidth < oldwidth)
+ {
+ int t;
+
+ ret = MBRTOWC (&wc, new+newbytes, mb_cur_max, &ps);
+ if (MB_INVALIDCH (ret))
+ {
+ newwidth += 1;
+ newbytes += 1;
+ }
+ else if (MB_NULLWCH (ret))
+ break;
+ else
+ {
+ t = WCWIDTH (wc);
+ newwidth += (t >= 0) ? t : 1;
+ newbytes += ret;
+ }
+ }
+ /* 4. If the new width is more than the old width, keep going in old
+ until we have consumed exactly that many screen positions, and
+ figure out how many bytes that will take. This is an optimization */
+ while (oldbytes < omax && oldwidth < newwidth)
+ {
+ int t;
+
+ ret = MBRTOWC (&wc, old+oldbytes, mb_cur_max, &ps);
+ if (MB_INVALIDCH (ret))
+ {
+ oldwidth += 1;
+ oldbytes += 1;
+ }
+ else if (MB_NULLWCH (ret))
+ break;
+ else
+ {
+ t = WCWIDTH (wc);
+ oldwidth += (t >= 0) ? t : 1;
+ oldbytes += ret;
+ }
+ }
+ /* 5. write the first newbytes of new, which takes newwidth. This is
+ where the screen wrapping takes place, and we are now writing
+ characters onto the new line. We need to fix up old so it
+ accurately reflects what is on the screen after the
+ _rl_output_some_chars below. */
+ if (newwidth > 0)
+ {
+ int count, i, j;
+ char *optr;
+
+ puts_face (new, new_face, newbytes);
+ _rl_last_c_pos = newwidth;
+ _rl_last_v_pos++;
+
+ /* 5a. If the number of screen positions doesn't match, punt
+ and do a dumb update.
+ 5b. If the number of bytes is greater in the new line than
+ the old, do a dumb update, because there is no guarantee we
+ can extend the old line enough to fit the new bytes. */
+ if (newwidth != oldwidth || newbytes > oldbytes)
+ {
+ oe = old + omax;
+ ne = new + nmax;
+ nd = newbytes;
+ nfd = new + nd;
+ ofdf = old_face + oldbytes;
+ nfdf = new_face + newbytes;
+
+ goto dumb_update;
+ }
+ if (oldbytes != 0 && newbytes != 0)
+ {
+ /* We have written as many bytes from new as we need to
+ consume the first character of old. Fix up `old' so it
+ reflects the new screen contents. We use +1 in the
+ memmove call to copy the trailing NUL. */
+ /* (strlen(old+oldbytes) == (omax - oldbytes - 1)) */
+
+ /* Don't bother trying to fit the bytes if the number of bytes
+ doesn't change. */
+ if (oldbytes != newbytes)
+ {
+ memmove (old+newbytes, old+oldbytes, strlen (old+oldbytes) + 1);
+ memmove (old_face+newbytes, old_face+oldbytes, strlen (old+oldbytes) + 1);
+ }
+ memcpy (old, new, newbytes);
+ memcpy (old_face, new_face, newbytes);
+ j = newbytes - oldbytes;
+ omax += j;
+ /* Fix up indices if we copy data from one line to another */
+ for (i = current_line+1; j != 0 && i <= inv_botlin+1 && i <=_rl_vis_botlin+1; i++)
+ vis_lbreaks[i] += j;
+ }
+ }
+ else
+ {
+ putc (' ', rl_outstream);
+ _rl_last_c_pos = 1;
+ _rl_last_v_pos++;
+ if (old[0] && new[0])
+ {
+ old[0] = new[0];
+ old_face[0] = new_face[0];
+ }
+ }
+ }
+ else
+#endif
+ {
+ if (new[0])
+ puts_face (new, new_face, 1);
+ else
+ putc (' ', rl_outstream);
+ _rl_last_c_pos = 1;
+ _rl_last_v_pos++;
+ if (old[0] && new[0])
+ {
+ old[0] = new[0];
+ old_face[0] = new_face[0];
+ }
+ }
+ }
+
+ /* We know that we are dealing with a single screen line here */
+ if (_rl_quick_redisplay)
+ {
+ nfd = new;
+ nfdf = new_face;
+ ofd = old;
+ ofdf = old_face;
+ for (od = 0, oe = ofd; od < omax && *oe; oe++, od++);
+ for (nd = 0, ne = nfd; nd < nmax && *ne; ne++, nd++);
+ od = nd = 0;
+ _rl_move_cursor_relative (0, old, old_face);
+
+ bytes_to_insert = ne - nfd;
+ if (bytes_to_insert < local_prompt_len) /* ??? */
+ goto dumb_update;
+
+ /* output the prompt, output the line contents, clear the rest */
+ _rl_output_some_chars (nfd, local_prompt_len);
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ _rl_last_c_pos = prompt_physical_chars;
+ else
+ _rl_last_c_pos = local_prompt_len;
+
+ bytes_to_insert -= local_prompt_len;
+ if (bytes_to_insert > 0)
+ {
+ puts_face (new+local_prompt_len, nfdf+local_prompt_len, bytes_to_insert);
+ if (mb_cur_max > 1 && rl_byte_oriented)
+ _rl_last_c_pos += _rl_col_width (new, local_prompt_len, ne-new, 1);
+ else
+ _rl_last_c_pos += bytes_to_insert;
+ }
+
+ /* See comments at dumb_update: for an explanation of this heuristic */
+ if (nmax < omax)
+ goto clear_rest_of_line;
+ else if ((nmax - W_OFFSET(current_line, wrap_offset)) < (omax - W_OFFSET (current_line, visible_wrap_offset)))
+ goto clear_rest_of_line;
+ else
+ return;
+ }
+
+ /* Find first difference. */
+#if defined (HANDLE_MULTIBYTE)
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ {
+ /* See if the old line is a subset of the new line, so that the
+ only change is adding characters. */
+ temp = (omax < nmax) ? omax : nmax;
+ if (memcmp (old, new, temp) == 0 && memcmp (old_face, new_face, temp) == 0)
+ {
+ new_offset = old_offset = temp; /* adding at the end */
+ ofd = old + temp;
+ ofdf = old_face + temp;
+ nfd = new + temp;
+ nfdf = new_face + temp;
+ }
+ else
+ {
+ memset (&ps_new, 0, sizeof(mbstate_t));
+ memset (&ps_old, 0, sizeof(mbstate_t));
+
+ /* Are the old and new lines the same? */
+ if (omax == nmax && memcmp (new, old, omax) == 0 && memcmp (new_face, old_face, omax) == 0)
+ {
+ old_offset = omax;
+ new_offset = nmax;
+ ofd = old + omax;
+ ofdf = old_face + omax;
+ nfd = new + nmax;
+ nfdf = new_face + nmax;
+ }
+ else
+ {
+ /* Go through the line from the beginning and find the first
+ difference. We assume that faces change at (possibly multi-
+ byte) character boundaries. */
+ new_offset = old_offset = 0;
+ for (ofd = old, ofdf = old_face, nfd = new, nfdf = new_face;
+ (ofd - old < omax) && *ofd &&
+ _rl_compare_chars(old, old_offset, &ps_old, new, new_offset, &ps_new) &&
+ *ofdf == *nfdf; )
+ {
+ old_offset = _rl_find_next_mbchar (old, old_offset, 1, MB_FIND_ANY);
+ new_offset = _rl_find_next_mbchar (new, new_offset, 1, MB_FIND_ANY);
+
+ ofd = old + old_offset;
+ ofdf = old_face + old_offset;
+ nfd = new + new_offset;
+ nfdf = new_face + new_offset;
+ }
+ }
+ }
+ }
+ else
+#endif
+ for (ofd = old, ofdf = old_face, nfd = new, nfdf = new_face;
+ (ofd - old < omax) && *ofd && (*ofd == *nfd) && (*ofdf == *nfdf);
+ ofd++, nfd++, ofdf++, nfdf++)
+ ;
+
+ /* Move to the end of the screen line. ND and OD are used to keep track
+ of the distance between ne and new and oe and old, respectively, to
+ move a subtraction out of each loop. */
+ for (od = ofd - old, oe = ofd; od < omax && *oe; oe++, od++);
+ for (nd = nfd - new, ne = nfd; nd < nmax && *ne; ne++, nd++);
+
+ /* If no difference, continue to next line. */
+ if (ofd == oe && nfd == ne)
+ return;
+
+#if defined (HANDLE_MULTIBYTE)
+ if (mb_cur_max > 1 && rl_byte_oriented == 0 && _rl_utf8locale)
+ {
+ WCHAR_T wc;
+ mbstate_t ps = { 0 };
+ int t;
+
+ /* If the first character in the difference is a zero-width character,
+ assume it's a combining character and back one up so the two base
+ characters no longer compare equivalently. */
+ t = MBRTOWC (&wc, ofd, mb_cur_max, &ps);
+ if (t > 0 && UNICODE_COMBINING_CHAR (wc) && WCWIDTH (wc) == 0)
+ {
+ old_offset = _rl_find_prev_mbchar (old, ofd - old, MB_FIND_ANY);
+ new_offset = _rl_find_prev_mbchar (new, nfd - new, MB_FIND_ANY);
+ ofd = old + old_offset; /* equal by definition */
+ ofdf = old_face + old_offset;
+ nfd = new + new_offset;
+ nfdf = new_face + new_offset;
+ }
+ }
+#endif
+
+ wsatend = 1; /* flag for trailing whitespace */
+
+#if defined (HANDLE_MULTIBYTE)
+ /* Find the last character that is the same between the two lines. This
+ bounds the region that needs to change. */
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ {
+ ols = old + _rl_find_prev_mbchar (old, oe - old, MB_FIND_ANY);
+ olsf = old_face + (ols - old);
+ nls = new + _rl_find_prev_mbchar (new, ne - new, MB_FIND_ANY);
+ nlsf = new_face + (nls - new);
+
+ while ((ols > ofd) && (nls > nfd))
+ {
+ memset (&ps_old, 0, sizeof (mbstate_t));
+ memset (&ps_new, 0, sizeof (mbstate_t));
+
+ if (_rl_compare_chars (old, ols - old, &ps_old, new, nls - new, &ps_new) == 0 ||
+ *olsf != *nlsf)
+ break;
+
+ if (*ols == ' ')
+ wsatend = 0;
+
+ ols = old + _rl_find_prev_mbchar (old, ols - old, MB_FIND_ANY);
+ olsf = old_face + (ols - old);
+ nls = new + _rl_find_prev_mbchar (new, nls - new, MB_FIND_ANY);
+ nlsf = new_face + (nls - new);
+ }
+ }
+ else
+ {
+#endif /* HANDLE_MULTIBYTE */
+ ols = oe - 1; /* find last same */
+ olsf = old_face + (ols - old);
+ nls = ne - 1;
+ nlsf = new_face + (nls - new);
+ while ((ols > ofd) && (nls > nfd) && (*ols == *nls) && (*olsf == *nlsf))
+ {
+ if (*ols != ' ')
+ wsatend = 0;
+ ols--; olsf--;
+ nls--; nlsf--;
+ }
+#if defined (HANDLE_MULTIBYTE)
+ }
+#endif
+
+ if (wsatend)
+ {
+ ols = oe;
+ olsf = old_face + (ols - old);
+ nls = ne;
+ nlsf = new_face + (nls - new);
+ }
+#if defined (HANDLE_MULTIBYTE)
+ /* This may not work for stateful encoding, but who cares? To handle
+ stateful encoding properly, we have to scan each string from the
+ beginning and compare. */
+ else if (_rl_compare_chars (ols, 0, NULL, nls, 0, NULL) == 0 || *olsf != *nlsf)
+#else
+ else if (*ols != *nls || *olsf != *nlsf)
+#endif
+ {
+ if (*ols) /* don't step past the NUL */
+ {
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ ols = old + _rl_find_next_mbchar (old, ols - old, 1, MB_FIND_ANY);
+ else
+ ols++;
+ }
+ if (*nls)
+ {
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ nls = new + _rl_find_next_mbchar (new, nls - new, 1, MB_FIND_ANY);
+ else
+ nls++;
+ }
+ olsf = old_face + (ols - old);
+ nlsf = new_face + (nls - new);
+ }
+
+ /* count of invisible characters in the current invisible line. */
+ current_invis_chars = W_OFFSET (current_line, wrap_offset);
+ if (_rl_last_v_pos != current_line)
+ {
+ _rl_move_vert (current_line);
+ /* We have moved up to a new screen line. This line may or may not have
+ invisible characters on it, but we do our best to recalculate
+ visible_wrap_offset based on what we know. */
+ if (current_line == 0)
+ visible_wrap_offset = prompt_invis_chars_first_line; /* XXX */
+#if 0 /* XXX - not yet */
+ else if (current_line == prompt_last_screen_line && wrap_offset > prompt_invis_chars_first_line)
+ visible_wrap_offset = wrap_offset - prompt_invis_chars_first_line
+#endif
+ if ((mb_cur_max == 1 || rl_byte_oriented) && current_line == 0 && visible_wrap_offset)
+ _rl_last_c_pos += visible_wrap_offset;
+ }
+
+ /* If this is the first line and there are invisible characters in the
+ prompt string, and the prompt string has not changed, and the current
+ cursor position is before the last invisible character in the prompt,
+ and the index of the character to move to is past the end of the prompt
+ string, then redraw the entire prompt string. We can only do this
+ reliably if the terminal supports a `cr' capability.
+
+ This can also happen if the prompt string has changed, and the first
+ difference in the line is in the middle of the prompt string, after a
+ sequence of invisible characters (worst case) and before the end of
+ the prompt. In this case, we have to redraw the entire prompt string
+ so that the entire sequence of invisible characters is drawn. We need
+ to handle the worst case, when the difference is after (or in the middle
+ of) a sequence of invisible characters that changes the text color and
+ before the sequence that restores the text color to normal. Then we have
+ to make sure that the lines still differ -- if they don't, we can
+ return immediately.
+
+ This is not an efficiency hack -- there is a problem with redrawing
+ portions of the prompt string if they contain terminal escape
+ sequences (like drawing the `unbold' sequence without a corresponding
+ `bold') that manifests itself on certain terminals. */
+
+ lendiff = local_prompt_len;
+ if (lendiff > nmax)
+ lendiff = nmax;
+ od = ofd - old; /* index of first difference in visible line */
+ nd = nfd - new; /* nd, od are buffer indexes */
+ if (current_line == 0 && !_rl_horizontal_scroll_mode &&
+ _rl_term_cr && lendiff > prompt_visible_length && _rl_last_c_pos > 0 &&
+ (((od > 0 || nd > 0) && (od <= prompt_last_invisible || nd <= prompt_last_invisible)) ||
+ ((od >= lendiff) && _rl_last_c_pos < PROMPT_ENDING_INDEX)))
+ {
+ _rl_cr ();
+ if (modmark)
+ _rl_output_some_chars ("*", 1);
+ _rl_output_some_chars (local_prompt, lendiff);
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ {
+ /* If we just output the entire prompt string we can take advantage
+ of knowing the number of physical characters in the prompt. If
+ the prompt wraps lines (lendiff clamped at nmax), we can't. */
+ if (lendiff == local_prompt_len)
+ _rl_last_c_pos = prompt_physical_chars + modmark;
+ else
+ /* We take wrap_offset into account here so we can pass correct
+ information to _rl_move_cursor_relative. */
+ _rl_last_c_pos = _rl_col_width (local_prompt, 0, lendiff, 1) - wrap_offset + modmark;
+ cpos_adjusted = 1;
+ }
+ else
+ _rl_last_c_pos = lendiff + modmark;
+
+ /* Now if we have printed the prompt string because the first difference
+ was within the prompt, see if we need to recompute where the lines
+ differ. Check whether where we are now is past the last place where
+ the old and new lines are the same and short-circuit now if we are. */
+ if ((od <= prompt_last_invisible || nd <= prompt_last_invisible) &&
+ omax == nmax &&
+ lendiff > (ols-old) && lendiff > (nls-new))
+ return;
+
+ /* XXX - we need to fix up our calculations if we are now past the
+ old ofd/nfd and the prompt length (or line length) has changed.
+ We punt on the problem and do a dumb update. We'd like to be able
+ to just output the prompt from the beginning of the line up to the
+ first difference, but you don't know the number of invisible
+ characters in that case.
+ This needs a lot of work to be efficient, but it usually doesn't matter. */
+ if ((od <= prompt_last_invisible || nd <= prompt_last_invisible))
+ {
+ nfd = new + lendiff; /* number of characters we output above */
+ nfdf = new_face + lendiff;
+ nd = lendiff;
+
+ /* Do a dumb update and return */
+dumb_update:
+ temp = ne - nfd;
+ if (temp > 0)
+ {
+ puts_face (nfd, nfdf, temp);
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ {
+ _rl_last_c_pos += _rl_col_width (new, nd, ne - new, 1);
+ /* Need to adjust here based on wrap_offset. Guess that if
+ this is the line containing the last line of the prompt
+ we need to adjust by
+ wrap_offset-prompt_invis_chars_first_line
+ on the assumption that this is the number of invisible
+ characters in the last line of the prompt. */
+ if (wrap_offset > prompt_invis_chars_first_line &&
+ current_line == prompt_last_screen_line &&
+ prompt_physical_chars > _rl_screenwidth &&
+ _rl_horizontal_scroll_mode == 0)
+ ADJUST_CPOS (wrap_offset - prompt_invis_chars_first_line);
+
+ /* If we just output a new line including the prompt, and
+ the prompt includes invisible characters, we need to
+ account for them in the _rl_last_c_pos calculation, since
+ _rl_col_width does not. This happens when other code does
+ a goto dumb_update; */
+ else if (current_line == 0 &&
+ nfd == new &&
+ prompt_invis_chars_first_line &&
+ local_prompt_len <= temp &&
+ wrap_offset >= prompt_invis_chars_first_line &&
+ _rl_horizontal_scroll_mode == 0)
+ ADJUST_CPOS (prompt_invis_chars_first_line);
+ }
+ else
+ _rl_last_c_pos += temp;
+ }
+ /* This is a useful heuristic, but what we really want is to clear
+ if the new number of visible screen characters is less than the
+ old number of visible screen characters. If the prompt has changed,
+ we don't really have enough information about the visible line to
+ know for sure, so we use another heuristic calclulation below. */
+ if (nmax < omax)
+ goto clear_rest_of_line; /* XXX */
+ else if ((nmax - W_OFFSET(current_line, wrap_offset)) < (omax - W_OFFSET (current_line, visible_wrap_offset)))
+ goto clear_rest_of_line;
+ else
+ return;
+ }
+ }
+
+ o_cpos = _rl_last_c_pos;
+
+ /* When this function returns, _rl_last_c_pos is correct, and an absolute
+ cursor position in multibyte mode, but a buffer index when not in a
+ multibyte locale. */
+ _rl_move_cursor_relative (od, old, old_face);
+
+#if defined (HANDLE_MULTIBYTE)
+ /* We need to indicate that the cursor position is correct in the presence of
+ invisible characters in the prompt string. Let's see if setting this when
+ we make sure we're at the end of the drawn prompt string works. */
+ if (current_line == 0 && mb_cur_max > 1 && rl_byte_oriented == 0 &&
+ (_rl_last_c_pos > 0 || o_cpos > 0) &&
+ _rl_last_c_pos == prompt_physical_chars)
+ cpos_adjusted = 1;
+#endif
+
+ /* if (len (new) > len (old))
+ lendiff == difference in buffer (bytes)
+ col_lendiff == difference on screen (columns)
+ When not using multibyte characters, these are equal */
+ lendiff = (nls - nfd) - (ols - ofd);
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ {
+ int newchars, newwidth, newind;
+ int oldchars, oldwidth, oldind;
+
+ newchars = nls - new;
+ oldchars = ols - old;
+
+ /* If we can do it, try to adjust nls and ols so that nls-new will
+ contain the entire new prompt string. That way we can use
+ prompt_physical_chars and not have to recompute column widths.
+ _rl_col_width adds wrap_offset and expects the caller to compensate,
+ which we do below, so we do the same thing if we don't call
+ _rl_col_width.
+ We don't have to compare, since we know the characters are the same.
+ The check of differing numbers of invisible chars may be extraneous.
+ XXX - experimental */
+ if (current_line == 0 && nfd == new && newchars > prompt_last_invisible &&
+ newchars <= local_prompt_len &&
+ local_prompt_len <= nmax &&
+ current_invis_chars != visible_wrap_offset)
+ {
+ while (newchars < nmax && oldchars < omax && newchars < local_prompt_len)
+ {
+#if defined (HANDLE_MULTIBYTE)
+ newind = _rl_find_next_mbchar (new, newchars, 1, MB_FIND_NONZERO);
+ oldind = _rl_find_next_mbchar (old, oldchars, 1, MB_FIND_NONZERO);
+
+ nls += newind - newchars;
+ ols += oldind - oldchars;
+
+ newchars = newind;
+ oldchars = oldind;
+#else
+ nls++; ols++;
+ newchars++; oldchars++;
+#endif
+ }
+ newwidth = (newchars == local_prompt_len) ? prompt_physical_chars + wrap_offset
+ : _rl_col_width (new, 0, nls - new, 1);
+ /* if we changed nls and ols, we need to recompute lendiff */
+ lendiff = (nls - nfd) - (ols - ofd);
+
+ nlsf = new_face + (nls - new);
+ olsf = old_face + (ols - old);
+ }
+ else
+ newwidth = _rl_col_width (new, nfd - new, nls - new, 1);
+
+ oldwidth = _rl_col_width (old, ofd - old, ols - old, 1);
+
+ col_lendiff = newwidth - oldwidth;
+ }
+ else
+ col_lendiff = lendiff;
+
+ /* col_lendiff uses _rl_col_width(), which doesn't know about whether or not
+ the multibyte characters it counts are invisible, so unless we're printing
+ the entire prompt string (in which case we can use prompt_physical_chars)
+ the count is short by the number of bytes in the invisible multibyte
+ characters - the number of multibyte characters.
+
+ We don't have a good way to solve this without moving to something like
+ a bitmap that indicates which characters are visible and which are
+ invisible. We fix it up (imperfectly) in the caller and by trying to use
+ the entire prompt string wherever we can. */
+
+ /* If we are changing the number of invisible characters in a line, and
+ the spot of first difference is before the end of the invisible chars,
+ lendiff needs to be adjusted. */
+ if (current_line == 0 && current_invis_chars != visible_wrap_offset)
+ {
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ {
+ lendiff += visible_wrap_offset - current_invis_chars;
+ col_lendiff += visible_wrap_offset - current_invis_chars;
+ }
+ else
+ {
+ lendiff += visible_wrap_offset - current_invis_chars;
+ col_lendiff = lendiff;
+ }
+ }
+
+ /* We use temp as a count of the number of bytes from the first difference
+ to the end of the new line. col_temp is the corresponding number of
+ screen columns. A `dumb' update moves to the spot of first difference
+ and writes TEMP bytes. */
+ /* Insert (diff (len (old), len (new)) ch. */
+ temp = ne - nfd;
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ col_temp = _rl_col_width (new, nfd - new, ne - new, 1);
+ else
+ col_temp = temp;
+
+ /* how many bytes from the new line buffer to write to the display */
+ bytes_to_insert = nls - nfd;
+
+ /* col_lendiff > 0 if we are adding characters to the line */
+ if (col_lendiff > 0) /* XXX - was lendiff */
+ {
+ /* Non-zero if we're increasing the number of lines. */
+ int gl = current_line >= _rl_vis_botlin && inv_botlin > _rl_vis_botlin;
+
+ /* If col_lendiff is > 0, implying that the new string takes up more
+ screen real estate than the old, but lendiff is < 0, meaning that it
+ takes fewer bytes, we need to just output the characters starting
+ from the first difference. These will overwrite what is on the
+ display, so there's no reason to do a smart update. This can really
+ only happen in a multibyte environment. */
+ if (lendiff < 0)
+ {
+ puts_face (nfd, nfdf, temp);
+ _rl_last_c_pos += col_temp;
+ /* If nfd begins before any invisible characters in the prompt,
+ adjust _rl_last_c_pos to account for wrap_offset and set
+ cpos_adjusted to let the caller know. */
+ if (current_line == 0 && displaying_prompt_first_line && wrap_offset && ((nfd - new) <= prompt_last_invisible))
+ ADJUST_CPOS (wrap_offset); /* XXX - prompt_invis_chars_first_line? */
+ return;
+ }
+ /* Sometimes it is cheaper to print the characters rather than
+ use the terminal's capabilities. If we're growing the number
+ of lines, make sure we actually cause the new line to wrap
+ around on auto-wrapping terminals. */
+ else if (_rl_terminal_can_insert && ((2 * col_temp) >= col_lendiff || _rl_term_IC) && (!_rl_term_autowrap || !gl))
+ {
+ /* If lendiff > prompt_visible_length and _rl_last_c_pos == 0 and
+ _rl_horizontal_scroll_mode == 1, inserting the characters with
+ _rl_term_IC or _rl_term_ic will screw up the screen because of the
+ invisible characters. We need to just draw them. */
+ /* The same thing happens if we're trying to draw before the last
+ invisible character in the prompt string or we're increasing the
+ number of invisible characters in the line and we're not drawing
+ the entire prompt string. */
+ if (*ols && ((_rl_horizontal_scroll_mode &&
+ _rl_last_c_pos == 0 &&
+ lendiff > prompt_visible_length &&
+ current_invis_chars > 0) == 0) &&
+ (((mb_cur_max > 1 && rl_byte_oriented == 0) &&
+ current_line == 0 && wrap_offset &&
+ ((nfd - new) <= prompt_last_invisible) &&
+ (col_lendiff < prompt_visible_length)) == 0) &&
+ (visible_wrap_offset >= current_invis_chars))
+ {
+ open_some_spaces (col_lendiff);
+ puts_face (nfd, nfdf, bytes_to_insert);
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ _rl_last_c_pos += _rl_col_width (nfd, 0, bytes_to_insert, 1);
+ else
+ _rl_last_c_pos += bytes_to_insert;
+ }
+ else if ((mb_cur_max == 1 || rl_byte_oriented != 0) && *ols == 0 && lendiff > 0)
+ {
+ /* At the end of a line the characters do not have to
+ be "inserted". They can just be placed on the screen. */
+ puts_face (nfd, nfdf, temp);
+ _rl_last_c_pos += col_temp;
+ return;
+ }
+ else /* just write from first difference to end of new line */
+ {
+ puts_face (nfd, nfdf, temp);
+ _rl_last_c_pos += col_temp;
+ /* If nfd begins before the last invisible character in the
+ prompt, adjust _rl_last_c_pos to account for wrap_offset
+ and set cpos_adjusted to let the caller know. */
+ if ((mb_cur_max > 1 && rl_byte_oriented == 0) && current_line == 0 && displaying_prompt_first_line && wrap_offset && ((nfd - new) <= prompt_last_invisible))
+ ADJUST_CPOS (wrap_offset); /* XXX - prompt_invis_chars_first_line? */
+ return;
+ }
+
+ if (bytes_to_insert > lendiff)
+ {
+ /* If nfd begins before the last invisible character in the
+ prompt, adjust _rl_last_c_pos to account for wrap_offset
+ and set cpos_adjusted to let the caller know. */
+ if ((mb_cur_max > 1 && rl_byte_oriented == 0) && current_line == 0 && displaying_prompt_first_line && wrap_offset && ((nfd - new) <= prompt_last_invisible))
+ ADJUST_CPOS (wrap_offset); /* XXX - prompt_invis_chars_first_line? */
+ }
+ }
+ else
+ {
+ /* cannot insert chars, write to EOL */
+ puts_face (nfd, nfdf, temp);
+ _rl_last_c_pos += col_temp;
+ /* If we're in a multibyte locale and were before the last invisible
+ char in the current line (which implies we just output some invisible
+ characters) we need to adjust _rl_last_c_pos, since it represents
+ a physical character position. */
+ /* The current_line*rl_screenwidth+prompt_invis_chars_first_line is a
+ crude attempt to compute how far into the new line buffer we are.
+ It doesn't work well in the face of multibyte characters and needs
+ to be rethought. XXX */
+ if ((mb_cur_max > 1 && rl_byte_oriented == 0) &&
+ current_line == prompt_last_screen_line && wrap_offset &&
+ displaying_prompt_first_line &&
+ wrap_offset != prompt_invis_chars_first_line &&
+ ((nfd-new) < (prompt_last_invisible-(current_line*_rl_screenwidth+prompt_invis_chars_first_line))))
+ ADJUST_CPOS (wrap_offset - prompt_invis_chars_first_line);
+
+ /* What happens if wrap_offset == prompt_invis_chars_first_line
+ and we are drawing the first line (current_line == 0), or if we
+ are drawing the first line and changing the number of invisible
+ characters in the line? If we're starting to draw before the last
+ invisible character in the prompt, we need to adjust by
+ _rl_last_c_pos -= prompt_invis_chars_first_line. This can happen
+ when we finish reading a digit argument (with the "(arg: N)"
+ prompt) and are switching back to displaying a line with a prompt
+ containing invisible characters, since we have to redraw the
+ entire prompt string. */
+ if ((mb_cur_max > 1 && rl_byte_oriented == 0) &&
+ current_line == 0 && wrap_offset &&
+ displaying_prompt_first_line &&
+ wrap_offset == prompt_invis_chars_first_line &&
+ visible_wrap_offset != current_invis_chars &&
+ visible_wrap_offset != prompt_invis_chars_first_line &&
+ ((nfd-new) < prompt_last_invisible))
+ ADJUST_CPOS (prompt_invis_chars_first_line);
+ }
+ }
+ else /* Delete characters from line. */
+ {
+ /* If possible and inexpensive to use terminal deletion, then do so. */
+ if (_rl_term_dc && (2 * col_temp) >= -col_lendiff)
+ {
+ /* If all we're doing is erasing the invisible characters in the
+ prompt string, don't bother. It screws up the assumptions
+ about what's on the screen. */
+ if (_rl_horizontal_scroll_mode && _rl_last_c_pos == 0 &&
+ displaying_prompt_first_line &&
+ -lendiff == visible_wrap_offset)
+ col_lendiff = 0;
+
+ /* If we have moved lmargin and we're shrinking the line, we've
+ already moved the cursor to the first character of the new line,
+ so deleting -col_lendiff characters will mess up the cursor
+ position calculation */
+ if (_rl_horizontal_scroll_mode && displaying_prompt_first_line == 0 &&
+ col_lendiff && _rl_last_c_pos < -col_lendiff)
+ col_lendiff = 0;
+
+ if (col_lendiff)
+ delete_chars (-col_lendiff); /* delete (diff) characters */
+
+ /* Copy (new) chars to screen from first diff to last match,
+ overwriting what is there. */
+ if (bytes_to_insert > 0)
+ {
+ /* If nfd begins at the prompt, or before the invisible
+ characters in the prompt, we need to adjust _rl_last_c_pos
+ in a multibyte locale to account for the wrap offset and
+ set cpos_adjusted accordingly. */
+ puts_face (nfd, nfdf, bytes_to_insert);
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ {
+ /* This still doesn't take into account whether or not the
+ characters that this counts are invisible. */
+ _rl_last_c_pos += _rl_col_width (nfd, 0, bytes_to_insert, 1);
+ if (current_line == 0 && wrap_offset &&
+ displaying_prompt_first_line &&
+ prompt_invis_chars_first_line &&
+ _rl_last_c_pos >= prompt_invis_chars_first_line &&
+ ((nfd - new) <= prompt_last_invisible))
+ ADJUST_CPOS (prompt_invis_chars_first_line);
+
+#if 1
+#ifdef HANDLE_MULTIBYTE
+ /* If we write a non-space into the last screen column,
+ remove the note that we added a space to compensate for
+ a multibyte double-width character that didn't fit, since
+ it's only valid for what was previously there. */
+ /* XXX - watch this */
+ if (_rl_last_c_pos == _rl_screenwidth &&
+ line_state_invisible->wrapped_line[current_line+1] &&
+ nfd[bytes_to_insert-1] != ' ')
+ line_state_invisible->wrapped_line[current_line+1] = 0;
+#endif
+#endif
+ }
+ else
+ _rl_last_c_pos += bytes_to_insert;
+
+ /* XXX - we only want to do this if we are at the end of the line
+ so we move there with _rl_move_cursor_relative */
+ if (_rl_horizontal_scroll_mode && ((oe-old) > (ne-new)))
+ {
+ _rl_move_cursor_relative (ne-new, new, new_face);
+ goto clear_rest_of_line;
+ }
+ }
+ }
+ /* Otherwise, print over the existing material. */
+ else
+ {
+ if (temp > 0)
+ {
+ /* If nfd begins at the prompt, or before the invisible
+ characters in the prompt, we need to adjust _rl_last_c_pos
+ in a multibyte locale to account for the wrap offset and
+ set cpos_adjusted accordingly. */
+ puts_face (nfd, nfdf, temp);
+ _rl_last_c_pos += col_temp; /* XXX */
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ {
+ if (current_line == 0 && wrap_offset &&
+ displaying_prompt_first_line &&
+ _rl_last_c_pos > wrap_offset &&
+ ((nfd - new) <= prompt_last_invisible))
+ ADJUST_CPOS (wrap_offset); /* XXX - prompt_invis_chars_first_line? */
+ }
+ }
+clear_rest_of_line:
+ lendiff = (oe - old) - (ne - new);
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ col_lendiff = _rl_col_width (old, 0, oe - old, 1) - _rl_col_width (new, 0, ne - new, 1);
+ else
+ col_lendiff = lendiff;
+
+ /* If we've already printed over the entire width of the screen,
+ including the old material, then col_lendiff doesn't matter and
+ space_to_eol will insert too many spaces. XXX - maybe we should
+ adjust col_lendiff based on the difference between _rl_last_c_pos
+ and _rl_screenwidth */
+ if (col_lendiff && ((mb_cur_max == 1 || rl_byte_oriented) || (_rl_last_c_pos < _rl_screenwidth)))
+ {
+ if (_rl_term_autowrap && current_line < inv_botlin)
+ space_to_eol (col_lendiff);
+ else
+ _rl_clear_to_eol (col_lendiff);
+ }
+ }
+ }
+}
+
+/* Tell the update routines that we have moved onto a new (empty) line. */
+int
+rl_on_new_line (void)
+{
+ if (visible_line)
+ visible_line[0] = '\0';
+
+ _rl_last_c_pos = _rl_last_v_pos = 0;
+ _rl_vis_botlin = last_lmargin = 0;
+ if (vis_lbreaks)
+ vis_lbreaks[0] = vis_lbreaks[1] = 0;
+ visible_wrap_offset = 0;
+ return 0;
+}
+
+/* Clear all screen lines occupied by the current readline line buffer
+ (visible line) */
+int
+rl_clear_visible_line (void)
+{
+ int curr_line;
+
+ /* Make sure we move to column 0 so we clear the entire line */
+ _rl_cr ();
+ _rl_last_c_pos = 0;
+
+ /* Move to the last screen line of the current visible line */
+ _rl_move_vert (_rl_vis_botlin);
+
+ /* And erase screen lines going up to line 0 (first visible line) */
+ for (curr_line = _rl_last_v_pos; curr_line >= 0; curr_line--)
+ {
+ _rl_move_vert (curr_line);
+ _rl_clear_to_eol (_rl_screenwidth);
+ _rl_cr (); /* in case we use space_to_eol() */
+ }
+
+ return 0;
+}
+
+/* Tell the update routines that we have moved onto a new line with the
+ prompt already displayed. Code originally from the version of readline
+ distributed with CLISP. rl_expand_prompt must have already been called
+ (explicitly or implicitly). This still doesn't work exactly right; it
+ should use expand_prompt() */
+int
+rl_on_new_line_with_prompt (void)
+{
+ int prompt_size, i, l, real_screenwidth, newlines;
+ char *prompt_last_line, *lprompt;
+
+ /* Initialize visible_line and invisible_line to ensure that they can hold
+ the already-displayed prompt. */
+ prompt_size = strlen (rl_prompt) + 1;
+ init_line_structures (prompt_size);
+
+ /* Make sure the line structures hold the already-displayed prompt for
+ redisplay. */
+ lprompt = local_prompt ? local_prompt : rl_prompt;
+ strcpy (visible_line, lprompt);
+ strcpy (invisible_line, lprompt);
+
+ /* If the prompt contains newlines, take the last tail. */
+ prompt_last_line = strrchr (rl_prompt, '\n');
+ if (!prompt_last_line)
+ prompt_last_line = rl_prompt;
+
+ l = strlen (prompt_last_line);
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ _rl_last_c_pos = _rl_col_width (prompt_last_line, 0, l, 1); /* XXX */
+ else
+ _rl_last_c_pos = l;
+
+ /* Dissect prompt_last_line into screen lines. Note that here we have
+ to use the real screenwidth. Readline's notion of screenwidth might be
+ one less, see terminal.c. */
+ real_screenwidth = _rl_screenwidth + (_rl_term_autowrap ? 0 : 1);
+ _rl_last_v_pos = l / real_screenwidth;
+ /* If the prompt length is a multiple of real_screenwidth, we don't know
+ whether the cursor is at the end of the last line, or already at the
+ beginning of the next line. Output a newline just to be safe. */
+ if (l > 0 && (l % real_screenwidth) == 0)
+ _rl_output_some_chars ("\n", 1);
+ last_lmargin = 0;
+
+ newlines = 0; i = 0;
+ while (i <= l)
+ {
+ _rl_vis_botlin = newlines;
+ vis_lbreaks[newlines++] = i;
+ i += real_screenwidth;
+ }
+ vis_lbreaks[newlines] = l;
+ visible_wrap_offset = 0;
+
+ rl_display_prompt = rl_prompt; /* XXX - make sure it's set */
+
+ return 0;
+}
+
+/* Actually update the display, period. */
+int
+rl_forced_update_display (void)
+{
+ register char *temp;
+
+ if (visible_line)
+ {
+ temp = visible_line;
+ while (*temp)
+ *temp++ = '\0';
+ }
+ rl_on_new_line ();
+ forced_display++;
+ (*rl_redisplay_function) ();
+ return 0;
+}
+
+/* Redraw only the last line of a multi-line prompt. */
+void
+rl_redraw_prompt_last_line (void)
+{
+ char *t;
+
+ t = strrchr (rl_display_prompt, '\n');
+ if (t)
+ redraw_prompt (++t);
+ else
+ rl_forced_update_display ();
+}
+
+/* Move the cursor from _rl_last_c_pos to NEW, which are buffer indices.
+ (Well, when we don't have multibyte characters, _rl_last_c_pos is a
+ buffer index.)
+ DATA is the contents of the screen line of interest; i.e., where
+ the movement is being done.
+ DATA is always the visible line or the invisible line */
+static void
+_rl_move_cursor_relative (int new, const char *data, const char *dataf)
+{
+ register int i;
+ int woff; /* number of invisible chars on current line */
+ int cpos, dpos; /* current and desired cursor positions */
+ int adjust;
+ int in_invisline;
+ int mb_cur_max = MB_CUR_MAX;
+
+ woff = WRAP_OFFSET (_rl_last_v_pos, wrap_offset);
+ cpos = _rl_last_c_pos;
+
+ if (cpos == 0 && cpos == new)
+ return;
+
+#if defined (HANDLE_MULTIBYTE)
+ /* If we have multibyte characters, NEW is indexed by the buffer point in
+ a multibyte string, but _rl_last_c_pos is the display position. In
+ this case, NEW's display position is not obvious and must be
+ calculated. We need to account for invisible characters in this line,
+ as long as we are past them and they are counted by _rl_col_width. */
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ {
+ adjust = 1;
+ /* Try to short-circuit common cases and eliminate a bunch of multibyte
+ character function calls. */
+ /* 1. prompt string */
+ if (new == local_prompt_len && memcmp (data, local_prompt, new) == 0)
+ {
+ dpos = prompt_physical_chars;
+ cpos_adjusted = 1;
+ adjust = 0;
+ }
+ /* 2. prompt_string + line contents */
+ else if (new > local_prompt_len && local_prompt && memcmp (data, local_prompt, local_prompt_len) == 0)
+ {
+ dpos = prompt_physical_chars + _rl_col_width (data, local_prompt_len, new, 1);
+ cpos_adjusted = 1;
+ adjust = 0;
+ }
+ else
+ dpos = _rl_col_width (data, 0, new, 1);
+
+ if (displaying_prompt_first_line == 0)
+ adjust = 0;
+
+ /* yet another special case: printing the last line of a prompt with
+ multibyte characters and invisible characters whose printable length
+ exceeds the screen width with the last invisible character
+ (prompt_last_invisible) in the last line. IN_INVISLINE is the
+ offset of DATA in invisible_line */
+ in_invisline = 0;
+ if (data > invisible_line && data < invisible_line+inv_lbreaks[_rl_inv_botlin+1])
+ in_invisline = data - invisible_line;
+
+ /* Use NEW when comparing against the last invisible character in the
+ prompt string, since they're both buffer indices and DPOS is a
+ desired display position. */
+ /* NEW is relative to the current displayed line, while
+ PROMPT_LAST_INVISIBLE is relative to the entire (wrapped) line.
+ Need a way to reconcile these two variables by turning NEW into a
+ buffer position relative to the start of the line */
+ if (adjust && ((new > prompt_last_invisible) || /* XXX - don't use woff here */
+ (new+in_invisline > prompt_last_invisible) || /* invisible line */
+ (prompt_physical_chars >= _rl_screenwidth && /* visible line */
+ _rl_last_v_pos == prompt_last_screen_line &&
+ wrap_offset >= woff && dpos >= woff &&
+ new > (prompt_last_invisible-(vis_lbreaks[_rl_last_v_pos])-wrap_offset))))
+ /* XXX last comparison might need to be >= */
+ {
+ dpos -= woff;
+ /* Since this will be assigned to _rl_last_c_pos at the end (more
+ precisely, _rl_last_c_pos == dpos when this function returns),
+ let the caller know. */
+ cpos_adjusted = 1;
+ }
+ }
+ else
+#endif
+ dpos = new;
+
+ /* If we don't have to do anything, then return. */
+ if (cpos == dpos)
+ return;
+
+ /* It may be faster to output a CR, and then move forwards instead
+ of moving backwards. */
+ /* i == current physical cursor position. */
+#if defined (HANDLE_MULTIBYTE)
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ i = _rl_last_c_pos;
+ else
+#endif
+ i = _rl_last_c_pos - woff;
+ if (dpos == 0 || CR_FASTER (dpos, _rl_last_c_pos) ||
+ (_rl_term_autowrap && i == _rl_screenwidth))
+ {
+ _rl_cr ();
+ cpos = _rl_last_c_pos = 0;
+ }
+
+ if (cpos < dpos)
+ {
+ /* Move the cursor forward. We do it by printing the command
+ to move the cursor forward if there is one, else print that
+ portion of the output buffer again. Which is cheaper? */
+
+ /* The above comment is left here for posterity. It is faster
+ to print one character (non-control) than to print a control
+ sequence telling the terminal to move forward one character.
+ That kind of control is for people who don't know what the
+ data is underneath the cursor. */
+
+ /* However, we need a handle on where the current display position is
+ in the buffer for the immediately preceding comment to be true.
+ In multibyte locales, we don't currently have that info available.
+ Without it, we don't know where the data we have to display begins
+ in the buffer and we have to go back to the beginning of the screen
+ line. In this case, we can use the terminal sequence to move forward
+ if it's available. */
+ if (mb_cur_max > 1 && rl_byte_oriented == 0)
+ {
+ if (_rl_term_forward_char)
+ {
+ for (i = cpos; i < dpos; i++)
+ tputs (_rl_term_forward_char, 1, _rl_output_character_function);
+ }
+ else
+ {
+ _rl_cr ();
+ puts_face (data, dataf, new);
+ }
+ }
+ else
+ puts_face (data + cpos, dataf + cpos, new - cpos);
+ }
+
+#if defined (HANDLE_MULTIBYTE)
+ /* NEW points to the buffer point, but _rl_last_c_pos is the display point.
+ The byte length of the string is probably bigger than the column width
+ of the string, which means that if NEW == _rl_last_c_pos, then NEW's
+ display point is less than _rl_last_c_pos. */
+#endif
+ else if (cpos > dpos)
+ _rl_backspace (cpos - dpos);
+
+ _rl_last_c_pos = dpos;
+}
+
+/* PWP: move the cursor up or down. */
+void
+_rl_move_vert (int to)
+{
+ register int delta, i;
+
+ if (_rl_last_v_pos == to || to > _rl_screenheight)
+ return;
+
+ if ((delta = to - _rl_last_v_pos) > 0)
+ {
+ for (i = 0; i < delta; i++)
+ putc ('\n', rl_outstream);
+ _rl_cr ();
+ _rl_last_c_pos = 0;
+ }
+ else
+ { /* delta < 0 */
+#ifdef __DJGPP__
+ int row, col;
+
+ fflush (rl_outstream);
+ ScreenGetCursor (&row, &col);
+ ScreenSetCursor (row + delta, col);
+ i = -delta;
+#else
+ if (_rl_term_up && *_rl_term_up)
+ for (i = 0; i < -delta; i++)
+ tputs (_rl_term_up, 1, _rl_output_character_function);
+#endif /* !__DJGPP__ */
+ }
+
+ _rl_last_v_pos = to; /* Now TO is here */
+}
+
+/* Physically print C on rl_outstream. This is for functions which know
+ how to optimize the display. Return the number of characters output. */
+int
+rl_show_char (int c)
+{
+ int n = 1;
+ if (META_CHAR (c) && (_rl_output_meta_chars == 0))
+ {
+ fprintf (rl_outstream, "M-");
+ n += 2;
+ c = UNMETA (c);
+ }
+
+#if defined (DISPLAY_TABS)
+ if ((CTRL_CHAR (c) && c != '\t') || c == RUBOUT)
+#else
+ if (CTRL_CHAR (c) || c == RUBOUT)
+#endif /* !DISPLAY_TABS */
+ {
+ fprintf (rl_outstream, "C-");
+ n += 2;
+ c = CTRL_CHAR (c) ? UNCTRL (c) : '?';
+ }
+
+ putc (c, rl_outstream);
+ fflush (rl_outstream);
+ return n;
+}
+
+int
+rl_character_len (int c, int pos)
+{
+ unsigned char uc;
+
+ uc = (unsigned char)c;
+
+ if (META_CHAR (uc))
+ return ((_rl_output_meta_chars == 0) ? 4 : 1);
+
+ if (uc == '\t')
+ {
+#if defined (DISPLAY_TABS)
+ return (((pos | 7) + 1) - pos);
+#else
+ return (2);
+#endif /* !DISPLAY_TABS */
+ }
+
+ if (CTRL_CHAR (c) || c == RUBOUT)
+ return (2);
+
+ return ((ISPRINT (uc)) ? 1 : 2);
+}
+/* How to print things in the "echo-area". The prompt is treated as a
+ mini-modeline. */
+static int msg_saved_prompt = 0;
+
+#if defined (USE_VARARGS)
+int
+#if defined (PREFER_STDARG)
+rl_message (const char *format, ...)
+#else
+rl_message (va_alist)
+ va_dcl
+#endif
+{
+ va_list args;
+#if defined (PREFER_VARARGS)
+ char *format;
+#endif
+#if defined (HAVE_VSNPRINTF)
+ int bneed;
+#endif
+
+#if defined (PREFER_STDARG)
+ va_start (args, format);
+#else
+ va_start (args);
+ format = va_arg (args, char *);
+#endif
+
+ if (msg_buf == 0)
+ msg_buf = xmalloc (msg_bufsiz = 128);
+
+#if defined (HAVE_VSNPRINTF)
+ bneed = vsnprintf (msg_buf, msg_bufsiz, format, args);
+ if (bneed >= msg_bufsiz - 1)
+ {
+ msg_bufsiz = bneed + 1;
+ msg_buf = xrealloc (msg_buf, msg_bufsiz);
+ va_end (args);
+
+#if defined (PREFER_STDARG)
+ va_start (args, format);
+#else
+ va_start (args);
+ format = va_arg (args, char *);
+#endif
+ vsnprintf (msg_buf, msg_bufsiz - 1, format, args);
+ }
+#else
+ vsprintf (msg_buf, format, args);
+ msg_buf[msg_bufsiz - 1] = '\0'; /* overflow? */
+#endif
+ va_end (args);
+
+ if (saved_local_prompt == 0)
+ {
+ rl_save_prompt ();
+ msg_saved_prompt = 1;
+ }
+ else if (local_prompt != saved_local_prompt)
+ {
+ FREE (local_prompt);
+ FREE (local_prompt_prefix);
+ local_prompt = (char *)NULL;
+ }
+ rl_display_prompt = msg_buf;
+ local_prompt = expand_prompt (msg_buf, 0, &prompt_visible_length,
+ &prompt_last_invisible,
+ &prompt_invis_chars_first_line,
+ &prompt_physical_chars);
+ local_prompt_prefix = (char *)NULL;
+ local_prompt_len = local_prompt ? strlen (local_prompt) : 0;
+ (*rl_redisplay_function) ();
+
+ return 0;
+}
+#else /* !USE_VARARGS */
+int
+rl_message (format, arg1, arg2)
+ char *format;
+{
+ if (msg_buf == 0)
+ msg_buf = xmalloc (msg_bufsiz = 128);
+
+ sprintf (msg_buf, format, arg1, arg2);
+ msg_buf[msg_bufsiz - 1] = '\0'; /* overflow? */
+
+ rl_display_prompt = msg_buf;
+ if (saved_local_prompt == 0)
+ {
+ rl_save_prompt ();
+ msg_saved_prompt = 1;
+ }
+ else if (local_prompt != saved_local_prompt)
+ {
+ FREE (local_prompt);
+ FREE (local_prompt_prefix);
+ local_prompt = (char *)NULL;
+ }
+ local_prompt = expand_prompt (msg_buf, 0, &prompt_visible_length,
+ &prompt_last_invisible,
+ &prompt_invis_chars_first_line,
+ &prompt_physical_chars);
+ local_prompt_prefix = (char *)NULL;
+ local_prompt_len = local_prompt ? strlen (local_prompt) : 0;
+ (*rl_redisplay_function) ();
+
+ return 0;
+}
+#endif /* !USE_VARARGS */
+
+/* How to clear things from the "echo-area". */
+int
+rl_clear_message (void)
+{
+ rl_display_prompt = rl_prompt;
+ if (msg_saved_prompt)
+ {
+ rl_restore_prompt ();
+ msg_saved_prompt = 0;
+ }
+ (*rl_redisplay_function) ();
+ return 0;
+}
+
+int
+rl_reset_line_state (void)
+{
+ rl_on_new_line ();
+
+ rl_display_prompt = rl_prompt ? rl_prompt : "";
+ forced_display = 1;
+ return 0;
+}
+
+/* Save all of the variables associated with the prompt and its display. Most
+ of the complexity is dealing with the invisible characters in the prompt
+ string and where they are. There are enough of these that I should consider
+ a struct. */
+void
+rl_save_prompt (void)
+{
+ saved_local_prompt = local_prompt;
+ saved_local_prefix = local_prompt_prefix;
+ saved_prefix_length = prompt_prefix_length;
+ saved_local_length = local_prompt_len;
+ saved_last_invisible = prompt_last_invisible;
+ saved_visible_length = prompt_visible_length;
+ saved_invis_chars_first_line = prompt_invis_chars_first_line;
+ saved_physical_chars = prompt_physical_chars;
+ saved_local_prompt_newlines = local_prompt_newlines;
+
+ local_prompt = local_prompt_prefix = (char *)0;
+ local_prompt_len = 0;
+ local_prompt_newlines = (int *)0;
+
+ prompt_last_invisible = prompt_visible_length = prompt_prefix_length = 0;
+ prompt_invis_chars_first_line = prompt_physical_chars = 0;
+}
+
+void
+rl_restore_prompt (void)
+{
+ FREE (local_prompt);
+ FREE (local_prompt_prefix);
+ FREE (local_prompt_newlines);
+
+ local_prompt = saved_local_prompt;
+ local_prompt_prefix = saved_local_prefix;
+ local_prompt_len = saved_local_length;
+ local_prompt_newlines = saved_local_prompt_newlines;
+
+ prompt_prefix_length = saved_prefix_length;
+ prompt_last_invisible = saved_last_invisible;
+ prompt_visible_length = saved_visible_length;
+ prompt_invis_chars_first_line = saved_invis_chars_first_line;
+ prompt_physical_chars = saved_physical_chars;
+
+ /* can test saved_local_prompt to see if prompt info has been saved. */
+ saved_local_prompt = saved_local_prefix = (char *)0;
+ saved_local_length = 0;
+ saved_last_invisible = saved_visible_length = saved_prefix_length = 0;
+ saved_invis_chars_first_line = saved_physical_chars = 0;
+ saved_local_prompt_newlines = 0;
+}
+
+char *
+_rl_make_prompt_for_search (int pchar)
+{
+ int len;
+ char *pmt, *p;
+
+ rl_save_prompt ();
+
+ /* We've saved the prompt, and can do anything with the various prompt
+ strings we need before they're restored. We want the unexpanded
+ portion of the prompt string after any final newline. */
+ p = rl_prompt ? strrchr (rl_prompt, '\n') : 0;
+ if (p == 0)
+ {
+ len = (rl_prompt && *rl_prompt) ? strlen (rl_prompt) : 0;
+ pmt = (char *)xmalloc (len + 2);
+ if (len)
+ strcpy (pmt, rl_prompt);
+ pmt[len] = pchar;
+ pmt[len+1] = '\0';
+ }
+ else
+ {
+ p++;
+ len = strlen (p);
+ pmt = (char *)xmalloc (len + 2);
+ if (len)
+ strcpy (pmt, p);
+ pmt[len] = pchar;
+ pmt[len+1] = '\0';
+ }
+
+ /* will be overwritten by expand_prompt, called from rl_message */
+ prompt_physical_chars = saved_physical_chars + 1;
+ return pmt;
+}
+
+/* Quick redisplay hack when erasing characters at the end of the line. */
+void
+_rl_erase_at_end_of_line (int l)
+{
+ register int i;
+
+ _rl_backspace (l);
+ for (i = 0; i < l; i++)
+ putc (' ', rl_outstream);
+ _rl_backspace (l);
+ for (i = 0; i < l; i++)
+ visible_line[--_rl_last_c_pos] = '\0';
+ rl_display_fixed++;
+}
+
+/* Clear to the end of the line. COUNT is the minimum
+ number of character spaces to clear, but we use a terminal escape
+ sequence if available. */
+void
+_rl_clear_to_eol (int count)
+{
+#ifndef __MSDOS__
+ if (_rl_term_clreol)
+ tputs (_rl_term_clreol, 1, _rl_output_character_function);
+ else
+#endif
+ if (count)
+ space_to_eol (count);
+}
+
+/* Clear to the end of the line using spaces. COUNT is the minimum
+ number of character spaces to clear, */
+static void
+space_to_eol (int count)
+{
+ register int i;
+
+ for (i = 0; i < count; i++)
+ putc (' ', rl_outstream);
+
+ _rl_last_c_pos += count;
+}
+
+void
+_rl_clear_screen (int clrscr)
+{
+#if defined (__DJGPP__)
+ ScreenClear ();
+ ScreenSetCursor (0, 0);
+#else
+ if (_rl_term_clrpag)
+ {
+ tputs (_rl_term_clrpag, 1, _rl_output_character_function);
+ if (clrscr && _rl_term_clrscroll)
+ tputs (_rl_term_clrscroll, 1, _rl_output_character_function);
+ }
+ else
+ rl_crlf ();
+#endif /* __DJGPP__ */
+}
+
+/* Insert COUNT characters from STRING to the output stream at column COL. */
+static void
+insert_some_chars (char *string, int count, int col)
+{
+ open_some_spaces (col);
+ _rl_output_some_chars (string, count);
+}
+
+/* Insert COL spaces, keeping the cursor at the same position. We follow the
+ ncurses documentation and use either im/ei with explicit spaces, or IC/ic
+ by itself. We assume there will either be ei or we don't need to use it. */
+static void
+open_some_spaces (int col)
+{
+#if !defined (__MSDOS__) && (!defined (__MINGW32__) || defined (NCURSES_VERSION))
+ char *buffer;
+ register int i;
+
+ /* If IC is defined, then we do not have to "enter" insert mode. */
+ if (_rl_term_IC)
+ {
+ buffer = tgoto (_rl_term_IC, 0, col);
+ tputs (buffer, 1, _rl_output_character_function);
+ }
+ else if (_rl_term_im && *_rl_term_im)
+ {
+ tputs (_rl_term_im, 1, _rl_output_character_function);
+ /* just output the desired number of spaces */
+ for (i = col; i--; )
+ _rl_output_character_function (' ');
+ /* If there is a string to turn off insert mode, use it now. */
+ if (_rl_term_ei && *_rl_term_ei)
+ tputs (_rl_term_ei, 1, _rl_output_character_function);
+ /* and move back the right number of spaces */
+ _rl_backspace (col);
+ }
+ else if (_rl_term_ic && *_rl_term_ic)
+ {
+ /* If there is a special command for inserting characters, then
+ use that first to open up the space. */
+ for (i = col; i--; )
+ tputs (_rl_term_ic, 1, _rl_output_character_function);
+ }
+#endif /* !__MSDOS__ && (!__MINGW32__ || NCURSES_VERSION)*/
+}
+
+/* Delete COUNT characters from the display line. */
+static void
+delete_chars (int count)
+{
+ if (count > _rl_screenwidth) /* XXX */
+ return;
+
+#if !defined (__MSDOS__) && (!defined (__MINGW32__) || defined (NCURSES_VERSION))
+ if (_rl_term_DC && *_rl_term_DC)
+ {
+ char *buffer;
+ buffer = tgoto (_rl_term_DC, count, count);
+ tputs (buffer, count, _rl_output_character_function);
+ }
+ else
+ {
+ if (_rl_term_dc && *_rl_term_dc)
+ while (count--)
+ tputs (_rl_term_dc, 1, _rl_output_character_function);
+ }
+#endif /* !__MSDOS__ && (!__MINGW32__ || NCURSES_VERSION)*/
+}
+
+void
+_rl_update_final (void)
+{
+ int full_lines, woff, botline_length;
+
+ if (line_structures_initialized == 0)
+ return;
+
+ full_lines = 0;
+ /* If the cursor is the only thing on an otherwise-blank last line,
+ compensate so we don't print an extra CRLF. */
+ if (_rl_vis_botlin && _rl_last_c_pos == 0 &&
+ visible_line[vis_lbreaks[_rl_vis_botlin]] == 0)
+ {
+ _rl_vis_botlin--;
+ full_lines = 1;
+ }
+ _rl_move_vert (_rl_vis_botlin);
+ woff = W_OFFSET(_rl_vis_botlin, wrap_offset);
+ botline_length = VIS_LLEN(_rl_vis_botlin) - woff;
+ /* If we've wrapped lines, remove the final xterm line-wrap flag. */
+ if (full_lines && _rl_term_autowrap && botline_length == _rl_screenwidth)
+ {
+ char *last_line, *last_face;
+
+ /* LAST_LINE includes invisible characters, so if you want to get the
+ last character of the first line, you have to take WOFF into account.
+ This needs to be done for both calls to _rl_move_cursor_relative,
+ which takes a buffer position as the first argument, and any direct
+ subscripts of LAST_LINE. */
+ last_line = &visible_line[vis_lbreaks[_rl_vis_botlin]]; /* = VIS_CHARS(_rl_vis_botlin); */
+ last_face = &vis_face[vis_lbreaks[_rl_vis_botlin]]; /* = VIS_CHARS(_rl_vis_botlin); */
+ cpos_buffer_position = -1; /* don't know where we are in buffer */
+ _rl_move_cursor_relative (_rl_screenwidth - 1 + woff, last_line, last_face); /* XXX */
+ _rl_clear_to_eol (0);
+ puts_face (&last_line[_rl_screenwidth - 1 + woff],
+ &last_face[_rl_screenwidth - 1 + woff], 1);
+ }
+ _rl_vis_botlin = 0;
+ if (botline_length > 0 || _rl_last_c_pos > 0)
+ rl_crlf ();
+ fflush (rl_outstream);
+ rl_display_fixed++;
+}
+
+/* Move to the start of the current line. */
+static void
+cr (void)
+{
+ _rl_cr ();
+ _rl_last_c_pos = 0;
+}
+
+/* Redraw the last line of a multi-line prompt that may possibly contain
+ terminal escape sequences. Called with the cursor at column 0 of the
+ line to draw the prompt on. */
+static void
+redraw_prompt (char *t)
+{
+ char *oldp;
+
+ oldp = rl_display_prompt;
+ rl_save_prompt ();
+
+ rl_display_prompt = t;
+ local_prompt = expand_prompt (t, PMT_MULTILINE,
+ &prompt_visible_length,
+ &prompt_last_invisible,
+ &prompt_invis_chars_first_line,
+ &prompt_physical_chars);
+ local_prompt_prefix = (char *)NULL;
+ local_prompt_len = local_prompt ? strlen (local_prompt) : 0;
+
+ rl_forced_update_display ();
+
+ rl_display_prompt = oldp;
+ rl_restore_prompt();
+}
+
+/* Redisplay the current line after a SIGWINCH is received. */
+void
+_rl_redisplay_after_sigwinch (void)
+{
+ char *t;
+
+ /* Clear the last line (assuming that the screen size change will result in
+ either more or fewer characters on that line only) and put the cursor at
+ column 0. Make sure the right thing happens if we have wrapped to a new
+ screen line. */
+ if (_rl_term_cr)
+ {
+ rl_clear_visible_line ();
+ if (_rl_last_v_pos > 0)
+ _rl_move_vert (0);
+ }
+ else
+ rl_crlf ();
+
+ if (_rl_screenwidth < prompt_visible_length)
+ _rl_reset_prompt (); /* update local_prompt_newlines array */
+
+ /* Redraw only the last line of a multi-line prompt. */
+ t = strrchr (rl_display_prompt, '\n');
+ if (t)
+ redraw_prompt (++t);
+ else
+ rl_forced_update_display ();
+}
+
+void
+_rl_clean_up_for_exit (void)
+{
+ if (_rl_echoing_p)
+ {
+ if (_rl_vis_botlin > 0) /* minor optimization plus bug fix */
+ _rl_move_vert (_rl_vis_botlin);
+ _rl_vis_botlin = 0;
+ fflush (rl_outstream);
+ rl_restart_output (1, 0);
+ }
+}
+
+void
+_rl_erase_entire_line (void)
+{
+ cr ();
+ _rl_clear_to_eol (0);
+ cr ();
+ fflush (rl_outstream);
+}
+
+void
+_rl_ttyflush (void)
+{
+ fflush (rl_outstream);
+}
+
+/* return the `current display line' of the cursor -- the number of lines to
+ move up to get to the first screen line of the current readline line. */
+int
+_rl_current_display_line (void)
+{
+ int ret, nleft;
+
+ /* Find out whether or not there might be invisible characters in the
+ editing buffer. */
+ if (rl_display_prompt == rl_prompt)
+ nleft = _rl_last_c_pos - _rl_screenwidth - rl_visible_prompt_length;
+ else
+ nleft = _rl_last_c_pos - _rl_screenwidth;
+
+ if (nleft > 0)
+ ret = 1 + nleft / _rl_screenwidth;
+ else
+ ret = 0;
+
+ return ret;
+}
+
+void
+_rl_refresh_line (void)
+{
+ rl_clear_visible_line ();
+ rl_redraw_prompt_last_line ();
+ rl_keep_mark_active ();
+}
+
+#if defined (HANDLE_MULTIBYTE)
+/* Calculate the number of screen columns occupied by STR from START to END.
+ In the case of multibyte characters with stateful encoding, we have to
+ scan from the beginning of the string to take the state into account. */
+static int
+_rl_col_width (const char *str, int start, int end, int flags)
+{
+ WCHAR_T wc;
+ mbstate_t ps;
+ int tmp, point, width, max;
+
+ if (end <= start)
+ return 0;
+ if (MB_CUR_MAX == 1 || rl_byte_oriented)
+ /* this can happen in some cases where it's inconvenient to check */
+ return (end - start);
+
+ memset (&ps, 0, sizeof (mbstate_t));
+
+ point = 0;
+ max = end;
+
+ /* Try to short-circuit common cases. The adjustment to remove wrap_offset
+ is done by the caller. */
+ /* 1. prompt string */
+ if (flags && start == 0 && end == local_prompt_len && memcmp (str, local_prompt, local_prompt_len) == 0)
+ return (prompt_physical_chars + wrap_offset);
+ /* 2. prompt string + line contents */
+ else if (flags && start == 0 && local_prompt_len > 0 && end > local_prompt_len && local_prompt && memcmp (str, local_prompt, local_prompt_len) == 0)
+ {
+ tmp = prompt_physical_chars + wrap_offset;
+ /* XXX - try to call ourselves recursively with non-prompt portion */
+ tmp += _rl_col_width (str, local_prompt_len, end, flags);
+ return (tmp);
+ }
+
+ while (point < start)
+ {
+ if (_rl_utf8locale && UTF8_SINGLEBYTE(str[point]))
+ {
+ memset (&ps, 0, sizeof (mbstate_t));
+ tmp = 1;
+ }
+ else
+ tmp = mbrlen (str + point, max, &ps);
+ if (MB_INVALIDCH ((size_t)tmp))
+ {
+ /* In this case, the bytes are invalid or too short to compose a
+ multibyte character, so we assume that the first byte represents
+ a single character. */
+ point++;
+ max--;
+
+ /* Clear the state of the byte sequence, because in this case the
+ effect of mbstate is undefined. */
+ memset (&ps, 0, sizeof (mbstate_t));
+ }
+ else if (MB_NULLWCH (tmp))
+ break; /* Found '\0' */
+ else
+ {
+ point += tmp;
+ max -= tmp;
+ }
+ }
+
+ /* If START is not a byte that starts a character, then POINT will be
+ greater than START. In this case, assume that (POINT - START) gives
+ a byte count that is the number of columns of difference. */
+ width = point - start;
+
+ while (point < end)
+ {
+ if (_rl_utf8locale && UTF8_SINGLEBYTE(str[point]))
+ {
+ tmp = 1;
+ wc = (WCHAR_T) str[point];
+ }
+ else
+ tmp = MBRTOWC (&wc, str + point, max, &ps);
+ if (MB_INVALIDCH ((size_t)tmp))
+ {
+ /* In this case, the bytes are invalid or too short to compose a
+ multibyte character, so we assume that the first byte represents
+ a single character. */
+ point++;
+ max--;
+
+ /* and assume that the byte occupies a single column. */
+ width++;
+
+ /* Clear the state of the byte sequence, because in this case the
+ effect of mbstate is undefined. */
+ memset (&ps, 0, sizeof (mbstate_t));
+ }
+ else if (MB_NULLWCH (tmp))
+ break; /* Found '\0' */
+ else
+ {
+ point += tmp;
+ max -= tmp;
+ tmp = WCWIDTH(wc);
+ width += (tmp >= 0) ? tmp : 1;
+ }
+ }
+
+ width += point - end;
+
+ return width;
+}
+#endif /* HANDLE_MULTIBYTE */
diff --git a/third_party/readline/emacs_keymap.inc b/third_party/readline/emacs_keymap.inc
new file mode 100644
index 000000000..02597dad3
--- /dev/null
+++ b/third_party/readline/emacs_keymap.inc
@@ -0,0 +1,872 @@
+/* emacs_keymap.c -- the keymap for emacs_mode in readline (). */
+
+/* Copyright (C) 1987-2017 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#if !defined (BUFSIZ)
+#include
+#endif /* !BUFSIZ */
+
+#include "readline.h"
+
+/* An array of function pointers, one for each possible key.
+ If the type byte is ISKMAP, then the pointer is the address of
+ a keymap. */
+
+KEYMAP_ENTRY_ARRAY emacs_standard_keymap = {
+
+ /* Control keys. */
+ { ISFUNC, rl_set_mark }, /* Control-@ */
+ { ISFUNC, rl_beg_of_line }, /* Control-a */
+ { ISFUNC, rl_backward_char }, /* Control-b */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-c */
+ { ISFUNC, rl_delete }, /* Control-d */
+ { ISFUNC, rl_end_of_line }, /* Control-e */
+ { ISFUNC, rl_forward_char }, /* Control-f */
+ { ISFUNC, rl_abort }, /* Control-g */
+ { ISFUNC, rl_rubout }, /* Control-h */
+ { ISFUNC, rl_complete }, /* Control-i */
+ { ISFUNC, rl_newline }, /* Control-j */
+ { ISFUNC, rl_kill_line }, /* Control-k */
+ { ISFUNC, rl_clear_screen }, /* Control-l */
+ { ISFUNC, rl_newline }, /* Control-m */
+ { ISFUNC, rl_get_next_history }, /* Control-n */
+ { ISFUNC, rl_operate_and_get_next }, /* Control-o */
+ { ISFUNC, rl_get_previous_history }, /* Control-p */
+ { ISFUNC, rl_quoted_insert }, /* Control-q */
+ { ISFUNC, rl_reverse_search_history }, /* Control-r */
+ { ISFUNC, rl_forward_search_history }, /* Control-s */
+ { ISFUNC, rl_transpose_chars }, /* Control-t */
+ { ISFUNC, rl_unix_line_discard }, /* Control-u */
+ { ISFUNC, rl_quoted_insert }, /* Control-v */
+ { ISFUNC, rl_unix_word_rubout }, /* Control-w */
+ { ISKMAP, (rl_command_func_t *)emacs_ctlx_keymap }, /* Control-x */
+ { ISFUNC, rl_yank }, /* Control-y */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-z */
+ { ISKMAP, (rl_command_func_t *)emacs_meta_keymap }, /* Control-[ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-\ */
+ { ISFUNC, rl_char_search }, /* Control-] */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-^ */
+ { ISFUNC, rl_undo_command }, /* Control-_ */
+
+ /* The start of printing characters. */
+ { ISFUNC, rl_insert }, /* SPACE */
+ { ISFUNC, rl_insert }, /* ! */
+ { ISFUNC, rl_insert }, /* " */
+ { ISFUNC, rl_insert }, /* # */
+ { ISFUNC, rl_insert }, /* $ */
+ { ISFUNC, rl_insert }, /* % */
+ { ISFUNC, rl_insert }, /* & */
+ { ISFUNC, rl_insert }, /* ' */
+ { ISFUNC, rl_insert }, /* ( */
+ { ISFUNC, rl_insert }, /* ) */
+ { ISFUNC, rl_insert }, /* * */
+ { ISFUNC, rl_insert }, /* + */
+ { ISFUNC, rl_insert }, /* , */
+ { ISFUNC, rl_insert }, /* - */
+ { ISFUNC, rl_insert }, /* . */
+ { ISFUNC, rl_insert }, /* / */
+
+ /* Regular digits. */
+ { ISFUNC, rl_insert }, /* 0 */
+ { ISFUNC, rl_insert }, /* 1 */
+ { ISFUNC, rl_insert }, /* 2 */
+ { ISFUNC, rl_insert }, /* 3 */
+ { ISFUNC, rl_insert }, /* 4 */
+ { ISFUNC, rl_insert }, /* 5 */
+ { ISFUNC, rl_insert }, /* 6 */
+ { ISFUNC, rl_insert }, /* 7 */
+ { ISFUNC, rl_insert }, /* 8 */
+ { ISFUNC, rl_insert }, /* 9 */
+
+ /* A little more punctuation. */
+ { ISFUNC, rl_insert }, /* : */
+ { ISFUNC, rl_insert }, /* ; */
+ { ISFUNC, rl_insert }, /* < */
+ { ISFUNC, rl_insert }, /* = */
+ { ISFUNC, rl_insert }, /* > */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* @ */
+
+ /* Uppercase alphabet. */
+ { ISFUNC, rl_insert }, /* A */
+ { ISFUNC, rl_insert }, /* B */
+ { ISFUNC, rl_insert }, /* C */
+ { ISFUNC, rl_insert }, /* D */
+ { ISFUNC, rl_insert }, /* E */
+ { ISFUNC, rl_insert }, /* F */
+ { ISFUNC, rl_insert }, /* G */
+ { ISFUNC, rl_insert }, /* H */
+ { ISFUNC, rl_insert }, /* I */
+ { ISFUNC, rl_insert }, /* J */
+ { ISFUNC, rl_insert }, /* K */
+ { ISFUNC, rl_insert }, /* L */
+ { ISFUNC, rl_insert }, /* M */
+ { ISFUNC, rl_insert }, /* N */
+ { ISFUNC, rl_insert }, /* O */
+ { ISFUNC, rl_insert }, /* P */
+ { ISFUNC, rl_insert }, /* Q */
+ { ISFUNC, rl_insert }, /* R */
+ { ISFUNC, rl_insert }, /* S */
+ { ISFUNC, rl_insert }, /* T */
+ { ISFUNC, rl_insert }, /* U */
+ { ISFUNC, rl_insert }, /* V */
+ { ISFUNC, rl_insert }, /* W */
+ { ISFUNC, rl_insert }, /* X */
+ { ISFUNC, rl_insert }, /* Y */
+ { ISFUNC, rl_insert }, /* Z */
+
+ /* Some more punctuation. */
+ { ISFUNC, rl_insert }, /* [ */
+ { ISFUNC, rl_insert }, /* \ */
+ { ISFUNC, rl_insert }, /* ] */
+ { ISFUNC, rl_insert }, /* ^ */
+ { ISFUNC, rl_insert }, /* _ */
+ { ISFUNC, rl_insert }, /* ` */
+
+ /* Lowercase alphabet. */
+ { ISFUNC, rl_insert }, /* a */
+ { ISFUNC, rl_insert }, /* b */
+ { ISFUNC, rl_insert }, /* c */
+ { ISFUNC, rl_insert }, /* d */
+ { ISFUNC, rl_insert }, /* e */
+ { ISFUNC, rl_insert }, /* f */
+ { ISFUNC, rl_insert }, /* g */
+ { ISFUNC, rl_insert }, /* h */
+ { ISFUNC, rl_insert }, /* i */
+ { ISFUNC, rl_insert }, /* j */
+ { ISFUNC, rl_insert }, /* k */
+ { ISFUNC, rl_insert }, /* l */
+ { ISFUNC, rl_insert }, /* m */
+ { ISFUNC, rl_insert }, /* n */
+ { ISFUNC, rl_insert }, /* o */
+ { ISFUNC, rl_insert }, /* p */
+ { ISFUNC, rl_insert }, /* q */
+ { ISFUNC, rl_insert }, /* r */
+ { ISFUNC, rl_insert }, /* s */
+ { ISFUNC, rl_insert }, /* t */
+ { ISFUNC, rl_insert }, /* u */
+ { ISFUNC, rl_insert }, /* v */
+ { ISFUNC, rl_insert }, /* w */
+ { ISFUNC, rl_insert }, /* x */
+ { ISFUNC, rl_insert }, /* y */
+ { ISFUNC, rl_insert }, /* z */
+
+ /* Final punctuation. */
+ { ISFUNC, rl_insert }, /* { */
+ { ISFUNC, rl_insert }, /* | */
+ { ISFUNC, rl_insert }, /* } */
+ { ISFUNC, rl_insert }, /* ~ */
+ { ISFUNC, rl_rubout }, /* RUBOUT */
+
+#if KEYMAP_SIZE > 128
+ /* Pure 8-bit characters (128 - 159).
+ These might be used in some
+ character sets. */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* ? */
+
+ /* ISO Latin-1 characters (160 - 255) */
+ { ISFUNC, rl_insert }, /* No-break space */
+ { ISFUNC, rl_insert }, /* Inverted exclamation mark */
+ { ISFUNC, rl_insert }, /* Cent sign */
+ { ISFUNC, rl_insert }, /* Pound sign */
+ { ISFUNC, rl_insert }, /* Currency sign */
+ { ISFUNC, rl_insert }, /* Yen sign */
+ { ISFUNC, rl_insert }, /* Broken bar */
+ { ISFUNC, rl_insert }, /* Section sign */
+ { ISFUNC, rl_insert }, /* Diaeresis */
+ { ISFUNC, rl_insert }, /* Copyright sign */
+ { ISFUNC, rl_insert }, /* Feminine ordinal indicator */
+ { ISFUNC, rl_insert }, /* Left pointing double angle quotation mark */
+ { ISFUNC, rl_insert }, /* Not sign */
+ { ISFUNC, rl_insert }, /* Soft hyphen */
+ { ISFUNC, rl_insert }, /* Registered sign */
+ { ISFUNC, rl_insert }, /* Macron */
+ { ISFUNC, rl_insert }, /* Degree sign */
+ { ISFUNC, rl_insert }, /* Plus-minus sign */
+ { ISFUNC, rl_insert }, /* Superscript two */
+ { ISFUNC, rl_insert }, /* Superscript three */
+ { ISFUNC, rl_insert }, /* Acute accent */
+ { ISFUNC, rl_insert }, /* Micro sign */
+ { ISFUNC, rl_insert }, /* Pilcrow sign */
+ { ISFUNC, rl_insert }, /* Middle dot */
+ { ISFUNC, rl_insert }, /* Cedilla */
+ { ISFUNC, rl_insert }, /* Superscript one */
+ { ISFUNC, rl_insert }, /* Masculine ordinal indicator */
+ { ISFUNC, rl_insert }, /* Right pointing double angle quotation mark */
+ { ISFUNC, rl_insert }, /* Vulgar fraction one quarter */
+ { ISFUNC, rl_insert }, /* Vulgar fraction one half */
+ { ISFUNC, rl_insert }, /* Vulgar fraction three quarters */
+ { ISFUNC, rl_insert }, /* Inverted questionk mark */
+ { ISFUNC, rl_insert }, /* Latin capital letter a with grave */
+ { ISFUNC, rl_insert }, /* Latin capital letter a with acute */
+ { ISFUNC, rl_insert }, /* Latin capital letter a with circumflex */
+ { ISFUNC, rl_insert }, /* Latin capital letter a with tilde */
+ { ISFUNC, rl_insert }, /* Latin capital letter a with diaeresis */
+ { ISFUNC, rl_insert }, /* Latin capital letter a with ring above */
+ { ISFUNC, rl_insert }, /* Latin capital letter ae */
+ { ISFUNC, rl_insert }, /* Latin capital letter c with cedilla */
+ { ISFUNC, rl_insert }, /* Latin capital letter e with grave */
+ { ISFUNC, rl_insert }, /* Latin capital letter e with acute */
+ { ISFUNC, rl_insert }, /* Latin capital letter e with circumflex */
+ { ISFUNC, rl_insert }, /* Latin capital letter e with diaeresis */
+ { ISFUNC, rl_insert }, /* Latin capital letter i with grave */
+ { ISFUNC, rl_insert }, /* Latin capital letter i with acute */
+ { ISFUNC, rl_insert }, /* Latin capital letter i with circumflex */
+ { ISFUNC, rl_insert }, /* Latin capital letter i with diaeresis */
+ { ISFUNC, rl_insert }, /* Latin capital letter eth (Icelandic) */
+ { ISFUNC, rl_insert }, /* Latin capital letter n with tilde */
+ { ISFUNC, rl_insert }, /* Latin capital letter o with grave */
+ { ISFUNC, rl_insert }, /* Latin capital letter o with acute */
+ { ISFUNC, rl_insert }, /* Latin capital letter o with circumflex */
+ { ISFUNC, rl_insert }, /* Latin capital letter o with tilde */
+ { ISFUNC, rl_insert }, /* Latin capital letter o with diaeresis */
+ { ISFUNC, rl_insert }, /* Multiplication sign */
+ { ISFUNC, rl_insert }, /* Latin capital letter o with stroke */
+ { ISFUNC, rl_insert }, /* Latin capital letter u with grave */
+ { ISFUNC, rl_insert }, /* Latin capital letter u with acute */
+ { ISFUNC, rl_insert }, /* Latin capital letter u with circumflex */
+ { ISFUNC, rl_insert }, /* Latin capital letter u with diaeresis */
+ { ISFUNC, rl_insert }, /* Latin capital letter Y with acute */
+ { ISFUNC, rl_insert }, /* Latin capital letter thorn (Icelandic) */
+ { ISFUNC, rl_insert }, /* Latin small letter sharp s (German) */
+ { ISFUNC, rl_insert }, /* Latin small letter a with grave */
+ { ISFUNC, rl_insert }, /* Latin small letter a with acute */
+ { ISFUNC, rl_insert }, /* Latin small letter a with circumflex */
+ { ISFUNC, rl_insert }, /* Latin small letter a with tilde */
+ { ISFUNC, rl_insert }, /* Latin small letter a with diaeresis */
+ { ISFUNC, rl_insert }, /* Latin small letter a with ring above */
+ { ISFUNC, rl_insert }, /* Latin small letter ae */
+ { ISFUNC, rl_insert }, /* Latin small letter c with cedilla */
+ { ISFUNC, rl_insert }, /* Latin small letter e with grave */
+ { ISFUNC, rl_insert }, /* Latin small letter e with acute */
+ { ISFUNC, rl_insert }, /* Latin small letter e with circumflex */
+ { ISFUNC, rl_insert }, /* Latin small letter e with diaeresis */
+ { ISFUNC, rl_insert }, /* Latin small letter i with grave */
+ { ISFUNC, rl_insert }, /* Latin small letter i with acute */
+ { ISFUNC, rl_insert }, /* Latin small letter i with circumflex */
+ { ISFUNC, rl_insert }, /* Latin small letter i with diaeresis */
+ { ISFUNC, rl_insert }, /* Latin small letter eth (Icelandic) */
+ { ISFUNC, rl_insert }, /* Latin small letter n with tilde */
+ { ISFUNC, rl_insert }, /* Latin small letter o with grave */
+ { ISFUNC, rl_insert }, /* Latin small letter o with acute */
+ { ISFUNC, rl_insert }, /* Latin small letter o with circumflex */
+ { ISFUNC, rl_insert }, /* Latin small letter o with tilde */
+ { ISFUNC, rl_insert }, /* Latin small letter o with diaeresis */
+ { ISFUNC, rl_insert }, /* Division sign */
+ { ISFUNC, rl_insert }, /* Latin small letter o with stroke */
+ { ISFUNC, rl_insert }, /* Latin small letter u with grave */
+ { ISFUNC, rl_insert }, /* Latin small letter u with acute */
+ { ISFUNC, rl_insert }, /* Latin small letter u with circumflex */
+ { ISFUNC, rl_insert }, /* Latin small letter u with diaeresis */
+ { ISFUNC, rl_insert }, /* Latin small letter y with acute */
+ { ISFUNC, rl_insert }, /* Latin small letter thorn (Icelandic) */
+ { ISFUNC, rl_insert } /* Latin small letter y with diaeresis */
+#endif /* KEYMAP_SIZE > 128 */
+};
+
+KEYMAP_ENTRY_ARRAY emacs_meta_keymap = {
+
+ /* Meta keys. Just like above, but the high bit is set. */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-@ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-a */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-b */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-c */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-d */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-e */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-f */
+ { ISFUNC, rl_abort }, /* Meta-Control-g */
+ { ISFUNC, rl_backward_kill_word }, /* Meta-Control-h */
+ { ISFUNC, rl_tab_insert }, /* Meta-Control-i */
+ { ISFUNC, rl_vi_editing_mode }, /* Meta-Control-j */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-k */
+ { ISFUNC, rl_clear_display }, /* Meta-Control-l */
+ { ISFUNC, rl_vi_editing_mode }, /* Meta-Control-m */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-n */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-o */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-p */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-q */
+ { ISFUNC, rl_revert_line }, /* Meta-Control-r */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-s */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-t */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-u */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-v */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-w */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-x */
+ { ISFUNC, rl_yank_nth_arg }, /* Meta-Control-y */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-z */
+
+ { ISFUNC, rl_complete }, /* Meta-Control-[ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-\ */
+ { ISFUNC, rl_backward_char_search }, /* Meta-Control-] */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-^ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-_ */
+
+ /* The start of printing characters. */
+ { ISFUNC, rl_set_mark }, /* Meta-SPACE */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-! */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-" */
+ { ISFUNC, rl_insert_comment }, /* Meta-# */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-$ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-% */
+ { ISFUNC, rl_tilde_expand }, /* Meta-& */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-' */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-( */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-) */
+ { ISFUNC, rl_insert_completions }, /* Meta-* */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-+ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-, */
+ { ISFUNC, rl_digit_argument }, /* Meta-- */
+ { ISFUNC, rl_yank_last_arg}, /* Meta-. */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-/ */
+
+ /* Regular digits. */
+ { ISFUNC, rl_digit_argument }, /* Meta-0 */
+ { ISFUNC, rl_digit_argument }, /* Meta-1 */
+ { ISFUNC, rl_digit_argument }, /* Meta-2 */
+ { ISFUNC, rl_digit_argument }, /* Meta-3 */
+ { ISFUNC, rl_digit_argument }, /* Meta-4 */
+ { ISFUNC, rl_digit_argument }, /* Meta-5 */
+ { ISFUNC, rl_digit_argument }, /* Meta-6 */
+ { ISFUNC, rl_digit_argument }, /* Meta-7 */
+ { ISFUNC, rl_digit_argument }, /* Meta-8 */
+ { ISFUNC, rl_digit_argument }, /* Meta-9 */
+
+ /* A little more punctuation. */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-: */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-; */
+ { ISFUNC, rl_beginning_of_history }, /* Meta-< */
+ { ISFUNC, rl_possible_completions }, /* Meta-= */
+ { ISFUNC, rl_end_of_history }, /* Meta-> */
+ { ISFUNC, rl_possible_completions }, /* Meta-? */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-@ */
+
+ /* Uppercase alphabet. */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-A */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-B */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-C */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-D */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-E */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-F */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-G */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-H */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-I */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-J */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-K */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-L */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-M */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-N */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-O */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-P */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-Q */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-R */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-S */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-T */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-U */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-V */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-W */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-X */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-Y */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-Z */
+
+ /* Some more punctuation. */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-[ */ /* was rl_arrow_keys */
+ { ISFUNC, rl_delete_horizontal_space }, /* Meta-\ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-] */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-^ */
+ { ISFUNC, rl_yank_last_arg }, /* Meta-_ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-` */
+
+ /* Lowercase alphabet. */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-a */
+ { ISFUNC, rl_backward_word }, /* Meta-b */
+ { ISFUNC, rl_capitalize_word }, /* Meta-c */
+ { ISFUNC, rl_kill_word }, /* Meta-d */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-e */
+ { ISFUNC, rl_forward_word }, /* Meta-f */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-g */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-h */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-i */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-j */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-k */
+ { ISFUNC, rl_downcase_word }, /* Meta-l */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-m */
+ { ISFUNC, rl_noninc_forward_search }, /* Meta-n */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-o */ /* was rl_arrow_keys */
+ { ISFUNC, rl_noninc_reverse_search }, /* Meta-p */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-q */
+ { ISFUNC, rl_revert_line }, /* Meta-r */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-s */
+ { ISFUNC, rl_transpose_words }, /* Meta-t */
+ { ISFUNC, rl_upcase_word }, /* Meta-u */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-v */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-w */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-x */
+ { ISFUNC, rl_yank_pop }, /* Meta-y */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-z */
+
+ /* Final punctuation. */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-{ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-| */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-} */
+ { ISFUNC, rl_tilde_expand }, /* Meta-~ */
+ { ISFUNC, rl_backward_kill_word }, /* Meta-rubout */
+
+#if KEYMAP_SIZE > 128
+ /* Undefined keys. */
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 }
+#endif /* KEYMAP_SIZE > 128 */
+};
+
+KEYMAP_ENTRY_ARRAY emacs_ctlx_keymap = {
+
+ /* Control keys. */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-@ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-a */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-b */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-c */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-d */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-e */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-f */
+ { ISFUNC, rl_abort }, /* Control-g */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-h */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-i */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-j */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-k */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-l */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-m */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-n */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-o */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-p */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-q */
+ { ISFUNC, rl_re_read_init_file }, /* Control-r */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-s */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-t */
+ { ISFUNC, rl_undo_command }, /* Control-u */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-v */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-w */
+ { ISFUNC, rl_exchange_point_and_mark }, /* Control-x */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-y */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-z */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-[ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-\ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-] */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-^ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-_ */
+
+ /* The start of printing characters. */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* SPACE */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* ! */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* " */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* # */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* $ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* % */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* & */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* ' */
+ { ISFUNC, rl_start_kbd_macro }, /* ( */
+ { ISFUNC, rl_end_kbd_macro }, /* ) */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* * */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* + */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* , */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* - */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* . */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* / */
+
+ /* Regular digits. */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* 0 */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* 1 */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* 2 */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* 3 */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* 4 */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* 5 */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* 6 */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* 7 */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* 8 */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* 9 */
+
+ /* A little more punctuation. */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* : */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* ; */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* < */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* = */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* > */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* ? */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* @ */
+
+ /* Uppercase alphabet. */
+ { ISFUNC, rl_do_lowercase_version }, /* A */
+ { ISFUNC, rl_do_lowercase_version }, /* B */
+ { ISFUNC, rl_do_lowercase_version }, /* C */
+ { ISFUNC, rl_do_lowercase_version }, /* D */
+ { ISFUNC, rl_do_lowercase_version }, /* E */
+ { ISFUNC, rl_do_lowercase_version }, /* F */
+ { ISFUNC, rl_do_lowercase_version }, /* G */
+ { ISFUNC, rl_do_lowercase_version }, /* H */
+ { ISFUNC, rl_do_lowercase_version }, /* I */
+ { ISFUNC, rl_do_lowercase_version }, /* J */
+ { ISFUNC, rl_do_lowercase_version }, /* K */
+ { ISFUNC, rl_do_lowercase_version }, /* L */
+ { ISFUNC, rl_do_lowercase_version }, /* M */
+ { ISFUNC, rl_do_lowercase_version }, /* N */
+ { ISFUNC, rl_do_lowercase_version }, /* O */
+ { ISFUNC, rl_do_lowercase_version }, /* P */
+ { ISFUNC, rl_do_lowercase_version }, /* Q */
+ { ISFUNC, rl_do_lowercase_version }, /* R */
+ { ISFUNC, rl_do_lowercase_version }, /* S */
+ { ISFUNC, rl_do_lowercase_version }, /* T */
+ { ISFUNC, rl_do_lowercase_version }, /* U */
+ { ISFUNC, rl_do_lowercase_version }, /* V */
+ { ISFUNC, rl_do_lowercase_version }, /* W */
+ { ISFUNC, rl_do_lowercase_version }, /* X */
+ { ISFUNC, rl_do_lowercase_version }, /* Y */
+ { ISFUNC, rl_do_lowercase_version }, /* Z */
+
+ /* Some more punctuation. */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* [ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* \ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* ] */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* ^ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* _ */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* ` */
+
+ /* Lowercase alphabet. */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* a */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* b */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* c */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* d */
+ { ISFUNC, rl_call_last_kbd_macro }, /* e */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* f */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* g */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* h */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* i */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* j */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* k */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* l */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* m */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* n */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* o */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* p */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* q */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* r */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* s */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* t */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* u */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* v */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* w */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* x */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* y */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* z */
+
+ /* Final punctuation. */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* { */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* | */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* } */
+ { ISFUNC, (rl_command_func_t *)0x0 }, /* ~ */
+ { ISFUNC, rl_backward_kill_line }, /* RUBOUT */
+
+#if KEYMAP_SIZE > 128
+ /* Undefined keys. */
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 },
+ { ISFUNC, (rl_command_func_t *)0x0 }
+#endif /* KEYMAP_SIZE > 128 */
+};
diff --git a/third_party/readline/funmap.c b/third_party/readline/funmap.c
new file mode 100644
index 000000000..588ca7596
--- /dev/null
+++ b/third_party/readline/funmap.c
@@ -0,0 +1,273 @@
+/* funmap.c -- attach names to functions. */
+
+/* Copyright (C) 1987-2021 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#if !defined (BUFSIZ)
+#include
+#endif /* BUFSIZ */
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#include "rlconf.h"
+#include "readline.h"
+
+#include "xmalloc.h"
+
+#ifdef __STDC__
+typedef int QSFUNC (const void *, const void *);
+#else
+typedef int QSFUNC ();
+#endif
+
+extern int _rl_qsort_string_compare (char **, char **);
+
+FUNMAP **funmap;
+static int funmap_size;
+static int funmap_entry;
+
+/* After initializing the function map, this is the index of the first
+ program specific function. */
+int funmap_program_specific_entry_start;
+
+static const FUNMAP default_funmap[] = {
+ { "abort", rl_abort },
+ { "accept-line", rl_newline },
+ { "arrow-key-prefix", rl_arrow_keys },
+ { "backward-byte", rl_backward_byte },
+ { "backward-char", rl_backward_char },
+ { "backward-delete-char", rl_rubout },
+ { "backward-kill-line", rl_backward_kill_line },
+ { "backward-kill-word", rl_backward_kill_word },
+ { "backward-word", rl_backward_word },
+ { "beginning-of-history", rl_beginning_of_history },
+ { "beginning-of-line", rl_beg_of_line },
+ { "bracketed-paste-begin", rl_bracketed_paste_begin },
+ { "call-last-kbd-macro", rl_call_last_kbd_macro },
+ { "capitalize-word", rl_capitalize_word },
+ { "character-search", rl_char_search },
+ { "character-search-backward", rl_backward_char_search },
+ { "clear-display", rl_clear_display },
+ { "clear-screen", rl_clear_screen },
+ { "complete", rl_complete },
+ { "copy-backward-word", rl_copy_backward_word },
+ { "copy-forward-word", rl_copy_forward_word },
+ { "copy-region-as-kill", rl_copy_region_to_kill },
+ { "delete-char", rl_delete },
+ { "delete-char-or-list", rl_delete_or_show_completions },
+ { "delete-horizontal-space", rl_delete_horizontal_space },
+ { "digit-argument", rl_digit_argument },
+ { "do-lowercase-version", rl_do_lowercase_version },
+ { "downcase-word", rl_downcase_word },
+ { "dump-functions", rl_dump_functions },
+ { "dump-macros", rl_dump_macros },
+ { "dump-variables", rl_dump_variables },
+ { "emacs-editing-mode", rl_emacs_editing_mode },
+ { "end-kbd-macro", rl_end_kbd_macro },
+ { "end-of-history", rl_end_of_history },
+ { "end-of-line", rl_end_of_line },
+ { "exchange-point-and-mark", rl_exchange_point_and_mark },
+ { "fetch-history", rl_fetch_history },
+ { "forward-backward-delete-char", rl_rubout_or_delete },
+ { "forward-byte", rl_forward_byte },
+ { "forward-char", rl_forward_char },
+ { "forward-search-history", rl_forward_search_history },
+ { "forward-word", rl_forward_word },
+ { "history-search-backward", rl_history_search_backward },
+ { "history-search-forward", rl_history_search_forward },
+ { "history-substring-search-backward", rl_history_substr_search_backward },
+ { "history-substring-search-forward", rl_history_substr_search_forward },
+ { "insert-comment", rl_insert_comment },
+ { "insert-completions", rl_insert_completions },
+ { "kill-whole-line", rl_kill_full_line },
+ { "kill-line", rl_kill_line },
+ { "kill-region", rl_kill_region },
+ { "kill-word", rl_kill_word },
+ { "menu-complete", rl_menu_complete },
+ { "menu-complete-backward", rl_backward_menu_complete },
+ { "next-history", rl_get_next_history },
+ { "next-screen-line", rl_next_screen_line },
+ { "non-incremental-forward-search-history", rl_noninc_forward_search },
+ { "non-incremental-reverse-search-history", rl_noninc_reverse_search },
+ { "non-incremental-forward-search-history-again", rl_noninc_forward_search_again },
+ { "non-incremental-reverse-search-history-again", rl_noninc_reverse_search_again },
+ { "old-menu-complete", rl_old_menu_complete },
+ { "operate-and-get-next", rl_operate_and_get_next },
+ { "overwrite-mode", rl_overwrite_mode },
+#if defined (_WIN32)
+ { "paste-from-clipboard", rl_paste_from_clipboard },
+#endif
+ { "possible-completions", rl_possible_completions },
+ { "previous-history", rl_get_previous_history },
+ { "previous-screen-line", rl_previous_screen_line },
+ { "print-last-kbd-macro", rl_print_last_kbd_macro },
+ { "quoted-insert", rl_quoted_insert },
+ { "re-read-init-file", rl_re_read_init_file },
+ { "redraw-current-line", rl_refresh_line},
+ { "reverse-search-history", rl_reverse_search_history },
+ { "revert-line", rl_revert_line },
+ { "self-insert", rl_insert },
+ { "set-mark", rl_set_mark },
+ { "skip-csi-sequence", rl_skip_csi_sequence },
+ { "start-kbd-macro", rl_start_kbd_macro },
+ { "tab-insert", rl_tab_insert },
+ { "tilde-expand", rl_tilde_expand },
+ { "transpose-chars", rl_transpose_chars },
+ { "transpose-words", rl_transpose_words },
+ { "tty-status", rl_tty_status },
+ { "undo", rl_undo_command },
+ { "universal-argument", rl_universal_argument },
+ { "unix-filename-rubout", rl_unix_filename_rubout },
+ { "unix-line-discard", rl_unix_line_discard },
+ { "unix-word-rubout", rl_unix_word_rubout },
+ { "upcase-word", rl_upcase_word },
+ { "yank", rl_yank },
+ { "yank-last-arg", rl_yank_last_arg },
+ { "yank-nth-arg", rl_yank_nth_arg },
+ { "yank-pop", rl_yank_pop },
+
+#if defined (VI_MODE)
+ { "vi-append-eol", rl_vi_append_eol },
+ { "vi-append-mode", rl_vi_append_mode },
+ { "vi-arg-digit", rl_vi_arg_digit },
+ { "vi-back-to-indent", rl_vi_back_to_indent },
+ { "vi-backward-bigword", rl_vi_bWord },
+ { "vi-backward-word", rl_vi_bword },
+ { "vi-bWord", rl_vi_bWord },
+ { "vi-bword", rl_vi_bword }, /* BEWARE: name matching is case insensitive */
+ { "vi-change-case", rl_vi_change_case },
+ { "vi-change-char", rl_vi_change_char },
+ { "vi-change-to", rl_vi_change_to },
+ { "vi-char-search", rl_vi_char_search },
+ { "vi-column", rl_vi_column },
+ { "vi-complete", rl_vi_complete },
+ { "vi-delete", rl_vi_delete },
+ { "vi-delete-to", rl_vi_delete_to },
+ { "vi-eWord", rl_vi_eWord },
+ { "vi-editing-mode", rl_vi_editing_mode },
+ { "vi-end-bigword", rl_vi_eWord },
+ { "vi-end-word", rl_vi_end_word },
+ { "vi-eof-maybe", rl_vi_eof_maybe },
+ { "vi-eword", rl_vi_eword }, /* BEWARE: name matching is case insensitive */
+ { "vi-fWord", rl_vi_fWord },
+ { "vi-fetch-history", rl_vi_fetch_history },
+ { "vi-first-print", rl_vi_first_print },
+ { "vi-forward-bigword", rl_vi_fWord },
+ { "vi-forward-word", rl_vi_fword },
+ { "vi-fword", rl_vi_fword }, /* BEWARE: name matching is case insensitive */
+ { "vi-goto-mark", rl_vi_goto_mark },
+ { "vi-insert-beg", rl_vi_insert_beg },
+ { "vi-insertion-mode", rl_vi_insert_mode },
+ { "vi-match", rl_vi_match },
+ { "vi-movement-mode", rl_vi_movement_mode },
+ { "vi-next-word", rl_vi_next_word },
+ { "vi-overstrike", rl_vi_overstrike },
+ { "vi-overstrike-delete", rl_vi_overstrike_delete },
+ { "vi-prev-word", rl_vi_prev_word },
+ { "vi-put", rl_vi_put },
+ { "vi-redo", rl_vi_redo },
+ { "vi-replace", rl_vi_replace },
+ { "vi-rubout", rl_vi_rubout },
+ { "vi-search", rl_vi_search },
+ { "vi-search-again", rl_vi_search_again },
+ { "vi-set-mark", rl_vi_set_mark },
+ { "vi-subst", rl_vi_subst },
+ { "vi-tilde-expand", rl_vi_tilde_expand },
+ { "vi-undo", rl_vi_undo },
+ { "vi-unix-word-rubout", rl_vi_unix_word_rubout },
+ { "vi-yank-arg", rl_vi_yank_arg },
+ { "vi-yank-pop", rl_vi_yank_pop },
+ { "vi-yank-to", rl_vi_yank_to },
+#endif /* VI_MODE */
+
+ {(char *)NULL, (rl_command_func_t *)NULL }
+};
+
+int
+rl_add_funmap_entry (const char *name, rl_command_func_t *function)
+{
+ if (funmap_entry + 2 >= funmap_size)
+ {
+ funmap_size += 64;
+ funmap = (FUNMAP **)xrealloc (funmap, funmap_size * sizeof (FUNMAP *));
+ }
+
+ funmap[funmap_entry] = (FUNMAP *)xmalloc (sizeof (FUNMAP));
+ funmap[funmap_entry]->name = name;
+ funmap[funmap_entry]->function = function;
+
+ funmap[++funmap_entry] = (FUNMAP *)NULL;
+ return funmap_entry;
+}
+
+static int funmap_initialized;
+
+/* Make the funmap contain all of the default entries. */
+void
+rl_initialize_funmap (void)
+{
+ register int i;
+
+ if (funmap_initialized)
+ return;
+
+ for (i = 0; default_funmap[i].name; i++)
+ rl_add_funmap_entry (default_funmap[i].name, default_funmap[i].function);
+
+ funmap_initialized = 1;
+ funmap_program_specific_entry_start = i;
+}
+
+/* Produce a NULL terminated array of known function names. The array
+ is sorted. The array itself is allocated, but not the strings inside.
+ You should free () the array when you done, but not the pointers. */
+const char **
+rl_funmap_names (void)
+{
+ const char **result;
+ int result_size, result_index;
+
+ /* Make sure that the function map has been initialized. */
+ rl_initialize_funmap ();
+
+ for (result_index = result_size = 0, result = (const char **)NULL; funmap[result_index]; result_index++)
+ {
+ if (result_index + 2 > result_size)
+ {
+ result_size += 20;
+ result = (const char **)xrealloc (result, result_size * sizeof (char *));
+ }
+
+ result[result_index] = funmap[result_index]->name;
+ result[result_index + 1] = (char *)NULL;
+ }
+
+ qsort (result, result_index, sizeof (char *), (QSFUNC *)_rl_qsort_string_compare);
+ return (result);
+}
diff --git a/third_party/readline/histexpand.c b/third_party/readline/histexpand.c
new file mode 100644
index 000000000..3ce03568b
--- /dev/null
+++ b/third_party/readline/histexpand.c
@@ -0,0 +1,1718 @@
+/* histexpand.c -- history expansion. */
+
+/* Copyright (C) 1989-2021 Free Software Foundation, Inc.
+
+ This file contains the GNU History Library (History), a set of
+ routines for managing the text of previously typed lines.
+
+ History is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ History 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 History. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#if defined (HAVE_UNISTD_H)
+# ifndef _MINIX
+# include
+# endif
+# include
+#endif
+
+#include "rlmbutil.h"
+
+#include "history.h"
+#include "histlib.h"
+#include "chardefs.h"
+
+#include "rlshell.h"
+#include "xmalloc.h"
+
+#define HISTORY_WORD_DELIMITERS " \t\n;&()|<>"
+#define HISTORY_QUOTE_CHARACTERS "\"'`"
+#define HISTORY_EVENT_DELIMITERS "^$*%-"
+
+#define slashify_in_quotes "\\`\"$"
+
+#define fielddelim(c) (whitespace(c) || (c) == '\n')
+
+typedef int _hist_search_func_t (const char *, int);
+
+static char error_pointer;
+
+static char *subst_lhs;
+static char *subst_rhs;
+static int subst_lhs_len;
+static int subst_rhs_len;
+
+/* Characters that delimit history event specifications and separate event
+ specifications from word designators. Static for now */
+static char *history_event_delimiter_chars = HISTORY_EVENT_DELIMITERS;
+
+static char *get_history_word_specifier (char *, char *, int *);
+static int history_tokenize_word (const char *, int);
+static char **history_tokenize_internal (const char *, int, int *);
+static char *history_substring (const char *, int, int);
+static void freewords (char **, int);
+static char *history_find_word (char *, int);
+
+static char *quote_breaks (char *);
+
+/* Variables exported by this file. */
+/* The character that represents the start of a history expansion
+ request. This is usually `!'. */
+char history_expansion_char = '!';
+
+/* The character that invokes word substitution if found at the start of
+ a line. This is usually `^'. */
+char history_subst_char = '^';
+
+/* During tokenization, if this character is seen as the first character
+ of a word, then it, and all subsequent characters up to a newline are
+ ignored. For a Bourne shell, this should be '#'. Bash special cases
+ the interactive comment character to not be a comment delimiter. */
+char history_comment_char = '\0';
+
+/* The list of characters which inhibit the expansion of text if found
+ immediately following history_expansion_char. */
+char *history_no_expand_chars = " \t\n\r=";
+
+/* If set to a non-zero value, single quotes inhibit history expansion.
+ The default is 0. */
+int history_quotes_inhibit_expansion = 0;
+
+/* Used to split words by history_tokenize_internal. */
+char *history_word_delimiters = HISTORY_WORD_DELIMITERS;
+
+/* If set, this points to a function that is called to verify that a
+ particular history expansion should be performed. */
+rl_linebuf_func_t *history_inhibit_expansion_function;
+
+int history_quoting_state = 0;
+
+/* **************************************************************** */
+/* */
+/* History Expansion */
+/* */
+/* **************************************************************** */
+
+/* Hairy history expansion on text, not tokens. This is of general
+ use, and thus belongs in this library. */
+
+/* The last string searched for by a !?string? search. */
+static char *search_string;
+/* The last string matched by a !?string? search. */
+static char *search_match;
+
+/* Return the event specified at TEXT + OFFSET modifying OFFSET to
+ point to after the event specifier. Just a pointer to the history
+ line is returned; NULL is returned in the event of a bad specifier.
+ You pass STRING with *INDEX equal to the history_expansion_char that
+ begins this specification.
+ DELIMITING_QUOTE is a character that is allowed to end the string
+ specification for what to search for in addition to the normal
+ characters `:', ` ', `\t', `\n', and sometimes `?'.
+ So you might call this function like:
+ line = get_history_event ("!echo:p", &index, 0); */
+char *
+get_history_event (const char *string, int *caller_index, int delimiting_quote)
+{
+ register int i;
+ register char c;
+ HIST_ENTRY *entry;
+ int which, sign, local_index, substring_okay;
+ _hist_search_func_t *search_func;
+ char *temp;
+
+ /* The event can be specified in a number of ways.
+
+ !! the previous command
+ !n command line N
+ !-n current command-line minus N
+ !str the most recent command starting with STR
+ !?str[?]
+ the most recent command containing STR
+
+ All values N are determined via HISTORY_BASE. */
+
+ i = *caller_index;
+
+ if (string[i] != history_expansion_char)
+ return ((char *)NULL);
+
+ /* Move on to the specification. */
+ i++;
+
+ sign = 1;
+ substring_okay = 0;
+
+#define RETURN_ENTRY(e, w) \
+ return ((e = history_get (w)) ? e->line : (char *)NULL)
+
+ /* Handle !! case. */
+ if (string[i] == history_expansion_char)
+ {
+ i++;
+ which = history_base + (history_length - 1);
+ *caller_index = i;
+ RETURN_ENTRY (entry, which);
+ }
+
+ /* Hack case of numeric line specification. */
+ if (string[i] == '-' && _rl_digit_p (string[i+1]))
+ {
+ sign = -1;
+ i++;
+ }
+
+ if (_rl_digit_p (string[i]))
+ {
+ /* Get the extent of the digits and compute the value. */
+ for (which = 0; _rl_digit_p (string[i]); i++)
+ which = (which * 10) + _rl_digit_value (string[i]);
+
+ *caller_index = i;
+
+ if (sign < 0)
+ which = (history_length + history_base) - which;
+
+ RETURN_ENTRY (entry, which);
+ }
+
+ /* This must be something to search for. If the spec begins with
+ a '?', then the string may be anywhere on the line. Otherwise,
+ the string must be found at the start of a line. */
+ if (string[i] == '?')
+ {
+ substring_okay++;
+ i++;
+ }
+
+ /* Only a closing `?' or a newline delimit a substring search string. */
+ for (local_index = i; c = string[i]; i++)
+ {
+#if defined (HANDLE_MULTIBYTE)
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ {
+ int v;
+ mbstate_t ps;
+
+ memset (&ps, 0, sizeof (mbstate_t));
+ /* These produce warnings because we're passing a const string to a
+ function that takes a non-const string. */
+ _rl_adjust_point ((char *)string, i, &ps);
+ if ((v = _rl_get_char_len ((char *)string + i, &ps)) > 1)
+ {
+ i += v - 1;
+ continue;
+ }
+ }
+
+#endif /* HANDLE_MULTIBYTE */
+ if ((!substring_okay &&
+ (whitespace (c) || c == ':' ||
+ (i > local_index && history_event_delimiter_chars && c == '-') ||
+ (c != '-' && history_event_delimiter_chars && member (c, history_event_delimiter_chars)) ||
+ (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
+ string[i] == delimiting_quote)) ||
+ string[i] == '\n' ||
+ (substring_okay && string[i] == '?'))
+ break;
+ }
+
+ which = i - local_index;
+ temp = (char *)xmalloc (1 + which);
+ if (which)
+ strncpy (temp, string + local_index, which);
+ temp[which] = '\0';
+
+ if (substring_okay && string[i] == '?')
+ i++;
+
+ *caller_index = i;
+
+#define FAIL_SEARCH() \
+ do { \
+ history_offset = history_length; xfree (temp) ; return (char *)NULL; \
+ } while (0)
+
+ /* If there is no search string, try to use the previous search string,
+ if one exists. If not, fail immediately. */
+ if (*temp == '\0' && substring_okay)
+ {
+ if (search_string)
+ {
+ xfree (temp);
+ temp = savestring (search_string);
+ }
+ else
+ FAIL_SEARCH ();
+ }
+
+ search_func = substring_okay ? history_search : history_search_prefix;
+ while (1)
+ {
+ local_index = (*search_func) (temp, -1);
+
+ if (local_index < 0)
+ FAIL_SEARCH ();
+
+ if (local_index == 0 || substring_okay)
+ {
+ entry = current_history ();
+ if (entry == 0)
+ FAIL_SEARCH ();
+ history_offset = history_length;
+
+ /* If this was a substring search, then remember the
+ string that we matched for word substitution. */
+ if (substring_okay)
+ {
+ FREE (search_string);
+ search_string = temp;
+
+ FREE (search_match);
+ search_match = history_find_word (entry->line, local_index);
+ }
+ else
+ xfree (temp);
+
+ return (entry->line);
+ }
+
+ if (history_offset)
+ history_offset--;
+ else
+ FAIL_SEARCH ();
+ }
+#undef FAIL_SEARCH
+#undef RETURN_ENTRY
+}
+
+/* Function for extracting single-quoted strings. Used for inhibiting
+ history expansion within single quotes. */
+
+/* Extract the contents of STRING as if it is enclosed in single quotes.
+ SINDEX, when passed in, is the offset of the character immediately
+ following the opening single quote; on exit, SINDEX is left pointing
+ to the closing single quote. FLAGS currently used to allow backslash
+ to escape a single quote (e.g., for bash $'...'). */
+static void
+hist_string_extract_single_quoted (char *string, int *sindex, int flags)
+{
+ register int i;
+
+ for (i = *sindex; string[i] && string[i] != '\''; i++)
+ {
+ if ((flags & 1) && string[i] == '\\' && string[i+1])
+ i++;
+ }
+
+ *sindex = i;
+}
+
+static char *
+quote_breaks (char *s)
+{
+ register char *p, *r;
+ char *ret;
+ int len = 3;
+
+ for (p = s; p && *p; p++, len++)
+ {
+ if (*p == '\'')
+ len += 3;
+ else if (whitespace (*p) || *p == '\n')
+ len += 2;
+ }
+
+ r = ret = (char *)xmalloc (len);
+ *r++ = '\'';
+ for (p = s; p && *p; )
+ {
+ if (*p == '\'')
+ {
+ *r++ = '\'';
+ *r++ = '\\';
+ *r++ = '\'';
+ *r++ = '\'';
+ p++;
+ }
+ else if (whitespace (*p) || *p == '\n')
+ {
+ *r++ = '\'';
+ *r++ = *p++;
+ *r++ = '\'';
+ }
+ else
+ *r++ = *p++;
+ }
+ *r++ = '\'';
+ *r = '\0';
+ return ret;
+}
+
+static char *
+hist_error(char *s, int start, int current, int errtype)
+{
+ char *temp;
+ const char *emsg;
+ int ll, elen;
+
+ ll = current - start;
+
+ switch (errtype)
+ {
+ case EVENT_NOT_FOUND:
+ emsg = "event not found";
+ elen = 15;
+ break;
+ case BAD_WORD_SPEC:
+ emsg = "bad word specifier";
+ elen = 18;
+ break;
+ case SUBST_FAILED:
+ emsg = "substitution failed";
+ elen = 19;
+ break;
+ case BAD_MODIFIER:
+ emsg = "unrecognized history modifier";
+ elen = 29;
+ break;
+ case NO_PREV_SUBST:
+ emsg = "no previous substitution";
+ elen = 24;
+ break;
+ default:
+ emsg = "unknown expansion error";
+ elen = 23;
+ break;
+ }
+
+ temp = (char *)xmalloc (ll + elen + 3);
+ if (s[start])
+ strncpy (temp, s + start, ll);
+ else
+ ll = 0;
+ temp[ll] = ':';
+ temp[ll + 1] = ' ';
+ strcpy (temp + ll + 2, emsg);
+ return (temp);
+}
+
+/* Get a history substitution string from STR starting at *IPTR
+ and return it. The length is returned in LENPTR.
+
+ A backslash can quote the delimiter. If the string is the
+ empty string, the previous pattern is used. If there is
+ no previous pattern for the lhs, the last history search
+ string is used.
+
+ If IS_RHS is 1, we ignore empty strings and set the pattern
+ to "" anyway. subst_lhs is not changed if the lhs is empty;
+ subst_rhs is allowed to be set to the empty string. */
+
+static char *
+get_subst_pattern (char *str, int *iptr, int delimiter, int is_rhs, int *lenptr)
+{
+ register int si, i, j, k;
+ char *s;
+#if defined (HANDLE_MULTIBYTE)
+ mbstate_t ps;
+#endif
+
+ s = (char *)NULL;
+ i = *iptr;
+
+#if defined (HANDLE_MULTIBYTE)
+ memset (&ps, 0, sizeof (mbstate_t));
+ _rl_adjust_point (str, i, &ps);
+#endif
+
+ for (si = i; str[si] && str[si] != delimiter; si++)
+#if defined (HANDLE_MULTIBYTE)
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ {
+ int v;
+ if ((v = _rl_get_char_len (str + si, &ps)) > 1)
+ si += v - 1;
+ else if (str[si] == '\\' && str[si + 1] == delimiter)
+ si++;
+ }
+ else
+#endif /* HANDLE_MULTIBYTE */
+ if (str[si] == '\\' && str[si + 1] == delimiter)
+ si++;
+
+ if (si > i || is_rhs)
+ {
+ s = (char *)xmalloc (si - i + 1);
+ for (j = 0, k = i; k < si; j++, k++)
+ {
+ /* Remove a backslash quoting the search string delimiter. */
+ if (str[k] == '\\' && str[k + 1] == delimiter)
+ k++;
+ s[j] = str[k];
+ }
+ s[j] = '\0';
+ if (lenptr)
+ *lenptr = j;
+ }
+
+ i = si;
+ if (str[i])
+ i++;
+ *iptr = i;
+
+ return s;
+}
+
+static void
+postproc_subst_rhs (void)
+{
+ char *new;
+ int i, j, new_size;
+
+ new = (char *)xmalloc (new_size = subst_rhs_len + subst_lhs_len);
+ for (i = j = 0; i < subst_rhs_len; i++)
+ {
+ if (subst_rhs[i] == '&')
+ {
+ if (j + subst_lhs_len >= new_size)
+ new = (char *)xrealloc (new, (new_size = new_size * 2 + subst_lhs_len));
+ strcpy (new + j, subst_lhs);
+ j += subst_lhs_len;
+ }
+ else
+ {
+ /* a single backslash protects the `&' from lhs interpolation */
+ if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&')
+ i++;
+ if (j >= new_size)
+ new = (char *)xrealloc (new, new_size *= 2);
+ new[j++] = subst_rhs[i];
+ }
+ }
+ new[j] = '\0';
+ xfree (subst_rhs);
+ subst_rhs = new;
+ subst_rhs_len = j;
+}
+
+/* Expand the bulk of a history specifier starting at STRING[START].
+ Returns 0 if everything is OK, -1 if an error occurred, and 1
+ if the `p' modifier was supplied and the caller should just print
+ the returned string. Returns the new index into string in
+ *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */
+/* need current line for !# */
+static int
+history_expand_internal (char *string, int start, int qc, int *end_index_ptr, char **ret_string, char *current_line)
+{
+ int i, n, starting_index;
+ int substitute_globally, subst_bywords, want_quotes, print_only;
+ char *event, *temp, *result, *tstr, *t, c, *word_spec;
+ int result_len;
+#if defined (HANDLE_MULTIBYTE)
+ mbstate_t ps;
+
+ memset (&ps, 0, sizeof (mbstate_t));
+#endif
+
+ result = (char *)xmalloc (result_len = 128);
+
+ i = start;
+
+ /* If it is followed by something that starts a word specifier,
+ then !! is implied as the event specifier. */
+
+ if (member (string[i + 1], ":$*%^"))
+ {
+ char fake_s[3];
+ int fake_i = 0;
+ i++;
+ fake_s[0] = fake_s[1] = history_expansion_char;
+ fake_s[2] = '\0';
+ event = get_history_event (fake_s, &fake_i, 0);
+ }
+ else if (string[i + 1] == '#')
+ {
+ i += 2;
+ event = current_line;
+ }
+ else
+ event = get_history_event (string, &i, qc);
+
+ if (event == 0)
+ {
+ *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND);
+ xfree (result);
+ return (-1);
+ }
+
+ /* If a word specifier is found, then do what that requires. */
+ starting_index = i;
+ word_spec = get_history_word_specifier (string, event, &i);
+
+ /* There is no such thing as a `malformed word specifier'. However,
+ it is possible for a specifier that has no match. In that case,
+ we complain. */
+ if (word_spec == (char *)&error_pointer)
+ {
+ *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC);
+ xfree (result);
+ return (-1);
+ }
+
+ /* If no word specifier, than the thing of interest was the event. */
+ temp = word_spec ? savestring (word_spec) : savestring (event);
+ FREE (word_spec);
+
+ /* Perhaps there are other modifiers involved. Do what they say. */
+ want_quotes = substitute_globally = subst_bywords = print_only = 0;
+ starting_index = i;
+
+ while (string[i] == ':')
+ {
+ c = string[i + 1];
+
+ if (c == 'g' || c == 'a')
+ {
+ substitute_globally = 1;
+ i++;
+ c = string[i + 1];
+ }
+ else if (c == 'G')
+ {
+ subst_bywords = 1;
+ i++;
+ c = string[i + 1];
+ }
+
+ switch (c)
+ {
+ default:
+ *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER);
+ xfree (result);
+ xfree (temp);
+ return -1;
+
+ case 'q':
+ want_quotes = 'q';
+ break;
+
+ case 'x':
+ want_quotes = 'x';
+ break;
+
+ /* :p means make this the last executed line. So we
+ return an error state after adding this line to the
+ history. */
+ case 'p':
+ print_only = 1;
+ break;
+
+ /* :t discards all but the last part of the pathname. */
+ case 't':
+ tstr = strrchr (temp, '/');
+ if (tstr)
+ {
+ tstr++;
+ t = savestring (tstr);
+ xfree (temp);
+ temp = t;
+ }
+ break;
+
+ /* :h discards the last part of a pathname. */
+ case 'h':
+ tstr = strrchr (temp, '/');
+ if (tstr)
+ *tstr = '\0';
+ break;
+
+ /* :r discards the suffix. */
+ case 'r':
+ tstr = strrchr (temp, '.');
+ if (tstr)
+ *tstr = '\0';
+ break;
+
+ /* :e discards everything but the suffix. */
+ case 'e':
+ tstr = strrchr (temp, '.');
+ if (tstr)
+ {
+ t = savestring (tstr);
+ xfree (temp);
+ temp = t;
+ }
+ break;
+
+ /* :s/this/that substitutes `that' for the first
+ occurrence of `this'. :gs/this/that substitutes `that'
+ for each occurrence of `this'. :& repeats the last
+ substitution. :g& repeats the last substitution
+ globally. */
+
+ case '&':
+ case 's':
+ {
+ char *new_event;
+ int delimiter, failed, si, l_temp, ws, we;
+
+ if (c == 's')
+ {
+ if (i + 2 < (int)strlen (string))
+ {
+#if defined (HANDLE_MULTIBYTE)
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ {
+ _rl_adjust_point (string, i + 2, &ps);
+ if (_rl_get_char_len (string + i + 2, &ps) > 1)
+ delimiter = 0;
+ else
+ delimiter = string[i + 2];
+ }
+ else
+#endif /* HANDLE_MULTIBYTE */
+ delimiter = string[i + 2];
+ }
+ else
+ break; /* no search delimiter */
+
+ i += 3;
+
+ t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len);
+ /* An empty substitution lhs with no previous substitution
+ uses the last search string as the lhs. */
+ if (t)
+ {
+ FREE (subst_lhs);
+ subst_lhs = t;
+ }
+ else if (!subst_lhs)
+ {
+ if (search_string && *search_string)
+ {
+ subst_lhs = savestring (search_string);
+ subst_lhs_len = strlen (subst_lhs);
+ }
+ else
+ {
+ subst_lhs = (char *) NULL;
+ subst_lhs_len = 0;
+ }
+ }
+
+ FREE (subst_rhs);
+ subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len);
+
+ /* If `&' appears in the rhs, it's supposed to be replaced
+ with the lhs. */
+ if (member ('&', subst_rhs))
+ postproc_subst_rhs ();
+ }
+ else
+ i += 2;
+
+ /* If there is no lhs, the substitution can't succeed. */
+ if (subst_lhs_len == 0)
+ {
+ *ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST);
+ xfree (result);
+ xfree (temp);
+ return -1;
+ }
+
+ l_temp = strlen (temp);
+ /* Ignore impossible cases. */
+ if (subst_lhs_len > l_temp)
+ {
+ *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
+ xfree (result);
+ xfree (temp);
+ return (-1);
+ }
+
+ /* Find the first occurrence of THIS in TEMP. */
+ /* Substitute SUBST_RHS for SUBST_LHS in TEMP. There are three
+ cases to consider:
+
+ 1. substitute_globally == subst_bywords == 0
+ 2. substitute_globally == 1 && subst_bywords == 0
+ 3. substitute_globally == 0 && subst_bywords == 1
+
+ In the first case, we substitute for the first occurrence only.
+ In the second case, we substitute for every occurrence.
+ In the third case, we tokenize into words and substitute the
+ first occurrence of each word. */
+
+ si = we = 0;
+ for (failed = 1; (si + subst_lhs_len) <= l_temp; si++)
+ {
+ /* First skip whitespace and find word boundaries if
+ we're past the end of the word boundary we found
+ the last time. */
+ if (subst_bywords && si > we)
+ {
+ for (; temp[si] && fielddelim (temp[si]); si++)
+ ;
+ ws = si;
+ we = history_tokenize_word (temp, si);
+ }
+
+ if (STREQN (temp+si, subst_lhs, subst_lhs_len))
+ {
+ int len = subst_rhs_len - subst_lhs_len + l_temp;
+ new_event = (char *)xmalloc (1 + len);
+ strncpy (new_event, temp, si);
+ strncpy (new_event + si, subst_rhs, subst_rhs_len);
+ strncpy (new_event + si + subst_rhs_len,
+ temp + si + subst_lhs_len,
+ l_temp - (si + subst_lhs_len));
+ new_event[len] = '\0';
+ xfree (temp);
+ temp = new_event;
+
+ failed = 0;
+
+ if (substitute_globally)
+ {
+ /* Reported to fix a bug that causes it to skip every
+ other match when matching a single character. Was
+ si += subst_rhs_len previously. */
+ si += subst_rhs_len - 1;
+ l_temp = strlen (temp);
+ substitute_globally++;
+ continue;
+ }
+ else if (subst_bywords)
+ {
+ si = we;
+ l_temp = strlen (temp);
+ continue;
+ }
+ else
+ break;
+ }
+ }
+
+ if (substitute_globally > 1)
+ {
+ substitute_globally = 0;
+ continue; /* don't want to increment i */
+ }
+
+ if (failed == 0)
+ continue; /* don't want to increment i */
+
+ *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
+ xfree (result);
+ xfree (temp);
+ return (-1);
+ }
+ }
+ i += 2;
+ }
+ /* Done with modifiers. */
+ /* Believe it or not, we have to back the pointer up by one. */
+ --i;
+
+ if (want_quotes)
+ {
+ char *x;
+
+ if (want_quotes == 'q')
+ x = sh_single_quote (temp);
+ else if (want_quotes == 'x')
+ x = quote_breaks (temp);
+ else
+ x = savestring (temp);
+
+ xfree (temp);
+ temp = x;
+ }
+
+ n = strlen (temp);
+ if (n >= result_len)
+ result = (char *)xrealloc (result, n + 2);
+ strcpy (result, temp);
+ xfree (temp);
+
+ *end_index_ptr = i;
+ *ret_string = result;
+ return (print_only);
+}
+
+/* Expand the string STRING, placing the result into OUTPUT, a pointer
+ to a string. Returns:
+
+ -1) If there was an error in expansion.
+ 0) If no expansions took place (or, if the only change in
+ the text was the de-slashifying of the history expansion
+ character)
+ 1) If expansions did take place
+ 2) If the `p' modifier was given and the caller should print the result
+
+ If an error occurred in expansion, then OUTPUT contains a descriptive
+ error message. */
+
+#define ADD_STRING(s) \
+ do \
+ { \
+ int sl = strlen (s); \
+ j += sl; \
+ if (j >= result_len) \
+ { \
+ while (j >= result_len) \
+ result_len += 128; \
+ result = (char *)xrealloc (result, result_len); \
+ } \
+ strcpy (result + j - sl, s); \
+ } \
+ while (0)
+
+#define ADD_CHAR(c) \
+ do \
+ { \
+ if (j >= result_len - 1) \
+ result = (char *)xrealloc (result, result_len += 64); \
+ result[j++] = c; \
+ result[j] = '\0'; \
+ } \
+ while (0)
+
+int
+history_expand (char *hstring, char **output)
+{
+ register int j;
+ int i, r, l, passc, cc, modified, eindex, only_printing, dquote, squote, flag;
+ char *string;
+
+ /* The output string, and its length. */
+ int result_len;
+ char *result;
+
+#if defined (HANDLE_MULTIBYTE)
+ char mb[MB_LEN_MAX];
+ mbstate_t ps;
+#endif
+
+ /* Used when adding the string. */
+ char *temp;
+
+ if (output == 0)
+ return 0;
+
+ /* Setting the history expansion character to 0 inhibits all
+ history expansion. */
+ if (history_expansion_char == 0)
+ {
+ *output = savestring (hstring);
+ return (0);
+ }
+
+ /* Prepare the buffer for printing error messages. */
+ result = (char *)xmalloc (result_len = 256);
+ result[0] = '\0';
+
+ only_printing = modified = 0;
+ l = strlen (hstring);
+
+ /* Grovel the string. Only backslash and single quotes can quote the
+ history escape character. We also handle arg specifiers. */
+
+ /* Before we grovel forever, see if the history_expansion_char appears
+ anywhere within the text. */
+
+ /* The quick substitution character is a history expansion all right. That
+ is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
+ that is the substitution that we do. */
+ if (hstring[0] == history_subst_char)
+ {
+ string = (char *)xmalloc (l + 5);
+
+ string[0] = string[1] = history_expansion_char;
+ string[2] = ':';
+ string[3] = 's';
+ strcpy (string + 4, hstring);
+ l += 4;
+ }
+ else
+ {
+#if defined (HANDLE_MULTIBYTE)
+ memset (&ps, 0, sizeof (mbstate_t));
+#endif
+
+ string = hstring;
+ /* If not quick substitution, still maybe have to do expansion. */
+
+ /* `!' followed by one of the characters in history_no_expand_chars
+ is NOT an expansion. */
+ dquote = history_quoting_state == '"';
+ squote = history_quoting_state == '\'';
+
+ /* If the calling application tells us we are already reading a
+ single-quoted string, consume the rest of the string right now
+ and then go on. */
+ i = 0;
+ if (squote && history_quotes_inhibit_expansion)
+ {
+ hist_string_extract_single_quoted (string, &i, 0);
+ squote = 0;
+ if (string[i])
+ i++;
+ }
+
+ for ( ; string[i]; i++)
+ {
+#if defined (HANDLE_MULTIBYTE)
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ {
+ int v;
+ v = _rl_get_char_len (string + i, &ps);
+ if (v > 1)
+ {
+ i += v - 1;
+ continue;
+ }
+ }
+#endif /* HANDLE_MULTIBYTE */
+
+ cc = string[i + 1];
+ /* The history_comment_char, if set, appearing at the beginning
+ of a word signifies that the rest of the line should not have
+ history expansion performed on it.
+ Skip the rest of the line and break out of the loop. */
+ if (history_comment_char && string[i] == history_comment_char &&
+ dquote == 0 &&
+ (i == 0 || member (string[i - 1], history_word_delimiters)))
+ {
+ while (string[i])
+ i++;
+ break;
+ }
+ else if (string[i] == history_expansion_char)
+ {
+ if (cc == 0 || member (cc, history_no_expand_chars))
+ continue;
+ /* DQUOTE won't be set unless history_quotes_inhibit_expansion
+ is set. The idea here is to treat double-quoted strings the
+ same as the word outside double quotes; in effect making the
+ double quote part of history_no_expand_chars when DQUOTE is
+ set. */
+ else if (dquote && cc == '"')
+ continue;
+ /* If the calling application has set
+ history_inhibit_expansion_function to a function that checks
+ for special cases that should not be history expanded,
+ call the function and skip the expansion if it returns a
+ non-zero value. */
+ else if (history_inhibit_expansion_function &&
+ (*history_inhibit_expansion_function) (string, i))
+ continue;
+ else
+ break;
+ }
+ /* Shell-like quoting: allow backslashes to quote double quotes
+ inside a double-quoted string. */
+ else if (dquote && string[i] == '\\' && cc == '"')
+ i++;
+ /* More shell-like quoting: if we're paying attention to single
+ quotes and letting them quote the history expansion character,
+ then we need to pay attention to double quotes, because single
+ quotes are not special inside double-quoted strings. */
+ else if (history_quotes_inhibit_expansion && string[i] == '"')
+ {
+ dquote = 1 - dquote;
+ }
+ else if (dquote == 0 && history_quotes_inhibit_expansion && string[i] == '\'')
+ {
+ /* If this is bash, single quotes inhibit history expansion. */
+ flag = (i > 0 && string[i - 1] == '$');
+ i++;
+ hist_string_extract_single_quoted (string, &i, flag);
+ }
+ else if (history_quotes_inhibit_expansion && string[i] == '\\')
+ {
+ /* If this is bash, allow backslashes to quote single
+ quotes and the history expansion character. */
+ if (cc == '\'' || cc == history_expansion_char)
+ i++;
+ }
+
+ }
+
+ if (string[i] != history_expansion_char)
+ {
+ xfree (result);
+ *output = savestring (string);
+ return (0);
+ }
+ }
+
+ /* Extract and perform the substitution. */
+ dquote = history_quoting_state == '"';
+ squote = history_quoting_state == '\'';
+
+ /* If the calling application tells us we are already reading a
+ single-quoted string, consume the rest of the string right now
+ and then go on. */
+ i = j = 0;
+ if (squote && history_quotes_inhibit_expansion)
+ {
+ int c;
+
+ hist_string_extract_single_quoted (string, &i, 0);
+ squote = 0;
+ for (c = 0; c < i; c++)
+ ADD_CHAR (string[c]);
+ if (string[i])
+ {
+ ADD_CHAR (string[i]);
+ i++;
+ }
+ }
+
+ for (passc = 0; i < l; i++)
+ {
+ int qc, tchar = string[i];
+
+ if (passc)
+ {
+ passc = 0;
+ ADD_CHAR (tchar);
+ continue;
+ }
+
+#if defined (HANDLE_MULTIBYTE)
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ {
+ int k, c;
+
+ c = tchar;
+ memset (mb, 0, sizeof (mb));
+ for (k = 0; k < MB_LEN_MAX; k++)
+ {
+ mb[k] = (char)c;
+ memset (&ps, 0, sizeof (mbstate_t));
+ if (_rl_get_char_len (mb, &ps) == -2)
+ c = string[++i];
+ else
+ break;
+ }
+ if (strlen (mb) > 1)
+ {
+ ADD_STRING (mb);
+ continue;
+ }
+ }
+#endif /* HANDLE_MULTIBYTE */
+
+ if (tchar == history_expansion_char)
+ tchar = -3;
+ else if (tchar == history_comment_char)
+ tchar = -2;
+
+ switch (tchar)
+ {
+ default:
+ ADD_CHAR (string[i]);
+ break;
+
+ case '\\':
+ passc++;
+ ADD_CHAR (tchar);
+ break;
+
+ case '"':
+ dquote = 1 - dquote;
+ ADD_CHAR (tchar);
+ break;
+
+ case '\'':
+ {
+ /* If history_quotes_inhibit_expansion is set, single quotes
+ inhibit history expansion, otherwise they are treated like
+ double quotes. */
+ if (squote)
+ {
+ squote = 0;
+ ADD_CHAR (tchar);
+ }
+ else if (dquote == 0 && history_quotes_inhibit_expansion)
+ {
+ int quote, slen;
+
+ flag = (i > 0 && string[i - 1] == '$');
+ quote = i++;
+ hist_string_extract_single_quoted (string, &i, flag);
+
+ slen = i - quote + 2;
+ temp = (char *)xmalloc (slen);
+ strncpy (temp, string + quote, slen);
+ temp[slen - 1] = '\0';
+ ADD_STRING (temp);
+ xfree (temp);
+ }
+ else if (dquote == 0 && squote == 0 && history_quotes_inhibit_expansion == 0)
+ {
+ squote = 1;
+ ADD_CHAR (string[i]);
+ }
+ else
+ ADD_CHAR (string[i]);
+ break;
+ }
+
+ case -2: /* history_comment_char */
+ if ((dquote == 0 || history_quotes_inhibit_expansion == 0) &&
+ (i == 0 || member (string[i - 1], history_word_delimiters)))
+ {
+ temp = (char *)xmalloc (l - i + 1);
+ strcpy (temp, string + i);
+ ADD_STRING (temp);
+ xfree (temp);
+ i = l;
+ }
+ else
+ ADD_CHAR (string[i]);
+ break;
+
+ case -3: /* history_expansion_char */
+ cc = string[i + 1];
+
+ /* If the history_expansion_char is followed by one of the
+ characters in history_no_expand_chars, then it is not a
+ candidate for expansion of any kind. */
+ if (cc == 0 || member (cc, history_no_expand_chars) ||
+ (dquote && cc == '"'))
+ {
+ ADD_CHAR (string[i]);
+ break;
+ }
+
+ /* If the application has defined a function to determine whether
+ or not a history expansion should be performed, call it here. */
+ /* We check against what we've expanded so far, with the current
+ expansion appended, because that seems to be what csh does. We
+ decide to expand based on what we have to this point, not what
+ we started with. */
+ if (history_inhibit_expansion_function)
+ {
+ int save_j, temp;
+
+ save_j = j;
+ ADD_CHAR (string[i]);
+ ADD_CHAR (cc);
+
+ temp = (*history_inhibit_expansion_function) (result, save_j);
+ if (temp)
+ {
+ result[--j] = '\0'; /* `unadd' cc, leaving ADD_CHAR(string[i]) */
+ break;
+ }
+ else
+ result[j = save_j] = '\0';
+ }
+
+#if defined (NO_BANG_HASH_MODIFIERS)
+ /* There is something that is listed as a `word specifier' in csh
+ documentation which means `the expanded text to this point'.
+ That is not a word specifier, it is an event specifier. If we
+ don't want to allow modifiers with `!#', just stick the current
+ output line in again. */
+ if (cc == '#')
+ {
+ if (result)
+ {
+ temp = (char *)xmalloc (1 + strlen (result));
+ strcpy (temp, result);
+ ADD_STRING (temp);
+ xfree (temp);
+ }
+ i++;
+ break;
+ }
+#endif
+ qc = squote ? '\'' : (dquote ? '"' : 0);
+ r = history_expand_internal (string, i, qc, &eindex, &temp, result);
+ if (r < 0)
+ {
+ *output = temp;
+ xfree (result);
+ if (string != hstring)
+ xfree (string);
+ return -1;
+ }
+ else
+ {
+ if (temp)
+ {
+ modified++;
+ if (*temp)
+ ADD_STRING (temp);
+ xfree (temp);
+ }
+ only_printing += r == 1;
+ i = eindex;
+ }
+ break;
+ }
+ }
+
+ *output = result;
+ if (string != hstring)
+ xfree (string);
+
+ if (only_printing)
+ {
+#if 0
+ add_history (result);
+#endif
+ return (2);
+ }
+
+ return (modified != 0);
+}
+
+/* Return a consed string which is the word specified in SPEC, and found
+ in FROM. NULL is returned if there is no spec. The address of
+ ERROR_POINTER is returned if the word specified cannot be found.
+ CALLER_INDEX is the offset in SPEC to start looking; it is updated
+ to point to just after the last character parsed. */
+static char *
+get_history_word_specifier (char *spec, char *from, int *caller_index)
+{
+ register int i = *caller_index;
+ int first, last;
+ int expecting_word_spec = 0;
+ char *result;
+
+ /* The range of words to return doesn't exist yet. */
+ first = last = 0;
+ result = (char *)NULL;
+
+ /* If we found a colon, then this *must* be a word specification. If
+ it isn't, then it is an error. */
+ if (spec[i] == ':')
+ {
+ i++;
+ expecting_word_spec++;
+ }
+
+ /* Handle special cases first. */
+
+ /* `%' is the word last searched for. */
+ if (spec[i] == '%')
+ {
+ *caller_index = i + 1;
+ return (search_match ? savestring (search_match) : savestring (""));
+ }
+
+ /* `*' matches all of the arguments, but not the command. */
+ if (spec[i] == '*')
+ {
+ *caller_index = i + 1;
+ result = history_arg_extract (1, '$', from);
+ return (result ? result : savestring (""));
+ }
+
+ /* `$' is last arg. */
+ if (spec[i] == '$')
+ {
+ *caller_index = i + 1;
+ return (history_arg_extract ('$', '$', from));
+ }
+
+ /* Try to get FIRST and LAST figured out. */
+
+ if (spec[i] == '-')
+ first = 0;
+ else if (spec[i] == '^')
+ {
+ first = 1;
+ i++;
+ }
+ else if (_rl_digit_p (spec[i]) && expecting_word_spec)
+ {
+ for (first = 0; _rl_digit_p (spec[i]); i++)
+ first = (first * 10) + _rl_digit_value (spec[i]);
+ }
+ else
+ return ((char *)NULL); /* no valid `first' for word specifier */
+
+ if (spec[i] == '^' || spec[i] == '*')
+ {
+ last = (spec[i] == '^') ? 1 : '$'; /* x* abbreviates x-$ */
+ i++;
+ }
+ else if (spec[i] != '-')
+ last = first;
+ else
+ {
+ i++;
+
+ if (_rl_digit_p (spec[i]))
+ {
+ for (last = 0; _rl_digit_p (spec[i]); i++)
+ last = (last * 10) + _rl_digit_value (spec[i]);
+ }
+ else if (spec[i] == '$')
+ {
+ i++;
+ last = '$';
+ }
+ else if (spec[i] == '^')
+ {
+ i++;
+ last = 1;
+ }
+#if 0
+ else if (!spec[i] || spec[i] == ':')
+ /* check against `:' because there could be a modifier separator */
+#else
+ else
+ /* csh seems to allow anything to terminate the word spec here,
+ leaving it as an abbreviation. */
+#endif
+ last = -1; /* x- abbreviates x-$ omitting word `$' */
+ }
+
+ *caller_index = i;
+
+ if (last >= first || last == '$' || last < 0)
+ result = history_arg_extract (first, last, from);
+
+ return (result ? result : (char *)&error_pointer);
+}
+
+/* Extract the args specified, starting at FIRST, and ending at LAST.
+ The args are taken from STRING. If either FIRST or LAST is < 0,
+ then make that arg count from the right (subtract from the number of
+ tokens, so that FIRST = -1 means the next to last token on the line).
+ If LAST is `$' the last arg from STRING is used. */
+char *
+history_arg_extract (int first, int last, const char *string)
+{
+ register int i, len;
+ char *result;
+ int size, offset;
+ char **list;
+
+ /* XXX - think about making history_tokenize return a struct array,
+ each struct in array being a string and a length to avoid the
+ calls to strlen below. */
+ if ((list = history_tokenize (string)) == NULL)
+ return ((char *)NULL);
+
+ for (len = 0; list[len]; len++)
+ ;
+
+ if (last < 0)
+ last = len + last - 1;
+
+ if (first < 0)
+ first = len + first - 1;
+
+ if (last == '$')
+ last = len - 1;
+
+ if (first == '$')
+ first = len - 1;
+
+ last++;
+
+ if (first >= len || last > len || first < 0 || last < 0 || first > last)
+ result = ((char *)NULL);
+ else
+ {
+ for (size = 0, i = first; i < last; i++)
+ size += strlen (list[i]) + 1;
+ result = (char *)xmalloc (size + 1);
+ result[0] = '\0';
+
+ for (i = first, offset = 0; i < last; i++)
+ {
+ strcpy (result + offset, list[i]);
+ offset += strlen (list[i]);
+ if (i + 1 < last)
+ {
+ result[offset++] = ' ';
+ result[offset] = 0;
+ }
+ }
+ }
+
+ for (i = 0; i < len; i++)
+ xfree (list[i]);
+ xfree (list);
+
+ return (result);
+}
+
+static int
+history_tokenize_word (const char *string, int ind)
+{
+ register int i, j;
+ int delimiter, nestdelim, delimopen;
+
+ i = ind;
+ delimiter = nestdelim = 0;
+
+ if (member (string[i], "()\n")) /* XXX - included \n, but why? been here forever */
+ {
+ i++;
+ return i;
+ }
+
+ if (ISDIGIT (string[i]))
+ {
+ j = i;
+ while (string[j] && ISDIGIT (string[j]))
+ j++;
+ if (string[j] == 0)
+ return (j);
+ if (string[j] == '<' || string[j] == '>')
+ i = j; /* digit sequence is a file descriptor */
+ else
+ {
+ i = j;
+ goto get_word; /* digit sequence is part of a word */
+ }
+ }
+
+ if (member (string[i], "<>;&|"))
+ {
+ int peek = string[i + 1];
+
+ if (peek == string[i])
+ {
+ if (peek == '<' && string[i + 2] == '-')
+ i++;
+ else if (peek == '<' && string[i + 2] == '<')
+ i++;
+ i += 2;
+ return i;
+ }
+ else if (peek == '&' && (string[i] == '>' || string[i] == '<'))
+ {
+ j = i + 2;
+ while (string[j] && ISDIGIT (string[j])) /* file descriptor */
+ j++;
+ if (string[j] =='-') /* <&[digits]-, >&[digits]- */
+ j++;
+ return j;
+ }
+ else if ((peek == '>' && string[i] == '&') || (peek == '|' && string[i] == '>'))
+ {
+ i += 2;
+ return i;
+ }
+ /* XXX - process substitution -- separated out for later -- bash-4.2 */
+ else if (peek == '(' && (string[i] == '>' || string[i] == '<')) /*)*/
+ {
+ i += 2;
+ delimopen = '(';
+ delimiter = ')';
+ nestdelim = 1;
+ goto get_word;
+ }
+
+ i++;
+ return i;
+ }
+
+get_word:
+ /* Get word from string + i; */
+
+ if (delimiter == 0 && member (string[i], HISTORY_QUOTE_CHARACTERS))
+ delimiter = string[i++];
+
+ for (; string[i]; i++)
+ {
+ if (string[i] == '\\' && string[i + 1] == '\n')
+ {
+ i++;
+ continue;
+ }
+
+ if (string[i] == '\\' && delimiter != '\'' &&
+ (delimiter != '"' || member (string[i], slashify_in_quotes)))
+ {
+ i++;
+ continue;
+ }
+
+ /* delimiter must be set and set to something other than a quote if
+ nestdelim is set, so these tests are safe. */
+ if (nestdelim && string[i] == delimopen)
+ {
+ nestdelim++;
+ continue;
+ }
+ if (nestdelim && string[i] == delimiter)
+ {
+ nestdelim--;
+ if (nestdelim == 0)
+ delimiter = 0;
+ continue;
+ }
+
+ if (delimiter && string[i] == delimiter)
+ {
+ delimiter = 0;
+ continue;
+ }
+
+ /* Command and process substitution; shell extended globbing patterns */
+ if (nestdelim == 0 && delimiter == 0 && member (string[i], "<>$!@?+*") && string[i+1] == '(') /*)*/
+ {
+ i += 2;
+ delimopen = '(';
+ delimiter = ')';
+ nestdelim = 1;
+ continue;
+ }
+
+ if (delimiter == 0 && (member (string[i], history_word_delimiters)))
+ break;
+
+ if (delimiter == 0 && member (string[i], HISTORY_QUOTE_CHARACTERS))
+ delimiter = string[i];
+ }
+
+ return i;
+}
+
+static char *
+history_substring (const char *string, int start, int end)
+{
+ register int len;
+ register char *result;
+
+ len = end - start;
+ result = (char *)xmalloc (len + 1);
+ strncpy (result, string + start, len);
+ result[len] = '\0';
+ return result;
+}
+
+/* Parse STRING into tokens and return an array of strings. If WIND is
+ not -1 and INDP is not null, we also want the word surrounding index
+ WIND. The position in the returned array of strings is returned in
+ *INDP. */
+static char **
+history_tokenize_internal (const char *string, int wind, int *indp)
+{
+ char **result;
+ register int i, start, result_index, size;
+
+ /* If we're searching for a string that's not part of a word (e.g., " "),
+ make sure we set *INDP to a reasonable value. */
+ if (indp && wind != -1)
+ *indp = -1;
+
+ /* Get a token, and stuff it into RESULT. The tokens are split
+ exactly where the shell would split them. */
+ for (i = result_index = size = 0, result = (char **)NULL; string[i]; )
+ {
+ /* Skip leading whitespace. */
+ for (; string[i] && fielddelim (string[i]); i++)
+ ;
+ if (string[i] == 0 || string[i] == history_comment_char)
+ return (result);
+
+ start = i;
+
+ i = history_tokenize_word (string, start);
+
+ /* If we have a non-whitespace delimiter character (which would not be
+ skipped by the loop above), use it and any adjacent delimiters to
+ make a separate field. Any adjacent white space will be skipped the
+ next time through the loop. */
+ if (i == start && history_word_delimiters)
+ {
+ i++;
+ while (string[i] && member (string[i], history_word_delimiters))
+ i++;
+ }
+
+ /* If we are looking for the word in which the character at a
+ particular index falls, remember it. */
+ if (indp && wind != -1 && wind >= start && wind < i)
+ *indp = result_index;
+
+ if (result_index + 2 >= size)
+ result = (char **)xrealloc (result, ((size += 10) * sizeof (char *)));
+
+ result[result_index++] = history_substring (string, start, i);
+ result[result_index] = (char *)NULL;
+ }
+
+ return (result);
+}
+
+/* Return an array of tokens, much as the shell might. The tokens are
+ parsed out of STRING. */
+char **
+history_tokenize (const char *string)
+{
+ return (history_tokenize_internal (string, -1, (int *)NULL));
+}
+
+/* Free members of WORDS from START to an empty string */
+static void
+freewords (char **words, int start)
+{
+ register int i;
+
+ for (i = start; words[i]; i++)
+ xfree (words[i]);
+}
+
+/* Find and return the word which contains the character at index IND
+ in the history line LINE. Used to save the word matched by the
+ last history !?string? search. */
+static char *
+history_find_word (char *line, int ind)
+{
+ char **words, *s;
+ int i, wind;
+
+ words = history_tokenize_internal (line, ind, &wind);
+ if (wind == -1 || words == 0)
+ {
+ if (words)
+ freewords (words, 0);
+ FREE (words);
+ return ((char *)NULL);
+ }
+ s = words[wind];
+ for (i = 0; i < wind; i++)
+ xfree (words[i]);
+ freewords (words, wind + 1);
+ xfree (words);
+ return s;
+}
diff --git a/third_party/readline/histfile.c b/third_party/readline/histfile.c
new file mode 100644
index 000000000..2fb171f64
--- /dev/null
+++ b/third_party/readline/histfile.c
@@ -0,0 +1,833 @@
+/* histfile.c - functions to manipulate the history file. */
+
+/* Copyright (C) 1989-2019 Free Software Foundation, Inc.
+
+ This file contains the GNU History Library (History), a set of
+ routines for managing the text of previously typed lines.
+
+ History is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ History 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 History. If not, see .
+*/
+
+/* The goal is to make the implementation transparent, so that you
+ don't have to know what data types are used, just what functions
+ you can call. I think I have done that. */
+
+#define READLINE_LIBRARY
+
+#if defined (__TANDEM)
+# define _XOPEN_SOURCE_EXTENDED 1
+# include
+# include
+#endif
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+
+#if defined (HAVE_LIMITS_H)
+# include
+#endif
+
+#include
+#if ! defined (_MINIX) && defined (HAVE_SYS_FILE_H)
+# include
+#endif
+#include "posixstat.h"
+#include
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#if defined (HAVE_UNISTD_H)
+# include
+#endif
+
+#include
+
+#if defined (__EMX__)
+# undef HAVE_MMAP
+#endif
+
+#ifdef HISTORY_USE_MMAP
+# include
+
+# ifdef MAP_FILE
+# define MAP_RFLAGS (MAP_FILE|MAP_PRIVATE)
+# define MAP_WFLAGS (MAP_FILE|MAP_SHARED)
+# else
+# define MAP_RFLAGS MAP_PRIVATE
+# define MAP_WFLAGS MAP_SHARED
+# endif
+
+# ifndef MAP_FAILED
+# define MAP_FAILED ((void *)-1)
+# endif
+
+#endif /* HISTORY_USE_MMAP */
+
+#if defined(_WIN32)
+# define WIN32_LEAN_AND_MEAN
+# include
+#endif
+
+/* If we're compiling for __EMX__ (OS/2) or __CYGWIN__ (cygwin32 environment
+ on win 95/98/nt), we want to open files with O_BINARY mode so that there
+ is no \n -> \r\n conversion performed. On other systems, we don't want to
+ mess around with O_BINARY at all, so we ensure that it's defined to 0. */
+#if defined (__EMX__) || defined (__CYGWIN__)
+# ifndef O_BINARY
+# define O_BINARY 0
+# endif
+#else /* !__EMX__ && !__CYGWIN__ */
+# undef O_BINARY
+# define O_BINARY 0
+#endif /* !__EMX__ && !__CYGWIN__ */
+
+#include
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+#include "history.h"
+#include "histlib.h"
+
+#include "rlshell.h"
+#include "xmalloc.h"
+
+#if !defined (PATH_MAX)
+# define PATH_MAX 1024 /* default */
+#endif
+
+/* history file version; currently unused */
+int history_file_version = 1;
+
+/* If non-zero, we write timestamps to the history file in history_do_write() */
+int history_write_timestamps = 0;
+
+/* If non-zero, we assume that a history file that starts with a timestamp
+ uses timestamp-delimited entries and can include multi-line history
+ entries. Used by read_history_range */
+int history_multiline_entries = 0;
+
+/* Immediately after a call to read_history() or read_history_range(), this
+ will return the number of lines just read from the history file in that
+ call. */
+int history_lines_read_from_file = 0;
+
+/* Immediately after a call to write_history() or history_do_write(), this
+ will return the number of lines just written to the history file in that
+ call. This also works with history_truncate_file. */
+int history_lines_written_to_file = 0;
+
+/* Does S look like the beginning of a history timestamp entry? Placeholder
+ for more extensive tests. */
+#define HIST_TIMESTAMP_START(s) (*(s) == history_comment_char && isdigit ((unsigned char)(s)[1]) )
+
+static char *history_backupfile (const char *);
+static char *history_tempfile (const char *);
+static int histfile_backup (const char *, const char *);
+static int histfile_restore (const char *, const char *);
+static int history_rename (const char *, const char *);
+
+/* Return the string that should be used in the place of this
+ filename. This only matters when you don't specify the
+ filename to read_history (), or write_history (). */
+static char *
+history_filename (const char *filename)
+{
+ char *return_val;
+ const char *home;
+ int home_len;
+
+ return_val = filename ? savestring (filename) : (char *)NULL;
+
+ if (return_val)
+ return (return_val);
+
+ home = sh_get_env_value ("HOME");
+#if defined (_WIN32)
+ if (home == 0)
+ home = sh_get_env_value ("APPDATA");
+#endif
+
+ if (home == 0)
+ return (NULL);
+ else
+ home_len = strlen (home);
+
+ return_val = (char *)xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */
+ strcpy (return_val, home);
+ return_val[home_len] = '/';
+#if defined (__MSDOS__)
+ strcpy (return_val + home_len + 1, "_history");
+#else
+ strcpy (return_val + home_len + 1, ".history");
+#endif
+
+ return (return_val);
+}
+
+static char *
+history_backupfile (const char *filename)
+{
+ const char *fn;
+ char *ret, linkbuf[PATH_MAX+1];
+ size_t len;
+ ssize_t n;
+ struct stat fs;
+
+ fn = filename;
+#if defined (HAVE_READLINK)
+ /* Follow symlink to avoid backing up symlink itself; call will fail if
+ not a symlink */
+ if ((n = readlink (filename, linkbuf, sizeof (linkbuf) - 1)) > 0)
+ {
+ linkbuf[n] = '\0';
+ fn = linkbuf;
+ }
+#endif
+
+ len = strlen (fn);
+ ret = xmalloc (len + 2);
+ strcpy (ret, fn);
+ ret[len] = '-';
+ ret[len+1] = '\0';
+ return ret;
+}
+
+static char *
+history_tempfile (const char *filename)
+{
+ const char *fn;
+ char *ret, linkbuf[PATH_MAX+1];
+ size_t len;
+ ssize_t n;
+ struct stat fs;
+ int pid;
+
+ fn = filename;
+#if defined (HAVE_READLINK)
+ /* Follow symlink so tempfile created in the same directory as any symlinked
+ history file; call will fail if not a symlink */
+ if ((n = readlink (filename, linkbuf, sizeof (linkbuf) - 1)) > 0)
+ {
+ linkbuf[n] = '\0';
+ fn = linkbuf;
+ }
+#endif
+
+ len = strlen (fn);
+ ret = xmalloc (len + 11);
+ strcpy (ret, fn);
+
+ pid = (int)getpid ();
+
+ /* filename-PID.tmp */
+ ret[len] = '-';
+ ret[len+1] = (pid / 10000 % 10) + '0';
+ ret[len+2] = (pid / 1000 % 10) + '0';
+ ret[len+3] = (pid / 100 % 10) + '0';
+ ret[len+4] = (pid / 10 % 10) + '0';
+ ret[len+5] = (pid % 10) + '0';
+ strcpy (ret + len + 6, ".tmp");
+
+ return ret;
+}
+
+/* Add the contents of FILENAME to the history list, a line at a time.
+ If FILENAME is NULL, then read from ~/.history. Returns 0 if
+ successful, or errno if not. */
+int
+read_history (const char *filename)
+{
+ return (read_history_range (filename, 0, -1));
+}
+
+/* Read a range of lines from FILENAME, adding them to the history list.
+ Start reading at the FROM'th line and end at the TO'th. If FROM
+ is zero, start at the beginning. If TO is less than FROM, read
+ until the end of the file. If FILENAME is NULL, then read from
+ ~/.history. Returns 0 if successful, or errno if not. */
+int
+read_history_range (const char *filename, int from, int to)
+{
+ register char *line_start, *line_end, *p;
+ char *input, *buffer, *bufend, *last_ts;
+ int file, current_line, chars_read, has_timestamps, reset_comment_char;
+ struct stat finfo;
+ size_t file_size;
+#if defined (EFBIG)
+ int overflow_errno = EFBIG;
+#elif defined (EOVERFLOW)
+ int overflow_errno = EOVERFLOW;
+#else
+ int overflow_errno = EIO;
+#endif
+
+ history_lines_read_from_file = 0;
+
+ buffer = last_ts = (char *)NULL;
+ input = history_filename (filename);
+ file = input ? open (input, O_RDONLY|O_BINARY, 0666) : -1;
+
+ if ((file < 0) || (fstat (file, &finfo) == -1))
+ goto error_and_exit;
+
+ if (S_ISREG (finfo.st_mode) == 0)
+ {
+#ifdef EFTYPE
+ errno = EFTYPE;
+#else
+ errno = EINVAL;
+#endif
+ goto error_and_exit;
+ }
+
+ file_size = (size_t)finfo.st_size;
+
+ /* check for overflow on very large files */
+ if (file_size != finfo.st_size || file_size + 1 < file_size)
+ {
+ errno = overflow_errno;
+ goto error_and_exit;
+ }
+
+ if (file_size == 0)
+ {
+ xfree (input);
+ close (file);
+ return 0; /* don't waste time if we don't have to */
+ }
+
+#ifdef HISTORY_USE_MMAP
+ /* We map read/write and private so we can change newlines to NULs without
+ affecting the underlying object. */
+ buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0);
+ if ((void *)buffer == MAP_FAILED)
+ {
+ errno = overflow_errno;
+ goto error_and_exit;
+ }
+ chars_read = file_size;
+#else
+ buffer = (char *)malloc (file_size + 1);
+ if (buffer == 0)
+ {
+ errno = overflow_errno;
+ goto error_and_exit;
+ }
+
+ chars_read = read (file, buffer, file_size);
+#endif
+ if (chars_read < 0)
+ {
+ error_and_exit:
+ if (errno != 0)
+ chars_read = errno;
+ else
+ chars_read = EIO;
+ if (file >= 0)
+ close (file);
+
+ FREE (input);
+#ifndef HISTORY_USE_MMAP
+ FREE (buffer);
+#endif
+
+ return (chars_read);
+ }
+
+ close (file);
+
+ /* Set TO to larger than end of file if negative. */
+ if (to < 0)
+ to = chars_read;
+
+ /* Start at beginning of file, work to end. */
+ bufend = buffer + chars_read;
+ *bufend = '\0'; /* null-terminate buffer for timestamp checks */
+ current_line = 0;
+
+ /* Heuristic: the history comment character rarely changes, so assume we
+ have timestamps if the buffer starts with `#[:digit:]' and temporarily
+ set history_comment_char so timestamp parsing works right */
+ reset_comment_char = 0;
+ if (history_comment_char == '\0' && buffer[0] == '#' && isdigit ((unsigned char)buffer[1]))
+ {
+ history_comment_char = '#';
+ reset_comment_char = 1;
+ }
+
+ has_timestamps = HIST_TIMESTAMP_START (buffer);
+ history_multiline_entries += has_timestamps && history_write_timestamps;
+
+ /* Skip lines until we are at FROM. */
+ if (has_timestamps)
+ last_ts = buffer;
+ for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
+ if (*line_end == '\n')
+ {
+ p = line_end + 1;
+ /* If we see something we think is a timestamp, continue with this
+ line. We should check more extensively here... */
+ if (HIST_TIMESTAMP_START(p) == 0)
+ current_line++;
+ else
+ last_ts = p;
+ line_start = p;
+ /* If we are at the last line (current_line == from) but we have
+ timestamps (has_timestamps), then line_start points to the
+ text of the last command, and we need to skip to its end. */
+ if (current_line >= from && has_timestamps)
+ {
+ for (line_end = p; line_end < bufend && *line_end != '\n'; line_end++)
+ ;
+ line_start = (*line_end == '\n') ? line_end + 1 : line_end;
+ }
+ }
+
+ /* If there are lines left to gobble, then gobble them now. */
+ for (line_end = line_start; line_end < bufend; line_end++)
+ if (*line_end == '\n')
+ {
+ /* Change to allow Windows-like \r\n end of line delimiter. */
+ if (line_end > line_start && line_end[-1] == '\r')
+ line_end[-1] = '\0';
+ else
+ *line_end = '\0';
+
+ if (*line_start)
+ {
+ if (HIST_TIMESTAMP_START(line_start) == 0)
+ {
+ if (last_ts == NULL && history_length > 0 && history_multiline_entries)
+ _hs_append_history_line (history_length - 1, line_start);
+ else
+ add_history (line_start);
+ if (last_ts)
+ {
+ add_history_time (last_ts);
+ last_ts = NULL;
+ }
+ }
+ else
+ {
+ last_ts = line_start;
+ current_line--;
+ }
+ }
+
+ current_line++;
+
+ if (current_line >= to)
+ break;
+
+ line_start = line_end + 1;
+ }
+
+ history_lines_read_from_file = current_line;
+ if (reset_comment_char)
+ history_comment_char = '\0';
+
+ FREE (input);
+#ifndef HISTORY_USE_MMAP
+ FREE (buffer);
+#else
+ munmap (buffer, file_size);
+#endif
+
+ return (0);
+}
+
+/* We need a special version for WIN32 because Windows rename() refuses to
+ overwrite an existing file. */
+static int
+history_rename (const char *old, const char *new)
+{
+#if defined (_WIN32)
+ return (MoveFileEx (old, new, MOVEFILE_REPLACE_EXISTING) == 0 ? -1 : 0);
+#else
+ return (rename (old, new));
+#endif
+}
+
+/* Save FILENAME to BACK, handling case where FILENAME is a symlink
+ (e.g., ~/.bash_history -> .histfiles/.bash_history.$HOSTNAME) */
+static int
+histfile_backup (const char *filename, const char *back)
+{
+#if defined (HAVE_READLINK)
+ char linkbuf[PATH_MAX+1];
+ ssize_t n;
+
+ /* Follow to target of symlink to avoid renaming symlink itself */
+ if ((n = readlink (filename, linkbuf, sizeof (linkbuf) - 1)) > 0)
+ {
+ linkbuf[n] = '\0';
+ return (history_rename (linkbuf, back));
+ }
+#endif
+ return (history_rename (filename, back));
+}
+
+/* Restore ORIG from BACKUP handling case where ORIG is a symlink
+ (e.g., ~/.bash_history -> .histfiles/.bash_history.$HOSTNAME) */
+static int
+histfile_restore (const char *backup, const char *orig)
+{
+#if defined (HAVE_READLINK)
+ char linkbuf[PATH_MAX+1];
+ ssize_t n;
+
+ /* Follow to target of symlink to avoid renaming symlink itself */
+ if ((n = readlink (orig, linkbuf, sizeof (linkbuf) - 1)) > 0)
+ {
+ linkbuf[n] = '\0';
+ return (history_rename (backup, linkbuf));
+ }
+#endif
+ return (history_rename (backup, orig));
+}
+
+/* Should we call chown, based on whether finfo and nfinfo describe different
+ files with different owners? */
+
+#define SHOULD_CHOWN(finfo, nfinfo) \
+ (finfo.st_uid != nfinfo.st_uid || finfo.st_gid != nfinfo.st_gid)
+
+/* Truncate the history file FNAME, leaving only LINES trailing lines.
+ If FNAME is NULL, then use ~/.history. Writes a new file and renames
+ it to the original name. Returns 0 on success, errno on failure. */
+int
+history_truncate_file (const char *fname, int lines)
+{
+ char *buffer, *filename, *tempname, *bp, *bp1; /* bp1 == bp+1 */
+ int file, chars_read, rv, orig_lines, exists, r;
+ struct stat finfo, nfinfo;
+ size_t file_size;
+
+ history_lines_written_to_file = 0;
+
+ buffer = (char *)NULL;
+ filename = history_filename (fname);
+ tempname = 0;
+ file = filename ? open (filename, O_RDONLY|O_BINARY, 0666) : -1;
+ rv = exists = 0;
+
+ /* Don't try to truncate non-regular files. */
+ if (file == -1 || fstat (file, &finfo) == -1)
+ {
+ rv = errno;
+ if (file != -1)
+ close (file);
+ goto truncate_exit;
+ }
+ exists = 1;
+
+ nfinfo.st_uid = finfo.st_uid;
+ nfinfo.st_gid = finfo.st_gid;
+
+ if (S_ISREG (finfo.st_mode) == 0)
+ {
+ close (file);
+#ifdef EFTYPE
+ rv = EFTYPE;
+#else
+ rv = EINVAL;
+#endif
+ goto truncate_exit;
+ }
+
+ file_size = (size_t)finfo.st_size;
+
+ /* check for overflow on very large files */
+ if (file_size != finfo.st_size || file_size + 1 < file_size)
+ {
+ close (file);
+#if defined (EFBIG)
+ rv = errno = EFBIG;
+#elif defined (EOVERFLOW)
+ rv = errno = EOVERFLOW;
+#else
+ rv = errno = EINVAL;
+#endif
+ goto truncate_exit;
+ }
+
+ buffer = (char *)malloc (file_size + 1);
+ if (buffer == 0)
+ {
+ rv = errno;
+ close (file);
+ goto truncate_exit;
+ }
+
+ chars_read = read (file, buffer, file_size);
+ close (file);
+
+ if (chars_read <= 0)
+ {
+ rv = (chars_read < 0) ? errno : 0;
+ goto truncate_exit;
+ }
+
+ orig_lines = lines;
+ /* Count backwards from the end of buffer until we have passed
+ LINES lines. bp1 is set funny initially. But since bp[1] can't
+ be a comment character (since it's off the end) and *bp can't be
+ both a newline and the history comment character, it should be OK. */
+ for (bp1 = bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
+ {
+ if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
+ lines--;
+ bp1 = bp;
+ }
+
+ /* If this is the first line, then the file contains exactly the
+ number of lines we want to truncate to, so we don't need to do
+ anything. It's the first line if we don't find a newline between
+ the current value of i and 0. Otherwise, write from the start of
+ this line until the end of the buffer. */
+ for ( ; bp > buffer; bp--)
+ {
+ if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
+ {
+ bp++;
+ break;
+ }
+ bp1 = bp;
+ }
+
+ /* Write only if there are more lines in the file than we want to
+ truncate to. */
+ if (bp <= buffer)
+ {
+ rv = 0;
+ /* No-op if LINES == 0 at this point */
+ history_lines_written_to_file = orig_lines - lines;
+ goto truncate_exit;
+ }
+
+ tempname = history_tempfile (filename);
+
+ if ((file = open (tempname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0600)) != -1)
+ {
+ if (write (file, bp, chars_read - (bp - buffer)) < 0)
+ rv = errno;
+
+ if (fstat (file, &nfinfo) < 0 && rv == 0)
+ rv = errno;
+
+ if (close (file) < 0 && rv == 0)
+ rv = errno;
+ }
+ else
+ rv = errno;
+
+ truncate_exit:
+ FREE (buffer);
+
+ history_lines_written_to_file = orig_lines - lines;
+
+ if (rv == 0 && filename && tempname)
+ rv = histfile_restore (tempname, filename);
+
+ if (rv != 0)
+ {
+ rv = errno;
+ if (tempname)
+ unlink (tempname);
+ history_lines_written_to_file = 0;
+ }
+
+#if defined (HAVE_CHOWN)
+ /* Make sure the new filename is owned by the same user as the old. If one
+ user is running this, it's a no-op. If the shell is running after sudo
+ with a shared history file, we don't want to leave the history file
+ owned by root. */
+ if (rv == 0 && exists && SHOULD_CHOWN (finfo, nfinfo))
+ r = chown (filename, finfo.st_uid, finfo.st_gid);
+#endif
+
+ xfree (filename);
+ FREE (tempname);
+
+ return rv;
+}
+
+/* Workhorse function for writing history. Writes the last NELEMENT entries
+ from the history list to FILENAME. OVERWRITE is non-zero if you
+ wish to replace FILENAME with the entries. */
+static int
+history_do_write (const char *filename, int nelements, int overwrite)
+{
+ register int i;
+ char *output, *tempname, *histname;
+ int file, mode, rv, exists;
+ struct stat finfo, nfinfo;
+#ifdef HISTORY_USE_MMAP
+ size_t cursize;
+
+ history_lines_written_to_file = 0;
+
+ mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
+#else
+ mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
+#endif
+ histname = history_filename (filename);
+ exists = histname ? (stat (histname, &finfo) == 0) : 0;
+
+ tempname = (overwrite && exists && S_ISREG (finfo.st_mode)) ? history_tempfile (histname) : 0;
+ output = tempname ? tempname : histname;
+
+ file = output ? open (output, mode, 0600) : -1;
+ rv = 0;
+
+ if (file == -1)
+ {
+ rv = errno;
+ FREE (histname);
+ FREE (tempname);
+ return (rv);
+ }
+
+#ifdef HISTORY_USE_MMAP
+ cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
+#endif
+
+ if (nelements > history_length)
+ nelements = history_length;
+
+ /* Build a buffer of all the lines to write, and write them in one syscall.
+ Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
+ {
+ HIST_ENTRY **the_history; /* local */
+ register int j;
+ int buffer_size;
+ char *buffer;
+
+ the_history = history_list ();
+ /* Calculate the total number of bytes to write. */
+ for (buffer_size = 0, i = history_length - nelements; i < history_length; i++)
+ {
+ if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
+ buffer_size += strlen (the_history[i]->timestamp) + 1;
+ buffer_size += strlen (the_history[i]->line) + 1;
+ }
+
+ /* Allocate the buffer, and fill it. */
+#ifdef HISTORY_USE_MMAP
+ if (ftruncate (file, buffer_size+cursize) == -1)
+ goto mmap_error;
+ buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize);
+ if ((void *)buffer == MAP_FAILED)
+ {
+mmap_error:
+ rv = errno;
+ close (file);
+ if (tempname)
+ unlink (tempname);
+ FREE (histname);
+ FREE (tempname);
+ return rv;
+ }
+#else
+ buffer = (char *)malloc (buffer_size);
+ if (buffer == 0)
+ {
+ rv = errno;
+ close (file);
+ if (tempname)
+ unlink (tempname);
+ FREE (histname);
+ FREE (tempname);
+ return rv;
+ }
+#endif
+
+ for (j = 0, i = history_length - nelements; i < history_length; i++)
+ {
+ if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
+ {
+ strcpy (buffer + j, the_history[i]->timestamp);
+ j += strlen (the_history[i]->timestamp);
+ buffer[j++] = '\n';
+ }
+ strcpy (buffer + j, the_history[i]->line);
+ j += strlen (the_history[i]->line);
+ buffer[j++] = '\n';
+ }
+
+#ifdef HISTORY_USE_MMAP
+ if (msync (buffer, buffer_size, MS_ASYNC) != 0 || munmap (buffer, buffer_size) != 0)
+ rv = errno;
+#else
+ if (write (file, buffer, buffer_size) < 0)
+ rv = errno;
+ xfree (buffer);
+#endif
+ }
+
+ history_lines_written_to_file = nelements;
+
+ if (close (file) < 0 && rv == 0)
+ rv = errno;
+
+ if (rv == 0 && histname && tempname)
+ rv = histfile_restore (tempname, histname);
+
+ if (rv != 0)
+ {
+ rv = errno;
+ if (tempname)
+ unlink (tempname);
+ history_lines_written_to_file = 0;
+ }
+
+#if defined (HAVE_CHOWN)
+ /* Make sure the new filename is owned by the same user as the old. If one
+ user is running this, it's a no-op. If the shell is running after sudo
+ with a shared history file, we don't want to leave the history file
+ owned by root. */
+ if (rv == 0 && exists)
+ mode = chown (histname, finfo.st_uid, finfo.st_gid);
+#endif
+
+ FREE (histname);
+ FREE (tempname);
+
+ return (rv);
+}
+
+/* Append NELEMENT entries to FILENAME. The entries appended are from
+ the end of the list minus NELEMENTs up to the end of the list. */
+int
+append_history (int nelements, const char *filename)
+{
+ return (history_do_write (filename, nelements, HISTORY_APPEND));
+}
+
+/* Overwrite FILENAME with the current history. If FILENAME is NULL,
+ then write the history list to ~/.history. Values returned
+ are as in read_history ().*/
+int
+write_history (const char *filename)
+{
+ return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
+}
diff --git a/third_party/readline/histlib.h b/third_party/readline/histlib.h
new file mode 100644
index 000000000..29fc4d2e5
--- /dev/null
+++ b/third_party/readline/histlib.h
@@ -0,0 +1,92 @@
+/* histlib.h -- internal definitions for the history library. */
+
+/* Copyright (C) 1989-2009,2021-2022 Free Software Foundation, Inc.
+
+ This file contains the GNU History Library (History), a set of
+ routines for managing the text of previously typed lines.
+
+ History is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ History 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 History. If not, see .
+*/
+
+#if !defined (_HISTLIB_H_)
+#define _HISTLIB_H_
+
+#if defined (HAVE_STRING_H)
+# include
+#else
+# include
+#endif /* !HAVE_STRING_H */
+
+#if !defined (STREQ)
+#define STREQ(a, b) (((a)[0] == (b)[0]) && (strcmp ((a), (b)) == 0))
+#define STREQN(a, b, n) (((n) == 0) ? (1) \
+ : ((a)[0] == (b)[0]) && (strncmp ((a), (b), (n)) == 0))
+#endif
+
+#ifndef savestring
+#define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x))
+#endif
+
+#ifndef whitespace
+#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
+#endif
+
+#ifndef _rl_digit_p
+#define _rl_digit_p(c) ((c) >= '0' && (c) <= '9')
+#endif
+
+#ifndef _rl_digit_value
+#define _rl_digit_value(c) ((c) - '0')
+#endif
+
+#ifndef member
+# if !defined (strchr) && !defined (__STDC__)
+extern char *strchr ();
+# endif /* !strchr && !__STDC__ */
+#define member(c, s) ((c) ? ((char *)strchr ((s), (c)) != (char *)NULL) : 0)
+#endif
+
+#ifndef FREE
+# define FREE(x) if (x) free (x)
+#endif
+
+/* Possible history errors passed to hist_error. */
+#define EVENT_NOT_FOUND 0
+#define BAD_WORD_SPEC 1
+#define SUBST_FAILED 2
+#define BAD_MODIFIER 3
+#define NO_PREV_SUBST 4
+
+/* Possible definitions for history starting point specification. */
+#define NON_ANCHORED_SEARCH 0
+#define ANCHORED_SEARCH 0x01
+#define PATTERN_SEARCH 0x02
+
+/* Possible definitions for what style of writing the history file we want. */
+#define HISTORY_APPEND 0
+#define HISTORY_OVERWRITE 1
+
+/* internal extern function declarations used by other parts of the library */
+
+/* histsearch.c */
+extern int _hs_history_patsearch (const char *, int, int);
+
+/* history.c */
+extern void _hs_replace_history_data (int, histdata_t *, histdata_t *);
+extern int _hs_at_end_of_history (void);
+
+/* histfile.c */
+extern void _hs_append_history_line (int, const char *);
+
+#endif /* !_HISTLIB_H_ */
diff --git a/third_party/readline/history.c b/third_party/readline/history.c
new file mode 100644
index 000000000..31ddd4a36
--- /dev/null
+++ b/third_party/readline/history.c
@@ -0,0 +1,613 @@
+/* history.c -- standalone history library */
+
+/* Copyright (C) 1989-2021 Free Software Foundation, Inc.
+
+ This file contains the GNU History Library (History), a set of
+ routines for managing the text of previously typed lines.
+
+ History is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ History 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 History. If not, see .
+*/
+
+/* The goal is to make the implementation transparent, so that you
+ don't have to know what data types are used, just what functions
+ you can call. I think I have done that. */
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#if defined (HAVE_UNISTD_H)
+# ifdef _MINIX
+# include
+# endif
+# include
+#endif
+
+#include
+
+#include "history.h"
+#include "histlib.h"
+
+#include "xmalloc.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+/* How big to make the_history when we first allocate it. */
+#define DEFAULT_HISTORY_INITIAL_SIZE 502
+
+#define MAX_HISTORY_INITIAL_SIZE 8192
+
+/* The number of slots to increase the_history by. */
+#define DEFAULT_HISTORY_GROW_SIZE 50
+
+static char *hist_inittime (void);
+
+/* **************************************************************** */
+/* */
+/* History Functions */
+/* */
+/* **************************************************************** */
+
+/* An array of HIST_ENTRY. This is where we store the history. */
+static HIST_ENTRY **the_history = (HIST_ENTRY **)NULL;
+
+/* Non-zero means that we have enforced a limit on the amount of
+ history that we save. */
+static int history_stifled;
+
+/* The current number of slots allocated to the input_history. */
+static int history_size;
+
+/* If HISTORY_STIFLED is non-zero, then this is the maximum number of
+ entries to remember. */
+int history_max_entries;
+int max_input_history; /* backwards compatibility */
+
+/* The current location of the interactive history pointer. Just makes
+ life easier for outside callers. */
+int history_offset;
+
+/* The number of strings currently stored in the history list. */
+int history_length;
+
+/* The logical `base' of the history array. It defaults to 1. */
+int history_base = 1;
+
+/* Return the current HISTORY_STATE of the history. */
+HISTORY_STATE *
+history_get_history_state (void)
+{
+ HISTORY_STATE *state;
+
+ state = (HISTORY_STATE *)xmalloc (sizeof (HISTORY_STATE));
+ state->entries = the_history;
+ state->offset = history_offset;
+ state->length = history_length;
+ state->size = history_size;
+ state->flags = 0;
+ if (history_stifled)
+ state->flags |= HS_STIFLED;
+
+ return (state);
+}
+
+/* Set the state of the current history array to STATE. */
+void
+history_set_history_state (HISTORY_STATE *state)
+{
+ the_history = state->entries;
+ history_offset = state->offset;
+ history_length = state->length;
+ history_size = state->size;
+ if (state->flags & HS_STIFLED)
+ history_stifled = 1;
+}
+
+/* Begin a session in which the history functions might be used. This
+ initializes interactive variables. */
+void
+using_history (void)
+{
+ history_offset = history_length;
+}
+
+/* Return the number of bytes that the primary history entries are using.
+ This just adds up the lengths of the_history->lines and the associated
+ timestamps. */
+int
+history_total_bytes (void)
+{
+ register int i, result;
+
+ for (i = result = 0; the_history && the_history[i]; i++)
+ result += HISTENT_BYTES (the_history[i]);
+
+ return (result);
+}
+
+/* Returns the magic number which says what history element we are
+ looking at now. In this implementation, it returns history_offset. */
+int
+where_history (void)
+{
+ return (history_offset);
+}
+
+/* Make the current history item be the one at POS, an absolute index.
+ Returns zero if POS is out of range, else non-zero. */
+int
+history_set_pos (int pos)
+{
+ if (pos > history_length || pos < 0 || !the_history)
+ return (0);
+ history_offset = pos;
+ return (1);
+}
+
+/* Are we currently at the end of the history list? */
+int
+_hs_at_end_of_history (void)
+{
+ return (the_history == 0 || history_offset == history_length);
+}
+
+/* Return the current history array. The caller has to be careful, since this
+ is the actual array of data, and could be bashed or made corrupt easily.
+ The array is terminated with a NULL pointer. */
+HIST_ENTRY **
+history_list (void)
+{
+ return (the_history);
+}
+
+/* Return the history entry at the current position, as determined by
+ history_offset. If there is no entry there, return a NULL pointer. */
+HIST_ENTRY *
+current_history (void)
+{
+ return ((history_offset == history_length) || the_history == 0)
+ ? (HIST_ENTRY *)NULL
+ : the_history[history_offset];
+}
+
+/* Back up history_offset to the previous history entry, and return
+ a pointer to that entry. If there is no previous entry then return
+ a NULL pointer. */
+HIST_ENTRY *
+previous_history (void)
+{
+ return history_offset ? the_history[--history_offset] : (HIST_ENTRY *)NULL;
+}
+
+/* Move history_offset forward to the next history entry, and return
+ a pointer to that entry. If there is no next entry then return a
+ NULL pointer. */
+HIST_ENTRY *
+next_history (void)
+{
+ return (history_offset == history_length) ? (HIST_ENTRY *)NULL : the_history[++history_offset];
+}
+
+/* Return the history entry which is logically at OFFSET in the history array.
+ OFFSET is relative to history_base. */
+HIST_ENTRY *
+history_get (int offset)
+{
+ int local_index;
+
+ local_index = offset - history_base;
+ return (local_index >= history_length || local_index < 0 || the_history == 0)
+ ? (HIST_ENTRY *)NULL
+ : the_history[local_index];
+}
+
+HIST_ENTRY *
+alloc_history_entry (char *string, char *ts)
+{
+ HIST_ENTRY *temp;
+
+ temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
+
+ temp->line = string ? savestring (string) : string;
+ temp->data = (char *)NULL;
+ temp->timestamp = ts;
+
+ return temp;
+}
+
+time_t
+history_get_time (HIST_ENTRY *hist)
+{
+ char *ts;
+ time_t t;
+
+ if (hist == 0 || hist->timestamp == 0)
+ return 0;
+ ts = hist->timestamp;
+ if (ts[0] != history_comment_char)
+ return 0;
+ errno = 0;
+ t = (time_t) strtol (ts + 1, (char **)NULL, 10); /* XXX - should use strtol() here */
+ if (errno == ERANGE)
+ return (time_t)0;
+ return t;
+}
+
+static char *
+hist_inittime (void)
+{
+ time_t t;
+ char ts[64], *ret;
+
+ t = (time_t) time ((time_t *)0);
+#if defined (HAVE_VSNPRINTF) /* assume snprintf if vsnprintf exists */
+ snprintf (ts, sizeof (ts) - 1, "X%lu", (unsigned long) t);
+#else
+ sprintf (ts, "X%lu", (unsigned long) t);
+#endif
+ ret = savestring (ts);
+ ret[0] = history_comment_char;
+
+ return ret;
+}
+
+/* Place STRING at the end of the history list. The data field
+ is set to NULL. */
+void
+add_history (const char *string)
+{
+ HIST_ENTRY *temp;
+ int new_length;
+
+ if (history_stifled && (history_length == history_max_entries))
+ {
+ /* If the history is stifled, and history_length is zero,
+ and it equals history_max_entries, we don't save items. */
+ if (history_length == 0)
+ return;
+
+ /* If there is something in the slot, then remove it. */
+ if (the_history[0])
+ (void) free_history_entry (the_history[0]);
+
+ /* Copy the rest of the entries, moving down one slot. Copy includes
+ trailing NULL. */
+ memmove (the_history, the_history + 1, history_length * sizeof (HIST_ENTRY *));
+
+ new_length = history_length;
+ history_base++;
+ }
+ else
+ {
+ if (history_size == 0)
+ {
+ if (history_stifled && history_max_entries > 0)
+ history_size = (history_max_entries > MAX_HISTORY_INITIAL_SIZE)
+ ? MAX_HISTORY_INITIAL_SIZE
+ : history_max_entries + 2;
+ else
+ history_size = DEFAULT_HISTORY_INITIAL_SIZE;
+ the_history = (HIST_ENTRY **)xmalloc (history_size * sizeof (HIST_ENTRY *));
+ new_length = 1;
+ }
+ else
+ {
+ if (history_length == (history_size - 1))
+ {
+ history_size += DEFAULT_HISTORY_GROW_SIZE;
+ the_history = (HIST_ENTRY **)
+ xrealloc (the_history, history_size * sizeof (HIST_ENTRY *));
+ }
+ new_length = history_length + 1;
+ }
+ }
+
+ temp = alloc_history_entry ((char *)string, hist_inittime ());
+
+ the_history[new_length] = (HIST_ENTRY *)NULL;
+ the_history[new_length - 1] = temp;
+ history_length = new_length;
+}
+
+/* Change the time stamp of the most recent history entry to STRING. */
+void
+add_history_time (const char *string)
+{
+ HIST_ENTRY *hs;
+
+ if (string == 0 || history_length < 1)
+ return;
+ hs = the_history[history_length - 1];
+ FREE (hs->timestamp);
+ hs->timestamp = savestring (string);
+}
+
+/* Free HIST and return the data so the calling application can free it
+ if necessary and desired. */
+histdata_t
+free_history_entry (HIST_ENTRY *hist)
+{
+ histdata_t x;
+
+ if (hist == 0)
+ return ((histdata_t) 0);
+ FREE (hist->line);
+ FREE (hist->timestamp);
+ x = hist->data;
+ xfree (hist);
+ return (x);
+}
+
+HIST_ENTRY *
+copy_history_entry (HIST_ENTRY *hist)
+{
+ HIST_ENTRY *ret;
+ char *ts;
+
+ if (hist == 0)
+ return hist;
+
+ ret = alloc_history_entry (hist->line, (char *)NULL);
+
+ ts = hist->timestamp ? savestring (hist->timestamp) : hist->timestamp;
+ ret->timestamp = ts;
+
+ ret->data = hist->data;
+
+ return ret;
+}
+
+/* Make the history entry at WHICH have LINE and DATA. This returns
+ the old entry so you can dispose of the data. In the case of an
+ invalid WHICH, a NULL pointer is returned. */
+HIST_ENTRY *
+replace_history_entry (int which, const char *line, histdata_t data)
+{
+ HIST_ENTRY *temp, *old_value;
+
+ if (which < 0 || which >= history_length)
+ return ((HIST_ENTRY *)NULL);
+
+ temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
+ old_value = the_history[which];
+
+ temp->line = savestring (line);
+ temp->data = data;
+ temp->timestamp = old_value->timestamp ? savestring (old_value->timestamp) : 0;
+ the_history[which] = temp;
+
+ return (old_value);
+}
+
+/* Append LINE to the history line at offset WHICH, adding a newline to the
+ end of the current line first. This can be used to construct multi-line
+ history entries while reading lines from the history file. */
+void
+_hs_append_history_line (int which, const char *line)
+{
+ HIST_ENTRY *hent;
+ size_t newlen, curlen, minlen;
+ char *newline;
+
+ hent = the_history[which];
+ curlen = strlen (hent->line);
+ minlen = curlen + strlen (line) + 2; /* min space needed */
+ if (curlen > 256) /* XXX - for now */
+ {
+ newlen = 512; /* now realloc in powers of 2 */
+ /* we recalcluate every time; the operations are cheap */
+ while (newlen < minlen)
+ newlen <<= 1;
+ }
+ else
+ newlen = minlen;
+ /* Assume that realloc returns the same pointer and doesn't try a new
+ alloc/copy if the new size is the same as the one last passed. */
+ newline = realloc (hent->line, newlen);
+ if (newline)
+ {
+ hent->line = newline;
+ hent->line[curlen++] = '\n';
+ strcpy (hent->line + curlen, line);
+ }
+}
+
+/* Replace the DATA in the specified history entries, replacing OLD with
+ NEW. WHICH says which one(s) to replace: WHICH == -1 means to replace
+ all of the history entries where entry->data == OLD; WHICH == -2 means
+ to replace the `newest' history entry where entry->data == OLD; and
+ WHICH >= 0 means to replace that particular history entry's data, as
+ long as it matches OLD. */
+void
+_hs_replace_history_data (int which, histdata_t *old, histdata_t *new)
+{
+ HIST_ENTRY *entry;
+ register int i, last;
+
+ if (which < -2 || which >= history_length || history_length == 0 || the_history == 0)
+ return;
+
+ if (which >= 0)
+ {
+ entry = the_history[which];
+ if (entry && entry->data == old)
+ entry->data = new;
+ return;
+ }
+
+ last = -1;
+ for (i = 0; i < history_length; i++)
+ {
+ entry = the_history[i];
+ if (entry == 0)
+ continue;
+ if (entry->data == old)
+ {
+ last = i;
+ if (which == -1)
+ entry->data = new;
+ }
+ }
+ if (which == -2 && last >= 0)
+ {
+ entry = the_history[last];
+ entry->data = new; /* XXX - we don't check entry->old */
+ }
+}
+
+/* Remove history element WHICH from the history. The removed
+ element is returned to you so you can free the line, data,
+ and containing structure. */
+HIST_ENTRY *
+remove_history (int which)
+{
+ HIST_ENTRY *return_value;
+ register int i;
+ (void)i;
+#if 1
+ int nentries;
+ HIST_ENTRY **start, **end;
+#endif
+
+ if (which < 0 || which >= history_length || history_length == 0 || the_history == 0)
+ return ((HIST_ENTRY *)NULL);
+
+ return_value = the_history[which];
+
+#if 1
+ /* Copy the rest of the entries, moving down one slot. Copy includes
+ trailing NULL. */
+ nentries = history_length - which;
+ start = the_history + which;
+ end = start + 1;
+ memmove (start, end, nentries * sizeof (HIST_ENTRY *));
+#else
+ for (i = which; i < history_length; i++)
+ the_history[i] = the_history[i + 1];
+#endif
+
+ history_length--;
+
+ return (return_value);
+}
+
+HIST_ENTRY **
+remove_history_range (int first, int last)
+{
+ HIST_ENTRY **return_value;
+ register int i;
+ int nentries;
+ HIST_ENTRY **start, **end;
+
+ if (the_history == 0 || history_length == 0)
+ return ((HIST_ENTRY **)NULL);
+ if (first < 0 || first >= history_length || last < 0 || last >= history_length)
+ return ((HIST_ENTRY **)NULL);
+ if (first > last)
+ return (HIST_ENTRY **)NULL;
+
+ nentries = last - first + 1;
+ return_value = (HIST_ENTRY **)malloc ((nentries + 1) * sizeof (HIST_ENTRY *));
+ if (return_value == 0)
+ return return_value;
+
+ /* Return all the deleted entries in a list */
+ for (i = first ; i <= last; i++)
+ return_value[i - first] = the_history[i];
+ return_value[i - first] = (HIST_ENTRY *)NULL;
+
+ /* Copy the rest of the entries, moving down NENTRIES slots. Copy includes
+ trailing NULL. */
+ start = the_history + first;
+ end = the_history + last + 1;
+ memmove (start, end, (history_length - last) * sizeof (HIST_ENTRY *));
+
+ history_length -= nentries;
+
+ return (return_value);
+}
+
+/* Stifle the history list, remembering only MAX number of lines. */
+void
+stifle_history (int max)
+{
+ register int i, j;
+
+ if (max < 0)
+ max = 0;
+
+ if (history_length > max)
+ {
+ /* This loses because we cannot free the data. */
+ for (i = 0, j = history_length - max; i < j; i++)
+ free_history_entry (the_history[i]);
+
+ history_base = i;
+ for (j = 0, i = history_length - max; j < max; i++, j++)
+ the_history[j] = the_history[i];
+ the_history[j] = (HIST_ENTRY *)NULL;
+ history_length = j;
+ }
+
+ history_stifled = 1;
+ max_input_history = history_max_entries = max;
+}
+
+/* Stop stifling the history. This returns the previous maximum
+ number of history entries. The value is positive if the history
+ was stifled, negative if it wasn't. */
+int
+unstifle_history (void)
+{
+ if (history_stifled)
+ {
+ history_stifled = 0;
+ return (history_max_entries);
+ }
+ else
+ return (-history_max_entries);
+}
+
+int
+history_is_stifled (void)
+{
+ return (history_stifled);
+}
+
+void
+clear_history (void)
+{
+ register int i;
+
+ /* This loses because we cannot free the data. */
+ for (i = 0; i < history_length; i++)
+ {
+ free_history_entry (the_history[i]);
+ the_history[i] = (HIST_ENTRY *)NULL;
+ }
+
+ history_offset = history_length = 0;
+ history_base = 1; /* reset history base to default */
+}
diff --git a/third_party/readline/history.h b/third_party/readline/history.h
new file mode 100644
index 000000000..15c3a3fe3
--- /dev/null
+++ b/third_party/readline/history.h
@@ -0,0 +1,291 @@
+/* history.h -- the names of functions that you can call in history. */
+
+/* Copyright (C) 1989-2022 Free Software Foundation, Inc.
+
+ This file contains the GNU History Library (History), a set of
+ routines for managing the text of previously typed lines.
+
+ History is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ History 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 History. If not, see .
+*/
+
+#ifndef _HISTORY_H_
+#define _HISTORY_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include /* XXX - for history timestamp code */
+
+#if defined READLINE_LIBRARY
+# include "rlstdc.h"
+# include "rltypedefs.h"
+#else
+# include "third_party/readline/rlstdc.h"
+# include "third_party/readline/rltypedefs.h"
+#endif
+
+#ifdef __STDC__
+typedef void *histdata_t;
+#else
+typedef char *histdata_t;
+#endif
+
+/* Let's not step on anyone else's define for now, since we don't use this yet. */
+#ifndef HS_HISTORY_VERSION
+# define HS_HISTORY_VERSION 0x0802 /* History 8.2 */
+#endif
+
+/* The structure used to store a history entry. */
+typedef struct _hist_entry {
+ char *line;
+ char *timestamp; /* char * rather than time_t for read/write */
+ histdata_t data;
+} HIST_ENTRY;
+
+/* Size of the history-library-managed space in history entry HS. */
+#define HISTENT_BYTES(hs) (strlen ((hs)->line) + strlen ((hs)->timestamp))
+
+/* A structure used to pass the current state of the history stuff around. */
+typedef struct _hist_state {
+ HIST_ENTRY **entries; /* Pointer to the entries themselves. */
+ int offset; /* The location pointer within this array. */
+ int length; /* Number of elements within this array. */
+ int size; /* Number of slots allocated to this array. */
+ int flags;
+} HISTORY_STATE;
+
+/* Flag values for the `flags' member of HISTORY_STATE. */
+#define HS_STIFLED 0x01
+
+/* Initialization and state management. */
+
+/* Begin a session in which the history functions might be used. This
+ just initializes the interactive variables. */
+extern void using_history (void);
+
+/* Return the current HISTORY_STATE of the history. */
+extern HISTORY_STATE *history_get_history_state (void);
+
+/* Set the state of the current history array to STATE. */
+extern void history_set_history_state (HISTORY_STATE *);
+
+/* Manage the history list. */
+
+/* Place STRING at the end of the history list.
+ The associated data field (if any) is set to NULL. */
+extern void add_history (const char *);
+
+/* Change the timestamp associated with the most recent history entry to
+ STRING. */
+extern void add_history_time (const char *);
+
+/* Remove an entry from the history list. WHICH is the magic number that
+ tells us which element to delete. The elements are numbered from 0. */
+extern HIST_ENTRY *remove_history (int);
+
+/* Remove a set of entries from the history list: FIRST to LAST, inclusive */
+extern HIST_ENTRY **remove_history_range (int, int);
+
+/* Allocate a history entry consisting of STRING and TIMESTAMP and return
+ a pointer to it. */
+extern HIST_ENTRY *alloc_history_entry (char *, char *);
+
+/* Copy the history entry H, but not the (opaque) data pointer */
+extern HIST_ENTRY *copy_history_entry (HIST_ENTRY *);
+
+/* Free the history entry H and return any application-specific data
+ associated with it. */
+extern histdata_t free_history_entry (HIST_ENTRY *);
+
+/* Make the history entry at WHICH have LINE and DATA. This returns
+ the old entry so you can dispose of the data. In the case of an
+ invalid WHICH, a NULL pointer is returned. */
+extern HIST_ENTRY *replace_history_entry (int, const char *, histdata_t);
+
+/* Clear the history list and start over. */
+extern void clear_history (void);
+
+/* Stifle the history list, remembering only MAX number of entries. */
+extern void stifle_history (int);
+
+/* Stop stifling the history. This returns the previous amount the
+ history was stifled by. The value is positive if the history was
+ stifled, negative if it wasn't. */
+extern int unstifle_history (void);
+
+/* Return 1 if the history is stifled, 0 if it is not. */
+extern int history_is_stifled (void);
+
+/* Information about the history list. */
+
+/* Return a NULL terminated array of HIST_ENTRY which is the current input
+ history. Element 0 of this list is the beginning of time. If there
+ is no history, return NULL. */
+extern HIST_ENTRY **history_list (void);
+
+/* Returns the number which says what history element we are now
+ looking at. */
+extern int where_history (void);
+
+/* Return the history entry at the current position, as determined by
+ history_offset. If there is no entry there, return a NULL pointer. */
+extern HIST_ENTRY *current_history (void);
+
+/* Return the history entry which is logically at OFFSET in the history
+ array. OFFSET is relative to history_base. */
+extern HIST_ENTRY *history_get (int);
+
+/* Return the timestamp associated with the HIST_ENTRY * passed as an
+ argument */
+extern time_t history_get_time (HIST_ENTRY *);
+
+/* Return the number of bytes that the primary history entries are using.
+ This just adds up the lengths of the_history->lines. */
+extern int history_total_bytes (void);
+
+/* Moving around the history list. */
+
+/* Set the position in the history list to POS. */
+extern int history_set_pos (int);
+
+/* Back up history_offset to the previous history entry, and return
+ a pointer to that entry. If there is no previous entry, return
+ a NULL pointer. */
+extern HIST_ENTRY *previous_history (void);
+
+/* Move history_offset forward to the next item in the input_history,
+ and return the a pointer to that entry. If there is no next entry,
+ return a NULL pointer. */
+extern HIST_ENTRY *next_history (void);
+
+/* Searching the history list. */
+
+/* Search the history for STRING, starting at history_offset.
+ If DIRECTION < 0, then the search is through previous entries,
+ else through subsequent. If the string is found, then
+ current_history () is the history entry, and the value of this function
+ is the offset in the line of that history entry that the string was
+ found in. Otherwise, nothing is changed, and a -1 is returned. */
+extern int history_search (const char *, int);
+
+/* Search the history for STRING, starting at history_offset.
+ The search is anchored: matching lines must begin with string.
+ DIRECTION is as in history_search(). */
+extern int history_search_prefix (const char *, int);
+
+/* Search for STRING in the history list, starting at POS, an
+ absolute index into the list. DIR, if negative, says to search
+ backwards from POS, else forwards.
+ Returns the absolute index of the history element where STRING
+ was found, or -1 otherwise. */
+extern int history_search_pos (const char *, int, int);
+
+/* Managing the history file. */
+
+/* Add the contents of FILENAME to the history list, a line at a time.
+ If FILENAME is NULL, then read from ~/.history. Returns 0 if
+ successful, or errno if not. */
+extern int read_history (const char *);
+
+/* Read a range of lines from FILENAME, adding them to the history list.
+ Start reading at the FROM'th line and end at the TO'th. If FROM
+ is zero, start at the beginning. If TO is less than FROM, read
+ until the end of the file. If FILENAME is NULL, then read from
+ ~/.history. Returns 0 if successful, or errno if not. */
+extern int read_history_range (const char *, int, int);
+
+/* Write the current history to FILENAME. If FILENAME is NULL,
+ then write the history list to ~/.history. Values returned
+ are as in read_history (). */
+extern int write_history (const char *);
+
+/* Append NELEMENT entries to FILENAME. The entries appended are from
+ the end of the list minus NELEMENTs up to the end of the list. */
+extern int append_history (int, const char *);
+
+/* Truncate the history file, leaving only the last NLINES lines. */
+extern int history_truncate_file (const char *, int);
+
+/* History expansion. */
+
+/* Expand the string STRING, placing the result into OUTPUT, a pointer
+ to a string. Returns:
+
+ 0) If no expansions took place (or, if the only change in
+ the text was the de-slashifying of the history expansion
+ character)
+ 1) If expansions did take place
+ -1) If there was an error in expansion.
+ 2) If the returned line should just be printed.
+
+ If an error occurred in expansion, then OUTPUT contains a descriptive
+ error message. */
+extern int history_expand (char *, char **);
+
+/* Extract a string segment consisting of the FIRST through LAST
+ arguments present in STRING. Arguments are broken up as in
+ the shell. */
+extern char *history_arg_extract (int, int, const char *);
+
+/* Return the text of the history event beginning at the current
+ offset into STRING. Pass STRING with *INDEX equal to the
+ history_expansion_char that begins this specification.
+ DELIMITING_QUOTE is a character that is allowed to end the string
+ specification for what to search for in addition to the normal
+ characters `:', ` ', `\t', `\n', and sometimes `?'. */
+extern char *get_history_event (const char *, int *, int);
+
+/* Return an array of tokens, much as the shell might. The tokens are
+ parsed out of STRING. */
+extern char **history_tokenize (const char *);
+
+/* Exported history variables. */
+extern int history_base;
+extern int history_length;
+extern int history_max_entries;
+extern int history_offset;
+
+extern int history_lines_read_from_file;
+extern int history_lines_written_to_file;
+
+extern char history_expansion_char;
+extern char history_subst_char;
+extern char *history_word_delimiters;
+extern char history_comment_char;
+extern char *history_no_expand_chars;
+extern char *history_search_delimiter_chars;
+
+extern int history_quotes_inhibit_expansion;
+extern int history_quoting_state;
+
+extern int history_write_timestamps;
+
+/* These two are undocumented; the second is reserved for future use */
+extern int history_multiline_entries;
+extern int history_file_version;
+
+/* Backwards compatibility */
+extern int max_input_history;
+
+/* If set, this function is called to decide whether or not a particular
+ history expansion should be treated as a special case for the calling
+ application and not expanded. */
+extern rl_linebuf_func_t *history_inhibit_expansion_function;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_HISTORY_H_ */
diff --git a/third_party/readline/histsearch.c b/third_party/readline/histsearch.c
new file mode 100644
index 000000000..59f9dd067
--- /dev/null
+++ b/third_party/readline/histsearch.c
@@ -0,0 +1,287 @@
+/* histsearch.c -- searching the history list. */
+
+/* Copyright (C) 1989, 1992-2009,2017,2021 Free Software Foundation, Inc.
+
+ This file contains the GNU History Library (History), a set of
+ routines for managing the text of previously typed lines.
+
+ History is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ History 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 History. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#if defined (HAVE_UNISTD_H)
+# ifdef _MINIX
+# include
+# endif
+# include
+#endif
+
+#if defined (HAVE_FNMATCH)
+# include
+#endif
+
+#include "history.h"
+#include "histlib.h"
+#include "xmalloc.h"
+
+/* The list of alternate characters that can delimit a history search
+ string. */
+char *history_search_delimiter_chars = (char *)NULL;
+
+static int history_search_internal (const char *, int, int);
+
+/* Search the history for STRING, starting at history_offset.
+ If DIRECTION < 0, then the search is through previous entries, else
+ through subsequent. If ANCHORED is non-zero, the string must
+ appear at the beginning of a history line, otherwise, the string
+ may appear anywhere in the line. If the string is found, then
+ current_history () is the history entry, and the value of this
+ function is the offset in the line of that history entry that the
+ string was found in. Otherwise, nothing is changed, and a -1 is
+ returned. */
+
+static int
+history_search_internal (const char *string, int direction, int flags)
+{
+ register int i, reverse;
+ register char *line;
+ register int line_index;
+ int string_len, anchored, patsearch;
+ HIST_ENTRY **the_history; /* local */
+
+ i = history_offset;
+ reverse = (direction < 0);
+ anchored = (flags & ANCHORED_SEARCH);
+#if defined (HAVE_FNMATCH)
+ patsearch = (flags & PATTERN_SEARCH);
+#else
+ patsearch = 0;
+#endif
+
+ /* Take care of trivial cases first. */
+ if (string == 0 || *string == '\0')
+ return (-1);
+
+ if (!history_length || ((i >= history_length) && !reverse))
+ return (-1);
+
+ if (reverse && (i >= history_length))
+ i = history_length - 1;
+
+#define NEXT_LINE() do { if (reverse) i--; else i++; } while (0)
+
+ the_history = history_list ();
+ string_len = strlen (string);
+ while (1)
+ {
+ /* Search each line in the history list for STRING. */
+
+ /* At limit for direction? */
+ if ((reverse && i < 0) || (!reverse && i == history_length))
+ return (-1);
+
+ line = the_history[i]->line;
+ line_index = strlen (line);
+
+ /* If STRING is longer than line, no match. */
+ if (patsearch == 0 && (string_len > line_index))
+ {
+ NEXT_LINE ();
+ continue;
+ }
+
+ /* Handle anchored searches first. */
+ if (anchored == ANCHORED_SEARCH)
+ {
+#if defined (HAVE_FNMATCH)
+ if (patsearch)
+ {
+ if (fnmatch (string, line, 0) == 0)
+ {
+ history_offset = i;
+ return (0);
+ }
+ }
+ else
+#endif
+ if (STREQN (string, line, string_len))
+ {
+ history_offset = i;
+ return (0);
+ }
+
+ NEXT_LINE ();
+ continue;
+ }
+
+ /* Do substring search. */
+ if (reverse)
+ {
+ line_index -= (patsearch == 0) ? string_len : 1;
+
+ while (line_index >= 0)
+ {
+#if defined (HAVE_FNMATCH)
+ if (patsearch)
+ {
+ if (fnmatch (string, line + line_index, 0) == 0)
+ {
+ history_offset = i;
+ return (line_index);
+ }
+ }
+ else
+#endif
+ if (STREQN (string, line + line_index, string_len))
+ {
+ history_offset = i;
+ return (line_index);
+ }
+ line_index--;
+ }
+ }
+ else
+ {
+ register int limit;
+
+ limit = line_index - string_len + 1;
+ line_index = 0;
+
+ while (line_index < limit)
+ {
+#if defined (HAVE_FNMATCH)
+ if (patsearch)
+ {
+ if (fnmatch (string, line + line_index, 0) == 0)
+ {
+ history_offset = i;
+ return (line_index);
+ }
+ }
+ else
+#endif
+ if (STREQN (string, line + line_index, string_len))
+ {
+ history_offset = i;
+ return (line_index);
+ }
+ line_index++;
+ }
+ }
+ NEXT_LINE ();
+ }
+}
+
+int
+_hs_history_patsearch (const char *string, int direction, int flags)
+{
+ char *pat;
+ size_t len, start;
+ int ret, unescaped_backslash;
+
+#if defined (HAVE_FNMATCH)
+ /* Assume that the string passed does not have a leading `^' and any
+ anchored search request is captured in FLAGS */
+ len = strlen (string);
+ ret = len - 1;
+ /* fnmatch is required to reject a pattern that ends with an unescaped
+ backslash */
+ if ((unescaped_backslash = (string[ret] == '\\')))
+ {
+ while (ret > 0 && string[--ret] == '\\')
+ unescaped_backslash = 1 - unescaped_backslash;
+ }
+ if (unescaped_backslash)
+ return -1;
+ pat = (char *)xmalloc (len + 3);
+ /* If the search string is not anchored, we'll be calling fnmatch (assuming
+ we have it). Prefix a `*' to the front of the search string so we search
+ anywhere in the line. */
+ if ((flags & ANCHORED_SEARCH) == 0 && string[0] != '*')
+ {
+ pat[0] = '*';
+ start = 1;
+ len++;
+ }
+ else
+ {
+ start = 0;
+ }
+
+ /* Attempt to reduce the number of searches by tacking a `*' onto the end
+ of a pattern that doesn't have one. Assume a pattern that ends in a
+ backslash contains an even number of trailing backslashes; we check
+ above */
+ strcpy (pat + start, string);
+ if (pat[len - 1] != '*')
+ {
+ pat[len] = '*'; /* XXX */
+ pat[len+1] = '\0';
+ }
+#else
+ pat = string;
+#endif
+
+ ret = history_search_internal (pat, direction, flags|PATTERN_SEARCH);
+
+ if (pat != string)
+ xfree (pat);
+ return ret;
+}
+
+/* Do a non-anchored search for STRING through the history in DIRECTION. */
+int
+history_search (const char *string, int direction)
+{
+ return (history_search_internal (string, direction, NON_ANCHORED_SEARCH));
+}
+
+/* Do an anchored search for string through the history in DIRECTION. */
+int
+history_search_prefix (const char *string, int direction)
+{
+ return (history_search_internal (string, direction, ANCHORED_SEARCH));
+}
+
+/* Search for STRING in the history list. DIR is < 0 for searching
+ backwards. POS is an absolute index into the history list at
+ which point to begin searching. */
+int
+history_search_pos (const char *string, int dir, int pos)
+{
+ int ret, old;
+
+ old = where_history ();
+ history_set_pos (pos);
+ if (history_search (string, dir) == -1)
+ {
+ history_set_pos (old);
+ return (-1);
+ }
+ ret = where_history ();
+ history_set_pos (old);
+ return ret;
+}
diff --git a/third_party/readline/input.c b/third_party/readline/input.c
new file mode 100644
index 000000000..9214eb310
--- /dev/null
+++ b/third_party/readline/input.c
@@ -0,0 +1,1002 @@
+/* input.c -- character input functions for readline. */
+
+/* Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (__TANDEM)
+# define _XOPEN_SOURCE_EXTENDED 1
+# define _TANDEM_SOURCE 1
+# include
+#endif
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+#include
+#if defined (HAVE_SYS_FILE_H)
+# include
+#endif /* HAVE_SYS_FILE_H */
+
+#if defined (HAVE_UNISTD_H)
+# include
+#endif /* HAVE_UNISTD_H */
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#include
+
+#include "posixselect.h"
+#include "posixtime.h"
+
+#if defined (FIONREAD_IN_SYS_IOCTL)
+# include
+#endif
+
+#include
+#include
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+/* System-specific feature definitions and include files. */
+#include "rldefs.h"
+#include "rlmbutil.h"
+
+/* Some standard library routines. */
+#include "readline.h"
+
+#include "rlprivate.h"
+#include "rlshell.h"
+#include "xmalloc.h"
+
+/* What kind of non-blocking I/O do we have? */
+#if !defined (O_NDELAY) && defined (O_NONBLOCK)
+# define O_NDELAY O_NONBLOCK /* Posix style */
+#endif
+
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+extern sigset_t _rl_orig_sigset;
+#endif
+
+/* Non-null means it is a pointer to a function to run while waiting for
+ character input. */
+rl_hook_func_t *rl_event_hook = (rl_hook_func_t *)NULL;
+
+/* A function to call if a read(2) is interrupted by a signal. */
+rl_hook_func_t *rl_signal_event_hook = (rl_hook_func_t *)NULL;
+
+/* A function to call when readline times out after a time is specified. */
+rl_hook_func_t *rl_timeout_event_hook = (rl_hook_func_t *)NULL;
+
+/* A function to replace _rl_input_available for applications using the
+ callback interface. */
+rl_hook_func_t *rl_input_available_hook = (rl_hook_func_t *)NULL;
+
+rl_getc_func_t *rl_getc_function = rl_getc;
+
+static int _keyboard_input_timeout = 100000; /* 0.1 seconds; it's in usec */
+
+static int ibuffer_space (void);
+static int rl_get_char (int *);
+static int rl_gather_tyi (void);
+
+/* Windows isatty returns true for every character device, including the null
+ device, so we need to perform additional checks. */
+#if defined (_WIN32) && !defined (__CYGWIN__)
+#include
+#include
+#define WIN32_LEAN_AND_MEAN 1
+#include
+
+int
+win32_isatty (int fd)
+{
+ if (_isatty(fd))
+ {
+ HANDLE h;
+ DWORD ignored;
+
+ if ((h = (HANDLE) _get_osfhandle (fd)) == INVALID_HANDLE_VALUE)
+ {
+ errno = EBADF;
+ return 0;
+ }
+ if (GetConsoleMode (h, &ignored) != 0)
+ return 1;
+ }
+ errno = ENOTTY;
+ return 0;
+}
+
+#define isatty(x) win32_isatty(x)
+#endif
+
+/* Readline timeouts */
+
+/* I don't know how to set a timeout for _getch() in MinGW32, so we use
+ SIGALRM. */
+#if (defined (HAVE_PSELECT) || defined (HAVE_SELECT)) && !defined (__MINGW32__)
+# define RL_TIMEOUT_USE_SELECT
+#else
+# define RL_TIMEOUT_USE_SIGALRM
+#endif
+
+int rl_set_timeout (unsigned int, unsigned int);
+int rl_timeout_remaining (unsigned int *, unsigned int *);
+
+int _rl_timeout_init (void);
+int _rl_timeout_sigalrm_handler (void);
+int _rl_timeout_select (int, fd_set *, fd_set *, fd_set *, const struct timeval *, const sigset_t *);
+
+static void _rl_timeout_handle (void);
+#if defined (RL_TIMEOUT_USE_SIGALRM)
+static int set_alarm (unsigned int *, unsigned int *);
+static void reset_alarm (void);
+#endif
+
+/* We implement timeouts as a future time using a supplied interval
+ (timeout_duration) from when the timeout is set (timeout_point).
+ That allows us to easily determine whether the timeout has occurred
+ and compute the time remaining until it does. */
+static struct timeval timeout_point;
+static struct timeval timeout_duration;
+
+/* **************************************************************** */
+/* */
+/* Character Input Buffering */
+/* */
+/* **************************************************************** */
+
+static int pop_index, push_index;
+static unsigned char ibuffer[512];
+static int ibuffer_len = sizeof (ibuffer) - 1;
+
+#define any_typein (push_index != pop_index)
+
+int
+_rl_any_typein (void)
+{
+ return any_typein;
+}
+
+int
+_rl_pushed_input_available (void)
+{
+ return (push_index != pop_index);
+}
+
+/* Return the amount of space available in the buffer for stuffing
+ characters. */
+static int
+ibuffer_space (void)
+{
+ if (pop_index > push_index)
+ return (pop_index - push_index - 1);
+ else
+ return (ibuffer_len - (push_index - pop_index));
+}
+
+/* Get a key from the buffer of characters to be read.
+ Return the key in KEY.
+ Result is non-zero if there was a key, or 0 if there wasn't. */
+static int
+rl_get_char (int *key)
+{
+ if (push_index == pop_index)
+ return (0);
+
+ *key = ibuffer[pop_index++];
+#if 0
+ if (pop_index >= ibuffer_len)
+#else
+ if (pop_index > ibuffer_len)
+#endif
+ pop_index = 0;
+
+ return (1);
+}
+
+/* Stuff KEY into the *front* of the input buffer.
+ Returns non-zero if successful, zero if there is
+ no space left in the buffer. */
+int
+_rl_unget_char (int key)
+{
+ if (ibuffer_space ())
+ {
+ pop_index--;
+ if (pop_index < 0)
+ pop_index = ibuffer_len;
+ ibuffer[pop_index] = key;
+ return (1);
+ }
+ return (0);
+}
+
+/* If a character is available to be read, then read it and stuff it into
+ IBUFFER. Otherwise, just return. Returns number of characters read
+ (0 if none available) and -1 on error (EIO). */
+static int
+rl_gather_tyi (void)
+{
+ int tty;
+ register int tem, result;
+ int chars_avail, k;
+ char input;
+#if defined(HAVE_SELECT)
+ fd_set readfds, exceptfds;
+ struct timeval timeout;
+#endif
+
+ chars_avail = 0;
+ input = 0;
+ tty = fileno (rl_instream);
+
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+ FD_ZERO (&readfds);
+ FD_ZERO (&exceptfds);
+ FD_SET (tty, &readfds);
+ FD_SET (tty, &exceptfds);
+ USEC_TO_TIMEVAL (_keyboard_input_timeout, timeout);
+#if defined (RL_TIMEOUT_USE_SELECT)
+ result = _rl_timeout_select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout, NULL);
+#else
+ result = select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout);
+#endif
+ if (result <= 0)
+ return 0; /* Nothing to read. */
+#endif
+
+ result = -1;
+ errno = 0;
+#if defined (FIONREAD)
+ result = ioctl (tty, FIONREAD, &chars_avail);
+ if (result == -1 && errno == EIO)
+ return -1;
+ if (result == -1)
+ chars_avail = 0;
+#endif
+
+#if defined (O_NDELAY)
+ if (result == -1)
+ {
+ tem = fcntl (tty, F_GETFL, 0);
+
+ fcntl (tty, F_SETFL, (tem | O_NDELAY));
+ chars_avail = read (tty, &input, 1);
+
+ fcntl (tty, F_SETFL, tem);
+ if (chars_avail == -1 && errno == EAGAIN)
+ return 0;
+ if (chars_avail == -1 && errno == EIO)
+ return -1;
+ if (chars_avail == 0) /* EOF */
+ {
+ rl_stuff_char (EOF);
+ return (0);
+ }
+ }
+#endif /* O_NDELAY */
+
+#if defined (__MINGW32__)
+ /* Use getch/_kbhit to check for available console input, in the same way
+ that we read it normally. */
+ chars_avail = isatty (tty) ? _kbhit () : 0;
+ result = 0;
+#endif
+
+ /* If there's nothing available, don't waste time trying to read
+ something. */
+ if (chars_avail <= 0)
+ return 0;
+
+ tem = ibuffer_space ();
+
+ if (chars_avail > tem)
+ chars_avail = tem;
+
+ /* One cannot read all of the available input. I can only read a single
+ character at a time, or else programs which require input can be
+ thwarted. If the buffer is larger than one character, I lose.
+ Damn! */
+ if (tem < ibuffer_len)
+ chars_avail = 0;
+
+ if (result != -1)
+ {
+ while (chars_avail--)
+ {
+ RL_CHECK_SIGNALS ();
+ k = (*rl_getc_function) (rl_instream);
+ if (rl_stuff_char (k) == 0)
+ break; /* some problem; no more room */
+ if (k == NEWLINE || k == RETURN)
+ break;
+ }
+ }
+ else
+ {
+ if (chars_avail)
+ rl_stuff_char (input);
+ }
+
+ return 1;
+}
+
+int
+rl_set_keyboard_input_timeout (int u)
+{
+ int o;
+
+ o = _keyboard_input_timeout;
+ if (u >= 0)
+ _keyboard_input_timeout = u;
+ return (o);
+}
+
+/* Is there input available to be read on the readline input file
+ descriptor? Only works if the system has select(2) or FIONREAD.
+ Uses the value of _keyboard_input_timeout as the timeout; if another
+ readline function wants to specify a timeout and not leave it up to
+ the user, it should use _rl_input_queued(timeout_value_in_microseconds)
+ instead. */
+int
+_rl_input_available (void)
+{
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+ fd_set readfds, exceptfds;
+ struct timeval timeout;
+#endif
+#if !defined (HAVE_SELECT) && defined (FIONREAD)
+ int chars_avail;
+#endif
+ int tty;
+
+ if (rl_input_available_hook)
+ return (*rl_input_available_hook) ();
+
+ tty = fileno (rl_instream);
+
+#if 0 // TODO(ahgamut): Why do we need it?
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+ FD_ZERO (&readfds);
+ FD_ZERO (&exceptfds);
+ FD_SET (tty, &readfds);
+ FD_SET (tty, &exceptfds);
+ USEC_TO_TIMEVAL (_keyboard_input_timeout, timeout);
+# if defined (RL_TIMEOUT_USE_SELECT)
+ return (_rl_timeout_select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout, NULL) > 0);
+# else
+ return (select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout) > 0);
+# endif
+#else
+#endif
+
+#if defined (FIONREAD)
+ if (ioctl (tty, FIONREAD, &chars_avail) == 0)
+ return (chars_avail);
+#endif
+
+#endif
+
+#if defined (__MINGW32__)
+ if (isatty (tty))
+ return (_kbhit ());
+#endif
+
+ return 0;
+}
+
+int
+_rl_nchars_available ()
+{
+ int chars_avail, fd, result;
+
+ chars_avail = 0;
+
+#if defined (FIONREAD)
+ fd = fileno (rl_instream);
+ errno = 0;
+ result = ioctl (fd, FIONREAD, &chars_avail);
+ if (result == -1 && errno == EIO)
+ return -1;
+#endif
+
+ return chars_avail;
+}
+
+int
+_rl_input_queued (int t)
+{
+ int old_timeout, r;
+
+ old_timeout = rl_set_keyboard_input_timeout (t);
+ r = _rl_input_available ();
+ rl_set_keyboard_input_timeout (old_timeout);
+ return r;
+}
+
+void
+_rl_insert_typein (int c)
+{
+ int key, t, i;
+ char *string;
+
+ i = key = 0;
+ string = (char *)xmalloc (ibuffer_len + 1);
+ string[i++] = (char) c;
+
+ while ((t = rl_get_char (&key)) &&
+ _rl_keymap[key].type == ISFUNC &&
+ _rl_keymap[key].function == rl_insert)
+ string[i++] = key;
+
+ if (t)
+ _rl_unget_char (key);
+
+ string[i] = '\0';
+ rl_insert_text (string);
+ xfree (string);
+}
+
+/* Add KEY to the buffer of characters to be read. Returns 1 if the
+ character was stuffed correctly; 0 otherwise. */
+int
+rl_stuff_char (int key)
+{
+ if (ibuffer_space () == 0)
+ return 0;
+
+ if (key == EOF)
+ {
+ key = NEWLINE;
+ rl_pending_input = EOF;
+ RL_SETSTATE (RL_STATE_INPUTPENDING);
+ }
+ ibuffer[push_index++] = key;
+#if 0
+ if (push_index >= ibuffer_len)
+#else
+ if (push_index > ibuffer_len)
+#endif
+ push_index = 0;
+
+ return 1;
+}
+
+/* Make C be the next command to be executed. */
+int
+rl_execute_next (int c)
+{
+ rl_pending_input = c;
+ RL_SETSTATE (RL_STATE_INPUTPENDING);
+ return 0;
+}
+
+/* Clear any pending input pushed with rl_execute_next() */
+int
+rl_clear_pending_input (void)
+{
+ rl_pending_input = 0;
+ RL_UNSETSTATE (RL_STATE_INPUTPENDING);
+ return 0;
+}
+
+/* **************************************************************** */
+/* */
+/* Timeout utility */
+/* */
+/* **************************************************************** */
+
+#if defined (RL_TIMEOUT_USE_SIGALRM)
+# if defined (HAVE_SETITIMER)
+
+static int
+set_alarm (unsigned int *secs, unsigned int *usecs)
+{
+ struct itimerval it;
+
+ timerclear (&it.it_interval);
+ timerset (&it.it_value, *secs, *usecs);
+ return setitimer (ITIMER_REAL, &it, NULL);
+}
+
+static void
+reset_alarm ()
+{
+ struct itimerval it;
+
+ timerclear (&it.it_interval);
+ timerclear (&it.it_value);
+ setitimer (ITIMER_REAL, &it, NULL);
+}
+# else
+static int
+set_alarm (unsigned int *secs, unsigned int *usecs)
+{
+ if (*secs == 0 || *usecs >= USEC_PER_SEC / 2)
+ (*secs)++;
+ *usecs = 0;
+
+ return alarm (*secs);
+}
+static void
+reset_alarm ()
+{
+ alarm (0);
+}
+# endif
+#endif
+
+/* Set a timeout which will be used for the next call of `readline
+ ()'. When (0, 0) are specified the timeout is cleared. */
+int
+rl_set_timeout (unsigned int secs, unsigned int usecs)
+{
+ timeout_duration.tv_sec = secs + usecs / USEC_PER_SEC;
+ timeout_duration.tv_usec = usecs % USEC_PER_SEC;
+
+ return 0;
+}
+
+/* Start measuring the time. Returns 0 on success. Returns -1 on
+ error. */
+int
+_rl_timeout_init (void)
+{
+ unsigned int secs, usecs;
+
+ /* Clear the timeout state of the previous edit */
+ RL_UNSETSTATE(RL_STATE_TIMEOUT);
+ timerclear (&timeout_point);
+
+ /* Return 0 when timeout is unset. */
+ if (timerisunset (&timeout_duration))
+ return 0;
+
+ /* Return -1 on gettimeofday error. */
+ if (gettimeofday(&timeout_point, 0) != 0)
+ {
+ timerclear (&timeout_point);
+ return -1;
+ }
+
+ secs = timeout_duration.tv_sec;
+ usecs = timeout_duration.tv_usec;
+
+#if defined (RL_TIMEOUT_USE_SIGALRM)
+ /* If select(2)/pselect(2) is unavailable, use SIGALRM. */
+ if (set_alarm (&secs, &usecs) < 0)
+ return -1;
+#endif
+
+ timeout_point.tv_sec += secs;
+ timeout_point.tv_usec += usecs;
+ if (timeout_point.tv_usec >= USEC_PER_SEC)
+ {
+ timeout_point.tv_sec++;
+ timeout_point.tv_usec -= USEC_PER_SEC;
+ }
+
+ return 0;
+}
+
+/* Get the remaining time until the scheduled timeout. Returns -1 on
+ error or no timeout set with secs and usecs unchanged. Returns 0
+ on an expired timeout with secs and usecs unchanged. Returns 1
+ when the timeout has not yet expired. The remaining time is stored
+ in secs and usecs. When NULL is specified to either of the
+ arguments, just the expiration is tested. */
+int
+rl_timeout_remaining (unsigned int *secs, unsigned int *usecs)
+{
+ struct timeval current_time;
+
+ /* Return -1 when timeout is unset. */
+ if (timerisunset (&timeout_point))
+ {
+ errno = 0;
+ return -1;
+ }
+
+ /* Return -1 on error. errno is set by gettimeofday. */
+ if (gettimeofday(¤t_time, 0) != 0)
+ return -1;
+
+ /* Return 0 when timeout has already expired. */
+ /* could use timercmp (&timeout_point, ¤t_time, <) here */
+ if (current_time.tv_sec > timeout_point.tv_sec ||
+ (current_time.tv_sec == timeout_point.tv_sec &&
+ current_time.tv_usec >= timeout_point.tv_usec))
+ return 0;
+
+ if (secs && usecs)
+ {
+ *secs = timeout_point.tv_sec - current_time.tv_sec;
+ *usecs = timeout_point.tv_usec - current_time.tv_usec;
+ if (timeout_point.tv_usec < current_time.tv_usec)
+ {
+ (*secs)--;
+ *usecs += USEC_PER_SEC;
+ }
+ }
+
+ return 1;
+}
+
+/* This should only be called if RL_TIMEOUT_USE_SELECT is defined. */
+
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+int
+_rl_timeout_select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout, const sigset_t *sigmask)
+{
+ int result;
+#if defined (HAVE_PSELECT)
+ struct timespec ts;
+#else
+ sigset_t origmask;
+ struct timeval tv;
+#endif
+ int tmout_status;
+ struct timeval tmout;
+ unsigned int sec, usec;
+
+ /* When the remaining time for rl_timeout is shorter than the
+ keyboard input timeout, replace `timeout' with the remaining time
+ for `rl_timeout' and set `tmout_status = 1'. */
+ tmout_status = rl_timeout_remaining (&sec, &usec);
+ tmout.tv_sec = sec;
+ tmout.tv_usec = usec;
+
+ if (tmout_status == 0)
+ _rl_timeout_handle ();
+ else if (tmout_status == 1)
+ {
+ if (timeout == NULL || timercmp (&tmout, timeout, <))
+ timeout = &tmout;
+ else
+ tmout_status = -1;
+ }
+
+#if defined (HAVE_PSELECT)
+ if (timeout)
+ {
+ TIMEVAL_TO_TIMESPEC (timeout, &ts);
+ result = pselect (nfds, readfds, writefds, exceptfds, &ts, sigmask);
+ }
+ else
+ result = pselect (nfds, readfds, writefds, exceptfds, NULL, sigmask);
+#else
+ if (sigmask)
+ sigprocmask (SIG_SETMASK, sigmask, &origmask);
+
+ if (timeout)
+ {
+ tv.tv_sec = timeout->tv_sec;
+ tv.tv_usec = timeout->tv_usec;
+ result = select (nfds, readfds, writefds, exceptfds, &tv);
+ }
+ else
+ result = select (nfds, readfds, writefds, exceptfds, NULL);
+
+ if (sigmask)
+ sigprocmask (SIG_SETMASK, &origmask, NULL);
+#endif
+
+ if (tmout_status == 1 && result == 0)
+ _rl_timeout_handle ();
+
+ return result;
+}
+#endif
+
+static void
+_rl_timeout_handle ()
+{
+ if (rl_timeout_event_hook)
+ (*rl_timeout_event_hook) ();
+
+ RL_SETSTATE(RL_STATE_TIMEOUT);
+ _rl_abort_internal ();
+}
+
+int
+_rl_timeout_handle_sigalrm ()
+{
+#if defined (RL_TIMEOUT_USE_SIGALRM)
+ if (timerisunset (&timeout_point))
+ return -1;
+
+ /* Reset `timeout_point' to the current time to ensure that later
+ calls of `rl_timeout_pending ()' return 0 (timeout expired). */
+ if (gettimeofday(&timeout_point, 0) != 0)
+ timerclear (&timeout_point);
+
+ reset_alarm ();
+
+ _rl_timeout_handle ();
+#endif
+ return -1;
+}
+/* **************************************************************** */
+/* */
+/* Character Input */
+/* */
+/* **************************************************************** */
+
+/* Read a key, including pending input. */
+int
+rl_read_key (void)
+{
+ int c, r;
+
+ if (rl_pending_input)
+ {
+ c = rl_pending_input; /* XXX - cast to unsigned char if > 0? */
+ rl_clear_pending_input ();
+ }
+ else
+ {
+ /* If input is coming from a macro, then use that. */
+ if ((c = _rl_next_macro_key ()))
+ return ((unsigned char)c);
+
+ /* If the user has an event function, then call it periodically. */
+ if (rl_event_hook)
+ {
+ while (rl_event_hook)
+ {
+ if (rl_get_char (&c) != 0)
+ break;
+
+ if ((r = rl_gather_tyi ()) < 0) /* XXX - EIO */
+ {
+ rl_done = 1;
+ RL_SETSTATE (RL_STATE_DONE);
+ return (errno == EIO ? (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF) : '\n');
+ }
+ else if (r > 0) /* read something */
+ continue;
+
+ RL_CHECK_SIGNALS ();
+ if (rl_done) /* XXX - experimental */
+ return ('\n');
+ (*rl_event_hook) ();
+ }
+ }
+ else
+ {
+ if (rl_get_char (&c) == 0)
+ c = (*rl_getc_function) (rl_instream);
+/* fprintf(stderr, "rl_read_key: calling RL_CHECK_SIGNALS: _rl_caught_signal = %d\r\n", _rl_caught_signal); */
+ RL_CHECK_SIGNALS ();
+ }
+ }
+
+ return (c);
+}
+
+int
+rl_getc (FILE *stream)
+{
+ int result;
+ unsigned char c;
+ int fd;
+#if defined (HAVE_PSELECT)
+ sigset_t empty_set;
+ fd_set readfds;
+#endif
+
+ fd = fileno (stream);
+ while (1)
+ {
+ RL_CHECK_SIGNALS ();
+
+ /* We know at this point that _rl_caught_signal == 0 */
+
+#if defined (__MINGW32__)
+ if (isatty (fd)
+ return (_getch ()); /* "There is no error return." */
+#endif
+ result = 0;
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+ /* At this point, if we have pselect, we're using select/pselect for the
+ timeouts. We handled MinGW above. */
+ FD_ZERO (&readfds);
+ FD_SET (fd, &readfds);
+# if defined (HANDLE_SIGNALS)
+ result = _rl_timeout_select (fd + 1, &readfds, NULL, NULL, NULL, &_rl_orig_sigset);
+# else
+ sigemptyset (&empty_set);
+ sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &empty_set);
+ result = _rl_timeout_select (fd + 1, &readfds, NULL, NULL, NULL, &empty_set);
+# endif /* HANDLE_SIGNALS */
+ if (result == 0)
+ _rl_timeout_handle (); /* check the timeout */
+#endif
+ if (result >= 0)
+ result = read (fd, &c, sizeof (unsigned char));
+
+ if (result == sizeof (unsigned char))
+ return (c);
+
+ /* If zero characters are returned, then the file that we are
+ reading from is empty! Return EOF in that case. */
+ if (result == 0)
+ return (EOF);
+
+#if defined (__BEOS__)
+ if (errno == EINTR)
+ continue;
+#endif
+
+#if defined (EWOULDBLOCK)
+# define X_EWOULDBLOCK EWOULDBLOCK
+#else
+# define X_EWOULDBLOCK -99
+#endif
+
+#if defined (EAGAIN)
+# define X_EAGAIN EAGAIN
+#else
+# define X_EAGAIN -99
+#endif
+
+ if (errno == X_EWOULDBLOCK || errno == X_EAGAIN)
+ {
+ if (sh_unset_nodelay_mode (fd) < 0)
+ return (EOF);
+ continue;
+ }
+
+#undef X_EWOULDBLOCK
+#undef X_EAGAIN
+
+/* fprintf(stderr, "rl_getc: result = %d errno = %d\n", result, errno); */
+
+handle_error:
+ /* If the error that we received was EINTR, then try again,
+ this is simply an interrupted system call to read (). We allow
+ the read to be interrupted if we caught SIGHUP, SIGTERM, or any
+ of the other signals readline treats specially. If the
+ application sets an event hook, call it for other signals.
+ Otherwise (not EINTR), some error occurred, also signifying EOF. */
+ if (errno != EINTR)
+ return (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF);
+ /* fatal signals of interest */
+#if defined (SIGHUP)
+ else if (_rl_caught_signal == SIGHUP || _rl_caught_signal == SIGTERM)
+#else
+ else if (_rl_caught_signal == SIGTERM)
+#endif
+ return (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF);
+ /* keyboard-generated signals of interest */
+#if defined (SIGQUIT)
+ else if (_rl_caught_signal == SIGINT || _rl_caught_signal == SIGQUIT)
+#else
+ else if (_rl_caught_signal == SIGINT)
+#endif
+ RL_CHECK_SIGNALS ();
+#if defined (SIGTSTP)
+ else if (_rl_caught_signal == SIGTSTP)
+ RL_CHECK_SIGNALS ();
+#endif
+ /* non-keyboard-generated signals of interest */
+#if defined (SIGWINCH)
+ else if (_rl_caught_signal == SIGWINCH)
+ RL_CHECK_SIGNALS ();
+#endif /* SIGWINCH */
+#if defined (SIGALRM)
+ else if (_rl_caught_signal == SIGALRM
+# if defined (SIGVTALRM)
+ || _rl_caught_signal == SIGVTALRM
+# endif
+ )
+ RL_CHECK_SIGNALS ();
+#endif /* SIGALRM */
+
+ if (rl_signal_event_hook)
+ (*rl_signal_event_hook) ();
+ }
+}
+
+#if defined (HANDLE_MULTIBYTE)
+/* read multibyte char */
+int
+_rl_read_mbchar (char *mbchar, int size)
+{
+ int mb_len, c;
+ size_t mbchar_bytes_length;
+ WCHAR_T wc;
+ mbstate_t ps, ps_back;
+
+ memset(&ps, 0, sizeof (mbstate_t));
+ memset(&ps_back, 0, sizeof (mbstate_t));
+
+ mb_len = 0;
+ while (mb_len < size)
+ {
+ c = (mb_len == 0) ? _rl_bracketed_read_key () : rl_read_key ();
+
+ if (c < 0)
+ break;
+
+ mbchar[mb_len++] = c;
+
+ mbchar_bytes_length = MBRTOWC (&wc, mbchar, mb_len, &ps);
+ if (mbchar_bytes_length == (size_t)(-1))
+ break; /* invalid byte sequence for the current locale */
+ else if (mbchar_bytes_length == (size_t)(-2))
+ {
+ /* shorted bytes */
+ ps = ps_back;
+ continue;
+ }
+ else if (mbchar_bytes_length == 0)
+ {
+ mbchar[0] = '\0'; /* null wide character */
+ mb_len = 1;
+ break;
+ }
+ else if (mbchar_bytes_length > (size_t)(0))
+ break;
+ }
+
+ return mb_len;
+}
+
+/* Read a multibyte-character string whose first character is FIRST into
+ the buffer MB of length MLEN. Returns the last character read, which
+ may be FIRST. Used by the search functions, among others. Very similar
+ to _rl_read_mbchar. */
+int
+_rl_read_mbstring (int first, char *mb, int mlen)
+{
+ int i, c, n;
+ mbstate_t ps;
+
+ c = first;
+ memset (mb, 0, mlen);
+ for (i = 0; c >= 0 && i < mlen; i++)
+ {
+ mb[i] = (char)c;
+ memset (&ps, 0, sizeof (mbstate_t));
+ n = _rl_get_char_len (mb, &ps);
+ if (n == -2)
+ {
+ /* Read more for multibyte character */
+ RL_SETSTATE (RL_STATE_MOREINPUT);
+ c = rl_read_key ();
+ RL_UNSETSTATE (RL_STATE_MOREINPUT);
+ }
+ else
+ break;
+ }
+ return c;
+}
+#endif /* HANDLE_MULTIBYTE */
diff --git a/third_party/readline/isearch.c b/third_party/readline/isearch.c
new file mode 100644
index 000000000..27da91005
--- /dev/null
+++ b/third_party/readline/isearch.c
@@ -0,0 +1,893 @@
+/* isearch.c - incremental searching */
+
+/* **************************************************************** */
+/* */
+/* I-Search and Searching */
+/* */
+/* **************************************************************** */
+
+/* Copyright (C) 1987-2021 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+
+#include
+
+#if defined (HAVE_UNISTD_H)
+# include
+#endif
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif
+
+#include "rldefs.h"
+#include "rlmbutil.h"
+
+#include "readline.h"
+#include "history.h"
+
+#include "rlprivate.h"
+#include "xmalloc.h"
+
+/* Variables exported to other files in the readline library. */
+char *_rl_isearch_terminators = (char *)NULL;
+
+_rl_search_cxt *_rl_iscxt = 0;
+
+static int rl_search_history (int, int);
+
+static _rl_search_cxt *_rl_isearch_init (int);
+static void _rl_isearch_fini (_rl_search_cxt *);
+
+/* Last line found by the current incremental search, so we don't `find'
+ identical lines many times in a row. Now part of isearch context. */
+/* static char *prev_line_found; */
+
+/* Last search string and its length. */
+static char *last_isearch_string;
+static int last_isearch_string_len;
+
+static char * const default_isearch_terminators = "\033\012";
+
+_rl_search_cxt *
+_rl_scxt_alloc (int type, int flags)
+{
+ _rl_search_cxt *cxt;
+
+ cxt = (_rl_search_cxt *)xmalloc (sizeof (_rl_search_cxt));
+
+ cxt->type = type;
+ cxt->sflags = flags;
+
+ cxt->search_string = 0;
+ cxt->search_string_size = cxt->search_string_index = 0;
+
+ cxt->lines = 0;
+ cxt->allocated_line = 0;
+ cxt->hlen = cxt->hindex = 0;
+
+ cxt->save_point = rl_point;
+ cxt->save_mark = rl_mark;
+ cxt->save_line = where_history ();
+ cxt->last_found_line = cxt->save_line;
+ cxt->prev_line_found = 0;
+
+ cxt->save_undo_list = 0;
+
+ cxt->keymap = _rl_keymap;
+ cxt->okeymap = _rl_keymap;
+
+ cxt->history_pos = 0;
+ cxt->direction = 0;
+
+ cxt->prevc = cxt->lastc = 0;
+
+ cxt->sline = 0;
+ cxt->sline_len = cxt->sline_index = 0;
+
+ cxt->search_terminators = 0;
+
+ return cxt;
+}
+
+void
+_rl_scxt_dispose (_rl_search_cxt *cxt, int flags)
+{
+ FREE (cxt->search_string);
+ FREE (cxt->allocated_line);
+ FREE (cxt->lines);
+
+ xfree (cxt);
+}
+
+/* Search backwards through the history looking for a string which is typed
+ interactively. Start with the current line. */
+int
+rl_reverse_search_history (int sign, int key)
+{
+ return (rl_search_history (-sign, key));
+}
+
+/* Search forwards through the history looking for a string which is typed
+ interactively. Start with the current line. */
+int
+rl_forward_search_history (int sign, int key)
+{
+ return (rl_search_history (sign, key));
+}
+
+/* Display the current state of the search in the echo-area.
+ SEARCH_STRING contains the string that is being searched for,
+ DIRECTION is zero for forward, or non-zero for reverse,
+ WHERE is the history list number of the current line. If it is
+ -1, then this line is the starting one. */
+static void
+rl_display_search (char *search_string, int flags, int where)
+{
+ char *message;
+ int msglen, searchlen;
+
+ searchlen = (search_string && *search_string) ? strlen (search_string) : 0;
+
+ message = (char *)xmalloc (searchlen + 64);
+ msglen = 0;
+
+#if defined (NOTDEF)
+ if (where != -1)
+ {
+ sprintf (message, "[%d]", where + history_base);
+ msglen = strlen (message);
+ }
+#endif /* NOTDEF */
+
+ message[msglen++] = '(';
+
+ if (flags & SF_FAILED)
+ {
+ strcpy (message + msglen, "failed ");
+ msglen += 7;
+ }
+
+ if (flags & SF_REVERSE)
+ {
+ strcpy (message + msglen, "reverse-");
+ msglen += 8;
+ }
+
+ strcpy (message + msglen, "i-search)`");
+ msglen += 10;
+
+ if (search_string && *search_string)
+ {
+ strcpy (message + msglen, search_string);
+ msglen += searchlen;
+ }
+ else
+ _rl_optimize_redisplay ();
+
+ strcpy (message + msglen, "': ");
+
+ rl_message ("%s", message);
+ xfree (message);
+#if 0
+ /* rl_message calls this */
+ (*rl_redisplay_function) ();
+#endif
+}
+
+static _rl_search_cxt *
+_rl_isearch_init (int direction)
+{
+ _rl_search_cxt *cxt;
+ register int i;
+ HIST_ENTRY **hlist;
+
+ cxt = _rl_scxt_alloc (RL_SEARCH_ISEARCH, 0);
+ if (direction < 0)
+ cxt->sflags |= SF_REVERSE;
+
+ cxt->search_terminators = _rl_isearch_terminators ? _rl_isearch_terminators
+ : default_isearch_terminators;
+
+ /* Create an array of pointers to the lines that we want to search. */
+ hlist = history_list ();
+ rl_maybe_replace_line ();
+ i = 0;
+ if (hlist)
+ for (i = 0; hlist[i]; i++);
+
+ /* Allocate space for this many lines, +1 for the current input line,
+ and remember those lines. */
+ cxt->lines = (char **)xmalloc ((1 + (cxt->hlen = i)) * sizeof (char *));
+ for (i = 0; i < cxt->hlen; i++)
+ cxt->lines[i] = hlist[i]->line;
+
+ if (_rl_saved_line_for_history)
+ cxt->lines[i] = _rl_saved_line_for_history->line;
+ else
+ {
+ /* Keep track of this so we can free it. */
+ cxt->allocated_line = (char *)xmalloc (1 + strlen (rl_line_buffer));
+ strcpy (cxt->allocated_line, &rl_line_buffer[0]);
+ cxt->lines[i] = cxt->allocated_line;
+ }
+
+ cxt->hlen++;
+
+ /* The line where we start the search. */
+ cxt->history_pos = cxt->save_line;
+
+ rl_save_prompt ();
+
+ /* Initialize search parameters. */
+ cxt->search_string = (char *)xmalloc (cxt->search_string_size = 128);
+ cxt->search_string[cxt->search_string_index = 0] = '\0';
+
+ /* Normalize DIRECTION into 1 or -1. */
+ cxt->direction = (direction >= 0) ? 1 : -1;
+
+ cxt->sline = rl_line_buffer;
+ cxt->sline_len = strlen (cxt->sline);
+ cxt->sline_index = rl_point;
+
+ _rl_iscxt = cxt; /* save globally */
+
+ /* experimental right now */
+ _rl_init_executing_keyseq ();
+
+ return cxt;
+}
+
+static void
+_rl_isearch_fini (_rl_search_cxt *cxt)
+{
+ /* First put back the original state. */
+ rl_replace_line (cxt->lines[cxt->save_line], 0);
+
+ rl_restore_prompt ();
+
+ /* Save the search string for possible later use. */
+ FREE (last_isearch_string);
+ last_isearch_string = cxt->search_string;
+ last_isearch_string_len = cxt->search_string_index;
+ cxt->search_string = 0;
+ cxt->search_string_size = 0;
+ cxt->search_string_index = 0;
+
+ if (cxt->last_found_line < cxt->save_line)
+ rl_get_previous_history (cxt->save_line - cxt->last_found_line, 0);
+ else
+ rl_get_next_history (cxt->last_found_line - cxt->save_line, 0);
+
+ /* If the string was not found, put point at the end of the last matching
+ line. If last_found_line == orig_line, we didn't find any matching
+ history lines at all, so put point back in its original position. */
+ if (cxt->sline_index < 0)
+ {
+ if (cxt->last_found_line == cxt->save_line)
+ cxt->sline_index = cxt->save_point;
+ else
+ cxt->sline_index = strlen (rl_line_buffer);
+ rl_mark = cxt->save_mark;
+ rl_deactivate_mark ();
+ }
+
+ rl_point = cxt->sline_index;
+ /* Don't worry about where to put the mark here; rl_get_previous_history
+ and rl_get_next_history take care of it.
+ If we want to highlight the search string, this is where to set the
+ point and mark to do it. */
+ _rl_fix_point (0);
+ rl_deactivate_mark ();
+
+/* _rl_optimize_redisplay (); */
+ rl_clear_message ();
+}
+
+/* XXX - we could use _rl_bracketed_read_mbstring () here. */
+int
+_rl_search_getchar (_rl_search_cxt *cxt)
+{
+ int c;
+
+ /* Read a key and decide how to proceed. */
+ RL_SETSTATE(RL_STATE_MOREINPUT);
+ c = cxt->lastc = rl_read_key ();
+ RL_UNSETSTATE(RL_STATE_MOREINPUT);
+
+#if defined (HANDLE_MULTIBYTE)
+ /* This ends up with C (and LASTC) being set to the last byte of the
+ multibyte character. In most cases c == lastc == mb[0] */
+ if (c >= 0 && MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ c = cxt->lastc = _rl_read_mbstring (cxt->lastc, cxt->mb, MB_LEN_MAX);
+#endif
+
+ RL_CHECK_SIGNALS ();
+ return c;
+}
+
+#define ENDSRCH_CHAR(c) \
+ ((CTRL_CHAR (c) || META_CHAR (c) || (c) == RUBOUT) && ((c) != CTRL ('G')))
+
+/* Process just-read character C according to isearch context CXT. Return
+ -1 if the caller should just free the context and return, 0 if we should
+ break out of the loop, and 1 if we should continue to read characters. */
+int
+_rl_isearch_dispatch (_rl_search_cxt *cxt, int c)
+{
+ int n, wstart, wlen, limit, cval, incr;
+ char *paste;
+ size_t pastelen;
+ int j;
+ rl_command_func_t *f;
+
+ f = (rl_command_func_t *)NULL;
+
+ if (c < 0)
+ {
+ cxt->sflags |= SF_FAILED;
+ cxt->history_pos = cxt->last_found_line;
+ return -1;
+ }
+
+ _rl_add_executing_keyseq (c);
+
+ /* XXX - experimental code to allow users to bracketed-paste into the search
+ string even when ESC is one of the isearch-terminators. Not perfect yet. */
+ if (_rl_enable_bracketed_paste && c == ESC && strchr (cxt->search_terminators, c) && (n = _rl_nchars_available ()) > (BRACK_PASTE_SLEN-1))
+ {
+ j = _rl_read_bracketed_paste_prefix (c);
+ if (j == 1)
+ {
+ cxt->lastc = -7; /* bracketed paste, see below */
+ goto opcode_dispatch;
+ }
+ else if (_rl_pushed_input_available ()) /* eat extra char we pushed back */
+ c = cxt->lastc = rl_read_key ();
+ else
+ c = cxt->lastc; /* last ditch */
+ }
+
+ /* If we are moving into a new keymap, modify cxt->keymap and go on.
+ This can be a problem if c == ESC and we want to terminate the
+ incremental search, so we check */
+ if (c >= 0 && cxt->keymap[c].type == ISKMAP && strchr (cxt->search_terminators, cxt->lastc) == 0)
+ {
+ /* _rl_keyseq_timeout specified in milliseconds; _rl_input_queued
+ takes microseconds, so multiply by 1000. If we don't get any
+ additional input and this keymap shadows another function, process
+ that key as if it was all we read. */
+ if (_rl_keyseq_timeout > 0 &&
+ RL_ISSTATE (RL_STATE_CALLBACK) == 0 &&
+ RL_ISSTATE (RL_STATE_INPUTPENDING) == 0 &&
+ _rl_pushed_input_available () == 0 &&
+ ((Keymap)(cxt->keymap[c].function))[ANYOTHERKEY].function &&
+ _rl_input_queued (_rl_keyseq_timeout*1000) == 0)
+ goto add_character;
+
+ cxt->okeymap = cxt->keymap;
+ cxt->keymap = FUNCTION_TO_KEYMAP (cxt->keymap, c);
+ cxt->sflags |= SF_CHGKMAP;
+ /* XXX - we should probably save this sequence, so we can do
+ something useful if this doesn't end up mapping to a command we
+ interpret here. Right now we just save the most recent character
+ that caused the index into a new keymap. */
+ cxt->prevc = c;
+#if defined (HANDLE_MULTIBYTE)
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ {
+ if (cxt->mb[1] == 0)
+ {
+ cxt->pmb[0] = c; /* XXX should be == cxt->mb[0] */
+ cxt->pmb[1] = '\0';
+ }
+ else
+ memcpy (cxt->pmb, cxt->mb, sizeof (cxt->pmb));
+ }
+#endif
+ return 1;
+ }
+
+add_character:
+
+ /* Translate the keys we do something with to opcodes. */
+ if (c >= 0 && cxt->keymap[c].type == ISFUNC)
+ {
+ /* If we have a multibyte character, see if it's bound to something that
+ affects the search. */
+#if defined (HANDLE_MULTIBYTE)
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0 && cxt->mb[1])
+ f = rl_function_of_keyseq (cxt->mb, cxt->keymap, (int *)NULL);
+ else
+#endif
+ {
+ f = cxt->keymap[c].function;
+ if (f == rl_do_lowercase_version)
+ f = cxt->keymap[_rl_to_lower (c)].function;
+ }
+
+ if (f == rl_reverse_search_history)
+ cxt->lastc = (cxt->sflags & SF_REVERSE) ? -1 : -2;
+ else if (f == rl_forward_search_history)
+ cxt->lastc = (cxt->sflags & SF_REVERSE) ? -2 : -1;
+ else if (f == rl_rubout)
+ cxt->lastc = -3;
+ else if (c == CTRL ('G') || f == rl_abort)
+ cxt->lastc = -4;
+ else if (c == CTRL ('W') || f == rl_unix_word_rubout) /* XXX */
+ cxt->lastc = -5;
+ else if (c == CTRL ('Y') || f == rl_yank) /* XXX */
+ cxt->lastc = -6;
+ else if (f == rl_bracketed_paste_begin)
+ cxt->lastc = -7;
+ }
+
+ /* If we changed the keymap earlier while translating a key sequence into
+ a command, restore it now that we've succeeded. */
+ if (cxt->sflags & SF_CHGKMAP)
+ {
+ cxt->keymap = cxt->okeymap;
+ cxt->sflags &= ~SF_CHGKMAP;
+ /* If we indexed into a new keymap, but didn't map to a command that
+ affects the search (lastc > 0), and the character that mapped to a
+ new keymap would have ended the search (ENDSRCH_CHAR(cxt->prevc)),
+ handle that now as if the previous char would have ended the search
+ and we would have read the current character. */
+ /* XXX - should we check cxt->mb? */
+ if (cxt->lastc > 0 && ENDSRCH_CHAR (cxt->prevc))
+ {
+ rl_stuff_char (cxt->lastc);
+ rl_execute_next (cxt->prevc);
+ /* XXX - do we insert everything in cxt->pmb? */
+ return (0);
+ }
+ /* Otherwise, if the current character is mapped to self-insert or
+ nothing (i.e., not an editing command), and the previous character
+ was a keymap index, then we need to insert both the previous
+ character and the current character into the search string. */
+ else if (cxt->lastc > 0 && cxt->prevc > 0 &&
+ cxt->keymap[cxt->prevc].type == ISKMAP &&
+ (f == 0 || f == rl_insert))
+ {
+ /* Make lastc be the next character read */
+ /* XXX - do we insert everything in cxt->mb? */
+ rl_execute_next (cxt->lastc);
+ /* Dispatch on the previous character (insert into search string) */
+ cxt->lastc = cxt->prevc;
+#if defined (HANDLE_MULTIBYTE)
+ /* Have to overwrite cxt->mb here because dispatch uses it below */
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ {
+ if (cxt->pmb[1] == 0)
+ {
+ cxt->mb[0] = cxt->lastc; /* == cxt->prevc */
+ cxt->mb[1] = '\0';
+ }
+ else
+ memcpy (cxt->mb, cxt->pmb, sizeof (cxt->mb));
+ }
+#endif
+ cxt->prevc = 0;
+ }
+ else if (cxt->lastc > 0 && cxt->prevc > 0 && f && f != rl_insert)
+ {
+ _rl_term_executing_keyseq (); /* should this go in the caller? */
+
+ _rl_pending_command.map = cxt->keymap;
+ _rl_pending_command.count = 1; /* XXX */
+ _rl_pending_command.key = cxt->lastc;
+ _rl_pending_command.func = f;
+ _rl_command_to_execute = &_rl_pending_command;
+
+ return (0);
+ }
+ }
+
+ /* The characters in isearch_terminators (set from the user-settable
+ variable isearch-terminators) are used to terminate the search but
+ not subsequently execute the character as a command. The default
+ value is "\033\012" (ESC and C-J). */
+ if (cxt->lastc > 0 && strchr (cxt->search_terminators, cxt->lastc))
+ {
+ /* ESC still terminates the search, but if there is pending
+ input or if input arrives within 0.1 seconds (on systems
+ with select(2)) it is used as a prefix character
+ with rl_execute_next. WATCH OUT FOR THIS! This is intended
+ to allow the arrow keys to be used like ^F and ^B are used
+ to terminate the search and execute the movement command.
+ XXX - since _rl_input_available depends on the application-
+ settable keyboard timeout value, this could alternatively
+ use _rl_input_queued(100000) */
+ if (cxt->lastc == ESC && (_rl_pushed_input_available () || _rl_input_available ()))
+ rl_execute_next (ESC);
+ return (0);
+ }
+
+#if defined (HANDLE_MULTIBYTE)
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ {
+ if (cxt->lastc >= 0 && (cxt->mb[0] && cxt->mb[1] == '\0') && ENDSRCH_CHAR (cxt->lastc))
+ {
+ /* This sets rl_pending_input to LASTC; it will be picked up the next
+ time rl_read_key is called. */
+ rl_execute_next (cxt->lastc);
+ return (0);
+ }
+ }
+ else
+#endif
+ if (cxt->lastc >= 0 && ENDSRCH_CHAR (cxt->lastc))
+ {
+ /* This sets rl_pending_input to LASTC; it will be picked up the next
+ time rl_read_key is called. */
+ rl_execute_next (cxt->lastc);
+ return (0);
+ }
+
+ _rl_init_executing_keyseq ();
+
+opcode_dispatch:
+ /* Now dispatch on the character. `Opcodes' affect the search string or
+ state. Other characters are added to the string. */
+ switch (cxt->lastc)
+ {
+ /* search again */
+ case -1:
+ if (cxt->search_string_index == 0)
+ {
+ if (last_isearch_string)
+ {
+ cxt->search_string_size = 64 + last_isearch_string_len;
+ cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size);
+ strcpy (cxt->search_string, last_isearch_string);
+ cxt->search_string_index = last_isearch_string_len;
+ rl_display_search (cxt->search_string, cxt->sflags, -1);
+ break;
+ }
+ /* XXX - restore keymap here? */
+ return (1);
+ }
+ else if ((cxt->sflags & SF_REVERSE) && cxt->sline_index >= 0)
+ cxt->sline_index--;
+ else if (cxt->sline_index != cxt->sline_len)
+ cxt->sline_index++;
+ else
+ rl_ding ();
+ break;
+
+ /* switch directions */
+ case -2:
+ cxt->direction = -cxt->direction;
+ if (cxt->direction < 0)
+ cxt->sflags |= SF_REVERSE;
+ else
+ cxt->sflags &= ~SF_REVERSE;
+ break;
+
+ /* delete character from search string. */
+ case -3: /* C-H, DEL */
+ /* This is tricky. To do this right, we need to keep a
+ stack of search positions for the current search, with
+ sentinels marking the beginning and end. But this will
+ do until we have a real isearch-undo. */
+ if (cxt->search_string_index == 0)
+ rl_ding ();
+ else if (MB_CUR_MAX == 1 || rl_byte_oriented)
+ cxt->search_string[--cxt->search_string_index] = '\0';
+ else
+ {
+ wstart = _rl_find_prev_mbchar (cxt->search_string, cxt->search_string_index, MB_FIND_NONZERO);
+ if (wstart >= 0)
+ cxt->search_string[cxt->search_string_index = wstart] = '\0';
+ else
+ cxt->search_string[cxt->search_string_index = 0] = '\0';
+ }
+
+ if (cxt->search_string_index == 0)
+ rl_ding ();
+
+ break;
+
+ case -4: /* C-G, abort */
+ rl_replace_line (cxt->lines[cxt->save_line], 0);
+ rl_point = cxt->save_point;
+ rl_mark = cxt->save_mark;
+ rl_deactivate_mark ();
+ rl_restore_prompt();
+ rl_clear_message ();
+
+ _rl_fix_point (1); /* in case save_line and save_point are out of sync */
+ return -1;
+
+ case -5: /* C-W */
+ /* skip over portion of line we already matched and yank word */
+ wstart = rl_point + cxt->search_string_index;
+ if (wstart >= rl_end)
+ {
+ rl_ding ();
+ break;
+ }
+
+ /* if not in a word, move to one. */
+ cval = _rl_char_value (rl_line_buffer, wstart);
+ if (_rl_walphabetic (cval) == 0)
+ {
+ rl_ding ();
+ break;
+ }
+ n = MB_NEXTCHAR (rl_line_buffer, wstart, 1, MB_FIND_NONZERO);;
+ while (n < rl_end)
+ {
+ cval = _rl_char_value (rl_line_buffer, n);
+ if (_rl_walphabetic (cval) == 0)
+ break;
+ n = MB_NEXTCHAR (rl_line_buffer, n, 1, MB_FIND_NONZERO);;
+ }
+ wlen = n - wstart + 1;
+ if (cxt->search_string_index + wlen + 1 >= cxt->search_string_size)
+ {
+ cxt->search_string_size += wlen + 1;
+ cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size);
+ }
+ for (; wstart < n; wstart++)
+ cxt->search_string[cxt->search_string_index++] = rl_line_buffer[wstart];
+ cxt->search_string[cxt->search_string_index] = '\0';
+ break;
+
+ case -6: /* C-Y */
+ /* skip over portion of line we already matched and yank rest */
+ wstart = rl_point + cxt->search_string_index;
+ if (wstart >= rl_end)
+ {
+ rl_ding ();
+ break;
+ }
+ n = rl_end - wstart + 1;
+ if (cxt->search_string_index + n + 1 >= cxt->search_string_size)
+ {
+ cxt->search_string_size += n + 1;
+ cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size);
+ }
+ for (n = wstart; n < rl_end; n++)
+ cxt->search_string[cxt->search_string_index++] = rl_line_buffer[n];
+ cxt->search_string[cxt->search_string_index] = '\0';
+ break;
+
+ case -7: /* bracketed paste */
+ paste = _rl_bracketed_text (&pastelen);
+ if (paste == 0 || *paste == 0)
+ {
+ xfree (paste);
+ break;
+ }
+ if (_rl_enable_active_region)
+ rl_activate_mark ();
+ if (cxt->search_string_index + pastelen + 1 >= cxt->search_string_size)
+ {
+ cxt->search_string_size += pastelen + 2;
+ cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size);
+ }
+ memcpy (cxt->search_string + cxt->search_string_index, paste, pastelen);
+ cxt->search_string_index += pastelen;
+ cxt->search_string[cxt->search_string_index] = '\0';
+ xfree (paste);
+ break;
+
+ /* Add character to search string and continue search. */
+ default:
+#if defined (HANDLE_MULTIBYTE)
+ wlen = (cxt->mb[0] == 0 || cxt->mb[1] == 0) ? 1 : RL_STRLEN (cxt->mb);
+#else
+ wlen = 1;
+#endif
+ if (cxt->search_string_index + wlen + 1 >= cxt->search_string_size)
+ {
+ cxt->search_string_size += 128; /* 128 much greater than MB_CUR_MAX */
+ cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size);
+ }
+#if defined (HANDLE_MULTIBYTE)
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ {
+ int j;
+
+ if (cxt->mb[0] == 0 || cxt->mb[1] == 0)
+ cxt->search_string[cxt->search_string_index++] = cxt->mb[0];
+ else
+ for (j = 0; j < wlen; )
+ cxt->search_string[cxt->search_string_index++] = cxt->mb[j++];
+ }
+ else
+#endif
+ cxt->search_string[cxt->search_string_index++] = cxt->lastc; /* XXX - was c instead of lastc */
+ cxt->search_string[cxt->search_string_index] = '\0';
+ break;
+ }
+
+ for (cxt->sflags &= ~(SF_FOUND|SF_FAILED);; )
+ {
+ if (cxt->search_string_index == 0)
+ {
+ cxt->sflags |= SF_FAILED;
+ break;
+ }
+
+ limit = cxt->sline_len - cxt->search_string_index + 1;
+
+ /* Search the current line. */
+ while ((cxt->sflags & SF_REVERSE) ? (cxt->sline_index >= 0) : (cxt->sline_index < limit))
+ {
+ if (STREQN (cxt->search_string, cxt->sline + cxt->sline_index, cxt->search_string_index))
+ {
+ cxt->sflags |= SF_FOUND;
+ break;
+ }
+ else
+ cxt->sline_index += cxt->direction;
+
+ if (cxt->sline_index < 0)
+ {
+ cxt->sline_index = 0;
+ break;
+ }
+ }
+ if (cxt->sflags & SF_FOUND)
+ break;
+
+ /* Move to the next line, but skip new copies of the line
+ we just found and lines shorter than the string we're
+ searching for. */
+ do
+ {
+ /* Move to the next line. */
+ cxt->history_pos += cxt->direction;
+
+ /* At limit for direction? */
+ if ((cxt->sflags & SF_REVERSE) ? (cxt->history_pos < 0) : (cxt->history_pos == cxt->hlen))
+ {
+ cxt->sflags |= SF_FAILED;
+ break;
+ }
+
+ /* We will need these later. */
+ cxt->sline = cxt->lines[cxt->history_pos];
+ cxt->sline_len = strlen (cxt->sline);
+ }
+ while ((cxt->prev_line_found && STREQ (cxt->prev_line_found, cxt->lines[cxt->history_pos])) ||
+ (cxt->search_string_index > cxt->sline_len));
+
+ if (cxt->sflags & SF_FAILED)
+ {
+ /* XXX - reset sline_index if < 0 */
+ if (cxt->sline_index < 0)
+ cxt->sline_index = 0;
+ break;
+ }
+
+ /* Now set up the line for searching... */
+ cxt->sline_index = (cxt->sflags & SF_REVERSE) ? cxt->sline_len - cxt->search_string_index : 0;
+ }
+
+ /* reset the keymaps for the next time through the loop */
+ cxt->keymap = cxt->okeymap = _rl_keymap;
+
+ if (cxt->sflags & SF_FAILED)
+ {
+ /* We cannot find the search string. Ding the bell. */
+ rl_ding ();
+ cxt->history_pos = cxt->last_found_line;
+ rl_deactivate_mark ();
+ rl_display_search (cxt->search_string, cxt->sflags, (cxt->history_pos == cxt->save_line) ? -1 : cxt->history_pos);
+ return 1;
+ }
+
+ /* We have found the search string. Just display it. But don't
+ actually move there in the history list until the user accepts
+ the location. */
+ if (cxt->sflags & SF_FOUND)
+ {
+ cxt->prev_line_found = cxt->lines[cxt->history_pos];
+ rl_replace_line (cxt->lines[cxt->history_pos], 0);
+ if (_rl_enable_active_region)
+ rl_activate_mark ();
+ rl_point = cxt->sline_index;
+ if (rl_mark_active_p () && cxt->search_string_index > 0)
+ rl_mark = rl_point + cxt->search_string_index;
+ cxt->last_found_line = cxt->history_pos;
+ rl_display_search (cxt->search_string, cxt->sflags, (cxt->history_pos == cxt->save_line) ? -1 : cxt->history_pos);
+ }
+
+ return 1;
+}
+
+int
+_rl_isearch_cleanup (_rl_search_cxt *cxt, int r)
+{
+ if (r >= 0)
+ _rl_isearch_fini (cxt);
+ _rl_scxt_dispose (cxt, 0);
+ _rl_iscxt = 0;
+
+ RL_UNSETSTATE(RL_STATE_ISEARCH);
+
+ return (r != 0);
+}
+
+/* Search through the history looking for an interactively typed string.
+ This is analogous to i-search. We start the search in the current line.
+ DIRECTION is which direction to search; >= 0 means forward, < 0 means
+ backwards. */
+static int
+rl_search_history (int direction, int invoking_key)
+{
+ _rl_search_cxt *cxt; /* local for now, but saved globally */
+ int c, r;
+
+ RL_SETSTATE(RL_STATE_ISEARCH);
+ cxt = _rl_isearch_init (direction);
+
+ rl_display_search (cxt->search_string, cxt->sflags, -1);
+
+ /* If we are using the callback interface, all we do is set up here and
+ return. The key is that we leave RL_STATE_ISEARCH set. */
+ if (RL_ISSTATE (RL_STATE_CALLBACK))
+ return (0);
+
+ r = -1;
+ for (;;)
+ {
+ c = _rl_search_getchar (cxt);
+ /* We might want to handle EOF here (c == 0) */
+ r = _rl_isearch_dispatch (cxt, cxt->lastc);
+ if (r <= 0)
+ break;
+ }
+
+ /* The searching is over. The user may have found the string that she
+ was looking for, or else she may have exited a failing search. If
+ LINE_INDEX is -1, then that shows that the string searched for was
+ not found. We use this to determine where to place rl_point. */
+ return (_rl_isearch_cleanup (cxt, r));
+}
+
+#if defined (READLINE_CALLBACKS)
+/* Called from the callback functions when we are ready to read a key. The
+ callback functions know to call this because RL_ISSTATE(RL_STATE_ISEARCH).
+ If _rl_isearch_dispatch finishes searching, this function is responsible
+ for turning off RL_STATE_ISEARCH, which it does using _rl_isearch_cleanup. */
+int
+_rl_isearch_callback (_rl_search_cxt *cxt)
+{
+ int c, r;
+
+ c = _rl_search_getchar (cxt);
+ /* We might want to handle EOF here */
+ r = _rl_isearch_dispatch (cxt, cxt->lastc);
+
+ return (r <= 0) ? _rl_isearch_cleanup (cxt, r) : 0;
+}
+#endif
diff --git a/third_party/readline/keymaps.c b/third_party/readline/keymaps.c
new file mode 100644
index 000000000..084e0ecb3
--- /dev/null
+++ b/third_party/readline/keymaps.c
@@ -0,0 +1,174 @@
+/* keymaps.c -- Functions and keymaps for the GNU Readline library. */
+
+/* Copyright (C) 1988,1989-2009,2017 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#include /* for FILE * definition for readline.h */
+
+#include "readline.h"
+#include "rlconf.h"
+
+#include "emacs_keymap.inc"
+
+#if defined (VI_MODE)
+#include "vi_keymap.inc"
+#endif
+
+#include "xmalloc.h"
+
+/* **************************************************************** */
+/* */
+/* Functions for manipulating Keymaps. */
+/* */
+/* **************************************************************** */
+
+
+/* Return a new, empty keymap.
+ Free it with free() when you are done. */
+Keymap
+rl_make_bare_keymap (void)
+{
+ register int i;
+ Keymap keymap;
+
+ keymap = (Keymap)xmalloc (KEYMAP_SIZE * sizeof (KEYMAP_ENTRY));
+ for (i = 0; i < KEYMAP_SIZE; i++)
+ {
+ keymap[i].type = ISFUNC;
+ keymap[i].function = (rl_command_func_t *)NULL;
+ }
+
+#if 0
+ for (i = 'A'; i < ('Z' + 1); i++)
+ {
+ keymap[i].type = ISFUNC;
+ keymap[i].function = rl_do_lowercase_version;
+ }
+#endif
+
+ return (keymap);
+}
+
+/* A convenience function that returns 1 if there are no keys bound to
+ functions in KEYMAP */
+int
+rl_empty_keymap (Keymap keymap)
+{
+ int i;
+
+ for (i = 0; i < ANYOTHERKEY; i++)
+ {
+ if (keymap[i].type != ISFUNC || keymap[i].function)
+ return 0;
+ }
+ return 1;
+}
+
+/* Return a new keymap which is a copy of MAP. Just copies pointers, does
+ not copy text of macros or descend into child keymaps. */
+Keymap
+rl_copy_keymap (Keymap map)
+{
+ register int i;
+ Keymap temp;
+
+ temp = rl_make_bare_keymap ();
+ for (i = 0; i < KEYMAP_SIZE; i++)
+ {
+ temp[i].type = map[i].type;
+ temp[i].function = map[i].function;
+ }
+ return (temp);
+}
+
+/* Return a new keymap with the printing characters bound to rl_insert,
+ the uppercase Meta characters bound to run their lowercase equivalents,
+ and the Meta digits bound to produce numeric arguments. */
+Keymap
+rl_make_keymap (void)
+{
+ register int i;
+ Keymap newmap;
+
+ newmap = rl_make_bare_keymap ();
+
+ /* All ASCII printing characters are self-inserting. */
+ for (i = ' '; i < 127; i++)
+ newmap[i].function = rl_insert;
+
+ newmap[TAB].function = rl_insert;
+ newmap[RUBOUT].function = rl_rubout; /* RUBOUT == 127 */
+ newmap[CTRL('H')].function = rl_rubout;
+
+#if KEYMAP_SIZE > 128
+ /* Printing characters in ISO Latin-1 and some 8-bit character sets. */
+ for (i = 128; i < 256; i++)
+ newmap[i].function = rl_insert;
+#endif /* KEYMAP_SIZE > 128 */
+
+ return (newmap);
+}
+
+/* Free the storage associated with MAP. */
+void
+rl_discard_keymap (Keymap map)
+{
+ int i;
+
+ if (map == 0)
+ return;
+
+ for (i = 0; i < KEYMAP_SIZE; i++)
+ {
+ switch (map[i].type)
+ {
+ case ISFUNC:
+ break;
+
+ case ISKMAP:
+ rl_discard_keymap ((Keymap)map[i].function);
+ xfree ((char *)map[i].function);
+ break;
+
+ case ISMACR:
+ xfree ((char *)map[i].function);
+ break;
+ }
+ }
+}
+
+/* Convenience function that discards, then frees, MAP. */
+void
+rl_free_keymap (Keymap map)
+{
+ rl_discard_keymap (map);
+ xfree ((char *)map);
+}
diff --git a/third_party/readline/keymaps.h b/third_party/readline/keymaps.h
new file mode 100644
index 000000000..7cd0a5de0
--- /dev/null
+++ b/third_party/readline/keymaps.h
@@ -0,0 +1,100 @@
+/* keymaps.h -- Manipulation of readline keymaps. */
+
+/* Copyright (C) 1987, 1989, 1992-2021 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#ifndef _KEYMAPS_H_
+#define _KEYMAPS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined (READLINE_LIBRARY)
+# include "rlstdc.h"
+# include "chardefs.h"
+# include "rltypedefs.h"
+#else
+# include "third_party/readline/rlstdc.h"
+# include "third_party/readline/chardefs.h"
+# include "third_party/readline/rltypedefs.h"
+#endif
+
+/* A keymap contains one entry for each key in the ASCII set.
+ Each entry consists of a type and a pointer.
+ FUNCTION is the address of a function to run, or the
+ address of a keymap to indirect through.
+ TYPE says which kind of thing FUNCTION is. */
+typedef struct _keymap_entry {
+ char type;
+ rl_command_func_t *function;
+} KEYMAP_ENTRY;
+
+/* This must be large enough to hold bindings for all of the characters
+ in a desired character set (e.g, 128 for ASCII, 256 for ISO Latin-x,
+ and so on) plus one for subsequence matching. */
+#define KEYMAP_SIZE 257
+#define ANYOTHERKEY KEYMAP_SIZE-1
+
+typedef KEYMAP_ENTRY KEYMAP_ENTRY_ARRAY[KEYMAP_SIZE];
+typedef KEYMAP_ENTRY *Keymap;
+
+/* The values that TYPE can have in a keymap entry. */
+#define ISFUNC 0
+#define ISKMAP 1
+#define ISMACR 2
+
+extern KEYMAP_ENTRY_ARRAY emacs_standard_keymap, emacs_meta_keymap, emacs_ctlx_keymap;
+extern KEYMAP_ENTRY_ARRAY vi_insertion_keymap, vi_movement_keymap;
+
+/* Return a new, empty keymap.
+ Free it with free() when you are done. */
+extern Keymap rl_make_bare_keymap (void);
+
+/* Return a new keymap which is a copy of MAP. */
+extern Keymap rl_copy_keymap (Keymap);
+
+/* Return a new keymap with the printing characters bound to rl_insert,
+ the lowercase Meta characters bound to run their equivalents, and
+ the Meta digits bound to produce numeric arguments. */
+extern Keymap rl_make_keymap (void);
+
+/* Free the storage associated with a keymap. */
+extern void rl_discard_keymap (Keymap);
+
+/* These functions actually appear in bind.c */
+
+/* Return the keymap corresponding to a given name. Names look like
+ `emacs' or `emacs-meta' or `vi-insert'. */
+extern Keymap rl_get_keymap_by_name (const char *);
+
+/* Return the current keymap. */
+extern Keymap rl_get_keymap (void);
+
+/* Set the current keymap to MAP. */
+extern void rl_set_keymap (Keymap);
+
+/* Set the name of MAP to NAME */
+extern int rl_set_keymap_name (const char *, Keymap);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KEYMAPS_H_ */
diff --git a/third_party/readline/kill.c b/third_party/readline/kill.c
new file mode 100644
index 000000000..879232ed1
--- /dev/null
+++ b/third_party/readline/kill.c
@@ -0,0 +1,900 @@
+/* kill.c -- kill ring management. */
+
+/* Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+
+#if defined (HAVE_UNISTD_H)
+# include /* for _POSIX_VERSION */
+#endif /* HAVE_UNISTD_H */
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#include
+
+/* System-specific feature definitions and include files. */
+#include "rldefs.h"
+
+/* Some standard library routines. */
+#include "readline.h"
+#include "history.h"
+
+#include "rlprivate.h"
+#include "xmalloc.h"
+
+/* **************************************************************** */
+/* */
+/* Killing Mechanism */
+/* */
+/* **************************************************************** */
+
+/* What we assume for a max number of kills. */
+#define DEFAULT_MAX_KILLS 10
+
+/* The real variable to look at to find out when to flush kills. */
+static int rl_max_kills = DEFAULT_MAX_KILLS;
+
+/* Where to store killed text. */
+static char **rl_kill_ring = (char **)NULL;
+
+/* Where we are in the kill ring. */
+static int rl_kill_index;
+
+/* How many slots we have in the kill ring. */
+static int rl_kill_ring_length;
+
+static int _rl_copy_to_kill_ring (char *, int);
+static int region_kill_internal (int);
+static int _rl_copy_word_as_kill (int, int);
+static int rl_yank_nth_arg_internal (int, int, int);
+
+/* How to say that you only want to save a certain amount
+ of kill material. */
+int
+rl_set_retained_kills (int num)
+{
+ return 0;
+}
+
+/* Add TEXT to the kill ring, allocating a new kill ring slot as necessary.
+ This uses TEXT directly, so the caller must not free it. If APPEND is
+ non-zero, and the last command was a kill, the text is appended to the
+ current kill ring slot, otherwise prepended. */
+static int
+_rl_copy_to_kill_ring (char *text, int append)
+{
+ char *old, *new;
+ int slot;
+
+ /* First, find the slot to work with. */
+ if (_rl_last_command_was_kill == 0 || rl_kill_ring == 0)
+ {
+ /* Get a new slot. */
+ if (rl_kill_ring == 0)
+ {
+ /* If we don't have any defined, then make one. */
+ rl_kill_ring = (char **)
+ xmalloc (((rl_kill_ring_length = 1) + 1) * sizeof (char *));
+ rl_kill_ring[slot = 0] = (char *)NULL;
+ }
+ else
+ {
+ /* We have to add a new slot on the end, unless we have
+ exceeded the max limit for remembering kills. */
+ slot = rl_kill_ring_length;
+ if (slot == rl_max_kills)
+ {
+ register int i;
+ xfree (rl_kill_ring[0]);
+ for (i = 0; i < slot; i++)
+ rl_kill_ring[i] = rl_kill_ring[i + 1];
+ }
+ else
+ {
+ slot = rl_kill_ring_length += 1;
+ rl_kill_ring = (char **)xrealloc (rl_kill_ring, (slot + 1) * sizeof (char *));
+ }
+ rl_kill_ring[--slot] = (char *)NULL;
+ }
+ }
+ else
+ slot = rl_kill_ring_length - 1;
+
+ /* If the last command was a kill, prepend or append. */
+ if (_rl_last_command_was_kill && rl_kill_ring[slot] && rl_editing_mode != vi_mode)
+ {
+ old = rl_kill_ring[slot];
+ new = (char *)xmalloc (1 + strlen (old) + strlen (text));
+
+ if (append)
+ {
+ strcpy (new, old);
+ strcat (new, text);
+ }
+ else
+ {
+ strcpy (new, text);
+ strcat (new, old);
+ }
+ xfree (old);
+ xfree (text);
+ rl_kill_ring[slot] = new;
+ }
+ else
+ rl_kill_ring[slot] = text;
+
+ rl_kill_index = slot;
+ return 0;
+}
+
+/* The way to kill something. This appends or prepends to the last
+ kill, if the last command was a kill command. if FROM is less
+ than TO, then the text is appended, otherwise prepended. If the
+ last command was not a kill command, then a new slot is made for
+ this kill. */
+int
+rl_kill_text (int from, int to)
+{
+ char *text;
+
+ /* Is there anything to kill? */
+ if (from == to)
+ {
+ _rl_last_command_was_kill++;
+ return 0;
+ }
+
+ text = rl_copy_text (from, to);
+
+ /* Delete the copied text from the line. */
+ rl_delete_text (from, to);
+
+ _rl_copy_to_kill_ring (text, from < to);
+
+ _rl_last_command_was_kill++;
+ return 0;
+}
+
+/* Now REMEMBER! In order to do prepending or appending correctly, kill
+ commands always make rl_point's original position be the FROM argument,
+ and rl_point's extent be the TO argument. */
+
+/* **************************************************************** */
+/* */
+/* Killing Commands */
+/* */
+/* **************************************************************** */
+
+/* Delete the word at point, saving the text in the kill ring. */
+int
+rl_kill_word (int count, int key)
+{
+ int orig_point;
+
+ if (count < 0)
+ return (rl_backward_kill_word (-count, key));
+ else
+ {
+ orig_point = rl_point;
+ rl_forward_word (count, key);
+
+ if (rl_point != orig_point)
+ rl_kill_text (orig_point, rl_point);
+
+ rl_point = orig_point;
+ if (rl_editing_mode == emacs_mode)
+ rl_mark = rl_point;
+ }
+ return 0;
+}
+
+/* Rubout the word before point, placing it on the kill ring. */
+int
+rl_backward_kill_word (int count, int key)
+{
+ int orig_point;
+
+ if (count < 0)
+ return (rl_kill_word (-count, key));
+ else
+ {
+ orig_point = rl_point;
+ rl_backward_word (count, key);
+
+ if (rl_point != orig_point)
+ rl_kill_text (orig_point, rl_point);
+
+ if (rl_editing_mode == emacs_mode)
+ rl_mark = rl_point;
+ }
+ return 0;
+}
+
+/* Kill from here to the end of the line. If DIRECTION is negative, kill
+ back to the line start instead. */
+int
+rl_kill_line (int direction, int key)
+{
+ int orig_point;
+
+ if (direction < 0)
+ return (rl_backward_kill_line (1, key));
+ else
+ {
+ orig_point = rl_point;
+ rl_end_of_line (1, key);
+ if (orig_point != rl_point)
+ rl_kill_text (orig_point, rl_point);
+ rl_point = orig_point;
+ if (rl_editing_mode == emacs_mode)
+ rl_mark = rl_point;
+ }
+ return 0;
+}
+
+/* Kill backwards to the start of the line. If DIRECTION is negative, kill
+ forwards to the line end instead. */
+int
+rl_backward_kill_line (int direction, int key)
+{
+ int orig_point;
+
+ if (direction < 0)
+ return (rl_kill_line (1, key));
+ else
+ {
+ if (rl_point == 0)
+ rl_ding ();
+ else
+ {
+ orig_point = rl_point;
+ rl_beg_of_line (1, key);
+ if (rl_point != orig_point)
+ rl_kill_text (orig_point, rl_point);
+ if (rl_editing_mode == emacs_mode)
+ rl_mark = rl_point;
+ }
+ }
+ return 0;
+}
+
+/* Kill the whole line, no matter where point is. */
+int
+rl_kill_full_line (int count, int key)
+{
+ rl_begin_undo_group ();
+ rl_point = 0;
+ rl_kill_text (rl_point, rl_end);
+ rl_mark = 0;
+ rl_end_undo_group ();
+ return 0;
+}
+
+/* The next two functions mimic unix line editing behaviour, except they
+ save the deleted text on the kill ring. This is safer than not saving
+ it, and since we have a ring, nobody should get screwed. */
+
+/* This does what C-w does in Unix. We can't prevent people from
+ using behaviour that they expect. */
+int
+rl_unix_word_rubout (int count, int key)
+{
+ int orig_point;
+
+ if (rl_point == 0)
+ rl_ding ();
+ else
+ {
+ orig_point = rl_point;
+ if (count <= 0)
+ count = 1;
+
+ while (count--)
+ {
+ while (rl_point && whitespace (rl_line_buffer[rl_point - 1]))
+ rl_point--;
+
+ while (rl_point && (whitespace (rl_line_buffer[rl_point - 1]) == 0))
+ rl_point--; /* XXX - multibyte? */
+ }
+
+ rl_kill_text (orig_point, rl_point);
+ if (rl_editing_mode == emacs_mode)
+ rl_mark = rl_point;
+ }
+
+ return 0;
+}
+
+/* This deletes one filename component in a Unix pathname. That is, it
+ deletes backward to directory separator (`/') or whitespace. */
+int
+rl_unix_filename_rubout (int count, int key)
+{
+ int orig_point, c;
+
+ if (rl_point == 0)
+ rl_ding ();
+ else
+ {
+ orig_point = rl_point;
+ if (count <= 0)
+ count = 1;
+
+ while (count--)
+ {
+ c = rl_line_buffer[rl_point - 1];
+
+ /* First move backwards through whitespace */
+ while (rl_point && whitespace (c))
+ {
+ rl_point--;
+ c = rl_line_buffer[rl_point - 1];
+ }
+
+ /* Consume one or more slashes. */
+ if (c == '/')
+ {
+ int i;
+
+ i = rl_point - 1;
+ while (i > 0 && c == '/')
+ c = rl_line_buffer[--i];
+ if (i == 0 || whitespace (c))
+ {
+ rl_point = i + whitespace (c);
+ continue; /* slashes only */
+ }
+ c = '/';
+ }
+
+ while (rl_point && (whitespace (c) || c == '/'))
+ {
+ rl_point--;
+ c = rl_line_buffer[rl_point - 1];
+ }
+
+ while (rl_point && (whitespace (c) == 0) && c != '/')
+ {
+ rl_point--; /* XXX - multibyte? */
+ c = rl_line_buffer[rl_point - 1];
+ }
+ }
+
+ rl_kill_text (orig_point, rl_point);
+ if (rl_editing_mode == emacs_mode)
+ rl_mark = rl_point;
+ }
+
+ return 0;
+}
+
+/* Here is C-u doing what Unix does. You don't *have* to use these
+ key-bindings. We have a choice of killing the entire line, or
+ killing from where we are to the start of the line. We choose the
+ latter, because if you are a Unix weenie, then you haven't backspaced
+ into the line at all, and if you aren't, then you know what you are
+ doing. */
+int
+rl_unix_line_discard (int count, int key)
+{
+ if (rl_point == 0)
+ rl_ding ();
+ else
+ {
+ rl_kill_text (rl_point, 0);
+ rl_point = 0;
+ if (rl_editing_mode == emacs_mode)
+ rl_mark = rl_point;
+ }
+ return 0;
+}
+
+/* Copy the text in the `region' to the kill ring. If DELETE is non-zero,
+ delete the text from the line as well. */
+static int
+region_kill_internal (int delete)
+{
+ char *text;
+
+ if (rl_mark != rl_point)
+ {
+ text = rl_copy_text (rl_point, rl_mark);
+ if (delete)
+ rl_delete_text (rl_point, rl_mark);
+ _rl_copy_to_kill_ring (text, rl_point < rl_mark);
+ }
+
+ _rl_fix_point (1);
+ _rl_last_command_was_kill++;
+ return 0;
+}
+
+/* Copy the text in the region to the kill ring. */
+int
+rl_copy_region_to_kill (int count, int key)
+{
+ return (region_kill_internal (0));
+}
+
+/* Kill the text between the point and mark. */
+int
+rl_kill_region (int count, int key)
+{
+ int r, npoint;
+
+ npoint = (rl_point < rl_mark) ? rl_point : rl_mark;
+ r = region_kill_internal (1);
+ rl_point = npoint;
+ _rl_fix_point (1);
+ return r;
+}
+
+/* Copy COUNT words to the kill ring. DIR says which direction we look
+ to find the words. */
+static int
+_rl_copy_word_as_kill (int count, int dir)
+{
+ int om, op, r;
+
+ om = rl_mark;
+ op = rl_point;
+
+ if (dir > 0)
+ rl_forward_word (count, 0);
+ else
+ rl_backward_word (count, 0);
+
+ rl_mark = rl_point;
+
+ if (dir > 0)
+ rl_backward_word (count, 0);
+ else
+ rl_forward_word (count, 0);
+
+ r = region_kill_internal (0);
+
+ rl_mark = om;
+ rl_point = op;
+
+ return r;
+}
+
+int
+rl_copy_forward_word (int count, int key)
+{
+ if (count < 0)
+ return (rl_copy_backward_word (-count, key));
+
+ return (_rl_copy_word_as_kill (count, 1));
+}
+
+int
+rl_copy_backward_word (int count, int key)
+{
+ if (count < 0)
+ return (rl_copy_forward_word (-count, key));
+
+ return (_rl_copy_word_as_kill (count, -1));
+}
+
+/* Yank back the last killed text. This ignores arguments. */
+int
+rl_yank (int count, int key)
+{
+ if (rl_kill_ring == 0)
+ {
+ _rl_abort_internal ();
+ return 1;
+ }
+
+ _rl_set_mark_at_pos (rl_point);
+ rl_insert_text (rl_kill_ring[rl_kill_index]);
+ return 0;
+}
+
+/* If the last command was yank, or yank_pop, and the text just
+ before point is identical to the current kill item, then
+ delete that text from the line, rotate the index down, and
+ yank back some other text. */
+int
+rl_yank_pop (int count, int key)
+{
+ int l, n;
+
+ if (((rl_last_func != rl_yank_pop) && (rl_last_func != rl_yank)) ||
+ !rl_kill_ring)
+ {
+ _rl_abort_internal ();
+ return 1;
+ }
+
+ l = strlen (rl_kill_ring[rl_kill_index]);
+ n = rl_point - l;
+ if (n >= 0 && STREQN (rl_line_buffer + n, rl_kill_ring[rl_kill_index], l))
+ {
+ rl_delete_text (n, rl_point);
+ rl_point = n;
+ rl_kill_index--;
+ if (rl_kill_index < 0)
+ rl_kill_index = rl_kill_ring_length - 1;
+ rl_yank (1, 0);
+ return 0;
+ }
+ else
+ {
+ _rl_abort_internal ();
+ return 1;
+ }
+}
+
+#if defined (VI_MODE)
+int
+rl_vi_yank_pop (int count, int key)
+{
+ int l, n, origpoint;
+
+ if (((rl_last_func != rl_vi_yank_pop) && (rl_last_func != rl_vi_put)) ||
+ !rl_kill_ring)
+ {
+ _rl_abort_internal ();
+ return 1;
+ }
+
+ l = strlen (rl_kill_ring[rl_kill_index]);
+#if 0 /* TAG:readline-8.3 8/29/2022 matteopaolini1995@gmail.com */
+ origpoint = rl_point;
+ n = rl_point - l + 1;
+#else
+ n = rl_point - l;
+#endif
+ if (n >= 0 && STREQN (rl_line_buffer + n, rl_kill_ring[rl_kill_index], l))
+ {
+#if 0 /* TAG:readline-8.3 */
+ rl_delete_text (n, n + l); /* remember vi cursor positioning */
+ rl_point = origpoint - l;
+#else
+ rl_delete_text (n, rl_point);
+ rl_point = n;
+#endif
+ rl_kill_index--;
+ if (rl_kill_index < 0)
+ rl_kill_index = rl_kill_ring_length - 1;
+ rl_vi_put (1, 'p');
+ return 0;
+ }
+ else
+ {
+ _rl_abort_internal ();
+ return 1;
+ }
+}
+#endif /* VI_MODE */
+
+/* Yank the COUNTh argument from the previous history line, skipping
+ HISTORY_SKIP lines before looking for the `previous line'. */
+static int
+rl_yank_nth_arg_internal (int count, int key, int history_skip)
+{
+ register HIST_ENTRY *entry;
+ char *arg;
+ int i, pos;
+
+ pos = where_history ();
+
+ if (history_skip)
+ {
+ for (i = 0; i < history_skip; i++)
+ entry = previous_history ();
+ }
+
+ entry = previous_history ();
+
+ history_set_pos (pos);
+
+ if (entry == 0)
+ {
+ rl_ding ();
+ return 1;
+ }
+
+ arg = history_arg_extract (count, count, entry->line);
+ if (!arg || !*arg)
+ {
+ rl_ding ();
+ FREE (arg);
+ return 1;
+ }
+
+ rl_begin_undo_group ();
+
+ _rl_set_mark_at_pos (rl_point);
+
+#if defined (VI_MODE)
+ /* Vi mode always inserts a space before yanking the argument, and it
+ inserts it right *after* rl_point. */
+ if (rl_editing_mode == vi_mode && _rl_keymap == vi_movement_keymap)
+ {
+ rl_vi_append_mode (1, key);
+ rl_insert_text (" ");
+ }
+#endif /* VI_MODE */
+
+ rl_insert_text (arg);
+ xfree (arg);
+
+ rl_end_undo_group ();
+ return 0;
+}
+
+/* Yank the COUNTth argument from the previous history line. */
+int
+rl_yank_nth_arg (int count, int key)
+{
+ return (rl_yank_nth_arg_internal (count, key, 0));
+}
+
+/* Yank the last argument from the previous history line. This `knows'
+ how rl_yank_nth_arg treats a count of `$'. With an argument, this
+ behaves the same as rl_yank_nth_arg. */
+int
+rl_yank_last_arg (int count, int key)
+{
+ static int history_skip = 0;
+ static int explicit_arg_p = 0;
+ static int count_passed = 1;
+ static int direction = 1;
+ static int undo_needed = 0;
+ int retval;
+
+ if (rl_last_func != rl_yank_last_arg)
+ {
+ history_skip = 0;
+ explicit_arg_p = rl_explicit_arg;
+ count_passed = count;
+ direction = 1;
+ }
+ else
+ {
+ if (undo_needed)
+ rl_do_undo ();
+ if (count < 0) /* XXX - was < 1 */
+ direction = -direction;
+ history_skip += direction;
+ if (history_skip < 0)
+ history_skip = 0;
+ }
+
+ if (explicit_arg_p)
+ retval = rl_yank_nth_arg_internal (count_passed, key, history_skip);
+ else
+ retval = rl_yank_nth_arg_internal ('$', key, history_skip);
+
+ undo_needed = retval == 0;
+ return retval;
+}
+
+/* Having read the special escape sequence denoting the beginning of a
+ `bracketed paste' sequence, read the rest of the pasted input until the
+ closing sequence and return the pasted text. */
+char *
+_rl_bracketed_text (size_t *lenp)
+{
+ int c;
+ size_t len, cap;
+ char *buf;
+
+ len = 0;
+ buf = xmalloc (cap = 64);
+ buf[0] = '\0';
+
+ RL_SETSTATE (RL_STATE_MOREINPUT);
+ while ((c = rl_read_key ()) >= 0)
+ {
+ if (RL_ISSTATE (RL_STATE_MACRODEF))
+ _rl_add_macro_char (c);
+
+ if (c == '\r') /* XXX */
+ c = '\n';
+
+ if (len == cap)
+ buf = xrealloc (buf, cap *= 2);
+
+ buf[len++] = c;
+ if (len >= BRACK_PASTE_SLEN && c == BRACK_PASTE_LAST &&
+ STREQN (buf + len - BRACK_PASTE_SLEN, BRACK_PASTE_SUFF, BRACK_PASTE_SLEN))
+ {
+ len -= BRACK_PASTE_SLEN;
+ break;
+ }
+ }
+ RL_UNSETSTATE (RL_STATE_MOREINPUT);
+
+ if (c >= 0)
+ {
+ if (len == cap)
+ buf = xrealloc (buf, cap + 1);
+ buf[len] = '\0';
+ }
+
+ if (lenp)
+ *lenp = len;
+ return (buf);
+}
+
+/* Having read the special escape sequence denoting the beginning of a
+ `bracketed paste' sequence, read the rest of the pasted input until the
+ closing sequence and insert the pasted text as a single unit without
+ interpretation. Temporarily highlight the inserted text. */
+int
+rl_bracketed_paste_begin (int count, int key)
+{
+ int retval, c;
+ size_t len, cap;
+ char *buf;
+
+ buf = _rl_bracketed_text (&len);
+ rl_mark = rl_point;
+ retval = rl_insert_text (buf) == len ? 0 : 1;
+ if (_rl_enable_active_region)
+ rl_activate_mark ();
+
+ xfree (buf);
+ return (retval);
+}
+
+int
+_rl_read_bracketed_paste_prefix (int c)
+{
+ char pbuf[BRACK_PASTE_SLEN+1], *pbpref;
+ int key, ind, j;
+
+ pbpref = BRACK_PASTE_PREF; /* XXX - debugging */
+ if (c != pbpref[0])
+ return (0);
+ pbuf[ind = 0] = c;
+ while (ind < BRACK_PASTE_SLEN-1 &&
+ (RL_ISSTATE (RL_STATE_INPUTPENDING|RL_STATE_MACROINPUT) == 0) &&
+ _rl_pushed_input_available () == 0 &&
+ _rl_input_queued (0))
+ {
+ key = rl_read_key (); /* XXX - for now */
+ if (key < 0)
+ break;
+ pbuf[++ind] = key;
+ if (pbuf[ind] != pbpref[ind])
+ break;
+ }
+
+ if (ind < BRACK_PASTE_SLEN-1) /* read incomplete sequence */
+ {
+ while (ind >= 0)
+ _rl_unget_char (pbuf[ind--]);
+ return (key < 0 ? key : 0);
+ }
+ return (key < 0 ? key : 1);
+}
+
+/* Get a character from wherever we read input, handling input in bracketed
+ paste mode. If we don't have or use bracketed paste mode, this can be
+ used in place of rl_read_key(). */
+int
+_rl_bracketed_read_key ()
+{
+ int c, r;
+ char *pbuf;
+ size_t pblen;
+
+ RL_SETSTATE(RL_STATE_MOREINPUT);
+ c = rl_read_key ();
+ RL_UNSETSTATE(RL_STATE_MOREINPUT);
+
+ if (c < 0)
+ return -1;
+
+ /* read pasted data with bracketed-paste mode enabled. */
+ if (_rl_enable_bracketed_paste && c == ESC && (r = _rl_read_bracketed_paste_prefix (c)) == 1)
+ {
+ pbuf = _rl_bracketed_text (&pblen);
+ if (pblen == 0)
+ {
+ xfree (pbuf);
+ return 0; /* XXX */
+ }
+ c = (unsigned char)pbuf[0];
+ if (pblen > 1)
+ {
+ while (--pblen > 0)
+ _rl_unget_char ((unsigned char)pbuf[pblen]);
+ }
+ xfree (pbuf);
+ }
+
+ return c;
+}
+
+/* Get a character from wherever we read input, handling input in bracketed
+ paste mode. If we don't have or use bracketed paste mode, this can be
+ used in place of rl_read_key(). */
+int
+_rl_bracketed_read_mbstring (char *mb, int mlen)
+{
+ int c, r;
+
+ c = _rl_bracketed_read_key ();
+ if (c < 0)
+ return -1;
+
+#if defined (HANDLE_MULTIBYTE)
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ c = _rl_read_mbstring (c, mb, mlen);
+ else
+#endif
+ mb[0] = c;
+ mb[mlen] = '\0'; /* just in case */
+
+ return c;
+}
+
+/* A special paste command for Windows users. */
+#if defined (_WIN32)
+#include
+
+int
+rl_paste_from_clipboard (int count, int key)
+{
+ char *data, *ptr;
+ int len;
+
+ if (OpenClipboard (NULL) == 0)
+ return (0);
+
+ data = (char *)GetClipboardData (CF_TEXT);
+ if (data)
+ {
+ ptr = strchr (data, '\r');
+ if (ptr)
+ {
+ len = ptr - data;
+ ptr = (char *)xmalloc (len + 1);
+ ptr[len] = '\0';
+ strncpy (ptr, data, len);
+ }
+ else
+ ptr = data;
+ _rl_set_mark_at_pos (rl_point);
+ rl_insert_text (ptr);
+ if (ptr != data)
+ xfree (ptr);
+ CloseClipboard ();
+ }
+ return (0);
+}
+#endif /* _WIN32 */
diff --git a/third_party/readline/macro.c b/third_party/readline/macro.c
new file mode 100644
index 000000000..eeb1c316a
--- /dev/null
+++ b/third_party/readline/macro.c
@@ -0,0 +1,334 @@
+/* macro.c -- keyboard macros for readline. */
+
+/* Copyright (C) 1994-2009,2017 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+
+#if defined (HAVE_UNISTD_H)
+# include /* for _POSIX_VERSION */
+#endif /* HAVE_UNISTD_H */
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#include
+
+/* System-specific feature definitions and include files. */
+#include "rldefs.h"
+
+/* Some standard library routines. */
+#include "readline.h"
+#include "history.h"
+
+#include "rlprivate.h"
+#include "xmalloc.h"
+
+#define MAX_MACRO_LEVEL 16
+
+/* **************************************************************** */
+/* */
+/* Hacking Keyboard Macros */
+/* */
+/* **************************************************************** */
+
+/* The currently executing macro string. If this is non-zero,
+ then it is a malloc ()'ed string where input is coming from. */
+char *rl_executing_macro = (char *)NULL;
+
+/* The offset in the above string to the next character to be read. */
+static int executing_macro_index;
+
+/* The current macro string being built. Characters get stuffed
+ in here by add_macro_char (). */
+static char *current_macro = (char *)NULL;
+
+/* The size of the buffer allocated to current_macro. */
+static int current_macro_size;
+
+/* The index at which characters are being added to current_macro. */
+static int current_macro_index;
+
+/* A structure used to save nested macro strings.
+ It is a linked list of string/index for each saved macro. */
+struct saved_macro {
+ struct saved_macro *next;
+ char *string;
+ int sindex;
+};
+
+/* The list of saved macros. */
+static struct saved_macro *macro_list = (struct saved_macro *)NULL;
+
+static int macro_level = 0;
+
+/* Set up to read subsequent input from STRING.
+ STRING is free ()'ed when we are done with it. */
+void
+_rl_with_macro_input (char *string)
+{
+ if (macro_level > MAX_MACRO_LEVEL)
+ {
+ _rl_errmsg ("maximum macro execution nesting level exceeded");
+ _rl_abort_internal ();
+ return;
+ }
+
+#if 0
+ if (rl_executing_macro) /* XXX - later */
+#endif
+ _rl_push_executing_macro ();
+ rl_executing_macro = string;
+ executing_macro_index = 0;
+ RL_SETSTATE(RL_STATE_MACROINPUT);
+}
+
+/* Return the next character available from a macro, or 0 if
+ there are no macro characters. */
+int
+_rl_next_macro_key (void)
+{
+ int c;
+
+ if (rl_executing_macro == 0)
+ return (0);
+
+ if (rl_executing_macro[executing_macro_index] == 0)
+ {
+ _rl_pop_executing_macro ();
+ return (_rl_next_macro_key ());
+ }
+
+#if defined (READLINE_CALLBACKS)
+ c = rl_executing_macro[executing_macro_index++];
+ if (RL_ISSTATE (RL_STATE_CALLBACK) && RL_ISSTATE (RL_STATE_READCMD|RL_STATE_MOREINPUT) && rl_executing_macro[executing_macro_index] == 0)
+ _rl_pop_executing_macro ();
+ return c;
+#else
+ /* XXX - consider doing the same as the callback code, just not testing
+ whether we're running in callback mode */
+ return (rl_executing_macro[executing_macro_index++]);
+#endif
+}
+
+int
+_rl_peek_macro_key (void)
+{
+ if (rl_executing_macro == 0)
+ return (0);
+ if (rl_executing_macro[executing_macro_index] == 0 && (macro_list == 0 || macro_list->string == 0))
+ return (0);
+ if (rl_executing_macro[executing_macro_index] == 0 && macro_list && macro_list->string)
+ return (macro_list->string[0]);
+ return (rl_executing_macro[executing_macro_index]);
+}
+
+int
+_rl_prev_macro_key (void)
+{
+ if (rl_executing_macro == 0)
+ return (0);
+
+ if (executing_macro_index == 0)
+ return (0);
+
+ executing_macro_index--;
+ return (rl_executing_macro[executing_macro_index]);
+}
+
+/* Save the currently executing macro on a stack of saved macros. */
+void
+_rl_push_executing_macro (void)
+{
+ struct saved_macro *saver;
+
+ saver = (struct saved_macro *)xmalloc (sizeof (struct saved_macro));
+ saver->next = macro_list;
+ saver->sindex = executing_macro_index;
+ saver->string = rl_executing_macro;
+
+ macro_list = saver;
+
+ macro_level++;
+}
+
+/* Discard the current macro, replacing it with the one
+ on the top of the stack of saved macros. */
+void
+_rl_pop_executing_macro (void)
+{
+ struct saved_macro *macro;
+
+ FREE (rl_executing_macro);
+ rl_executing_macro = (char *)NULL;
+ executing_macro_index = 0;
+
+ if (macro_list)
+ {
+ macro = macro_list;
+ rl_executing_macro = macro_list->string;
+ executing_macro_index = macro_list->sindex;
+ macro_list = macro_list->next;
+ xfree (macro);
+ }
+
+ macro_level--;
+
+ if (rl_executing_macro == 0)
+ RL_UNSETSTATE(RL_STATE_MACROINPUT);
+}
+
+/* Add a character to the macro being built. */
+void
+_rl_add_macro_char (int c)
+{
+ if (current_macro_index + 1 >= current_macro_size)
+ {
+ if (current_macro == 0)
+ current_macro = (char *)xmalloc (current_macro_size = 25);
+ else
+ current_macro = (char *)xrealloc (current_macro, current_macro_size += 25);
+ }
+
+ current_macro[current_macro_index++] = c;
+ current_macro[current_macro_index] = '\0';
+}
+
+void
+_rl_kill_kbd_macro (void)
+{
+ if (current_macro)
+ {
+ xfree (current_macro);
+ current_macro = (char *) NULL;
+ }
+ current_macro_size = current_macro_index = 0;
+
+ FREE (rl_executing_macro);
+ rl_executing_macro = (char *) NULL;
+ executing_macro_index = 0;
+
+ RL_UNSETSTATE(RL_STATE_MACRODEF);
+}
+
+/* Begin defining a keyboard macro.
+ Keystrokes are recorded as they are executed.
+ End the definition with rl_end_kbd_macro ().
+ If a numeric argument was explicitly typed, then append this
+ definition to the end of the existing macro, and start by
+ re-executing the existing macro. */
+int
+rl_start_kbd_macro (int ignore1, int ignore2)
+{
+ if (RL_ISSTATE (RL_STATE_MACRODEF))
+ {
+ _rl_abort_internal ();
+ return 1;
+ }
+
+ if (rl_explicit_arg)
+ {
+ if (current_macro)
+ _rl_with_macro_input (savestring (current_macro));
+ }
+ else
+ current_macro_index = 0;
+
+ RL_SETSTATE(RL_STATE_MACRODEF);
+ return 0;
+}
+
+/* Stop defining a keyboard macro.
+ A numeric argument says to execute the macro right now,
+ that many times, counting the definition as the first time. */
+int
+rl_end_kbd_macro (int count, int ignore)
+{
+ if (RL_ISSTATE (RL_STATE_MACRODEF) == 0)
+ {
+ _rl_abort_internal ();
+ return 1;
+ }
+
+ current_macro_index -= rl_key_sequence_length;
+ if (current_macro_index < 0)
+ current_macro_index = 0;
+ current_macro[current_macro_index] = '\0';
+
+ RL_UNSETSTATE(RL_STATE_MACRODEF);
+
+ return (rl_call_last_kbd_macro (--count, 0));
+}
+
+/* Execute the most recently defined keyboard macro.
+ COUNT says how many times to execute it. */
+int
+rl_call_last_kbd_macro (int count, int ignore)
+{
+ if (current_macro == 0)
+ _rl_abort_internal ();
+
+ if (RL_ISSTATE (RL_STATE_MACRODEF))
+ {
+ rl_ding (); /* no recursive macros */
+ current_macro[--current_macro_index] = '\0'; /* erase this char */
+ return 0;
+ }
+
+ while (count--)
+ _rl_with_macro_input (savestring (current_macro));
+ return 0;
+}
+
+int
+rl_print_last_kbd_macro (int count, int ignore)
+{
+ char *m;
+
+ if (current_macro == 0)
+ {
+ rl_ding ();
+ return 0;
+ }
+ m = _rl_untranslate_macro_value (current_macro, 1);
+ rl_crlf ();
+ printf ("%s", m);
+ fflush (stdout);
+ rl_crlf ();
+ FREE (m);
+ rl_forced_update_display ();
+ rl_display_fixed = 1;
+
+ return 0;
+}
+
+void
+rl_push_macro_input (char *macro)
+{
+ _rl_with_macro_input (macro);
+}
diff --git a/third_party/readline/mbutil.c b/third_party/readline/mbutil.c
new file mode 100644
index 000000000..2f7931feb
--- /dev/null
+++ b/third_party/readline/mbutil.c
@@ -0,0 +1,524 @@
+/* mbutil.c -- readline multibyte character utility functions */
+
+/* Copyright (C) 2001-2021 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+#include
+#include "posixjmp.h"
+
+#if defined (HAVE_UNISTD_H)
+# include /* for _POSIX_VERSION */
+#endif /* HAVE_UNISTD_H */
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#include
+#include
+
+/* System-specific feature definitions and include files. */
+#include "rldefs.h"
+#include "rlmbutil.h"
+
+#if defined (TIOCSTAT_IN_SYS_IOCTL)
+# include
+#endif /* TIOCSTAT_IN_SYS_IOCTL */
+
+/* Some standard library routines. */
+#include "readline.h"
+
+#include "rlprivate.h"
+#include "xmalloc.h"
+
+/* Declared here so it can be shared between the readline and history
+ libraries. */
+#if defined (HANDLE_MULTIBYTE)
+int rl_byte_oriented = 0;
+#else
+int rl_byte_oriented = 1;
+#endif
+
+/* Ditto */
+int _rl_utf8locale = 0;
+
+/* **************************************************************** */
+/* */
+/* Multibyte Character Utility Functions */
+/* */
+/* **************************************************************** */
+
+#if defined(HANDLE_MULTIBYTE)
+
+/* **************************************************************** */
+/* */
+/* UTF-8 specific Character Utility Functions */
+/* */
+/* **************************************************************** */
+
+/* Return the length in bytes of the possibly-multibyte character beginning
+ at S. Encoding is UTF-8. */
+static int
+_rl_utf8_mblen (const char *s, size_t n)
+{
+ unsigned char c, c1, c2, c3;
+
+ if (s == 0)
+ return (0); /* no shift states */
+ if (n <= 0)
+ return (-1);
+
+ c = (unsigned char)*s;
+ if (c < 0x80)
+ return (c != 0);
+ if (c >= 0xc2)
+ {
+ c1 = (unsigned char)s[1];
+ if (c < 0xe0)
+ {
+ if (n == 1)
+ return -2;
+ if (n >= 2 && (c1 ^ 0x80) < 0x40)
+ return 2;
+ }
+ else if (c < 0xf0)
+ {
+ if (n == 1)
+ return -2;
+ if ((c1 ^ 0x80) < 0x40
+ && (c >= 0xe1 || c1 >= 0xa0)
+ && (c != 0xed || c1 < 0xa0))
+ {
+ if (n == 2)
+ return -2;
+ c2 = (unsigned char)s[2];
+ if ((c2 ^ 0x80) < 0x40)
+ return 3;
+ }
+ }
+ else if (c < 0xf4)
+ {
+ if (n == 1)
+ return -2;
+ if (((c1 ^ 0x80) < 0x40)
+ && (c >= 0xf1 || c1 >= 0x90)
+ && (c < 0xf4 || (c == 0xf4 && c1 < 0x90)))
+ {
+ if (n == 2)
+ return -2;
+ c2 = (unsigned char)s[2];
+ if ((c2 ^ 0x80) < 0x40)
+ {
+ if (n == 3)
+ return -2;
+ c3 = (unsigned char)s[3];
+ if ((c3 ^ 0x80) < 0x40)
+ return 4;
+ }
+ }
+ }
+ }
+ /* invalid or incomplete multibyte character */
+ return -1;
+}
+
+static int
+_rl_find_next_mbchar_internal (char *string, int seed, int count, int find_non_zero)
+{
+ size_t tmp, len;
+ mbstate_t ps;
+ int point;
+ WCHAR_T wc;
+
+ tmp = 0;
+
+ memset(&ps, 0, sizeof (mbstate_t));
+ if (seed < 0)
+ seed = 0;
+ if (count <= 0)
+ return seed;
+
+ point = seed + _rl_adjust_point (string, seed, &ps);
+ /* if _rl_adjust_point returns -1, the character or string is invalid.
+ treat as a byte. */
+ if (point == seed - 1) /* invalid */
+ return seed + 1;
+
+ /* if this is true, means that seed was not pointing to a byte indicating
+ the beginning of a multibyte character. Correct the point and consume
+ one char. */
+ if (seed < point)
+ count--;
+
+ while (count > 0)
+ {
+ len = strlen (string + point);
+ if (len == 0)
+ break;
+ if (_rl_utf8locale && UTF8_SINGLEBYTE(string[point]))
+ {
+ tmp = 1;
+ wc = (WCHAR_T) string[point];
+ memset(&ps, 0, sizeof(mbstate_t));
+ }
+ else
+ tmp = MBRTOWC (&wc, string+point, len, &ps);
+ if (MB_INVALIDCH ((size_t)tmp))
+ {
+ /* invalid bytes. assume a byte represents a character */
+ point++;
+ count--;
+ /* reset states. */
+ memset(&ps, 0, sizeof(mbstate_t));
+ }
+ else if (MB_NULLWCH (tmp))
+ break; /* found wide '\0' */
+ else
+ {
+ /* valid bytes */
+ point += tmp;
+ if (find_non_zero)
+ {
+ if (WCWIDTH (wc) == 0)
+ continue;
+ else
+ count--;
+ }
+ else
+ count--;
+ }
+ }
+
+ if (find_non_zero)
+ {
+ tmp = MBRTOWC (&wc, string + point, strlen (string + point), &ps);
+ while (MB_NULLWCH (tmp) == 0 && MB_INVALIDCH (tmp) == 0 && WCWIDTH (wc) == 0)
+ {
+ point += tmp;
+ tmp = MBRTOWC (&wc, string + point, strlen (string + point), &ps);
+ }
+ }
+
+ return point;
+}
+
+static inline int
+_rl_test_nonzero (char *string, int ind, int len)
+{
+ size_t tmp;
+ WCHAR_T wc;
+ mbstate_t ps;
+
+ memset (&ps, 0, sizeof (mbstate_t));
+ tmp = MBRTOWC (&wc, string + ind, len - ind, &ps);
+ /* treat invalid multibyte sequences as non-zero-width */
+ return (MB_INVALIDCH (tmp) || MB_NULLWCH (tmp) || WCWIDTH (wc) > 0);
+}
+
+/* experimental -- needs to handle zero-width characters better */
+static int
+_rl_find_prev_utf8char (char *string, int seed, int find_non_zero)
+{
+ char *s;
+ unsigned char b;
+ int save, prev;
+ size_t len;
+
+ if (find_non_zero)
+ len = RL_STRLEN (string);
+
+ prev = seed - 1;
+ while (prev >= 0)
+ {
+ b = (unsigned char)string[prev];
+ if (UTF8_SINGLEBYTE (b))
+ return (prev);
+
+ save = prev;
+
+ /* Move back until we're not in the middle of a multibyte char */
+ if (UTF8_MBCHAR (b))
+ {
+ while (prev > 0 && (b = (unsigned char)string[--prev]) && UTF8_MBCHAR (b))
+ ;
+ }
+
+ if (UTF8_MBFIRSTCHAR (b))
+ {
+ if (find_non_zero)
+ {
+ if (_rl_test_nonzero (string, prev, len))
+ return (prev);
+ else /* valid but WCWIDTH (wc) == 0 */
+ prev = prev - 1;
+ }
+ else
+ return (prev);
+ }
+ else
+ return (save); /* invalid utf-8 multibyte sequence */
+ }
+
+ return ((prev < 0) ? 0 : prev);
+}
+
+/*static*/ int
+_rl_find_prev_mbchar_internal (char *string, int seed, int find_non_zero)
+{
+ mbstate_t ps;
+ int prev, non_zero_prev, point, length;
+ size_t tmp;
+ WCHAR_T wc;
+
+ if (_rl_utf8locale)
+ return (_rl_find_prev_utf8char (string, seed, find_non_zero));
+
+ memset(&ps, 0, sizeof(mbstate_t));
+ length = strlen(string);
+
+ if (seed < 0)
+ return 0;
+ else if (length < seed)
+ return length;
+
+ prev = non_zero_prev = point = 0;
+ while (point < seed)
+ {
+ if (_rl_utf8locale && UTF8_SINGLEBYTE(string[point]))
+ {
+ tmp = 1;
+ wc = (WCHAR_T) string[point];
+ memset(&ps, 0, sizeof(mbstate_t));
+ }
+ else
+ tmp = MBRTOWC (&wc, string + point, length - point, &ps);
+ if (MB_INVALIDCH ((size_t)tmp))
+ {
+ /* in this case, bytes are invalid or too short to compose
+ multibyte char, so assume that the first byte represents
+ a single character anyway. */
+ tmp = 1;
+ /* clear the state of the byte sequence, because
+ in this case effect of mbstate is undefined */
+ memset(&ps, 0, sizeof (mbstate_t));
+
+ /* Since we're assuming that this byte represents a single
+ non-zero-width character, don't forget about it. */
+ prev = point;
+ }
+ else if (MB_NULLWCH (tmp))
+ break; /* Found '\0' char. Can this happen? */
+ else
+ {
+ if (find_non_zero)
+ {
+ if (WCWIDTH (wc) != 0)
+ prev = point;
+ }
+ else
+ prev = point;
+ }
+
+ point += tmp;
+ }
+
+ return prev;
+}
+
+/* return the number of bytes parsed from the multibyte sequence starting
+ at src, if a non-L'\0' wide character was recognized. It returns 0,
+ if a L'\0' wide character was recognized. It returns (size_t)(-1),
+ if an invalid multibyte sequence was encountered. It returns (size_t)(-2)
+ if it couldn't parse a complete multibyte character. */
+int
+_rl_get_char_len (char *src, mbstate_t *ps)
+{
+ size_t tmp, l;
+ int mb_cur_max;
+
+ /* Look at no more than MB_CUR_MAX characters */
+ l = (size_t)strlen (src);
+ if (_rl_utf8locale && l > 0 && UTF8_SINGLEBYTE(*src))
+ tmp = (*src != 0) ? 1 : 0;
+ else
+ {
+ mb_cur_max = MB_CUR_MAX;
+ tmp = mbrlen((const char *)src, (l < mb_cur_max) ? l : mb_cur_max, ps);
+ }
+ if (tmp == (size_t)(-2))
+ {
+ /* too short to compose multibyte char */
+ if (ps)
+ memset (ps, 0, sizeof(mbstate_t));
+ return -2;
+ }
+ else if (tmp == (size_t)(-1))
+ {
+ /* invalid to compose multibyte char */
+ /* initialize the conversion state */
+ if (ps)
+ memset (ps, 0, sizeof(mbstate_t));
+ return -1;
+ }
+ else if (tmp == (size_t)0)
+ return 0;
+ else
+ return (int)tmp;
+}
+
+/* compare the specified two characters. If the characters matched,
+ return 1. Otherwise return 0. */
+int
+_rl_compare_chars (char *buf1, int pos1, mbstate_t *ps1, char *buf2, int pos2, mbstate_t *ps2)
+{
+ int i, w1, w2;
+
+ if ((w1 = _rl_get_char_len (&buf1[pos1], ps1)) <= 0 ||
+ (w2 = _rl_get_char_len (&buf2[pos2], ps2)) <= 0 ||
+ (w1 != w2) ||
+ (buf1[pos1] != buf2[pos2]))
+ return 0;
+
+ for (i = 1; i < w1; i++)
+ if (buf1[pos1+i] != buf2[pos2+i])
+ return 0;
+
+ return 1;
+}
+
+/* adjust pointed byte and find mbstate of the point of string.
+ adjusted point will be point <= adjusted_point, and returns
+ differences of the byte(adjusted_point - point).
+ if point is invalid (point < 0 || more than string length),
+ it returns -1 */
+int
+_rl_adjust_point (char *string, int point, mbstate_t *ps)
+{
+ size_t tmp;
+ int length, pos;
+
+ tmp = 0;
+ pos = 0;
+ length = strlen(string);
+ if (point < 0)
+ return -1;
+ if (length < point)
+ return -1;
+
+ while (pos < point)
+ {
+ if (_rl_utf8locale && UTF8_SINGLEBYTE(string[pos]))
+ tmp = 1;
+ else
+ tmp = mbrlen (string + pos, length - pos, ps);
+ if (MB_INVALIDCH ((size_t)tmp))
+ {
+ /* in this case, bytes are invalid or too short to compose
+ multibyte char, so assume that the first byte represents
+ a single character anyway. */
+ pos++;
+ /* clear the state of the byte sequence, because
+ in this case effect of mbstate is undefined */
+ if (ps)
+ memset (ps, 0, sizeof (mbstate_t));
+ }
+ else if (MB_NULLWCH (tmp))
+ pos++;
+ else
+ pos += tmp;
+ }
+
+ return (pos - point);
+}
+
+int
+_rl_is_mbchar_matched (char *string, int seed, int end, char *mbchar, int length)
+{
+ int i;
+
+ if ((end - seed) < length)
+ return 0;
+
+ for (i = 0; i < length; i++)
+ if (string[seed + i] != mbchar[i])
+ return 0;
+ return 1;
+}
+
+WCHAR_T
+_rl_char_value (char *buf, int ind)
+{
+ size_t tmp;
+ WCHAR_T wc;
+ mbstate_t ps;
+ int l;
+
+ if (MB_LEN_MAX == 1 || rl_byte_oriented)
+ return ((WCHAR_T) buf[ind]);
+ if (_rl_utf8locale && UTF8_SINGLEBYTE(buf[ind]))
+ return ((WCHAR_T) buf[ind]);
+ l = strlen (buf);
+ if (ind >= l - 1)
+ return ((WCHAR_T) buf[ind]);
+ if (l < ind) /* Sanity check */
+ l = strlen (buf+ind);
+ memset (&ps, 0, sizeof (mbstate_t));
+ tmp = MBRTOWC (&wc, buf + ind, l - ind, &ps);
+ if (MB_INVALIDCH (tmp) || MB_NULLWCH (tmp))
+ return ((WCHAR_T) buf[ind]);
+ return wc;
+}
+#endif /* HANDLE_MULTIBYTE */
+
+/* Find next `count' characters started byte point of the specified seed.
+ If flags is MB_FIND_NONZERO, we look for non-zero-width multibyte
+ characters. */
+#undef _rl_find_next_mbchar
+int
+_rl_find_next_mbchar (char *string, int seed, int count, int flags)
+{
+#if defined (HANDLE_MULTIBYTE)
+ return _rl_find_next_mbchar_internal (string, seed, count, flags);
+#else
+ return (seed + count);
+#endif
+}
+
+/* Find previous character started byte point of the specified seed.
+ Returned point will be point <= seed. If flags is MB_FIND_NONZERO,
+ we look for non-zero-width multibyte characters. */
+#undef _rl_find_prev_mbchar
+int
+_rl_find_prev_mbchar (char *string, int seed, int flags)
+{
+#if defined (HANDLE_MULTIBYTE)
+ return _rl_find_prev_mbchar_internal (string, seed, flags);
+#else
+ return ((seed == 0) ? seed : seed - 1);
+#endif
+}
diff --git a/third_party/readline/misc.c b/third_party/readline/misc.c
new file mode 100644
index 000000000..ab01e10cf
--- /dev/null
+++ b/third_party/readline/misc.c
@@ -0,0 +1,781 @@
+/* misc.c -- miscellaneous bindable readline functions. */
+
+/* Copyright (C) 1987-2022 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#if defined (HAVE_UNISTD_H)
+# include
+#endif /* HAVE_UNISTD_H */
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#if defined (HAVE_LOCALE_H)
+# include
+#endif
+
+#include
+
+/* System-specific feature definitions and include files. */
+#include "rldefs.h"
+#include "rlmbutil.h"
+
+/* Some standard library routines. */
+#include "readline.h"
+#include "history.h"
+
+#include "rlprivate.h"
+#include "histlib.h"
+#include "rlshell.h"
+#include "xmalloc.h"
+
+static int rl_digit_loop (void);
+static void _rl_history_set_point (void);
+
+/* If non-zero, rl_get_previous_history and rl_get_next_history attempt
+ to preserve the value of rl_point from line to line. */
+int _rl_history_preserve_point = 0;
+
+_rl_arg_cxt _rl_argcxt;
+
+/* Saved target point for when _rl_history_preserve_point is set. Special
+ value of -1 means that point is at the end of the line. */
+int _rl_history_saved_point = -1;
+
+/* **************************************************************** */
+/* */
+/* Numeric Arguments */
+/* */
+/* **************************************************************** */
+
+int
+_rl_arg_overflow (void)
+{
+ if (rl_numeric_arg > 1000000)
+ {
+ _rl_argcxt = 0;
+ rl_explicit_arg = rl_numeric_arg = 0;
+ rl_ding ();
+ rl_restore_prompt ();
+ rl_clear_message ();
+ RL_UNSETSTATE(RL_STATE_NUMERICARG);
+ return 1;
+ }
+ return 0;
+}
+
+void
+_rl_arg_init (void)
+{
+ rl_save_prompt ();
+ _rl_argcxt = 0;
+ RL_SETSTATE(RL_STATE_NUMERICARG);
+}
+
+int
+_rl_arg_getchar (void)
+{
+ int c;
+
+ rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg);
+ RL_SETSTATE(RL_STATE_MOREINPUT);
+ c = rl_read_key ();
+ RL_UNSETSTATE(RL_STATE_MOREINPUT);
+
+ return c;
+}
+
+/* Process C as part of the current numeric argument. Return -1 if the
+ argument should be aborted, 0 if we should not read any more chars, and
+ 1 if we should continue to read chars. */
+int
+_rl_arg_dispatch (_rl_arg_cxt cxt, int c)
+{
+ int key, r;
+
+ key = c;
+
+ /* If we see a key bound to `universal-argument' after seeing digits,
+ it ends the argument but is otherwise ignored. */
+ if (c >= 0 && _rl_keymap[c].type == ISFUNC && _rl_keymap[c].function == rl_universal_argument)
+ {
+ if ((cxt & NUM_SAWDIGITS) == 0)
+ {
+ rl_numeric_arg *= 4;
+ return 1;
+ }
+ else if (RL_ISSTATE (RL_STATE_CALLBACK))
+ {
+ _rl_argcxt |= NUM_READONE;
+ return 0; /* XXX */
+ }
+ else
+ {
+ key = _rl_bracketed_read_key ();
+ rl_restore_prompt ();
+ rl_clear_message ();
+ RL_UNSETSTATE(RL_STATE_NUMERICARG);
+ if (key < 0)
+ return -1;
+ return (_rl_dispatch (key, _rl_keymap));
+ }
+ }
+
+ c = UNMETA (c);
+
+ if (_rl_digit_p (c))
+ {
+ r = _rl_digit_value (c);
+ rl_numeric_arg = rl_explicit_arg ? (rl_numeric_arg * 10) + r : r;
+ rl_explicit_arg = 1;
+ _rl_argcxt |= NUM_SAWDIGITS;
+ }
+ else if (c == '-' && rl_explicit_arg == 0)
+ {
+ rl_numeric_arg = 1;
+ _rl_argcxt |= NUM_SAWMINUS;
+ rl_arg_sign = -1;
+ }
+ else
+ {
+ /* Make M-- command equivalent to M--1 command. */
+ if ((_rl_argcxt & NUM_SAWMINUS) && rl_numeric_arg == 1 && rl_explicit_arg == 0)
+ rl_explicit_arg = 1;
+ rl_restore_prompt ();
+ rl_clear_message ();
+ RL_UNSETSTATE(RL_STATE_NUMERICARG);
+
+ r = _rl_dispatch (key, _rl_keymap);
+ if (RL_ISSTATE (RL_STATE_CALLBACK))
+ {
+ /* At worst, this will cause an extra redisplay. Otherwise,
+ we have to wait until the next character comes in. */
+ if (rl_done == 0)
+ (*rl_redisplay_function) ();
+ r = 0;
+ }
+ return r;
+ }
+
+ return 1;
+}
+
+/* Handle C-u style numeric args, as well as M--, and M-digits. */
+static int
+rl_digit_loop (void)
+{
+ int c, r;
+
+ while (1)
+ {
+ if (_rl_arg_overflow ())
+ return 1;
+
+ c = _rl_arg_getchar ();
+
+ if (c < 0)
+ {
+ _rl_abort_internal ();
+ return -1;
+ }
+
+ r = _rl_arg_dispatch (_rl_argcxt, c);
+ if (r <= 0 || (RL_ISSTATE (RL_STATE_NUMERICARG) == 0))
+ break;
+ }
+
+ return r;
+}
+
+/* Create a default argument. */
+void
+_rl_reset_argument (void)
+{
+ rl_numeric_arg = rl_arg_sign = 1;
+ rl_explicit_arg = 0;
+ _rl_argcxt = 0;
+}
+
+/* Start a numeric argument with initial value KEY */
+int
+rl_digit_argument (int ignore, int key)
+{
+ _rl_arg_init ();
+ if (RL_ISSTATE (RL_STATE_CALLBACK))
+ {
+ _rl_arg_dispatch (_rl_argcxt, key);
+ rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg);
+ return 0;
+ }
+ else
+ {
+ rl_execute_next (key);
+ return (rl_digit_loop ());
+ }
+}
+
+/* C-u, universal argument. Multiply the current argument by 4.
+ Read a key. If the key has nothing to do with arguments, then
+ dispatch on it. If the key is the abort character then abort. */
+int
+rl_universal_argument (int count, int key)
+{
+ _rl_arg_init ();
+ rl_numeric_arg *= 4;
+
+ return (RL_ISSTATE (RL_STATE_CALLBACK) ? 0 : rl_digit_loop ());
+}
+
+int
+_rl_arg_callback (_rl_arg_cxt cxt)
+{
+ int c, r;
+
+ c = _rl_arg_getchar ();
+ if (c < 0)
+ return (1); /* EOF */
+
+ if (_rl_argcxt & NUM_READONE)
+ {
+ _rl_argcxt &= ~NUM_READONE;
+ rl_restore_prompt ();
+ rl_clear_message ();
+ RL_UNSETSTATE(RL_STATE_NUMERICARG);
+ rl_execute_next (c);
+ return 0;
+ }
+
+ r = _rl_arg_dispatch (cxt, c);
+ if (r > 0)
+ rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg);
+ return (r != 1);
+}
+
+/* What to do when you abort reading an argument. */
+int
+rl_discard_argument (void)
+{
+ rl_ding ();
+ rl_clear_message ();
+ _rl_reset_argument ();
+
+ return 0;
+}
+
+/* **************************************************************** */
+/* */
+/* History Utilities */
+/* */
+/* **************************************************************** */
+
+/* We already have a history library, and that is what we use to control
+ the history features of readline. This is our local interface to
+ the history mechanism. */
+
+/* While we are editing the history, this is the saved
+ version of the original line. */
+HIST_ENTRY *_rl_saved_line_for_history = (HIST_ENTRY *)NULL;
+
+/* Set the history pointer back to the last entry in the history. */
+void
+_rl_start_using_history (void)
+{
+ using_history ();
+ if (_rl_saved_line_for_history)
+ _rl_free_saved_history_line ();
+ _rl_saved_line_for_history = (HIST_ENTRY *)NULL;
+ _rl_history_search_pos = -99; /* some random invalid history position */
+}
+
+/* Free the contents (and containing structure) of a HIST_ENTRY. */
+void
+_rl_free_history_entry (HIST_ENTRY *entry)
+{
+ if (entry == 0)
+ return;
+
+ FREE (entry->line);
+ FREE (entry->timestamp);
+
+ xfree (entry);
+}
+
+/* Perhaps put back the current line if it has changed. */
+int
+rl_maybe_replace_line (void)
+{
+ HIST_ENTRY *temp;
+
+ temp = current_history ();
+ /* If the current line has changed, save the changes. */
+ if (temp && ((UNDO_LIST *)(temp->data) != rl_undo_list))
+ {
+ temp = replace_history_entry (where_history (), rl_line_buffer, (histdata_t)rl_undo_list);
+ xfree (temp->line);
+ FREE (temp->timestamp);
+ xfree (temp);
+ }
+ return 0;
+}
+
+/* Restore the _rl_saved_line_for_history if there is one. */
+int
+rl_maybe_unsave_line (void)
+{
+ if (_rl_saved_line_for_history)
+ {
+ /* Can't call with `1' because rl_undo_list might point to an undo
+ list from a history entry, as in rl_replace_from_history() below. */
+ rl_replace_line (_rl_saved_line_for_history->line, 0);
+ rl_undo_list = (UNDO_LIST *)_rl_saved_line_for_history->data;
+
+ /* Doesn't free `data'. */
+ _rl_free_history_entry (_rl_saved_line_for_history);
+ _rl_saved_line_for_history = (HIST_ENTRY *)NULL;
+ rl_point = rl_end; /* rl_replace_line sets rl_end */
+ }
+ else
+ rl_ding ();
+ return 0;
+}
+
+/* Save the current line in _rl_saved_line_for_history. */
+int
+rl_maybe_save_line (void)
+{
+ if (_rl_saved_line_for_history == 0)
+ {
+ _rl_saved_line_for_history = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
+ _rl_saved_line_for_history->line = savestring (rl_line_buffer);
+ _rl_saved_line_for_history->timestamp = (char *)NULL;
+ _rl_saved_line_for_history->data = (char *)rl_undo_list;
+ }
+
+ return 0;
+}
+
+int
+_rl_free_saved_history_line (void)
+{
+ UNDO_LIST *orig;
+
+ if (_rl_saved_line_for_history)
+ {
+ if (rl_undo_list && rl_undo_list == (UNDO_LIST *)_rl_saved_line_for_history->data)
+ rl_undo_list = 0;
+ /* Have to free this separately because _rl_free_history entry can't:
+ it doesn't know whether or not this has application data. Only the
+ callers that know this is _rl_saved_line_for_history can know that
+ it's an undo list. */
+ if (_rl_saved_line_for_history->data)
+ _rl_free_undo_list ((UNDO_LIST *)_rl_saved_line_for_history->data);
+ _rl_free_history_entry (_rl_saved_line_for_history);
+ _rl_saved_line_for_history = (HIST_ENTRY *)NULL;
+ }
+ return 0;
+}
+
+static void
+_rl_history_set_point (void)
+{
+ rl_point = (_rl_history_preserve_point && _rl_history_saved_point != -1)
+ ? _rl_history_saved_point
+ : rl_end;
+ if (rl_point > rl_end)
+ rl_point = rl_end;
+
+#if defined (VI_MODE)
+ if (rl_editing_mode == vi_mode && _rl_keymap != vi_insertion_keymap)
+ rl_point = 0;
+#endif /* VI_MODE */
+
+ if (rl_editing_mode == emacs_mode)
+ rl_mark = (rl_point == rl_end ? 0 : rl_end);
+}
+
+void
+rl_replace_from_history (HIST_ENTRY *entry, int flags)
+{
+ /* Can't call with `1' because rl_undo_list might point to an undo list
+ from a history entry, just like we're setting up here. */
+ rl_replace_line (entry->line, 0);
+ rl_undo_list = (UNDO_LIST *)entry->data;
+ rl_point = rl_end;
+ rl_mark = 0;
+
+#if defined (VI_MODE)
+ if (rl_editing_mode == vi_mode)
+ {
+ rl_point = 0;
+ rl_mark = rl_end;
+ }
+#endif
+}
+
+/* Process and free undo lists attached to each history entry prior to the
+ current entry, inclusive, reverting each line to its saved state. This
+ is destructive, and state about the current line is lost. This is not
+ intended to be called while actively editing, and the current line is
+ not assumed to have been added to the history list. */
+void
+_rl_revert_previous_lines (void)
+{
+ int hpos;
+ HIST_ENTRY *entry;
+ UNDO_LIST *ul, *saved_undo_list;
+ char *lbuf;
+
+ lbuf = savestring (rl_line_buffer);
+ saved_undo_list = rl_undo_list;
+ hpos = where_history ();
+
+ entry = (hpos == history_length) ? previous_history () : current_history ();
+ while (entry)
+ {
+ if (ul = (UNDO_LIST *)entry->data)
+ {
+ if (ul == saved_undo_list)
+ saved_undo_list = 0;
+ /* Set up rl_line_buffer and other variables from history entry */
+ rl_replace_from_history (entry, 0); /* entry->line is now current */
+ entry->data = 0; /* entry->data is now current undo list */
+ /* Undo all changes to this history entry */
+ while (rl_undo_list)
+ rl_do_undo ();
+ /* And copy the reverted line back to the history entry, preserving
+ the timestamp. */
+ FREE (entry->line);
+ entry->line = savestring (rl_line_buffer);
+ }
+ entry = previous_history ();
+ }
+
+ /* Restore history state */
+ rl_undo_list = saved_undo_list; /* may have been set to null */
+ history_set_pos (hpos);
+
+ /* reset the line buffer */
+ rl_replace_line (lbuf, 0);
+ _rl_set_the_line ();
+
+ /* and clean up */
+ xfree (lbuf);
+}
+
+/* Revert all lines in the history by making sure we are at the end of the
+ history before calling _rl_revert_previous_lines() */
+void
+_rl_revert_all_lines (void)
+{
+ int pos;
+
+ pos = where_history ();
+ using_history ();
+ _rl_revert_previous_lines ();
+ history_set_pos (pos);
+}
+
+/* Free the history list, including private readline data and take care
+ of pointer aliases to history data. Resets rl_undo_list if it points
+ to an UNDO_LIST * saved as some history entry's data member. This
+ should not be called while editing is active. */
+void
+rl_clear_history (void)
+{
+ HIST_ENTRY **hlist, *hent;
+ register int i;
+ UNDO_LIST *ul, *saved_undo_list;
+
+ saved_undo_list = rl_undo_list;
+ hlist = history_list (); /* direct pointer, not copy */
+
+ for (i = 0; i < history_length; i++)
+ {
+ hent = hlist[i];
+ if (ul = (UNDO_LIST *)hent->data)
+ {
+ if (ul == saved_undo_list)
+ saved_undo_list = 0;
+ _rl_free_undo_list (ul);
+ hent->data = 0;
+ }
+ _rl_free_history_entry (hent);
+ }
+
+ history_offset = history_length = 0;
+ rl_undo_list = saved_undo_list; /* should be NULL */
+}
+
+/* **************************************************************** */
+/* */
+/* History Commands */
+/* */
+/* **************************************************************** */
+
+/* Meta-< goes to the start of the history. */
+int
+rl_beginning_of_history (int count, int key)
+{
+ return (rl_get_previous_history (1 + where_history (), key));
+}
+
+/* Meta-> goes to the end of the history. (The current line). */
+int
+rl_end_of_history (int count, int key)
+{
+ rl_maybe_replace_line ();
+ using_history ();
+ rl_maybe_unsave_line ();
+ return 0;
+}
+
+/* Move down to the next history line. */
+int
+rl_get_next_history (int count, int key)
+{
+ HIST_ENTRY *temp;
+
+ if (count < 0)
+ return (rl_get_previous_history (-count, key));
+
+ if (count == 0)
+ return 0;
+
+ rl_maybe_replace_line ();
+
+ /* either not saved by rl_newline or at end of line, so set appropriately. */
+ if (_rl_history_saved_point == -1 && (rl_point || rl_end))
+ _rl_history_saved_point = (rl_point == rl_end) ? -1 : rl_point;
+
+ temp = (HIST_ENTRY *)NULL;
+ while (count)
+ {
+ temp = next_history ();
+ if (!temp)
+ break;
+ --count;
+ }
+
+ if (temp == 0)
+ rl_maybe_unsave_line ();
+ else
+ {
+ rl_replace_from_history (temp, 0);
+ _rl_history_set_point ();
+ }
+ return 0;
+}
+
+/* Get the previous item out of our interactive history, making it the current
+ line. If there is no previous history, just ding. */
+int
+rl_get_previous_history (int count, int key)
+{
+ HIST_ENTRY *old_temp, *temp;
+ int had_saved_line;
+
+ if (count < 0)
+ return (rl_get_next_history (-count, key));
+
+ if (count == 0 || history_list () == 0)
+ return 0;
+
+ /* either not saved by rl_newline or at end of line, so set appropriately. */
+ if (_rl_history_saved_point == -1 && (rl_point || rl_end))
+ _rl_history_saved_point = (rl_point == rl_end) ? -1 : rl_point;
+
+ /* If we don't have a line saved, then save this one. */
+ had_saved_line = _rl_saved_line_for_history != 0;
+ rl_maybe_save_line ();
+
+ /* If the current line has changed, save the changes. */
+ rl_maybe_replace_line ();
+
+ temp = old_temp = (HIST_ENTRY *)NULL;
+ while (count)
+ {
+ temp = previous_history ();
+ if (temp == 0)
+ break;
+
+ old_temp = temp;
+ --count;
+ }
+
+ /* If there was a large argument, and we moved back to the start of the
+ history, that is not an error. So use the last value found. */
+ if (!temp && old_temp)
+ temp = old_temp;
+
+ if (temp == 0)
+ {
+ if (had_saved_line == 0)
+ _rl_free_saved_history_line ();
+ rl_ding ();
+ }
+ else
+ {
+ rl_replace_from_history (temp, 0);
+ _rl_history_set_point ();
+ }
+
+ return 0;
+}
+
+/* With an argument, move back that many history lines, else move to the
+ beginning of history. */
+int
+rl_fetch_history (int count, int c)
+{
+ int wanted, nhist;
+
+ /* Giving an argument of n means we want the nth command in the history
+ file. The command number is interpreted the same way that the bash
+ `history' command does it -- that is, giving an argument count of 450
+ to this command would get the command listed as number 450 in the
+ output of `history'. */
+ if (rl_explicit_arg)
+ {
+ nhist = history_base + where_history ();
+ /* Negative arguments count back from the end of the history list. */
+ wanted = (count >= 0) ? nhist - count : -count;
+
+ if (wanted <= 0 || wanted >= nhist)
+ {
+ /* In vi mode, we don't change the line with an out-of-range
+ argument, as for the `G' command. */
+ if (rl_editing_mode == vi_mode)
+ rl_ding ();
+ else
+ rl_beginning_of_history (0, 0);
+ }
+ else
+ rl_get_previous_history (wanted, c);
+ }
+ else
+ rl_beginning_of_history (count, 0);
+
+ return (0);
+}
+
+/* The equivalent of the Korn shell C-o operate-and-get-next-history-line
+ editing command. */
+
+/* This could stand to be global to the readline library */
+static rl_hook_func_t *_rl_saved_internal_startup_hook = 0;
+static int saved_history_logical_offset = -1;
+
+#define HISTORY_FULL() (history_is_stifled () && history_length >= history_max_entries)
+
+static int
+set_saved_history ()
+{
+ int absolute_offset, count;
+
+ if (saved_history_logical_offset >= 0)
+ {
+ absolute_offset = saved_history_logical_offset - history_base;
+ count = where_history () - absolute_offset;
+ rl_get_previous_history (count, 0);
+ }
+ saved_history_logical_offset = -1;
+ _rl_internal_startup_hook = _rl_saved_internal_startup_hook;
+
+ return (0);
+}
+
+int
+rl_operate_and_get_next (int count, int c)
+{
+ /* Accept the current line. */
+ rl_newline (1, c);
+
+ saved_history_logical_offset = rl_explicit_arg ? count : where_history () + history_base + 1;
+
+ _rl_saved_internal_startup_hook = _rl_internal_startup_hook;
+ _rl_internal_startup_hook = set_saved_history;
+
+ return 0;
+}
+
+/* **************************************************************** */
+/* */
+/* Editing Modes */
+/* */
+/* **************************************************************** */
+/* How to toggle back and forth between editing modes. */
+int
+rl_vi_editing_mode (int count, int key)
+{
+#if defined (VI_MODE)
+ _rl_set_insert_mode (RL_IM_INSERT, 1); /* vi mode ignores insert mode */
+ rl_editing_mode = vi_mode;
+ rl_vi_insert_mode (1, key);
+#endif /* VI_MODE */
+
+ return 0;
+}
+
+int
+rl_emacs_editing_mode (int count, int key)
+{
+ rl_editing_mode = emacs_mode;
+ _rl_set_insert_mode (RL_IM_INSERT, 1); /* emacs mode default is insert mode */
+ _rl_keymap = emacs_standard_keymap;
+
+ if (_rl_show_mode_in_prompt)
+ _rl_reset_prompt ();
+
+ return 0;
+}
+
+/* Function for the rest of the library to use to set insert/overwrite mode. */
+void
+_rl_set_insert_mode (int im, int force)
+{
+#ifdef CURSOR_MODE
+ _rl_set_cursor (im, force);
+#endif
+
+ rl_insert_mode = im;
+}
+
+/* Toggle overwrite mode. A positive explicit argument selects overwrite
+ mode. A negative or zero explicit argument selects insert mode. */
+int
+rl_overwrite_mode (int count, int key)
+{
+ if (rl_explicit_arg == 0)
+ _rl_set_insert_mode (rl_insert_mode ^ 1, 0);
+ else if (count > 0)
+ _rl_set_insert_mode (RL_IM_OVERWRITE, 0);
+ else
+ _rl_set_insert_mode (RL_IM_INSERT, 0);
+
+ return 0;
+}
diff --git a/third_party/readline/nls.c b/third_party/readline/nls.c
new file mode 100644
index 000000000..4c5853b4b
--- /dev/null
+++ b/third_party/readline/nls.c
@@ -0,0 +1,345 @@
+/* nls.c -- skeletal internationalization code. */
+
+/* Copyright (C) 1996-2022 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+
+#include
+
+#if defined (HAVE_UNISTD_H)
+# include
+#endif /* HAVE_UNISTD_H */
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#if defined (HAVE_LOCALE_H)
+# include
+#endif
+
+#if defined (HAVE_LANGINFO_CODESET)
+# include
+#endif
+
+#include
+
+#include "rldefs.h"
+#include "readline.h"
+#include "rlshell.h"
+#include "rlprivate.h"
+#include "xmalloc.h"
+
+static int utf8locale (char *);
+
+#define RL_DEFAULT_LOCALE "C"
+static char *_rl_current_locale = 0;
+
+#if !defined (HAVE_SETLOCALE)
+/* A list of legal values for the LANG or LC_CTYPE environment variables.
+ If a locale name in this list is the value for the LC_ALL, LC_CTYPE,
+ or LANG environment variable (using the first of those with a value),
+ readline eight-bit mode is enabled. */
+static char *legal_lang_values[] =
+{
+ "iso88591",
+ "iso88592",
+ "iso88593",
+ "iso88594",
+ "iso88595",
+ "iso88596",
+ "iso88597",
+ "iso88598",
+ "iso88599",
+ "iso885910",
+ "koi8r",
+ "utf8",
+ 0
+};
+
+static char *normalize_codeset (char *);
+#endif /* !HAVE_SETLOCALE */
+
+static char *find_codeset (char *, size_t *);
+
+static char *_rl_get_locale_var (const char *);
+
+static char *
+_rl_get_locale_var (const char *v)
+{
+ char *lspec;
+
+ lspec = sh_get_env_value ("LC_ALL");
+ if (lspec == 0 || *lspec == 0)
+ lspec = sh_get_env_value (v);
+ if (lspec == 0 || *lspec == 0)
+ lspec = sh_get_env_value ("LANG");
+
+ return lspec;
+}
+
+static int
+utf8locale (char *lspec)
+{
+ char *cp;
+ size_t len;
+
+#if HAVE_LANGINFO_CODESET
+ (void)len;
+ cp = nl_langinfo (CODESET);
+ return (STREQ (cp, "UTF-8") || STREQ (cp, "utf8"));
+#else
+ cp = find_codeset (lspec, &len);
+
+ if (cp == 0 || len < 4 || len > 5)
+ return 0;
+ return ((len == 5) ? strncmp (cp, "UTF-8", len) == 0 : strncmp (cp, "utf8", 4) == 0);
+#endif
+}
+
+/* Query the right environment variables and call setlocale() to initialize
+ the C library locale settings. */
+char *
+_rl_init_locale (void)
+{
+ char *ret, *lspec;
+
+ /* Set the LC_CTYPE locale category from environment variables. */
+ lspec = _rl_get_locale_var ("LC_CTYPE");
+ /* Since _rl_get_locale_var queries the right environment variables,
+ we query the current locale settings with setlocale(), and, if
+ that doesn't return anything, we set lspec to the empty string to
+ force the subsequent call to setlocale() to define the `native'
+ environment. */
+#if defined (HAVE_SETLOCALE)
+ if (lspec == 0 || *lspec == 0)
+ lspec = setlocale (LC_CTYPE, (char *)NULL);
+ if (lspec == 0)
+ lspec = "";
+ ret = setlocale (LC_CTYPE, lspec); /* ok, since it does not change locale */
+#else
+ ret = (lspec == 0 || *lspec == 0) ? RL_DEFAULT_LOCALE : lspec;
+#endif
+
+ _rl_utf8locale = (ret && *ret) ? utf8locale (ret) : 0;
+
+ _rl_current_locale = savestring (ret);
+ return ret;
+}
+
+/* If we have setlocale(3), just check the current LC_CTYPE category
+ value (passed as LOCALESTR), and go into eight-bit mode if it's not "C"
+ or "POSIX". If FORCE is non-zero, we reset the locale variables to values
+ appropriate for the C locale if the locale is "C" or "POSIX". FORCE is 0
+ when this is called from _rl_init_eightbit, since we're modifying the
+ default initial values and don't need to change anything else. If we
+ don't have setlocale(3), we check the codeset portion of LOCALESTR against
+ a set of known values and go into eight-bit mode if it matches one of those.
+ Returns 1 if we set eight-bit (multibyte) mode. */
+static int
+_rl_set_localevars (char *localestr, int force)
+{
+#if defined (HAVE_SETLOCALE)
+ if (localestr && *localestr && (localestr[0] != 'C' || localestr[1]) && (STREQ (localestr, "POSIX") == 0))
+ {
+ _rl_meta_flag = 1;
+ _rl_convert_meta_chars_to_ascii = 0;
+ _rl_output_meta_chars = 1;
+ return (1);
+ }
+ else if (force)
+ {
+ /* Default "C" locale settings. */
+ _rl_meta_flag = 0;
+ _rl_convert_meta_chars_to_ascii = 1;
+ _rl_output_meta_chars = 0;
+ return (0);
+ }
+ else
+ return (0);
+
+#else /* !HAVE_SETLOCALE */
+ char *t;
+ int i;
+
+ /* We don't have setlocale. Finesse it. Check the environment for the
+ appropriate variables and set eight-bit mode if they have the right
+ values. */
+ if (localestr == 0 || (t = normalize_codeset (localestr)) == 0)
+ return (0);
+ for (i = 0; t && legal_lang_values[i]; i++)
+ if (STREQ (t, legal_lang_values[i]))
+ {
+ _rl_meta_flag = 1;
+ _rl_convert_meta_chars_to_ascii = 0;
+ _rl_output_meta_chars = 1;
+ break;
+ }
+
+ if (force && legal_lang_values[i] == 0) /* didn't find it */
+ {
+ /* Default "C" locale settings. */
+ _rl_meta_flag = 0;
+ _rl_convert_meta_chars_to_ascii = 1;
+ _rl_output_meta_chars = 0;
+ }
+
+ _rl_utf8locale = *t ? STREQ (t, "utf8") : 0;
+
+ xfree (t);
+ return (legal_lang_values[i] ? 1 : 0);
+#endif /* !HAVE_SETLOCALE */
+}
+
+/* Check for LC_ALL, LC_CTYPE, and LANG and use the first with a value
+ to decide the defaults for 8-bit character input and output. Returns
+ 1 if we set eight-bit mode. */
+int
+_rl_init_eightbit (void)
+{
+ char *t, *ol;
+
+ ol = _rl_current_locale;
+ t = _rl_init_locale (); /* resets _rl_current_locale, returns static pointer */
+ xfree (ol);
+
+ return (_rl_set_localevars (t, 0));
+}
+
+#if !defined (HAVE_SETLOCALE)
+static char *
+normalize_codeset (char *codeset)
+{
+ size_t namelen, i;
+ int len, all_digits;
+ char *wp, *retval;
+
+ codeset = find_codeset (codeset, &namelen);
+
+ if (codeset == 0)
+ return (codeset);
+
+ all_digits = 1;
+ for (len = 0, i = 0; i < namelen; i++)
+ {
+ if (ISALNUM ((unsigned char)codeset[i]))
+ {
+ len++;
+ all_digits &= _rl_digit_p (codeset[i]);
+ }
+ }
+
+ retval = (char *)malloc ((all_digits ? 3 : 0) + len + 1);
+ if (retval == 0)
+ return ((char *)0);
+
+ wp = retval;
+ /* Add `iso' to beginning of an all-digit codeset */
+ if (all_digits)
+ {
+ *wp++ = 'i';
+ *wp++ = 's';
+ *wp++ = 'o';
+ }
+
+ for (i = 0; i < namelen; i++)
+ if (ISALPHA ((unsigned char)codeset[i]))
+ *wp++ = _rl_to_lower (codeset[i]);
+ else if (_rl_digit_p (codeset[i]))
+ *wp++ = codeset[i];
+ *wp = '\0';
+
+ return retval;
+}
+#endif /* !HAVE_SETLOCALE */
+
+/* Isolate codeset portion of locale specification. */
+static char *
+find_codeset (char *name, size_t *lenp)
+{
+ char *cp, *language, *result;
+
+ cp = language = name;
+ result = (char *)0;
+
+ while (*cp && *cp != '_' && *cp != '@' && *cp != '+' && *cp != ',')
+ cp++;
+
+ /* This does not make sense: language has to be specified. As
+ an exception we allow the variable to contain only the codeset
+ name. Perhaps there are funny codeset names. */
+ if (language == cp)
+ {
+ *lenp = strlen (language);
+ result = language;
+ }
+ else
+ {
+ /* Next is the territory. */
+ if (*cp == '_')
+ do
+ ++cp;
+ while (*cp && *cp != '.' && *cp != '@' && *cp != '+' && *cp != ',' && *cp != '_');
+
+ /* Now, finally, is the codeset. */
+ result = cp;
+ if (*cp == '.')
+ do
+ ++cp;
+ while (*cp && *cp != '@');
+
+ if (cp - result > 2)
+ {
+ result++;
+ *lenp = cp - result;
+ }
+ else
+ {
+ *lenp = strlen (language);
+ result = language;
+ }
+ }
+
+ return result;
+}
+
+void
+_rl_reset_locale (void)
+{
+ char *ol, *nl;
+
+ /* This should not be NULL; _rl_init_eightbit sets it on the first call to
+ readline() or rl_initialize(). */
+ ol = _rl_current_locale;
+ nl = _rl_init_locale (); /* resets _rl_current_locale */
+
+ if ((ol == 0 && nl) || (ol && nl && (STREQ (ol, nl) == 0)))
+ (void)_rl_set_localevars (nl, 1);
+
+ xfree (ol);
+}
diff --git a/third_party/readline/parens.c b/third_party/readline/parens.c
new file mode 100644
index 000000000..a25f519f5
--- /dev/null
+++ b/third_party/readline/parens.c
@@ -0,0 +1,185 @@
+/* parens.c -- implementation of matching parentheses feature. */
+
+/* Copyright (C) 1987, 1989, 1992-2015, 2017, 2021 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (__TANDEM)
+# include
+#endif
+
+#include "rlconf.h"
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+#include
+
+#if defined (HAVE_UNISTD_H)
+# include
+#endif
+
+#include "posixselect.h"
+
+#if defined (HAVE_STRING_H)
+# include
+#else /* !HAVE_STRING_H */
+# include
+#endif /* !HAVE_STRING_H */
+
+#if !defined (strchr) && !defined (__STDC__)
+extern char *strchr (), *strrchr ();
+#endif /* !strchr && !__STDC__ */
+
+#include "readline.h"
+#include "rlprivate.h"
+
+static int find_matching_open (char *, int, int);
+
+/* Non-zero means try to blink the matching open parenthesis when the
+ close parenthesis is inserted. */
+int rl_blink_matching_paren = 0;
+
+static int _paren_blink_usec = 500000;
+
+/* Change emacs_standard_keymap to have bindings for paren matching when
+ ON_OR_OFF is 1, change them back to self_insert when ON_OR_OFF == 0. */
+void
+_rl_enable_paren_matching (int on_or_off)
+{
+ if (on_or_off)
+ {
+ /* ([{ */
+ rl_bind_key_in_map (')', rl_insert_close, emacs_standard_keymap);
+ rl_bind_key_in_map (']', rl_insert_close, emacs_standard_keymap);
+ rl_bind_key_in_map ('}', rl_insert_close, emacs_standard_keymap);
+
+#if defined (VI_MODE)
+ /* ([{ */
+ rl_bind_key_in_map (')', rl_insert_close, vi_insertion_keymap);
+ rl_bind_key_in_map (']', rl_insert_close, vi_insertion_keymap);
+ rl_bind_key_in_map ('}', rl_insert_close, vi_insertion_keymap);
+#endif
+ }
+ else
+ {
+ /* ([{ */
+ rl_bind_key_in_map (')', rl_insert, emacs_standard_keymap);
+ rl_bind_key_in_map (']', rl_insert, emacs_standard_keymap);
+ rl_bind_key_in_map ('}', rl_insert, emacs_standard_keymap);
+
+#if defined (VI_MODE)
+ /* ([{ */
+ rl_bind_key_in_map (')', rl_insert, vi_insertion_keymap);
+ rl_bind_key_in_map (']', rl_insert, vi_insertion_keymap);
+ rl_bind_key_in_map ('}', rl_insert, vi_insertion_keymap);
+#endif
+ }
+}
+
+int
+rl_set_paren_blink_timeout (int u)
+{
+ int o;
+
+ o = _paren_blink_usec;
+ if (u > 0)
+ _paren_blink_usec = u;
+ return (o);
+}
+
+int
+rl_insert_close (int count, int invoking_key)
+{
+ if (rl_explicit_arg || !rl_blink_matching_paren)
+ _rl_insert_char (count, invoking_key);
+ else
+ {
+#if defined (HAVE_SELECT)
+ int orig_point, match_point, ready;
+ struct timeval timer;
+ fd_set readfds;
+ (void)ready;
+
+ _rl_insert_char (1, invoking_key);
+ (*rl_redisplay_function) ();
+ match_point =
+ find_matching_open (rl_line_buffer, rl_point - 2, invoking_key);
+
+ /* Emacs might message or ring the bell here, but I don't. */
+ if (match_point < 0)
+ return 1;
+
+ FD_ZERO (&readfds);
+ FD_SET (fileno (rl_instream), &readfds);
+ USEC_TO_TIMEVAL (_paren_blink_usec, timer);
+
+ orig_point = rl_point;
+ rl_point = match_point;
+ (*rl_redisplay_function) ();
+# if defined (RL_TIMEOUT_USE_SELECT)
+ ready = _rl_timeout_select (1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer, NULL);
+# else
+ ready = select (1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer);
+# endif
+ rl_point = orig_point;
+#else /* !HAVE_SELECT */
+ _rl_insert_char (count, invoking_key);
+#endif /* !HAVE_SELECT */
+ }
+ return 0;
+}
+
+static int
+find_matching_open (char *string, int from, int closer)
+{
+ register int i;
+ int opener, level, delimiter;
+
+ switch (closer)
+ {
+ case ']': opener = '['; break;
+ case '}': opener = '{'; break;
+ case ')': opener = '('; break;
+ default:
+ return (-1);
+ }
+
+ level = 1; /* The closer passed in counts as 1. */
+ delimiter = 0; /* Delimited state unknown. */
+
+ for (i = from; i > -1; i--)
+ {
+ if (delimiter && (string[i] == delimiter))
+ delimiter = 0;
+ else if (rl_basic_quote_characters && strchr (rl_basic_quote_characters, string[i]))
+ delimiter = string[i];
+ else if (!delimiter && (string[i] == closer))
+ level++;
+ else if (!delimiter && (string[i] == opener))
+ level--;
+
+ if (!level)
+ break;
+ }
+ return (i);
+}
diff --git a/third_party/readline/parse-colors.c b/third_party/readline/parse-colors.c
new file mode 100644
index 000000000..15af1b884
--- /dev/null
+++ b/third_party/readline/parse-colors.c
@@ -0,0 +1,440 @@
+/* `dir', `vdir' and `ls' directory listing programs for GNU.
+
+ Modified by Chet Ramey for Readline.
+
+ Copyright (C) 1985, 1988, 1990-1991, 1995-2010, 2012, 2017
+ Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see . */
+
+/* Written by Richard Stallman and David MacKenzie. */
+
+/* Color support by Peter Anvin and Dennis
+ Flaherty based on original patches by
+ Greg Lee . */
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+
+// strdup() / strcpy()
+#if defined (HAVE_STRING_H)
+# include
+#else /* !HAVE_STRING_H */
+# include
+#endif /* !HAVE_STRING_H */
+
+// abort()
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#include "rldefs.h" // STREQ, savestring
+#include "readline.h"
+#include "rlprivate.h"
+#include "rlshell.h"
+#include "xmalloc.h"
+
+#include "colors.h"
+#include "parse-colors.h"
+
+#if defined (COLOR_SUPPORT)
+
+static bool get_funky_string (char **dest, const char **src, bool equals_end, size_t *output_count);
+
+struct bin_str _rl_color_indicator[] =
+ {
+ { LEN_STR_PAIR ("\033[") }, // lc: Left of color sequence
+ { LEN_STR_PAIR ("m") }, // rc: Right of color sequence
+ { 0, NULL }, // ec: End color (replaces lc+no+rc)
+ { LEN_STR_PAIR ("0") }, // rs: Reset to ordinary colors
+ { 0, NULL }, // no: Normal
+ { 0, NULL }, // fi: File: default
+ { LEN_STR_PAIR ("01;34") }, // di: Directory: bright blue
+ { LEN_STR_PAIR ("01;36") }, // ln: Symlink: bright cyan
+ { LEN_STR_PAIR ("33") }, // pi: Pipe: yellow/brown
+ { LEN_STR_PAIR ("01;35") }, // so: Socket: bright magenta
+ { LEN_STR_PAIR ("01;33") }, // bd: Block device: bright yellow
+ { LEN_STR_PAIR ("01;33") }, // cd: Char device: bright yellow
+ { 0, NULL }, // mi: Missing file: undefined
+ { 0, NULL }, // or: Orphaned symlink: undefined
+ { LEN_STR_PAIR ("01;32") }, // ex: Executable: bright green
+ { LEN_STR_PAIR ("01;35") }, // do: Door: bright magenta
+ { LEN_STR_PAIR ("37;41") }, // su: setuid: white on red
+ { LEN_STR_PAIR ("30;43") }, // sg: setgid: black on yellow
+ { LEN_STR_PAIR ("37;44") }, // st: sticky: black on blue
+ { LEN_STR_PAIR ("34;42") }, // ow: other-writable: blue on green
+ { LEN_STR_PAIR ("30;42") }, // tw: ow w/ sticky: black on green
+ { LEN_STR_PAIR ("30;41") }, // ca: black on red
+ { 0, NULL }, // mh: disabled by default
+ { LEN_STR_PAIR ("\033[K") }, // cl: clear to end of line
+ };
+
+/* Parse a string as part of the LS_COLORS variable; this may involve
+ decoding all kinds of escape characters. If equals_end is set an
+ unescaped equal sign ends the string, otherwise only a : or \0
+ does. Set *OUTPUT_COUNT to the number of bytes output. Return
+ true if successful.
+
+ The resulting string is *not* null-terminated, but may contain
+ embedded nulls.
+
+ Note that both dest and src are char **; on return they point to
+ the first free byte after the array and the character that ended
+ the input string, respectively. */
+
+static bool
+get_funky_string (char **dest, const char **src, bool equals_end, size_t *output_count) {
+ char num; /* For numerical codes */
+ size_t count; /* Something to count with */
+ enum {
+ ST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX, ST_CARET, ST_END, ST_ERROR
+ } state;
+ const char *p;
+ char *q;
+
+ p = *src; /* We don't want to double-indirect */
+ q = *dest; /* the whole darn time. */
+
+ count = 0; /* No characters counted in yet. */
+ num = 0;
+
+ state = ST_GND; /* Start in ground state. */
+ while (state < ST_END)
+ {
+ switch (state)
+ {
+ case ST_GND: /* Ground state (no escapes) */
+ switch (*p)
+ {
+ case ':':
+ case '\0':
+ state = ST_END; /* End of string */
+ break;
+ case '\\':
+ state = ST_BACKSLASH; /* Backslash scape sequence */
+ ++p;
+ break;
+ case '^':
+ state = ST_CARET; /* Caret escape */
+ ++p;
+ break;
+ case '=':
+ if (equals_end)
+ {
+ state = ST_END; /* End */
+ break;
+ }
+ /* else fall through */
+ default:
+ *(q++) = *(p++);
+ ++count;
+ break;
+ }
+ break;
+
+ case ST_BACKSLASH: /* Backslash escaped character */
+ switch (*p)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ state = ST_OCTAL; /* Octal sequence */
+ num = *p - '0';
+ break;
+ case 'x':
+ case 'X':
+ state = ST_HEX; /* Hex sequence */
+ num = 0;
+ break;
+ case 'a': /* Bell */
+ num = '\a';
+ break;
+ case 'b': /* Backspace */
+ num = '\b';
+ break;
+ case 'e': /* Escape */
+ num = 27;
+ break;
+ case 'f': /* Form feed */
+ num = '\f';
+ break;
+ case 'n': /* Newline */
+ num = '\n';
+ break;
+ case 'r': /* Carriage return */
+ num = '\r';
+ break;
+ case 't': /* Tab */
+ num = '\t';
+ break;
+ case 'v': /* Vtab */
+ num = '\v';
+ break;
+ case '?': /* Delete */
+ num = 127;
+ break;
+ case '_': /* Space */
+ num = ' ';
+ break;
+ case '\0': /* End of string */
+ state = ST_ERROR; /* Error! */
+ break;
+ default: /* Escaped character like \ ^ : = */
+ num = *p;
+ break;
+ }
+ if (state == ST_BACKSLASH)
+ {
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ }
+ ++p;
+ break;
+
+ case ST_OCTAL: /* Octal sequence */
+ if (*p < '0' || *p > '7')
+ {
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ }
+ else
+ num = (num << 3) + (*(p++) - '0');
+ break;
+
+ case ST_HEX: /* Hex sequence */
+ switch (*p)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ num = (num << 4) + (*(p++) - '0');
+ break;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ num = (num << 4) + (*(p++) - 'a') + 10;
+ break;
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ num = (num << 4) + (*(p++) - 'A') + 10;
+ break;
+ default:
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ break;
+ }
+ break;
+
+ case ST_CARET: /* Caret escape */
+ state = ST_GND; /* Should be the next state... */
+ if (*p >= '@' && *p <= '~')
+ {
+ *(q++) = *(p++) & 037;
+ ++count;
+ }
+ else if (*p == '?')
+ {
+ *(q++) = 127;
+ ++count;
+ }
+ else
+ state = ST_ERROR;
+ break;
+
+ default:
+ /* should we ? */
+ /* abort (); no, we should not */
+ state = ST_ERROR;
+ break;
+ }
+ }
+
+ *dest = q;
+ *src = p;
+ *output_count = count;
+
+ return state != ST_ERROR;
+}
+#endif /* COLOR_SUPPORT */
+
+void _rl_parse_colors(void)
+{
+#if defined (COLOR_SUPPORT)
+ const char *p; /* Pointer to character being parsed */
+ char *buf; /* color_buf buffer pointer */
+ int state; /* State of parser */
+ int ind_no; /* Indicator number */
+ char label[3]; /* Indicator label */
+ COLOR_EXT_TYPE *ext; /* Extension we are working on */
+
+ p = sh_get_env_value ("LS_COLORS");
+ if (p == 0 || *p == '\0')
+ {
+ _rl_color_ext_list = NULL;
+ return;
+ }
+
+ ext = NULL;
+ strcpy (label, "??");
+
+ /* This is an overly conservative estimate, but any possible
+ LS_COLORS string will *not* generate a color_buf longer than
+ itself, so it is a safe way of allocating a buffer in
+ advance. */
+ buf = color_buf = savestring (p);
+
+ state = 1;
+ while (state > 0)
+ {
+ switch (state)
+ {
+ case 1: /* First label character */
+ switch (*p)
+ {
+ case ':':
+ ++p;
+ break;
+
+ case '*':
+ /* Allocate new extension block and add to head of
+ linked list (this way a later definition will
+ override an earlier one, which can be useful for
+ having terminal-specific defs override global). */
+
+ ext = (COLOR_EXT_TYPE *)xmalloc (sizeof *ext);
+ ext->next = _rl_color_ext_list;
+ _rl_color_ext_list = ext;
+
+ ++p;
+ ext->ext.string = buf;
+
+ state = (get_funky_string (&buf, &p, true, &ext->ext.len)
+ ? 4 : -1);
+ break;
+
+ case '\0':
+ state = 0; /* Done! */
+ break;
+
+ default: /* Assume it is file type label */
+ label[0] = *(p++);
+ state = 2;
+ break;
+ }
+ break;
+
+ case 2: /* Second label character */
+ if (*p)
+ {
+ label[1] = *(p++);
+ state = 3;
+ }
+ else
+ state = -1; /* Error */
+ break;
+
+ case 3: /* Equal sign after indicator label */
+ state = -1; /* Assume failure... */
+ if (*(p++) == '=')/* It *should* be... */
+ {
+ for (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no)
+ {
+ if (STREQ (label, indicator_name[ind_no]))
+ {
+ _rl_color_indicator[ind_no].string = buf;
+ state = (get_funky_string (&buf, &p, false,
+ &_rl_color_indicator[ind_no].len)
+ ? 1 : -1);
+ break;
+ }
+ }
+ if (state == -1)
+ {
+ _rl_errmsg ("LS_COLORS: unrecognized prefix: %s", label);
+ /* recover from an unrecognized prefix */
+ while (p && *p && *p != ':')
+ p++;
+ if (p && *p == ':')
+ state = 1;
+ else if (p && *p == 0)
+ state = 0;
+ }
+ }
+ break;
+
+ case 4: /* Equal sign after *.ext */
+ if (*(p++) == '=')
+ {
+ ext->seq.string = buf;
+ state = (get_funky_string (&buf, &p, false, &ext->seq.len)
+ ? 1 : -1);
+ }
+ else
+ state = -1;
+ /* XXX - recover here as with an unrecognized prefix? */
+ if (state == -1 && ext->ext.string)
+ _rl_errmsg ("LS_COLORS: syntax error: %s", ext->ext.string);
+ break;
+ }
+ }
+
+ if (state < 0)
+ {
+ COLOR_EXT_TYPE *e;
+ COLOR_EXT_TYPE *e2;
+
+ _rl_errmsg ("unparsable value for LS_COLORS environment variable");
+ free (color_buf);
+ for (e = _rl_color_ext_list; e != NULL; /* empty */)
+ {
+ e2 = e;
+ e = e->next;
+ free (e2);
+ }
+ _rl_color_ext_list = NULL;
+ _rl_colored_stats = 0; /* can't have colored stats without colors */
+ }
+#else /* !COLOR_SUPPORT */
+ ;
+#endif /* !COLOR_SUPPORT */
+}
diff --git a/third_party/readline/parse-colors.h b/third_party/readline/parse-colors.h
new file mode 100644
index 000000000..aef86f784
--- /dev/null
+++ b/third_party/readline/parse-colors.h
@@ -0,0 +1,46 @@
+/* `dir', `vdir' and `ls' directory listing programs for GNU.
+
+ Modified by Chet Ramey for Readline.
+
+ Copyright (C) 1985, 1988, 1990-1991, 1995-2010, 2012 Free Software Foundation,
+ Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see . */
+
+/* Written by Richard Stallman and David MacKenzie. */
+
+/* Color support by Peter Anvin and Dennis
+ Flaherty based on original patches by
+ Greg Lee . */
+
+#ifndef _PARSE_COLORS_H_
+#define _PARSE_COLORS_H_
+
+#include "readline.h"
+
+#define LEN_STR_PAIR(s) sizeof (s) - 1, s
+
+void _rl_parse_colors (void);
+
+static const char *const indicator_name[]=
+ {
+ "lc", "rc", "ec", "rs", "no", "fi", "di", "ln", "pi", "so",
+ "bd", "cd", "mi", "or", "ex", "do", "su", "sg", "st",
+ "ow", "tw", "ca", "mh", "cl", NULL
+ };
+
+/* Buffer for color sequences */
+static char *color_buf;
+
+#endif /* !_PARSE_COLORS_H_ */
diff --git a/third_party/readline/posixdir.h b/third_party/readline/posixdir.h
new file mode 100644
index 000000000..b737bd7d1
--- /dev/null
+++ b/third_party/readline/posixdir.h
@@ -0,0 +1,71 @@
+/* posixdir.h -- Posix directory reading includes and defines. */
+
+/* Copyright (C) 1987,1991,2012,2019,2021 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash 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 Bash. If not, see .
+*/
+
+/* This file should be included instead of or . */
+
+#if !defined (_POSIXDIR_H_)
+#define _POSIXDIR_H_
+
+#if defined (HAVE_DIRENT_H)
+# include
+# if defined (HAVE_STRUCT_DIRENT_D_NAMLEN)
+# define D_NAMLEN(d) ((d)->d_namlen)
+# else
+# define D_NAMLEN(d) (strlen ((d)->d_name))
+# endif /* !HAVE_STRUCT_DIRENT_D_NAMLEN */
+#else
+# if defined (HAVE_SYS_NDIR_H)
+# include
+# endif
+# if defined (HAVE_SYS_DIR_H)
+# include
+# endif
+# if defined (HAVE_NDIR_H)
+# include
+# endif
+# if !defined (dirent)
+# define dirent direct
+# endif /* !dirent */
+# define D_NAMLEN(d) ((d)->d_namlen)
+#endif /* !HAVE_DIRENT_H */
+
+/* The bash code fairly consistently uses d_fileno; make sure it's available */
+#if defined (HAVE_STRUCT_DIRENT_D_INO) && !defined (HAVE_STRUCT_DIRENT_D_FILENO)
+# define d_fileno d_ino
+#endif
+
+/* Posix does not require that the d_ino field be present, and some
+ systems do not provide it. */
+#if !defined (HAVE_STRUCT_DIRENT_D_INO) || defined (BROKEN_DIRENT_D_INO)
+# define REAL_DIR_ENTRY(dp) 1
+#else
+# define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
+#endif /* _POSIX_SOURCE */
+
+#if defined (HAVE_STRUCT_DIRENT_D_INO) && !defined (BROKEN_DIRENT_D_INO)
+# define D_INO_AVAILABLE
+#endif
+
+/* Signal the rest of the code that it can safely use dirent.d_fileno */
+#if defined (D_INO_AVAILABLE) || defined (HAVE_STRUCT_DIRENT_D_FILENO)
+# define D_FILENO_AVAILABLE 1
+#endif
+
+#endif /* !_POSIXDIR_H_ */
diff --git a/third_party/readline/posixjmp.h b/third_party/readline/posixjmp.h
new file mode 100644
index 000000000..9c7e99ed4
--- /dev/null
+++ b/third_party/readline/posixjmp.h
@@ -0,0 +1,46 @@
+/* posixjmp.h -- wrapper for setjmp.h with changes for POSIX systems. */
+
+/* Copyright (C) 1987,1991-2015 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash 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 Bash. If not, see .
+*/
+
+#ifndef _POSIXJMP_H_
+#define _POSIXJMP_H_
+
+#include
+
+/* This *must* be included *after* config.h */
+
+#if defined (HAVE_POSIX_SIGSETJMP)
+# define procenv_t sigjmp_buf
+
+# define setjmp_nosigs(x) sigsetjmp((x), 0)
+# define setjmp_sigs(x) sigsetjmp((x), 1)
+
+# define _rl_longjmp(x, n) siglongjmp((x), (n))
+# define sh_longjmp(x, n) siglongjmp((x), (n))
+#else
+# define procenv_t jmp_buf
+
+# define setjmp_nosigs setjmp
+# define setjmp_sigs setjmp
+
+# define _rl_longjmp(x, n) longjmp((x), (n))
+# define sh_longjmp(x, n) longjmp((x), (n))
+#endif
+
+#endif /* _POSIXJMP_H_ */
diff --git a/third_party/readline/posixselect.h b/third_party/readline/posixselect.h
new file mode 100644
index 000000000..da6a1ace0
--- /dev/null
+++ b/third_party/readline/posixselect.h
@@ -0,0 +1,47 @@
+/* posixselect.h -- wrapper for select(2) includes and definitions */
+
+/* Copyright (C) 2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash 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 Bash. If not, see .
+*/
+
+#ifndef _POSIXSELECT_H_
+#define _POSIXSELECT_H_
+
+#if defined (FD_SET) && !defined (HAVE_SELECT)
+# define HAVE_SELECT 1
+#endif
+
+#if defined (HAVE_SELECT)
+# if !defined (HAVE_SYS_SELECT_H) || !defined (M_UNIX)
+# include
+# endif
+#endif /* HAVE_SELECT */
+#if defined (HAVE_SYS_SELECT_H)
+# include
+#endif
+
+#ifndef USEC_PER_SEC
+# define USEC_PER_SEC 1000000
+#endif
+
+#define USEC_TO_TIMEVAL(us, tv) \
+do { \
+ (tv).tv_sec = (us) / USEC_PER_SEC; \
+ (tv).tv_usec = (us) % USEC_PER_SEC; \
+} while (0)
+
+#endif /* _POSIXSELECT_H_ */
diff --git a/third_party/readline/posixstat.h b/third_party/readline/posixstat.h
new file mode 100644
index 000000000..b60778606
--- /dev/null
+++ b/third_party/readline/posixstat.h
@@ -0,0 +1,162 @@
+/* posixstat.h -- Posix stat(2) definitions for systems that
+ don't have them. */
+
+/* Copyright (C) 1987-2019 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash 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 Bash. If not, see .
+*/
+
+/* This file should be included instead of .
+ It relies on the local sys/stat.h to work though. */
+#if !defined (_POSIXSTAT_H_)
+#define _POSIXSTAT_H_
+
+#include
+
+#if defined (STAT_MACROS_BROKEN)
+# undef S_ISBLK
+# undef S_ISCHR
+# undef S_ISDIR
+# undef S_ISFIFO
+# undef S_ISREG
+# undef S_ISLNK
+#endif /* STAT_MACROS_BROKEN */
+
+/* These are guaranteed to work only on isc386 */
+#if !defined (S_IFDIR) && !defined (S_ISDIR)
+# define S_IFDIR 0040000
+#endif /* !S_IFDIR && !S_ISDIR */
+#if !defined (S_IFMT)
+# define S_IFMT 0170000
+#endif /* !S_IFMT */
+
+/* Posix 1003.1 5.6.1.1 file types */
+
+/* Some Posix-wannabe systems define _S_IF* macros instead of S_IF*, but
+ do not provide the S_IS* macros that Posix requires. */
+
+#if defined (_S_IFMT) && !defined (S_IFMT)
+#define S_IFMT _S_IFMT
+#endif
+#if defined (_S_IFIFO) && !defined (S_IFIFO)
+#define S_IFIFO _S_IFIFO
+#endif
+#if defined (_S_IFCHR) && !defined (S_IFCHR)
+#define S_IFCHR _S_IFCHR
+#endif
+#if defined (_S_IFDIR) && !defined (S_IFDIR)
+#define S_IFDIR _S_IFDIR
+#endif
+#if defined (_S_IFBLK) && !defined (S_IFBLK)
+#define S_IFBLK _S_IFBLK
+#endif
+#if defined (_S_IFREG) && !defined (S_IFREG)
+#define S_IFREG _S_IFREG
+#endif
+#if defined (_S_IFLNK) && !defined (S_IFLNK)
+#define S_IFLNK _S_IFLNK
+#endif
+#if defined (_S_IFSOCK) && !defined (S_IFSOCK)
+#define S_IFSOCK _S_IFSOCK
+#endif
+
+/* Test for each symbol individually and define the ones necessary (some
+ systems claiming Posix compatibility define some but not all). */
+
+#if defined (S_IFBLK) && !defined (S_ISBLK)
+#define S_ISBLK(m) (((m)&S_IFMT) == S_IFBLK) /* block device */
+#endif
+
+#if defined (S_IFCHR) && !defined (S_ISCHR)
+#define S_ISCHR(m) (((m)&S_IFMT) == S_IFCHR) /* character device */
+#endif
+
+#if defined (S_IFDIR) && !defined (S_ISDIR)
+#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) /* directory */
+#endif
+
+#if defined (S_IFREG) && !defined (S_ISREG)
+#define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) /* file */
+#endif
+
+#if defined (S_IFIFO) && !defined (S_ISFIFO)
+#define S_ISFIFO(m) (((m)&S_IFMT) == S_IFIFO) /* fifo - named pipe */
+#endif
+
+#if defined (S_IFLNK) && !defined (S_ISLNK)
+#define S_ISLNK(m) (((m)&S_IFMT) == S_IFLNK) /* symbolic link */
+#endif
+
+#if defined (S_IFSOCK) && !defined (S_ISSOCK)
+#define S_ISSOCK(m) (((m)&S_IFMT) == S_IFSOCK) /* socket */
+#endif
+
+/*
+ * POSIX 1003.1 5.6.1.2 File Modes
+ */
+
+#if !defined (S_IRWXU)
+# if !defined (S_IREAD)
+# define S_IREAD 00400
+# define S_IWRITE 00200
+# define S_IEXEC 00100
+# endif /* S_IREAD */
+
+# if !defined (S_IRUSR)
+# define S_IRUSR S_IREAD /* read, owner */
+# define S_IWUSR S_IWRITE /* write, owner */
+# define S_IXUSR S_IEXEC /* execute, owner */
+
+# define S_IRGRP (S_IREAD >> 3) /* read, group */
+# define S_IWGRP (S_IWRITE >> 3) /* write, group */
+# define S_IXGRP (S_IEXEC >> 3) /* execute, group */
+
+# define S_IROTH (S_IREAD >> 6) /* read, other */
+# define S_IWOTH (S_IWRITE >> 6) /* write, other */
+# define S_IXOTH (S_IEXEC >> 6) /* execute, other */
+# endif /* !S_IRUSR */
+
+# define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
+# define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
+# define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
+#else /* !S_IRWXU */
+ /* S_IRWXU is defined, but "group" and "other" bits might not be
+ (happens in certain versions of MinGW). */
+# if !defined (S_IRGRP)
+# define S_IRGRP (S_IREAD >> 3) /* read, group */
+# define S_IWGRP (S_IWRITE >> 3) /* write, group */
+# define S_IXGRP (S_IEXEC >> 3) /* execute, group */
+# endif /* !S_IRGRP */
+
+# if !defined (S_IROTH)
+# define S_IROTH (S_IREAD >> 6) /* read, other */
+# define S_IWOTH (S_IWRITE >> 6) /* write, other */
+# define S_IXOTH (S_IEXEC >> 6) /* execute, other */
+# endif /* !S_IROTH */
+# if !defined (S_IRWXG)
+# define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
+# endif
+# if !defined (S_IRWXO)
+# define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
+# endif
+#endif /* !S_IRWXU */
+
+/* These are non-standard, but are used in builtins.c$symbolic_umask() */
+#define S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH)
+#define S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH)
+#define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
+
+#endif /* _POSIXSTAT_H_ */
diff --git a/third_party/readline/posixtime.h b/third_party/readline/posixtime.h
new file mode 100644
index 000000000..e70ebec67
--- /dev/null
+++ b/third_party/readline/posixtime.h
@@ -0,0 +1,84 @@
+/* posixtime.h -- wrapper for time.h, sys/times.h mess. */
+
+/* Copyright (C) 1999-2021 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash 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 Bash. If not, see .
+*/
+
+#ifndef _POSIXTIME_H_
+#define _POSIXTIME_H_
+
+/* include this after config.h */
+/* Some systems require this, mostly for the definition of `struct timezone'.
+ For example, Dynix/ptx has that definition in rather than
+ sys/time.h */
+#if defined (HAVE_SYS_TIME_H)
+# include
+#endif
+#include
+
+#if !defined (HAVE_SYSCONF) || !defined (_SC_CLK_TCK)
+# if !defined (CLK_TCK)
+# if defined (HZ)
+# define CLK_TCK HZ
+# else
+# define CLK_TCK 60 /* 60HZ */
+# endif
+# endif /* !CLK_TCK */
+#endif /* !HAVE_SYSCONF && !_SC_CLK_TCK */
+
+#if !HAVE_TIMEVAL
+struct timeval
+{
+ time_t tv_sec;
+ long int tv_usec;
+};
+#endif
+
+#if !HAVE_GETTIMEOFDAY
+extern int gettimeofday PARAMS((struct timeval *, void *));
+#endif
+
+/* These exist on BSD systems, at least. */
+#if !defined (timerclear)
+# define timerclear(tvp) do { (tvp)->tv_sec = 0; (tvp)->tv_usec = 0; } while (0)
+#endif
+#if !defined (timerisset)
+# define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
+#endif
+#if !defined (timercmp)
+# define timercmp(a, b, CMP) \
+ (((a)->tv_sec == (b)->tv_sec) ? ((a)->tv_usec CMP (b)->tv_usec) \
+ : ((a)->tv_sec CMP (b)->tv_sec))
+#endif
+
+/* These are non-standard. */
+#if !defined (timerisunset)
+# define timerisunset(tvp) ((tvp)->tv_sec == 0 && (tvp)->tv_usec == 0)
+#endif
+#if !defined (timerset)
+# define timerset(tvp, s, u) do { tvp->tv_sec = s; tvp->tv_usec = u; } while (0)
+#endif
+
+#ifndef TIMEVAL_TO_TIMESPEC
+# define TIMEVAL_TO_TIMESPEC(tv, ts) \
+ do { \
+ (ts)->tv_sec = (tv)->tv_sec; \
+ (ts)->tv_nsec = (tv)->tv_usec * 1000; \
+ } while (0)
+#endif
+
+#endif /* _POSIXTIME_H_ */
diff --git a/third_party/readline/readline.c b/third_party/readline/readline.c
new file mode 100644
index 000000000..802cc0908
--- /dev/null
+++ b/third_party/readline/readline.c
@@ -0,0 +1,1575 @@
+/* readline.c -- a general facility for reading lines of input
+ with emacs style editing and completion. */
+
+/* Copyright (C) 1987-2022 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+#include "posixstat.h"
+#include
+#if defined (HAVE_SYS_FILE_H)
+# include
+#endif /* HAVE_SYS_FILE_H */
+
+#if defined (HAVE_UNISTD_H)
+# include
+#endif /* HAVE_UNISTD_H */
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#if defined (HAVE_LOCALE_H)
+# include
+#endif
+
+#include
+#include "posixjmp.h"
+#include
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+/* System-specific feature definitions and include files. */
+#include "rldefs.h"
+#include "rlmbutil.h"
+
+#if defined (__EMX__)
+# define INCL_DOSPROCESS
+# include
+#endif /* __EMX__ */
+
+/* Some standard library routines. */
+#include "readline.h"
+#include "history.h"
+
+#include "rlprivate.h"
+#include "rlshell.h"
+#include "xmalloc.h"
+
+#if defined (COLOR_SUPPORT)
+# include "parse-colors.h"
+#endif
+
+#ifndef RL_LIBRARY_VERSION
+# define RL_LIBRARY_VERSION "8.1"
+#endif
+
+#ifndef RL_READLINE_VERSION
+# define RL_READLINE_VERSION 0x0801
+#endif
+
+/* Forward declarations used in this file. */
+static char *readline_internal (void);
+static void readline_initialize_everything (void);
+
+static void run_startup_hooks (void);
+
+static void bind_arrow_keys_internal (Keymap);
+static void bind_arrow_keys (void);
+
+static void bind_bracketed_paste_prefix (void);
+
+static void readline_default_bindings (void);
+static void reset_default_bindings (void);
+
+static int _rl_subseq_result (int, Keymap, int, int);
+static int _rl_subseq_getchar (int);
+
+/* **************************************************************** */
+/* */
+/* Line editing input utility */
+/* */
+/* **************************************************************** */
+
+const char *rl_library_version = RL_LIBRARY_VERSION;
+
+int rl_readline_version = RL_READLINE_VERSION;
+
+/* True if this is `real' readline as opposed to some stub substitute. */
+int rl_gnu_readline_p = 1;
+
+/* A pointer to the keymap that is currently in use.
+ By default, it is the standard emacs keymap. */
+Keymap _rl_keymap = emacs_standard_keymap;
+
+/* The current style of editing. */
+int rl_editing_mode = emacs_mode;
+
+/* The current insert mode: input (the default) or overwrite */
+int rl_insert_mode = RL_IM_DEFAULT;
+
+/* Non-zero if we called this function from _rl_dispatch(). It's present
+ so functions can find out whether they were called from a key binding
+ or directly from an application. */
+int rl_dispatching;
+
+/* Non-zero if the previous command was a kill command. */
+int _rl_last_command_was_kill = 0;
+
+/* The current value of the numeric argument specified by the user. */
+int rl_numeric_arg = 1;
+
+/* Non-zero if an argument was typed. */
+int rl_explicit_arg = 0;
+
+/* Temporary value used while generating the argument. */
+int rl_arg_sign = 1;
+
+/* Non-zero means we have been called at least once before. */
+static int rl_initialized;
+
+#if 0
+/* If non-zero, this program is running in an EMACS buffer. */
+static int running_in_emacs;
+#endif
+
+/* Flags word encapsulating the current readline state. */
+unsigned long rl_readline_state = RL_STATE_NONE;
+
+/* The current offset in the current input line. */
+int rl_point;
+
+/* Mark in the current input line. */
+int rl_mark;
+
+/* Length of the current input line. */
+int rl_end;
+
+/* Make this non-zero to return the current input_line. */
+int rl_done;
+
+/* If non-zero when readline_internal returns, it means we found EOF */
+int rl_eof_found = 0;
+
+/* The last function executed by readline. */
+rl_command_func_t *rl_last_func = (rl_command_func_t *)NULL;
+
+/* Top level environment for readline_internal (). */
+procenv_t _rl_top_level;
+
+/* The streams we interact with. */
+FILE *_rl_in_stream, *_rl_out_stream;
+
+/* The names of the streams that we do input and output to. */
+FILE *rl_instream = (FILE *)NULL;
+FILE *rl_outstream = (FILE *)NULL;
+
+/* Non-zero means echo characters as they are read. Defaults to no echo;
+ set to 1 if there is a controlling terminal, we can get its attributes,
+ and the attributes include `echo'. Look at rltty.c:prepare_terminal_settings
+ for the code that sets it. */
+int _rl_echoing_p = 0;
+
+/* Current prompt. */
+char *rl_prompt = (char *)NULL;
+int rl_visible_prompt_length = 0;
+
+/* Set to non-zero by calling application if it has already printed rl_prompt
+ and does not want readline to do it the first time. */
+int rl_already_prompted = 0;
+
+/* The number of characters read in order to type this complete command. */
+int rl_key_sequence_length = 0;
+
+/* If non-zero, then this is the address of a function to call just
+ before readline_internal_setup () prints the first prompt. */
+rl_hook_func_t *rl_startup_hook = (rl_hook_func_t *)NULL;
+
+/* Any readline function can set this and have it run just before the user's
+ rl_startup_hook. */
+rl_hook_func_t *_rl_internal_startup_hook = (rl_hook_func_t *)NULL;
+
+/* If non-zero, this is the address of a function to call just before
+ readline_internal_setup () returns and readline_internal starts
+ reading input characters. */
+rl_hook_func_t *rl_pre_input_hook = (rl_hook_func_t *)NULL;
+
+/* What we use internally. You should always refer to RL_LINE_BUFFER. */
+static char *the_line;
+
+/* The character that can generate an EOF. Really read from
+ the terminal driver... just defaulted here. */
+int _rl_eof_char = CTRL ('D');
+
+/* Non-zero makes this the next keystroke to read. */
+int rl_pending_input = 0;
+
+/* Pointer to a useful terminal name. */
+const char *rl_terminal_name = (const char *)NULL;
+
+/* Non-zero means to always use horizontal scrolling in line display. */
+int _rl_horizontal_scroll_mode = 0;
+
+/* Non-zero means to display an asterisk at the starts of history lines
+ which have been modified. */
+int _rl_mark_modified_lines = 0;
+
+/* The style of `bell' notification preferred. This can be set to NO_BELL,
+ AUDIBLE_BELL, or VISIBLE_BELL. */
+int _rl_bell_preference = AUDIBLE_BELL;
+
+/* String inserted into the line by rl_insert_comment (). */
+char *_rl_comment_begin;
+
+/* Keymap holding the function currently being executed. */
+Keymap rl_executing_keymap;
+
+/* The function currently being executed. */
+rl_command_func_t *_rl_executing_func;
+
+/* Keymap we're currently using to dispatch. */
+Keymap _rl_dispatching_keymap;
+
+/* Non-zero means to erase entire line, including prompt, on empty input lines. */
+int rl_erase_empty_line = 0;
+
+/* Non-zero means to read only this many characters rather than up to a
+ character bound to accept-line. */
+int rl_num_chars_to_read = 0;
+
+/* Line buffer and maintenance. */
+char *rl_line_buffer = (char *)NULL;
+int rl_line_buffer_len = 0;
+
+/* Key sequence `contexts' */
+_rl_keyseq_cxt *_rl_kscxt = 0;
+
+int rl_executing_key;
+char *rl_executing_keyseq = 0;
+int _rl_executing_keyseq_size = 0;
+
+struct _rl_cmd _rl_pending_command;
+struct _rl_cmd *_rl_command_to_execute = (struct _rl_cmd *)NULL;
+
+/* Timeout (specified in milliseconds) when reading characters making up an
+ ambiguous multiple-key sequence */
+int _rl_keyseq_timeout = 500;
+
+#define RESIZE_KEYSEQ_BUFFER() \
+ do \
+ { \
+ if (rl_key_sequence_length + 2 >= _rl_executing_keyseq_size) \
+ { \
+ _rl_executing_keyseq_size += 16; \
+ rl_executing_keyseq = xrealloc (rl_executing_keyseq, _rl_executing_keyseq_size); \
+ } \
+ } \
+ while (0);
+
+/* Forward declarations used by the display, termcap, and history code. */
+
+/* **************************************************************** */
+/* */
+/* `Forward' declarations */
+/* */
+/* **************************************************************** */
+
+/* Non-zero means do not parse any lines other than comments and
+ parser directives. */
+unsigned char _rl_parsing_conditionalized_out = 0;
+
+/* Non-zero means to convert characters with the meta bit set to
+ escape-prefixed characters so we can indirect through
+ emacs_meta_keymap or vi_escape_keymap. */
+int _rl_convert_meta_chars_to_ascii = 1;
+
+/* Non-zero means to output characters with the meta bit set directly
+ rather than as a meta-prefixed escape sequence. */
+int _rl_output_meta_chars = 0;
+
+/* Non-zero means to look at the termios special characters and bind
+ them to equivalent readline functions at startup. */
+int _rl_bind_stty_chars = 1;
+
+/* Non-zero means to go through the history list at every newline (or
+ whenever rl_done is set and readline returns) and revert each line to
+ its initial state. */
+int _rl_revert_all_at_newline = 0;
+
+/* Non-zero means to honor the termios ECHOCTL bit and echo control
+ characters corresponding to keyboard-generated signals. */
+int _rl_echo_control_chars = 1;
+
+/* Non-zero means to prefix the displayed prompt with a character indicating
+ the editing mode: @ for emacs, : for vi-command, + for vi-insert. */
+int _rl_show_mode_in_prompt = 0;
+
+/* Non-zero means to attempt to put the terminal in `bracketed paste mode',
+ where it will prefix pasted text with an escape sequence and send
+ another to mark the end of the paste. */
+int _rl_enable_bracketed_paste = BRACKETED_PASTE_DEFAULT;
+int _rl_enable_active_region = BRACKETED_PASTE_DEFAULT;
+
+/* **************************************************************** */
+/* */
+/* Top Level Functions */
+/* */
+/* **************************************************************** */
+
+/* Non-zero means treat 0200 bit in terminal input as Meta bit. */
+int _rl_meta_flag = 0; /* Forward declaration */
+
+/* Set up the prompt and expand it. Called from readline() and
+ rl_callback_handler_install (). */
+int
+rl_set_prompt (const char *prompt)
+{
+ FREE (rl_prompt);
+ rl_prompt = prompt ? savestring (prompt) : (char *)NULL;
+ rl_display_prompt = rl_prompt ? rl_prompt : "";
+
+ rl_visible_prompt_length = rl_expand_prompt (rl_prompt);
+ return 0;
+}
+
+/* Read a line of input. Prompt with PROMPT. An empty PROMPT means
+ none. A return value of NULL means that EOF was encountered. */
+char *
+readline (const char *prompt)
+{
+ char *value;
+#if 0
+ int in_callback;
+#endif
+
+ /* If we are at EOF return a NULL string. */
+ if (rl_pending_input == EOF)
+ {
+ rl_clear_pending_input ();
+ return ((char *)NULL);
+ }
+
+#if 0
+ /* If readline() is called after installing a callback handler, temporarily
+ turn off the callback state to avoid ensuing messiness. Patch supplied
+ by the gdb folks. XXX -- disabled. This can be fooled and readline
+ left in a strange state by a poorly-timed longjmp. */
+ if (in_callback = RL_ISSTATE (RL_STATE_CALLBACK))
+ RL_UNSETSTATE (RL_STATE_CALLBACK);
+#endif
+
+ rl_set_prompt (prompt);
+
+ rl_initialize ();
+ if (rl_prep_term_function)
+ (*rl_prep_term_function) (_rl_meta_flag);
+
+#if defined (HANDLE_SIGNALS)
+ rl_set_signals ();
+#endif
+
+ value = readline_internal ();
+ if (rl_deprep_term_function)
+ (*rl_deprep_term_function) ();
+
+#if defined (HANDLE_SIGNALS)
+ rl_clear_signals ();
+#endif
+
+#if 0
+ if (in_callback)
+ RL_SETSTATE (RL_STATE_CALLBACK);
+#endif
+
+#if HAVE_DECL_AUDIT_USER_TTY && defined (HAVE_LIBAUDIT_H) && defined (ENABLE_TTY_AUDIT_SUPPORT)
+ if (value)
+ _rl_audit_tty (value);
+#endif
+
+ return (value);
+}
+
+static void
+run_startup_hooks (void)
+{
+ if (rl_startup_hook)
+ (*rl_startup_hook) ();
+
+ if (_rl_internal_startup_hook)
+ (*_rl_internal_startup_hook) ();
+}
+
+#if defined (READLINE_CALLBACKS)
+# define STATIC_CALLBACK
+#else
+# define STATIC_CALLBACK static
+#endif
+
+STATIC_CALLBACK void
+readline_internal_setup (void)
+{
+ char *nprompt;
+
+ _rl_in_stream = rl_instream;
+ _rl_out_stream = rl_outstream;
+
+ /* Enable the meta key only for the duration of readline(), if this
+ terminal has one and the terminal has been initialized */
+ if (_rl_enable_meta & RL_ISSTATE (RL_STATE_TERMPREPPED))
+ _rl_enable_meta_key ();
+
+ run_startup_hooks ();
+
+ rl_deactivate_mark ();
+
+#if defined (VI_MODE)
+ if (rl_editing_mode == vi_mode)
+ rl_vi_insertion_mode (1, 'i'); /* don't want to reset last */
+ else
+#endif /* VI_MODE */
+ if (_rl_show_mode_in_prompt)
+ _rl_reset_prompt ();
+
+ /* If we're not echoing, we still want to at least print a prompt, because
+ rl_redisplay will not do it for us. If the calling application has a
+ custom redisplay function, though, let that function handle it. */
+ if (_rl_echoing_p == 0 && rl_redisplay_function == rl_redisplay)
+ {
+ if (rl_prompt && rl_already_prompted == 0)
+ {
+ nprompt = _rl_strip_prompt (rl_prompt);
+ fprintf (_rl_out_stream, "%s", nprompt);
+ fflush (_rl_out_stream);
+ xfree (nprompt);
+ }
+ }
+ else
+ {
+ if (rl_prompt && rl_already_prompted)
+ rl_on_new_line_with_prompt ();
+ else
+ rl_on_new_line ();
+ (*rl_redisplay_function) ();
+ }
+
+ if (rl_pre_input_hook)
+ (*rl_pre_input_hook) ();
+
+ RL_CHECK_SIGNALS ();
+}
+
+STATIC_CALLBACK char *
+readline_internal_teardown (int eof)
+{
+ char *temp;
+ HIST_ENTRY *entry;
+
+ RL_CHECK_SIGNALS ();
+
+ if (eof)
+ RL_SETSTATE (RL_STATE_EOF); /* XXX */
+
+ /* Restore the original of this history line, iff the line that we
+ are editing was originally in the history, AND the line has changed. */
+ entry = current_history ();
+
+ /* We don't want to do this if we executed functions that call
+ history_set_pos to set the history offset to the line containing the
+ non-incremental search string. */
+ if (entry && rl_undo_list)
+ {
+ temp = savestring (the_line);
+ rl_revert_line (1, 0);
+ entry = replace_history_entry (where_history (), the_line, (histdata_t)NULL);
+ _rl_free_history_entry (entry);
+
+ strcpy (the_line, temp);
+ xfree (temp);
+ }
+
+ if (_rl_revert_all_at_newline)
+ _rl_revert_all_lines ();
+
+ /* At any rate, it is highly likely that this line has an undo list. Get
+ rid of it now. */
+ if (rl_undo_list)
+ rl_free_undo_list ();
+
+ /* Disable the meta key, if this terminal has one and we were told to use it.
+ The check whether or not we sent the enable string is in
+ _rl_disable_meta_key(); the flag is set in _rl_enable_meta_key */
+ _rl_disable_meta_key ();
+
+ /* Restore normal cursor, if available. */
+ _rl_set_insert_mode (RL_IM_INSERT, 0);
+
+ return (eof ? (char *)NULL : savestring (the_line));
+}
+
+void
+_rl_internal_char_cleanup (void)
+{
+ if (_rl_keep_mark_active)
+ _rl_keep_mark_active = 0;
+ else if (rl_mark_active_p ())
+ rl_deactivate_mark ();
+
+#if defined (VI_MODE)
+ /* In vi mode, when you exit insert mode, the cursor moves back
+ over the previous character. We explicitly check for that here. */
+ if (rl_editing_mode == vi_mode && _rl_keymap == vi_movement_keymap)
+ rl_vi_check ();
+#endif /* VI_MODE */
+
+ if (rl_num_chars_to_read && rl_end >= rl_num_chars_to_read)
+ {
+ (*rl_redisplay_function) ();
+ _rl_want_redisplay = 0;
+ rl_newline (1, '\n');
+ }
+
+ if (rl_done == 0)
+ {
+ (*rl_redisplay_function) ();
+ _rl_want_redisplay = 0;
+ }
+
+ /* If the application writer has told us to erase the entire line if
+ the only character typed was something bound to rl_newline, do so. */
+ if (rl_erase_empty_line && rl_done && rl_last_func == rl_newline &&
+ rl_point == 0 && rl_end == 0)
+ _rl_erase_entire_line ();
+}
+
+STATIC_CALLBACK int
+#if defined (READLINE_CALLBACKS)
+readline_internal_char (void)
+#else
+readline_internal_charloop (void)
+#endif
+{
+ static int lastc, eof_found;
+ int c, code, lk, r;
+
+ lastc = EOF;
+
+#if !defined (READLINE_CALLBACKS)
+ eof_found = 0;
+ while (rl_done == 0)
+ {
+#endif
+ lk = _rl_last_command_was_kill;
+
+#if defined (HAVE_POSIX_SIGSETJMP)
+ code = sigsetjmp (_rl_top_level, 0);
+#else
+ code = setjmp (_rl_top_level);
+#endif
+
+ if (code)
+ {
+ (*rl_redisplay_function) ();
+ _rl_want_redisplay = 0;
+
+ /* If we longjmped because of a timeout, handle it here. */
+ if (RL_ISSTATE (RL_STATE_TIMEOUT))
+ {
+ RL_SETSTATE (RL_STATE_DONE);
+ rl_done = 1;
+ return 1;
+ }
+
+ /* If we get here, we're not being called from something dispatched
+ from _rl_callback_read_char(), which sets up its own value of
+ _rl_top_level (saving and restoring the old, of course), so
+ we can just return here. */
+ if (RL_ISSTATE (RL_STATE_CALLBACK))
+ return (0);
+ }
+
+ if (rl_pending_input == 0)
+ {
+ /* Then initialize the argument and number of keys read. */
+ _rl_reset_argument ();
+ rl_executing_keyseq[rl_key_sequence_length = 0] = '\0';
+ }
+
+ RL_SETSTATE(RL_STATE_READCMD);
+ c = rl_read_key ();
+ RL_UNSETSTATE(RL_STATE_READCMD);
+
+ /* look at input.c:rl_getc() for the circumstances under which this will
+ be returned; punt immediately on read error without converting it to
+ a newline; assume that rl_read_key has already called the signal
+ handler. */
+ if (c == READERR)
+ {
+#if defined (READLINE_CALLBACKS)
+ RL_SETSTATE(RL_STATE_DONE);
+ return (rl_done = 1);
+#else
+ RL_SETSTATE(RL_STATE_EOF);
+ eof_found = 1;
+ break;
+#endif
+ }
+
+ /* EOF typed to a non-blank line is ^D the first time, EOF the second
+ time in a row. This won't return any partial line read from the tty.
+ If we want to change this, to force any existing line to be returned
+ when read(2) reads EOF, for example, this is the place to change. */
+ if (c == EOF && rl_end)
+ {
+ if (RL_SIG_RECEIVED ())
+ {
+ RL_CHECK_SIGNALS ();
+ if (rl_signal_event_hook)
+ (*rl_signal_event_hook) (); /* XXX */
+ }
+
+ /* XXX - reading two consecutive EOFs returns EOF */
+ if (RL_ISSTATE (RL_STATE_TERMPREPPED))
+ {
+ if (lastc == _rl_eof_char || lastc == EOF)
+ rl_end = 0;
+ else
+ c = _rl_eof_char;
+ }
+ else
+ c = NEWLINE;
+ }
+
+ /* The character _rl_eof_char typed to blank line, and not as the
+ previous character is interpreted as EOF. This doesn't work when
+ READLINE_CALLBACKS is defined, so hitting a series of ^Ds will
+ erase all the chars on the line and then return EOF. */
+ if (((c == _rl_eof_char && lastc != c) || c == EOF) && rl_end == 0)
+ {
+#if defined (READLINE_CALLBACKS)
+ RL_SETSTATE(RL_STATE_DONE);
+ return (rl_done = 1);
+#else
+ RL_SETSTATE(RL_STATE_EOF);
+ eof_found = 1;
+ break;
+#endif
+ }
+
+ lastc = c;
+ r = _rl_dispatch ((unsigned char)c, _rl_keymap);
+ RL_CHECK_SIGNALS ();
+
+ if (_rl_command_to_execute)
+ {
+ (*rl_redisplay_function) ();
+
+ rl_executing_keymap = _rl_command_to_execute->map;
+ rl_executing_key = _rl_command_to_execute->key;
+
+ _rl_executing_func = _rl_command_to_execute->func;
+
+ rl_dispatching = 1;
+ RL_SETSTATE(RL_STATE_DISPATCHING);
+ r = (*(_rl_command_to_execute->func)) (_rl_command_to_execute->count, _rl_command_to_execute->key);
+ _rl_command_to_execute = 0;
+ RL_UNSETSTATE(RL_STATE_DISPATCHING);
+ rl_dispatching = 0;
+
+ RL_CHECK_SIGNALS ();
+ }
+
+ /* If there was no change in _rl_last_command_was_kill, then no kill
+ has taken place. Note that if input is pending we are reading
+ a prefix command, so nothing has changed yet. */
+ if (rl_pending_input == 0 && lk == _rl_last_command_was_kill)
+ _rl_last_command_was_kill = 0;
+
+ _rl_internal_char_cleanup ();
+
+#if defined (READLINE_CALLBACKS)
+ return 0;
+#else
+ }
+
+ return (eof_found);
+#endif
+}
+
+#if defined (READLINE_CALLBACKS)
+static int
+readline_internal_charloop (void)
+{
+ int eof = 1;
+
+ while (rl_done == 0)
+ eof = readline_internal_char ();
+ return (eof);
+}
+#endif /* READLINE_CALLBACKS */
+
+/* Read a line of input from the global rl_instream, doing output on
+ the global rl_outstream.
+ If rl_prompt is non-null, then that is our prompt. */
+static char *
+readline_internal (void)
+{
+ readline_internal_setup ();
+ rl_eof_found = readline_internal_charloop ();
+ return (readline_internal_teardown (rl_eof_found));
+}
+
+void
+_rl_init_line_state (void)
+{
+ rl_point = rl_end = rl_mark = 0;
+ the_line = rl_line_buffer;
+ the_line[0] = 0;
+}
+
+void
+_rl_set_the_line (void)
+{
+ the_line = rl_line_buffer;
+}
+
+#if defined (READLINE_CALLBACKS)
+_rl_keyseq_cxt *
+_rl_keyseq_cxt_alloc (void)
+{
+ _rl_keyseq_cxt *cxt;
+
+ cxt = (_rl_keyseq_cxt *)xmalloc (sizeof (_rl_keyseq_cxt));
+
+ cxt->flags = cxt->subseq_arg = cxt->subseq_retval = 0;
+
+ cxt->okey = 0;
+ cxt->ocxt = _rl_kscxt;
+ cxt->childval = 42; /* sentinel value */
+
+ return cxt;
+}
+
+void
+_rl_keyseq_cxt_dispose (_rl_keyseq_cxt *cxt)
+{
+ xfree (cxt);
+}
+
+void
+_rl_keyseq_chain_dispose (void)
+{
+ _rl_keyseq_cxt *cxt;
+
+ while (_rl_kscxt)
+ {
+ cxt = _rl_kscxt;
+ _rl_kscxt = _rl_kscxt->ocxt;
+ _rl_keyseq_cxt_dispose (cxt);
+ }
+}
+#endif
+
+static int
+_rl_subseq_getchar (int key)
+{
+ int k;
+
+ if (key == ESC)
+ RL_SETSTATE(RL_STATE_METANEXT);
+ RL_SETSTATE(RL_STATE_MOREINPUT);
+ k = rl_read_key ();
+ RL_UNSETSTATE(RL_STATE_MOREINPUT);
+ if (key == ESC)
+ RL_UNSETSTATE(RL_STATE_METANEXT);
+
+ return k;
+}
+
+#if defined (READLINE_CALLBACKS)
+int
+_rl_dispatch_callback (_rl_keyseq_cxt *cxt)
+{
+ int nkey, r;
+
+ /* For now */
+ /* The first time this context is used, we want to read input and dispatch
+ on it. When traversing the chain of contexts back `up', we want to use
+ the value from the next context down. We're simulating recursion using
+ a chain of contexts. */
+ if ((cxt->flags & KSEQ_DISPATCHED) == 0)
+ {
+ nkey = _rl_subseq_getchar (cxt->okey);
+ if (nkey < 0)
+ {
+ _rl_abort_internal ();
+ return -1;
+ }
+ r = _rl_dispatch_subseq (nkey, cxt->dmap, cxt->subseq_arg);
+ cxt->flags |= KSEQ_DISPATCHED;
+ }
+ else
+ r = cxt->childval;
+
+ /* For now */
+ if (r != -3) /* don't do this if we indicate there will be other matches */
+ r = _rl_subseq_result (r, cxt->oldmap, cxt->okey, (cxt->flags & KSEQ_SUBSEQ));
+
+ RL_CHECK_SIGNALS ();
+ /* We only treat values < 0 specially to simulate recursion. */
+ if (r >= 0 || (r == -1 && (cxt->flags & KSEQ_SUBSEQ) == 0)) /* success! or failure! */
+ {
+ _rl_keyseq_chain_dispose ();
+ RL_UNSETSTATE (RL_STATE_MULTIKEY);
+ return r;
+ }
+
+ if (r != -3) /* magic value that says we added to the chain */
+ _rl_kscxt = cxt->ocxt;
+ if (_rl_kscxt)
+ _rl_kscxt->childval = r;
+ if (r != -3)
+ _rl_keyseq_cxt_dispose (cxt);
+
+ return r;
+}
+#endif /* READLINE_CALLBACKS */
+
+/* Do the command associated with KEY in MAP.
+ If the associated command is really a keymap, then read
+ another key, and dispatch into that map. */
+int
+_rl_dispatch (register int key, Keymap map)
+{
+ _rl_dispatching_keymap = map;
+ return _rl_dispatch_subseq (key, map, 0);
+}
+
+int
+_rl_dispatch_subseq (register int key, Keymap map, int got_subseq)
+{
+ int r, newkey;
+ char *macro;
+ rl_command_func_t *func;
+#if defined (READLINE_CALLBACKS)
+ _rl_keyseq_cxt *cxt;
+#endif
+
+ if (META_CHAR (key) && _rl_convert_meta_chars_to_ascii)
+ {
+ if (map[ESC].type == ISKMAP)
+ {
+ if (RL_ISSTATE (RL_STATE_MACRODEF))
+ _rl_add_macro_char (ESC);
+ RESIZE_KEYSEQ_BUFFER ();
+ rl_executing_keyseq[rl_key_sequence_length++] = ESC;
+ map = FUNCTION_TO_KEYMAP (map, ESC);
+ key = UNMETA (key);
+ return (_rl_dispatch (key, map));
+ }
+ else
+ rl_ding ();
+ return 0;
+ }
+
+ if (RL_ISSTATE (RL_STATE_MACRODEF))
+ _rl_add_macro_char (key);
+
+ r = 0;
+ switch (map[key].type)
+ {
+ case ISFUNC:
+ func = map[key].function;
+ if (func)
+ {
+ /* Special case rl_do_lowercase_version (). */
+ if (func == rl_do_lowercase_version)
+ /* Should we do anything special if key == ANYOTHERKEY? */
+ return (_rl_dispatch (_rl_to_lower ((unsigned char)key), map));
+
+ rl_executing_keymap = map;
+ rl_executing_key = key;
+
+ _rl_executing_func = func;
+
+ RESIZE_KEYSEQ_BUFFER();
+ rl_executing_keyseq[rl_key_sequence_length++] = key;
+ rl_executing_keyseq[rl_key_sequence_length] = '\0';
+
+ rl_dispatching = 1;
+ RL_SETSTATE(RL_STATE_DISPATCHING);
+ r = (*func) (rl_numeric_arg * rl_arg_sign, key);
+ RL_UNSETSTATE(RL_STATE_DISPATCHING);
+ rl_dispatching = 0;
+
+ /* If we have input pending, then the last command was a prefix
+ command. Don't change the state of rl_last_func. Otherwise,
+ remember the last command executed in this variable. */
+#if defined (VI_MODE)
+ if (rl_pending_input == 0 && map[key].function != rl_digit_argument && map[key].function != rl_vi_arg_digit)
+#else
+ if (rl_pending_input == 0 && map[key].function != rl_digit_argument)
+#endif
+ rl_last_func = map[key].function;
+
+ RL_CHECK_SIGNALS ();
+ }
+ else if (map[ANYOTHERKEY].function)
+ {
+ /* OK, there's no function bound in this map, but there is a
+ shadow function that was overridden when the current keymap
+ was created. Return -2 to note that. */
+ if (RL_ISSTATE (RL_STATE_MACROINPUT))
+ _rl_prev_macro_key ();
+ else
+ _rl_unget_char (key);
+ if (rl_key_sequence_length > 0)
+ rl_executing_keyseq[--rl_key_sequence_length] = '\0';
+ return -2;
+ }
+ else if (got_subseq)
+ {
+ /* Return -1 to note that we're in a subsequence, but we don't
+ have a matching key, nor was one overridden. This means
+ we need to back up the recursion chain and find the last
+ subsequence that is bound to a function. */
+ if (RL_ISSTATE (RL_STATE_MACROINPUT))
+ _rl_prev_macro_key ();
+ else
+ _rl_unget_char (key);
+ if (rl_key_sequence_length > 0)
+ rl_executing_keyseq[--rl_key_sequence_length] = '\0';
+ return -1;
+ }
+ else
+ {
+#if defined (READLINE_CALLBACKS)
+ RL_UNSETSTATE (RL_STATE_MULTIKEY);
+ _rl_keyseq_chain_dispose ();
+#endif
+ _rl_abort_internal ();
+ return -1;
+ }
+ break;
+
+ case ISKMAP:
+ if (map[key].function != 0)
+ {
+#if defined (VI_MODE)
+ /* The only way this test will be true is if a subsequence has been
+ bound starting with ESC, generally the arrow keys. What we do is
+ check whether there's input in the queue, which there generally
+ will be if an arrow key has been pressed, and, if there's not,
+ just dispatch to (what we assume is) rl_vi_movement_mode right
+ away. This is essentially an input test with a zero timeout (by
+ default) or a timeout determined by the value of `keyseq-timeout' */
+ /* _rl_keyseq_timeout specified in milliseconds; _rl_input_queued
+ takes microseconds, so multiply by 1000 */
+ if (rl_editing_mode == vi_mode && key == ESC && map == vi_insertion_keymap &&
+ (RL_ISSTATE (RL_STATE_INPUTPENDING|RL_STATE_MACROINPUT) == 0) &&
+ _rl_pushed_input_available () == 0 &&
+ _rl_input_queued ((_rl_keyseq_timeout > 0) ? _rl_keyseq_timeout*1000 : 0) == 0)
+ return (_rl_dispatch (ANYOTHERKEY, FUNCTION_TO_KEYMAP (map, key)));
+ /* This is a very specific test. It can possibly be generalized in
+ the future, but for now it handles a specific case of ESC being
+ the last character in a keyboard macro. */
+ if (rl_editing_mode == vi_mode && key == ESC && map == vi_insertion_keymap &&
+ (RL_ISSTATE (RL_STATE_INPUTPENDING) == 0) &&
+ (RL_ISSTATE (RL_STATE_MACROINPUT) && _rl_peek_macro_key () == 0) &&
+ _rl_pushed_input_available () == 0 &&
+ _rl_input_queued ((_rl_keyseq_timeout > 0) ? _rl_keyseq_timeout*1000 : 0) == 0)
+ return (_rl_dispatch (ANYOTHERKEY, FUNCTION_TO_KEYMAP (map, key)));
+#endif
+
+ RESIZE_KEYSEQ_BUFFER ();
+ rl_executing_keyseq[rl_key_sequence_length++] = key;
+ _rl_dispatching_keymap = FUNCTION_TO_KEYMAP (map, key);
+
+ /* Allocate new context here. Use linked contexts (linked through
+ cxt->ocxt) to simulate recursion */
+#if defined (READLINE_CALLBACKS)
+# if defined (VI_MODE)
+ /* If we're redoing a vi mode command and we know there is a shadowed
+ function corresponding to this key, just call it -- all the redoable
+ vi mode commands already have all the input they need, and rl_vi_redo
+ assumes that one call to rl_dispatch is sufficient to complete the
+ command. */
+ if (_rl_vi_redoing && RL_ISSTATE (RL_STATE_CALLBACK) &&
+ map[ANYOTHERKEY].function != 0)
+ return (_rl_subseq_result (-2, map, key, got_subseq));
+# endif
+ if (RL_ISSTATE (RL_STATE_CALLBACK))
+ {
+ /* Return 0 only the first time, to indicate success to
+ _rl_callback_read_char. The rest of the time, we're called
+ from _rl_dispatch_callback, so we return -3 to indicate
+ special handling is necessary. */
+ r = RL_ISSTATE (RL_STATE_MULTIKEY) ? -3 : 0;
+ cxt = _rl_keyseq_cxt_alloc ();
+
+ if (got_subseq)
+ cxt->flags |= KSEQ_SUBSEQ;
+ cxt->okey = key;
+ cxt->oldmap = map;
+ cxt->dmap = _rl_dispatching_keymap;
+ cxt->subseq_arg = got_subseq || cxt->dmap[ANYOTHERKEY].function;
+
+ RL_SETSTATE (RL_STATE_MULTIKEY);
+ _rl_kscxt = cxt;
+
+ return r; /* don't indicate immediate success */
+ }
+#endif
+
+ /* Tentative inter-character timeout for potential multi-key
+ sequences? If no input within timeout, abort sequence and
+ act as if we got non-matching input. */
+ /* _rl_keyseq_timeout specified in milliseconds; _rl_input_queued
+ takes microseconds, so multiply by 1000 */
+ if (_rl_keyseq_timeout > 0 &&
+ (RL_ISSTATE (RL_STATE_INPUTPENDING|RL_STATE_MACROINPUT) == 0) &&
+ _rl_pushed_input_available () == 0 &&
+ _rl_dispatching_keymap[ANYOTHERKEY].function &&
+ _rl_input_queued (_rl_keyseq_timeout*1000) == 0)
+ {
+ if (rl_key_sequence_length > 0)
+ rl_executing_keyseq[--rl_key_sequence_length] = '\0';
+ return (_rl_subseq_result (-2, map, key, got_subseq));
+ }
+
+ newkey = _rl_subseq_getchar (key);
+ if (newkey < 0)
+ {
+ _rl_abort_internal ();
+ return -1;
+ }
+
+ r = _rl_dispatch_subseq (newkey, _rl_dispatching_keymap, got_subseq || map[ANYOTHERKEY].function);
+ return _rl_subseq_result (r, map, key, got_subseq);
+ }
+ else
+ {
+ _rl_abort_internal (); /* XXX */
+ return -1;
+ }
+ break;
+
+ case ISMACR:
+ if (map[key].function != 0)
+ {
+ rl_executing_keyseq[rl_key_sequence_length] = '\0';
+ macro = savestring ((char *)map[key].function);
+ _rl_with_macro_input (macro);
+ return 0;
+ }
+ break;
+ }
+
+#if defined (VI_MODE)
+ if (rl_editing_mode == vi_mode && _rl_keymap == vi_movement_keymap &&
+ key != ANYOTHERKEY &&
+ _rl_dispatching_keymap == vi_movement_keymap &&
+ _rl_vi_textmod_command (key))
+ _rl_vi_set_last (key, rl_numeric_arg, rl_arg_sign);
+#endif
+
+ return (r);
+}
+
+static int
+_rl_subseq_result (int r, Keymap map, int key, int got_subseq)
+{
+ Keymap m;
+ int type, nt;
+ rl_command_func_t *func, *nf;
+
+ if (r == -2)
+ /* We didn't match anything, and the keymap we're indexed into
+ shadowed a function previously bound to that prefix. Call
+ the function. The recursive call to _rl_dispatch_subseq has
+ already taken care of pushing any necessary input back onto
+ the input queue with _rl_unget_char. */
+ {
+ m = _rl_dispatching_keymap;
+ type = m[ANYOTHERKEY].type;
+ func = m[ANYOTHERKEY].function;
+ if (type == ISFUNC && func == rl_do_lowercase_version)
+ r = _rl_dispatch (_rl_to_lower ((unsigned char)key), map);
+ else if (type == ISFUNC)
+ {
+ /* If we shadowed a function, whatever it is, we somehow need a
+ keymap with map[key].func == shadowed-function.
+ Let's use this one. Then we can dispatch using the original
+ key, since there are commands (e.g., in vi mode) for which it
+ matters. */
+ nt = m[key].type;
+ nf = m[key].function;
+
+ m[key].type = type;
+ m[key].function = func;
+ /* Don't change _rl_dispatching_keymap, set it here */
+ _rl_dispatching_keymap = map; /* previous map */
+ r = _rl_dispatch_subseq (key, m, 0);
+ m[key].type = nt;
+ m[key].function = nf;
+ }
+ else
+ /* We probably shadowed a keymap, so keep going. */
+ r = _rl_dispatch (ANYOTHERKEY, m);
+ }
+ else if (r < 0 && map[ANYOTHERKEY].function)
+ {
+ /* We didn't match (r is probably -1), so return something to
+ tell the caller that it should try ANYOTHERKEY for an
+ overridden function. */
+ if (RL_ISSTATE (RL_STATE_MACROINPUT))
+ _rl_prev_macro_key ();
+ else
+ _rl_unget_char (key);
+ if (rl_key_sequence_length > 0)
+ rl_executing_keyseq[--rl_key_sequence_length] = '\0';
+ _rl_dispatching_keymap = map;
+ return -2;
+ }
+ else if (r < 0 && got_subseq) /* XXX */
+ {
+ /* OK, back up the chain. */
+ if (RL_ISSTATE (RL_STATE_MACROINPUT))
+ _rl_prev_macro_key ();
+ else
+ _rl_unget_char (key);
+ if (rl_key_sequence_length > 0)
+ rl_executing_keyseq[--rl_key_sequence_length] = '\0';
+ _rl_dispatching_keymap = map;
+ return -1;
+ }
+
+ return r;
+}
+
+/* **************************************************************** */
+/* */
+/* Initializations */
+/* */
+/* **************************************************************** */
+
+/* Initialize readline (and terminal if not already). */
+int
+rl_initialize (void)
+{
+ /* Initialize the timeout first to get the precise start time. */
+ _rl_timeout_init ();
+
+ /* If we have never been called before, initialize the
+ terminal and data structures. */
+ if (rl_initialized == 0)
+ {
+ RL_SETSTATE(RL_STATE_INITIALIZING);
+ readline_initialize_everything ();
+ RL_UNSETSTATE(RL_STATE_INITIALIZING);
+ rl_initialized++;
+ RL_SETSTATE(RL_STATE_INITIALIZED);
+ }
+ else
+ _rl_reset_locale (); /* check current locale and set locale variables */
+
+ /* Initialize the current line information. */
+ _rl_init_line_state ();
+
+ /* We aren't done yet. We haven't even gotten started yet! */
+ rl_done = 0;
+ RL_UNSETSTATE(RL_STATE_DONE|RL_STATE_TIMEOUT|RL_STATE_EOF);
+
+ /* Tell the history routines what is going on. */
+ _rl_start_using_history ();
+
+ /* Make the display buffer match the state of the line. */
+ rl_reset_line_state ();
+
+ /* No such function typed yet. */
+ rl_last_func = (rl_command_func_t *)NULL;
+
+ /* Parsing of key-bindings begins in an enabled state. */
+ _rl_parsing_conditionalized_out = 0;
+
+#if defined (VI_MODE)
+ if (rl_editing_mode == vi_mode)
+ _rl_vi_initialize_line ();
+#endif
+
+ /* Each line starts in insert mode (the default). */
+ _rl_set_insert_mode (RL_IM_DEFAULT, 1);
+
+ return 0;
+}
+
+#if 0
+#if defined (__EMX__)
+static void
+_emx_build_environ (void)
+{
+ TIB *tibp;
+ PIB *pibp;
+ char *t, **tp;
+ int c;
+
+ DosGetInfoBlocks (&tibp, &pibp);
+ t = pibp->pib_pchenv;
+ for (c = 1; *t; c++)
+ t += strlen (t) + 1;
+ tp = environ = (char **)xmalloc ((c + 1) * sizeof (char *));
+ t = pibp->pib_pchenv;
+ while (*t)
+ {
+ *tp++ = t;
+ t += strlen (t) + 1;
+ }
+ *tp = 0;
+}
+#endif /* __EMX__ */
+#endif
+
+/* Initialize the entire state of the world. */
+static void
+readline_initialize_everything (void)
+{
+#if 0
+#if defined (__EMX__)
+ if (environ == 0)
+ _emx_build_environ ();
+#endif
+#endif
+
+#if 0
+ /* Find out if we are running in Emacs -- UNUSED. */
+ running_in_emacs = sh_get_env_value ("EMACS") != (char *)0;
+#endif
+
+ /* Set up input and output if they are not already set up. */
+ if (!rl_instream)
+ rl_instream = stdin;
+
+ if (!rl_outstream)
+ rl_outstream = stdout;
+
+ /* Bind _rl_in_stream and _rl_out_stream immediately. These values
+ may change, but they may also be used before readline_internal ()
+ is called. */
+ _rl_in_stream = rl_instream;
+ _rl_out_stream = rl_outstream;
+
+ /* Allocate data structures. */
+ if (rl_line_buffer == 0)
+ rl_line_buffer = (char *)xmalloc (rl_line_buffer_len = DEFAULT_BUFFER_SIZE);
+
+ /* Initialize the terminal interface. */
+ if (rl_terminal_name == 0)
+ rl_terminal_name = sh_get_env_value ("TERM");
+ _rl_init_terminal_io (rl_terminal_name);
+
+ /* Bind tty characters to readline functions. */
+ readline_default_bindings ();
+
+ /* Initialize the function names. */
+ rl_initialize_funmap ();
+
+ /* Decide whether we should automatically go into eight-bit mode. */
+ _rl_init_eightbit ();
+
+ /* Read in the init file. */
+ rl_read_init_file ((char *)NULL);
+
+ /* XXX */
+ if (_rl_horizontal_scroll_mode && _rl_term_autowrap)
+ {
+ _rl_screenwidth--;
+ _rl_screenchars -= _rl_screenheight;
+ }
+
+ /* Override the effect of any `set keymap' assignments in the
+ inputrc file. */
+ rl_set_keymap_from_edit_mode ();
+
+ /* Try to bind a common arrow key prefix, if not already bound. */
+ bind_arrow_keys ();
+
+ /* Bind the bracketed paste prefix assuming that the user will enable
+ it on terminals that support it. */
+ bind_bracketed_paste_prefix ();
+
+ /* If the completion parser's default word break characters haven't
+ been set yet, then do so now. */
+ if (rl_completer_word_break_characters == 0)
+ rl_completer_word_break_characters = rl_basic_word_break_characters;
+
+#if defined (COLOR_SUPPORT)
+ if (_rl_colored_stats || _rl_colored_completion_prefix)
+ _rl_parse_colors ();
+#endif
+
+ rl_executing_keyseq = malloc (_rl_executing_keyseq_size = 16);
+ if (rl_executing_keyseq)
+ rl_executing_keyseq[rl_key_sequence_length = 0] = '\0';
+}
+
+/* If this system allows us to look at the values of the regular
+ input editing characters, then bind them to their readline
+ equivalents, iff the characters are not bound to keymaps. */
+static void
+readline_default_bindings (void)
+{
+ if (_rl_bind_stty_chars)
+ rl_tty_set_default_bindings (_rl_keymap);
+}
+
+/* Reset the default bindings for the terminal special characters we're
+ interested in back to rl_insert and read the new ones. */
+static void
+reset_default_bindings (void)
+{
+ if (_rl_bind_stty_chars)
+ {
+ rl_tty_unset_default_bindings (_rl_keymap);
+ rl_tty_set_default_bindings (_rl_keymap);
+ }
+}
+
+/* Bind some common arrow key sequences in MAP. */
+static void
+bind_arrow_keys_internal (Keymap map)
+{
+ Keymap xkeymap;
+
+ xkeymap = _rl_keymap;
+ _rl_keymap = map;
+
+#if defined (__MSDOS__)
+ rl_bind_keyseq_if_unbound ("\033[0A", rl_get_previous_history);
+ rl_bind_keyseq_if_unbound ("\033[0B", rl_backward_char);
+ rl_bind_keyseq_if_unbound ("\033[0C", rl_forward_char);
+ rl_bind_keyseq_if_unbound ("\033[0D", rl_get_next_history);
+#endif
+
+ rl_bind_keyseq_if_unbound ("\033[A", rl_get_previous_history);
+ rl_bind_keyseq_if_unbound ("\033[B", rl_get_next_history);
+ rl_bind_keyseq_if_unbound ("\033[C", rl_forward_char);
+ rl_bind_keyseq_if_unbound ("\033[D", rl_backward_char);
+ rl_bind_keyseq_if_unbound ("\033[H", rl_beg_of_line);
+ rl_bind_keyseq_if_unbound ("\033[F", rl_end_of_line);
+
+ rl_bind_keyseq_if_unbound ("\033OA", rl_get_previous_history);
+ rl_bind_keyseq_if_unbound ("\033OB", rl_get_next_history);
+ rl_bind_keyseq_if_unbound ("\033OC", rl_forward_char);
+ rl_bind_keyseq_if_unbound ("\033OD", rl_backward_char);
+ rl_bind_keyseq_if_unbound ("\033OH", rl_beg_of_line);
+ rl_bind_keyseq_if_unbound ("\033OF", rl_end_of_line);
+
+ /* Key bindings for control-arrow keys */
+ rl_bind_keyseq_if_unbound ("\033[1;5C", rl_forward_word);
+ rl_bind_keyseq_if_unbound ("\033[1;5D", rl_backward_word);
+ rl_bind_keyseq_if_unbound ("\033[3;5~", rl_kill_word);
+
+ /* Key bindings for alt-arrow keys */
+ rl_bind_keyseq_if_unbound ("\033[1;3C", rl_forward_word);
+ rl_bind_keyseq_if_unbound ("\033[1;3D", rl_backward_word);
+
+#if defined (__MINGW32__)
+ rl_bind_keyseq_if_unbound ("\340H", rl_get_previous_history);
+ rl_bind_keyseq_if_unbound ("\340P", rl_get_next_history);
+ rl_bind_keyseq_if_unbound ("\340M", rl_forward_char);
+ rl_bind_keyseq_if_unbound ("\340K", rl_backward_char);
+ rl_bind_keyseq_if_unbound ("\340G", rl_beg_of_line);
+ rl_bind_keyseq_if_unbound ("\340O", rl_end_of_line);
+ rl_bind_keyseq_if_unbound ("\340S", rl_delete);
+ rl_bind_keyseq_if_unbound ("\340R", rl_overwrite_mode);
+
+ /* These may or may not work because of the embedded NUL. */
+ rl_bind_keyseq_if_unbound ("\\000H", rl_get_previous_history);
+ rl_bind_keyseq_if_unbound ("\\000P", rl_get_next_history);
+ rl_bind_keyseq_if_unbound ("\\000M", rl_forward_char);
+ rl_bind_keyseq_if_unbound ("\\000K", rl_backward_char);
+ rl_bind_keyseq_if_unbound ("\\000G", rl_beg_of_line);
+ rl_bind_keyseq_if_unbound ("\\000O", rl_end_of_line);
+ rl_bind_keyseq_if_unbound ("\\000S", rl_delete);
+ rl_bind_keyseq_if_unbound ("\\000R", rl_overwrite_mode);
+#endif
+
+ _rl_keymap = xkeymap;
+}
+
+/* Try and bind the common arrow key prefixes after giving termcap and
+ the inputrc file a chance to bind them and create `real' keymaps
+ for the arrow key prefix. */
+static void
+bind_arrow_keys (void)
+{
+ bind_arrow_keys_internal (emacs_standard_keymap);
+
+#if defined (VI_MODE)
+ bind_arrow_keys_internal (vi_movement_keymap);
+ /* Unbind vi_movement_keymap[ESC] to allow users to repeatedly hit ESC
+ in vi command mode while still allowing the arrow keys to work. */
+ if (vi_movement_keymap[ESC].type == ISKMAP)
+ rl_bind_keyseq_in_map ("\033", (rl_command_func_t *)NULL, vi_movement_keymap);
+ bind_arrow_keys_internal (vi_insertion_keymap);
+#endif
+}
+
+static void
+bind_bracketed_paste_prefix (void)
+{
+ Keymap xkeymap;
+
+ xkeymap = _rl_keymap;
+
+ _rl_keymap = emacs_standard_keymap;
+ rl_bind_keyseq_if_unbound (BRACK_PASTE_PREF, rl_bracketed_paste_begin);
+
+#if defined (VI_MODE)
+ _rl_keymap = vi_insertion_keymap;
+ rl_bind_keyseq_if_unbound (BRACK_PASTE_PREF, rl_bracketed_paste_begin);
+ /* XXX - is there a reason to do this in the vi command keymap? */
+#endif
+
+ _rl_keymap = xkeymap;
+}
+
+/* **************************************************************** */
+/* */
+/* Saving and Restoring Readline's state */
+/* */
+/* **************************************************************** */
+
+int
+rl_save_state (struct readline_state *sp)
+{
+ if (sp == 0)
+ return -1;
+
+ sp->point = rl_point;
+ sp->end = rl_end;
+ sp->mark = rl_mark;
+ sp->buffer = rl_line_buffer;
+ sp->buflen = rl_line_buffer_len;
+ sp->ul = rl_undo_list;
+ sp->prompt = rl_prompt;
+
+ sp->rlstate = rl_readline_state;
+ sp->done = rl_done;
+ sp->kmap = _rl_keymap;
+
+ sp->lastfunc = rl_last_func;
+ sp->insmode = rl_insert_mode;
+ sp->edmode = rl_editing_mode;
+ sp->kseq = rl_executing_keyseq;
+ sp->kseqlen = rl_key_sequence_length;
+ sp->inf = rl_instream;
+ sp->outf = rl_outstream;
+ sp->pendingin = rl_pending_input;
+ sp->macro = rl_executing_macro;
+
+ sp->catchsigs = rl_catch_signals;
+ sp->catchsigwinch = rl_catch_sigwinch;
+
+ sp->entryfunc = rl_completion_entry_function;
+ sp->menuentryfunc = rl_menu_completion_entry_function;
+ sp->ignorefunc = rl_ignore_some_completions_function;
+ sp->attemptfunc = rl_attempted_completion_function;
+ sp->wordbreakchars = rl_completer_word_break_characters;
+
+ return (0);
+}
+
+int
+rl_restore_state (struct readline_state *sp)
+{
+ if (sp == 0)
+ return -1;
+
+ rl_point = sp->point;
+ rl_end = sp->end;
+ rl_mark = sp->mark;
+ the_line = rl_line_buffer = sp->buffer;
+ rl_line_buffer_len = sp->buflen;
+ rl_undo_list = sp->ul;
+ rl_prompt = sp->prompt;
+
+ rl_readline_state = sp->rlstate;
+ rl_done = sp->done;
+ _rl_keymap = sp->kmap;
+
+ rl_last_func = sp->lastfunc;
+ rl_insert_mode = sp->insmode;
+ rl_editing_mode = sp->edmode;
+ rl_executing_keyseq = sp->kseq;
+ rl_key_sequence_length = sp->kseqlen;
+ rl_instream = sp->inf;
+ rl_outstream = sp->outf;
+ rl_pending_input = sp->pendingin;
+ rl_executing_macro = sp->macro;
+
+ rl_catch_signals = sp->catchsigs;
+ rl_catch_sigwinch = sp->catchsigwinch;
+
+ rl_completion_entry_function = sp->entryfunc;
+ rl_menu_completion_entry_function = sp->menuentryfunc;
+ rl_ignore_some_completions_function = sp->ignorefunc;
+ rl_attempted_completion_function = sp->attemptfunc;
+ rl_completer_word_break_characters = sp->wordbreakchars;
+
+ rl_deactivate_mark ();
+
+ return (0);
+}
+
+/* Functions to manage the string that is the current key sequence. */
+
+void
+_rl_init_executing_keyseq (void)
+{
+ rl_executing_keyseq[rl_key_sequence_length = 0] = '\0';
+}
+
+void
+_rl_term_executing_keyseq (void)
+{
+ rl_executing_keyseq[rl_key_sequence_length] = '\0';
+}
+
+void
+_rl_end_executing_keyseq (void)
+{
+ if (rl_key_sequence_length > 0)
+ rl_executing_keyseq[--rl_key_sequence_length] = '\0';
+}
+
+void
+_rl_add_executing_keyseq (int key)
+{
+ RESIZE_KEYSEQ_BUFFER ();
+ rl_executing_keyseq[rl_key_sequence_length++] = key;
+}
+
+/* `delete' the last character added to the executing key sequence. Use this
+ before calling rl_execute_next to avoid keys being added twice. */
+void
+_rl_del_executing_keyseq (void)
+{
+ if (rl_key_sequence_length > 0)
+ rl_key_sequence_length--;
+}
diff --git a/third_party/readline/readline.h b/third_party/readline/readline.h
new file mode 100644
index 000000000..8df51695f
--- /dev/null
+++ b/third_party/readline/readline.h
@@ -0,0 +1,986 @@
+/* Readline.h -- the names of functions callable from within readline. */
+
+/* Copyright (C) 1987-2022 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#if !defined (_READLINE_H_)
+#define _READLINE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined (READLINE_LIBRARY)
+# include "rlstdc.h"
+# include "rltypedefs.h"
+# include "keymaps.h"
+# include "tilde.h"
+#else
+# include "third_party/readline/rlstdc.h"
+# include "third_party/readline/rltypedefs.h"
+# include "third_party/readline/keymaps.h"
+# include "third_party/readline/tilde.h"
+#endif
+
+/* Hex-encoded Readline version number. */
+#define RL_READLINE_VERSION 0x0802 /* Readline 8.2 */
+#define RL_VERSION_MAJOR 8
+#define RL_VERSION_MINOR 2
+
+/* Readline data structures. */
+
+/* Maintaining the state of undo. We remember individual deletes and inserts
+ on a chain of things to do. */
+
+/* The actions that undo knows how to undo. Notice that UNDO_DELETE means
+ to insert some text, and UNDO_INSERT means to delete some text. I.e.,
+ the code tells undo what to undo, not how to undo it. */
+enum undo_code { UNDO_DELETE, UNDO_INSERT, UNDO_BEGIN, UNDO_END };
+
+/* What an element of THE_UNDO_LIST looks like. */
+typedef struct undo_list {
+ struct undo_list *next;
+ int start, end; /* Where the change took place. */
+ char *text; /* The text to insert, if undoing a delete. */
+ enum undo_code what; /* Delete, Insert, Begin, End. */
+} UNDO_LIST;
+
+/* The current undo list for RL_LINE_BUFFER. */
+extern UNDO_LIST *rl_undo_list;
+
+/* The data structure for mapping textual names to code addresses. */
+typedef struct _funmap {
+ const char *name;
+ rl_command_func_t *function;
+} FUNMAP;
+
+extern FUNMAP **funmap;
+
+/* **************************************************************** */
+/* */
+/* Functions available to bind to key sequences */
+/* */
+/* **************************************************************** */
+
+/* Bindable commands for numeric arguments. */
+extern int rl_digit_argument (int, int);
+extern int rl_universal_argument (int, int);
+
+/* Bindable commands for moving the cursor. */
+extern int rl_forward_byte (int, int);
+extern int rl_forward_char (int, int);
+extern int rl_forward (int, int);
+extern int rl_backward_byte (int, int);
+extern int rl_backward_char (int, int);
+extern int rl_backward (int, int);
+extern int rl_beg_of_line (int, int);
+extern int rl_end_of_line (int, int);
+extern int rl_forward_word (int, int);
+extern int rl_backward_word (int, int);
+extern int rl_refresh_line (int, int);
+extern int rl_clear_screen (int, int);
+extern int rl_clear_display (int, int);
+extern int rl_skip_csi_sequence (int, int);
+extern int rl_arrow_keys (int, int);
+
+extern int rl_previous_screen_line (int, int);
+extern int rl_next_screen_line (int, int);
+
+/* Bindable commands for inserting and deleting text. */
+extern int rl_insert (int, int);
+extern int rl_quoted_insert (int, int);
+extern int rl_tab_insert (int, int);
+extern int rl_newline (int, int);
+extern int rl_do_lowercase_version (int, int);
+extern int rl_rubout (int, int);
+extern int rl_delete (int, int);
+extern int rl_rubout_or_delete (int, int);
+extern int rl_delete_horizontal_space (int, int);
+extern int rl_delete_or_show_completions (int, int);
+extern int rl_insert_comment (int, int);
+
+/* Bindable commands for changing case. */
+extern int rl_upcase_word (int, int);
+extern int rl_downcase_word (int, int);
+extern int rl_capitalize_word (int, int);
+
+/* Bindable commands for transposing characters and words. */
+extern int rl_transpose_words (int, int);
+extern int rl_transpose_chars (int, int);
+
+/* Bindable commands for searching within a line. */
+extern int rl_char_search (int, int);
+extern int rl_backward_char_search (int, int);
+
+/* Bindable commands for readline's interface to the command history. */
+extern int rl_beginning_of_history (int, int);
+extern int rl_end_of_history (int, int);
+extern int rl_get_next_history (int, int);
+extern int rl_get_previous_history (int, int);
+extern int rl_operate_and_get_next (int, int);
+extern int rl_fetch_history (int, int);
+
+/* Bindable commands for managing the mark and region. */
+extern int rl_set_mark (int, int);
+extern int rl_exchange_point_and_mark (int, int);
+
+/* Bindable commands to set the editing mode (emacs or vi). */
+extern int rl_vi_editing_mode (int, int);
+extern int rl_emacs_editing_mode (int, int);
+
+/* Bindable commands to change the insert mode (insert or overwrite) */
+extern int rl_overwrite_mode (int, int);
+
+/* Bindable commands for managing key bindings. */
+extern int rl_re_read_init_file (int, int);
+extern int rl_dump_functions (int, int);
+extern int rl_dump_macros (int, int);
+extern int rl_dump_variables (int, int);
+
+/* Bindable commands for word completion. */
+extern int rl_complete (int, int);
+extern int rl_possible_completions (int, int);
+extern int rl_insert_completions (int, int);
+extern int rl_old_menu_complete (int, int);
+extern int rl_menu_complete (int, int);
+extern int rl_backward_menu_complete (int, int);
+
+/* Bindable commands for killing and yanking text, and managing the kill ring. */
+extern int rl_kill_word (int, int);
+extern int rl_backward_kill_word (int, int);
+extern int rl_kill_line (int, int);
+extern int rl_backward_kill_line (int, int);
+extern int rl_kill_full_line (int, int);
+extern int rl_unix_word_rubout (int, int);
+extern int rl_unix_filename_rubout (int, int);
+extern int rl_unix_line_discard (int, int);
+extern int rl_copy_region_to_kill (int, int);
+extern int rl_kill_region (int, int);
+extern int rl_copy_forward_word (int, int);
+extern int rl_copy_backward_word (int, int);
+extern int rl_yank (int, int);
+extern int rl_yank_pop (int, int);
+extern int rl_yank_nth_arg (int, int);
+extern int rl_yank_last_arg (int, int);
+extern int rl_bracketed_paste_begin (int, int);
+/* Not available unless _WIN32 is defined. */
+#if defined (_WIN32)
+extern int rl_paste_from_clipboard (int, int);
+#endif
+
+/* Bindable commands for incremental searching. */
+extern int rl_reverse_search_history (int, int);
+extern int rl_forward_search_history (int, int);
+
+/* Bindable keyboard macro commands. */
+extern int rl_start_kbd_macro (int, int);
+extern int rl_end_kbd_macro (int, int);
+extern int rl_call_last_kbd_macro (int, int);
+extern int rl_print_last_kbd_macro (int, int);
+
+/* Bindable undo commands. */
+extern int rl_revert_line (int, int);
+extern int rl_undo_command (int, int);
+
+/* Bindable tilde expansion commands. */
+extern int rl_tilde_expand (int, int);
+
+/* Bindable terminal control commands. */
+extern int rl_restart_output (int, int);
+extern int rl_stop_output (int, int);
+
+/* Miscellaneous bindable commands. */
+extern int rl_abort (int, int);
+extern int rl_tty_status (int, int);
+
+/* Bindable commands for incremental and non-incremental history searching. */
+extern int rl_history_search_forward (int, int);
+extern int rl_history_search_backward (int, int);
+extern int rl_history_substr_search_forward (int, int);
+extern int rl_history_substr_search_backward (int, int);
+extern int rl_noninc_forward_search (int, int);
+extern int rl_noninc_reverse_search (int, int);
+extern int rl_noninc_forward_search_again (int, int);
+extern int rl_noninc_reverse_search_again (int, int);
+
+/* Bindable command used when inserting a matching close character. */
+extern int rl_insert_close (int, int);
+
+/* Not available unless READLINE_CALLBACKS is defined. */
+extern void rl_callback_handler_install (const char *, rl_vcpfunc_t *);
+extern void rl_callback_read_char (void);
+extern void rl_callback_handler_remove (void);
+extern void rl_callback_sigcleanup (void);
+
+/* Things for vi mode. Not available unless readline is compiled -DVI_MODE. */
+/* VI-mode bindable commands. */
+extern int rl_vi_redo (int, int);
+extern int rl_vi_undo (int, int);
+extern int rl_vi_yank_arg (int, int);
+extern int rl_vi_fetch_history (int, int);
+extern int rl_vi_search_again (int, int);
+extern int rl_vi_search (int, int);
+extern int rl_vi_complete (int, int);
+extern int rl_vi_tilde_expand (int, int);
+extern int rl_vi_prev_word (int, int);
+extern int rl_vi_next_word (int, int);
+extern int rl_vi_end_word (int, int);
+extern int rl_vi_insert_beg (int, int);
+extern int rl_vi_append_mode (int, int);
+extern int rl_vi_append_eol (int, int);
+extern int rl_vi_eof_maybe (int, int);
+extern int rl_vi_insertion_mode (int, int);
+extern int rl_vi_insert_mode (int, int);
+extern int rl_vi_movement_mode (int, int);
+extern int rl_vi_arg_digit (int, int);
+extern int rl_vi_change_case (int, int);
+extern int rl_vi_put (int, int);
+extern int rl_vi_column (int, int);
+extern int rl_vi_delete_to (int, int);
+extern int rl_vi_change_to (int, int);
+extern int rl_vi_yank_to (int, int);
+extern int rl_vi_yank_pop (int, int);
+extern int rl_vi_rubout (int, int);
+extern int rl_vi_delete (int, int);
+extern int rl_vi_back_to_indent (int, int);
+extern int rl_vi_unix_word_rubout (int, int);
+extern int rl_vi_first_print (int, int);
+extern int rl_vi_char_search (int, int);
+extern int rl_vi_match (int, int);
+extern int rl_vi_change_char (int, int);
+extern int rl_vi_subst (int, int);
+extern int rl_vi_overstrike (int, int);
+extern int rl_vi_overstrike_delete (int, int);
+extern int rl_vi_replace (int, int);
+extern int rl_vi_set_mark (int, int);
+extern int rl_vi_goto_mark (int, int);
+
+/* VI-mode utility functions. */
+extern int rl_vi_check (void);
+extern int rl_vi_domove (int, int *);
+extern int rl_vi_bracktype (int);
+
+extern void rl_vi_start_inserting (int, int, int);
+
+/* VI-mode pseudo-bindable commands, used as utility functions. */
+extern int rl_vi_fWord (int, int);
+extern int rl_vi_bWord (int, int);
+extern int rl_vi_eWord (int, int);
+extern int rl_vi_fword (int, int);
+extern int rl_vi_bword (int, int);
+extern int rl_vi_eword (int, int);
+
+/* **************************************************************** */
+/* */
+/* Well Published Functions */
+/* */
+/* **************************************************************** */
+
+/* Readline functions. */
+/* Read a line of input. Prompt with PROMPT. A NULL PROMPT means none. */
+extern char *readline (const char *);
+
+extern int rl_set_prompt (const char *);
+extern int rl_expand_prompt (char *);
+
+extern int rl_initialize (void);
+
+/* Undocumented; unused by readline */
+extern int rl_discard_argument (void);
+
+/* Utility functions to bind keys to readline commands. */
+extern int rl_add_defun (const char *, rl_command_func_t *, int);
+extern int rl_bind_key (int, rl_command_func_t *);
+extern int rl_bind_key_in_map (int, rl_command_func_t *, Keymap);
+extern int rl_unbind_key (int);
+extern int rl_unbind_key_in_map (int, Keymap);
+extern int rl_bind_key_if_unbound (int, rl_command_func_t *);
+extern int rl_bind_key_if_unbound_in_map (int, rl_command_func_t *, Keymap);
+extern int rl_unbind_function_in_map (rl_command_func_t *, Keymap);
+extern int rl_unbind_command_in_map (const char *, Keymap);
+extern int rl_bind_keyseq (const char *, rl_command_func_t *);
+extern int rl_bind_keyseq_in_map (const char *, rl_command_func_t *, Keymap);
+extern int rl_bind_keyseq_if_unbound (const char *, rl_command_func_t *);
+extern int rl_bind_keyseq_if_unbound_in_map (const char *, rl_command_func_t *, Keymap);
+extern int rl_generic_bind (int, const char *, char *, Keymap);
+
+extern char *rl_variable_value (const char *);
+extern int rl_variable_bind (const char *, const char *);
+
+/* Backwards compatibility, use rl_bind_keyseq_in_map instead. */
+extern int rl_set_key (const char *, rl_command_func_t *, Keymap);
+
+/* Backwards compatibility, use rl_generic_bind instead. */
+extern int rl_macro_bind (const char *, const char *, Keymap);
+
+/* Undocumented in the texinfo manual; not really useful to programs. */
+extern int rl_translate_keyseq (const char *, char *, int *);
+extern char *rl_untranslate_keyseq (int);
+
+extern rl_command_func_t *rl_named_function (const char *);
+extern rl_command_func_t *rl_function_of_keyseq (const char *, Keymap, int *);
+extern rl_command_func_t *rl_function_of_keyseq_len (const char *, size_t, Keymap, int *);
+extern int rl_trim_arg_from_keyseq (const char *, size_t, Keymap);
+
+extern void rl_list_funmap_names (void);
+extern char **rl_invoking_keyseqs_in_map (rl_command_func_t *, Keymap);
+extern char **rl_invoking_keyseqs (rl_command_func_t *);
+
+extern void rl_function_dumper (int);
+extern void rl_macro_dumper (int);
+extern void rl_variable_dumper (int);
+
+extern int rl_read_init_file (const char *);
+extern int rl_parse_and_bind (char *);
+
+/* Functions for manipulating keymaps. */
+extern Keymap rl_make_bare_keymap (void);
+extern int rl_empty_keymap (Keymap);
+extern Keymap rl_copy_keymap (Keymap);
+extern Keymap rl_make_keymap (void);
+extern void rl_discard_keymap (Keymap);
+extern void rl_free_keymap (Keymap);
+
+extern Keymap rl_get_keymap_by_name (const char *);
+extern char *rl_get_keymap_name (Keymap);
+extern void rl_set_keymap (Keymap);
+extern Keymap rl_get_keymap (void);
+
+extern int rl_set_keymap_name (const char *, Keymap);
+
+/* Undocumented; used internally only. */
+extern void rl_set_keymap_from_edit_mode (void);
+extern char *rl_get_keymap_name_from_edit_mode (void);
+
+/* Functions for manipulating the funmap, which maps command names to functions. */
+extern int rl_add_funmap_entry (const char *, rl_command_func_t *);
+extern const char **rl_funmap_names (void);
+/* Undocumented, only used internally -- there is only one funmap, and this
+ function may be called only once. */
+extern void rl_initialize_funmap (void);
+
+/* Utility functions for managing keyboard macros. */
+extern void rl_push_macro_input (char *);
+
+/* Functions for undoing, from undo.c */
+extern void rl_add_undo (enum undo_code, int, int, char *);
+extern void rl_free_undo_list (void);
+extern int rl_do_undo (void);
+extern int rl_begin_undo_group (void);
+extern int rl_end_undo_group (void);
+extern int rl_modifying (int, int);
+
+/* Functions for redisplay. */
+extern void rl_redisplay (void);
+extern int rl_on_new_line (void);
+extern int rl_on_new_line_with_prompt (void);
+extern int rl_forced_update_display (void);
+extern int rl_clear_visible_line (void);
+extern int rl_clear_message (void);
+extern int rl_reset_line_state (void);
+extern int rl_crlf (void);
+
+/* Functions to manage the mark and region, especially the notion of an
+ active mark and an active region. */
+extern void rl_keep_mark_active (void);
+
+extern void rl_activate_mark (void);
+extern void rl_deactivate_mark (void);
+extern int rl_mark_active_p (void);
+
+#if defined (USE_VARARGS) && defined (PREFER_STDARG)
+extern int rl_message (const char *, ...) __attribute__((__format__ (printf, 1, 2)));
+#else
+extern int rl_message ();
+#endif
+
+extern int rl_show_char (int);
+
+/* Undocumented in texinfo manual. */
+extern int rl_character_len (int, int);
+extern void rl_redraw_prompt_last_line (void);
+
+/* Save and restore internal prompt redisplay information. */
+extern void rl_save_prompt (void);
+extern void rl_restore_prompt (void);
+
+/* Modifying text. */
+extern void rl_replace_line (const char *, int);
+extern int rl_insert_text (const char *);
+extern int rl_delete_text (int, int);
+extern int rl_kill_text (int, int);
+extern char *rl_copy_text (int, int);
+
+/* Terminal and tty mode management. */
+extern void rl_prep_terminal (int);
+extern void rl_deprep_terminal (void);
+extern void rl_tty_set_default_bindings (Keymap);
+extern void rl_tty_unset_default_bindings (Keymap);
+
+extern int rl_tty_set_echoing (int);
+extern int rl_reset_terminal (const char *);
+extern void rl_resize_terminal (void);
+extern void rl_set_screen_size (int, int);
+extern void rl_get_screen_size (int *, int *);
+extern void rl_reset_screen_size (void);
+
+extern char *rl_get_termcap (const char *);
+
+/* Functions for character input. */
+extern int rl_stuff_char (int);
+extern int rl_execute_next (int);
+extern int rl_clear_pending_input (void);
+extern int rl_read_key (void);
+extern int rl_getc (FILE *);
+extern int rl_set_keyboard_input_timeout (int);
+
+/* Functions to set and reset timeouts. */
+extern int rl_set_timeout (unsigned int, unsigned int);
+extern int rl_timeout_remaining (unsigned int *, unsigned int *);
+
+#undef rl_clear_timeout
+#define rl_clear_timeout() rl_set_timeout (0, 0)
+
+/* `Public' utility functions . */
+extern void rl_extend_line_buffer (int);
+extern int rl_ding (void);
+extern int rl_alphabetic (int);
+extern void rl_free (void *);
+
+/* Readline signal handling, from signals.c */
+extern int rl_set_signals (void);
+extern int rl_clear_signals (void);
+extern void rl_cleanup_after_signal (void);
+extern void rl_reset_after_signal (void);
+extern void rl_free_line_state (void);
+
+extern int rl_pending_signal (void);
+extern void rl_check_signals (void);
+
+extern void rl_echo_signal_char (int);
+
+extern int rl_set_paren_blink_timeout (int);
+
+/* History management functions. */
+
+extern void rl_clear_history (void);
+
+/* Undocumented. */
+extern int rl_maybe_save_line (void);
+extern int rl_maybe_unsave_line (void);
+extern int rl_maybe_replace_line (void);
+
+/* Completion functions. */
+extern int rl_complete_internal (int);
+extern void rl_display_match_list (char **, int, int);
+
+extern char **rl_completion_matches (const char *, rl_compentry_func_t *);
+extern char *rl_username_completion_function (const char *, int);
+extern char *rl_filename_completion_function (const char *, int);
+
+extern int rl_completion_mode (rl_command_func_t *);
+
+#if 0
+/* Backwards compatibility (compat.c). These will go away sometime. */
+extern void free_undo_list (void);
+extern int maybe_save_line (void);
+extern int maybe_unsave_line (void);
+extern int maybe_replace_line (void);
+
+extern int ding (void);
+extern int alphabetic (int);
+extern int crlf (void);
+
+extern char **completion_matches (char *, rl_compentry_func_t *);
+extern char *username_completion_function (const char *, int);
+extern char *filename_completion_function (const char *, int);
+#endif
+
+/* **************************************************************** */
+/* */
+/* Well Published Variables */
+/* */
+/* **************************************************************** */
+
+/* The version of this incarnation of the readline library. */
+extern const char *rl_library_version; /* e.g., "4.2" */
+extern int rl_readline_version; /* e.g., 0x0402 */
+
+/* True if this is real GNU readline. */
+extern int rl_gnu_readline_p;
+
+/* Flags word encapsulating the current readline state. */
+extern unsigned long rl_readline_state;
+
+/* Says which editing mode readline is currently using. 1 means emacs mode;
+ 0 means vi mode. */
+extern int rl_editing_mode;
+
+/* Insert or overwrite mode for emacs mode. 1 means insert mode; 0 means
+ overwrite mode. Reset to insert mode on each input line. */
+extern int rl_insert_mode;
+
+/* The name of the calling program. You should initialize this to
+ whatever was in argv[0]. It is used when parsing conditionals. */
+extern const char *rl_readline_name;
+
+/* The prompt readline uses. This is set from the argument to
+ readline (), and should not be assigned to directly. */
+extern char *rl_prompt;
+
+/* The prompt string that is actually displayed by rl_redisplay. Public so
+ applications can more easily supply their own redisplay functions. */
+extern char *rl_display_prompt;
+
+/* The line buffer that is in use. */
+extern char *rl_line_buffer;
+
+/* The location of point, and end. */
+extern int rl_point;
+extern int rl_end;
+
+/* The mark, or saved cursor position. */
+extern int rl_mark;
+
+/* Flag to indicate that readline has finished with the current input
+ line and should return it. */
+extern int rl_done;
+
+/* Flag to indicate that readline has read an EOF character or read has
+ returned 0 or error, and is returning a NULL line as a result. */
+extern int rl_eof_found;
+
+/* If set to a character value, that will be the next keystroke read. */
+extern int rl_pending_input;
+
+/* Non-zero if we called this function from _rl_dispatch(). It's present
+ so functions can find out whether they were called from a key binding
+ or directly from an application. */
+extern int rl_dispatching;
+
+/* Non-zero if the user typed a numeric argument before executing the
+ current function. */
+extern int rl_explicit_arg;
+
+/* The current value of the numeric argument specified by the user. */
+extern int rl_numeric_arg;
+
+/* The address of the last command function Readline executed. */
+extern rl_command_func_t *rl_last_func;
+
+/* The name of the terminal to use. */
+extern const char *rl_terminal_name;
+
+/* The input and output streams. */
+extern FILE *rl_instream;
+extern FILE *rl_outstream;
+
+/* If non-zero, Readline gives values of LINES and COLUMNS from the environment
+ greater precedence than values fetched from the kernel when computing the
+ screen dimensions. */
+extern int rl_prefer_env_winsize;
+
+/* If non-zero, then this is the address of a function to call just
+ before readline_internal () prints the first prompt. */
+extern rl_hook_func_t *rl_startup_hook;
+
+/* If non-zero, this is the address of a function to call just before
+ readline_internal_setup () returns and readline_internal starts
+ reading input characters. */
+extern rl_hook_func_t *rl_pre_input_hook;
+
+/* The address of a function to call periodically while Readline is
+ awaiting character input, or NULL, for no event handling. */
+extern rl_hook_func_t *rl_event_hook;
+
+/* The address of a function to call if a read is interrupted by a signal. */
+extern rl_hook_func_t *rl_signal_event_hook;
+
+extern rl_hook_func_t *rl_timeout_event_hook;
+
+/* The address of a function to call if Readline needs to know whether or not
+ there is data available from the current input source. */
+extern rl_hook_func_t *rl_input_available_hook;
+
+/* The address of the function to call to fetch a character from the current
+ Readline input stream */
+extern rl_getc_func_t *rl_getc_function;
+
+extern rl_voidfunc_t *rl_redisplay_function;
+
+extern rl_vintfunc_t *rl_prep_term_function;
+extern rl_voidfunc_t *rl_deprep_term_function;
+
+/* Dispatch variables. */
+extern Keymap rl_executing_keymap;
+extern Keymap rl_binding_keymap;
+
+extern int rl_executing_key;
+extern char *rl_executing_keyseq;
+extern int rl_key_sequence_length;
+
+/* Display variables. */
+/* If non-zero, readline will erase the entire line, including any prompt,
+ if the only thing typed on an otherwise-blank line is something bound to
+ rl_newline. */
+extern int rl_erase_empty_line;
+
+/* If non-zero, the application has already printed the prompt (rl_prompt)
+ before calling readline, so readline should not output it the first time
+ redisplay is done. */
+extern int rl_already_prompted;
+
+/* A non-zero value means to read only this many characters rather than
+ up to a character bound to accept-line. */
+extern int rl_num_chars_to_read;
+
+/* The text of a currently-executing keyboard macro. */
+extern char *rl_executing_macro;
+
+/* Variables to control readline signal handling. */
+/* If non-zero, readline will install its own signal handlers for
+ SIGINT, SIGTERM, SIGQUIT, SIGALRM, SIGTSTP, SIGTTIN, and SIGTTOU. */
+extern int rl_catch_signals;
+
+/* If non-zero, readline will install a signal handler for SIGWINCH
+ that also attempts to call any calling application's SIGWINCH signal
+ handler. Note that the terminal is not cleaned up before the
+ application's signal handler is called; use rl_cleanup_after_signal()
+ to do that. */
+extern int rl_catch_sigwinch;
+
+/* If non-zero, the readline SIGWINCH handler will modify LINES and
+ COLUMNS in the environment. */
+extern int rl_change_environment;
+
+/* Completion variables. */
+/* Pointer to the generator function for completion_matches ().
+ NULL means to use rl_filename_completion_function (), the default
+ filename completer. */
+extern rl_compentry_func_t *rl_completion_entry_function;
+
+/* Optional generator for menu completion. Default is
+ rl_completion_entry_function (rl_filename_completion_function). */
+extern rl_compentry_func_t *rl_menu_completion_entry_function;
+
+/* If rl_ignore_some_completions_function is non-NULL it is the address
+ of a function to call after all of the possible matches have been
+ generated, but before the actual completion is done to the input line.
+ The function is called with one argument; a NULL terminated array
+ of (char *). If your function removes any of the elements, they
+ must be free()'ed. */
+extern rl_compignore_func_t *rl_ignore_some_completions_function;
+
+/* Pointer to alternative function to create matches.
+ Function is called with TEXT, START, and END.
+ START and END are indices in RL_LINE_BUFFER saying what the boundaries
+ of TEXT are.
+ If this function exists and returns NULL then call the value of
+ rl_completion_entry_function to try to match, otherwise use the
+ array of strings returned. */
+extern rl_completion_func_t *rl_attempted_completion_function;
+
+/* The basic list of characters that signal a break between words for the
+ completer routine. The initial contents of this variable is what
+ breaks words in the shell, i.e. "n\"\\'`@$>". */
+extern const char *rl_basic_word_break_characters;
+
+/* The list of characters that signal a break between words for
+ rl_complete_internal. The default list is the contents of
+ rl_basic_word_break_characters. */
+extern const char *rl_completer_word_break_characters;
+
+/* Hook function to allow an application to set the completion word
+ break characters before readline breaks up the line. Allows
+ position-dependent word break characters. */
+extern rl_cpvfunc_t *rl_completion_word_break_hook;
+
+/* List of characters which can be used to quote a substring of the line.
+ Completion occurs on the entire substring, and within the substring
+ rl_completer_word_break_characters are treated as any other character,
+ unless they also appear within this list. */
+extern const char *rl_completer_quote_characters;
+
+/* List of quote characters which cause a word break. */
+extern const char *rl_basic_quote_characters;
+
+/* List of characters that need to be quoted in filenames by the completer. */
+extern const char *rl_filename_quote_characters;
+
+/* List of characters that are word break characters, but should be left
+ in TEXT when it is passed to the completion function. The shell uses
+ this to help determine what kind of completing to do. */
+extern const char *rl_special_prefixes;
+
+/* If non-zero, then this is the address of a function to call when
+ completing on a directory name. The function is called with
+ the address of a string (the current directory name) as an arg. It
+ changes what is displayed when the possible completions are printed
+ or inserted. The directory completion hook should perform
+ any necessary dequoting. This function should return 1 if it modifies
+ the directory name pointer passed as an argument. If the directory
+ completion hook returns 0, it should not modify the directory name
+ pointer passed as an argument. */
+extern rl_icppfunc_t *rl_directory_completion_hook;
+
+/* If non-zero, this is the address of a function to call when completing
+ a directory name. This function takes the address of the directory name
+ to be modified as an argument. Unlike rl_directory_completion_hook, it
+ only modifies the directory name used in opendir(2), not what is displayed
+ when the possible completions are printed or inserted. If set, it takes
+ precedence over rl_directory_completion_hook. The directory rewrite
+ hook should perform any necessary dequoting. This function has the same
+ return value properties as the directory_completion_hook.
+
+ I'm not happy with how this works yet, so it's undocumented. I'm trying
+ it in bash to see how well it goes. */
+extern rl_icppfunc_t *rl_directory_rewrite_hook;
+
+/* If non-zero, this is the address of a function for the completer to call
+ before deciding which character to append to a completed name. It should
+ modify the directory name passed as an argument if appropriate, and return
+ non-zero if it modifies the name. This should not worry about dequoting
+ the filename; that has already happened by the time it gets here. */
+extern rl_icppfunc_t *rl_filename_stat_hook;
+
+/* If non-zero, this is the address of a function to call when reading
+ directory entries from the filesystem for completion and comparing
+ them to the partial word to be completed. The function should
+ either return its first argument (if no conversion takes place) or
+ newly-allocated memory. This can, for instance, convert filenames
+ between character sets for comparison against what's typed at the
+ keyboard. The returned value is what is added to the list of
+ matches. The second argument is the length of the filename to be
+ converted. */
+extern rl_dequote_func_t *rl_filename_rewrite_hook;
+
+/* Backwards compatibility with previous versions of readline. */
+#define rl_symbolic_link_hook rl_directory_completion_hook
+
+/* If non-zero, then this is the address of a function to call when
+ completing a word would normally display the list of possible matches.
+ This function is called instead of actually doing the display.
+ It takes three arguments: (char **matches, int num_matches, int max_length)
+ where MATCHES is the array of strings that matched, NUM_MATCHES is the
+ number of strings in that array, and MAX_LENGTH is the length of the
+ longest string in that array. */
+extern rl_compdisp_func_t *rl_completion_display_matches_hook;
+
+/* Non-zero means that the results of the matches are to be treated
+ as filenames. This is ALWAYS zero on entry, and can only be changed
+ within a completion entry finder function. */
+extern int rl_filename_completion_desired;
+
+/* Non-zero means that the results of the matches are to be quoted using
+ double quotes (or an application-specific quoting mechanism) if the
+ filename contains any characters in rl_word_break_chars. This is
+ ALWAYS non-zero on entry, and can only be changed within a completion
+ entry finder function. */
+extern int rl_filename_quoting_desired;
+
+/* Set to a function to quote a filename in an application-specific fashion.
+ Called with the text to quote, the type of match found (single or multiple)
+ and a pointer to the quoting character to be used, which the function can
+ reset if desired. */
+extern rl_quote_func_t *rl_filename_quoting_function;
+
+/* Function to call to remove quoting characters from a filename. Called
+ before completion is attempted, so the embedded quotes do not interfere
+ with matching names in the file system. */
+extern rl_dequote_func_t *rl_filename_dequoting_function;
+
+/* Function to call to decide whether or not a word break character is
+ quoted. If a character is quoted, it does not break words for the
+ completer. */
+extern rl_linebuf_func_t *rl_char_is_quoted_p;
+
+/* Non-zero means to suppress normal filename completion after the
+ user-specified completion function has been called. */
+extern int rl_attempted_completion_over;
+
+/* Set to a character describing the type of completion being attempted by
+ rl_complete_internal; available for use by application completion
+ functions. */
+extern int rl_completion_type;
+
+/* Set to the last key used to invoke one of the completion functions */
+extern int rl_completion_invoking_key;
+
+/* Up to this many items will be displayed in response to a
+ possible-completions call. After that, we ask the user if she
+ is sure she wants to see them all. The default value is 100. */
+extern int rl_completion_query_items;
+
+/* Character appended to completed words when at the end of the line. The
+ default is a space. Nothing is added if this is '\0'. */
+extern int rl_completion_append_character;
+
+/* If set to non-zero by an application completion function,
+ rl_completion_append_character will not be appended. */
+extern int rl_completion_suppress_append;
+
+/* Set to any quote character readline thinks it finds before any application
+ completion function is called. */
+extern int rl_completion_quote_character;
+
+/* Set to a non-zero value if readline found quoting anywhere in the word to
+ be completed; set before any application completion function is called. */
+extern int rl_completion_found_quote;
+
+/* If non-zero, the completion functions don't append any closing quote.
+ This is set to 0 by rl_complete_internal and may be changed by an
+ application-specific completion function. */
+extern int rl_completion_suppress_quote;
+
+/* If non-zero, readline will sort the completion matches. On by default. */
+extern int rl_sort_completion_matches;
+
+/* If non-zero, a slash will be appended to completed filenames that are
+ symbolic links to directory names, subject to the value of the
+ mark-directories variable (which is user-settable). This exists so
+ that application completion functions can override the user's preference
+ (set via the mark-symlinked-directories variable) if appropriate.
+ It's set to the value of _rl_complete_mark_symlink_dirs in
+ rl_complete_internal before any application-specific completion
+ function is called, so without that function doing anything, the user's
+ preferences are honored. */
+extern int rl_completion_mark_symlink_dirs;
+
+/* If non-zero, then disallow duplicates in the matches. */
+extern int rl_ignore_completion_duplicates;
+
+/* If this is non-zero, completion is (temporarily) inhibited, and the
+ completion character will be inserted as any other. */
+extern int rl_inhibit_completion;
+
+/* Applications can set this to non-zero to have readline's signal handlers
+ installed during the entire duration of reading a complete line, as in
+ readline-6.2. This should be used with care, because it can result in
+ readline receiving signals and not handling them until it's called again
+ via rl_callback_read_char, thereby stealing them from the application.
+ By default, signal handlers are only active while readline is active. */
+extern int rl_persistent_signal_handlers;
+
+/* Input error; can be returned by (*rl_getc_function) if readline is reading
+ a top-level command (RL_ISSTATE (RL_STATE_READCMD)). */
+#define READERR (-2)
+
+/* Definitions available for use by readline clients. */
+#define RL_PROMPT_START_IGNORE '\001'
+#define RL_PROMPT_END_IGNORE '\002'
+
+/* Possible values for do_replace argument to rl_filename_quoting_function,
+ called by rl_complete_internal. */
+#define NO_MATCH 0
+#define SINGLE_MATCH 1
+#define MULT_MATCH 2
+
+/* Possible state values for rl_readline_state */
+#define RL_STATE_NONE 0x000000 /* no state; before first call */
+
+#define RL_STATE_INITIALIZING 0x0000001 /* initializing */
+#define RL_STATE_INITIALIZED 0x0000002 /* initialization done */
+#define RL_STATE_TERMPREPPED 0x0000004 /* terminal is prepped */
+#define RL_STATE_READCMD 0x0000008 /* reading a command key */
+#define RL_STATE_METANEXT 0x0000010 /* reading input after ESC */
+#define RL_STATE_DISPATCHING 0x0000020 /* dispatching to a command */
+#define RL_STATE_MOREINPUT 0x0000040 /* reading more input in a command function */
+#define RL_STATE_ISEARCH 0x0000080 /* doing incremental search */
+#define RL_STATE_NSEARCH 0x0000100 /* doing non-inc search */
+#define RL_STATE_SEARCH 0x0000200 /* doing a history search */
+#define RL_STATE_NUMERICARG 0x0000400 /* reading numeric argument */
+#define RL_STATE_MACROINPUT 0x0000800 /* getting input from a macro */
+#define RL_STATE_MACRODEF 0x0001000 /* defining keyboard macro */
+#define RL_STATE_OVERWRITE 0x0002000 /* overwrite mode */
+#define RL_STATE_COMPLETING 0x0004000 /* doing completion */
+#define RL_STATE_SIGHANDLER 0x0008000 /* in readline sighandler */
+#define RL_STATE_UNDOING 0x0010000 /* doing an undo */
+#define RL_STATE_INPUTPENDING 0x0020000 /* rl_execute_next called */
+#define RL_STATE_TTYCSAVED 0x0040000 /* tty special chars saved */
+#define RL_STATE_CALLBACK 0x0080000 /* using the callback interface */
+#define RL_STATE_VIMOTION 0x0100000 /* reading vi motion arg */
+#define RL_STATE_MULTIKEY 0x0200000 /* reading multiple-key command */
+#define RL_STATE_VICMDONCE 0x0400000 /* entered vi command mode at least once */
+#define RL_STATE_CHARSEARCH 0x0800000 /* vi mode char search */
+#define RL_STATE_REDISPLAYING 0x1000000 /* updating terminal display */
+
+#define RL_STATE_DONE 0x2000000 /* done; accepted line */
+#define RL_STATE_TIMEOUT 0x4000000 /* done; timed out */
+#define RL_STATE_EOF 0x8000000 /* done; got eof on read */
+
+#define RL_SETSTATE(x) (rl_readline_state |= (x))
+#define RL_UNSETSTATE(x) (rl_readline_state &= ~(x))
+#define RL_ISSTATE(x) (rl_readline_state & (x))
+
+struct readline_state {
+ /* line state */
+ int point;
+ int end;
+ int mark;
+ int buflen;
+ char *buffer;
+ UNDO_LIST *ul;
+ char *prompt;
+
+ /* global state */
+ int rlstate;
+ int done;
+ Keymap kmap;
+
+ /* input state */
+ rl_command_func_t *lastfunc;
+ int insmode;
+ int edmode;
+ char *kseq;
+ int kseqlen;
+
+ int pendingin;
+ FILE *inf;
+ FILE *outf;
+ char *macro;
+
+ /* signal state */
+ int catchsigs;
+ int catchsigwinch;
+
+ /* search state */
+
+ /* completion state */
+ rl_compentry_func_t *entryfunc;
+ rl_compentry_func_t *menuentryfunc;
+ rl_compignore_func_t *ignorefunc;
+ rl_completion_func_t *attemptfunc;
+ const char *wordbreakchars;
+
+ /* options state */
+
+ /* hook state */
+
+ /* reserved for future expansion, so the struct size doesn't change */
+ char reserved[64];
+};
+
+extern int rl_save_state (struct readline_state *);
+extern int rl_restore_state (struct readline_state *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _READLINE_H_ */
diff --git a/third_party/readline/rlconf.h b/third_party/readline/rlconf.h
new file mode 100644
index 000000000..b6d6a2f12
--- /dev/null
+++ b/third_party/readline/rlconf.h
@@ -0,0 +1,79 @@
+/* rlconf.h -- readline configuration definitions */
+
+/* Copyright (C) 1992-2015 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#if !defined (_RLCONF_H_)
+#define _RLCONF_H_
+
+/* Define this if you want the vi-mode editing available. */
+#define VI_MODE
+
+/* Define this to get an indication of file type when listing completions. */
+#define VISIBLE_STATS
+
+/* Define this to get support for colors when listing completions and in
+ other places. */
+#define COLOR_SUPPORT
+
+/* This definition is needed by readline.c, rltty.c, and signals.c. */
+/* If on, then readline handles signals in a way that doesn't suck. */
+#define HANDLE_SIGNALS
+
+/* Ugly but working hack for binding prefix meta. */
+#define PREFIX_META_HACK
+
+/* The next-to-last-ditch effort file name for a user-specific init file. */
+#define DEFAULT_INPUTRC "~/.inputrc"
+
+/* The ultimate last-ditch filename for an init file -- system-wide. */
+#define SYS_INPUTRC "/etc/inputrc"
+
+/* If defined, expand tabs to spaces. */
+#define DISPLAY_TABS
+
+/* If defined, use the terminal escape sequence to move the cursor forward
+ over a character when updating the line rather than rewriting it. */
+/* #define HACK_TERMCAP_MOTION */
+
+/* The string inserted by the `insert comment' command. */
+#define RL_COMMENT_BEGIN_DEFAULT "#"
+
+/* Define this if you want code that allows readline to be used in an
+ X `callback' style. */
+#define READLINE_CALLBACKS
+
+/* Define this if you want the cursor to indicate insert or overwrite mode. */
+/* #define CURSOR_MODE */
+
+/* Define this if you want to enable code that talks to the Linux kernel
+ tty auditing system. */
+/* #define ENABLE_TTY_AUDIT_SUPPORT */
+
+/* Defaults for the various editing mode indicators, inserted at the beginning
+ of the last (maybe only) line of the prompt if show-mode-in-prompt is on */
+#define RL_EMACS_MODESTR_DEFAULT "@"
+#define RL_EMACS_MODESTR_DEFLEN 1
+
+#define RL_VI_INS_MODESTR_DEFAULT "(ins)"
+#define RL_VI_INS_MODESTR_DEFLEN 5
+#define RL_VI_CMD_MODESTR_DEFAULT "(cmd)"
+#define RL_VI_CMD_MODESTR_DEFLEN 5
+
+#endif /* _RLCONF_H_ */
diff --git a/third_party/readline/rldefs.h b/third_party/readline/rldefs.h
new file mode 100644
index 000000000..98577a64b
--- /dev/null
+++ b/third_party/readline/rldefs.h
@@ -0,0 +1,166 @@
+/* rldefs.h -- an attempt to isolate some of the system-specific defines
+ for readline. This should be included after any files that define
+ system-specific constants like _POSIX_VERSION or USG. */
+
+/* Copyright (C) 1987-2021 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#if !defined (_RLDEFS_H_)
+#define _RLDEFS_H_
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include "rlstdc.h"
+
+#if defined (STRCOLL_BROKEN)
+# undef HAVE_STRCOLL
+#endif
+
+#if defined (_POSIX_VERSION) && !defined (TERMIOS_MISSING)
+# define TERMIOS_TTY_DRIVER
+#else
+# if defined (HAVE_TERMIO_H)
+# define TERMIO_TTY_DRIVER
+# else
+# if !defined (__MINGW32__)
+# define NEW_TTY_DRIVER
+# else
+# define NO_TTY_DRIVER
+# endif
+# endif
+#endif
+
+/* Posix macro to check file in statbuf for directory-ness.
+ This requires that be included before this test. */
+#if defined (S_IFDIR) && !defined (S_ISDIR)
+# define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
+#endif
+
+/* Decide which flavor of the header file describing the C library
+ string functions to include and include it. */
+
+#if defined (HAVE_STRING_H)
+# include
+#else /* !HAVE_STRING_H */
+# include
+#endif /* !HAVE_STRING_H */
+
+#if !defined (strchr) && !defined (__STDC__)
+extern char *strchr (), *strrchr ();
+#endif /* !strchr && !__STDC__ */
+
+#if defined (PREFER_STDARG)
+# include
+#else
+# if defined (PREFER_VARARGS)
+# include
+# endif
+#endif
+
+#if defined (HAVE_STRCASECMP)
+#define _rl_stricmp strcasecmp
+#define _rl_strnicmp strncasecmp
+#else
+extern int _rl_stricmp (const char *, const char *);
+extern int _rl_strnicmp (const char *, const char *, int);
+#endif
+
+#if defined (HAVE_STRPBRK) && !defined (HAVE_MULTIBYTE)
+# define _rl_strpbrk(a,b) strpbrk((a),(b))
+#else
+extern char *_rl_strpbrk (const char *, const char *);
+#endif
+
+#if !defined (emacs_mode)
+# define no_mode -1
+# define vi_mode 0
+# define emacs_mode 1
+#endif
+
+#if !defined (RL_IM_INSERT)
+# define RL_IM_INSERT 1
+# define RL_IM_OVERWRITE 0
+#
+# define RL_IM_DEFAULT RL_IM_INSERT
+#endif
+
+/* If you cast map[key].function to type (Keymap) on a Cray,
+ the compiler takes the value of map[key].function and
+ divides it by 4 to convert between pointer types (pointers
+ to functions and pointers to structs are different sizes).
+ This is not what is wanted. */
+#if defined (CRAY)
+# define FUNCTION_TO_KEYMAP(map, key) (Keymap)((int)map[key].function)
+# define KEYMAP_TO_FUNCTION(data) (rl_command_func_t *)((int)(data))
+#else
+# define FUNCTION_TO_KEYMAP(map, key) (Keymap)(map[key].function)
+# define KEYMAP_TO_FUNCTION(data) (rl_command_func_t *)(data)
+#endif
+
+#ifndef savestring
+#define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
+#endif
+
+/* Possible values for _rl_bell_preference. */
+#define NO_BELL 0
+#define AUDIBLE_BELL 1
+#define VISIBLE_BELL 2
+
+/* Definitions used when searching the line for characters. */
+/* NOTE: it is necessary that opposite directions are inverses */
+#define FTO 1 /* forward to */
+#define BTO -1 /* backward to */
+#define FFIND 2 /* forward find */
+#define BFIND -2 /* backward find */
+
+/* Possible values for the found_quote flags word used by the completion
+ functions. It says what kind of (shell-like) quoting we found anywhere
+ in the line. */
+#define RL_QF_SINGLE_QUOTE 0x01
+#define RL_QF_DOUBLE_QUOTE 0x02
+#define RL_QF_BACKSLASH 0x04
+#define RL_QF_OTHER_QUOTE 0x08
+
+/* Default readline line buffer length. */
+#define DEFAULT_BUFFER_SIZE 256
+
+#if !defined (STREQ)
+#define STREQ(a, b) (((a)[0] == (b)[0]) && (strcmp ((a), (b)) == 0))
+#define STREQN(a, b, n) (((n) == 0) ? (1) \
+ : ((a)[0] == (b)[0]) && (strncmp ((a), (b), (n)) == 0))
+#endif
+
+#if !defined (RL_STRLEN)
+# define RL_STRLEN(s) (((s) && (s)[0]) ? ((s)[1] ? ((s)[2] ? strlen(s) : 2) : 1) : 0)
+#endif
+
+#if !defined (FREE)
+# define FREE(x) if (x) free (x)
+#endif
+
+#if !defined (SWAP)
+# define SWAP(s, e) do { int t; t = s; s = e; e = t; } while (0)
+#endif
+
+/* CONFIGURATION SECTION */
+#include "rlconf.h"
+
+#endif /* !_RLDEFS_H_ */
diff --git a/third_party/readline/rlmbutil.h b/third_party/readline/rlmbutil.h
new file mode 100644
index 000000000..d9060572e
--- /dev/null
+++ b/third_party/readline/rlmbutil.h
@@ -0,0 +1,225 @@
+/* rlmbutil.h -- utility functions for multibyte characters. */
+
+/* Copyright (C) 2001-2021 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#if !defined (_RL_MBUTIL_H_)
+#define _RL_MBUTIL_H_
+
+#include "rlstdc.h"
+
+/************************************************/
+/* check multibyte capability for I18N code */
+/************************************************/
+
+/* For platforms which support the ISO C amendment 1 functionality we
+ support user defined character classes. */
+ /* Solaris 2.5 has a bug: must be included before . */
+#if defined (HAVE_WCTYPE_H) && defined (HAVE_WCHAR_H) && defined (HAVE_LOCALE_H)
+# include
+# include
+# if defined (HAVE_ISWCTYPE) && \
+ defined (HAVE_ISWLOWER) && \
+ defined (HAVE_ISWUPPER) && \
+ defined (HAVE_MBSRTOWCS) && \
+ defined (HAVE_MBRTOWC) && \
+ defined (HAVE_MBRLEN) && \
+ defined (HAVE_TOWLOWER) && \
+ defined (HAVE_TOWUPPER) && \
+ defined (HAVE_WCHAR_T) && \
+ defined (HAVE_WCWIDTH)
+ /* system is supposed to support XPG5 */
+# define HANDLE_MULTIBYTE 1
+# endif
+#endif
+
+/* If we don't want multibyte chars even on a system that supports them, let
+ the configuring user turn multibyte support off. */
+#if defined (NO_MULTIBYTE_SUPPORT)
+# undef HANDLE_MULTIBYTE
+#endif
+
+/* Some systems, like BeOS, have multibyte encodings but lack mbstate_t. */
+#if HANDLE_MULTIBYTE && !defined (HAVE_MBSTATE_T)
+# define wcsrtombs(dest, src, len, ps) (wcsrtombs) (dest, src, len, 0)
+# define mbsrtowcs(dest, src, len, ps) (mbsrtowcs) (dest, src, len, 0)
+# define wcrtomb(s, wc, ps) (wcrtomb) (s, wc, 0)
+# define mbrtowc(pwc, s, n, ps) (mbrtowc) (pwc, s, n, 0)
+# define mbrlen(s, n, ps) (mbrlen) (s, n, 0)
+# define mbstate_t int
+#endif
+
+/* Make sure MB_LEN_MAX is at least 16 on systems that claim to be able to
+ handle multibyte chars (some systems define MB_LEN_MAX as 1) */
+#ifdef HANDLE_MULTIBYTE
+# include
+# if defined(MB_LEN_MAX) && (MB_LEN_MAX < 16)
+# undef MB_LEN_MAX
+# endif
+# if !defined (MB_LEN_MAX)
+# define MB_LEN_MAX 16
+# endif
+#endif
+
+/************************************************/
+/* end of multibyte capability checks for I18N */
+/************************************************/
+
+/*
+ * wchar_t doesn't work for 32-bit values on Windows using MSVC
+ */
+#ifdef WCHAR_T_BROKEN
+# define WCHAR_T char32_t
+# define MBRTOWC mbrtoc32
+# define WCRTOMB c32rtomb
+#else /* normal systems */
+# define WCHAR_T wchar_t
+# define MBRTOWC mbrtowc
+# define WCRTOMB wcrtomb
+#endif
+
+/*
+ * Flags for _rl_find_prev_mbchar and _rl_find_next_mbchar:
+ *
+ * MB_FIND_ANY find any multibyte character
+ * MB_FIND_NONZERO find a non-zero-width multibyte character
+ */
+
+#define MB_FIND_ANY 0x00
+#define MB_FIND_NONZERO 0x01
+
+extern int _rl_find_prev_mbchar (char *, int, int);
+extern int _rl_find_next_mbchar (char *, int, int, int);
+
+#ifdef HANDLE_MULTIBYTE
+
+extern int _rl_compare_chars (char *, int, mbstate_t *, char *, int, mbstate_t *);
+extern int _rl_get_char_len (char *, mbstate_t *);
+extern int _rl_adjust_point (char *, int, mbstate_t *);
+
+extern int _rl_read_mbchar (char *, int);
+extern int _rl_read_mbstring (int, char *, int);
+
+extern int _rl_is_mbchar_matched (char *, int, int, char *, int);
+
+extern WCHAR_T _rl_char_value (char *, int);
+extern int _rl_walphabetic (WCHAR_T);
+
+#define _rl_to_wupper(wc) (iswlower (wc) ? towupper (wc) : (wc))
+#define _rl_to_wlower(wc) (iswupper (wc) ? towlower (wc) : (wc))
+
+#define MB_NEXTCHAR(b,s,c,f) \
+ ((MB_CUR_MAX > 1 && rl_byte_oriented == 0) \
+ ? _rl_find_next_mbchar ((b), (s), (c), (f)) \
+ : ((s) + (c)))
+#define MB_PREVCHAR(b,s,f) \
+ ((MB_CUR_MAX > 1 && rl_byte_oriented == 0) \
+ ? _rl_find_prev_mbchar ((b), (s), (f)) \
+ : ((s) - 1))
+
+#define MB_INVALIDCH(x) ((x) == (size_t)-1 || (x) == (size_t)-2)
+#define MB_NULLWCH(x) ((x) == 0)
+
+/* Try and shortcut the printable ascii characters to cut down the number of
+ calls to a libc wcwidth() */
+static inline int
+_rl_wcwidth (WCHAR_T wc)
+{
+ switch (wc)
+ {
+ case ' ': case '!': case '"': case '#': case '%':
+ case '&': case '\'': case '(': case ')': case '*':
+ case '+': case ',': case '-': case '.': case '/':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case ':': case ';': case '<': case '=': case '>':
+ case '?':
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F': case 'G': case 'H': case 'I': case 'J':
+ case 'K': case 'L': case 'M': case 'N': case 'O':
+ case 'P': case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X': case 'Y':
+ case 'Z':
+ case '[': case '\\': case ']': case '^': case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f': case 'g': case 'h': case 'i': case 'j':
+ case 'k': case 'l': case 'm': case 'n': case 'o':
+ case 'p': case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x': case 'y':
+ case 'z': case '{': case '|': case '}': case '~':
+ return 1;
+ default:
+ return wcwidth (wc);
+ }
+}
+
+/* Unicode combining characters range from U+0300 to U+036F */
+#define UNICODE_COMBINING_CHAR(x) ((x) >= 768 && (x) <= 879)
+
+#if defined (WCWIDTH_BROKEN)
+# define WCWIDTH(wc) ((_rl_utf8locale && UNICODE_COMBINING_CHAR(wc)) ? 0 : _rl_wcwidth(wc))
+#else
+# define WCWIDTH(wc) _rl_wcwidth(wc)
+#endif
+
+#if defined (WCWIDTH_BROKEN)
+# define IS_COMBINING_CHAR(x) (WCWIDTH(x) == 0 && iswcntrl(x) == 0)
+#else
+# define IS_COMBINING_CHAR(x) (WCWIDTH(x) == 0)
+#endif
+
+#define UTF8_SINGLEBYTE(c) (((c) & 0x80) == 0)
+#define UTF8_MBFIRSTCHAR(c) (((c) & 0xc0) == 0xc0)
+#define UTF8_MBCHAR(c) (((c) & 0xc0) == 0x80)
+
+#else /* !HANDLE_MULTIBYTE */
+
+#undef MB_LEN_MAX
+#undef MB_CUR_MAX
+
+#define MB_LEN_MAX 1
+#define MB_CUR_MAX 1
+
+#define _rl_find_prev_mbchar(b, i, f) (((i) == 0) ? (i) : ((i) - 1))
+#define _rl_find_next_mbchar(b, i1, i2, f) ((i1) + (i2))
+
+#define _rl_char_value(buf,ind) ((buf)[(ind)])
+
+#define _rl_walphabetic(c) (rl_alphabetic (c))
+
+#define _rl_to_wupper(c) (_rl_to_upper (c))
+#define _rl_to_wlower(c) (_rl_to_lower (c))
+
+#define MB_NEXTCHAR(b,s,c,f) ((s) + (c))
+#define MB_PREVCHAR(b,s,f) ((s) - 1)
+
+#define MB_INVALIDCH(x) (0)
+#define MB_NULLWCH(x) (0)
+
+#define UTF8_SINGLEBYTE(c) (1)
+
+#if !defined (HAVE_WCHAR_T) && !defined (wchar_t)
+# define wchar_t int
+#endif
+
+#endif /* !HANDLE_MULTIBYTE */
+
+extern int rl_byte_oriented;
+
+#endif /* _RL_MBUTIL_H_ */
diff --git a/third_party/readline/rlprivate.h b/third_party/readline/rlprivate.h
new file mode 100644
index 000000000..d87d07a72
--- /dev/null
+++ b/third_party/readline/rlprivate.h
@@ -0,0 +1,633 @@
+/* rlprivate.h -- functions and variables global to the readline library,
+ but not intended for use by applications. */
+
+/* Copyright (C) 1999-2022 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#if !defined (_RL_PRIVATE_H_)
+#define _RL_PRIVATE_H_
+
+#include "rlconf.h" /* for VISIBLE_STATS */
+#include "rlstdc.h"
+#include "posixjmp.h" /* defines procenv_t */
+#include "rlmbutil.h" /* for HANDLE_MULTIBYTE */
+
+/*************************************************************************
+ * *
+ * Convenience definitions *
+ * *
+ *************************************************************************/
+
+#define EMACS_MODE() (rl_editing_mode == emacs_mode)
+#define VI_COMMAND_MODE() (rl_editing_mode == vi_mode && _rl_keymap == vi_movement_keymap)
+#define VI_INSERT_MODE() (rl_editing_mode == vi_mode && _rl_keymap == vi_insertion_keymap)
+
+#define RL_CHECK_SIGNALS() \
+ do { \
+ if (_rl_caught_signal) _rl_signal_handler (_rl_caught_signal); \
+ } while (0)
+
+#define RL_SIG_RECEIVED() (_rl_caught_signal != 0)
+#define RL_SIGINT_RECEIVED() (_rl_caught_signal == SIGINT)
+#define RL_SIGWINCH_RECEIVED() (_rl_caught_signal == SIGWINCH)
+
+#define CUSTOM_REDISPLAY_FUNC() (rl_redisplay_function != rl_redisplay)
+#define CUSTOM_INPUT_FUNC() (rl_getc_function != rl_getc)
+
+/*************************************************************************
+ * *
+ * Global structs undocumented in texinfo manual and not in readline.h *
+ * *
+ *************************************************************************/
+/* search types */
+#define RL_SEARCH_ISEARCH 0x01 /* incremental search */
+#define RL_SEARCH_NSEARCH 0x02 /* non-incremental search */
+#define RL_SEARCH_CSEARCH 0x04 /* intra-line char search */
+
+/* search flags */
+#define SF_REVERSE 0x01
+#define SF_FOUND 0x02
+#define SF_FAILED 0x04
+#define SF_CHGKMAP 0x08
+#define SF_PATTERN 0x10
+#define SF_NOCASE 0x20 /* unused so far */
+
+typedef struct __rl_search_context
+{
+ int type;
+ int sflags;
+
+ char *search_string;
+ int search_string_index;
+ int search_string_size;
+
+ char **lines;
+ char *allocated_line;
+ int hlen;
+ int hindex;
+
+ int save_point;
+ int save_mark;
+ int save_line;
+ int last_found_line;
+ char *prev_line_found;
+
+ UNDO_LIST *save_undo_list;
+
+ Keymap keymap; /* used when dispatching commands in search string */
+ Keymap okeymap; /* original keymap */
+
+ int history_pos;
+ int direction;
+
+ int prevc;
+ int lastc;
+#if defined (HANDLE_MULTIBYTE)
+ char mb[MB_LEN_MAX];
+ char pmb[MB_LEN_MAX];
+#endif
+
+ char *sline;
+ int sline_len;
+ int sline_index;
+
+ char *search_terminators;
+} _rl_search_cxt;
+
+struct _rl_cmd {
+ Keymap map;
+ int count;
+ int key;
+ rl_command_func_t *func;
+};
+extern struct _rl_cmd _rl_pending_command;
+extern struct _rl_cmd *_rl_command_to_execute;
+
+/* Callback data for reading numeric arguments */
+#define NUM_SAWMINUS 0x01
+#define NUM_SAWDIGITS 0x02
+#define NUM_READONE 0x04
+
+typedef int _rl_arg_cxt;
+
+/* A context for reading key sequences longer than a single character when
+ using the callback interface. */
+#define KSEQ_DISPATCHED 0x01
+#define KSEQ_SUBSEQ 0x02
+#define KSEQ_RECURSIVE 0x04
+
+typedef struct __rl_keyseq_context
+{
+ int flags;
+ int subseq_arg;
+ int subseq_retval; /* XXX */
+ int okey;
+
+ Keymap dmap;
+ Keymap oldmap;
+
+ struct __rl_keyseq_context *ocxt;
+ int childval;
+} _rl_keyseq_cxt;
+
+/* vi-mode commands that use result of motion command to define boundaries */
+#define VIM_DELETE 0x01
+#define VIM_CHANGE 0x02
+#define VIM_YANK 0x04
+
+/* various states for vi-mode commands that use motion commands. reflects
+ RL_READLINE_STATE */
+#define VMSTATE_READ 0x01
+#define VMSTATE_NUMARG 0x02
+
+typedef struct __rl_vimotion_context
+{
+ int op;
+ int state;
+ int flags; /* reserved */
+ _rl_arg_cxt ncxt;
+ int numeric_arg;
+ int start, end; /* rl_point, rl_end */
+ int key, motion; /* initial key, motion command */
+} _rl_vimotion_cxt;
+
+/* fill in more as needed */
+/* `Generic' callback data and functions */
+typedef struct __rl_callback_generic_arg
+{
+ int count;
+ int i1, i2;
+ /* add here as needed */
+} _rl_callback_generic_arg;
+
+typedef int _rl_callback_func_t (_rl_callback_generic_arg *);
+
+typedef void _rl_sigcleanup_func_t (int, void *);
+
+/*************************************************************************
+ * *
+ * Global functions undocumented in texinfo manual and not in readline.h *
+ * *
+ *************************************************************************/
+
+/*************************************************************************
+ * *
+ * Global variables undocumented in texinfo manual and not in readline.h *
+ * *
+ *************************************************************************/
+
+/* complete.c */
+extern int rl_complete_with_tilde_expansion;
+#if defined (VISIBLE_STATS)
+extern int rl_visible_stats;
+#endif /* VISIBLE_STATS */
+#if defined (COLOR_SUPPORT)
+extern int _rl_colored_stats;
+extern int _rl_colored_completion_prefix;
+#endif
+
+/* readline.c */
+extern int rl_line_buffer_len;
+extern int rl_arg_sign;
+extern int rl_visible_prompt_length;
+extern int rl_byte_oriented;
+
+/* display.c */
+extern int rl_display_fixed;
+
+/* parens.c */
+extern int rl_blink_matching_paren;
+
+/*************************************************************************
+ * *
+ * Global functions and variables unused and undocumented *
+ * *
+ *************************************************************************/
+
+/* kill.c */
+extern int rl_set_retained_kills (int);
+
+/* terminal.c */
+extern void _rl_set_screen_size (int, int);
+
+/* undo.c */
+extern int _rl_fix_last_undo_of_type (int, int, int);
+
+/* util.c */
+extern char *_rl_savestring (const char *);
+
+/*************************************************************************
+ * *
+ * Functions and variables private to the readline library *
+ * *
+ *************************************************************************/
+
+/* NOTE: Functions and variables prefixed with `_rl_' are
+ pseudo-global: they are global so they can be shared
+ between files in the readline library, but are not intended
+ to be visible to readline callers. */
+
+/*************************************************************************
+ * Undocumented private functions *
+ *************************************************************************/
+
+#if defined(READLINE_CALLBACKS)
+
+/* readline.c */
+extern void readline_internal_setup (void);
+extern char *readline_internal_teardown (int);
+extern int readline_internal_char (void);
+
+extern _rl_keyseq_cxt *_rl_keyseq_cxt_alloc (void);
+extern void _rl_keyseq_cxt_dispose (_rl_keyseq_cxt *);
+extern void _rl_keyseq_chain_dispose (void);
+
+extern int _rl_dispatch_callback (_rl_keyseq_cxt *);
+
+/* callback.c */
+extern _rl_callback_generic_arg *_rl_callback_data_alloc (int);
+extern void _rl_callback_data_dispose (_rl_callback_generic_arg *);
+
+#endif /* READLINE_CALLBACKS */
+
+/* bind.c */
+extern char *_rl_untranslate_macro_value (char *, int);
+
+/* complete.c */
+extern void _rl_reset_completion_state (void);
+extern char _rl_find_completion_word (int *, int *);
+extern void _rl_free_match_list (char **);
+
+/* display.c */
+extern char *_rl_strip_prompt (char *);
+extern void _rl_reset_prompt (void);
+extern void _rl_move_vert (int);
+extern void _rl_save_prompt (void);
+extern void _rl_restore_prompt (void);
+extern char *_rl_make_prompt_for_search (int);
+extern void _rl_erase_at_end_of_line (int);
+extern void _rl_clear_to_eol (int);
+extern void _rl_clear_screen (int);
+extern void _rl_update_final (void);
+extern void _rl_optimize_redisplay (void);
+extern void _rl_redisplay_after_sigwinch (void);
+extern void _rl_clean_up_for_exit (void);
+extern void _rl_erase_entire_line (void);
+extern int _rl_current_display_line (void);
+extern void _rl_refresh_line (void);
+
+/* input.c */
+extern int _rl_any_typein (void);
+extern int _rl_input_available (void);
+extern int _rl_nchars_available (void);
+extern int _rl_input_queued (int);
+extern void _rl_insert_typein (int);
+extern int _rl_unget_char (int);
+extern int _rl_pushed_input_available (void);
+
+extern int _rl_timeout_init (void);
+extern int _rl_timeout_handle_sigalrm (void);
+#if defined (_POSIXSELECT_H_)
+/* use as a sentinel for fd_set, struct timeval, and sigset_t definitions */
+extern int _rl_timeout_select (int, fd_set *, fd_set *, fd_set *, const struct timeval *, const sigset_t *);
+#endif
+
+/* isearch.c */
+extern _rl_search_cxt *_rl_scxt_alloc (int, int);
+extern void _rl_scxt_dispose (_rl_search_cxt *, int);
+
+extern int _rl_isearch_dispatch (_rl_search_cxt *, int);
+extern int _rl_isearch_callback (_rl_search_cxt *);
+extern int _rl_isearch_cleanup (_rl_search_cxt *, int);
+
+extern int _rl_search_getchar (_rl_search_cxt *);
+
+/* kill.c */
+#ifndef BRACKETED_PASTE_DEFAULT
+# define BRACKETED_PASTE_DEFAULT 1 /* XXX - for now */
+#endif
+
+#define BRACK_PASTE_PREF "\033[200~"
+#define BRACK_PASTE_SUFF "\033[201~"
+
+#define BRACK_PASTE_LAST '~'
+#define BRACK_PASTE_SLEN 6
+
+#define BRACK_PASTE_INIT "\033[?2004h"
+#define BRACK_PASTE_FINI "\033[?2004l\r"
+
+extern int _rl_read_bracketed_paste_prefix (int);
+extern char *_rl_bracketed_text (size_t *);
+extern int _rl_bracketed_read_key (void);
+extern int _rl_bracketed_read_mbstring (char *, int);
+
+/* macro.c */
+extern void _rl_with_macro_input (char *);
+extern int _rl_peek_macro_key (void);
+extern int _rl_next_macro_key (void);
+extern int _rl_prev_macro_key (void);
+extern void _rl_push_executing_macro (void);
+extern void _rl_pop_executing_macro (void);
+extern void _rl_add_macro_char (int);
+extern void _rl_kill_kbd_macro (void);
+
+/* misc.c */
+extern int _rl_arg_overflow (void);
+extern void _rl_arg_init (void);
+extern int _rl_arg_getchar (void);
+extern int _rl_arg_callback (_rl_arg_cxt);
+extern void _rl_reset_argument (void);
+
+extern void _rl_start_using_history (void);
+extern int _rl_free_saved_history_line (void);
+extern void _rl_set_insert_mode (int, int);
+
+extern void _rl_revert_previous_lines (void);
+extern void _rl_revert_all_lines (void);
+
+/* nls.c */
+extern char *_rl_init_locale (void);
+extern int _rl_init_eightbit (void);
+extern void _rl_reset_locale (void);
+
+/* parens.c */
+extern void _rl_enable_paren_matching (int);
+
+/* readline.c */
+extern void _rl_init_line_state (void);
+extern void _rl_set_the_line (void);
+extern int _rl_dispatch (int, Keymap);
+extern int _rl_dispatch_subseq (int, Keymap, int);
+extern void _rl_internal_char_cleanup (void);
+
+extern void _rl_init_executing_keyseq (void);
+extern void _rl_term_executing_keyseq (void);
+extern void _rl_end_executing_keyseq (void);
+extern void _rl_add_executing_keyseq (int);
+extern void _rl_del_executing_keyseq (void);
+
+extern rl_command_func_t *_rl_executing_func;
+
+/* rltty.c */
+extern int _rl_disable_tty_signals (void);
+extern int _rl_restore_tty_signals (void);
+
+/* search.c */
+extern int _rl_nsearch_callback (_rl_search_cxt *);
+extern int _rl_nsearch_cleanup (_rl_search_cxt *, int);
+
+/* signals.c */
+extern void _rl_signal_handler (int);
+
+extern void _rl_block_sigint (void);
+extern void _rl_release_sigint (void);
+extern void _rl_block_sigwinch (void);
+extern void _rl_release_sigwinch (void);
+
+/* terminal.c */
+extern void _rl_get_screen_size (int, int);
+extern void _rl_sigwinch_resize_terminal (void);
+extern int _rl_init_terminal_io (const char *);
+#ifdef _MINIX
+extern void _rl_output_character_function (int);
+#else
+extern int _rl_output_character_function (int);
+#endif
+extern void _rl_cr (void);
+extern void _rl_output_some_chars (const char *, int);
+extern int _rl_backspace (int);
+extern void _rl_enable_meta_key (void);
+extern void _rl_disable_meta_key (void);
+extern void _rl_control_keypad (int);
+extern void _rl_set_cursor (int, int);
+extern void _rl_standout_on (void);
+extern void _rl_standout_off (void);
+extern int _rl_reset_region_color (int, const char *);
+extern void _rl_region_color_on (void);
+extern void _rl_region_color_off (void);
+
+/* text.c */
+extern void _rl_fix_point (int);
+extern void _rl_fix_mark (void);
+extern int _rl_replace_text (const char *, int, int);
+extern int _rl_forward_char_internal (int);
+extern int _rl_backward_char_internal (int);
+extern int _rl_insert_char (int, int);
+extern int _rl_overwrite_char (int, int);
+extern int _rl_overwrite_rubout (int, int);
+extern int _rl_rubout_char (int, int);
+#if defined (HANDLE_MULTIBYTE)
+extern int _rl_char_search_internal (int, int, char *, int);
+#else
+extern int _rl_char_search_internal (int, int, int);
+#endif
+extern int _rl_set_mark_at_pos (int);
+
+/* undo.c */
+extern UNDO_LIST *_rl_copy_undo_entry (UNDO_LIST *);
+extern UNDO_LIST *_rl_copy_undo_list (UNDO_LIST *);
+extern void _rl_free_undo_list (UNDO_LIST *);
+
+/* util.c */
+#if defined (USE_VARARGS) && defined (PREFER_STDARG)
+extern void _rl_ttymsg (const char *, ...) __attribute__((__format__ (printf, 1, 2)));
+extern void _rl_errmsg (const char *, ...) __attribute__((__format__ (printf, 1, 2)));
+extern void _rl_trace (const char *, ...) __attribute__((__format__ (printf, 1, 2)));
+#else
+extern void _rl_ttymsg ();
+extern void _rl_errmsg ();
+extern void _rl_trace ();
+#endif
+extern void _rl_audit_tty (char *);
+
+extern int _rl_tropen (void);
+
+extern int _rl_abort_internal (void);
+extern int _rl_null_function (int, int);
+extern char *_rl_strindex (const char *, const char *);
+extern int _rl_qsort_string_compare (char **, char **);
+extern int (_rl_uppercase_p) (int);
+extern int (_rl_lowercase_p) (int);
+extern int (_rl_pure_alphabetic) (int);
+extern int (_rl_digit_p) (int);
+extern int (_rl_to_lower) (int);
+extern int (_rl_to_upper) (int);
+extern int (_rl_digit_value) (int);
+
+/* vi_mode.c */
+extern void _rl_vi_initialize_line (void);
+extern void _rl_vi_reset_last (void);
+extern void _rl_vi_set_last (int, int, int);
+extern int _rl_vi_textmod_command (int);
+extern int _rl_vi_motion_command (int);
+extern void _rl_vi_done_inserting (void);
+extern int _rl_vi_domove_callback (_rl_vimotion_cxt *);
+extern int _rl_vi_domove_motion_cleanup (int, _rl_vimotion_cxt *);
+
+/* Use HS_HISTORY_VERSION as the sentinel to see if we've included history.h
+ and so can use HIST_ENTRY */
+#if defined (HS_HISTORY_VERSION)
+extern void _rl_free_history_entry (HIST_ENTRY *);
+#endif
+
+/*************************************************************************
+ * Undocumented private variables *
+ *************************************************************************/
+
+/* bind.c */
+extern const char * const _rl_possible_control_prefixes[];
+extern const char * const _rl_possible_meta_prefixes[];
+
+/* callback.c */
+extern _rl_callback_func_t *_rl_callback_func;
+extern _rl_callback_generic_arg *_rl_callback_data;
+
+/* complete.c */
+extern int _rl_complete_show_all;
+extern int _rl_complete_show_unmodified;
+extern int _rl_complete_mark_directories;
+extern int _rl_complete_mark_symlink_dirs;
+extern int _rl_completion_prefix_display_length;
+extern int _rl_completion_columns;
+extern int _rl_print_completions_horizontally;
+extern int _rl_completion_case_fold;
+extern int _rl_completion_case_map;
+extern int _rl_match_hidden_files;
+extern int _rl_page_completions;
+extern int _rl_skip_completed_text;
+extern int _rl_menu_complete_prefix_first;
+
+/* display.c */
+extern int _rl_vis_botlin;
+extern int _rl_last_c_pos;
+extern int _rl_suppress_redisplay;
+extern int _rl_want_redisplay;
+
+extern char *_rl_emacs_mode_str;
+extern int _rl_emacs_modestr_len;
+extern char *_rl_vi_ins_mode_str;
+extern int _rl_vi_ins_modestr_len;
+extern char *_rl_vi_cmd_mode_str;
+extern int _rl_vi_cmd_modestr_len;
+
+/* isearch.c */
+extern char *_rl_isearch_terminators;
+
+extern _rl_search_cxt *_rl_iscxt;
+
+/* macro.c */
+extern char *_rl_executing_macro;
+
+/* misc.c */
+extern int _rl_history_preserve_point;
+extern int _rl_history_saved_point;
+
+extern _rl_arg_cxt _rl_argcxt;
+
+/* nls.c */
+extern int _rl_utf8locale;
+
+/* readline.c */
+extern int _rl_echoing_p;
+extern int _rl_horizontal_scroll_mode;
+extern int _rl_mark_modified_lines;
+extern int _rl_bell_preference;
+extern int _rl_meta_flag;
+extern int _rl_convert_meta_chars_to_ascii;
+extern int _rl_output_meta_chars;
+extern int _rl_bind_stty_chars;
+extern int _rl_revert_all_at_newline;
+extern int _rl_echo_control_chars;
+extern int _rl_show_mode_in_prompt;
+extern int _rl_enable_bracketed_paste;
+extern int _rl_enable_active_region;
+extern char *_rl_active_region_start_color;
+extern char *_rl_active_region_end_color;
+extern char *_rl_comment_begin;
+extern unsigned char _rl_parsing_conditionalized_out;
+extern Keymap _rl_keymap;
+extern FILE *_rl_in_stream;
+extern FILE *_rl_out_stream;
+extern int _rl_last_command_was_kill;
+extern int _rl_eof_char;
+extern procenv_t _rl_top_level;
+extern _rl_keyseq_cxt *_rl_kscxt;
+extern int _rl_keyseq_timeout;
+
+extern int _rl_executing_keyseq_size;
+
+extern rl_hook_func_t *_rl_internal_startup_hook;
+
+/* search.c */
+extern _rl_search_cxt *_rl_nscxt;
+extern int _rl_history_search_pos;
+
+/* signals.c */
+extern int volatile _rl_caught_signal;
+
+extern _rl_sigcleanup_func_t *_rl_sigcleanup;
+extern void *_rl_sigcleanarg;
+
+extern int _rl_echoctl;
+
+extern int _rl_intr_char;
+extern int _rl_quit_char;
+extern int _rl_susp_char;
+
+/* terminal.c */
+extern int _rl_enable_keypad;
+extern int _rl_enable_meta;
+extern char *_rl_term_clreol;
+extern char *_rl_term_clrpag;
+extern char *_rl_term_clrscroll;
+extern char *_rl_term_im;
+extern char *_rl_term_ic;
+extern char *_rl_term_ei;
+extern char *_rl_term_DC;
+extern char *_rl_term_up;
+extern char *_rl_term_dc;
+extern char *_rl_term_cr;
+extern char *_rl_term_IC;
+extern char *_rl_term_forward_char;
+extern int _rl_screenheight;
+extern int _rl_screenwidth;
+extern int _rl_screenchars;
+extern int _rl_terminal_can_insert;
+extern int _rl_term_autowrap;
+
+/* text.c */
+extern int _rl_optimize_typeahead;
+extern int _rl_keep_mark_active;
+
+/* undo.c */
+extern int _rl_doing_an_undo;
+extern int _rl_undo_group_level;
+
+/* vi_mode.c */
+extern int _rl_vi_last_command;
+extern int _rl_vi_redoing;
+extern _rl_vimotion_cxt *_rl_vimvcxt;
+
+/* Use HS_HISTORY_VERSION as the sentinel to see if we've included history.h
+ and so can use HIST_ENTRY */
+#if defined (HS_HISTORY_VERSION)
+extern HIST_ENTRY *_rl_saved_line_for_history;
+#endif
+
+#endif /* _RL_PRIVATE_H_ */
diff --git a/third_party/readline/rlshell.h b/third_party/readline/rlshell.h
new file mode 100644
index 000000000..6055b1442
--- /dev/null
+++ b/third_party/readline/rlshell.h
@@ -0,0 +1,33 @@
+/* rlshell.h -- utility functions normally provided by bash. */
+
+/* Copyright (C) 1999-2021 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#if !defined (_RL_SHELL_H_)
+#define _RL_SHELL_H_
+
+#include "rlstdc.h"
+
+extern char *sh_single_quote (char *);
+extern void sh_set_lines_and_columns (int, int);
+extern char *sh_get_env_value (const char *);
+extern char *sh_get_home_dir (void);
+extern int sh_unset_nodelay_mode (int);
+
+#endif /* _RL_SHELL_H_ */
diff --git a/third_party/readline/rlstdc.h b/third_party/readline/rlstdc.h
new file mode 100644
index 000000000..2aaa30bab
--- /dev/null
+++ b/third_party/readline/rlstdc.h
@@ -0,0 +1,57 @@
+/* stdc.h -- macros to make source compile on both ANSI C and K&R C compilers. */
+
+/* Copyright (C) 1993-2009 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#if !defined (_RL_STDC_H_)
+#define _RL_STDC_H_
+
+/* Adapted from BSD /usr/include/sys/cdefs.h. */
+
+/* A function can be defined using prototypes and compile on both ANSI C
+ and traditional C compilers with something like this:
+ extern char *func PARAMS((char *, char *, int)); */
+
+#if !defined (PARAMS)
+# if defined (__STDC__) || defined (__GNUC__) || defined (__cplusplus)
+# define PARAMS(protos) protos
+# else
+# define PARAMS(protos) ()
+# endif
+#endif
+
+#ifndef __attribute__
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8)
+# define __attribute__(x)
+# endif
+#endif
+
+/* Moved from config.h.in because readline.h:rl_message depends on these
+ defines. */
+#if defined (__STDC__) && defined (HAVE_STDARG_H)
+# define PREFER_STDARG
+# define USE_VARARGS
+#else
+# if defined (HAVE_VARARGS_H)
+# define PREFER_VARARGS
+# define USE_VARARGS
+# endif
+#endif
+
+#endif /* !_RL_STDC_H_ */
diff --git a/third_party/readline/rltty.c b/third_party/readline/rltty.c
new file mode 100644
index 000000000..5a24a076e
--- /dev/null
+++ b/third_party/readline/rltty.c
@@ -0,0 +1,995 @@
+/* rltty.c -- functions to prepare and restore the terminal for readline's
+ use. */
+
+/* Copyright (C) 1992-2022 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+#include
+#include
+#include
+
+#if defined (HAVE_UNISTD_H)
+# include
+#endif /* HAVE_UNISTD_H */
+
+#include "rldefs.h"
+
+#include "rltty.h"
+#if defined (HAVE_SYS_IOCTL_H)
+# include /* include for declaration of ioctl */
+#endif
+
+#include "readline.h"
+#include "rlprivate.h"
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+rl_vintfunc_t *rl_prep_term_function = rl_prep_terminal;
+rl_voidfunc_t *rl_deprep_term_function = rl_deprep_terminal;
+
+static void set_winsize (int);
+
+/* **************************************************************** */
+/* */
+/* Saving and Restoring the TTY */
+/* */
+/* **************************************************************** */
+
+/* Non-zero means that the terminal is in a prepped state. There are several
+ flags that are OR'd in to denote whether or not we have sent various
+ init strings to the terminal. */
+#define TPX_PREPPED 0x01
+#define TPX_BRACKPASTE 0x02
+#define TPX_METAKEY 0x04
+
+static int terminal_prepped;
+
+static _RL_TTY_CHARS _rl_tty_chars, _rl_last_tty_chars;
+
+/* If non-zero, means that this process has called tcflow(fd, TCOOFF)
+ and output is suspended. */
+#if defined (__ksr1__)
+static int ksrflow;
+#endif
+
+/* Dummy call to force a backgrounded readline to stop before it tries
+ to get the tty settings. */
+static void
+set_winsize (tty)
+ int tty;
+{
+#if defined (TIOCGWINSZ)
+ struct winsize w;
+
+ if (ioctl (tty, TIOCGWINSZ, &w) == 0)
+ (void) ioctl (tty, TIOCSWINSZ, &w);
+#endif /* TIOCGWINSZ */
+}
+
+#if defined (NO_TTY_DRIVER)
+/* Nothing */
+#elif defined (NEW_TTY_DRIVER)
+
+/* Values for the `flags' field of a struct bsdtty. This tells which
+ elements of the struct bsdtty have been fetched from the system and
+ are valid. */
+#define SGTTY_SET 0x01
+#define LFLAG_SET 0x02
+#define TCHARS_SET 0x04
+#define LTCHARS_SET 0x08
+
+struct bsdtty {
+ struct sgttyb sgttyb; /* Basic BSD tty driver information. */
+ int lflag; /* Local mode flags, like LPASS8. */
+#if defined (TIOCGETC)
+ struct tchars tchars; /* Terminal special characters, including ^S and ^Q. */
+#endif
+#if defined (TIOCGLTC)
+ struct ltchars ltchars; /* 4.2 BSD editing characters */
+#endif
+ int flags; /* Bitmap saying which parts of the struct are valid. */
+};
+
+#define TIOTYPE struct bsdtty
+
+static TIOTYPE otio;
+
+static void save_tty_chars (TIOTYPE *);
+static int _get_tty_settings (int, TIOTYPE *);
+static int get_tty_settings (int, TIOTYPE *);
+static int _set_tty_settings (int, TIOTYPE *);
+static int set_tty_settings (int, TIOTYPE *);
+
+static void prepare_terminal_settings (int, TIOTYPE, TIOTYPE *);
+
+static void set_special_char (Keymap, TIOTYPE *, int, rl_command_func_t *);
+
+static void
+save_tty_chars (TIOTYPE *tiop)
+{
+ _rl_last_tty_chars = _rl_tty_chars;
+
+ if (tiop->flags & SGTTY_SET)
+ {
+ _rl_tty_chars.t_erase = tiop->sgttyb.sg_erase;
+ _rl_tty_chars.t_kill = tiop->sgttyb.sg_kill;
+ }
+
+ if (tiop->flags & TCHARS_SET)
+ {
+ _rl_intr_char = _rl_tty_chars.t_intr = tiop->tchars.t_intrc;
+ _rl_quit_char = _rl_tty_chars.t_quit = tiop->tchars.t_quitc;
+
+ _rl_tty_chars.t_start = tiop->tchars.t_startc;
+ _rl_tty_chars.t_stop = tiop->tchars.t_stopc;
+ _rl_tty_chars.t_eof = tiop->tchars.t_eofc;
+ _rl_tty_chars.t_eol = '\n';
+ _rl_tty_chars.t_eol2 = tiop->tchars.t_brkc;
+ }
+
+ if (tiop->flags & LTCHARS_SET)
+ {
+ _rl_susp_char = _rl_tty_chars.t_susp = tiop->ltchars.t_suspc;
+
+ _rl_tty_chars.t_dsusp = tiop->ltchars.t_dsuspc;
+ _rl_tty_chars.t_reprint = tiop->ltchars.t_rprntc;
+ _rl_tty_chars.t_flush = tiop->ltchars.t_flushc;
+ _rl_tty_chars.t_werase = tiop->ltchars.t_werasc;
+ _rl_tty_chars.t_lnext = tiop->ltchars.t_lnextc;
+ }
+
+ _rl_tty_chars.t_status = -1;
+}
+
+static int
+get_tty_settings (int tty, TIOTYPE *tiop)
+{
+ set_winsize (tty);
+
+ tiop->flags = tiop->lflag = 0;
+
+ errno = 0;
+ if (ioctl (tty, TIOCGETP, &(tiop->sgttyb)) < 0)
+ return -1;
+ tiop->flags |= SGTTY_SET;
+
+#if defined (TIOCLGET)
+ if (ioctl (tty, TIOCLGET, &(tiop->lflag)) == 0)
+ tiop->flags |= LFLAG_SET;
+#endif
+
+#if defined (TIOCGETC)
+ if (ioctl (tty, TIOCGETC, &(tiop->tchars)) == 0)
+ tiop->flags |= TCHARS_SET;
+#endif
+
+#if defined (TIOCGLTC)
+ if (ioctl (tty, TIOCGLTC, &(tiop->ltchars)) == 0)
+ tiop->flags |= LTCHARS_SET;
+#endif
+
+ return 0;
+}
+
+static int
+set_tty_settings (int tty, TIOTYPE *tiop)
+{
+ if (tiop->flags & SGTTY_SET)
+ {
+ ioctl (tty, TIOCSETN, &(tiop->sgttyb));
+ tiop->flags &= ~SGTTY_SET;
+ }
+ _rl_echoing_p = 1;
+
+#if defined (TIOCLSET)
+ if (tiop->flags & LFLAG_SET)
+ {
+ ioctl (tty, TIOCLSET, &(tiop->lflag));
+ tiop->flags &= ~LFLAG_SET;
+ }
+#endif
+
+#if defined (TIOCSETC)
+ if (tiop->flags & TCHARS_SET)
+ {
+ ioctl (tty, TIOCSETC, &(tiop->tchars));
+ tiop->flags &= ~TCHARS_SET;
+ }
+#endif
+
+#if defined (TIOCSLTC)
+ if (tiop->flags & LTCHARS_SET)
+ {
+ ioctl (tty, TIOCSLTC, &(tiop->ltchars));
+ tiop->flags &= ~LTCHARS_SET;
+ }
+#endif
+
+ return 0;
+}
+
+static void
+prepare_terminal_settings (int meta_flag, TIOTYPE oldtio, TIOTYPE *tiop)
+{
+ _rl_echoing_p = (oldtio.sgttyb.sg_flags & ECHO);
+ _rl_echoctl = (oldtio.sgttyb.sg_flags & ECHOCTL);
+
+ /* Copy the original settings to the structure we're going to use for
+ our settings. */
+ tiop->sgttyb = oldtio.sgttyb;
+ tiop->lflag = oldtio.lflag;
+#if defined (TIOCGETC)
+ tiop->tchars = oldtio.tchars;
+#endif
+#if defined (TIOCGLTC)
+ tiop->ltchars = oldtio.ltchars;
+#endif
+ tiop->flags = oldtio.flags;
+
+ /* First, the basic settings to put us into character-at-a-time, no-echo
+ input mode. */
+ tiop->sgttyb.sg_flags &= ~(ECHO | CRMOD);
+ tiop->sgttyb.sg_flags |= CBREAK;
+
+ /* If this terminal doesn't care how the 8th bit is used, then we can
+ use it for the meta-key. If only one of even or odd parity is
+ specified, then the terminal is using parity, and we cannot. */
+#if !defined (ANYP)
+# define ANYP (EVENP | ODDP)
+#endif
+ if (((oldtio.sgttyb.sg_flags & ANYP) == ANYP) ||
+ ((oldtio.sgttyb.sg_flags & ANYP) == 0))
+ {
+ tiop->sgttyb.sg_flags |= ANYP;
+
+ /* Hack on local mode flags if we can. */
+#if defined (TIOCLGET)
+# if defined (LPASS8)
+ tiop->lflag |= LPASS8;
+# endif /* LPASS8 */
+#endif /* TIOCLGET */
+ }
+
+#if defined (TIOCGETC)
+# if defined (USE_XON_XOFF)
+ /* Get rid of terminal output start and stop characters. */
+ tiop->tchars.t_stopc = -1; /* C-s */
+ tiop->tchars.t_startc = -1; /* C-q */
+
+ /* If there is an XON character, bind it to restart the output. */
+ if (oldtio.tchars.t_startc != -1)
+ rl_bind_key (oldtio.tchars.t_startc, rl_restart_output);
+# endif /* USE_XON_XOFF */
+
+ /* If there is an EOF char, bind _rl_eof_char to it. */
+ if (oldtio.tchars.t_eofc != -1)
+ _rl_eof_char = oldtio.tchars.t_eofc;
+
+# if defined (NO_KILL_INTR)
+ /* Get rid of terminal-generated SIGQUIT and SIGINT. */
+ tiop->tchars.t_quitc = -1; /* C-\ */
+ tiop->tchars.t_intrc = -1; /* C-c */
+# endif /* NO_KILL_INTR */
+#endif /* TIOCGETC */
+
+#if defined (TIOCGLTC)
+ /* Make the interrupt keys go away. Just enough to make people happy. */
+ tiop->ltchars.t_dsuspc = -1; /* C-y */
+ tiop->ltchars.t_lnextc = -1; /* C-v */
+#endif /* TIOCGLTC */
+}
+
+#else /* !defined (NEW_TTY_DRIVER) */
+
+#if !defined (VMIN)
+# define VMIN VEOF
+#endif
+
+#if !defined (VTIME)
+# define VTIME VEOL
+#endif
+
+#if defined (TERMIOS_TTY_DRIVER)
+# define TIOTYPE struct termios
+# define DRAIN_OUTPUT(fd) tcdrain (fd)
+# define GETATTR(tty, tiop) (tcgetattr (tty, tiop))
+# ifdef M_UNIX
+# define SETATTR(tty, tiop) (tcsetattr (tty, TCSANOW, tiop))
+# else
+# define SETATTR(tty, tiop) (tcsetattr (tty, TCSADRAIN, tiop))
+# endif /* !M_UNIX */
+#else
+# define TIOTYPE struct termio
+# define DRAIN_OUTPUT(fd)
+# define GETATTR(tty, tiop) (ioctl (tty, TCGETA, tiop))
+# define SETATTR(tty, tiop) (ioctl (tty, TCSETAW, tiop))
+#endif /* !TERMIOS_TTY_DRIVER */
+
+static TIOTYPE otio;
+
+static void save_tty_chars (TIOTYPE *);
+static int _get_tty_settings (int, TIOTYPE *);
+static int get_tty_settings (int, TIOTYPE *);
+static int _set_tty_settings (int, TIOTYPE *);
+static int set_tty_settings (int, TIOTYPE *);
+
+static void prepare_terminal_settings (int, TIOTYPE, TIOTYPE *);
+
+static void set_special_char (Keymap, TIOTYPE *, int, rl_command_func_t *);
+static void _rl_bind_tty_special_chars (Keymap, TIOTYPE);
+
+#if defined (FLUSHO)
+# define OUTPUT_BEING_FLUSHED(tp) (tp->c_lflag & FLUSHO)
+#else
+# define OUTPUT_BEING_FLUSHED(tp) 0
+#endif
+
+static void
+save_tty_chars (TIOTYPE *tiop)
+{
+ _rl_last_tty_chars = _rl_tty_chars;
+
+ _rl_tty_chars.t_eof = tiop->c_cc[VEOF];
+ _rl_tty_chars.t_eol = tiop->c_cc[VEOL];
+#ifdef VEOL2
+ _rl_tty_chars.t_eol2 = tiop->c_cc[VEOL2];
+#endif
+ _rl_tty_chars.t_erase = tiop->c_cc[VERASE];
+#ifdef VWERASE
+ _rl_tty_chars.t_werase = tiop->c_cc[VWERASE];
+#endif
+ _rl_tty_chars.t_kill = tiop->c_cc[VKILL];
+#ifdef VREPRINT
+ _rl_tty_chars.t_reprint = tiop->c_cc[VREPRINT];
+#endif
+ _rl_intr_char = _rl_tty_chars.t_intr = tiop->c_cc[VINTR];
+ _rl_quit_char = _rl_tty_chars.t_quit = tiop->c_cc[VQUIT];
+#ifdef VSUSP
+ _rl_susp_char = _rl_tty_chars.t_susp = tiop->c_cc[VSUSP];
+#endif
+#ifdef VDSUSP
+ _rl_tty_chars.t_dsusp = tiop->c_cc[VDSUSP];
+#endif
+#ifdef VSTART
+ _rl_tty_chars.t_start = tiop->c_cc[VSTART];
+#endif
+#ifdef VSTOP
+ _rl_tty_chars.t_stop = tiop->c_cc[VSTOP];
+#endif
+#ifdef VLNEXT
+ _rl_tty_chars.t_lnext = tiop->c_cc[VLNEXT];
+#endif
+#ifdef VDISCARD
+ _rl_tty_chars.t_flush = tiop->c_cc[VDISCARD];
+#endif
+#ifdef VSTATUS
+ _rl_tty_chars.t_status = tiop->c_cc[VSTATUS];
+#endif
+}
+
+#if defined (_AIX) || defined (_AIX41)
+/* Currently this is only used on AIX */
+static void
+rltty_warning (char *msg)
+{
+ _rl_errmsg ("warning: %s", msg);
+}
+#endif
+
+#if defined (_AIX)
+void
+setopost (TIOTYPE *tp)
+{
+ if ((tp->c_oflag & OPOST) == 0)
+ {
+ _rl_errmsg ("warning: turning on OPOST for terminal\r");
+ tp->c_oflag |= OPOST|ONLCR;
+ }
+}
+#endif
+
+static int
+_get_tty_settings (int tty, TIOTYPE *tiop)
+{
+ int ioctl_ret;
+
+ while (1)
+ {
+ ioctl_ret = GETATTR (tty, tiop);
+ if (ioctl_ret < 0)
+ {
+ if (errno != EINTR)
+ return -1;
+ else
+ continue;
+ }
+ if (OUTPUT_BEING_FLUSHED (tiop))
+ {
+#if defined (FLUSHO)
+ _rl_errmsg ("warning: turning off output flushing");
+ tiop->c_lflag &= ~FLUSHO;
+ break;
+#else
+ continue;
+#endif
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int
+get_tty_settings (int tty, TIOTYPE *tiop)
+{
+ set_winsize (tty);
+
+ errno = 0;
+ if (_get_tty_settings (tty, tiop) < 0)
+ return -1;
+
+#if defined (_AIX)
+ setopost(tiop);
+#endif
+
+ return 0;
+}
+
+static int
+_set_tty_settings (int tty, TIOTYPE *tiop)
+{
+ while (SETATTR (tty, tiop) < 0)
+ {
+ if (errno != EINTR)
+ return -1;
+ errno = 0;
+ }
+ return 0;
+}
+
+static int
+set_tty_settings (int tty, TIOTYPE *tiop)
+{
+ if (_set_tty_settings (tty, tiop) < 0)
+ return -1;
+
+#if 0
+
+#if defined (TERMIOS_TTY_DRIVER)
+# if defined (__ksr1__)
+ if (ksrflow)
+ {
+ ksrflow = 0;
+ tcflow (tty, TCOON);
+ }
+# else /* !ksr1 */
+ tcflow (tty, TCOON); /* Simulate a ^Q. */
+# endif /* !ksr1 */
+#else
+ ioctl (tty, TCXONC, 1); /* Simulate a ^Q. */
+#endif /* !TERMIOS_TTY_DRIVER */
+
+#endif /* 0 */
+
+ return 0;
+}
+
+static void
+prepare_terminal_settings (int meta_flag, TIOTYPE oldtio, TIOTYPE *tiop)
+{
+ int sc;
+ Keymap kmap;
+
+ _rl_echoing_p = (oldtio.c_lflag & ECHO);
+#if defined (ECHOCTL)
+ _rl_echoctl = (oldtio.c_lflag & ECHOCTL);
+#endif
+
+ tiop->c_lflag &= ~(ICANON | ECHO);
+
+ if ((unsigned char) oldtio.c_cc[VEOF] != (unsigned char) _POSIX_VDISABLE)
+ _rl_eof_char = oldtio.c_cc[VEOF];
+
+#if defined (USE_XON_XOFF)
+#if defined (IXANY)
+ tiop->c_iflag &= ~(IXON | IXANY);
+#else
+ /* `strict' Posix systems do not define IXANY. */
+ tiop->c_iflag &= ~IXON;
+#endif /* IXANY */
+#endif /* USE_XON_XOFF */
+
+ /* Only turn this off if we are using all 8 bits. */
+ if (((tiop->c_cflag & CSIZE) == CS8) || meta_flag)
+ tiop->c_iflag &= ~(ISTRIP | INPCK);
+
+ /* Make sure we differentiate between CR and NL on input. */
+ tiop->c_iflag &= ~(ICRNL | INLCR);
+
+#if !defined (HANDLE_SIGNALS)
+ tiop->c_lflag &= ~ISIG;
+#else
+ tiop->c_lflag |= ISIG;
+#endif
+
+ tiop->c_cc[VMIN] = 1;
+ tiop->c_cc[VTIME] = 0;
+
+#if defined (FLUSHO)
+ if (OUTPUT_BEING_FLUSHED (tiop))
+ {
+ tiop->c_lflag &= ~FLUSHO;
+ oldtio.c_lflag &= ~FLUSHO;
+ }
+#endif
+
+ /* Turn off characters that we need on Posix systems with job control,
+ just to be sure. This includes ^Y and ^V. This should not really
+ be necessary. */
+#if defined (TERMIOS_TTY_DRIVER) && defined (_POSIX_VDISABLE)
+
+#if defined (VLNEXT)
+ tiop->c_cc[VLNEXT] = _POSIX_VDISABLE;
+#endif
+
+#if defined (VDSUSP)
+ tiop->c_cc[VDSUSP] = _POSIX_VDISABLE;
+#endif
+
+ /* Conditionally disable some other tty special characters if there is a
+ key binding for them in the current keymap. Readline ordinarily doesn't
+ bind these characters, but an application or user might. */
+#if defined (VI_MODE)
+ kmap = (rl_editing_mode == vi_mode) ? vi_insertion_keymap : _rl_keymap;
+#else
+ kmap = _rl_keymap;
+#endif
+#if defined (VDISCARD)
+ sc = tiop->c_cc[VDISCARD];
+ if (sc != _POSIX_VDISABLE && kmap[(unsigned char)sc].type == ISFUNC)
+ tiop->c_cc[VDISCARD] = _POSIX_VDISABLE;
+#endif /* VDISCARD */
+
+#endif /* TERMIOS_TTY_DRIVER && _POSIX_VDISABLE */
+}
+#endif /* !NEW_TTY_DRIVER */
+
+/* Put the terminal in CBREAK mode so that we can detect key presses. */
+#if defined (NO_TTY_DRIVER)
+void
+rl_prep_terminal (int meta_flag)
+{
+ _rl_echoing_p = 1;
+}
+
+void
+rl_deprep_terminal (void)
+{
+}
+
+#else /* ! NO_TTY_DRIVER */
+void
+rl_prep_terminal (int meta_flag)
+{
+ int tty, nprep;
+ TIOTYPE tio;
+
+ if (terminal_prepped)
+ return;
+
+ /* Try to keep this function from being INTerrupted. */
+ _rl_block_sigint ();
+
+ tty = rl_instream ? fileno (rl_instream) : fileno (stdin);
+
+ if (get_tty_settings (tty, &tio) < 0)
+ {
+#if defined (ENOTSUP)
+ /* MacOS X and Linux, at least, lie about the value of errno if
+ tcgetattr fails. */
+ if (errno == ENOTTY || errno == EINVAL || errno == ENOTSUP)
+#else
+ if (errno == ENOTTY || errno == EINVAL)
+#endif
+ _rl_echoing_p = 1; /* XXX */
+
+ _rl_release_sigint ();
+ return;
+ }
+
+ otio = tio;
+
+ if (_rl_bind_stty_chars)
+ {
+#if defined (VI_MODE)
+ /* If editing in vi mode, make sure we restore the bindings in the
+ insertion keymap no matter what keymap we ended up in. */
+ if (rl_editing_mode == vi_mode)
+ rl_tty_unset_default_bindings (vi_insertion_keymap);
+ else
+#endif
+ rl_tty_unset_default_bindings (_rl_keymap);
+ }
+ save_tty_chars (&otio);
+ RL_SETSTATE(RL_STATE_TTYCSAVED);
+ if (_rl_bind_stty_chars)
+ {
+#if defined (VI_MODE)
+ /* If editing in vi mode, make sure we set the bindings in the
+ insertion keymap no matter what keymap we ended up in. */
+ if (rl_editing_mode == vi_mode)
+ _rl_bind_tty_special_chars (vi_insertion_keymap, tio);
+ else
+#endif
+ _rl_bind_tty_special_chars (_rl_keymap, tio);
+ }
+
+ prepare_terminal_settings (meta_flag, otio, &tio);
+
+ if (set_tty_settings (tty, &tio) < 0)
+ {
+ _rl_release_sigint ();
+ return;
+ }
+
+ if (_rl_enable_keypad)
+ _rl_control_keypad (1);
+
+ nprep = TPX_PREPPED;
+
+ if (_rl_enable_bracketed_paste)
+ {
+ fprintf (rl_outstream, BRACK_PASTE_INIT);
+ nprep |= TPX_BRACKPASTE;
+ }
+
+ fflush (rl_outstream);
+ terminal_prepped = nprep;
+ RL_SETSTATE(RL_STATE_TERMPREPPED);
+
+ _rl_release_sigint ();
+}
+
+/* Restore the terminal's normal settings and modes. */
+void
+rl_deprep_terminal (void)
+{
+ int tty;
+
+ if (terminal_prepped == 0)
+ return;
+
+ /* Try to keep this function from being interrupted. */
+ _rl_block_sigint ();
+
+ tty = rl_instream ? fileno (rl_instream) : fileno (stdin);
+
+ if (terminal_prepped & TPX_BRACKPASTE)
+ {
+ fprintf (rl_outstream, BRACK_PASTE_FINI);
+ /* Since the last character in BRACK_PASTE_FINI is \r */
+ _rl_last_c_pos = 0;
+ if (rl_eof_found && (RL_ISSTATE (RL_STATE_TIMEOUT) == 0))
+ fprintf (rl_outstream, "\n");
+ else if (_rl_echoing_p == 0)
+ fprintf (rl_outstream, "\n");
+ }
+
+ if (_rl_enable_keypad)
+ _rl_control_keypad (0);
+
+ fflush (rl_outstream);
+
+ if (set_tty_settings (tty, &otio) < 0)
+ {
+ _rl_release_sigint ();
+ return;
+ }
+
+ terminal_prepped = 0;
+ RL_UNSETSTATE(RL_STATE_TERMPREPPED);
+
+ _rl_release_sigint ();
+}
+#endif /* !NO_TTY_DRIVER */
+
+/* Set readline's idea of whether or not it is echoing output to the terminal,
+ returning the old value. */
+int
+rl_tty_set_echoing (int u)
+{
+ int o;
+
+ o = _rl_echoing_p;
+ _rl_echoing_p = u;
+ return o;
+}
+
+/* **************************************************************** */
+/* */
+/* Bogus Flow Control */
+/* */
+/* **************************************************************** */
+
+int
+rl_restart_output (int count, int key)
+{
+#if defined (__MINGW32__)
+ return 0;
+#else /* !__MING32__ */
+
+ int fildes = fileno (rl_outstream);
+#if defined (TIOCSTART)
+#if defined (apollo)
+ ioctl (&fildes, TIOCSTART, 0);
+#else
+ ioctl (fildes, TIOCSTART, 0);
+#endif /* apollo */
+
+#else /* !TIOCSTART */
+# if defined (TERMIOS_TTY_DRIVER)
+# if defined (__ksr1__)
+ if (ksrflow)
+ {
+ ksrflow = 0;
+ tcflow (fildes, TCOON);
+ }
+# else /* !ksr1 */
+ tcflow (fildes, TCOON); /* Simulate a ^Q. */
+# endif /* !ksr1 */
+# else /* !TERMIOS_TTY_DRIVER */
+# if defined (TCXONC)
+ ioctl (fildes, TCXONC, TCOON);
+# endif /* TCXONC */
+# endif /* !TERMIOS_TTY_DRIVER */
+#endif /* !TIOCSTART */
+
+ return 0;
+#endif /* !__MINGW32__ */
+}
+
+int
+rl_stop_output (int count, int key)
+{
+#if defined (__MINGW32__)
+ return 0;
+#else
+
+ int fildes = fileno (rl_instream);
+
+#if defined (TIOCSTOP)
+# if defined (apollo)
+ ioctl (&fildes, TIOCSTOP, 0);
+# else
+ ioctl (fildes, TIOCSTOP, 0);
+# endif /* apollo */
+#else /* !TIOCSTOP */
+# if defined (TERMIOS_TTY_DRIVER)
+# if defined (__ksr1__)
+ ksrflow = 1;
+# endif /* ksr1 */
+ tcflow (fildes, TCOOFF);
+# else
+# if defined (TCXONC)
+ ioctl (fildes, TCXONC, TCOON);
+# endif /* TCXONC */
+# endif /* !TERMIOS_TTY_DRIVER */
+#endif /* !TIOCSTOP */
+
+ return 0;
+#endif /* !__MINGW32__ */
+}
+
+/* **************************************************************** */
+/* */
+/* Default Key Bindings */
+/* */
+/* **************************************************************** */
+
+#if !defined (NO_TTY_DRIVER)
+#define SET_SPECIAL(sc, func) set_special_char(kmap, &ttybuff, sc, func)
+#endif
+
+#if defined (NO_TTY_DRIVER)
+
+#define SET_SPECIAL(sc, func)
+#define RESET_SPECIAL(c)
+
+#elif defined (NEW_TTY_DRIVER)
+static void
+set_special_char (Keymap kmap, TIOTYPE *tiop, int sc, rl_command_func_t *func)
+{
+ if (sc != -1 && kmap[(unsigned char)sc].type == ISFUNC)
+ kmap[(unsigned char)sc].function = func;
+}
+
+#define RESET_SPECIAL(c) \
+ if (c != -1 && kmap[(unsigned char)c].type == ISFUNC) \
+ kmap[(unsigned char)c].function = rl_insert;
+
+static void
+_rl_bind_tty_special_chars (Keymap kmap, TIOTYPE ttybuff)
+{
+ if (ttybuff.flags & SGTTY_SET)
+ {
+ SET_SPECIAL (ttybuff.sgttyb.sg_erase, rl_rubout);
+ SET_SPECIAL (ttybuff.sgttyb.sg_kill, rl_unix_line_discard);
+ }
+
+# if defined (TIOCGLTC)
+ if (ttybuff.flags & LTCHARS_SET)
+ {
+ SET_SPECIAL (ttybuff.ltchars.t_werasc, rl_unix_word_rubout);
+ SET_SPECIAL (ttybuff.ltchars.t_lnextc, rl_quoted_insert);
+ }
+# endif /* TIOCGLTC */
+}
+
+#else /* !NEW_TTY_DRIVER */
+static void
+set_special_char (Keymap kmap, TIOTYPE *tiop, int sc, rl_command_func_t *func)
+{
+ unsigned char uc;
+
+ uc = tiop->c_cc[sc];
+ if (uc != (unsigned char)_POSIX_VDISABLE && kmap[uc].type == ISFUNC)
+ kmap[uc].function = func;
+}
+
+/* used later */
+#define RESET_SPECIAL(uc) \
+ if (uc != (unsigned char)_POSIX_VDISABLE && kmap[uc].type == ISFUNC) \
+ kmap[uc].function = rl_insert;
+
+static void
+_rl_bind_tty_special_chars (Keymap kmap, TIOTYPE ttybuff)
+{
+ SET_SPECIAL (VERASE, rl_rubout);
+ SET_SPECIAL (VKILL, rl_unix_line_discard);
+
+# if defined (VLNEXT) && defined (TERMIOS_TTY_DRIVER)
+ SET_SPECIAL (VLNEXT, rl_quoted_insert);
+# endif /* VLNEXT && TERMIOS_TTY_DRIVER */
+
+# if defined (VWERASE) && defined (TERMIOS_TTY_DRIVER)
+# if defined (VI_MODE)
+ if (rl_editing_mode == vi_mode)
+ SET_SPECIAL (VWERASE, rl_vi_unix_word_rubout);
+ else
+# endif
+ SET_SPECIAL (VWERASE, rl_unix_word_rubout);
+# endif /* VWERASE && TERMIOS_TTY_DRIVER */
+}
+
+#endif /* !NEW_TTY_DRIVER */
+
+/* Set the system's default editing characters to their readline equivalents
+ in KMAP. Should be static, now that we have rl_tty_set_default_bindings. */
+void
+rltty_set_default_bindings (Keymap kmap)
+{
+#if !defined (NO_TTY_DRIVER)
+ TIOTYPE ttybuff;
+ int tty;
+
+ tty = fileno (rl_instream);
+
+ if (get_tty_settings (tty, &ttybuff) == 0)
+ _rl_bind_tty_special_chars (kmap, ttybuff);
+#endif
+}
+
+/* New public way to set the system default editing chars to their readline
+ equivalents. */
+void
+rl_tty_set_default_bindings (Keymap kmap)
+{
+ rltty_set_default_bindings (kmap);
+}
+
+/* Rebind all of the tty special chars that readline worries about back
+ to self-insert. Call this before saving the current terminal special
+ chars with save_tty_chars(). This only works on POSIX termios or termio
+ systems. */
+void
+rl_tty_unset_default_bindings (Keymap kmap)
+{
+ /* Don't bother before we've saved the tty special chars at least once. */
+ if (RL_ISSTATE(RL_STATE_TTYCSAVED) == 0)
+ return;
+
+ RESET_SPECIAL (_rl_tty_chars.t_erase);
+ RESET_SPECIAL (_rl_tty_chars.t_kill);
+
+# if defined (VLNEXT) && defined (TERMIOS_TTY_DRIVER)
+ RESET_SPECIAL (_rl_tty_chars.t_lnext);
+# endif /* VLNEXT && TERMIOS_TTY_DRIVER */
+
+# if defined (VWERASE) && defined (TERMIOS_TTY_DRIVER)
+ RESET_SPECIAL (_rl_tty_chars.t_werase);
+# endif /* VWERASE && TERMIOS_TTY_DRIVER */
+}
+
+#if defined (HANDLE_SIGNALS)
+
+#if defined (NEW_TTY_DRIVER) || defined (NO_TTY_DRIVER)
+int
+_rl_disable_tty_signals (void)
+{
+ return 0;
+}
+
+int
+_rl_restore_tty_signals (void)
+{
+ return 0;
+}
+#else
+
+static TIOTYPE sigstty, nosigstty;
+static int tty_sigs_disabled = 0;
+
+int
+_rl_disable_tty_signals (void)
+{
+ if (tty_sigs_disabled)
+ return 0;
+
+ if (_get_tty_settings (fileno (rl_instream), &sigstty) < 0)
+ return -1;
+
+ nosigstty = sigstty;
+
+ nosigstty.c_lflag &= ~ISIG;
+ nosigstty.c_iflag &= ~IXON;
+
+ if (_set_tty_settings (fileno (rl_instream), &nosigstty) < 0)
+ return (_set_tty_settings (fileno (rl_instream), &sigstty));
+
+ tty_sigs_disabled = 1;
+ return 0;
+}
+
+int
+_rl_restore_tty_signals (void)
+{
+ int r;
+
+ if (tty_sigs_disabled == 0)
+ return 0;
+
+ r = _set_tty_settings (fileno (rl_instream), &sigstty);
+
+ if (r == 0)
+ tty_sigs_disabled = 0;
+
+ return r;
+}
+#endif /* !NEW_TTY_DRIVER */
+
+#endif /* HANDLE_SIGNALS */
diff --git a/third_party/readline/rltty.h b/third_party/readline/rltty.h
new file mode 100644
index 000000000..5bcc946b2
--- /dev/null
+++ b/third_party/readline/rltty.h
@@ -0,0 +1,80 @@
+/* rltty.h - tty driver-related definitions used by some library files. */
+
+/* Copyright (C) 1995-2009 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#if !defined (_RLTTY_H_)
+#define _RLTTY_H_
+
+/* Posix systems use termios and the Posix signal functions. */
+#if defined (TERMIOS_TTY_DRIVER)
+# include
+#endif /* TERMIOS_TTY_DRIVER */
+
+/* System V machines use termio. */
+#if defined (TERMIO_TTY_DRIVER)
+# include
+# if !defined (TCOON)
+# define TCOON 1
+# endif
+#endif /* TERMIO_TTY_DRIVER */
+
+/* Other (BSD) machines use sgtty. */
+#if defined (NEW_TTY_DRIVER)
+# include
+#endif
+
+#include "rlwinsize.h"
+
+/* Define _POSIX_VDISABLE if we are not using the `new' tty driver and
+ it is not already defined. It is used both to determine if a
+ special character is disabled and to disable certain special
+ characters. Posix systems should set to 0, USG systems to -1. */
+#if !defined (NEW_TTY_DRIVER) && !defined (_POSIX_VDISABLE)
+# if defined (_SVR4_VDISABLE)
+# define _POSIX_VDISABLE _SVR4_VDISABLE
+# else
+# if defined (_POSIX_VERSION)
+# define _POSIX_VDISABLE 0
+# else /* !_POSIX_VERSION */
+# define _POSIX_VDISABLE -1
+# endif /* !_POSIX_VERSION */
+# endif /* !_SVR4_DISABLE */
+#endif /* !NEW_TTY_DRIVER && !_POSIX_VDISABLE */
+
+typedef struct _rl_tty_chars {
+ unsigned char t_eof;
+ unsigned char t_eol;
+ unsigned char t_eol2;
+ unsigned char t_erase;
+ unsigned char t_werase;
+ unsigned char t_kill;
+ unsigned char t_reprint;
+ unsigned char t_intr;
+ unsigned char t_quit;
+ unsigned char t_susp;
+ unsigned char t_dsusp;
+ unsigned char t_start;
+ unsigned char t_stop;
+ unsigned char t_lnext;
+ unsigned char t_flush;
+ unsigned char t_status;
+} _RL_TTY_CHARS;
+
+#endif /* _RLTTY_H_ */
diff --git a/third_party/readline/rltypedefs.h b/third_party/readline/rltypedefs.h
new file mode 100644
index 000000000..163654921
--- /dev/null
+++ b/third_party/readline/rltypedefs.h
@@ -0,0 +1,100 @@
+/* rltypedefs.h -- Type declarations for readline functions. */
+
+/* Copyright (C) 2000-2021 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#ifndef _RL_TYPEDEFS_H_
+#define _RL_TYPEDEFS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Old-style, attempt to mark as deprecated in some way people will notice. */
+
+#if !defined (_FUNCTION_DEF)
+# define _FUNCTION_DEF
+
+#if defined(__GNUC__) || defined(__clang__)
+typedef int Function () __attribute__((deprecated));
+typedef void VFunction () __attribute__((deprecated));
+typedef char *CPFunction () __attribute__((deprecated));
+typedef char **CPPFunction () __attribute__((deprecated));
+#else
+typedef int Function ();
+typedef void VFunction ();
+typedef char *CPFunction ();
+typedef char **CPPFunction ();
+#endif
+
+#endif /* _FUNCTION_DEF */
+
+/* New style. */
+
+#if !defined (_RL_FUNCTION_TYPEDEF)
+# define _RL_FUNCTION_TYPEDEF
+
+/* Bindable functions */
+typedef int rl_command_func_t (int, int);
+
+/* Typedefs for the completion system */
+typedef char *rl_compentry_func_t (const char *, int);
+typedef char **rl_completion_func_t (const char *, int, int);
+
+typedef char *rl_quote_func_t (char *, int, char *);
+typedef char *rl_dequote_func_t (char *, int);
+
+typedef int rl_compignore_func_t (char **);
+
+typedef void rl_compdisp_func_t (char **, int, int);
+
+/* Type for input and pre-read hook functions like rl_event_hook */
+typedef int rl_hook_func_t (void);
+
+/* Input function type */
+typedef int rl_getc_func_t (FILE *);
+
+/* Generic function that takes a character buffer (which could be the readline
+ line buffer) and an index into it (which could be rl_point) and returns
+ an int. */
+typedef int rl_linebuf_func_t (char *, int);
+
+/* `Generic' function pointer typedefs */
+typedef int rl_intfunc_t (int);
+#define rl_ivoidfunc_t rl_hook_func_t
+typedef int rl_icpfunc_t (char *);
+typedef int rl_icppfunc_t (char **);
+
+typedef void rl_voidfunc_t (void);
+typedef void rl_vintfunc_t (int);
+typedef void rl_vcpfunc_t (char *);
+typedef void rl_vcppfunc_t (char **);
+
+typedef char *rl_cpvfunc_t (void);
+typedef char *rl_cpifunc_t (int);
+typedef char *rl_cpcpfunc_t (char *);
+typedef char *rl_cpcppfunc_t (char **);
+
+#endif /* _RL_FUNCTION_TYPEDEF */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RL_TYPEDEFS_H_ */
diff --git a/third_party/readline/rlwinsize.h b/third_party/readline/rlwinsize.h
new file mode 100644
index 000000000..d198fcf87
--- /dev/null
+++ b/third_party/readline/rlwinsize.h
@@ -0,0 +1,58 @@
+/* rlwinsize.h -- an attempt to isolate some of the system-specific defines
+ for `struct winsize' and TIOCGWINSZ. */
+
+/* Copyright (C) 1997-2009 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#if !defined (_RLWINSIZE_H_)
+#define _RLWINSIZE_H_
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+/* Try to find the definitions of `struct winsize' and TIOGCWINSZ */
+
+#if defined (GWINSZ_IN_SYS_IOCTL) && !defined (TIOCGWINSZ)
+# include
+#endif /* GWINSZ_IN_SYS_IOCTL && !TIOCGWINSZ */
+
+#if defined (STRUCT_WINSIZE_IN_TERMIOS) && !defined (STRUCT_WINSIZE_IN_SYS_IOCTL)
+# include
+#endif /* STRUCT_WINSIZE_IN_TERMIOS && !STRUCT_WINSIZE_IN_SYS_IOCTL */
+
+/* Not in either of the standard places, look around. */
+#if !defined (STRUCT_WINSIZE_IN_TERMIOS) && !defined (STRUCT_WINSIZE_IN_SYS_IOCTL)
+# if defined (HAVE_SYS_STREAM_H)
+# include
+# endif /* HAVE_SYS_STREAM_H */
+# if defined (HAVE_SYS_PTEM_H) /* SVR4.2, at least, has it here */
+# include
+# define _IO_PTEM_H /* work around SVR4.2 1.1.4 bug */
+# endif /* HAVE_SYS_PTEM_H */
+# if defined (HAVE_SYS_PTE_H) /* ??? */
+# include
+# endif /* HAVE_SYS_PTE_H */
+#endif /* !STRUCT_WINSIZE_IN_TERMIOS && !STRUCT_WINSIZE_IN_SYS_IOCTL */
+
+#if defined (M_UNIX) && !defined (_SCO_DS) && !defined (tcflow)
+# define tcflow(fd, action) ioctl(fd, TCXONC, action)
+#endif
+
+#endif /* _RL_WINSIZE_H */
diff --git a/third_party/readline/search.c b/third_party/readline/search.c
new file mode 100644
index 000000000..676e7bf69
--- /dev/null
+++ b/third_party/readline/search.c
@@ -0,0 +1,707 @@
+/* search.c - code for non-incremental searching in emacs and vi modes. */
+
+/* Copyright (C) 1992-2022 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+#include
+
+#if defined (HAVE_UNISTD_H)
+# include
+#endif
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif
+
+#include "rldefs.h"
+#include "rlmbutil.h"
+
+#include "readline.h"
+#include "history.h"
+#include "histlib.h"
+
+#include "rlprivate.h"
+#include "xmalloc.h"
+
+#ifdef abs
+# undef abs
+#endif
+#define abs(x) (((x) >= 0) ? (x) : -(x))
+
+_rl_search_cxt *_rl_nscxt = 0;
+
+static char *noninc_search_string = (char *) NULL;
+static int noninc_history_pos;
+
+static char *prev_line_found = (char *) NULL;
+
+static int _rl_history_search_len;
+/*static*/ int _rl_history_search_pos;
+static int _rl_history_search_flags;
+
+static char *history_search_string;
+static int history_string_size;
+
+static void make_history_line_current (HIST_ENTRY *);
+static int noninc_search_from_pos (char *, int, int, int, int *);
+static int noninc_dosearch (char *, int, int);
+static int noninc_search (int, int);
+static int rl_history_search_internal (int, int);
+static void rl_history_search_reinit (int);
+
+static _rl_search_cxt *_rl_nsearch_init (int, int);
+static void _rl_nsearch_abort (_rl_search_cxt *);
+static int _rl_nsearch_dispatch (_rl_search_cxt *, int);
+
+/* Make the data from the history entry ENTRY be the contents of the
+ current line. This doesn't do anything with rl_point; the caller
+ must set it. */
+static void
+make_history_line_current (HIST_ENTRY *entry)
+{
+ UNDO_LIST *xlist;
+
+ xlist = _rl_saved_line_for_history ? (UNDO_LIST *)_rl_saved_line_for_history->data : 0;
+ /* At this point, rl_undo_list points to a private search string list. */
+ if (rl_undo_list && rl_undo_list != (UNDO_LIST *)entry->data && rl_undo_list != xlist)
+ rl_free_undo_list ();
+
+ /* Now we create a new undo list with a single insert for this text.
+ WE DON'T CHANGE THE ORIGINAL HISTORY ENTRY UNDO LIST */
+ _rl_replace_text (entry->line, 0, rl_end);
+ _rl_fix_point (1);
+#if defined (VI_MODE)
+ if (rl_editing_mode == vi_mode)
+ /* POSIX.2 says that the `U' command doesn't affect the copy of any
+ command lines to the edit line. We're going to implement that by
+ making the undo list start after the matching line is copied to the
+ current editing buffer. */
+ rl_free_undo_list ();
+#endif
+
+ /* This will need to free the saved undo list associated with the original
+ (pre-search) line buffer.
+ XXX - look at _rl_free_saved_history_line and consider calling it if
+ rl_undo_list != xlist (or calling rl_free_undo list directly on
+ _rl_saved_line_for_history->data) */
+ if (_rl_saved_line_for_history)
+ _rl_free_history_entry (_rl_saved_line_for_history);
+ _rl_saved_line_for_history = (HIST_ENTRY *)NULL;
+}
+
+/* Search the history list for STRING starting at absolute history position
+ POS. If STRING begins with `^', the search must match STRING at the
+ beginning of a history line, otherwise a full substring match is performed
+ for STRING. DIR < 0 means to search backwards through the history list,
+ DIR >= 0 means to search forward. */
+static int
+noninc_search_from_pos (char *string, int pos, int dir, int flags, int *ncp)
+{
+ int ret, old, sflags;
+ char *s;
+
+ if (pos < 0)
+ return -1;
+
+ old = where_history ();
+ if (history_set_pos (pos) == 0)
+ return -1;
+
+ RL_SETSTATE(RL_STATE_SEARCH);
+ /* These functions return the match offset in the line; history_offset gives
+ the matching line in the history list */
+ if (flags & SF_PATTERN)
+ {
+ s = string;
+ sflags = 0; /* Non-anchored search */
+ if (*s == '^')
+ {
+ sflags |= ANCHORED_SEARCH;
+ s++;
+ }
+ ret = _hs_history_patsearch (s, dir, sflags);
+ }
+ else if (*string == '^')
+ ret = history_search_prefix (string + 1, dir);
+ else
+ ret = history_search (string, dir);
+ RL_UNSETSTATE(RL_STATE_SEARCH);
+
+ if (ncp)
+ *ncp = ret; /* caller will catch -1 to indicate no-op */
+
+ if (ret != -1)
+ ret = where_history ();
+
+ history_set_pos (old);
+ return (ret);
+}
+
+/* Search for a line in the history containing STRING. If DIR is < 0, the
+ search is backwards through previous entries, else through subsequent
+ entries. Returns 1 if the search was successful, 0 otherwise. */
+static int
+noninc_dosearch (char *string, int dir, int flags)
+{
+ int oldpos, pos, ind;
+ HIST_ENTRY *entry;
+
+ if (string == 0 || *string == '\0' || noninc_history_pos < 0)
+ {
+ rl_ding ();
+ return 0;
+ }
+
+ pos = noninc_search_from_pos (string, noninc_history_pos + dir, dir, flags, &ind);
+ if (pos == -1)
+ {
+ /* Search failed, current history position unchanged. */
+ rl_maybe_unsave_line ();
+ rl_clear_message ();
+ rl_point = 0;
+ rl_ding ();
+ return 0;
+ }
+
+ noninc_history_pos = pos;
+
+ oldpos = where_history ();
+ history_set_pos (noninc_history_pos);
+ entry = current_history (); /* will never be NULL after successful search */
+
+#if defined (VI_MODE)
+ if (rl_editing_mode != vi_mode)
+#endif
+ history_set_pos (oldpos);
+
+ make_history_line_current (entry);
+
+ if (_rl_enable_active_region && ((flags & SF_PATTERN) == 0) && ind > 0 && ind < rl_end)
+ {
+ rl_point = ind;
+ rl_mark = ind + strlen (string);
+ if (rl_mark > rl_end)
+ rl_mark = rl_end; /* can't happen? */
+ rl_activate_mark ();
+ }
+ else
+ {
+ rl_point = 0;
+ rl_mark = rl_end;
+ }
+
+ rl_clear_message ();
+ return 1;
+}
+
+static _rl_search_cxt *
+_rl_nsearch_init (int dir, int pchar)
+{
+ _rl_search_cxt *cxt;
+ char *p;
+
+ cxt = _rl_scxt_alloc (RL_SEARCH_NSEARCH, 0);
+ if (dir < 0)
+ cxt->sflags |= SF_REVERSE; /* not strictly needed */
+#if defined (VI_MODE)
+ if (VI_COMMAND_MODE() && (pchar == '?' || pchar == '/'))
+ cxt->sflags |= SF_PATTERN;
+#endif
+
+ cxt->direction = dir;
+ cxt->history_pos = cxt->save_line;
+
+ rl_maybe_save_line ();
+
+ /* Clear the undo list, since reading the search string should create its
+ own undo list, and the whole list will end up being freed when we
+ finish reading the search string. */
+ rl_undo_list = 0;
+
+ /* Use the line buffer to read the search string. */
+ rl_line_buffer[0] = 0;
+ rl_end = rl_point = 0;
+
+ p = _rl_make_prompt_for_search (pchar ? pchar : ':');
+ rl_message ("%s", p);
+ xfree (p);
+
+ RL_SETSTATE(RL_STATE_NSEARCH);
+
+ _rl_nscxt = cxt;
+
+ return cxt;
+}
+
+int
+_rl_nsearch_cleanup (_rl_search_cxt *cxt, int r)
+{
+ _rl_scxt_dispose (cxt, 0);
+ _rl_nscxt = 0;
+
+ RL_UNSETSTATE(RL_STATE_NSEARCH);
+
+ return (r != 1);
+}
+
+static void
+_rl_nsearch_abort (_rl_search_cxt *cxt)
+{
+ rl_maybe_unsave_line ();
+ rl_point = cxt->save_point;
+ rl_mark = cxt->save_mark;
+ rl_restore_prompt ();
+ rl_clear_message ();
+ _rl_fix_point (1);
+
+ RL_UNSETSTATE (RL_STATE_NSEARCH);
+}
+
+/* Process just-read character C according to search context CXT. Return -1
+ if the caller should abort the search, 0 if we should break out of the
+ loop, and 1 if we should continue to read characters. */
+static int
+_rl_nsearch_dispatch (_rl_search_cxt *cxt, int c)
+{
+ int n;
+
+ if (c < 0)
+ c = CTRL ('C');
+
+ switch (c)
+ {
+ case CTRL('W'):
+ rl_unix_word_rubout (1, c);
+ break;
+
+ case CTRL('U'):
+ rl_unix_line_discard (1, c);
+ break;
+
+ case RETURN:
+ case NEWLINE:
+ return 0;
+
+ case CTRL('H'):
+ case RUBOUT:
+ if (rl_point == 0)
+ {
+ _rl_nsearch_abort (cxt);
+ return -1;
+ }
+ _rl_rubout_char (1, c);
+ break;
+
+ case CTRL('C'):
+ case CTRL('G'):
+ rl_ding ();
+ _rl_nsearch_abort (cxt);
+ return -1;
+
+ case ESC:
+ /* XXX - experimental code to allow users to bracketed-paste into the
+ search string. Similar code is in isearch.c:_rl_isearch_dispatch().
+ The difference here is that the bracketed paste sometimes doesn't
+ paste everything, so checking for the prefix and the suffix in the
+ input queue doesn't work well. We just have to check to see if the
+ number of chars in the input queue is enough for the bracketed paste
+ prefix and hope for the best. */
+ if (_rl_enable_bracketed_paste && ((n = _rl_nchars_available ()) >= (BRACK_PASTE_SLEN-1)))
+ {
+ if (_rl_read_bracketed_paste_prefix (c) == 1)
+ rl_bracketed_paste_begin (1, c);
+ else
+ {
+ c = rl_read_key (); /* get the ESC that got pushed back */
+ _rl_insert_char (1, c);
+ }
+ }
+ else
+ _rl_insert_char (1, c);
+ break;
+
+ default:
+#if defined (HANDLE_MULTIBYTE)
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ rl_insert_text (cxt->mb);
+ else
+#endif
+ _rl_insert_char (1, c);
+ break;
+ }
+
+ (*rl_redisplay_function) ();
+ rl_deactivate_mark ();
+ return 1;
+}
+
+/* Perform one search according to CXT, using NONINC_SEARCH_STRING. Return
+ -1 if the search should be aborted, any other value means to clean up
+ using _rl_nsearch_cleanup (). Returns 1 if the search was successful,
+ 0 otherwise. */
+static int
+_rl_nsearch_dosearch (_rl_search_cxt *cxt)
+{
+ rl_mark = cxt->save_mark;
+
+ /* If rl_point == 0, we want to re-use the previous search string and
+ start from the saved history position. If there's no previous search
+ string, punt. */
+ if (rl_point == 0)
+ {
+ if (noninc_search_string == 0)
+ {
+ rl_ding ();
+ rl_restore_prompt ();
+ RL_UNSETSTATE (RL_STATE_NSEARCH);
+ return -1;
+ }
+ }
+ else
+ {
+ /* We want to start the search from the current history position. */
+ noninc_history_pos = cxt->save_line;
+ FREE (noninc_search_string);
+ noninc_search_string = savestring (rl_line_buffer);
+
+ /* If we don't want the subsequent undo list generated by the search
+ matching a history line to include the contents of the search string,
+ we need to clear rl_line_buffer here. For now, we just clear the
+ undo list generated by reading the search string. (If the search
+ fails, the old undo list will be restored by rl_maybe_unsave_line.) */
+ rl_free_undo_list ();
+ }
+
+ rl_restore_prompt ();
+ return (noninc_dosearch (noninc_search_string, cxt->direction, cxt->sflags&SF_PATTERN));
+}
+
+/* Search non-interactively through the history list. DIR < 0 means to
+ search backwards through the history of previous commands; otherwise
+ the search is for commands subsequent to the current position in the
+ history list. PCHAR is the character to use for prompting when reading
+ the search string; if not specified (0), it defaults to `:'. */
+static int
+noninc_search (int dir, int pchar)
+{
+ _rl_search_cxt *cxt;
+ int c, r;
+
+ cxt = _rl_nsearch_init (dir, pchar);
+
+ if (RL_ISSTATE (RL_STATE_CALLBACK))
+ return (0);
+
+ /* Read the search string. */
+ r = 0;
+ while (1)
+ {
+ c = _rl_search_getchar (cxt);
+
+ if (c < 0)
+ {
+ _rl_nsearch_abort (cxt);
+ return 1;
+ }
+
+ if (c == 0)
+ break;
+
+ r = _rl_nsearch_dispatch (cxt, c);
+ if (r < 0)
+ return 1;
+ else if (r == 0)
+ break;
+ }
+
+ r = _rl_nsearch_dosearch (cxt);
+ return ((r >= 0) ? _rl_nsearch_cleanup (cxt, r) : (r != 1));
+}
+
+/* Search forward through the history list for a string. If the vi-mode
+ code calls this, KEY will be `?'. */
+int
+rl_noninc_forward_search (int count, int key)
+{
+ return noninc_search (1, (key == '?') ? '?' : 0);
+}
+
+/* Reverse search the history list for a string. If the vi-mode code
+ calls this, KEY will be `/'. */
+int
+rl_noninc_reverse_search (int count, int key)
+{
+ return noninc_search (-1, (key == '/') ? '/' : 0);
+}
+
+/* Search forward through the history list for the last string searched
+ for. If there is no saved search string, abort. If the vi-mode code
+ calls this, KEY will be `N'. */
+int
+rl_noninc_forward_search_again (int count, int key)
+{
+ int r;
+
+ if (!noninc_search_string)
+ {
+ rl_ding ();
+ return (1);
+ }
+#if defined (VI_MODE)
+ if (VI_COMMAND_MODE() && key == 'N')
+ r = noninc_dosearch (noninc_search_string, 1, SF_PATTERN);
+ else
+#endif
+ r = noninc_dosearch (noninc_search_string, 1, 0);
+ return (r != 1);
+}
+
+/* Reverse search in the history list for the last string searched
+ for. If there is no saved search string, abort. If the vi-mode code
+ calls this, KEY will be `n'. */
+int
+rl_noninc_reverse_search_again (int count, int key)
+{
+ int r;
+
+ if (!noninc_search_string)
+ {
+ rl_ding ();
+ return (1);
+ }
+#if defined (VI_MODE)
+ if (VI_COMMAND_MODE() && key == 'n')
+ r = noninc_dosearch (noninc_search_string, -1, SF_PATTERN);
+ else
+#endif
+ r = noninc_dosearch (noninc_search_string, -1, 0);
+ return (r != 1);
+}
+
+#if defined (READLINE_CALLBACKS)
+int
+_rl_nsearch_callback (_rl_search_cxt *cxt)
+{
+ int c, r;
+
+ c = _rl_search_getchar (cxt);
+ if (c <= 0)
+ {
+ if (c < 0)
+ _rl_nsearch_abort (cxt);
+ return 1;
+ }
+ r = _rl_nsearch_dispatch (cxt, c);
+ if (r != 0)
+ return 1;
+
+ r = _rl_nsearch_dosearch (cxt);
+ return ((r >= 0) ? _rl_nsearch_cleanup (cxt, r) : (r != 1));
+}
+#endif
+
+static int
+rl_history_search_internal (int count, int dir)
+{
+ HIST_ENTRY *temp;
+ int ret, oldpos, newcol;
+ int had_saved_line;
+ char *t;
+
+ had_saved_line = _rl_saved_line_for_history != 0;
+ rl_maybe_save_line ();
+ temp = (HIST_ENTRY *)NULL;
+
+ /* Search COUNT times through the history for a line matching
+ history_search_string. If history_search_string[0] == '^', the
+ line must match from the start; otherwise any substring can match.
+ When this loop finishes, TEMP, if non-null, is the history line to
+ copy into the line buffer. */
+ while (count)
+ {
+ RL_CHECK_SIGNALS ();
+ ret = noninc_search_from_pos (history_search_string, _rl_history_search_pos + dir, dir, 0, &newcol);
+ if (ret == -1)
+ break;
+
+ /* Get the history entry we found. */
+ _rl_history_search_pos = ret;
+ oldpos = where_history ();
+ history_set_pos (_rl_history_search_pos);
+ temp = current_history (); /* will never be NULL after successful search */
+ history_set_pos (oldpos);
+
+ /* Don't find multiple instances of the same line. */
+ if (prev_line_found && STREQ (prev_line_found, temp->line))
+ continue;
+ prev_line_found = temp->line;
+ count--;
+ }
+
+ /* If we didn't find anything at all, return. */
+ if (temp == 0)
+ {
+ /* XXX - check had_saved_line here? */
+ rl_maybe_unsave_line ();
+ rl_ding ();
+ /* If you don't want the saved history line (last match) to show up
+ in the line buffer after the search fails, change the #if 0 to
+ #if 1 */
+#if 0
+ if (rl_point > _rl_history_search_len)
+ {
+ rl_point = rl_end = _rl_history_search_len;
+ rl_line_buffer[rl_end] = '\0';
+ rl_mark = 0;
+ }
+#else
+ rl_point = _rl_history_search_len; /* rl_maybe_unsave_line changes it */
+ rl_mark = rl_end;
+#endif
+ return 1;
+ }
+
+ /* Copy the line we found into the current line buffer. */
+ make_history_line_current (temp);
+
+ /* decide where to put rl_point -- need to change this for pattern search */
+ if (_rl_history_search_flags & ANCHORED_SEARCH)
+ rl_point = _rl_history_search_len; /* easy case */
+ else
+ {
+#if 0
+ t = strstr (rl_line_buffer, history_search_string); /* XXX */
+ rl_point = t ? (int)(t - rl_line_buffer) + _rl_history_search_len : rl_end;
+#else
+ rl_point = (newcol >= 0) ? newcol : rl_end;
+#endif
+ }
+ rl_mark = rl_end;
+
+ return 0;
+}
+
+static void
+rl_history_search_reinit (int flags)
+{
+ int sind;
+
+ _rl_history_search_pos = where_history ();
+ _rl_history_search_len = rl_point;
+ _rl_history_search_flags = flags;
+
+ prev_line_found = (char *)NULL;
+ if (rl_point)
+ {
+ /* Allocate enough space for anchored and non-anchored searches */
+ if (_rl_history_search_len >= history_string_size - 2)
+ {
+ history_string_size = _rl_history_search_len + 2;
+ history_search_string = (char *)xrealloc (history_search_string, history_string_size);
+ }
+ sind = 0;
+ if (flags & ANCHORED_SEARCH)
+ history_search_string[sind++] = '^';
+ strncpy (history_search_string + sind, rl_line_buffer, rl_point);
+ history_search_string[rl_point + sind] = '\0';
+ }
+ _rl_free_saved_history_line (); /* XXX rl_undo_list? */
+}
+
+/* Search forward in the history for the string of characters
+ from the start of the line to rl_point. This is a non-incremental
+ search. The search is anchored to the beginning of the history line. */
+int
+rl_history_search_forward (int count, int ignore)
+{
+ if (count == 0)
+ return (0);
+
+ if (rl_last_func != rl_history_search_forward &&
+ rl_last_func != rl_history_search_backward)
+ rl_history_search_reinit (ANCHORED_SEARCH);
+
+ if (_rl_history_search_len == 0)
+ return (rl_get_next_history (count, ignore));
+ return (rl_history_search_internal (abs (count), (count > 0) ? 1 : -1));
+}
+
+/* Search backward through the history for the string of characters
+ from the start of the line to rl_point. This is a non-incremental
+ search. */
+int
+rl_history_search_backward (int count, int ignore)
+{
+ if (count == 0)
+ return (0);
+
+ if (rl_last_func != rl_history_search_forward &&
+ rl_last_func != rl_history_search_backward)
+ rl_history_search_reinit (ANCHORED_SEARCH);
+
+ if (_rl_history_search_len == 0)
+ return (rl_get_previous_history (count, ignore));
+ return (rl_history_search_internal (abs (count), (count > 0) ? -1 : 1));
+}
+
+/* Search forward in the history for the string of characters
+ from the start of the line to rl_point. This is a non-incremental
+ search. The search succeeds if the search string is present anywhere
+ in the history line. */
+int
+rl_history_substr_search_forward (int count, int ignore)
+{
+ if (count == 0)
+ return (0);
+
+ if (rl_last_func != rl_history_substr_search_forward &&
+ rl_last_func != rl_history_substr_search_backward)
+ rl_history_search_reinit (NON_ANCHORED_SEARCH);
+
+ if (_rl_history_search_len == 0)
+ return (rl_get_next_history (count, ignore));
+ return (rl_history_search_internal (abs (count), (count > 0) ? 1 : -1));
+}
+
+/* Search backward through the history for the string of characters
+ from the start of the line to rl_point. This is a non-incremental
+ search. */
+int
+rl_history_substr_search_backward (int count, int ignore)
+{
+ if (count == 0)
+ return (0);
+
+ if (rl_last_func != rl_history_substr_search_forward &&
+ rl_last_func != rl_history_substr_search_backward)
+ rl_history_search_reinit (NON_ANCHORED_SEARCH);
+
+ if (_rl_history_search_len == 0)
+ return (rl_get_previous_history (count, ignore));
+ return (rl_history_search_internal (abs (count), (count > 0) ? -1 : 1));
+}
diff --git a/third_party/readline/shell.c b/third_party/readline/shell.c
new file mode 100644
index 000000000..60275a69c
--- /dev/null
+++ b/third_party/readline/shell.c
@@ -0,0 +1,214 @@
+/* shell.c -- readline utility functions that are normally provided by
+ bash when readline is linked as part of the shell. */
+
+/* Copyright (C) 1997-2009,2017,2021 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include
+
+#if defined (HAVE_UNISTD_H)
+# include
+#endif /* HAVE_UNISTD_H */
+
+#if defined (HAVE_STDLIB_H)
+# include
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#if defined (HAVE_STRING_H)
+# include
+#else
+# include
+#endif /* !HAVE_STRING_H */
+
+#if defined (HAVE_LIMITS_H)
+# include
+#endif
+
+#if defined (HAVE_FCNTL_H)
+#include
+#endif
+#if defined (HAVE_PWD_H)
+#include
+#endif
+
+#include
+
+#include "rlstdc.h"
+#include "rlshell.h"
+#include "rldefs.h"
+
+#include "xmalloc.h"
+
+#if defined (HAVE_GETPWUID) && !defined (HAVE_GETPW_DECLS)
+extern struct passwd *getpwuid (uid_t);
+#endif /* HAVE_GETPWUID && !HAVE_GETPW_DECLS */
+
+#ifndef NULL
+# define NULL 0
+#endif
+
+#ifndef CHAR_BIT
+# define CHAR_BIT 8
+#endif
+
+/* Nonzero if the integer type T is signed. */
+#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
+
+/* Bound on length of the string representing an integer value of type T.
+ Subtract one for the sign bit if T is signed;
+ 302 / 1000 is log10 (2) rounded up;
+ add one for integer division truncation;
+ add one more for a minus sign if t is signed. */
+#define INT_STRLEN_BOUND(t) \
+ ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 \
+ + 1 + TYPE_SIGNED (t))
+
+/* All of these functions are resolved from bash if we are linking readline
+ as part of bash. */
+
+/* Does shell-like quoting using single quotes. */
+char *
+sh_single_quote (char *string)
+{
+ register int c;
+ char *result, *r, *s;
+
+ result = (char *)xmalloc (3 + (4 * strlen (string)));
+ r = result;
+ *r++ = '\'';
+
+ for (s = string; s && (c = *s); s++)
+ {
+ *r++ = c;
+
+ if (c == '\'')
+ {
+ *r++ = '\\'; /* insert escaped single quote */
+ *r++ = '\'';
+ *r++ = '\''; /* start new quoted string */
+ }
+ }
+
+ *r++ = '\'';
+ *r = '\0';
+
+ return (result);
+}
+
+/* Set the environment variables LINES and COLUMNS to lines and cols,
+ respectively. */
+static char setenv_buf[INT_STRLEN_BOUND (int) + 1];
+static char putenv_buf1[INT_STRLEN_BOUND (int) + 6 + 1]; /* sizeof("LINES=") == 6 */
+static char putenv_buf2[INT_STRLEN_BOUND (int) + 8 + 1]; /* sizeof("COLUMNS=") == 8 */
+
+void
+sh_set_lines_and_columns (int lines, int cols)
+{
+#if defined (HAVE_SETENV)
+ sprintf (setenv_buf, "%d", lines);
+ setenv ("LINES", setenv_buf, 1);
+
+ sprintf (setenv_buf, "%d", cols);
+ setenv ("COLUMNS", setenv_buf, 1);
+#else /* !HAVE_SETENV */
+# if defined (HAVE_PUTENV)
+ sprintf (putenv_buf1, "LINES=%d", lines);
+ putenv (putenv_buf1);
+
+ sprintf (putenv_buf2, "COLUMNS=%d", cols);
+ putenv (putenv_buf2);
+# endif /* HAVE_PUTENV */
+#endif /* !HAVE_SETENV */
+}
+
+char *
+sh_get_env_value (const char *varname)
+{
+ return ((char *)getenv (varname));
+}
+
+char *
+sh_get_home_dir (void)
+{
+ static char *home_dir = (char *)NULL;
+ struct passwd *entry;
+
+ if (home_dir)
+ return (home_dir);
+
+ home_dir = (char *)NULL;
+#if defined (HAVE_GETPWUID)
+# if defined (__TANDEM)
+ entry = getpwnam (getlogin ());
+# else
+ entry = getpwuid (getuid ());
+# endif
+ if (entry)
+ home_dir = savestring (entry->pw_dir);
+#endif
+
+#if defined (HAVE_GETPWENT)
+ endpwent (); /* some systems need this */
+#endif
+
+ return (home_dir);
+}
+
+#if !defined (O_NDELAY)
+# if defined (FNDELAY)
+# define O_NDELAY FNDELAY
+# endif
+#endif
+
+int
+sh_unset_nodelay_mode (int fd)
+{
+#if defined (HAVE_FCNTL)
+ int flags, bflags;
+
+ if ((flags = fcntl (fd, F_GETFL, 0)) < 0)
+ return -1;
+
+ bflags = 0;
+
+#ifdef O_NONBLOCK
+ bflags |= O_NONBLOCK;
+#endif
+
+#ifdef O_NDELAY
+ bflags |= O_NDELAY;
+#endif
+
+ if (flags & bflags)
+ {
+ flags &= ~bflags;
+ return (fcntl (fd, F_SETFL, flags));
+ }
+#endif
+
+ return 0;
+}
diff --git a/third_party/readline/signals.c b/third_party/readline/signals.c
new file mode 100644
index 000000000..3fcbdda39
--- /dev/null
+++ b/third_party/readline/signals.c
@@ -0,0 +1,754 @@
+/* signals.c -- signal handling support for readline. */
+
+/* Copyright (C) 1987-2021 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Readline 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 Readline. If not, see .
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include /* Just for NULL. Yuck. */
+#include
+#include
+
+#if defined (HAVE_UNISTD_H)
+# include
+#endif /* HAVE_UNISTD_H */
+
+/* System-specific feature definitions and include files. */
+#include "rldefs.h"
+
+#if defined (GWINSZ_IN_SYS_IOCTL)
+# include
+#endif /* GWINSZ_IN_SYS_IOCTL */
+
+/* Some standard library routines. */
+#include "readline.h"
+#include "history.h"
+
+#include "rlprivate.h"
+
+#if defined (HANDLE_SIGNALS)
+
+#define SIGHANDLER_RETURN return
+
+/* This typedef is equivalent to the one for Function; it allows us
+ to say SigHandler *foo = signal (SIGKILL, SIG_IGN); */
+typedef void SigHandler (int);
+
+#if defined (HAVE_POSIX_SIGNALS)
+typedef struct sigaction sighandler_cxt;
+# define rl_sigaction(s, nh, oh) sigaction(s, nh, oh)
+#else
+typedef struct { SigHandler *sa_handler; int sa_mask, sa_flags; } sighandler_cxt;
+# define sigemptyset(m)
+#endif /* !HAVE_POSIX_SIGNALS */
+
+#ifndef SA_RESTART
+# define SA_RESTART 0
+#endif
+
+static SigHandler *rl_set_sighandler (int, SigHandler *, sighandler_cxt *);
+static void rl_maybe_set_sighandler (int, SigHandler *, sighandler_cxt *);
+static void rl_maybe_restore_sighandler (int, sighandler_cxt *);
+
+static void rl_signal_handler (int);
+static void _rl_handle_signal (int);
+
+/* Exported variables for use by applications. */
+
+/* If non-zero, readline will install its own signal handlers for
+ SIGINT, SIGTERM, SIGHUP, SIGQUIT, SIGALRM, SIGTSTP, SIGTTIN, and SIGTTOU. */
+int rl_catch_signals = 1;
+
+/* If non-zero, readline will install a signal handler for SIGWINCH. */
+#ifdef SIGWINCH
+int rl_catch_sigwinch = 1;
+#else
+int rl_catch_sigwinch = 0; /* for the readline state struct in readline.c */
+#endif
+
+/* Private variables. */
+int volatile _rl_caught_signal = 0; /* should be sig_atomic_t, but that requires including