[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/eat 06e45d68b9 01/12: Implement the terminal
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/eat 06e45d68b9 01/12: Implement the terminal |
Date: |
Sun, 27 Nov 2022 15:59:29 -0500 (EST) |
branch: elpa/eat
commit 06e45d68b9c1066cd3a170cb786a0693656d28a2
Author: Akib Azmain Turja <akib@disroot.org>
Commit: Akib Azmain Turja <akib@disroot.org>
Implement the terminal
This includes everything written from 2022-08-15 up to now.
* .dir-locals.el:
* CONTRIBUTE:
* COPYING:
* Makefile:
* NEWS:
* README.org:
* eat-tests.el:
* eat.el:
* eat.texi:
* eat.ti:
* fdl.texi:
* gitlog-to-changelog:
* gpl.texi:
* make-changelog:
* texinfo.tex:
New file.
---
.dir-locals.el | 4 +
CONTRIBUTE | 252 ++
COPYING | 674 +++
Makefile | 39 +
NEWS | 32 +
README.org | 137 +
e/eat-256color | Bin 0 -> 2216 bytes
e/eat-color | Bin 0 -> 2218 bytes
e/eat-mono | Bin 0 -> 2008 bytes
e/eat-truecolor | Bin 0 -> 2223 bytes
eat-tests.el | 6230 +++++++++++++++++++++++++++
eat.el | 6662 +++++++++++++++++++++++++++++
eat.texi | 1001 +++++
eat.ti | 206 +
fdl.texi | 505 +++
gitlog-to-changelog | 516 +++
gpl.texi | 717 ++++
make-changelog | 40 +
texinfo.tex | 11614 ++++++++++++++++++++++++++++++++++++++++++++++++++
19 files changed, 28629 insertions(+)
diff --git a/.dir-locals.el b/.dir-locals.el
new file mode 100644
index 0000000000..433dfac2d0
--- /dev/null
+++ b/.dir-locals.el
@@ -0,0 +1,4 @@
+;;; Directory Local Variables -*- no-byte-compile: t -*-
+;;; For more information see (info "(emacs) Directory Variables")
+
+((sh-mode . ((sh-basic-offset . 2))))
diff --git a/CONTRIBUTE b/CONTRIBUTE
new file mode 100644
index 0000000000..67aa6e5c73
--- /dev/null
+++ b/CONTRIBUTE
@@ -0,0 +1,252 @@
+* How developers contribute to Eat
+
+Here is how software developers can contribute to Eat.
+
+** Documenting your changes
+
+Any change that matters to end-users should have an entry in etc/NEWS.
+Try to start each NEWS entry with a sentence that summarizes the entry
+and takes just one line -- this will allow to read NEWS in Outline
+mode after hiding the body of each entry.
+
+Doc-strings should be updated together with the code.
+
+Think about whether your change requires updating the manuals. If you
+know it does not, mark the NEWS entry with "---". If you know
+that *all* the necessary documentation updates have been made as part
+of your changes or those by others, mark the entry with "+++".
+Otherwise do not mark it.
+
+If your change requires updating the manuals to document new
+functions/commands/variables/faces, then use the proper Texinfo
+command to index them; for instance, use @vindex for variables and
+@findex for functions/commands. For the full list of predefined indices, see
+https://www.gnu.org/software/texinfo/manual/texinfo/html_node/Predefined-Indices.html
+or run the shell command 'info "(texinfo)Predefined Indices"'.
+
+We prefer American English both in doc strings and in the manuals,
+just like Emacs. That includes both spelling (e.g., "behavior", not
+"behaviour") and the convention of leaving 2 spaces between sentences.
+
+Eat follows Emacs doc style. For more specific tips on Emacs's doc style, see
+https://www.gnu.org/software/emacs/manual/html_node/elisp/Documentation-Tips.html
+Use 'checkdoc' to check for documentation errors before submitting a
+patch.
+
+** Testing your changes
+
+Please test your changes before committing them or sending them to the
+list. "make check" runs all the tests. If possible, add a new test
+along with any bug fix or new functionality you commit (of course,
+some changes cannot be easily tested, but the terminal emulator part
+should be as much tested as possible).
+
+Eat uses ERT, Emacs Lisp Regression Testing, for testing. See
+https://www.gnu.org/software/emacs/manual/html_node/ert/ or run
+'info "(ert)"' for more information on writing and running tests.
+
+If your test lasts longer than some few seconds, mark it in its
+'ert-deftest' definition with ":tags '(:expensive-test)".
+
+** Commit messages
+
+Ordinarily, a change you commit should contain a log entry in its
+commit message and should not touch the repository's ChangeLog files.
+Here is an example commit message (indented):
+
+ Deactivate shifted region
+
+ Do not silently extend a region that is not highlighted;
+ this can happen after a shift (Bug#19003).
+ * doc/emacs/mark.texi (Shift Selection): Document the change.
+ * lisp/window.el (handle-select-window):
+ * src/frame.c (Fhandle_switch_frame, Fselected_frame):
+ Deactivate the mark.
+
+Here are guidelines for formatting commit message:
+
+- Start with a single unindented summary line explaining the change;
+ do not end this line with a period. If possible, try to keep the
+ summary line to 50 characters or fewer; this is for compatibility
+ with certain Git commands that print that line in width-constrained
+ contexts.
+
+ If the summary line starts with a semicolon and a space "; ", the
+ commit message will be ignored when generating the ChangeLog file.
+ Use this for minor commits that do not need separate ChangeLog
+ entries, such as changes in etc/NEWS.
+
+- After the summary line, there should be an empty line.
+
+- Unindented ChangeLog entries normally come next. However, if the
+ commit couldn't be properly summarized in the brief summary line,
+ you can put a paragraph (after the empty line and before the
+ individual ChangeLog entries) that further describes the commit.
+
+- Lines in ChangeLog entries should preferably be not longer than 63
+ characters, and must not exceed 78 characters, unless they consist
+ of a single word of at most 140 characters; this 78/140 limit is
+ enforced by a commit hook.
+
+- If only a single file is changed, the summary line can be the normal
+ file first line (starting with the asterisk). Then there is no
+ individual files section.
+
+- If the commit has more than one author, the commit message should
+ contain separate lines to mention the other authors, like the
+ following:
+
+ Co-authored-by: Joe Schmoe <j.schmoe@example.org>
+
+- If the commit is a tiny change that is exempt from copyright paperwork,
+ the commit message should contain a separate line like the following:
+
+ Copyright-paperwork-exempt: yes
+
+- The commit message should not contain any reference to any Codeberg
+ issue.
+
+- The commit message should contain "Bug#NNNNN" if it is related to
+ bug number NNNNN in the debbugs database. This string is often
+ parenthesized, as in "(Bug#19003)".
+
+- When citing URLs, prefer https: to http: when either will do.
+
+- Commit messages should contain only printable UTF-8 characters.
+
+- Commit messages should not contain the "Signed-off-by:" lines that
+ are used in some other projects.
+
+- Any lines of the commit message that start with "; " are omitted
+ from the generated ChangeLog.
+
+- Explaining the rationale for a design choice is best done in comments
+ in the source code. However, sometimes it is useful to describe just
+ the rationale for a change; that can be done in the commit message
+ between the summary line and the file entries.
+
+- Eat follows the GNU coding standards for ChangeLogs: see
+ https://www.gnu.org/prep/standards/html_node/Change-Logs.html
+ or run 'info "(standards)Change Logs"'.
+
+- Some commenting rules in the GNU coding standards also apply
+ to ChangeLog entries: they must be in English, and be complete
+ sentences starting with a capital and ending with a period (except
+ the summary line should not end in a period). See
+ https://www.gnu.org/prep/standards/html_node/Comments.html
+ or run 'info "(standards)Comments"'. American English is preferred
+ in Eat, just like Emacs; that includes spelling and leaving 2 blanks
+ between sentences.
+
+ They are preserved indefinitely, and have a reasonable chance of
+ being read in the future, so it's better that they have good
+ presentation.
+
+- Use the present tense; describe "what the change does", not "what
+ the change did".
+
+- Preferred form for several entries with the same content:
+
+ * lisp/menu-bar.el (clipboard-yank, clipboard-kill-ring-save)
+ (clipboard-kill-region):
+ * lisp/eshell/esh-io.el (eshell-virtual-targets)
+ (eshell-clipboard-append):
+ Replace option gui-select-enable-clipboard with
+ select-enable-clipboard; renamed October 2014. (Bug#25145)
+
+ (Rather than anything involving "ditto" and suchlike.)
+
+- There is no standard or recommended way to identify revisions in
+ ChangeLog entries. Using Git SHA1 values limits the usability of
+ the references to Git, and will become much less useful if Eat.
+ switches to a different VCS. So we recommend against doing only that.
+
+ One way to identify revisions is by quoting their summary line.
+ Prefixing the summary with the commit date can give useful context
+ (use 'git show -s "--pretty=format:%cd \"%s\"" --date=short HASH' to
+ produce that). Often, "my previous commit" will suffice.
+
+- There is no need to mention files such as NEWS and MAINTAINERS, or
+ to indicate regeneration of files such as 'lib/gnulib.mk', in the
+ ChangeLog entry. "There is no need" means you don't have to, but
+ you can if you want to.
+
+** Committing your changes.
+
+Your commit message must meet the following critiria:
+
+- commit log message must not be empty;
+- the first line of the commit log message doesn't start with
+ whitespace characters;
+- the second line of the commit log message must be empty;
+- commit log message should include only valid printable ASCII and
+ UTF-8 characters;
+- commit log message lines must be shorter than 79 characters, unless
+ a line consists of a single long word, in which case that word can
+ be up to 140 characters long;
+- there shouldn't be any "Signed-off-by:" tags in the commit log
+ message, and "git commit" should not be invoked with the '-s' option
+ (which automatically adds "Signed-off-by:");
+- if the commit adds new files, the file names must not begin with
+ '-' and must consist of ASCII letters, digits, and characters of the
+ set [-+./_];
+- the changes don't include unresolved merge conflict markers;
+- the changes don't introduce whitespace errors: trailing whitespace,
+ lines that include nothing but whitespace characters, and indented
+ lines where a SPC character is immediately followed by a TAB in the
+ line's initial indentation
+
+This is not enforced right now, but it'll happen eventually.
+
+** Committing changes by others
+
+If committing changes written by someone else, commit in their name,
+not yours. You can use 'git commit --author="AUTHOR"' to specify a
+change's author. When using Emacs VC to commit, the author can be
+specified in the log-edit buffer by adding an "Author: AUTHOR" header
+line (set 'log-edit-setup-add-author' non-nil to have this header line
+added automatically). Note that the validity checks described in the
+previous section are still applied, so you will have to correct any
+problems they uncover in the changes submitted by others.
+
+** git vs rename
+
+Git does not explicitly represent a file renaming; it uses a percent
+changed heuristic to deduce that a file was renamed. So if you are
+planning to make extensive changes to a file after renaming it (or
+moving it to another directory), you should:
+
+- Create a feature branch.
+
+- Commit the rename without any changes.
+
+- Make other changes.
+
+- Merge the feature branch to the master branch, instead of squashing
+ the commits into one. The commit message on this merge should
+ summarize the renames and all the changes.
+
+
+
+Adapted from CONTRIBUTE in Emacs source tree.
+
+This file is part of Eat and is not part of GNU Emacs.
+
+Eat 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.
+
+Eat 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+Local variables:
+mode: outline
+paragraph-separate: "[ ]*$"
+coding: utf-8
+end:
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000000..f288702d2f
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000..a0e2566934
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,39 @@
+# Makefile --- Build configuration
+
+# Copyright (C) 2022 Akib Azmain Turja.
+
+# This file is not part of GNU Emacs.
+
+# This file 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, 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.
+
+# For a full copy of the GNU General Public License
+# see <https://www.gnu.org/licenses/>.
+
+EMACS ?= emacs
+
+all: eat.elc terminfo check changelog
+
+terminfo: e/eat-mono e/eat-color eat-256color e/eat-truecolor
+
+check: eat.el
+ $(EMACS) -batch -l eat.el -l eat-tests.el \
+ -f ert-run-tests-batch-and-exit
+
+changelog:
+ ./make-changelog
+
+.PHONY: all terminfo check changelog
+
+eat.elc:
+ $(EMACS) -batch --eval '(byte-compile-file "eat.el")'
+
+e/eat-mono e/eat-color eat-256color e/eat-truecolor:
+ env TERMINFO=. tic -x eat.ti
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000000..125ea3a276
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,32 @@
+Eat NEWS -- History of user-visible changes
+
+Copyright (C) 2022 Akib Azmain Turja.
+See the end of the file for license conditions.
+
+This file is about changes in Eat.
+
+Note:
++++ indicates that Eat manual have been updated.
+--- means no change in the manuals is needed.
+When you add a new item, use the appropriate mark if you are sure it
+applies, and please also update docstrings as needed.
+
+
+----------------------------------------------------------------------
+Adapted from etc/NEWS in Emacs source tree.
+
+This file is part of Eat and is not part of GNU Emacs.
+
+Eat 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.
+
+Eat 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
diff --git a/README.org b/README.org
new file mode 100644
index 0000000000..d1604689eb
--- /dev/null
+++ b/README.org
@@ -0,0 +1,137 @@
+#+title: Eat: Emulate A Terminal
+
+Eat's name self-explainary, it stands for "Emulate A Terminal". Eat
+is a terminal emulator. It can run most (if not all) full-screen
+terminal programs, including Emacs.
+
+It is pretty fast, more than three times faster than Term, despite
+being implemented entirely in Emacs Lisp. So fast that you can
+comfortably run Emacs inside Eat, or even use your Emacs as a terminal
+multiplexer.
+
+It has many feature that other Emacs terminal emulator still don't
+have, for example complete mouse support.
+
+It flickers less than other Emacs terminal emulator, so you get more
+performance and a smooth experience.
+
+* Usage
+
+To start Eat, run =M-x eat=. Eat has three keybinding modes:
+
+- "emacs" mode: No special keybinding, except the following:
+
+ - =C-c C-s=: Switch to semi-char mode.
+ - =C-c C-j=: Switch to char mode.
+ - =C-c C-k=: Kill process.
+
+- "semi-char" mode: Most keys are bound to send the key to the
+ terminal, except the following keys: =C-\=, =C-c=, =C-x=, =C-g=,
+ =C-h=, =C-M-c=, =C-u=, =C-q=, =M-x=, =M-:=, =M-!=, =M-&=. The
+ following special keybinding are available:
+
+ - =C-q=: Send next key to the terminal.
+ - =C-y=: Like `yank', but send the text to the terminal.
+ - =M-y=: Like `yank-pop', but send the text to the terminal.
+ - =C-c C-k=: Kill process.
+
+- "char" mode: All supported keys are bound to send the key to the
+ terminal, except =C-M-m= or =M-RET=, which is bound to switch to
+ semi-char mode.
+
+If you like Eshell, then there is a good news for you. Eat integrates
+with Eshell. Eat has two global minor modes for Eshell:
+
+- ~eat-eshell-visual-command-mode~: Run visual commands with Eat
+ instead of Term.
+
+- ~eat-eshell-mode~: Run Eat inside Eshell. After enabling this, you
+ can run full-screen terminal programs directly in Eshell. You have
+ the above three keybinding modes here too, except that =C-c C-k= is
+ not special (i.e. not bound by Eat) in "emacs" mode and "line" mode.
+
+You can add any of these to ~eshell-first-time-mode-hook~ like the
+following:
+
+#+begin_src emacs-lisp
+;; For `eat-eshell-visual-command-mode'.
+(add-hook 'eshell-first-time-mode-hook
+ #'eat-eshell-visual-command-mode)
+
+;; For `eat-eshell-mode'.
+(add-hook 'eshell-first-time-mode-hook #'eat-eshell-mode)
+#+end_src
+
+* Installation
+
+Eat requires at Emacs 28.1 or above. Eat isn't available on any ELPA
+right now. So, you have to follow one of the following methods:
+
+** Quelpa
+
+#+begin_src emacs-lisp
+(quelpa (eat :fetcher git
+ :url "https://codeberg.org/akib/emacs-iwindow.git"
+ :files ("*.el" "dir"
+ "*.info" "*.texi"
+ "*.ti" ("e" "e/*")))
+#+end_src
+
+** Manual
+
+Clone the repository and put it in your ~load-path~.
+
+* Comparison With Other Terminal Emulators
+
+** Term
+
+Term is the Emacs built-in terminal emulator. Its terminal emulation
+is pretty good too. But it's slow. It is so slow that Eat can beat
+native-compiled Term even without byte-compilation, and when Eat is
+byte-compiled, Eat is more than three times fast. Also, Term
+flickers, just try to run =emacs -nw= in it. It doesn't support
+remote connections, for example over Tramp. However, it has "line"
+mode, which Eat still doesn't have. If you want line mode in a
+terminal, or use an old version of Emacs, you can use Term, but
+Coterm + Shell is probably a better choice in case your Emacs version
+is 26.1 or above.
+
+** Vterm
+
+Vterm is powered by a C library, libvterm. For this reason, it can
+process huge amount of text quickly. It is about 1.5 times faster
+than Eat (byte-compiled or native-compiled) (and about 2.75 faster
+then Eat without byte-compilation). But it doesn't have a char mode
+(however you can make a char mode spending some effort). And it too
+flickers like Term, so despite being much faster that Eat, it seems to
+be slow. If you need your terminal to handle huge bursts (megabytes)
+of data, you should Vterm.
+
+** Coterm + Shell
+
+Coterm adds terminal emulation to Shell mode. Although the terminal
+Coterm emulates is same as Term, it is much faster, about three times,
+just a bit slow than Eat. However, it too flickers like other
+terminals. Since it's an upgrade to Shell, you get all the features
+of Shell like "line" mode, completion using your favorite completion
+UI (Company, Corfu, etc), etc. Most of these features are available
+in Eat-Eshell-Mode as Eshell is similar to Shell, however it's not
+Shell mode. Recommended if you like Shell.
+
+* Acknowledgements
+
+This wouldn't have been possible if the following awesome softwares
+didn't exist:
+
+- [[https://gnu.org][GNU Operating System]]
+- [[https://st.suckless.org/][St]]
+- [[https://sw.kovidgoyal.net/kitty/][Kitty]]
+- [[https://invisible-island.net/xterm/][XTerm]]
+- [[https://www.gnu.org/software/linux-libre/][Linux-libre]]
+-
[[https://www.gnu.org/software/emacs/manual/html_node/emacs/Terminal-emulator.html][Term]]
+- [[https://repo.or.cz/emacs-coterm.git][Coterm]]
+-
[[https://www.gnu.org/software/emacs/manual/html_node/emacs/Interactive-Shell.html][Shell]]
+- [[https://github.com/akermu/emacs-libvterm][Vterm]]
+-
[[https://www.gnu.org/software/emacs/manual/html_node/eshell/index.html][Eshell]]
+- Numerous terminal programs
+- And obviously, [[https://www.gnu.org/software/emacs/][GNU Emacs]]
diff --git a/e/eat-256color b/e/eat-256color
new file mode 100644
index 0000000000..dda98bd68c
Binary files /dev/null and b/e/eat-256color differ
diff --git a/e/eat-color b/e/eat-color
new file mode 100644
index 0000000000..53ed6a6b11
Binary files /dev/null and b/e/eat-color differ
diff --git a/e/eat-mono b/e/eat-mono
new file mode 100644
index 0000000000..5d4b98b03a
Binary files /dev/null and b/e/eat-mono differ
diff --git a/e/eat-truecolor b/e/eat-truecolor
new file mode 100644
index 0000000000..c2c86b92d7
Binary files /dev/null and b/e/eat-truecolor differ
diff --git a/eat-tests.el b/eat-tests.el
new file mode 100644
index 0000000000..396504df03
--- /dev/null
+++ b/eat-tests.el
@@ -0,0 +1,6230 @@
+;;; eat-tests.el --- Tests for Eat -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022 Akib Azmain Turja.
+
+;; Author: Akib Azmain Turja <akib@disroot.org>
+;; Keywords: tests
+
+;; This file is not part of GNU Emacs.
+
+;; This file 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, 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.
+
+;; For a full copy of the GNU General Public License
+;; see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This file contains all tests for Eat. This includes tests for the
+;; terminal emulator itself.
+
+;;; Code:
+
+(require 'ert)
+(require 'eat)
+(require 'cl-lib)
+
+
+;;;; Helpers.
+
+(defun eat--tests-parse-text-properties (string)
+ "Parse and modify the text properties of STRING.
+
+Modify the text properties of STRING so that for two strings with
+identical contents and similar appearance `equal-including-properties'
+will return t."
+ (let ((pos 0))
+ (while (< pos (length string))
+ (let ((next-pos (or (next-single-property-change
+ pos 'face string)
+ (length string)))
+ (face (get-text-property pos 'face string)))
+ (set-text-properties
+ pos next-pos
+ `( :foreground ,(plist-get face :foreground)
+ :background ,(plist-get face :background)
+ :underline-type ,(plist-get (plist-get face :underline)
+ :style)
+ :underline-color ,(plist-get (plist-get face :underline)
+ :color)
+ :crossed ,(plist-get face :strike-through)
+ :intensity ,(let ((list (plist-get face :inherit)))
+ (cond ((member 'eat-term-faint list) 'faint)
+ ((member 'eat-term-bold list) 'bold)))
+ :italic ,(not (not (member 'eat-term-italic
+ (plist-get face :inherit))))
+ :blink ,(let ((list (plist-get face :inherit)))
+ (cond
+ ((member 'eat-term-slow-blink list) 'slow)
+ ((member 'eat-term-fast-blink list) 'fast)))
+ :font ,(let ((list (plist-get face :inherit)))
+ (cond ((member 'eat-term-font-0 list) 0)
+ ((member 'eat-term-font-1 list) 1)
+ ((member 'eat-term-font-2 list) 2)
+ ((member 'eat-term-font-3 list) 3)
+ ((member 'eat-term-font-4 list) 4)
+ ((member 'eat-term-font-5 list) 5)
+ ((member 'eat-term-font-6 list) 6)
+ ((member 'eat-term-font-7 list) 7)
+ ((member 'eat-term-font-8 list) 8)
+ ((member 'eat-term-font-9 list) 9)
+ (t 0))))
+ string)
+ (setq pos next-pos)))
+ string))
+
+(defun eat--tests-sanitize-expected-text (string)
+ "Sanitize the text properties of expected string STRING."
+ (let ((pos 0))
+ (while (< pos (length string))
+ (let ((next-pos (or (next-property-change pos string)
+ (length string))))
+ (set-text-properties
+ pos next-pos
+ (mapcan
+ (lambda (prop)
+ (list (car prop)
+ (or (get-text-property pos (car prop) string)
+ (cdr prop))))
+ '((:foreground . nil)
+ (:background . nil)
+ (:underline-type . nil)
+ (:underline-color . nil)
+ (:crossed . nil)
+ (:intensity . nil)
+ (:italic . nil)
+ (:blink . nil)
+ (:font . 0)))
+ string)
+ (setq pos next-pos)))
+ string))
+
+(defun eat--tests-compare-lines (actual expected)
+ "Compare ACTUAL and EXPECTED."
+ (let ((a (eat--tests-parse-text-properties actual))
+ (b (eat--tests-sanitize-expected-text expected)))
+ (cl-flet
+ ((visually-equal (a b)
+ (if (> emacs-major-version 28)
+ (should (equal-including-properties a b))
+ ;; On Emacs versions less than 29,
+ ;; `equal-including-properties' returns t only if the
+ ;; properties of a and b are `eq', so we compare the
+ ;; strings ourselves.
+ (and
+ (should (string= a b))
+ (cl-every
+ (lambda (i)
+ (cl-flet ((plist-to-alist (plist)
+ (let ((alist nil))
+ (while plist
+ (push (cons (pop plist)
+ (pop plist))
+ alist))
+ (sort
+ alist
+ (lambda (a b)
+ (string<
+ (symbol-name (car a))
+ (symbol-name (car b))))))))
+ (should
+ (equal
+ (plist-to-alist (text-properties-at i a))
+ (plist-to-alist (text-properties-at i b))))))
+ (number-sequence 0 (1- (length a))))))))
+ (cond ((= (length a) (length b))
+ (visually-equal a b))
+ ((< (length a) (length b))
+ (and (visually-equal
+ a (substring b 0 (length a)))
+ (let ((str (substring b (length a))))
+ (and (string-blank-p str)
+ (visually-equal
+ str (eat--tests-parse-text-properties
+ (substring-no-properties str)))))))
+ ((> (length a) (length b))
+ (and (visually-equal
+ (substring a 0 (length b)) b)
+ (let ((str (substring a (length b))))
+ (and (string-blank-p str)
+ (visually-equal
+ str (eat--tests-parse-text-properties
+ (substring-no-properties str)))))))))))
+
+(defun eat--tests-compare-scrollback (terminal lines)
+ "Compare TERMINAL's scrollback buffer with LINES."
+ (let ((scrollback
+ (nbutlast
+ (split-string
+ (buffer-substring
+ (eat-term-beginning terminal)
+ (eat-term-display-beginning terminal))
+ "\n" nil nil))))
+ (and (should
+ (or (= (eat-term-display-beginning terminal)
+ (eat-term-beginning terminal))
+ (= (char-before (eat-term-display-beginning terminal))
+ ?\n)))
+ (should (= (length scrollback) (length lines)))
+ (cl-every (lambda (pair)
+ (eat--tests-compare-lines
+ (car pair) (cdr pair)))
+ (cl-mapcar #'cons scrollback lines)))))
+
+(defun eat--tests-compare-display (terminal lines)
+ "Compare TERMINAL's scrollback buffer with LINES."
+ (let ((display
+ (split-string
+ (buffer-substring
+ (eat-term-display-beginning terminal)
+ (eat-term-end terminal))
+ "\n" nil nil)))
+ (and (should (<= (length display) (cdr (eat-term-size terminal))))
+ (cl-every
+ (lambda (i)
+ (let ((actual (or (nth i display) ""))
+ (expected (or (nth i lines) "")))
+ (and (<= (length actual) (car (eat-term-size terminal)))
+ (eat--tests-compare-lines actual expected))))
+ (number-sequence 0 (1- (cdr (eat-term-size terminal))))))))
+
+(defun eat--tests-compare-cursor-pos (terminal cursor-pos)
+ "Compare TERMINAL's cursor position with CURSOR-POS.
+
+CURSOR-POS should be a cons cell of form (Y . X)."
+ (let* ((prev-lines (split-string
+ (buffer-substring
+ (eat-term-display-beginning terminal)
+ (eat-term-display-cursor terminal))
+ "\n" nil nil))
+ (y (length prev-lines))
+ (x (1+ (length (car (last prev-lines))))))
+ (should (= (car cursor-pos) y))
+ (should (= (cdr cursor-pos) x))))
+
+(defun eat--tests-add-properties (string &rest intervals)
+ "Add properties to STRING according to INTERVALS.
+
+INTERVALS is a list. Each element of it is a list of form
+\((BEGIN . END) PLIST...). For each element in INTERVALS, add PLIST
+to string from BEGIN to END."
+ (dolist (interval intervals)
+ (set-text-properties (caar interval) (cdar interval)
+ (cdr interval) string))
+ string)
+
+(defmacro eat--tests-with-term (spec &rest body)
+ "Make a temporary terminal with SPEC and run BODY with it.
+
+SPEC is a form that should evaluate to a plist. The plist can have
+any of the following properties:
+
+ `:width' Width of terminal. Defaults to 20.
+ `:height' Height of terminal. Defaults to 6.
+
+The following functions are available within BODY:
+
+(terminal)
+ Return the terminal.
+
+(output &rest ARGS)
+ Output ARGS to the terminal, where ARGS is a list of string.
+
+(input-event EVENT &optional REF-POS N)
+ Input EVENT to the terminal. REF-POS is the starting position of
+ the terminal, a mouse position list. N is how times to input EVENT.
+
+(input)
+ Return all unread input.
+
+(should-term SCROLLBACK DISPLAY CURSOR)
+ Match the terminal with SCROLLBACK, DISPLAY and CURSOR. SCROLLBACK
+ is expected content of scrollback region. DISPLAY is the expected
+ display. CURSOR is the expected cursor position, as a cons (X . Y),
+ where X and Y are one-based.
+
+ Both SCROLLBACK and DISPLAY are list of strings. They matched with
+ the terminal for visual equivalence, not literal equivaence (i.e,
+ properties are also compared, and \"foo\" and \"foo \" are assumed
+ to be equivalent).
+
+(add-props STRING &rest INTERVALS)
+ Add text properties to STRING as specified in INTERVALS. Each
+ argument in INTERVALS is of form ((BEGIN . END) PROPERTY VALUE
+ PROPERTY VALUE...). Here is all PROPERTY is applied on STRING from
+ BEGIN to END. PROPERTY should one of `:foreground', `:background'
+ `:underline-type', `:underline-color', `:crossed', `:intensity',
+ `:italic', `:blink' and `:font'. Any other properties are also
+ applied but ignored by `should-term'."
+ (declare (indent 1))
+ (let ((term (make-symbol "term"))
+ (input (make-symbol "input")))
+ `(with-temp-buffer
+ (let ((,term (eat-term-make (current-buffer) (point)))
+ (,input ""))
+ (unwind-protect
+ (progn
+ (cl-destructuring-bind
+ (&key (width 20) (height 6)) ,spec
+ (eat-term-resize ,term width height))
+ (setf (eat-term-input-function ,term)
+ (lambda (_ str)
+ (setq ,input (concat ,input str))))
+ (cl-labels
+ ((terminal ()
+ ,term)
+ (output (&rest args)
+ (dolist (str args)
+ (eat-term-process-output ,term str))
+ (eat-term-redisplay ,term))
+ (input-event (event &optional ref-pos n)
+ (eat-term-input-event ,term n event ref-pos))
+ (input ()
+ (prog1 ,input
+ (setq ,input "")))
+ (should-term (&key scrollback display cursor)
+ (eat--tests-compare-scrollback ,term scrollback)
+ (eat--tests-compare-display ,term display)
+ (eat--tests-compare-cursor-pos ,term cursor))
+ (add-props (str &rest intervals)
+ (apply #'eat--tests-add-properties
+ str intervals)))
+ ,@body))
+ (eat-term-delete ,term))))))
+
+
+;;;; Tests.
+
+;;;;; Text Writing and Insertion Tests.
+
+(ert-deftest eat-test-plain-text ()
+ "Test plain text handling.
+
+Send only plain text (i.e. no control sequences, not even newline) and
+compare the output with the expected output. Don't do test automatic
+margin."
+ (eat--tests-with-term '()
+ (output "some test string")
+ (should-term :display '("some test string ")
+ :cursor '(1 . 17))))
+
+(ert-deftest eat-test-auto-margin ()
+ "Test automatic margin and toggling it."
+ (eat--tests-with-term '()
+ (should-term :cursor '(1 . 1))
+ ;; Default: Automatic margin enabled.
+ (output "some test string... and some more")
+ (should-term :display '("some test string..."
+ "and some more")
+ :cursor '(2 . 14))
+ ;; Automatic margin disabled.
+ (output "\n\e[?7lsome test string... and some more")
+ (should-term :display '("some test string..."
+ "and some more"
+ "some test string...e")
+ :cursor '(3 . 20))
+ ;; Automatic margin enabled.
+ (output "\n\e[?7hsome test string... and some more")
+ (should-term :display '("some test string..."
+ "and some more"
+ "some test string...e"
+ "some test string..."
+ "and some more")
+ :cursor '(5 . 14))))
+
+(ert-deftest eat-test-insert-mode ()
+ "Test automatic margin and toggling it."
+ (eat--tests-with-term '()
+ ;; Default: Insert mode disabled.
+ (output "a\bb")
+ (should-term :display '("b")
+ :cursor '(1 . 2))
+ ;; Insert mode enabled.
+ (output "\e[4hb\ba")
+ (should-term :display '("bab")
+ :cursor '(1 . 3))
+ ;; Insert mode disabled.
+ (output "\e[4lc\by")
+ (should-term :display '("bay")
+ :cursor '(1 . 4))))
+
+
+;;;;; Cursor Motion Tests.
+
+(ert-deftest eat-test-character-tabulation ()
+ "Test character tabulation control function."
+ (eat--tests-with-term '()
+ (output "\t")
+ (should-term :cursor '(1 . 9))
+ (output "\t")
+ (should-term :cursor '(1 . 17))
+ (output "\t")
+ (should-term :cursor '(1 . 20))
+ (output "\n ")
+ (should-term :cursor '(2 . 2))
+ (output "\t")
+ (should-term :cursor '(2 . 9))))
+
+(ert-deftest eat-test-cursor-backward-tabulation ()
+ "Test cursor backward tabulation control function."
+ (eat--tests-with-term '()
+ (output "\t")
+ (should-term :cursor '(1 . 9))
+ (output "\t")
+ (should-term :cursor '(1 . 17))
+ (output "\t")
+ (should-term :cursor '(1 . 20))
+ (output "\n ")
+ (should-term :cursor '(2 . 2))
+ (output "\t")
+ (should-term :cursor '(2 . 9))))
+
+(ert-deftest eat-test-line-tabulation ()
+ "Test line tabulation control function."
+ (eat--tests-with-term '()
+ (output "\v")
+ (should-term :cursor '(2 . 1))
+ (output " ")
+ (should-term :cursor '(2 . 3))
+ (output "\v")
+ (should-term :cursor '(3 . 3))
+ (output "\v\v\v")
+ (should-term :cursor '(6 . 3))
+ (output "\v")
+ (should-term :scrollback '("")
+ :cursor '(6 . 3))))
+
+(ert-deftest eat-test-form-feed ()
+ "Test form feed."
+ (eat--tests-with-term '()
+ (output "\f")
+ (should-term :cursor '(2 . 1))
+ (output " ")
+ (should-term :cursor '(2 . 3))
+ (output "\f")
+ (should-term :cursor '(3 . 3))
+ (output "\f\f\f")
+ (should-term :cursor '(6 . 3))
+ (output "\f")
+ (should-term :scrollback '("")
+ :cursor '(6 . 3))))
+
+(ert-deftest eat-test-line-feed ()
+ "Test line feed control function."
+ (eat--tests-with-term '()
+ (output "\n")
+ (should-term :cursor '(2 . 1))
+ (output " \n")
+ (should-term :cursor '(3 . 1))
+ (output "\eE")
+ (should-term :cursor '(4 . 1))
+ (output " \eE")
+ (should-term :cursor '(5 . 1))))
+
+(ert-deftest eat-test-index ()
+ "Test index control function."
+ (eat--tests-with-term '()
+ (output "\eD")
+ (should-term :cursor '(2 . 1))
+ (output " \eD")
+ (should-term :cursor '(3 . 5))
+ (output "\eD")
+ (should-term :cursor '(4 . 5))
+ (output " \eD")
+ (should-term :cursor '(5 . 9))))
+
+(ert-deftest eat-test-reverse-index ()
+ "Test reverse index control function.
+
+Use newlines to move to an initial position from where the control
+function is to be invoked."
+ (eat--tests-with-term '()
+ (output "\n\n")
+ (should-term :cursor '(3 . 1))
+ (output "\eM")
+ (should-term :cursor '(2 . 1))
+ (output " \eM")
+ (should-term :cursor '(1 . 5))))
+
+(ert-deftest eat-test-backspace ()
+ "Test backspace control function.
+
+Use spaces to move to an initial position from where the control
+function is to be invoked."
+ (eat--tests-with-term '()
+ (output " ")
+ (should-term :cursor '(1 . 2))
+ (output "\b")
+ (should-term :cursor '(1 . 1))))
+
+(ert-deftest eat-test-carriage-return ()
+ "Test carriage return control function.
+
+Use spaces to move to an initial position from where the control
+function is to be invoked."
+ (eat--tests-with-term '()
+ (output "\r")
+ (should-term :cursor '(1 . 1))
+ (output " ")
+ (should-term :cursor '(1 . 5))
+ (output "\r")
+ (should-term :cursor '(1 . 1))))
+
+(ert-deftest eat-test-cursor-right ()
+ "Test cursor right control function."
+ (eat--tests-with-term '()
+ (output "\e[C")
+ (should-term :cursor '(1 . 2))
+ (output "\e[0C")
+ (should-term :cursor '(1 . 3))
+ (output "\e[5C")
+ (should-term :cursor '(1 . 8))
+ (output "\e[20C")
+ (should-term :cursor '(1 . 20))))
+
+(ert-deftest eat-test-cursor-left ()
+ "Test cursor up control function.
+
+Use spaces to move to an initial position from where the control
+function is to be invoked."
+ (eat--tests-with-term '()
+ (output " ")
+ (should-term :cursor '(1 . 17))
+ (output "\e[D")
+ (should-term :cursor '(1 . 16))
+ (output "\e[0D")
+ (should-term :cursor '(1 . 15))
+ (output "\e[7D")
+ (should-term :cursor '(1 . 8))
+ (output "\e[10D")
+ (should-term :cursor '(1 . 1))))
+
+(ert-deftest eat-test-cursor-down ()
+ "Test cursor down control function."
+ (eat--tests-with-term '()
+ (output "\e[B")
+ (should-term :cursor '(2 . 1))
+ (output "\e[0B")
+ (should-term :cursor '(3 . 1))
+ (output " ")
+ (should-term :cursor '(3 . 5))
+ (output "\e[6B")
+ (should-term :cursor '(6 . 5))))
+
+(ert-deftest eat-test-cursor-up ()
+ "Test cursor up control function.
+
+Use spaces and newlines to move to an initial position from where the
+control function is to be invoked."
+ (eat--tests-with-term '()
+ (output "\n\n\n\n\n")
+ (should-term :cursor '(6 . 1))
+ (output "\e[A")
+ (should-term :cursor '(5 . 1))
+ (output "\e[0A")
+ (should-term :cursor '(4 . 1))
+ (output " ")
+ (should-term :cursor '(4 . 5))
+ (output "\e[2A")
+ (should-term :cursor '(2 . 5))
+ (output "\e[4A")
+ (should-term :cursor '(1 . 5))))
+
+(ert-deftest eat-test-cursor-next-line ()
+ "Test cursor next line control function."
+ (eat--tests-with-term '(:height 10)
+ (output "\e[F")
+ (should-term :cursor '(2 . 1))
+ (output "\e[0F")
+ (should-term :cursor '(3 . 1))
+ (output "\e[2F")
+ (should-term :cursor '(5 . 1))
+ (output " \e[F")
+ (should-term :cursor '(6 . 1))
+ (output " \e[0F")
+ (should-term :cursor '(7 . 1))
+ (output " \e[3F")
+ (should-term :cursor '(10 . 1))
+ (output " \e[F")
+ (should-term :cursor '(10 . 1))))
+
+(ert-deftest eat-test-cursor-previous-line ()
+ "Test cursor previous line control function.
+
+Use newlines to move to an initial position from where the control
+function is to be invoked."
+ (eat--tests-with-term '(:height 10)
+ (output "\n\n\n\n\n\n\n\n\n")
+ (should-term :cursor '(10 . 1))
+ (output "\e[E")
+ (should-term :cursor '(9 . 1))
+ (output "\e[0E")
+ (should-term :cursor '(8 . 1))
+ (output "\e[2E")
+ (should-term :cursor '(6 . 1))
+ (output " \e[E")
+ (should-term :cursor '(5 . 1))
+ (output " \e[0E")
+ (should-term :cursor '(4 . 1))
+ (output " \e[3E")
+ (should-term :cursor '(1 . 1))
+ (output " \e[E")
+ (should-term :cursor '(1 . 1))))
+
+(ert-deftest eat-test-cursor-character-absolute ()
+ "Test cursor character absolute control function."
+ (eat--tests-with-term '()
+ (output "\e[5G")
+ (should-term :cursor '(1 . 5))
+ (output "\e[0G")
+ (should-term :cursor '(1 . 1))
+ (output "\e[15G")
+ (should-term :cursor '(1 . 15))
+ (output "\e[G")
+ (should-term :cursor '(1 . 1))))
+
+(ert-deftest eat-test-character-position-absolute ()
+ "Test character position absolute control function."
+ (eat--tests-with-term '()
+ (output "\e[5`")
+ (should-term :cursor '(1 . 5))
+ (output "\e[0`")
+ (should-term :cursor '(1 . 1))
+ (output "\e[15`")
+ (should-term :cursor '(1 . 15))
+ (output "\e[`")
+ (should-term :cursor '(1 . 1))))
+
+(ert-deftest eat-test-line-position-absolute ()
+ "Test line position absolute control function."
+ (eat--tests-with-term '()
+ (output "\e[2d")
+ (should-term :cursor '(2 . 1))
+ (output "\e[0d")
+ (should-term :cursor '(1 . 1))
+ (output "\e[5d")
+ (should-term :cursor '(5 . 1))
+ (output "\e[d")
+ (should-term :cursor '(1 . 1))))
+
+(ert-deftest eat-test-cursor-position ()
+ "Test cursor position control function."
+ (eat--tests-with-term '()
+ (output "\e[2;2H")
+ (should-term :cursor '(2 . 2))
+ (output "\e[;5H")
+ (should-term :cursor '(1 . 5))
+ (output "\e[4;H")
+ (should-term :cursor '(4 . 1))
+ (output "\e[7;H")
+ (should-term :cursor '(6 . 1))
+ (output "\e[0;0H")
+ (should-term :cursor '(1 . 1))
+ (output "\e[;30H")
+ (should-term :cursor '(1 . 20))
+ (output "\e[10;25H")
+ (should-term :cursor '(6 . 20))
+ (output "\e[H")
+ (should-term :cursor '(1 . 1))))
+
+(ert-deftest eat-test-character-and-line-position ()
+ "Test character and line position control function."
+ (eat--tests-with-term '()
+ (output "\e[2;2f")
+ (should-term :cursor '(2 . 2))
+ (output "\e[;5f")
+ (should-term :cursor '(1 . 5))
+ (output "\e[4;f")
+ (should-term :cursor '(4 . 1))
+ (output "\e[7;f")
+ (should-term :cursor '(6 . 1))
+ (output "\e[0;0f")
+ (should-term :cursor '(1 . 1))
+ (output "\e[;30f")
+ (should-term :cursor '(1 . 20))
+ (output "\e[10;25f")
+ (should-term :cursor '(6 . 20))
+ (output "\e[f")
+ (should-term :cursor '(1 . 1))))
+
+
+;;;;; Scrolling Tests.
+
+(ert-deftest eat-test-scroll-up ()
+ "Test scroll up control function."
+ (eat--tests-with-term '()
+ (output "some test string...\nmore, more...\n"
+ "more, more, more...\nand some more")
+ (should-term :display '("some test string..."
+ "more, more..."
+ "more, more, more..."
+ "and some more")
+ :cursor '(4 . 14))
+ (output "\e[S")
+ (should-term :scrollback '("some test string...")
+ :display '("more, more..."
+ "more, more, more..."
+ "and some more")
+ :cursor '(4 . 14))
+ (output "\e[0S")
+ (should-term :scrollback '("some test string...")
+ :display '("more, more..."
+ "more, more, more..."
+ "and some more")
+ :cursor '(4 . 14))
+ (output "\e[2S")
+ (should-term :scrollback '("some test string..."
+ "more, more..."
+ "more, more, more...")
+ :display '("and some more")
+ :cursor '(4 . 14))
+ (output "\nnew line 1\nnew line 2\nnew line 3\n"
+ "new line 4\nnew line 5\nnew line 6\e[2;4r")
+ (should-term :scrollback '("some test string..."
+ "more, more..."
+ "more, more, more..."
+ "and some more"
+ ""
+ ""
+ "")
+ :display '("new line 1"
+ "new line 2"
+ "new line 3"
+ "new line 4"
+ "new line 5"
+ "new line 6")
+ :cursor '(1 . 1))
+ (output "\e[S")
+ (should-term :scrollback '("some test string..."
+ "more, more..."
+ "more, more, more..."
+ "and some more"
+ ""
+ ""
+ "")
+ :display '("new line 1"
+ "new line 3"
+ "new line 4"
+ ""
+ "new line 5"
+ "new line 6")
+ :cursor '(1 . 1))
+ (output "\e[0S")
+ (should-term :scrollback '("some test string..."
+ "more, more..."
+ "more, more, more..."
+ "and some more"
+ ""
+ ""
+ "")
+ :display '("new line 1"
+ "new line 3"
+ "new line 4"
+ ""
+ "new line 5"
+ "new line 6")
+ :cursor '(1 . 1))
+ (output "\e[2S")
+ (should-term :scrollback '("some test string..."
+ "more, more..."
+ "more, more, more..."
+ "and some more"
+ ""
+ ""
+ "")
+ :display '("new line 1"
+ ""
+ ""
+ ""
+ "new line 5"
+ "new line 6")
+ :cursor '(1 . 1))))
+
+(ert-deftest eat-test-scroll-down ()
+ "Test scroll down control function."
+ (eat--tests-with-term '()
+ (output "some test string...\nmore, more...\n"
+ "more, more, more...\nand some more")
+ (should-term :display '("some test string..."
+ "more, more..."
+ "more, more, more..."
+ "and some more")
+ :cursor '(4 . 14))
+ (output "\e[T")
+ (should-term :display '(""
+ "some test string..."
+ "more, more..."
+ "more, more, more..."
+ "and some more")
+ :cursor '(4 . 14))
+ (output "\e[0T")
+ (should-term :display '(""
+ "some test string..."
+ "more, more..."
+ "more, more, more..."
+ "and some more")
+ :cursor '(4 . 14))
+ (output "\e[2T")
+ (should-term :display '(""
+ ""
+ ""
+ "some test string..."
+ "more, more..."
+ "more, more, more...")
+ :cursor '(4 . 14))
+ (output "\n\n\nnew line 1\nnew line 2\nnew line 3\n"
+ "new line 4\nnew line 5\nnew line 6\e[2;5r")
+ (should-term :scrollback '(""
+ ""
+ ""
+ "some test string..."
+ "more, more..."
+ "more, more, more...")
+ :display '("new line 1"
+ "new line 2"
+ "new line 3"
+ "new line 4"
+ "new line 5"
+ "new line 6")
+ :cursor '(1 . 1))
+ (output "\e[T")
+ (should-term :scrollback '(""
+ ""
+ ""
+ "some test string..."
+ "more, more..."
+ "more, more, more...")
+ :display '("new line 1"
+ ""
+ "new line 2"
+ "new line 3"
+ "new line 4"
+ "new line 6")
+ :cursor '(1 . 1))
+ (output "\e[0T")
+ (should-term :scrollback '(""
+ ""
+ ""
+ "some test string..."
+ "more, more..."
+ "more, more, more...")
+ :display '("new line 1"
+ ""
+ "new line 2"
+ "new line 3"
+ "new line 4"
+ "new line 6")
+ :cursor '(1 . 1))
+ (output "\e[2T")
+ (should-term :scrollback '(""
+ ""
+ ""
+ "some test string..."
+ "more, more..."
+ "more, more, more...")
+ :display '("new line 1"
+ ""
+ ""
+ ""
+ "new line 2"
+ "new line 6")
+ :cursor '(1 . 1))))
+
+(ert-deftest eat-test-auto-scrolling ()
+ "Test automatic scrolling when cursor reaches end of display.
+
+Test with every control functions and text combination that trigger
+automatic scrolling as a side effect."
+ (eat--tests-with-term '()
+ ;; Test with newlines.
+ (output "some test string...\n\n\n\n\n\nand some more")
+ (should-term :scrollback '("some test string...")
+ :display '(""
+ ""
+ ""
+ ""
+ ""
+ "and some more")
+ :cursor '(6 . 14))
+ ;; Test with automatic margin.
+ (output "...more, more, stop.")
+ (should-term :scrollback '("some test string..."
+ "")
+ :display '(""
+ ""
+ ""
+ ""
+ "and some more...more"
+ ", more, stop.")
+ :cursor '(6 . 14))
+ ;; Test with reverse index.
+ (output "\eM\eM\eM\eM\eM\eM")
+ (should-term :scrollback '("some test string..."
+ "")
+ :display '(""
+ ""
+ ""
+ ""
+ ""
+ "and some more...more")
+ :cursor '(1 . 14))
+ ;; Test with newlines and scroll region.
+ (output "\e[2;5rline 1\nline 2\nline 3\nline 4\nline 5")
+ (should-term :scrollback '("some test string..."
+ "")
+ :display '("line 1"
+ "line 2"
+ "line 3"
+ "line 4"
+ "line 5"
+ "and some more...more")
+ :cursor '(5 . 7))
+ (output "\n")
+ (should-term :scrollback '("some test string..."
+ "")
+ :display '("line 1"
+ "line 3"
+ "line 4"
+ "line 5"
+ ""
+ "and some more...more")
+ :cursor '(5 . 1))
+ ;; Test with automatic margin and scroll region.
+ (output "...more content, more, stop.")
+ (should-term :scrollback '("some test string..."
+ "")
+ :display '("line 1"
+ "line 4"
+ "line 5"
+ "...more content, mor"
+ "e, stop."
+ "and some more...more")
+ :cursor '(5 . 9))
+ ;; Test with reverse index and scroll region.
+ (output "\eM\eM\eM\eM\eM")
+ (should-term :scrollback '("some test string..."
+ "")
+ :display '("line 1"
+ ""
+ ""
+ "line 4"
+ "line 5"
+ "and some more...more")
+ :cursor '(2 . 9))))
+
+
+;;;;; SGR Tests.
+
+;; Beware, this section is a nightware of code repetition.
+
+(ert-deftest eat-test-sgr-foreground ()
+ "Test SGR foreground color. Test 256 colors and 24-bit colors."
+ (eat--tests-with-term '()
+ ;; ANSI colors.
+ (output "\e[31mred\n")
+ (should-term
+ :display `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t))))
+ :cursor '(2 . 1))
+ (output "\e[32mgreen\n")
+ (should-term
+ :display `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t))))
+ :cursor '(3 . 1))
+ (output "\e[37mwhite\n")
+ (should-term
+ :display `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t))))
+ :cursor '(4 . 1))
+ (output "\e[30mblack\n")
+ (should-term
+ :display `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t))))
+ :cursor '(5 . 1))
+ ;; ANSI bright colors.
+ (output "\e[91mbright red\n")
+ (should-term
+ :display `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[92mbright green\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t))))
+ :display `(,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[97mbright white\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t))))
+ :display `(,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[90mbright black\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t))))
+ :display `(,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t))))
+ :cursor '(6 . 1))
+ ;; ANSI colors using 256-color sequence.
+ (output "\e[38;5;1mred\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t))))
+ :display `(,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[38;5;2mgreen\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t))))
+ :display `(,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[38;5;7mwhite\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t))))
+ :display `(,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[38;5;0mblack\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t))))
+ :display `(,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t))))
+ :cursor '(6 . 1))
+ ;; ANSI bright colors using 256-color sequence.
+ (output "\e[38;5;9mbright red\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t))))
+ :display `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[38;5;10mbright green\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t))))
+ :display `(,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[38;5;15mbright white\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t))))
+ :display `(,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[38;5;8mbright black\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t))))
+ :display `(,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t))))
+ :cursor '(6 . 1))
+ ;; 256-color.
+ (output "\e[38;5;119mcolor-119\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t))))
+ :display `(,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "color-119"
+ `((0 . 9)
+ :foreground ,(face-foreground
+ 'eat-term-color-119 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[38;5;255mcolor-255\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t))))
+ :display `(,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "color-119"
+ `((0 . 9)
+ :foreground ,(face-foreground
+ 'eat-term-color-119 nil t)))
+ ,(add-props
+ "color-255"
+ `((0 . 9)
+ :foreground ,(face-foreground
+ 'eat-term-color-255 nil t))))
+ :cursor '(6 . 1))
+ ;; 24-bit color (truecolor).
+ (output "\e[38;2;34;139;34mforest green\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t))))
+ :display `(,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "color-119"
+ `((0 . 9)
+ :foreground ,(face-foreground
+ 'eat-term-color-119 nil t)))
+ ,(add-props
+ "color-255"
+ `((0 . 9)
+ :foreground ,(face-foreground
+ 'eat-term-color-255 nil t)))
+ ,(add-props
+ "forest green"
+ '((0 . 12)
+ :foreground "#228b22")))
+ :cursor '(6 . 1))
+ (output "\e[38;2;160;32;240mpurple\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t))))
+ :display `(,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "color-119"
+ `((0 . 9)
+ :foreground ,(face-foreground
+ 'eat-term-color-119 nil t)))
+ ,(add-props
+ "color-255"
+ `((0 . 9)
+ :foreground ,(face-foreground
+ 'eat-term-color-255 nil t)))
+ ,(add-props
+ "forest green"
+ '((0 . 12)
+ :foreground "#228b22"))
+ ,(add-props
+ "purple"
+ '((0 . 6)
+ :foreground "#a020f0")))
+ :cursor '(6 . 1))
+ (output "\e[39mdefault\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :foreground ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-8 nil t))))
+ :display `(,(add-props
+ "color-119"
+ `((0 . 9)
+ :foreground ,(face-foreground
+ 'eat-term-color-119 nil t)))
+ ,(add-props
+ "color-255"
+ `((0 . 9)
+ :foreground ,(face-foreground
+ 'eat-term-color-255 nil t)))
+ ,(add-props
+ "forest green"
+ '((0 . 12)
+ :foreground "#228b22"))
+ ,(add-props
+ "purple"
+ '((0 . 6)
+ :foreground "#a020f0"))
+ "default")
+ :cursor '(6 . 1))))
+
+(ert-deftest eat-test-sgr-background ()
+ "Test SGR background color. Test 256 colors and 24-bit colors."
+ (eat--tests-with-term '()
+ ;; ANSI colors.
+ (output "\e[41mred\n")
+ (should-term
+ :display `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t))))
+ :cursor '(2 . 1))
+ (output "\e[42mgreen\n")
+ (should-term
+ :display `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t))))
+ :cursor '(3 . 1))
+ (output "\e[47mwhite\n")
+ (should-term
+ :display `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t))))
+ :cursor '(4 . 1))
+ (output "\e[40mblack\n")
+ (should-term
+ :display `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t))))
+ :cursor '(5 . 1))
+ ;; ANSI bright colors.
+ (output "\e[101mbright red\n")
+ (should-term
+ :display `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[102mbright green\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t))))
+ :display `(,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[107mbright white\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t))))
+ :display `(,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[100mbright black\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t))))
+ :display `(,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t))))
+ :cursor '(6 . 1))
+ ;; ANSI colors using 256-color sequence.
+ (output "\e[48;5;1mred\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t))))
+ :display `(,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[48;5;2mgreen\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t))))
+ :display `(,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[48;5;7mwhite\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t))))
+ :display `(,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[48;5;0mblack\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t))))
+ :display `(,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t))))
+ :cursor '(6 . 1))
+ ;; ANSI bright colors using 256-color sequence.
+ (output "\e[48;5;9mbright red\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t))))
+ :display `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[48;5;10mbright green\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t))))
+ :display `(,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[48;5;15mbright white\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t))))
+ :display `(,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[48;5;8mbright black\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t))))
+ :display `(,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t))))
+ :cursor '(6 . 1))
+ ;; 256-color.
+ (output "\e[48;5;119mcolor-119\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t))))
+ :display `(,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "color-119"
+ `((0 . 9)
+ :background ,(face-foreground
+ 'eat-term-color-119 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[48;5;255mcolor-255\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t))))
+ :display `(,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "color-119"
+ `((0 . 9)
+ :background ,(face-foreground
+ 'eat-term-color-119 nil t)))
+ ,(add-props
+ "color-255"
+ `((0 . 9)
+ :background ,(face-foreground
+ 'eat-term-color-255 nil t))))
+ :cursor '(6 . 1))
+ ;; 24-bit color (truecolor).
+ (output "\e[48;2;34;139;34mforest green\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t))))
+ :display `(,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "color-119"
+ `((0 . 9)
+ :background ,(face-foreground
+ 'eat-term-color-119 nil t)))
+ ,(add-props
+ "color-255"
+ `((0 . 9)
+ :background ,(face-foreground
+ 'eat-term-color-255 nil t)))
+ ,(add-props
+ "forest green"
+ '((0 . 12)
+ :background "#228b22")))
+ :cursor '(6 . 1))
+ (output "\e[48;2;160;32;240mpurple\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t))))
+ :display `(,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "color-119"
+ `((0 . 9)
+ :background ,(face-foreground
+ 'eat-term-color-119 nil t)))
+ ,(add-props
+ "color-255"
+ `((0 . 9)
+ :background ,(face-foreground
+ 'eat-term-color-255 nil t)))
+ ,(add-props
+ "forest green"
+ '((0 . 12)
+ :background "#228b22"))
+ ,(add-props
+ "purple"
+ '((0 . 6)
+ :background "#a020f0")))
+ :cursor '(6 . 1))
+ (output "\e[49mdefault\n")
+ (should-term
+ :scrollback `(,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "white"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ "black"
+ `((0 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t)))
+ ,(add-props
+ "bright red"
+ `((0 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-9 nil t)))
+ ,(add-props
+ "bright green"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-10 nil t)))
+ ,(add-props
+ "bright white"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t)))
+ ,(add-props
+ "bright black"
+ `((0 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t))))
+ :display `(,(add-props
+ "color-119"
+ `((0 . 9)
+ :background ,(face-foreground
+ 'eat-term-color-119 nil t)))
+ ,(add-props
+ "color-255"
+ `((0 . 9)
+ :background ,(face-foreground
+ 'eat-term-color-255 nil t)))
+ ,(add-props
+ "forest green"
+ '((0 . 12)
+ :background "#228b22"))
+ ,(add-props
+ "purple"
+ '((0 . 6)
+ :background "#a020f0"))
+ "default")
+ :cursor '(6 . 1))))
+
+(ert-deftest eat-test-sgr-intensity ()
+ "Test SGR intensity attributes (both bold and faint)."
+ (eat--tests-with-term '()
+ (output "\e[1mbold\n")
+ (should-term
+ :display `(,(add-props "bold" `((0 . 4)
+ :intensity bold)))
+ :cursor '(2 . 1))
+ (output "\e[2mfaint\n")
+ (should-term
+ :display `(,(add-props "bold" `((0 . 4)
+ :intensity bold))
+ ,(add-props "faint" `((0 . 5)
+ :intensity faint)))
+ :cursor '(3 . 1))
+ (output "\e[22mnormal\n")
+ (should-term
+ :display `(,(add-props "bold" `((0 . 4)
+ :intensity bold))
+ ,(add-props "faint" `((0 . 5)
+ :intensity faint))
+ "normal")
+ :cursor '(4 . 1))))
+
+(ert-deftest eat-test-sgr-italic ()
+ "Test SGR italic attribute."
+ (eat--tests-with-term '()
+ (output "\e[3mitalic\n")
+ (should-term
+ :display `(,(add-props "italic" `((0 . 6) :italic t)))
+ :cursor '(2 . 1))
+ (output "\e[23mnormal\n")
+ (should-term
+ :display `(,(add-props "italic" `((0 . 6) :italic t))
+ "normal")
+ :cursor '(3 . 1))))
+
+(ert-deftest eat-test-sgr-underline ()
+ "Test SGR underline with no color, 256 colors and 24-bit colors."
+ (eat--tests-with-term '()
+ ;; Without colors.
+ (output "\e[4mdefault line\n")
+ (should-term
+ :display `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line)))
+ :cursor '(2 . 1))
+ (output "\e[4:0mnormal\n")
+ (should-term
+ :display `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ "normal")
+ :cursor '(3 . 1))
+ (output "\e[4:1mdefault line\n")
+ (should-term
+ :display `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ "normal"
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line)))
+ :cursor '(4 . 1))
+ (output "\e[4:2mdefault line\n")
+ (should-term
+ :display `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ "normal"
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line)))
+ :cursor '(5 . 1))
+ (output "\e[4:3mdefault wave\n")
+ (should-term
+ :display `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ "normal"
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave)))
+ :cursor '(6 . 1))
+ (output "\e[4:4mdefault wave\n")
+ (should-term
+ :scrollback `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line)))
+ :display `("normal"
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave)))
+ :cursor '(6 . 1))
+ (output "\e[4:5mdefault wave\n")
+ (should-term
+ :scrollback `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ "normal")
+ :display `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave)))
+ :cursor '(6 . 1))
+ (output "\e[4;58;5;6mcyan line\n")
+ (should-term
+ :scrollback `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ "normal"
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line)))
+ :display `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "cyan line"
+ `((0 . 9)
+ :underline-type line
+ :underline-color ,(face-foreground
+ 'eat-term-color-6 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[4:3;58;5;3myellow wave\n")
+ (should-term
+ :scrollback `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ "normal"
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line)))
+ :display `(,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "cyan line"
+ `((0 . 9)
+ :underline-type line
+ :underline-color ,(face-foreground
+ 'eat-term-color-6 nil t)))
+ ,(add-props
+ "yellow wave"
+ `((0 . 11)
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-3 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[4:1;58;5;13mbright magenta line\n")
+ (should-term
+ :scrollback `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ "normal"
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave)))
+ :display `(,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "cyan line"
+ `((0 . 9)
+ :underline-type line
+ :underline-color ,(face-foreground
+ 'eat-term-color-6 nil t)))
+ ,(add-props
+ "yellow wave"
+ `((0 . 11)
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-3 nil t)))
+ ,(add-props
+ "bright magenta line"
+ `((0 . 19)
+ :underline-type line
+ :underline-color ,(face-foreground
+ 'eat-term-color-13 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[4:4;58;5;133mcolor-133 wave\n")
+ (should-term
+ :scrollback `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ "normal"
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave)))
+ :display `(,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "cyan line"
+ `((0 . 9)
+ :underline-type line
+ :underline-color ,(face-foreground
+ 'eat-term-color-6 nil t)))
+ ,(add-props
+ "yellow wave"
+ `((0 . 11)
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-3 nil t)))
+ ,(add-props
+ "bright magenta line"
+ `((0 . 19)
+ :underline-type line
+ :underline-color ,(face-foreground
+ 'eat-term-color-13 nil t)))
+ ,(add-props
+ "color-133 wave"
+ `((0 . 14)
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-133 nil t))))
+ :cursor '(6 . 1))
+ (output "\e[4:2;58;2;160;32;240mpurple line\n")
+ (should-term
+ :scrollback `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ "normal"
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave)))
+ :display `(,(add-props
+ "cyan line"
+ `((0 . 9)
+ :underline-type line
+ :underline-color ,(face-foreground
+ 'eat-term-color-6 nil t)))
+ ,(add-props
+ "yellow wave"
+ `((0 . 11)
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-3 nil t)))
+ ,(add-props
+ "bright magenta line"
+ `((0 . 19)
+ :underline-type line
+ :underline-color ,(face-foreground
+ 'eat-term-color-13 nil t)))
+ ,(add-props
+ "color-133 wave"
+ `((0 . 14)
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-133 nil t)))
+ ,(add-props
+ "purple line"
+ '((0 . 11)
+ :underline-type line
+ :underline-color "#a020f0")))
+ :cursor '(6 . 1))
+ (output "\e[4:5;58;2;0;0;139mdark blue wave\n")
+ (should-term
+ :scrollback `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ "normal"
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "cyan line"
+ `((0 . 9)
+ :underline-type line
+ :underline-color ,(face-foreground
+ 'eat-term-color-6 nil t))))
+ :display `(,(add-props
+ "yellow wave"
+ `((0 . 11)
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-3 nil t)))
+ ,(add-props
+ "bright magenta line"
+ `((0 . 19)
+ :underline-type line
+ :underline-color ,(face-foreground
+ 'eat-term-color-13 nil t)))
+ ,(add-props
+ "color-133 wave"
+ `((0 . 14)
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-133 nil t)))
+ ,(add-props
+ "purple line"
+ '((0 . 11)
+ :underline-type line
+ :underline-color "#a020f0"))
+ ,(add-props
+ "dark blue wave"
+ '((0 . 14)
+ :underline-type wave
+ :underline-color "#00008b")))
+ :cursor '(6 . 1))
+ (output "\e[59mdefault wave\n")
+ (should-term
+ :scrollback `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ "normal"
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "cyan line"
+ `((0 . 9)
+ :underline-type line
+ :underline-color ,(face-foreground
+ 'eat-term-color-6 nil t)))
+ ,(add-props
+ "yellow wave"
+ `((0 . 11)
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-3 nil t))))
+ :display `(,(add-props
+ "bright magenta line"
+ `((0 . 19)
+ :underline-type line
+ :underline-color ,(face-foreground
+ 'eat-term-color-13 nil t)))
+ ,(add-props
+ "color-133 wave"
+ `((0 . 14)
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-133 nil t)))
+ ,(add-props
+ "purple line"
+ '((0 . 11)
+ :underline-type line
+ :underline-color "#a020f0"))
+ ,(add-props
+ "dark blue wave"
+ '((0 . 14)
+ :underline-type wave
+ :underline-color "#00008b"))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave)))
+ :cursor '(6 . 1))
+ (output "\e[21mdefault line\n")
+ (should-term
+ :scrollback `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ "normal"
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "cyan line"
+ `((0 . 9)
+ :underline-type line
+ :underline-color ,(face-foreground
+ 'eat-term-color-6 nil t)))
+ ,(add-props
+ "yellow wave"
+ `((0 . 11)
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-3 nil t)))
+ ,(add-props
+ "bright magenta line"
+ `((0 . 19)
+ :underline-type line
+ :underline-color ,(face-foreground
+ 'eat-term-color-13 nil t))))
+ :display `(,(add-props
+ "color-133 wave"
+ `((0 . 14)
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-133 nil t)))
+ ,(add-props
+ "purple line"
+ '((0 . 11)
+ :underline-type line
+ :underline-color "#a020f0"))
+ ,(add-props
+ "dark blue wave"
+ '((0 . 14)
+ :underline-type wave
+ :underline-color "#00008b"))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line)))
+ :cursor '(6 . 1))
+ (output "\e[24mnormal\n")
+ (should-term
+ :scrollback `(,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ "normal"
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "cyan line"
+ `((0 . 9)
+ :underline-type line
+ :underline-color ,(face-foreground
+ 'eat-term-color-6 nil t)))
+ ,(add-props
+ "yellow wave"
+ `((0 . 11)
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-3 nil t)))
+ ,(add-props
+ "bright magenta line"
+ `((0 . 19)
+ :underline-type line
+ :underline-color ,(face-foreground
+ 'eat-term-color-13 nil t)))
+ ,(add-props
+ "color-133 wave"
+ `((0 . 14)
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-133 nil t))))
+ :display `(,(add-props
+ "purple line"
+ '((0 . 11)
+ :underline-type line
+ :underline-color "#a020f0"))
+ ,(add-props
+ "dark blue wave"
+ '((0 . 14)
+ :underline-type wave
+ :underline-color "#00008b"))
+ ,(add-props
+ "default wave"
+ '((0 . 12)
+ :underline-type wave))
+ ,(add-props
+ "default line"
+ '((0 . 12)
+ :underline-type line))
+ "normal")
+ :cursor '(6 . 1))))
+
+(ert-deftest eat-test-sgr-crossed ()
+ "Test SGR crossed attribute."
+ (eat--tests-with-term '()
+ (output "\e[9mcrossed\n")
+ (should-term
+ :display `(,(add-props "crossed" `((0 . 7) :crossed t)))
+ :cursor '(2 . 1))
+ (output "\e[29mnormal\n")
+ (should-term
+ :display `(,(add-props "crossed" `((0 . 7) :crossed t))
+ "normal")
+ :cursor '(3 . 1))))
+
+(ert-deftest eat-test-sgr-inverse ()
+ "Test SGR inverse attributes."
+ (eat--tests-with-term '()
+ (output "\e[7mdefault\n")
+ (should-term
+ :display `(,(add-props
+ "default"
+ `((0 . 7)
+ :foreground ,(face-background
+ 'default nil t)
+ :background ,(face-foreground
+ 'default nil t))))
+ :cursor '(2 . 1))
+ (output "\e[31mred fg\n")
+ (should-term
+ :display `(,(add-props
+ "default"
+ `((0 . 7)
+ :foreground ,(face-background
+ 'default nil t)
+ :background ,(face-foreground
+ 'default nil t)))
+ ,(add-props
+ "red fg"
+ `((0 . 6)
+ :foreground ,(face-background
+ 'default nil t)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t))))
+ :cursor '(3 . 1))
+ (output "\e[42mred fg green bg\n")
+ (should-term
+ :display `(,(add-props
+ "default"
+ `((0 . 7)
+ :foreground ,(face-background
+ 'default nil t)
+ :background ,(face-foreground
+ 'default nil t)))
+ ,(add-props
+ "red fg"
+ `((0 . 6)
+ :foreground ,(face-background
+ 'default nil t)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "red fg green bg"
+ `((0 . 15)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t))))
+ :cursor '(4 . 1))
+ (output "\e[39mgreen bg\n")
+ (should-term
+ :display `(,(add-props
+ "default"
+ `((0 . 7)
+ :foreground ,(face-background
+ 'default nil t)
+ :background ,(face-foreground
+ 'default nil t)))
+ ,(add-props
+ "red fg"
+ `((0 . 6)
+ :foreground ,(face-background
+ 'default nil t)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "red fg green bg"
+ `((0 . 15)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green bg"
+ `((0 . 8)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)
+ :background ,(face-foreground
+ 'default nil t))))
+ :cursor '(5 . 1))
+ (output "\e[27;49mnormal\n")
+ (should-term
+ :display `(,(add-props
+ "default"
+ `((0 . 7)
+ :foreground ,(face-background
+ 'default nil t)
+ :background ,(face-foreground
+ 'default nil t)))
+ ,(add-props
+ "red fg"
+ `((0 . 6)
+ :foreground ,(face-background
+ 'default nil t)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "red fg green bg"
+ `((0 . 15)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green bg"
+ `((0 . 8)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)
+ :background ,(face-foreground
+ 'default nil t)))
+ "normal")
+ :cursor '(6 . 1))))
+
+(ert-deftest eat-test-sgr-blink ()
+ "Test SGR blink attributes (both slow and fast blink)."
+ (eat--tests-with-term '()
+ (output "\e[5mslow\n")
+ (should-term
+ :display `(,(add-props "slow" `((0 . 4) :blink slow)))
+ :cursor '(2 . 1))
+ (output "\e[6mfast\n")
+ (should-term
+ :display `(,(add-props "slow" `((0 . 4) :blink slow))
+ ,(add-props "fast" `((0 . 4) :blink fast)))
+ :cursor '(3 . 1))
+ (output "\e[25mnormal\n")
+ (should-term
+ :display `(,(add-props "slow" `((0 . 4) :blink slow))
+ ,(add-props "fast" `((0 . 4) :blink fast))
+ "normal")
+ :cursor '(4 . 1))))
+
+(ert-deftest eat-test-sgr-conceal ()
+ (eat--tests-with-term '()
+ (output "\e[8mdefault\n")
+ (should-term
+ :display `(,(add-props
+ "default"
+ `((0 . 7)
+ :foreground ,(face-background
+ 'default nil t))))
+ :cursor '(2 . 1))
+ (output "\e[31mdefault with fg\n")
+ (should-term
+ :display `(,(add-props
+ "default"
+ `((0 . 7)
+ :foreground ,(face-background
+ 'default nil t)))
+ ,(add-props
+ "default with fg"
+ `((0 . 15)
+ :foreground ,(face-background
+ 'default nil t))))
+ :cursor '(3 . 1))
+ (output "\e[41mred\n")
+ (should-term
+ :display `(,(add-props
+ "default"
+ `((0 . 7)
+ :foreground ,(face-background
+ 'default nil t)))
+ ,(add-props
+ "default with fg"
+ `((0 . 15)
+ :foreground ,(face-background
+ 'default nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t))))
+ :cursor '(4 . 1))
+ (output "\e[31;42mgreen\n")
+ (should-term
+ :display `(,(add-props
+ "default"
+ `((0 . 7)
+ :foreground ,(face-background
+ 'default nil t)))
+ ,(add-props
+ "default with fg"
+ `((0 . 15)
+ :foreground ,(face-background
+ 'default nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t))))
+ :cursor '(5 . 1))
+ (output "\e[28mred on green\n")
+ (should-term
+ :display `(,(add-props
+ "default"
+ `((0 . 7)
+ :foreground ,(face-background
+ 'default nil t)))
+ ,(add-props
+ "default with fg"
+ `((0 . 15)
+ :foreground ,(face-background
+ 'default nil t)))
+ ,(add-props
+ "red"
+ `((0 . 3)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-1 nil t)))
+ ,(add-props
+ "green"
+ `((0 . 5)
+ :foreground ,(face-foreground
+ 'eat-term-color-2 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t)))
+ ,(add-props
+ "red on green"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-1 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t))))
+ :cursor '(6 . 1))))
+
+(ert-deftest eat-test-sgr-font ()
+ "Test SGR font attributes."
+ (eat--tests-with-term '()
+ (output "font 0\n")
+ (should-term
+ :display '("font 0")
+ :cursor '(2 . 1))
+ (should-term
+ :display `(,(add-props "font 0" `((0 . 6) :font 0)))
+ :cursor '(2 . 1))
+ (output "\e[13mfont 3\n")
+ (should-term
+ :display `("font 0"
+ ,(add-props "font 3" `((0 . 6) :font 3)))
+ :cursor '(3 . 1))
+ (output "\e[19mfont 9\n")
+ (should-term
+ :display `("font 0"
+ ,(add-props "font 3" `((0 . 6) :font 3))
+ ,(add-props "font 9" `((0 . 6) :font 9)))
+ :cursor '(4 . 1))
+ (output "\e[12mfont 2\n")
+ (should-term
+ :display `("font 0"
+ ,(add-props "font 3" `((0 . 6) :font 3))
+ ,(add-props "font 9" `((0 . 6) :font 9))
+ ,(add-props "font 2" `((0 . 6) :font 2)))
+ :cursor '(5 . 1))
+ (output "\e[10mfont 0, normal\n")
+ (should-term
+ :display `("font 0"
+ ,(add-props "font 3" `((0 . 6) :font 3))
+ ,(add-props "font 9" `((0 . 6) :font 9))
+ ,(add-props "font 2" `((0 . 6) :font 2))
+ "font 0, normal")
+ :cursor '(6 . 1))))
+
+(ert-deftest eat-test-sgr-reset ()
+ "Test SGR attributes reset sequence."
+ (eat--tests-with-term '(:width 30 :height 10)
+ (output "\e[1;3;4:3;6;7;9;15;33;105;"
+ "58;5;10mcrazy text 1\n")
+ (should-term
+ :display `(,(add-props
+ "crazy text 1"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-13 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-3 nil t)
+ :intensity bold
+ :italic t
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-10 nil t)
+ :blink fast
+ :crossed t
+ :font 5)))
+ :cursor '(2 . 1))
+ (output "\e[0mnormal text 1\r\n")
+ (should-term
+ :display `(,(add-props
+ "crazy text 1"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-13 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-3 nil t)
+ :intensity bold
+ :italic t
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-10 nil t)
+ :blink fast
+ :crossed t
+ :font 5))
+ "normal text 1")
+ :cursor '(3 . 1))
+ (output "\e[2;3;4:1;5;7;8;9;18;"
+ "38;2;50;90;100;48;2;100;50;9;"
+ "58;2;10;90;45mcrazy text 2\n")
+ (should-term
+ :display `(,(add-props
+ "crazy text 1"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-13 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-3 nil t)
+ :intensity bold
+ :italic t
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-10 nil t)
+ :blink fast
+ :crossed t
+ :font 5))
+ "normal text 1"
+ ,(add-props
+ "crazy text 2"
+ '((0 . 12)
+ :foreground "#643209"
+ :background "#643209"
+ :intensity faint
+ :italic t
+ :underline-type line
+ :underline-color "#0a5a2d"
+ :blink slow
+ :crossed t
+ :font 8)))
+ :cursor '(4 . 1))
+ (output "\e[mnormal text 2\n")
+ (should-term
+ :display `(,(add-props
+ "crazy text 1"
+ `((0 . 12)
+ :foreground ,(face-foreground
+ 'eat-term-color-13 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-3 nil t)
+ :intensity bold
+ :italic t
+ :underline-type wave
+ :underline-color ,(face-foreground
+ 'eat-term-color-10 nil t)
+ :blink fast
+ :crossed t
+ :font 5))
+ "normal text 1"
+ ,(add-props
+ "crazy text 2"
+ '((0 . 12)
+ :foreground "#643209"
+ :background "#643209"
+ :intensity faint
+ :italic t
+ :underline-type line
+ :underline-color "#0a5a2d"
+ :blink slow
+ :crossed t
+ :font 8))
+ "normal text 2")
+ :cursor '(5 . 1))))
+
+
+;;;;; Text Manipulation Tests.
+
+(ert-deftest eat-test-insert-character ()
+ "Test insert character control function."
+ (eat--tests-with-term '()
+ ;; Without background.
+ (output "in the universe,\nthere are\nbugs and antibugs\n"
+ "iwritebothoftheme")
+ (should-term :display '("in the universe,"
+ "there are"
+ "bugs and antibugs"
+ "iwritebothoftheme")
+ :cursor '(4 . 18))
+ (output "\e[2G\e[@")
+ (should-term :display '("in the universe,"
+ "there are"
+ "bugs and antibugs"
+ "i writebothoftheme")
+ :cursor '(4 . 2))
+ (output "\e[6C\e[1@")
+ (should-term :display '("in the universe,"
+ "there are"
+ "bugs and antibugs"
+ "i write bothoftheme")
+ :cursor '(4 . 8))
+ (output "\e[5C\e[@")
+ (should-term :display '("in the universe,"
+ "there are"
+ "bugs and antibugs"
+ "i write both oftheme")
+ :cursor '(4 . 13))
+ (output "\e[3C\e[0@")
+ (should-term :display '("in the universe,"
+ "there are"
+ "bugs and antibugs"
+ "i write both of them")
+ :cursor '(4 . 16))
+ ;; With background.
+ (output "\nfoobar\e[3D")
+ (should-term :display '("in the universe,"
+ "there are"
+ "bugs and antibugs"
+ "i write both of them"
+ "foobar")
+ :cursor '(5 . 4))
+ (output "\e[40m\e[@")
+ (should-term
+ :display `("in the universe,"
+ "there are"
+ "bugs and antibugs"
+ "i write both of them"
+ ,(add-props
+ "foo bar"
+ `((3 . 4)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t))))
+ :cursor '(5 . 4))
+ (output "\e[107m\e[0@")
+ (should-term
+ :display `("in the universe,"
+ "there are"
+ "bugs and antibugs"
+ "i write both of them"
+ ,(add-props
+ "foo bar"
+ `((3 . 4)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t))
+ `((4 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t))))
+ :cursor '(5 . 4))
+ (output "\e[48;5;133m\e[1@")
+ (should-term
+ :display `("in the universe,"
+ "there are"
+ "bugs and antibugs"
+ "i write both of them"
+ ,(add-props
+ "foo bar"
+ `((3 . 4)
+ :background ,(face-foreground
+ 'eat-term-color-133 nil t))
+ `((4 . 5)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t))
+ `((5 . 6)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t))))
+ :cursor '(5 . 4))
+ (output "\e[48;2;50;255;62m\e[5@")
+ (should-term
+ :display `("in the universe,"
+ "there are"
+ "bugs and antibugs"
+ "i write both of them"
+ ,(add-props
+ "foo bar"
+ '((3 . 8)
+ :background "#32ff3e")
+ `((8 . 9)
+ :background ,(face-foreground
+ 'eat-term-color-133 nil t))
+ `((9 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t))
+ `((10 . 11)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t))))
+ :cursor '(5 . 4))
+ (output "\e[49m\e[3@")
+ (should-term
+ :display `("in the universe,"
+ "there are"
+ "bugs and antibugs"
+ "i write both of them"
+ ,(add-props
+ "foo bar"
+ '((6 . 11)
+ :background "#32ff3e")
+ `((11 . 12)
+ :background ,(face-foreground
+ 'eat-term-color-133 nil t))
+ `((12 . 13)
+ :background ,(face-foreground
+ 'eat-term-color-15 nil t))
+ `((13 . 14)
+ :background ,(face-foreground
+ 'eat-term-color-0 nil t))))
+ :cursor '(5 . 4))))
+
+(ert-deftest eat-test-delete-character ()
+ "Test delete character control function."
+ (eat--tests-with-term '()
+ ;; Without background.
+ (output "sun is an star")
+ (should-term :display '("sun is an star")
+ :cursor '(1 . 15))
+ (output "\e[6D\e[P")
+ (should-term :display '("sun is a star")
+ :cursor '(1 . 9))
+ (output "\nmars is an exoplanet")
+ (should-term :display '("sun is a star"
+ "mars is an exoplanet")
+ :cursor '(2 . 20))
+ (output "\e[9D\e[3P")
+ (should-term :display '("sun is a star"
+ "mars is an planet")
+ :cursor '(2 . 12))
+ (output "\e[2D\e[0P")
+ (should-term :display '("sun is a star"
+ "mars is a planet")
+ :cursor '(2 . 10))
+ ;; With background.
+ (output "\nnill isn't false")
+ (should-term :display '("sun is a star"
+ "mars is a planet"
+ "nill isn't false")
+ :cursor '(3 . 17))
+ (output "\e[3G\e[46m\e[P\e[49m")
+ (should-term
+ :display `("sun is a star"
+ "mars is a planet"
+ ,(add-props
+ "nil isn't false "
+ `((19 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-6 nil t))))
+ :cursor '(3 . 3))
+ (output "\e[7G\e[48;5;14m\e[3P\e[49m")
+ (should-term
+ :display `("sun is a star"
+ "mars is a planet"
+ ,(add-props
+ "nil is false "
+ `((16 . 17)
+ :background ,(face-foreground
+ 'eat-term-color-6 nil t))
+ `((17 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-14 nil t))))
+ :cursor '(3 . 7))
+ (output "\nemacs is awesomes")
+ (should-term
+ :display `("sun is a star"
+ "mars is a planet"
+ ,(add-props
+ "nil is false "
+ `((16 . 17)
+ :background ,(face-foreground
+ 'eat-term-color-6 nil t))
+ `((17 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-14 nil t)))
+ "emacs is awesomes")
+ :cursor '(4 . 18))
+ (output "\e[D\e[48;2;226;43;93m\e[0P\e[49m")
+ (should-term
+ :display `("sun is a star"
+ "mars is a planet"
+ ,(add-props
+ "nil is false "
+ `((16 . 17)
+ :background ,(face-foreground
+ 'eat-term-color-6 nil t))
+ `((17 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-14 nil t)))
+ ,(add-props
+ "emacs is awesome "
+ '((19 . 20)
+ :background "#e22b5d")))
+ :cursor '(4 . 17))))
+
+(ert-deftest eat-test-erase-character ()
+ "Test erase character control function."
+ (eat--tests-with-term '()
+ ;; Without background.
+ (output "abbcccddddee")
+ (should-term :display '("abbcccddddee")
+ :cursor '(1 . 13))
+ (output "\e[2`\e[X")
+ (should-term :display '("a bcccddddee")
+ :cursor '(1 . 2))
+ (output "\e[2C\e[1X")
+ (should-term :display '("a b ccddddee")
+ :cursor '(1 . 4))
+ (output "\e[2C\e[2X")
+ (should-term :display '("a b c dddee")
+ :cursor '(1 . 6))
+ (output "\e[3C\e[2X")
+ (should-term :display '("a b c d ee")
+ :cursor '(1 . 9))
+ (output "\e[3C\e[0X")
+ (should-term :display '("a b c d e")
+ :cursor '(1 . 12))
+ ;; With background.
+ (output "\nabbcccddddee")
+ (should-term :display '("a b c d e"
+ "abbcccddddee")
+ :cursor '(2 . 13))
+ (output "\e[2`\e[42m\e[X")
+ (should-term
+ :display `("a b c d e"
+ ,(add-props
+ "a bcccddddee"
+ `((1 . 2)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t))))
+ :cursor '(2 . 2))
+ (output "\e[2C\e[48;5;42m\e[1X")
+ (should-term
+ :display `("a b c d e"
+ ,(add-props
+ "a b ccddddee"
+ `((1 . 2)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t))
+ `((3 . 4)
+ :background ,(face-foreground
+ 'eat-term-color-42 nil t))))
+ :cursor '(2 . 4))
+ (output "\e[2C\e[48;2;0;46;160m\e[2X")
+ (should-term
+ :display `("a b c d e"
+ ,(add-props
+ "a b c dddee"
+ `((1 . 2)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t))
+ `((3 . 4)
+ :background ,(face-foreground
+ 'eat-term-color-42 nil t))
+ '((5 . 7)
+ :background "#002ea0")))
+ :cursor '(2 . 6))
+ (output "\e[3C\e[103m\e[2X")
+ (should-term
+ :display `("a b c d e"
+ ,(add-props
+ "a b c d ee"
+ `((1 . 2)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t))
+ `((3 . 4)
+ :background ,(face-foreground
+ 'eat-term-color-42 nil t))
+ '((5 . 7)
+ :background "#002ea0")
+ `((8 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-11 nil t))))
+ :cursor '(2 . 9))
+ (output "\e[3C\e[48;2;162;96;198m\e[0X")
+ (should-term
+ :display `("a b c d e"
+ ,(add-props
+ "a b c d e "
+ `((1 . 2)
+ :background ,(face-foreground
+ 'eat-term-color-2 nil t))
+ `((3 . 4)
+ :background ,(face-foreground
+ 'eat-term-color-42 nil t))
+ '((5 . 7)
+ :background "#002ea0")
+ `((8 . 10)
+ :background ,(face-foreground
+ 'eat-term-color-11 nil t))
+ '((11 . 12)
+ :background "#a260c6")))
+ :cursor '(2 . 12))))
+
+(ert-deftest eat-test-repeat ()
+ "Test repeat control function."
+ (eat--tests-with-term '()
+ ;; Without SGR attributes.
+ (output "a\e[b")
+ (should-term :display '("aa")
+ :cursor '(1 . 3))
+ (output "\nb\e[2b")
+ (should-term :display '("aa"
+ "bbb")
+ :cursor '(2 . 4))
+ (output "\nc\e[0b")
+ (should-term :display '("aa"
+ "bbb"
+ "cc")
+ :cursor '(3 . 3))
+ ;; With SGR attributes.
+ (output "\n\e[34;43md\e[b")
+ (should-term
+ :display `("aa"
+ "bbb"
+ "cc"
+ ,(add-props
+ "dd"
+ `((0 . 2)
+ :foreground ,(face-foreground
+ 'eat-term-color-4 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-3 nil t))))
+ :cursor '(4 . 3))
+ (output "\n\e[;1;11me\e[5b")
+ (should-term
+ :display `("aa"
+ "bbb"
+ "cc"
+ ,(add-props
+ "dd"
+ `((0 . 2)
+ :foreground ,(face-foreground
+ 'eat-term-color-4 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-3 nil t)))
+ ,(add-props
+ "eeeeee"
+ '((0 . 6)
+ :intensity bold
+ :font 1)))
+ :cursor '(5 . 7))
+ (output "\n\e[;2;5mf\e[0b")
+ (should-term
+ :display `("aa"
+ "bbb"
+ "cc"
+ ,(add-props
+ "dd"
+ `((0 . 2)
+ :foreground ,(face-foreground
+ 'eat-term-color-4 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-3 nil t)))
+ ,(add-props
+ "eeeeee"
+ '((0 . 6)
+ :intensity bold
+ :font 1))
+ ,(add-props
+ "ff"
+ '((0 . 2)
+ :intensity faint
+ :blink slow)))
+ :cursor '(6 . 3))))
+
+(ert-deftest eat-test-insert-line ()
+ "Test insert line control function."
+ (eat--tests-with-term '()
+ ;; Without background.
+ (output "early to bed and\nearly to rise,\nmakes a man\n"
+ "healthy, wealthy,\nand wise")
+ (should-term :display '("early to bed and"
+ "early to rise,"
+ "makes a man"
+ "healthy, wealthy,"
+ "and wise")
+ :cursor '(5 . 9))
+ (output "\e[L")
+ (should-term :display '("early to bed and"
+ "early to rise,"
+ "makes a man"
+ "healthy, wealthy,"
+ ""
+ "and wise")
+ :cursor '(5 . 9))
+ (output "\e[2;15H\e[3L")
+ (should-term :display '("early to bed and"
+ ""
+ ""
+ ""
+ "early to rise,"
+ "makes a man")
+ :cursor '(2 . 15))
+ (output "\e[0L")
+ (should-term :display '("early to bed and"
+ ""
+ ""
+ ""
+ ""
+ "early to rise,")
+ :cursor '(2 . 15))
+ ;; With background.
+ (output "\e[2Jearly to bed and\nearly to rise,\nmakes a man\n"
+ "healthy, wealthy,\nand wise")
+ (should-term :display '("early to bed and"
+ "early to rise,"
+ "makes a man"
+ "healthy, wealthy,"
+ "and wise")
+ :cursor '(5 . 9))
+ (output "\e[44m\e[L")
+ (should-term
+ :display `("early to bed and"
+ "early to rise,"
+ "makes a man"
+ "healthy, wealthy,"
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-4 nil t)))
+ "and wise")
+ :cursor '(5 . 9))
+ (output "\e[2;15H\e[100m\e[3L")
+ (should-term
+ :display `("early to bed and"
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ "early to rise,"
+ "makes a man")
+ :cursor '(2 . 15))
+ (output "\e[48;2;100;100;50m\e[0L")
+ (should-term
+ :display `("early to bed and"
+ ,(add-props
+ " "
+ '((0 . 20)
+ :background "#646432"))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ "early to rise,")
+ :cursor '(2 . 15))
+ (output "\e[49m\e[1L")
+ (should-term
+ :display `("early to bed and"
+ ""
+ ,(add-props
+ " "
+ '((0 . 20)
+ :background "#646432"))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t))))
+ :cursor '(2 . 15))))
+
+(ert-deftest eat-test-delete-line ()
+ "Test insert line control function."
+ (eat--tests-with-term '()
+ ;; Without background.
+ (output "early to bed and\nearly to rise,\nmakes a man\n"
+ "healthy, wealthy,\nand wise")
+ (should-term :display '("early to bed and"
+ "early to rise,"
+ "makes a man"
+ "healthy, wealthy,"
+ "and wise")
+ :cursor '(5 . 9))
+ (output "\e[M")
+ (should-term :display '("early to bed and"
+ "early to rise,"
+ "makes a man"
+ "healthy, wealthy,")
+ :cursor '(5 . 9))
+ (output "\e[2;15H\e[3M")
+ (should-term :display '("early to bed and")
+ :cursor '(2 . 15))
+ (output "\e[;5H\e[0M")
+ (should-term :cursor '(1 . 5))
+ ;; With background.
+ (output "\e[Hearly to bed and\nearly to rise,\nmakes a man\n"
+ "healthy, wealthy,\nand wise")
+ (should-term :display '("early to bed and"
+ "early to rise,"
+ "makes a man"
+ "healthy, wealthy,"
+ "and wise")
+ :cursor '(5 . 9))
+ (output "\e[44m\e[M")
+ (should-term
+ :display `("early to bed and"
+ "early to rise,"
+ "makes a man"
+ "healthy, wealthy,"
+ ""
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-4 nil t))))
+ :cursor '(5 . 9))
+ (output "\e[2;15H\e[100m\e[3M")
+ (should-term
+ :display `("early to bed and"
+ ""
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-4 nil t)))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t))))
+ :cursor '(2 . 15))
+ (output "\e[H\e[48;2;100;100;50m\e[0M")
+ (should-term
+ :display `(""
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-4 nil t)))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ " "
+ '((0 . 20)
+ :background "#646432")))
+ :cursor '(1 . 1))
+ (output "\e[49m\e[1M")
+ (should-term
+ :display `(,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-4 nil t)))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ " "
+ '((0 . 20)
+ :background "#646432")))
+ :cursor '(1 . 1))))
+
+(ert-deftest eat-test-erase-in-line ()
+ "Test erase in line control function."
+ (eat--tests-with-term '()
+ ;; Without background.
+ (output "foo bar baz\e[6G")
+ (should-term :display '("foo bar baz")
+ :cursor '(1 . 6))
+ (output "\e[K")
+ (should-term :display '("foo b")
+ :cursor '(1 . 6))
+ (output "\nbar baz foo\e[6G")
+ (should-term :display '("foo b"
+ "bar baz foo")
+ :cursor '(2 . 6))
+ (output "\e[1K")
+ (should-term :display '("foo b"
+ " z foo")
+ :cursor '(2 . 6))
+ (output "\nbaz foo bar\e[6G")
+ (should-term :display '("foo b"
+ " z foo"
+ "baz foo bar")
+ :cursor '(3 . 6))
+ (output "\e[0K")
+ (should-term :display '("foo b"
+ " z foo"
+ "baz f")
+ :cursor '(3 . 6))
+ (output "\nfoo bar baz\e[6G")
+ (should-term :display '("foo b"
+ " z foo"
+ "baz f"
+ "foo bar baz")
+ :cursor '(4 . 6))
+ (output "\e[2K")
+ (should-term :display '("foo b"
+ " z foo"
+ "baz f")
+ :cursor '(4 . 6))
+ ;; With background.
+ (output "\nfoo bar baz\e[6G")
+ (should-term :display '("foo b"
+ " z foo"
+ "baz f"
+ ""
+ "foo bar baz")
+ :cursor '(5 . 6))
+ (output "\e[47m\e[K\e[m")
+ (should-term
+ :display `("foo b"
+ " z foo"
+ "baz f"
+ ""
+ ,(add-props
+ "foo b "
+ `((5 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t))))
+ :cursor '(5 . 6))
+ (output "\nbar baz foo\e[6G")
+ (should-term
+ :display `("foo b"
+ " z foo"
+ "baz f"
+ ""
+ ,(add-props
+ "foo b "
+ `((5 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ "bar baz foo")
+ :cursor '(6 . 6))
+ (output "\e[100m\e[1K\e[m")
+ (should-term
+ :display `("foo b"
+ " z foo"
+ "baz f"
+ ""
+ ,(add-props
+ "foo b "
+ `((5 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ " z foo"
+ `((0 . 6)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t))))
+ :cursor '(6 . 6))
+ (output "\nbaz foo bar\e[6G")
+ (should-term
+ :scrollback '("foo b")
+ :display `(" z foo"
+ "baz f"
+ ""
+ ,(add-props
+ "foo b "
+ `((5 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ " z foo"
+ `((0 . 6)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ "baz foo bar")
+ :cursor '(6 . 6))
+ (output "\e[48;5;55m\e[0K\e[m")
+ (should-term
+ :scrollback '("foo b")
+ :display `(" z foo"
+ "baz f"
+ ""
+ ,(add-props
+ "foo b "
+ `((5 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ " z foo"
+ `((0 . 6)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "baz f "
+ `((5 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-55 nil t))))
+ :cursor '(6 . 6))
+ (output "\nfoo bar baz\e[6G")
+ (should-term
+ :scrollback '("foo b"
+ " z foo")
+ :display `("baz f"
+ ""
+ ,(add-props
+ "foo b "
+ `((5 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ " z foo"
+ `((0 . 6)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "baz f "
+ `((5 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-55 nil t)))
+ "foo bar baz")
+ :cursor '(6 . 6))
+ (output "\e[48;2;255;255;255m\e[2K")
+ (should-term
+ :scrollback '("foo b"
+ " z foo")
+ :display `("baz f"
+ ""
+ ,(add-props
+ "foo b "
+ `((5 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-7 nil t)))
+ ,(add-props
+ " z foo"
+ `((0 . 6)
+ :background ,(face-foreground
+ 'eat-term-color-8 nil t)))
+ ,(add-props
+ "baz f "
+ `((5 . 20)
+ :background ,(face-foreground
+ 'eat-term-color-55 nil t)))
+ ,(add-props
+ " "
+ '((0 . 20)
+ :background "#ffffff")))
+ :cursor '(6 . 6))))
+
+(ert-deftest eat-test-erase-in-display ()
+ "Test erase in display control function."
+ (eat--tests-with-term '()
+ ;; Without background.
+ (output "foo bar baz\nbar baz foo\nbaz foo bar\e[2;6H")
+ (should-term :display '("foo bar baz"
+ "bar baz foo"
+ "baz foo bar")
+ :cursor '(2 . 6))
+ (output "\e[J")
+ (should-term :display '("foo bar baz"
+ "bar b")
+ :cursor '(2 . 6))
+ (output "\e[Hfoo bar baz\nbar baz foo\nbaz foo bar\e[2;6H")
+ (should-term :display '("foo bar baz"
+ "bar baz foo"
+ "baz foo bar")
+ :cursor '(2 . 6))
+ (output "\e[1J")
+ (should-term :display '(""
+ " z foo"
+ "baz foo bar")
+ :cursor '(2 . 6))
+ (output "\e[Hfoo bar baz\nbar baz foo\nbaz foo bar\e[2;6H")
+ (should-term :display '("foo bar baz"
+ "bar baz foo"
+ "baz foo bar")
+ :cursor '(2 . 6))
+ (output "\e[0J")
+ (should-term :display '("foo bar baz"
+ "bar b")
+ :cursor '(2 . 6))
+ (output "\e[Hfoo bar baz\nbar baz foo\nbaz foo bar\e[2;6H")
+ (should-term :display '("foo bar baz"
+ "bar baz foo"
+ "baz foo bar")
+ :cursor '(2 . 6))
+ (output "\e[2J")
+ (should-term :cursor '(1 . 1))
+ (output "foo bar baz\nbar baz foo\nbaz foo bar\n"
+ "foo bar baz\nbar baz foo\nbaz foo bar\n")
+ (should-term :scrollback '("foo bar baz")
+ :display '("bar baz foo"
+ "baz foo bar"
+ "foo bar baz"
+ "bar baz foo"
+ "baz foo bar")
+ :cursor '(6 . 1))
+ (output "\e[3J")
+ (should-term :cursor '(1 . 1))
+ ;; With background.
+ (output "foo bar baz\nbar baz foo\nbaz foo bar\e[2;6H")
+ (should-term :display '("foo bar baz"
+ "bar baz foo"
+ "baz foo bar")
+ :cursor '(2 . 6))
+ (output "\e[11;33;44m\e[J\e[m")
+ (should-term
+ :display `("foo bar baz"
+ ,(add-props
+ "bar b "
+ `((5 . 20)
+ :foreground ,(face-foreground
+ 'eat-term-color-3 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-4 nil t)
+ :font 1))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :foreground ,(face-foreground
+ 'eat-term-color-3 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-4 nil t)
+ :font 1))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :foreground ,(face-foreground
+ 'eat-term-color-3 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-4 nil t)
+ :font 1))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :foreground ,(face-foreground
+ 'eat-term-color-3 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-4 nil t)
+ :font 1))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :foreground ,(face-foreground
+ 'eat-term-color-3 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-4 nil t)
+ :font 1)))
+ :cursor '(2 . 6))
+ (output "\e[2Jfoo bar baz\nbar baz foo\nbaz foo bar\e[2;6H")
+ (should-term :display '("foo bar baz"
+ "bar baz foo"
+ "baz foo bar")
+ :cursor '(2 . 6))
+ (output "\e[1;97;103m\e[1J\e[m")
+ (should-term
+ :display `(,(add-props
+ " "
+ `((0 . 20)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-11 nil t)
+ :intensity bold))
+ ,(add-props
+ " z foo"
+ `((0 . 6)
+ :foreground ,(face-foreground
+ 'eat-term-color-15 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-11 nil t)
+ :intensity bold))
+ "baz foo bar")
+ :cursor '(2 . 6))
+ (output "\e[2Jfoo bar baz\nbar baz foo\nbaz foo bar\e[2;6H")
+ (should-term :display '("foo bar baz"
+ "bar baz foo"
+ "baz foo bar")
+ :cursor '(2 . 6))
+ (output "\e[11;34;43m\e[0J\e[m")
+ (should-term
+ :display `("foo bar baz"
+ ,(add-props
+ "bar b "
+ `((5 . 20)
+ :foreground ,(face-foreground
+ 'eat-term-color-4 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-3 nil t)
+ :font 1))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :foreground ,(face-foreground
+ 'eat-term-color-4 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-3 nil t)
+ :font 1))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :foreground ,(face-foreground
+ 'eat-term-color-4 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-3 nil t)
+ :font 1))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :foreground ,(face-foreground
+ 'eat-term-color-4 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-3 nil t)
+ :font 1))
+ ,(add-props
+ " "
+ `((0 . 20)
+ :foreground ,(face-foreground
+ 'eat-term-color-4 nil t)
+ :background ,(face-foreground
+ 'eat-term-color-3 nil t)
+ :font 1)))
+ :cursor '(2 . 6))
+ (output "\e[2Jfoo bar baz\nbar baz foo\nbaz foo bar\e[2;6H")
+ (should-term :display '("foo bar baz"
+ "bar baz foo"
+ "baz foo bar")
+ :cursor '(2 . 6))
+ (output "\e[48;2;50;200;100m\e[2J\e[m")
+ (should-term
+ :display `(,(add-props
+ " "
+ '((0 . 20) :background "#32c864"))
+ ,(add-props
+ " "
+ '((0 . 20) :background "#32c864"))
+ ,(add-props
+ " "
+ '((0 . 20) :background "#32c864"))
+ ,(add-props
+ " "
+ '((0 . 20) :background "#32c864"))
+ ,(add-props
+ " "
+ '((0 . 20) :background "#32c864"))
+ ,(add-props
+ " "
+ '((0 . 20) :background "#32c864")))
+ :cursor '(1 . 1))
+ (output "\e[2Jfoo bar baz\nbar baz foo\nbaz foo bar\n"
+ "foo bar baz\nbar baz foo\nbaz foo bar\n")
+ (should-term :scrollback '("foo bar baz")
+ :display '("bar baz foo"
+ "baz foo bar"
+ "foo bar baz"
+ "bar baz foo"
+ "baz foo bar")
+ :cursor '(6 . 1))
+ (output "\e[48;2;20;5;200m\e[3J\e[m")
+ (should-term
+ :display `(,(add-props
+ " "
+ '((0 . 20) :background "#1405c8"))
+ ,(add-props
+ " "
+ '((0 . 20) :background "#1405c8"))
+ ,(add-props
+ " "
+ '((0 . 20) :background "#1405c8"))
+ ,(add-props
+ " "
+ '((0 . 20) :background "#1405c8"))
+ ,(add-props
+ " "
+ '((0 . 20) :background "#1405c8"))
+ ,(add-props
+ " "
+ '((0 . 20) :background "#1405c8")))
+ :cursor '(1 . 1))))
+
+
+;;;;; Resizing Tests.
+
+(ert-deftest eat-test-resize-when-at-beginning-of-first-line ()
+ "Test resize when on the beginning of the first line of display."
+ (eat--tests-with-term '()
+ (output "1\n2\n3\n4\n5\n6\e[H")
+ (should-term :display '("1"
+ "2"
+ "3"
+ "4"
+ "5"
+ "6")
+ :cursor '(1 . 1))
+ (eat-term-resize (terminal) 20 5)
+ (should-term :scrollback '("1")
+ :display '("2"
+ "3"
+ "4"
+ "5"
+ "6")
+ :cursor '(1 . 1))
+ (output "7\n")
+ (should-term :scrollback '("1")
+ :display '("7"
+ "3"
+ "4"
+ "5"
+ "6")
+ :cursor '(2 . 1))))
+
+(ert-deftest eat-test-resize-when-at-beginning-of-last-line ()
+ "Test resize when on the beginning of the last line of display."
+ (eat--tests-with-term '()
+ (output "1\n2\n3\n4\n5\n")
+ (should-term :display '("1"
+ "2"
+ "3"
+ "4"
+ "5")
+ :cursor '(6 . 1))
+ (eat-term-resize (terminal) 20 5)
+ (should-term :scrollback '("1")
+ :display '("2"
+ "3"
+ "4"
+ "5")
+ :cursor '(5 . 1))
+ (output "6\n")
+ (should-term :scrollback '("1"
+ "2")
+ :display '("3"
+ "4"
+ "5"
+ "6")
+ :cursor '(5 . 1))))
+
+(ert-deftest eat-test-resize-alternative-display ()
+ "Test resize when the beginning of the first line on display."
+ (eat--tests-with-term '()
+ (output "\e[?1049htoo looooooooooooong\nl\no\no\nn\ng")
+ (should-term :display '("too looooooooooooong"
+ "l"
+ "o"
+ "o"
+ "n"
+ "g")
+ :cursor '(6 . 2))
+ (eat-term-resize (terminal) 18 4)
+ (should-term :display '("too looooooooooooo"
+ "l"
+ "o"
+ "o")
+ :cursor '(4 . 2))))
+
+
+;;;;; Miscellaneous Tests.
+
+(ert-deftest eat-test-bell ()
+ "Test bell control function."
+ (eat--tests-with-term '()
+ (let ((bell-rang nil))
+ (setf (eat-term-ring-bell-function (terminal))
+ (lambda (term)
+ (should (eq term (terminal)))
+ (setq bell-rang t)))
+ (output "\a")
+ (should bell-rang))))
+
+(ert-deftest eat-test-character-sets ()
+ "Test character sets."
+ (eat--tests-with-term '()
+ (output "some text")
+ (should-term :display '("some text")
+ :cursor '(1 . 10))
+ (output "\n\e(0some text")
+ (should-term :display '("some text"
+ "⎽⎺└␊ ├␊│├")
+ :cursor '(2 . 10))
+ (output "\n\e(Bsome text")
+ (should-term :display '("some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text")
+ :cursor '(3 . 10))
+ (output "\n\C-nsome text")
+ (should-term :display '("some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text")
+ :cursor '(4 . 10))
+ (output "\n\e)0some text")
+ (should-term :display '("some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├")
+ :cursor '(5 . 10))
+ (output "\n\e)Bsome text")
+ (should-term :display '("some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text")
+ :cursor '(6 . 10))
+ (output "\n\ensome text")
+ (should-term :scrollback '("some text")
+ :display '("⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text")
+ :cursor '(6 . 10))
+ (output "\n\e*0some text")
+ (should-term :scrollback '("some text"
+ "⎽⎺└␊ ├␊│├")
+ :display '("some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├")
+ :cursor '(6 . 10))
+ (output "\n\e*Bsome text")
+ (should-term :scrollback '("some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text")
+ :display '("some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text")
+ :cursor '(6 . 10))
+ (output "\n\eosome text")
+ (should-term :scrollback '("some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text")
+ :display '("⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text")
+ :cursor '(6 . 10))
+ (output "\n\e+0some text")
+ (should-term :scrollback '("some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├")
+ :display '("some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├")
+ :cursor '(6 . 10))
+ (output "\n\e+Bsome text")
+ (should-term :scrollback '("some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text")
+ :display '("some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text")
+ :cursor '(6 . 10))
+ (output "\n\C-osome text")
+ (should-term :scrollback '("some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text")
+ :display '("⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text")
+ :cursor '(6 . 10))
+ (output "\n\e(0some text")
+ (should-term :scrollback '("some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├")
+ :display '("some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├")
+ :cursor '(6 . 10))
+ (output "\n\e(Bsome text")
+ (should-term :scrollback '("some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text")
+ :display '("some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text")
+ :cursor '(6 . 10))
+ (output "\n\e(0+,-.0`abcdefghijklmnopqrstuvwxyz{|}~")
+ (should-term :scrollback '("some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├")
+ :display '("some text"
+ "some text"
+ "⎽⎺└␊ ├␊│├"
+ "some text"
+ "→←↑↓█�▒␉␌␍␊°±░#┘┐┌└┼"
+ "⎺⎻─⎼⎽├┤┴┬│≤≥π≠£•")
+ :cursor '(6 . 17))))
+
+(ert-deftest eat-test-save-and-restore-cursor ()
+ "Test saving and restoring cursor position.
+
+Write plain text and newline to move cursor."
+ (eat--tests-with-term '()
+ (output "foo")
+ (should-term :display '("foo")
+ :cursor '(1 . 4))
+ (output "\e7")
+ (should-term :display '("foo")
+ :cursor '(1 . 4))
+ (output "bar\nfrob")
+ (should-term :display '("foobar"
+ "frob")
+ :cursor '(2 . 5))
+ (output "\e8")
+ (should-term :display '("foobar"
+ "frob")
+ :cursor '(1 . 4))))
+
+
+;;;;; Input Event Tests.
+
+(ert-deftest eat-test-input-character ()
+ "Test character input events.
+
+This includes all events to which `self-insert-command' is bound to by
+default."
+ (eat--tests-with-term '()
+ (dolist (c '(?a ?E ?b ?D ?অ ?আ ?ক ?খ ?ম ?া ?π ?θ ?μ ?Δ))
+ (input-event c)
+ (should (string= (input) (string c))))))
+
+(ert-deftest eat-test-input-character-with-modifier ()
+ "Test character input events with modifiers (`control' and `meta').
+
+This includes all events to which `self-insert-command' is bound to by
+default."
+ (eat--tests-with-term '()
+ (dolist (p '((?\C-\ . "\C-@")
+ (?\C-a . "\C-a")
+ (?\C-E . "\C-e")
+ (?\C-b . "\C-b")
+ (?\C-D . "\C-d")
+ (?\M-x . "\ex")
+ (?\M-O . "\e")
+ (?\M-\[ . "\e")
+ (?\M-C . "\eC")
+ (?\C-\M-U . "\e\C-u")
+ (?\M-ম . "\eম")
+ (?\M-া . "\eা")
+ (?\M-π . "\eπ")
+ (?\M-θ . "\eθ")))
+ (input-event (car p))
+ (should (string= (input) (cdr p))))))
+
+(ert-deftest eat-test-input-special-keys ()
+ "Test special key input events like arrow keys, backspace, etc."
+ (eat--tests-with-term '()
+ (cl-labels ((check (alist)
+ (dolist (p alist)
+ (input-event (car p))
+ (should (string= (input) (cdr p))))))
+ (check '((backspace . "\C-?")
+ (C-backspace . "\C-h")
+ (left . "\e[D")
+ (C-right . "\e[1;5C")
+ (M-up . "\e[1;3A")
+ (S-down . "\e[1;2B")
+ (C-M-insert . "\e[2;7~")
+ (C-S-delete . "\e[3;6~")
+ (M-S-deletechar . "\e[3;4~")
+ (C-M-S-home . "\e[1;8H")
+ (C-S-end . "\e[1;6F")
+ (M-S-prior . "\e[5;4~")
+ (next . "\e[6~")))
+ (output "\e[?1h")
+ (check '((left . "\eOD")
+ (up . "\eOA")
+ (C-right . "\e[1;5C")
+ (M-up . "\e[1;3A")
+ (S-down . "\e[1;2B")))
+ (output "\e[?1l")
+ (check '((up . "\e[A"))))))
+
+(ert-deftest eat-test-input-focus-events ()
+ "Test focus events."
+ (eat--tests-with-term '()
+ (let ((focus-event-enabled nil))
+ (setf (eat-term-grab-focus-events-function (terminal))
+ (lambda (term value)
+ (should (eq term (terminal)))
+ (setq focus-event-enabled value)))
+ (cl-labels ((check (alist)
+ (dolist (p alist)
+ (input-event (car p))
+ (should (string= (input) (cdr p))))))
+ (check '(((eat-focus-in) . "")
+ ((eat-focus-out) . "")))
+ (output "\e[?1004h")
+ ;; The mode is MUST BE t, not non-nil.
+ (should (eq focus-event-enabled t))
+ (check '(((eat-focus-in) . "\e[I")
+ ((eat-focus-out) . "\e[O")))
+ (output "\e[?1004l")
+ (should (eq focus-event-enabled nil))
+ (check '(((eat-focus-in) . "")
+ ((eat-focus-out) . "")))))))
+
+(ert-deftest eat-test-input-mouse-events ()
+ "Test focus events."
+ (eat--tests-with-term '(:width 200 :height 200)
+ (cl-letf* ((mouse-mode nil)
+ ((eat-term-grab-mouse-function (terminal))
+ (lambda (term mouse)
+ (should (eq term (terminal)))
+ (setq mouse-mode mouse)))
+ ((symbol-function #'posn-col-row)
+ (lambda (posn &optional _use-window)
+ (nth 6 posn))))
+ (cl-labels ((check (alist)
+ (dolist (p alist)
+ (input-event (car p) (cadr p))
+ (should (string= (input) (caddr p)))))
+ (make-posn (&key (window (selected-window))
+ pos-or-area (timestamp 0)
+ (object (point-min)) text-pos
+ (col 0) (row 0) (x col) (y row)
+ image (dx x) (dy y) (width 1)
+ (height 1))
+ `( ,window ,pos-or-area (,x . ,y) ,timestamp
+ ,object ,text-pos (,col . ,row) ,image
+ (,dx . ,dy) (,width . ,height))))
+
+ ;; Default mouse event encoding.
+ ;; Mouse mode disabled.
+ (check
+ `(((mouse-1 ,(make-posn)) nil "")
+ ((mouse-2 ,(make-posn)) ,(make-posn) "")
+ ((mouse-3 ,(make-posn :col 8 :row 3))
+ ,(make-posn :col 1 :row 1) "")
+ ((mouse-4 ,(make-posn :col 13 :row 4))
+ ,(make-posn :col 5 :row 1) "")
+ ((mouse-movement ,(make-posn :col 90 :row 40))
+ ,(make-posn :col 82 :row 33) "")))
+
+ ;; X10 mouse mode.
+ (output "\e[?9h")
+ (should (eq mouse-mode :click))
+ (check
+ `(((mouse-1 ,(make-posn)) nil "\e[M !!")
+ ((mouse-2 ,(make-posn)) ,(make-posn) "\e[M!!!")
+ ((mouse-3 ,(make-posn :col 10 :row 5))
+ ,(make-posn :col 6 :row 3) "\e[M\"%#")
+ ;; Position out of range of default mouse event encoding.
+ ((mouse-1 ,(make-posn :col 95 :row 5)) nil "")
+ ;; Position not inside terminal.
+ ((mouse-2 ,(make-posn :col 94 :row 5))
+ ,(make-posn :col 96 :row 5) "")
+ ((mouse-2 ,(make-posn :col 95 :row 4))
+ ,(make-posn :col 96 :row 8) "")
+ ((mouse-4 ,(make-posn :col 24 :row 9))
+ ,(make-posn :col 8 :row 4) "")
+ ((mouse-movement ,(make-posn :col 30 :row 45))
+ ,(make-posn :col 28 :row 42) "")))
+
+ ;; Normal mouse mode.
+ (output "\e[?1000h")
+ (should (eq mouse-mode :modifier-click))
+ (check
+ `(((down-mouse-1 ,(make-posn)) nil "\e[M !!")
+ ((mouse-1 ,(make-posn)) nil "\e[M#!!")
+ ((down-mouse-2 ,(make-posn)) ,(make-posn) "\e[M!!!")
+ ((mouse-2 ,(make-posn)) ,(make-posn) "\e[M#!!")
+ ((down-mouse-3 ,(make-posn :col 10 :row 5))
+ ,(make-posn) "\e[M\"+&")
+ ;; Note that, although the mouse position has changed, it's
+ ;; still unmoved relative to the reference position, so the
+ ;; mouse position inputted should be same.
+ ((drag-mouse-3 ,(make-posn :col 10 :row 5)
+ ,(make-posn :col 20 :row 50))
+ ,(make-posn :col 10 :row 45) "\e[M#+&")
+ ((mouse-4 ,(make-posn :col 33 :row 10))
+ ,(make-posn :col 30 :row 5) "\e[M\`$&")
+ ((C-mouse-5 ,(make-posn :col 4250 :row 3145))
+ ,(make-posn :col 4242 :row 3141) "\e[Mq)%")
+ ((M-mouse-6 ,(make-posn :col 425 :row 314))
+ ,(make-posn :col 424 :row 314) "\e[Mj\"!")
+ ((S-mouse-7 ,(make-posn :col 85 :row 20))
+ ,(make-posn :col 32 :row 16) "\e[MgV%")
+ ((C-M-down-mouse-1 ,(make-posn :col 58 :row 32))
+ ,(make-posn :col 23 :row 31) "\e[M8D\"")
+ ((C-M-mouse-1 ,(make-posn :col 58 :row 32))
+ ,(make-posn :col 23 :row 31) "\e[M;D\"")
+ ((C-S-down-mouse-2 ,(make-posn :col 40 :row 39))
+ ,(make-posn :col 33 :row 13) "\e[M5(;")
+ ;; Modifier changed!
+ ((M-S-mouse-2 ,(make-posn :col 40 :row 39))
+ ,(make-posn :col 33 :row 13) "\e[M/(;")
+ ((C-M-S-down-mouse-3 ,(make-posn :col 48 :row 29))
+ ,(make-posn :col 39 :row 23) "\e[M>*'")
+ ;; Everything changed!
+ ((mouse-3 ,(make-posn :col 92 :row 83))
+ ,(make-posn :col 10 :row 8) "\e[M#sl")
+ ;; Button out of range of default mouse event encoding.
+ ((mouse-8 ,(make-posn :col 1 :row 1))
+ ,(make-posn :col 1 :row 1) "")
+ ((mouse-movement ,(make-posn :col 49 :row 85))
+ ,(make-posn :col 45 :row 82) "")))
+
+ ;; Button event mouse mode.
+ (output "\e[?1002h")
+ (should (eq mouse-mode :drag))
+ (check
+ `(((mouse-movement ,(make-posn :col 84 :row 10))
+ ,(make-posn :col 48 :row 8) "")
+ ((down-mouse-1 ,(make-posn :col 44 :row 88))
+ ,(make-posn :col 44 :row 88) "\e[M !!")
+ ((mouse-movement ,(make-posn :col 48 :row 1))
+ ,(make-posn :col 19 :row 0) "\e[M@>\"")
+ ((down-mouse-2 ,(make-posn :col 29 :row 21))
+ ,(make-posn :col 18 :row 8) "\e[M!,.")
+ ((mouse-movement ,(make-posn :col 93 :row 54))
+ ,(make-posn :col 29 :row 38) "\e[M@a1")
+ ((down-mouse-3 ,(make-posn :col 92 :row 63))
+ ,(make-posn :col 32 :row 38) "\e[M\"]:")
+ ((mouse-movement ,(make-posn :col 8 :row 92))
+ ,(make-posn :col 3 :row 34) "\e[M@&[")
+ ((mouse-1 ,(make-posn :col 93 :row 21))
+ ,(make-posn :col 0 :row 0) "\e[M#~6")
+ ((mouse-movement ,(make-posn :col 29 :row 74))
+ ,(make-posn :col 7 :row 64) "\e[MA7+")
+ ((mouse-2 ,(make-posn :col 28 :row 92))
+ ,(make-posn :col 23 :row 29) "\e[M#&`")
+ ((mouse-movement ,(make-posn :col 75 :row 36))
+ ,(make-posn :col 75 :row 19) "\e[MB!2")
+ ((mouse-3 ,(make-posn :col 36 :row 76))
+ ,(make-posn :col 17 :row 67) "\e[M#4*")
+ ((mouse-movement ,(make-posn :col 94 :row 58))
+ ,(make-posn :col 54 :row 28) "")))
+
+ ;; All event mouse mode.
+ (output "\e[?1003h")
+ (should (eq mouse-mode :all))
+ (check
+ `(((mouse-movement ,(make-posn :col 28 :row 38))
+ ,(make-posn :col 4 :row 2) "\e[MC9E")
+ ((mouse-movement ,(make-posn :col 49 :row 85))
+ ,(make-posn :col 45 :row 82) "\e[MC%$")
+ ((mouse-movement ,(make-posn :col 84 :row 10))
+ ,(make-posn :col 48 :row 8) "\e[MCE#")
+ ((down-mouse-1 ,(make-posn :col 44 :row 88))
+ ,(make-posn :col 44 :row 88) "\e[M !!")
+ ((mouse-movement ,(make-posn :col 48 :row 1))
+ ,(make-posn :col 19 :row 0) "\e[M@>\"")
+ ((down-mouse-2 ,(make-posn :col 29 :row 21))
+ ,(make-posn :col 18 :row 8) "\e[M!,.")
+ ((mouse-movement ,(make-posn :col 93 :row 54))
+ ,(make-posn :col 29 :row 38) "\e[M@a1")
+ ((down-mouse-3 ,(make-posn :col 92 :row 63))
+ ,(make-posn :col 32 :row 38) "\e[M\"]:")
+ ((mouse-movement ,(make-posn :col 8 :row 92))
+ ,(make-posn :col 3 :row 34) "\e[M@&[")
+ ((mouse-1 ,(make-posn :col 93 :row 21))
+ ,(make-posn :col 0 :row 0) "\e[M#~6")
+ ((mouse-movement ,(make-posn :col 29 :row 74))
+ ,(make-posn :col 7 :row 64) "\e[MA7+")
+ ((mouse-2 ,(make-posn :col 28 :row 92))
+ ,(make-posn :col 23 :row 29) "\e[M#&`")
+ ((mouse-movement ,(make-posn :col 75 :row 36))
+ ,(make-posn :col 75 :row 19) "\e[MB!2")
+ ((mouse-3 ,(make-posn :col 36 :row 76))
+ ,(make-posn :col 17 :row 67) "\e[M#4*")
+ ((mouse-movement ,(make-posn :col 94 :row 58))
+ ,(make-posn :col 54 :row 28) "\e[MCI?")
+ ((mouse-movement ,(make-posn :col 97 :row 79))
+ ,(make-posn :col 46 :row 69) "\e[MCT+")
+ ((mouse-movement ,(make-posn :col 34 :row 76))
+ ,(make-posn :col 28 :row 29) "\e[MC'P")))
+
+ ;; Mouse mode disabled.
+ (output "\e[?1003l")
+ (should (eq mouse-mode nil))
+ (check
+ `(((mouse-1 ,(make-posn)) nil "")
+ ((mouse-2 ,(make-posn)) ,(make-posn) "")
+ ((mouse-3 ,(make-posn :col 8 :row 3))
+ ,(make-posn :col 1 :row 1) "")
+ ((mouse-4 ,(make-posn :col 13 :row 4))
+ ,(make-posn :col 5 :row 1) "")
+ ((mouse-movement ,(make-posn :col 90 :row 40))
+ ,(make-posn :col 82 :row 33) "")))
+
+ ;; SGR mouse event encoding.
+ (output "\e[?1006h")
+
+ ;; Mouse mode disabled.
+ (check
+ `(((mouse-1 ,(make-posn)) nil "")
+ ((mouse-2 ,(make-posn)) ,(make-posn) "")
+ ((mouse-3 ,(make-posn :col 8 :row 3))
+ ,(make-posn :col 1 :row 1) "")
+ ((mouse-4 ,(make-posn :col 13 :row 4))
+ ,(make-posn :col 5 :row 1) "")
+ ((mouse-movement ,(make-posn :col 90 :row 40))
+ ,(make-posn :col 82 :row 33) "")))
+
+ ;; X10 mouse mode.
+ (output "\e[?9h")
+ (should (eq mouse-mode :click))
+ (check
+ `(((mouse-1 ,(make-posn)) nil "\e[<0;1;1M")
+ ((mouse-2 ,(make-posn)) ,(make-posn) "\e[<1;1;1M")
+ ((mouse-3 ,(make-posn :col 10 :row 5))
+ ,(make-posn :col 6 :row 3) "\e[<2;5;3M")
+ ((mouse-1 ,(make-posn :col 95 :row 5)) nil "\e[<0;96;6M")
+ ;; Position not inside terminal.
+ ((mouse-2 ,(make-posn :col 94 :row 5))
+ ,(make-posn :col 96 :row 5) "")
+ ((mouse-2 ,(make-posn :col 95 :row 4))
+ ,(make-posn :col 96 :row 8) "")
+ ((mouse-4 ,(make-posn :col 24 :row 9))
+ ,(make-posn :col 8 :row 4) "")
+ ((mouse-movement ,(make-posn :col 30 :row 45))
+ ,(make-posn :col 28 :row 42) "")))
+
+ ;; Normal mouse mode.
+ (output "\e[?1000h")
+ (should (eq mouse-mode :modifier-click))
+ (check
+ `(((down-mouse-1 ,(make-posn)) nil "\e[<0;1;1M")
+ ((mouse-1 ,(make-posn)) nil "\e[<0;1;1m")
+ ((down-mouse-2 ,(make-posn)) ,(make-posn) "\e[<1;1;1M")
+ ((mouse-2 ,(make-posn)) ,(make-posn) "\e[<1;1;1m")
+ ((down-mouse-3 ,(make-posn :col 10 :row 5))
+ ,(make-posn) "\e[<2;11;6M")
+ ;; Note that, although the mouse position has changed, it's
+ ;; still unmoved relative to the reference position, so the
+ ;; mouse position inputted should be same.
+ ((drag-mouse-3 ,(make-posn :col 10 :row 5)
+ ,(make-posn :col 20 :row 50))
+ ,(make-posn :col 10 :row 45) "\e[<2;11;6m")
+ ((mouse-4 ,(make-posn :col 33 :row 10))
+ ,(make-posn :col 30 :row 5) "\e[<64;4;6M")
+ ((C-mouse-5 ,(make-posn :col 4250 :row 3145))
+ ,(make-posn :col 4242 :row 3141) "\e[<81;9;5M")
+ ((M-mouse-6 ,(make-posn :col 425 :row 314))
+ ,(make-posn :col 424 :row 314) "\e[<74;2;1M")
+ ((S-mouse-7 ,(make-posn :col 85 :row 20))
+ ,(make-posn :col 32 :row 16) "\e[<71;54;5M")
+ ((mouse-8 ,(make-posn :col 91 :row 92))
+ ,(make-posn :col 75 :row 18) "\e[<128;17;75M")
+ ((mouse-9 ,(make-posn :col 71 :row 81))
+ ,(make-posn :col 37 :row 72) "\e[<129;35;10M")
+ ((mouse-10 ,(make-posn :col 38 :row 92))
+ ,(make-posn :col 29 :row 90) "\e[<130;10;3M")
+ ((mouse-11 ,(make-posn :col 29 :row 14))
+ ,(make-posn :col 28 :row 8) "\e[<131;2;7M")
+ ((C-M-down-mouse-1 ,(make-posn :col 58 :row 32))
+ ,(make-posn :col 23 :row 31) "\e[<24;36;2M")
+ ((C-M-mouse-1 ,(make-posn :col 58 :row 32))
+ ,(make-posn :col 23 :row 31) "\e[<24;36;2m")
+ ((C-S-down-mouse-2 ,(make-posn :col 40 :row 39))
+ ,(make-posn :col 33 :row 13) "\e[<21;8;27M")
+ ;; Modifier changed!
+ ((M-S-mouse-2 ,(make-posn :col 40 :row 39))
+ ,(make-posn :col 33 :row 13) "\e[<13;8;27m")
+ ((C-M-S-down-mouse-3 ,(make-posn :col 48 :row 29))
+ ,(make-posn :col 39 :row 23) "\e[<30;10;7M")
+ ;; Everything changed!
+ ((mouse-3 ,(make-posn :col 92 :row 83))
+ ,(make-posn :col 10 :row 8) "\e[<2;83;76m")
+ ((mouse-movement ,(make-posn :col 49 :row 85))
+ ,(make-posn :col 45 :row 82) "")))
+
+ ;; Button event mouse mode.
+ (output "\e[?1002h")
+ (should (eq mouse-mode :drag))
+ (check
+ `(((mouse-movement ,(make-posn :col 84 :row 10))
+ ,(make-posn :col 48 :row 8) "")
+ ((down-mouse-1 ,(make-posn :col 44 :row 88))
+ ,(make-posn :col 44 :row 88) "\e[<0;1;1M")
+ ((mouse-movement ,(make-posn :col 48 :row 1))
+ ,(make-posn :col 19 :row 0) "\e[<32;30;2M")
+ ((down-mouse-2 ,(make-posn :col 29 :row 21))
+ ,(make-posn :col 18 :row 8) "\e[<1;12;14M")
+ ((mouse-movement ,(make-posn :col 93 :row 54))
+ ,(make-posn :col 29 :row 38) "\e[<32;65;17M")
+ ((down-mouse-3 ,(make-posn :col 92 :row 63))
+ ,(make-posn :col 32 :row 38) "\e[<2;61;26M")
+ ((mouse-movement ,(make-posn :col 8 :row 92))
+ ,(make-posn :col 3 :row 34) "\e[<32;6;59M")
+ ((mouse-1 ,(make-posn :col 93 :row 21))
+ ,(make-posn :col 0 :row 0) "\e[<0;94;22m")
+ ((mouse-movement ,(make-posn :col 29 :row 74))
+ ,(make-posn :col 7 :row 64) "\e[<33;23;11M")
+ ((mouse-2 ,(make-posn :col 28 :row 92))
+ ,(make-posn :col 23 :row 29) "\e[<1;6;64m")
+ ((mouse-movement ,(make-posn :col 75 :row 36))
+ ,(make-posn :col 75 :row 19) "\e[<34;1;18M")
+ ((mouse-3 ,(make-posn :col 36 :row 76))
+ ,(make-posn :col 17 :row 67) "\e[<2;20;10m")
+ ((mouse-movement ,(make-posn :col 94 :row 58))
+ ,(make-posn :col 54 :row 28) "")))
+
+ ;; All event mouse mode.
+ (output "\e[?1003h")
+ (should (eq mouse-mode :all))
+ (check
+ `(((mouse-movement ,(make-posn :col 28 :row 38))
+ ,(make-posn :col 4 :row 2) "\e[<35;25;37M")
+ ((mouse-movement ,(make-posn :col 49 :row 85))
+ ,(make-posn :col 45 :row 82) "\e[<35;5;4M")
+ ((mouse-movement ,(make-posn :col 84 :row 10))
+ ,(make-posn :col 48 :row 8) "\e[<35;37;3M")
+ ((down-mouse-1 ,(make-posn :col 44 :row 88))
+ ,(make-posn :col 44 :row 88) "\e[<0;1;1M")
+ ((mouse-movement ,(make-posn :col 48 :row 1))
+ ,(make-posn :col 19 :row 0) "\e[<32;30;2M")
+ ((down-mouse-2 ,(make-posn :col 29 :row 21))
+ ,(make-posn :col 18 :row 8) "\e[<1;12;14M")
+ ((mouse-movement ,(make-posn :col 93 :row 54))
+ ,(make-posn :col 29 :row 38) "\e[<32;65;17M")
+ ((down-mouse-3 ,(make-posn :col 92 :row 63))
+ ,(make-posn :col 32 :row 38) "\e[<2;61;26M")
+ ((mouse-movement ,(make-posn :col 8 :row 92))
+ ,(make-posn :col 3 :row 34) "\e[<32;6;59M")
+ ((mouse-1 ,(make-posn :col 93 :row 21))
+ ,(make-posn :col 0 :row 0) "\e[<0;94;22m")
+ ((mouse-movement ,(make-posn :col 29 :row 74))
+ ,(make-posn :col 7 :row 64) "\e[<33;23;11M")
+ ((mouse-2 ,(make-posn :col 28 :row 92))
+ ,(make-posn :col 23 :row 29) "\e[<1;6;64m")
+ ((mouse-movement ,(make-posn :col 75 :row 36))
+ ,(make-posn :col 75 :row 19) "\e[<34;1;18M")
+ ((mouse-3 ,(make-posn :col 36 :row 76))
+ ,(make-posn :col 17 :row 67) "\e[<2;20;10m")
+ ((mouse-movement ,(make-posn :col 94 :row 58))
+ ,(make-posn :col 54 :row 28) "\e[<35;41;31M")
+ ((mouse-movement ,(make-posn :col 97 :row 79))
+ ,(make-posn :col 46 :row 69) "\e[<35;52;11M")
+ ((mouse-movement ,(make-posn :col 34 :row 76))
+ ,(make-posn :col 28 :row 29) "\e[<35;7;48M")))
+
+ ;; Default mouse encoding.
+ (output "\e[?1006l")
+ (check
+ `(((mouse-movement ,(make-posn :col 28 :row 38))
+ ,(make-posn :col 4 :row 2) "\e[MC9E")
+ ((mouse-movement ,(make-posn :col 49 :row 85))
+ ,(make-posn :col 45 :row 82) "\e[MC%$")
+ ((mouse-movement ,(make-posn :col 84 :row 10))
+ ,(make-posn :col 48 :row 8) "\e[MCE#")
+ ((down-mouse-1 ,(make-posn :col 44 :row 88))
+ ,(make-posn :col 44 :row 88) "\e[M !!")
+ ((mouse-movement ,(make-posn :col 48 :row 1))
+ ,(make-posn :col 19 :row 0) "\e[M@>\"")
+ ((down-mouse-2 ,(make-posn :col 29 :row 21))
+ ,(make-posn :col 18 :row 8) "\e[M!,.")
+ ((mouse-movement ,(make-posn :col 93 :row 54))
+ ,(make-posn :col 29 :row 38) "\e[M@a1")
+ ((down-mouse-3 ,(make-posn :col 92 :row 63))
+ ,(make-posn :col 32 :row 38) "\e[M\"]:")
+ ((mouse-movement ,(make-posn :col 8 :row 92))
+ ,(make-posn :col 3 :row 34) "\e[M@&[")
+ ((mouse-1 ,(make-posn :col 93 :row 21))
+ ,(make-posn :col 0 :row 0) "\e[M#~6")
+ ((mouse-movement ,(make-posn :col 29 :row 74))
+ ,(make-posn :col 7 :row 64) "\e[MA7+")
+ ((mouse-2 ,(make-posn :col 28 :row 92))
+ ,(make-posn :col 23 :row 29) "\e[M#&`")
+ ((mouse-movement ,(make-posn :col 75 :row 36))
+ ,(make-posn :col 75 :row 19) "\e[MB!2")
+ ((mouse-3 ,(make-posn :col 36 :row 76))
+ ,(make-posn :col 17 :row 67) "\e[M#4*")
+ ((mouse-movement ,(make-posn :col 94 :row 58))
+ ,(make-posn :col 54 :row 28) "\e[MCI?")
+ ((mouse-movement ,(make-posn :col 97 :row 79))
+ ,(make-posn :col 46 :row 69) "\e[MCT+")
+ ((mouse-movement ,(make-posn :col 34 :row 76))
+ ,(make-posn :col 28 :row 29) "\e[MC'P")))
+
+ ;; Mouse mode disabled.
+ ;; Disabling any mouse mode, either enabled or not, should
+ ;; disable mouse.
+ (output "\e[?9l")
+ (should (eq mouse-mode nil))
+ (check
+ `(((mouse-1 ,(make-posn)) nil "")
+ ((mouse-2 ,(make-posn)) ,(make-posn) "")
+ ((mouse-3 ,(make-posn :col 8 :row 3))
+ ,(make-posn :col 1 :row 1) "")
+ ((mouse-4 ,(make-posn :col 13 :row 4))
+ ,(make-posn :col 5 :row 1) "")
+ ((mouse-movement ,(make-posn :col 90 :row 40))
+ ,(make-posn :col 82 :row 33) "")))))))
+
+(provide 'eat-tests)
+;;; eat-tests.el ends here
diff --git a/eat.el b/eat.el
new file mode 100644
index 0000000000..32f387ff3f
--- /dev/null
+++ b/eat.el
@@ -0,0 +1,6662 @@
+;;; eat.el --- Emulate A Terminal, in a region, a buffer and in Eshell -*-
lexical-binding: t; -*-
+
+;; Copyright (C) 2022 Akib Azmain Turja.
+
+;; Author: Akib Azmain Turja <akib@disroot.org>
+;; Created: 2022-08-15
+;; Version: 0.1snapshot
+;; Package-Requires: ((emacs "28.1"))
+;; Keywords: terminals processes
+;; Homepage: https://codeberg.org/akib/emacs-eat
+
+;; This file is not part of GNU Emacs.
+
+;; This file 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, 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.
+
+;; For a full copy of the GNU General Public License
+;; see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Eat's name self-explainary, it stands for "Emulate A Terminal".
+;; Eat is a terminal emulator. It can run most (if not all)
+;; full-screen terminal programs, including Emacs.
+
+;; It is pretty fast, more than three times faster than Term, despite
+;; being implemented entirely in Emacs Lisp. So fast that you can
+;; comfortably run Emacs inside Eat, or even use your Emacs as a
+;; terminal multiplexer.
+
+;; It has many feature that other Emacs terminal emulator still don't
+;; have, for example complete mouse support.
+
+;; It flickers less than other Emacs terminal emulator, so you get
+;; more performance and a smooth experience.
+
+;; To start Eat, run M-x eat. Eat has three keybinding modes:
+
+;; * "emacs" mode: No special keybinding, except the following:
+
+;; * `C-c' `C-s': Switch to semi-char mode.
+;; * `C-c' `C-j': Switch to char mode.
+;; * `C-c' `C-k': Kill process.
+
+;; * "semi-char" mode: Most keys are bound to send the key to the
+;; terminal, except the following keys: `C-\', `C-c', `C-x',
+;; `C-g', `C-h', `C-M-c', `C-u', `M-x', `M-:', `M-!', `M-&'. The
+;; following special keybinding are available:
+
+;; * `C-q': Send next key to the terminal.
+;; * `C-y': Like `yank', but send the text to the terminal.
+;; * `M-y': Like `yank-pop', but send the text to the terminal.
+;; * `C-c' `C-k': Kill process.
+
+;; * "char" mode: All supported keys are bound to send the key to
+;; the terminal, except `C-M-m' or `M-RET', which is bound to
+;; switch to semi-char mode.
+
+;; If you like Eshell, then there is a good news for you. Eat
+;; integrates with Eshell. Eat has two global minor modes for Eshell:
+
+;; * `eat-eshell-visual-command-mode': Run visual commands with Eat
+;; instead of Term.
+
+;; * `eat-eshell-mode': Run Eat inside Eshell. After enabling this,
+;; you can run full-screen terminal programs directly in Eshell.
+;; You have three keybinding modes here too, except that `C-c'
+;; `C-k' is not special (i.e. not bound by Eat) in "emacs" mode
+;; and "line" mode.
+
+;;; Code:
+
+(require 'cl-lib)
+(require 'ansi-color)
+(require 'shell)
+
+
+;;;; User Options.
+
+(defgroup eat nil
+ "Emulate A Terminal."
+ :group 'processes
+ :group 'terminals
+ :link '(url-link "https://codeberg.org/akib/emacs-eat"))
+
+(defgroup eat-term nil
+ "Eat terminal emulator."
+ :group 'eat)
+
+(defgroup eat-ui nil
+ "Eat user interface."
+ :group 'eat)
+
+(defgroup eat-eshell nil
+ "Eat Eshell integration."
+ :group 'eat)
+
+(defcustom eat-buffer-name "*eat*"
+ "The basename used for Eat buffers.
+
+This is the default name used when running Eat."
+ :type 'string
+ :group 'eat-ui)
+
+(defcustom eat-kill-buffer-on-exit nil
+ "Non-nil means automatically kill Eat buffer when process exits."
+ :type 'boolean
+ :group 'eat-ui)
+
+(defcustom eat-term-scrollback-size 131072 ; 128 K
+ "Size of scrollback area in characters. nil means unlimited."
+ :type '(choice integer (const nil))
+ :group 'eat-term
+ :group 'eat-ui)
+
+(defcustom eat-enable-kill-from-terminal t
+ "Non-nil means allow terminal program to add text to `kill-ring'.
+
+When non-nil, terminal program can send special escape sequence to add
+some text to `kill-ring'."
+ :type 'boolean
+ :group 'eat-ui
+ :group 'eat-eshell)
+
+(defcustom eat-enable-yank-to-terminal nil
+ "Non-nil means allow terminal program to get text from `kill-ring'.
+
+When non-nil, terminal program can get killed text from `kill-ring'.
+This is left disabled for security reasons."
+ :type 'boolean
+ :group 'eat-ui
+ :group 'eat-eshell)
+
+(defconst eat--cursor-type-value-type
+ (let ((cur-type
+ '(choice
+ (const :tag "Frame default" t)
+ (const :tag "Filled box" box)
+ (cons :tag "Box with specified size" (const box) integer)
+ (const :tag "Hollow cursor" hollow)
+ (const :tag "Vertical bar" bar)
+ (cons :tag "Vertical bar with specified height" (const bar)
+ integer)
+ (const :tag "Horizontal bar" hbar)
+ (cons :tag "Horizontal bar with specified width"
+ (const hbar) integer)
+ (const :tag "None " nil))))
+ `(list
+ ,cur-type
+ (choice
+ (const :tag "No blinking" nil)
+ (number :tag "Blinking frequency"))
+ ,cur-type))
+ "Custom type specification for Eat's cursor type variables.")
+
+(defcustom eat-default-cursor-type
+ `(,(default-value 'cursor-type) nil nil)
+ "Cursor to use in Eat buffer.
+
+The value is a list of form (CURSOR-ON BLINKING-FREQUENCY CURSOR-OFF).
+
+When the cursor is on, CURSOR-ON is used as `cursor-type', which see.
+BLINKING-FREQUENCY is the blinking frequency of cursor's blinking.
+When the cursor is off, CURSOR-OFF is used as `cursor-type'. This
+should be nil when cursor is not blinking."
+ :type eat--cursor-type-value-type
+ :group 'eat-ui
+ :group 'eat-ehell)
+
+(defcustom eat-invisible-cursor-type '(nil nil nil)
+ "Invisible cursor to use in Eat buffer.
+
+The value is a list of form (CURSOR-ON BLINKING-FREQUENCY CURSOR-OFF).
+
+When the cursor is on, CURSOR-ON is used as `cursor-type', which see.
+BLINKING-FREQUENCY is the blinking frequency of cursor's blinking.
+When the cursor is off, CURSOR-OFF is used as `cursor-type'. This
+should be nil when cursor is not blinking."
+ :type eat--cursor-type-value-type
+ :group 'eat-ui
+ :group 'eat-ehell)
+
+(defcustom eat-very-visible-cursor-type
+ `(,(default-value 'cursor-type) 2 hollow)
+ "Very visible cursor to use in Eat buffer.
+
+When the cursor is invisible, the car of the value is used as
+`cursor-type', which see. The cdr of the value, when non-nil, is the
+blinking frequency of cursor."
+ :type eat--cursor-type-value-type
+ :group 'eat-ui
+ :group 'eat-ehell)
+
+(defcustom eat-minimum-latency 0.008
+ "Minimum display latency in seconds.
+
+Lowering it too much may cause (or increase) flickering and decrease
+performance due to too many redisplays. Increasing it too much will
+cause the terminal to feel less responsive. Try to increase this
+value if the terminal flickers."
+ :type 'number
+ :group 'eat-ui
+ :group 'eat-ehell)
+
+(defcustom eat-maximum-latency 0.033
+ "Minimum display latency in seconds.
+
+Increasing it too much may make the terminal feel less responsive in
+case of huge burst of output. Try to increase this value if the
+terminal flickers. Try to lower the value if the terminal feels less
+responsive."
+ :type 'number
+ :group 'eat-ui
+ :group 'eat-ehell)
+
+(defcustom eat-term-name #'eat-term-get-suitable-term-name
+ "Value for the `TERM' environment variable.
+
+The value can also be a function. In that case, the function is
+called without any argument and the return value is used as the value.
+For example, this can set to `eat-term-get-suitable-term-name' to set
+the value according to the number of colors supported by the current
+display.
+
+This value is used by terminal programs to identify the terminal."
+ :type '(choice
+ (string :tag "Value")
+ (const :tag "Automatic" eat-term-get-suitable-term-name)
+ (function :tag "Function"))
+ :group 'eat-term)
+
+;; Upgrading Eat causes `eat-term-terminfo-directory' to be outdated,
+;; so update it if not modified by user (or something else).
+(defvar eat--install-path nil
+ "Path to directory where Eat is installed.")
+
+(defvar eat-term-terminfo-directory)
+(let ((old-install-path eat--install-path))
+ (setq eat--install-path
+ (copy-sequence
+ (file-name-directory (or load-file-name
+ buffer-file-name))))
+
+ (defcustom eat-term-terminfo-directory eat--install-path
+ "Directory where require terminfo databases can be found.
+
+This value is used by terminal programs to find the terminfo databases
+that describe the capabilities of the terminal."
+ :type 'directory
+ :group 'eat-term)
+
+ (when (eq eat-term-terminfo-directory old-install-path)
+ (setq eat-term-terminfo-directory eat--install-path)))
+
+(defcustom eat-term-inside-emacs (format "%s,eat" emacs-version)
+ "Value for the `INSIDE_EMACS' environment variable."
+ :type 'string
+ :group 'eat-term)
+
+(defcustom eat-enable-blinking-text nil
+ "Non-nil means enable blinking of text with blink attribute.
+
+When non-nil, enable `eat-blink-mode' to enable blinking of text with
+blink attribute by default. You manually toggle `eat-blink-mode' to
+toggle this behavior buffer-locally."
+ :type 'boolean
+ :group 'eat-ui
+ :group 'eat-eshell)
+
+(defcustom eat-slow-blink-frequency 2
+ "Frequency of blinking of slowly text.
+
+This has an effect only if `eat-blink-mode' is enabled."
+ :type 'number
+ :group 'eat-ui)
+
+(defcustom eat-fast-blink-frequency 3
+ "Frequency of blinking of rapidly text.
+
+This has an effect only if `eat-blink-mode' is enabled."
+ :type 'number
+ :group 'eat-ui)
+
+(defcustom eat-enable-alternative-display t
+ "Non-nil means enable alternative display.
+
+Full screen programs often use alternative display to keep old
+contents on display unaltered."
+ :type 'boolean
+ :group 'eat-term)
+
+(defcustom eat-enable-mouse t
+ "Non-nil means enable mouse support.
+
+When non-nil, terminal programs can receive mouse events from Emacs."
+ :type 'boolean
+ :group 'eat-ui)
+
+(defcustom eat-input-chunk-size 1024
+ "Maximum size of chunk of data send at once.
+
+Long inputs send to Eat processes are broken up into chunks of this
+size.
+
+If your process is choking on big inputs, try lowering the value."
+ :type 'integer
+ :group 'eat-ui)
+
+(defface eat-term-bold '((t :inherit bold))
+ "Face used to render bold text."
+ :group 'eat-term)
+
+(defface eat-term-faint '((t :weight light))
+ "Face used to render faint text."
+ :group 'eat-term)
+
+(defface eat-term-italic '((t :inherit italic))
+ "Face used to render italic text."
+ :group 'eat-term)
+
+(defface eat-term-slow-blink '((t :inverse-video t))
+ "Face used to render slowly blinking text."
+ :group 'eat-term)
+
+(defface eat-term-fast-blink '((t :inverse-video t))
+ "Face used to render rapidly blinking text."
+ :group 'eat-term)
+
+(defface eat-term-color-0
+ '((t :inherit ansi-color-black))
+ "Face used to render black color text."
+ :group 'eat-term)
+
+(put 'eat-term-color-black 'face-alias 'eat-term-color-0)
+
+(defface eat-term-color-1
+ '((t :inherit ansi-color-red))
+ "Face used to render red color text."
+ :group 'eat-term)
+
+(put 'eat-term-color-red 'face-alias 'eat-term-color-1)
+
+(defface eat-term-color-2
+ '((t :inherit ansi-color-green))
+ "Face used to render green color text."
+ :group 'eat-term)
+
+(put 'eat-term-color-green 'face-alias 'eat-term-color-2)
+
+(defface eat-term-color-3
+ '((t :inherit ansi-color-yellow))
+ "Face used to render yellow color text."
+ :group 'eat-term)
+
+(put 'eat-term-color-yellow 'face-alias 'eat-term-color-3)
+
+(defface eat-term-color-4
+ '((t :inherit ansi-color-blue))
+ "Face used to render blue color text."
+ :group 'eat-term)
+
+(put 'eat-term-color-blue 'face-alias 'eat-term-color-4)
+
+(defface eat-term-color-5
+ '((t :inherit ansi-color-magenta))
+ "Face used to render magenta color text."
+ :group 'eat-term)
+
+(put 'eat-term-color-magenta 'face-alias 'eat-term-color-5)
+
+(defface eat-term-color-6
+ '((t :inherit ansi-color-cyan))
+ "Face used to render cyan color text."
+ :group 'eat-term)
+
+(put 'eat-term-color-cyan 'face-alias 'eat-term-color-6)
+
+(defface eat-term-color-7
+ '((t :inherit ansi-color-white))
+ "Face used to render white color text."
+ :group 'eat-term)
+
+(put 'eat-term-color-white 'face-alias 'eat-term-color-7)
+
+(defface eat-term-color-8
+ '((t :inherit ansi-color-bright-black))
+ "Face used to render bright black color text."
+ :group 'eat-term)
+
+(put 'eat-term-color-bright-black 'face-alias 'eat-term-color-8)
+
+(defface eat-term-color-9
+ '((t :inherit ansi-color-bright-red))
+ "Face used to render bright red color text."
+ :group 'eat-term)
+
+(put 'eat-term-color-bright-red 'face-alias 'eat-term-color-9)
+
+(defface eat-term-color-10
+ '((t :inherit ansi-color-bright-green))
+ "Face used to render bright green color text."
+ :group 'eat-term)
+
+(put 'eat-term-color-bright-green 'face-alias 'eat-term-color-10)
+
+(defface eat-term-color-11
+ '((t :inherit ansi-color-bright-yellow))
+ "Face used to render bright yellow color text."
+ :group 'eat-term)
+
+(put 'eat-term-color-bright-yellow 'face-alias 'eat-term-color-11)
+
+(defface eat-term-color-12
+ '((t :inherit ansi-color-bright-blue))
+ "Face used to render bright blue color text."
+ :group 'eat-term)
+
+(put 'eat-term-color-bright-blue 'face-alias 'eat-term-color-12)
+
+(defface eat-term-color-13
+ '((t :inherit ansi-color-bright-magenta))
+ "Face used to render bright magenta color text."
+ :group 'eat-term)
+
+(put 'eat-term-color-bright-magenta 'face-alias 'eat-term-color-13)
+
+(defface eat-term-color-14
+ '((t :inherit ansi-color-bright-cyan))
+ "Face used to render bright cyan color text."
+ :group 'eat-term)
+
+(put 'eat-term-color-bright-cyan 'face-alias 'eat-term-color-14)
+
+(defface eat-term-color-15
+ '((t :inherit ansi-color-bright-white))
+ "Face used to render bright white color text."
+ :group 'eat-term)
+
+(put 'eat-term-color-bright-white 'face-alias 'eat-term-color-15)
+
+(defface eat-term-color-16
+ '((t :foreground "#000000" :background "#000000"))
+ "Face used to render text with 16th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-17
+ '((t :foreground "#00005F" :background "#00005F"))
+ "Face used to render text with 17th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-18
+ '((t :foreground "#000087" :background "#000087"))
+ "Face used to render text with 18th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-19
+ '((t :foreground "#0000AF" :background "#0000AF"))
+ "Face used to render text with 19th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-20
+ '((t :foreground "#0000D7" :background "#0000D7"))
+ "Face used to render text with 20th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-21
+ '((t :foreground "#0000FF" :background "#0000FF"))
+ "Face used to render text with 21th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-22
+ '((t :foreground "#005F00" :background "#005F00"))
+ "Face used to render text with 22th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-23
+ '((t :foreground "#005F5F" :background "#005F5F"))
+ "Face used to render text with 23th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-24
+ '((t :foreground "#005F87" :background "#005F87"))
+ "Face used to render text with 24th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-25
+ '((t :foreground "#005FAF" :background "#005FAF"))
+ "Face used to render text with 25th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-26
+ '((t :foreground "#005FD7" :background "#005FD7"))
+ "Face used to render text with 26th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-27
+ '((t :foreground "#005FFF" :background "#005FFF"))
+ "Face used to render text with 27th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-28
+ '((t :foreground "#008700" :background "#008700"))
+ "Face used to render text with 28th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-29
+ '((t :foreground "#00875F" :background "#00875F"))
+ "Face used to render text with 29th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-30
+ '((t :foreground "#008787" :background "#008787"))
+ "Face used to render text with 30th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-31
+ '((t :foreground "#0087AF" :background "#0087AF"))
+ "Face used to render text with 31th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-32
+ '((t :foreground "#0087D7" :background "#0087D7"))
+ "Face used to render text with 32th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-33
+ '((t :foreground "#0087FF" :background "#0087FF"))
+ "Face used to render text with 33th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-34
+ '((t :foreground "#00AF00" :background "#00AF00"))
+ "Face used to render text with 34th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-35
+ '((t :foreground "#00AF5F" :background "#00AF5F"))
+ "Face used to render text with 35th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-36
+ '((t :foreground "#00AF87" :background "#00AF87"))
+ "Face used to render text with 36th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-37
+ '((t :foreground "#00AFAF" :background "#00AFAF"))
+ "Face used to render text with 37th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-38
+ '((t :foreground "#00AFD7" :background "#00AFD7"))
+ "Face used to render text with 38th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-39
+ '((t :foreground "#00AFFF" :background "#00AFFF"))
+ "Face used to render text with 39th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-40
+ '((t :foreground "#00D700" :background "#00D700"))
+ "Face used to render text with 40th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-41
+ '((t :foreground "#00D75F" :background "#00D75F"))
+ "Face used to render text with 41th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-42
+ '((t :foreground "#00D787" :background "#00D787"))
+ "Face used to render text with 42th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-43
+ '((t :foreground "#00D7AF" :background "#00D7AF"))
+ "Face used to render text with 43th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-44
+ '((t :foreground "#00D7D7" :background "#00D7D7"))
+ "Face used to render text with 44th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-45
+ '((t :foreground "#00D7FF" :background "#00D7FF"))
+ "Face used to render text with 45th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-46
+ '((t :foreground "#00FF00" :background "#00FF00"))
+ "Face used to render text with 46th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-47
+ '((t :foreground "#00FF5F" :background "#00FF5F"))
+ "Face used to render text with 47th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-48
+ '((t :foreground "#00FF87" :background "#00FF87"))
+ "Face used to render text with 48th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-49
+ '((t :foreground "#00FFAF" :background "#00FFAF"))
+ "Face used to render text with 49th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-50
+ '((t :foreground "#00FFD7" :background "#00FFD7"))
+ "Face used to render text with 50th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-51
+ '((t :foreground "#00FFFF" :background "#00FFFF"))
+ "Face used to render text with 51th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-52
+ '((t :foreground "#5F0000" :background "#5F0000"))
+ "Face used to render text with 52th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-53
+ '((t :foreground "#5F005F" :background "#5F005F"))
+ "Face used to render text with 53th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-54
+ '((t :foreground "#5F0087" :background "#5F0087"))
+ "Face used to render text with 54th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-55
+ '((t :foreground "#5F00AF" :background "#5F00AF"))
+ "Face used to render text with 55th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-56
+ '((t :foreground "#5F00D7" :background "#5F00D7"))
+ "Face used to render text with 56th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-57
+ '((t :foreground "#5F00FF" :background "#5F00FF"))
+ "Face used to render text with 57th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-58
+ '((t :foreground "#5F5F00" :background "#5F5F00"))
+ "Face used to render text with 58th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-59
+ '((t :foreground "#5F5F5F" :background "#5F5F5F"))
+ "Face used to render text with 59th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-60
+ '((t :foreground "#5F5F87" :background "#5F5F87"))
+ "Face used to render text with 60th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-61
+ '((t :foreground "#5F5FAF" :background "#5F5FAF"))
+ "Face used to render text with 61th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-62
+ '((t :foreground "#5F5FD7" :background "#5F5FD7"))
+ "Face used to render text with 62th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-63
+ '((t :foreground "#5F5FFF" :background "#5F5FFF"))
+ "Face used to render text with 63th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-64
+ '((t :foreground "#5F8700" :background "#5F8700"))
+ "Face used to render text with 64th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-65
+ '((t :foreground "#5F875F" :background "#5F875F"))
+ "Face used to render text with 65th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-66
+ '((t :foreground "#5F8787" :background "#5F8787"))
+ "Face used to render text with 66th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-67
+ '((t :foreground "#5F87AF" :background "#5F87AF"))
+ "Face used to render text with 67th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-68
+ '((t :foreground "#5F87D7" :background "#5F87D7"))
+ "Face used to render text with 68th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-69
+ '((t :foreground "#5F87FF" :background "#5F87FF"))
+ "Face used to render text with 69th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-70
+ '((t :foreground "#5FAF00" :background "#5FAF00"))
+ "Face used to render text with 70th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-71
+ '((t :foreground "#5FAF5F" :background "#5FAF5F"))
+ "Face used to render text with 71th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-72
+ '((t :foreground "#5FAF87" :background "#5FAF87"))
+ "Face used to render text with 72th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-73
+ '((t :foreground "#5FAFAF" :background "#5FAFAF"))
+ "Face used to render text with 73th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-74
+ '((t :foreground "#5FAFD7" :background "#5FAFD7"))
+ "Face used to render text with 74th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-75
+ '((t :foreground "#5FAFFF" :background "#5FAFFF"))
+ "Face used to render text with 75th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-76
+ '((t :foreground "#5FD700" :background "#5FD700"))
+ "Face used to render text with 76th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-77
+ '((t :foreground "#5FD75F" :background "#5FD75F"))
+ "Face used to render text with 77th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-78
+ '((t :foreground "#5FD787" :background "#5FD787"))
+ "Face used to render text with 78th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-79
+ '((t :foreground "#5FD7AF" :background "#5FD7AF"))
+ "Face used to render text with 79th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-80
+ '((t :foreground "#5FD7D7" :background "#5FD7D7"))
+ "Face used to render text with 80th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-81
+ '((t :foreground "#5FD7FF" :background "#5FD7FF"))
+ "Face used to render text with 81th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-82
+ '((t :foreground "#5FFF00" :background "#5FFF00"))
+ "Face used to render text with 82th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-83
+ '((t :foreground "#5FFF5F" :background "#5FFF5F"))
+ "Face used to render text with 83th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-84
+ '((t :foreground "#5FFF87" :background "#5FFF87"))
+ "Face used to render text with 84th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-85
+ '((t :foreground "#5FFFAF" :background "#5FFFAF"))
+ "Face used to render text with 85th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-86
+ '((t :foreground "#5FFFD7" :background "#5FFFD7"))
+ "Face used to render text with 86th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-87
+ '((t :foreground "#5FFFFF" :background "#5FFFFF"))
+ "Face used to render text with 87th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-88
+ '((t :foreground "#870000" :background "#870000"))
+ "Face used to render text with 88th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-89
+ '((t :foreground "#87005F" :background "#87005F"))
+ "Face used to render text with 89th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-90
+ '((t :foreground "#870087" :background "#870087"))
+ "Face used to render text with 90th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-91
+ '((t :foreground "#8700AF" :background "#8700AF"))
+ "Face used to render text with 91th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-92
+ '((t :foreground "#8700D7" :background "#8700D7"))
+ "Face used to render text with 92th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-93
+ '((t :foreground "#8700FF" :background "#8700FF"))
+ "Face used to render text with 93th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-94
+ '((t :foreground "#875F00" :background "#875F00"))
+ "Face used to render text with 94th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-95
+ '((t :foreground "#875F5F" :background "#875F5F"))
+ "Face used to render text with 95th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-96
+ '((t :foreground "#875F87" :background "#875F87"))
+ "Face used to render text with 96th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-97
+ '((t :foreground "#875FAF" :background "#875FAF"))
+ "Face used to render text with 97th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-98
+ '((t :foreground "#875FD7" :background "#875FD7"))
+ "Face used to render text with 98th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-99
+ '((t :foreground "#875FFF" :background "#875FFF"))
+ "Face used to render text with 99th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-100
+ '((t :foreground "#878700" :background "#878700"))
+ "Face used to render text with 100th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-101
+ '((t :foreground "#87875F" :background "#87875F"))
+ "Face used to render text with 101th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-102
+ '((t :foreground "#878787" :background "#878787"))
+ "Face used to render text with 102th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-103
+ '((t :foreground "#8787AF" :background "#8787AF"))
+ "Face used to render text with 103th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-104
+ '((t :foreground "#8787D7" :background "#8787D7"))
+ "Face used to render text with 104th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-105
+ '((t :foreground "#8787FF" :background "#8787FF"))
+ "Face used to render text with 105th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-106
+ '((t :foreground "#87AF00" :background "#87AF00"))
+ "Face used to render text with 106th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-107
+ '((t :foreground "#87AF5F" :background "#87AF5F"))
+ "Face used to render text with 107th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-108
+ '((t :foreground "#87AF87" :background "#87AF87"))
+ "Face used to render text with 108th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-109
+ '((t :foreground "#87AFAF" :background "#87AFAF"))
+ "Face used to render text with 109th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-110
+ '((t :foreground "#87AFD7" :background "#87AFD7"))
+ "Face used to render text with 110th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-111
+ '((t :foreground "#87AFFF" :background "#87AFFF"))
+ "Face used to render text with 111th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-112
+ '((t :foreground "#87D700" :background "#87D700"))
+ "Face used to render text with 112th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-113
+ '((t :foreground "#87D75F" :background "#87D75F"))
+ "Face used to render text with 113th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-114
+ '((t :foreground "#87D787" :background "#87D787"))
+ "Face used to render text with 114th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-115
+ '((t :foreground "#87D7AF" :background "#87D7AF"))
+ "Face used to render text with 115th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-116
+ '((t :foreground "#87D7D7" :background "#87D7D7"))
+ "Face used to render text with 116th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-117
+ '((t :foreground "#87D7FF" :background "#87D7FF"))
+ "Face used to render text with 117th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-118
+ '((t :foreground "#87FF00" :background "#87FF00"))
+ "Face used to render text with 118th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-119
+ '((t :foreground "#87FF5F" :background "#87FF5F"))
+ "Face used to render text with 119th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-120
+ '((t :foreground "#87FF87" :background "#87FF87"))
+ "Face used to render text with 120th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-121
+ '((t :foreground "#87FFAF" :background "#87FFAF"))
+ "Face used to render text with 121th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-122
+ '((t :foreground "#87FFD7" :background "#87FFD7"))
+ "Face used to render text with 122th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-123
+ '((t :foreground "#87FFFF" :background "#87FFFF"))
+ "Face used to render text with 123th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-124
+ '((t :foreground "#AF0000" :background "#AF0000"))
+ "Face used to render text with 124th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-125
+ '((t :foreground "#AF005F" :background "#AF005F"))
+ "Face used to render text with 125th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-126
+ '((t :foreground "#AF0087" :background "#AF0087"))
+ "Face used to render text with 126th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-127
+ '((t :foreground "#AF00AF" :background "#AF00AF"))
+ "Face used to render text with 127th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-128
+ '((t :foreground "#AF00D7" :background "#AF00D7"))
+ "Face used to render text with 128th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-129
+ '((t :foreground "#AF00FF" :background "#AF00FF"))
+ "Face used to render text with 129th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-130
+ '((t :foreground "#AF5F00" :background "#AF5F00"))
+ "Face used to render text with 130th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-131
+ '((t :foreground "#AF5F5F" :background "#AF5F5F"))
+ "Face used to render text with 131th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-132
+ '((t :foreground "#AF5F87" :background "#AF5F87"))
+ "Face used to render text with 132th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-133
+ '((t :foreground "#AF5FAF" :background "#AF5FAF"))
+ "Face used to render text with 133th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-134
+ '((t :foreground "#AF5FD7" :background "#AF5FD7"))
+ "Face used to render text with 134th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-135
+ '((t :foreground "#AF5FFF" :background "#AF5FFF"))
+ "Face used to render text with 135th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-136
+ '((t :foreground "#AF8700" :background "#AF8700"))
+ "Face used to render text with 136th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-137
+ '((t :foreground "#AF875F" :background "#AF875F"))
+ "Face used to render text with 137th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-138
+ '((t :foreground "#AF8787" :background "#AF8787"))
+ "Face used to render text with 138th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-139
+ '((t :foreground "#AF87AF" :background "#AF87AF"))
+ "Face used to render text with 139th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-140
+ '((t :foreground "#AF87D7" :background "#AF87D7"))
+ "Face used to render text with 140th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-141
+ '((t :foreground "#AF87FF" :background "#AF87FF"))
+ "Face used to render text with 141th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-142
+ '((t :foreground "#AFAF00" :background "#AFAF00"))
+ "Face used to render text with 142th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-143
+ '((t :foreground "#AFAF5F" :background "#AFAF5F"))
+ "Face used to render text with 143th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-144
+ '((t :foreground "#AFAF87" :background "#AFAF87"))
+ "Face used to render text with 144th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-145
+ '((t :foreground "#AFAFAF" :background "#AFAFAF"))
+ "Face used to render text with 145th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-146
+ '((t :foreground "#AFAFD7" :background "#AFAFD7"))
+ "Face used to render text with 146th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-147
+ '((t :foreground "#AFAFFF" :background "#AFAFFF"))
+ "Face used to render text with 147th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-148
+ '((t :foreground "#AFD700" :background "#AFD700"))
+ "Face used to render text with 148th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-149
+ '((t :foreground "#AFD75F" :background "#AFD75F"))
+ "Face used to render text with 149th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-150
+ '((t :foreground "#AFD787" :background "#AFD787"))
+ "Face used to render text with 150th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-151
+ '((t :foreground "#AFD7AF" :background "#AFD7AF"))
+ "Face used to render text with 151th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-152
+ '((t :foreground "#AFD7D7" :background "#AFD7D7"))
+ "Face used to render text with 152th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-153
+ '((t :foreground "#AFD7FF" :background "#AFD7FF"))
+ "Face used to render text with 153th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-154
+ '((t :foreground "#AFFF00" :background "#AFFF00"))
+ "Face used to render text with 154th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-155
+ '((t :foreground "#AFFF5F" :background "#AFFF5F"))
+ "Face used to render text with 155th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-156
+ '((t :foreground "#AFFF87" :background "#AFFF87"))
+ "Face used to render text with 156th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-157
+ '((t :foreground "#AFFFAF" :background "#AFFFAF"))
+ "Face used to render text with 157th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-158
+ '((t :foreground "#AFFFD7" :background "#AFFFD7"))
+ "Face used to render text with 158th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-159
+ '((t :foreground "#AFFFFF" :background "#AFFFFF"))
+ "Face used to render text with 159th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-160
+ '((t :foreground "#D70000" :background "#D70000"))
+ "Face used to render text with 160th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-161
+ '((t :foreground "#D7005F" :background "#D7005F"))
+ "Face used to render text with 161th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-162
+ '((t :foreground "#D70087" :background "#D70087"))
+ "Face used to render text with 162th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-163
+ '((t :foreground "#D700AF" :background "#D700AF"))
+ "Face used to render text with 163th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-164
+ '((t :foreground "#D700D7" :background "#D700D7"))
+ "Face used to render text with 164th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-165
+ '((t :foreground "#D700FF" :background "#D700FF"))
+ "Face used to render text with 165th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-166
+ '((t :foreground "#D75F00" :background "#D75F00"))
+ "Face used to render text with 166th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-167
+ '((t :foreground "#D75F5F" :background "#D75F5F"))
+ "Face used to render text with 167th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-168
+ '((t :foreground "#D75F87" :background "#D75F87"))
+ "Face used to render text with 168th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-169
+ '((t :foreground "#D75FAF" :background "#D75FAF"))
+ "Face used to render text with 169th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-170
+ '((t :foreground "#D75FD7" :background "#D75FD7"))
+ "Face used to render text with 170th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-171
+ '((t :foreground "#D75FFF" :background "#D75FFF"))
+ "Face used to render text with 171th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-172
+ '((t :foreground "#D78700" :background "#D78700"))
+ "Face used to render text with 172th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-173
+ '((t :foreground "#D7875F" :background "#D7875F"))
+ "Face used to render text with 173th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-174
+ '((t :foreground "#D78787" :background "#D78787"))
+ "Face used to render text with 174th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-175
+ '((t :foreground "#D787AF" :background "#D787AF"))
+ "Face used to render text with 175th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-176
+ '((t :foreground "#D787D7" :background "#D787D7"))
+ "Face used to render text with 176th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-177
+ '((t :foreground "#D787FF" :background "#D787FF"))
+ "Face used to render text with 177th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-178
+ '((t :foreground "#D7AF00" :background "#D7AF00"))
+ "Face used to render text with 178th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-179
+ '((t :foreground "#D7AF5F" :background "#D7AF5F"))
+ "Face used to render text with 179th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-180
+ '((t :foreground "#D7AF87" :background "#D7AF87"))
+ "Face used to render text with 180th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-181
+ '((t :foreground "#D7AFAF" :background "#D7AFAF"))
+ "Face used to render text with 181th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-182
+ '((t :foreground "#D7AFD7" :background "#D7AFD7"))
+ "Face used to render text with 182th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-183
+ '((t :foreground "#D7AFFF" :background "#D7AFFF"))
+ "Face used to render text with 183th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-184
+ '((t :foreground "#D7D700" :background "#D7D700"))
+ "Face used to render text with 184th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-185
+ '((t :foreground "#D7D75F" :background "#D7D75F"))
+ "Face used to render text with 185th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-186
+ '((t :foreground "#D7D787" :background "#D7D787"))
+ "Face used to render text with 186th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-187
+ '((t :foreground "#D7D7AF" :background "#D7D7AF"))
+ "Face used to render text with 187th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-188
+ '((t :foreground "#D7D7D7" :background "#D7D7D7"))
+ "Face used to render text with 188th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-189
+ '((t :foreground "#D7D7FF" :background "#D7D7FF"))
+ "Face used to render text with 189th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-190
+ '((t :foreground "#D7FF00" :background "#D7FF00"))
+ "Face used to render text with 190th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-191
+ '((t :foreground "#D7FF5F" :background "#D7FF5F"))
+ "Face used to render text with 191th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-192
+ '((t :foreground "#D7FF87" :background "#D7FF87"))
+ "Face used to render text with 192th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-193
+ '((t :foreground "#D7FFAF" :background "#D7FFAF"))
+ "Face used to render text with 193th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-194
+ '((t :foreground "#D7FFD7" :background "#D7FFD7"))
+ "Face used to render text with 194th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-195
+ '((t :foreground "#D7FFFF" :background "#D7FFFF"))
+ "Face used to render text with 195th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-196
+ '((t :foreground "#FF0000" :background "#FF0000"))
+ "Face used to render text with 196th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-197
+ '((t :foreground "#FF005F" :background "#FF005F"))
+ "Face used to render text with 197th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-198
+ '((t :foreground "#FF0087" :background "#FF0087"))
+ "Face used to render text with 198th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-199
+ '((t :foreground "#FF00AF" :background "#FF00AF"))
+ "Face used to render text with 199th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-200
+ '((t :foreground "#FF00D7" :background "#FF00D7"))
+ "Face used to render text with 200th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-201
+ '((t :foreground "#FF00FF" :background "#FF00FF"))
+ "Face used to render text with 201th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-202
+ '((t :foreground "#FF5F00" :background "#FF5F00"))
+ "Face used to render text with 202th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-203
+ '((t :foreground "#FF5F5F" :background "#FF5F5F"))
+ "Face used to render text with 203th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-204
+ '((t :foreground "#FF5F87" :background "#FF5F87"))
+ "Face used to render text with 204th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-205
+ '((t :foreground "#FF5FAF" :background "#FF5FAF"))
+ "Face used to render text with 205th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-206
+ '((t :foreground "#FF5FD7" :background "#FF5FD7"))
+ "Face used to render text with 206th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-207
+ '((t :foreground "#FF5FFF" :background "#FF5FFF"))
+ "Face used to render text with 207th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-208
+ '((t :foreground "#FF8700" :background "#FF8700"))
+ "Face used to render text with 208th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-209
+ '((t :foreground "#FF875F" :background "#FF875F"))
+ "Face used to render text with 209th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-210
+ '((t :foreground "#FF8787" :background "#FF8787"))
+ "Face used to render text with 210th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-211
+ '((t :foreground "#FF87AF" :background "#FF87AF"))
+ "Face used to render text with 211th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-212
+ '((t :foreground "#FF87D7" :background "#FF87D7"))
+ "Face used to render text with 212th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-213
+ '((t :foreground "#FF87FF" :background "#FF87FF"))
+ "Face used to render text with 213th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-214
+ '((t :foreground "#FFAF00" :background "#FFAF00"))
+ "Face used to render text with 214th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-215
+ '((t :foreground "#FFAF5F" :background "#FFAF5F"))
+ "Face used to render text with 215th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-216
+ '((t :foreground "#FFAF87" :background "#FFAF87"))
+ "Face used to render text with 216th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-217
+ '((t :foreground "#FFAFAF" :background "#FFAFAF"))
+ "Face used to render text with 217th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-218
+ '((t :foreground "#FFAFD7" :background "#FFAFD7"))
+ "Face used to render text with 218th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-219
+ '((t :foreground "#FFAFFF" :background "#FFAFFF"))
+ "Face used to render text with 219th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-220
+ '((t :foreground "#FFD700" :background "#FFD700"))
+ "Face used to render text with 220th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-221
+ '((t :foreground "#FFD75F" :background "#FFD75F"))
+ "Face used to render text with 221th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-222
+ '((t :foreground "#FFD787" :background "#FFD787"))
+ "Face used to render text with 222th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-223
+ '((t :foreground "#FFD7AF" :background "#FFD7AF"))
+ "Face used to render text with 223th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-224
+ '((t :foreground "#FFD7D7" :background "#FFD7D7"))
+ "Face used to render text with 224th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-225
+ '((t :foreground "#FFD7FF" :background "#FFD7FF"))
+ "Face used to render text with 225th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-226
+ '((t :foreground "#FFFF00" :background "#FFFF00"))
+ "Face used to render text with 226th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-227
+ '((t :foreground "#FFFF5F" :background "#FFFF5F"))
+ "Face used to render text with 227th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-228
+ '((t :foreground "#FFFF87" :background "#FFFF87"))
+ "Face used to render text with 228th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-229
+ '((t :foreground "#FFFFAF" :background "#FFFFAF"))
+ "Face used to render text with 229th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-230
+ '((t :foreground "#FFFFD7" :background "#FFFFD7"))
+ "Face used to render text with 230th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-231
+ '((t :foreground "#FFFFFF" :background "#FFFFFF"))
+ "Face used to render text with 231th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-232
+ '((t :foreground "#080808" :background "#080808"))
+ "Face used to render text with 232th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-233
+ '((t :foreground "#121212" :background "#121212"))
+ "Face used to render text with 233th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-234
+ '((t :foreground "#1C1C1C" :background "#1C1C1C"))
+ "Face used to render text with 234th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-235
+ '((t :foreground "#262626" :background "#262626"))
+ "Face used to render text with 235th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-236
+ '((t :foreground "#303030" :background "#303030"))
+ "Face used to render text with 236th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-237
+ '((t :foreground "#3A3A3A" :background "#3A3A3A"))
+ "Face used to render text with 237th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-238
+ '((t :foreground "#444444" :background "#444444"))
+ "Face used to render text with 238th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-239
+ '((t :foreground "#4E4E4E" :background "#4E4E4E"))
+ "Face used to render text with 239th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-240
+ '((t :foreground "#585858" :background "#585858"))
+ "Face used to render text with 240th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-241
+ '((t :foreground "#626262" :background "#626262"))
+ "Face used to render text with 241th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-242
+ '((t :foreground "#6C6C6C" :background "#6C6C6C"))
+ "Face used to render text with 242th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-243
+ '((t :foreground "#767676" :background "#767676"))
+ "Face used to render text with 243th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-244
+ '((t :foreground "#808080" :background "#808080"))
+ "Face used to render text with 244th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-245
+ '((t :foreground "#8A8A8A" :background "#8A8A8A"))
+ "Face used to render text with 245th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-246
+ '((t :foreground "#949494" :background "#949494"))
+ "Face used to render text with 246th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-247
+ '((t :foreground "#9E9E9E" :background "#9E9E9E"))
+ "Face used to render text with 247th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-248
+ '((t :foreground "#A8A8A8" :background "#A8A8A8"))
+ "Face used to render text with 248th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-249
+ '((t :foreground "#B2B2B2" :background "#B2B2B2"))
+ "Face used to render text with 249th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-250
+ '((t :foreground "#BCBCBC" :background "#BCBCBC"))
+ "Face used to render text with 250th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-251
+ '((t :foreground "#C6C6C6" :background "#C6C6C6"))
+ "Face used to render text with 251th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-252
+ '((t :foreground "#D0D0D0" :background "#D0D0D0"))
+ "Face used to render text with 252th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-253
+ '((t :foreground "#DADADA" :background "#DADADA"))
+ "Face used to render text with 253th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-254
+ '((t :foreground "#E4E4E4" :background "#E4E4E4"))
+ "Face used to render text with 254th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-color-255
+ '((t :foreground "#EEEEEE" :background "#EEEEEE"))
+ "Face used to render text with 255th color of 256 color palette."
+ :group 'eat-term)
+
+(defface eat-term-font-0 '((t))
+ "Default font.")
+
+(put 'eat-term-font-default 'face-alias 'eat-term-font-0)
+
+(defface eat-term-font-1 '((t))
+ "Alternative font 1.")
+
+(defface eat-term-font-2 '((t))
+ "Alternative font 2.")
+
+(defface eat-term-font-3 '((t))
+ "Alternative font 3.")
+
+(defface eat-term-font-4 '((t))
+ "Alternative font 4.")
+
+(defface eat-term-font-5 '((t))
+ "Alternative font 5.")
+
+(defface eat-term-font-6 '((t))
+ "Alternative font 6.")
+
+(defface eat-term-font-7 '((t))
+ "Alternative font 7.")
+
+(defface eat-term-font-8 '((t))
+ "Alternative font 8.")
+
+(defface eat-term-font-9 '((t))
+ "Alternative font 9.")
+
+
+;;;; Utility Functions.
+
+(defun eat--t-goto-bol (&optional n)
+ "Go to the beginning of current line.
+
+With optional argument N, go to the beginning of Nth next line if N is
+positive, otherwise go to the beginning of -Nth previous line. If the
+specified position is before `point-min' or after `point-max', go to
+that point.
+
+Return the number of lines moved.
+
+Treat LINE FEED (?\\n) as the line delimiter."
+ ;; TODO: Comment.
+ (let ((n (or n 0)))
+ (cond
+ ((> n 0)
+ (let ((moved 0))
+ (while (and (< (point) (point-max))
+ (< moved n))
+ (and (search-forward "\n" nil 'move)
+ (cl-incf moved)))
+ moved))
+ ((<= n 0)
+ (let ((moved 1))
+ (while (and (or (= moved 1)
+ (< (point-min) (point)))
+ (< n moved))
+ (cl-decf moved)
+ (and (search-backward "\n" nil 'move)
+ (= moved n)
+ (goto-char (match-end 0))))
+ moved)))))
+
+(defun eat--t-goto-eol (&optional n)
+ "Go to the end of current line.
+
+With optional argument N, go to the end of Nth next line if N is
+positive, otherwise go to the end of -Nth previous line. If the
+specified position is before `point-min' or after `point-max', go to
+that point.
+
+Return the number of lines moved.
+
+Treat LINE FEED (?\\n) as the line delimiter."
+ ;; TODO: Comment.
+ (let ((n (or n 0)))
+ (cond
+ ((>= n 0)
+ (let ((moved -1))
+ (while (and (or (= moved -1)
+ (< (point) (point-max)))
+ (< moved n))
+ (cl-incf moved)
+ (and (search-forward "\n" nil 'move)
+ (= moved n)
+ (goto-char (match-beginning 0))))
+ moved))
+ ((< n 0)
+ (let ((moved 0))
+ (while (and (< (point-min) (point))
+ (< n moved))
+ (and (search-backward "\n" nil 'move)
+ (cl-decf moved)))
+ moved)))))
+
+(defun eat--t-bol (&optional n)
+ "Return the beginning of current line.
+
+With optional argument N, return a cons cell whose car is the
+beginning of Nth next line and cdr is N, if N is positive, otherwise
+return a cons cell whose car is the beginning of -Nth previous line
+and cdr is N. If the specified position is before `point-min' or
+after `point-max', return a cons cell whose car is that point and cdr
+is number of lines that point is away from current line.
+
+Treat LINE FEED (?\\n) as the line delimiter."
+ ;; Move to the beginning of line, record the point, and return that
+ ;; point and the distance of that point from current line in lines.
+ (save-excursion
+ (let ((moved (eat--t-goto-bol n)))
+ (cons (point) moved))))
+
+(defun eat--t-eol (&optional n)
+ "Return the end of current line.
+
+With optional argument N, return a cons cell whose car the end of Nth
+next line and cdr is N, if N is positive, otherwise return a cons cell
+whose car is the end of -Nth previous line and cdr in N. If the
+specified position is before `point-min' or after `point-max', return
+a cons cell whose car is that point and cdr is number of lines that
+point is away from current line.
+
+Treat LINE FEED (?\\n) as the line delimiter."
+ ;; Move to the beginning of line, record the point, and return that
+ ;; point and the distance of that point from current line in lines.
+ (save-excursion
+ (let* ((moved (eat--t-goto-eol n)))
+ (cons (point) moved))))
+
+(defun eat--t-col-motion (n)
+ "Move to Nth next column.
+
+Go to Nth next column if N is positive, otherwise go to -Nth previous
+column. If the specified position is before `point-min' or after
+`point-max', go to that point.
+
+Return the number of columns moved.
+
+Assume all characters occupy a single column."
+ ;; Record the current position.
+ (let ((point (point)))
+ ;; Move to the new position.
+ (cond
+ ((> n 0)
+ (let ((eol (car (eat--t-eol)))
+ (pos (+ (point) n)))
+ (goto-char (min pos eol))))
+ ((< n 0)
+ (let ((bol (car (eat--t-bol)))
+ (pos (+ (point) n)))
+ (goto-char (max pos bol)))))
+ ;; Return the distance from the previous position.
+ (- (point) point)))
+
+(defun eat--t-current-col ()
+ "Return the current column.
+
+Assume all characters occupy a single column."
+ ;; We assume that that all characters occupy a single column, so a
+ ;; subtraction should work.
+ (- (point) (car (eat--t-bol))))
+
+(defun eat--t-goto-col (n)
+ "Go to column N.
+
+Return the current column after moving point.
+
+Assume all characters occupy a single column."
+ ;; Move to column 0.
+ (eat--t-goto-bol)
+ ;; Now the target column is N characters away.
+ (eat--t-col-motion n))
+
+(defun eat--t-repeated-insert (c n &optional face)
+ "Insert C, N times.
+
+C is a character. FACE is the face to use, or nil."
+ (let ((str (make-string n c)))
+ (insert (if face (propertize str 'face face) str))))
+
+(defun eat--t-join-long-line (&optional limit)
+ "Join long line once, but don't try to go beyond LIMIT.
+
+For example: \"*foo\\nbar\\nbaz\" is converted to \"foo*bar\\nbaz\",
+where `*' indicates point."
+ ;; Are we already at the end a part of a long line?
+ (unless (get-char-property (point) 'eat--t-wrap-line)
+ ;; Find the next end of a part of a long line.
+ (goto-char (next-single-char-property-change
+ (point) 'eat--t-wrap-line nil limit)))
+ ;; Remove the newline.
+ (when (< (point) (or limit (point-max)))
+ (1value (cl-assert (1value (= (1value (char-after)) ?\n))))
+ (delete-char 1)))
+
+(defun eat--t-break-long-line (threshold)
+ "Break a line longer than THRESHOLD once.
+
+For example: when THRESHOLD is 3, \"*foobarbaz\" is converted to
+\"foo\\n*barbaz\", where `*' indicates point."
+ (let ((loop t))
+ ;; Find a too long line.
+ (while (and loop (< (point) (point-max)))
+ ;; Go to the threshold column.
+ (eat--t-goto-col threshold)
+ ;; Are we at the end of line?
+ (if (eq (char-after) ?\n)
+ ;; We are already at the end of line, so move to the next
+ ;; line and start from the beginning.
+ (forward-char)
+ ;; The next character is not a newline, so we must be at a
+ ;; long, or we are the end of the accessible part of the
+ ;; buffer. Whatever the case, we break the loop, and if it is
+ ;; a long line, we break the line.
+ (setq loop nil)
+ (unless (= (point) (point-max))
+ (insert-before-markers
+ (propertize "\n" 'eat--t-wrap-line t)))))))
+
+
+;;;; Emulator.
+
+(cl-defstruct (eat--t-cur
+ (:constructor eat--t-make-cur)
+ (:copier eat--t-copy-cur))
+ "Structure describing cursor position."
+ (position nil :documentation "Position of cursor.")
+ (y 1 :documentation "Y coordinate of cursor.")
+ (x 1 :documentation "X coordinate of cursor."))
+
+(cl-defstruct (eat--t-disp
+ (:constructor eat--t-make-disp)
+ (:copier eat--t-copy-disp))
+ "Structure describing the display."
+ (begin nil :documentation "Beginning of visible display.")
+ (width 80 :documentation "Width of display.")
+ (height 24 :documentation "Height of display.")
+ (cursor nil :documentation "Cursor.")
+ (saved-cursor
+ (1value (eat--t-make-cur))
+ :documentation "Saved cursor.")
+ (old-begin
+ nil
+ :documentation
+ "Beginning of visible display during last Eat redisplay."))
+
+(cl-defstruct (eat--t-face
+ (:constructor eat--t-make-face)
+ (:copier eat--t-copy-face))
+ "Structure describing the display attributes to use."
+ (face nil :documentation "Face to use.")
+ (fg nil :documentation "Foreground color.")
+ (bg nil :documentation "Background color.")
+ (intensity nil :documentation "Intensity face, or nil.")
+ (italic nil :documentation "Non-nil means use italic face.")
+ (underline nil :documentation "Non-nil means underline text.")
+ (underline-color nil :documentation "Underline color.")
+ (crossed nil :documentation "Non-nil means strike-through text.")
+ (conceal nil :documentation "Non-nil means invisible text.")
+ (inverse nil :documentation "Non-nil means inverse colors.")
+ (blink nil :documentation "Blink face, or nil.")
+ (font 'eat-term-font-0 :documentation "Current font face."))
+
+(cl-defstruct (eat--t-term
+ (:constructor eat--t-make-term)
+ (:copier eat--t-copy-term))
+ "Structure describing a terminal."
+ (buffer nil :documentation "The buffer of terminal.")
+ (begin nil :documentation "Beginning of terminal.")
+ (end nil :documentation "End of terminal area.")
+ (title "" :documentation "The title of the terminal.")
+ (bell-fn
+ (1value #'ignore)
+ :documentation "Function to ring the bell.")
+ (input-fn
+ (1value #'ignore)
+ :documentation "Function to send input.")
+ (set-cursor-fn
+ (1value #'ignore)
+ :documentation "Function to set cursor.")
+ (manipulate-selection-fn
+ (1value #'ignore)
+ :documentation "Function to manipulate selection.")
+ (set-title-fn
+ (1value #'ignore)
+ :documentation "Function to set title.")
+ (grab-mouse-fn
+ (1value #'ignore)
+ :documentation "Function to grab mouse.")
+ (set-focus-ev-mode-fn
+ (1value #'ignore)
+ :documentation "Function to set focus event mode.")
+ (parser-state nil :documentation "State of parser.")
+ (scroll-begin 1 :documentation "First line of scroll region.")
+ (scroll-end 24 :documentation "Last line of scroll region.")
+ (display nil :documentation "The display.")
+ (main-display nil :documentation "Main display.
+
+Nil when not in alternative display mode.")
+ (face
+ (1value (eat--t-make-face))
+ :documentation "Display attributes.")
+ (auto-margin t :documentation "State of auto margin mode.")
+ (ins-mode nil :documentation "State of insert mode.")
+ (charset
+ (copy-tree '(g0 . ((g0 . us-ascii)
+ (g1 . us-ascii)
+ (g2 . us-ascii)
+ (g3 . us-ascii))))
+ :documentation "Current character set.")
+ (cur-state :default :documentation "Current state of cursor.")
+ (cur-blinking-p nil :documentation "Is the cursor blinking?")
+ (saved-face
+ (1value (eat--t-make-face))
+ :documentation "Saved SGR attributes.")
+ (bracketed-yank nil :documentation "State of bracketed yank mode.")
+ (keypad-mode nil :documentation "State of keypad mode.")
+ (mouse-mode nil :documentation "Current mouse mode.")
+ (mouse-pressed nil :documentation "Pressed mouse buttons.")
+ (mouse-encoding nil :documentation "Current mouse event encoding.")
+ (focus-event-mode nil :documentation "Whether to send focus event.")
+ (cut-buffers
+ (1value (make-vector 10 nil))
+ :documentation "Cut buffers."))
+
+(defvar eat--t-term nil
+ "The current terminal.
+
+Don't `set' it, bind it to a value with `let'.")
+
+(defun eat--t-reset ()
+ "Reset terminal."
+ (let* ((disp (eat--t-term-display eat--t-term)))
+ ;; Reset most of the things to their respective default values.
+ (setf (eat--t-term-parser-state eat--t-term) nil)
+ (setf (eat--t-disp-begin disp) (point-min-marker))
+ (setf (eat--t-disp-old-begin disp) (point-min-marker))
+ (setf (eat--t-disp-cursor disp)
+ (eat--t-make-cur :position (point-min-marker)))
+ (setf (eat--t-disp-saved-cursor disp) (eat--t-make-cur))
+ (setf (eat--t-term-scroll-begin eat--t-term) 1)
+ (setf (eat--t-term-scroll-end eat--t-term)
+ (eat--t-disp-height disp))
+ (setf (eat--t-term-main-display eat--t-term) nil)
+ (setf (eat--t-term-face eat--t-term) (eat--t-make-face))
+ (setf (eat--t-term-auto-margin eat--t-term) t)
+ (setf (eat--t-term-ins-mode eat--t-term) nil)
+ (setf (eat--t-term-charset eat--t-term)
+ '(g0 (g0 . us-ascii)
+ (g1 . dec-line-drawing)
+ (g2 . dec-line-drawing)
+ (g3 . dec-line-drawing)))
+ (setf (eat--t-term-saved-face eat--t-term) (eat--t-make-face))
+ (setf (eat--t-term-bracketed-yank eat--t-term) nil)
+ (setf (eat--t-term-cur-state eat--t-term) :default)
+ (setf (eat--t-term-cur-blinking-p eat--t-term) nil)
+ (setf (eat--t-term-title eat--t-term) "")
+ (setf (eat--t-term-keypad-mode eat--t-term) nil)
+ (setf (eat--t-term-mouse-mode eat--t-term) nil)
+ (setf (eat--t-term-mouse-encoding eat--t-term) nil)
+ (setf (eat--t-term-focus-event-mode eat--t-term) nil)
+ ;; Clear everything.
+ (delete-region (point-min) (point-max))
+ ;; Inform the UI about our new state.
+ (funcall (eat--t-term-grab-mouse-fn eat--t-term) eat--t-term nil)
+ (funcall (eat--t-term-set-focus-ev-mode-fn eat--t-term)
+ eat--t-term nil)
+ (funcall (eat--t-term-set-title-fn eat--t-term) eat--t-term "")
+ (funcall (eat--t-term-set-cursor-fn eat--t-term) eat--t-term
+ :default)))
+
+(defun eat--t-cur-right (&optional n)
+ "Move cursor N columns right.
+
+N default to 1. If N is out of range, place cursor at the edge of
+display."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ ;; If N is less than 1, set N to 1. If N is more than the
+ ;; number of available columns on the right side, set N to
+ ;; the maximum possible value.
+ (n (min (- (eat--t-disp-width disp) (eat--t-cur-x cursor))
+ (max (or n 1) 1))))
+ ;; N is non-zero in most cases, except at the edge of display.
+ (unless (zerop n)
+ ;; Move to the Nth next column, use spaces to reach that column
+ ;; if needed.
+ (eat--t-repeated-insert ?\ (- n (eat--t-col-motion n)))
+ (cl-incf (eat--t-cur-x cursor) n))))
+
+(defun eat--t-cur-left (&optional n)
+ "Move cursor N columns left.
+
+N default to 1. If N is out of range, place cursor at the edge of
+display."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ ;; If N is less than 1, set N to 1. If N is more than the
+ ;; number of available columns on the left side, set N to the
+ ;; maximum possible value.
+ (n (min (1- (eat--t-cur-x cursor)) (max (or n 1) 1))))
+ ;; N is non-zero in most cases, except at the edge of display.
+ (unless (zerop n)
+ ;; Move to the Nth previous column.
+ (cl-assert (1value (>= (eat--t-current-col) n)))
+ (backward-char n)
+ (cl-decf (eat--t-cur-x cursor) n))))
+
+(defun eat--t-cur-horizontal-abs (&optional n)
+ "Move cursor to Nth column on current line.
+
+N default to 1. If N is out of range, place cursor at the edge of
+display."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ ;; If N is out of range, bring it within the bounds of range.
+ (n (min (max (or n 1) 1) (eat--t-disp-width disp))))
+ ;; Depending on the current position of cursor, move right or
+ ;; left.
+ (cond ((< (eat--t-cur-x cursor) n)
+ (eat--t-cur-right (- n (eat--t-cur-x cursor))))
+ ((< n (eat--t-cur-x cursor))
+ (eat--t-cur-left (- (eat--t-cur-x cursor) n))))))
+
+(defun eat--t-beg-of-next-line (n)
+ "Move to beginning of Nth next line."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ ;; If N is less than 1, set N to 1. If N is more than the
+ ;; number of available lines below, set N to the maximum
+ ;; possible value.
+ (n (min (- (eat--t-disp-height disp) (eat--t-cur-y cursor))
+ (max (or n 1) 1))))
+ ;; N is non-zero in most cases, except at the edge of display.
+ ;; Whatever the case, we move to the beginning of line.
+ (if (zerop n)
+ (1value (eat--t-goto-bol))
+ ;; Move to the Nth next line, use newlines to reach that line if
+ ;; needed.
+ (eat--t-repeated-insert ?\n (- n (eat--t-goto-bol n)))
+ (cl-incf (eat--t-cur-y cursor) n))
+ (1value (setf (eat--t-cur-x cursor) 1))))
+
+(defun eat--t-beg-of-prev-line (n)
+ "Move to beginning of Nth previous line."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ ;; If N is less than 1, set N to 1. If N is more than the
+ ;; number of available lines above, set N to the maximum
+ ;; possible value.
+ (n (min (1- (eat--t-cur-y cursor)) (max (or n 1) 1))))
+ ;; Move to the beginning Nth previous line. Even if there are no
+ ;; line above, move to the beginning of the line.
+ (eat--t-goto-bol (- n))
+ (cl-decf (eat--t-cur-y cursor) n)
+ (1value (setf (eat--t-cur-x cursor) 1))))
+
+(defun eat--t-cur-down (&optional n)
+ "Move cursor N lines down.
+
+N default to 1. If N is out of range, place cursor at the edge of
+display."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ (x (eat--t-cur-x cursor)))
+ ;; Move to the beginning of target line.
+ (eat--t-beg-of-next-line n)
+ ;; If the cursor wasn't at column one, move the cursor to the
+ ;; cursor to that column.
+ (unless (= x 1)
+ (eat--t-cur-right (1- x)))))
+
+(defun eat--t-cur-up (&optional n)
+ "Move cursor N lines up.
+
+N default to 1. If N is out of range, place cursor at the edge of
+display."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ (x (eat--t-cur-x cursor)))
+ ;; Move to the beginning of target line.
+ (eat--t-beg-of-prev-line n)
+ ;; If the cursor wasn't at column one, move the cursor to the
+ ;; cursor to that column.
+ (unless (= x 1)
+ (eat--t-cur-right (1- x)))))
+
+(defun eat--t-cur-vertical-abs (&optional n)
+ "Move cursor to Nth line on display.
+
+N default to 1. If N is out of range, place cursor at the edge of
+display."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ ;; If N is out of range, bring it within the bounds of range.
+ (n (min (max (or n 1) 1) (eat--t-disp-height disp))))
+ ;; Depending on the current position of cursor, move downward or
+ ;; upward.
+ (cond ((< (eat--t-cur-y cursor) n)
+ (eat--t-cur-down (- n (eat--t-cur-y cursor))))
+ ((< n (eat--t-cur-y cursor))
+ (eat--t-cur-up (- (eat--t-cur-y cursor) n))))))
+
+(defun eat--t-scroll-up (&optional n as-side-effect)
+ "Scroll up N lines, preserving cursor position.
+
+N default to 1. By default, don't change current line and current
+column, but if AS-SIDE-EFFECT is given and non-nil, assume that
+scrolling is triggered as a side effect of some other control function
+and don't move the point relative to the text and change current line
+accordingly."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ (scroll-begin (eat--t-term-scroll-begin eat--t-term))
+ (scroll-end (eat--t-term-scroll-end eat--t-term))
+ ;; N shouldn't be more more than the number of lines in
+ ;; scroll region.
+ (n (min (max (or n 1) 0) (1+ (- scroll-end scroll-begin)))))
+ ;; Make sure that N is positive.
+ (unless (zerop n)
+ ;; Try to not point relative to the text.
+ (save-excursion
+ (goto-char (eat--t-disp-begin disp))
+ ;; Move to the beginning of scroll region.
+ (eat--t-goto-bol (1- scroll-begin))
+ ;; If the first line on display isn't in scroll region or
+ ;; if this is the alternative display, delete text.
+ (if (or (eat--t-term-main-display eat--t-term)
+ (> scroll-begin 1))
+ (delete-region (point) (car (eat--t-bol n)))
+ ;; Otherwise, send the text to the scrollback area by
+ ;; advancing the display beginning marker.
+ (eat--t-goto-bol n)
+ ;; Make sure we're at the beginning of a line, because we
+ ;; might be at `point-max'.
+ (unless (or (= (point) (point-min))
+ (= (char-before) ?\n))
+ (insert ?\n))
+ (set-marker (eat--t-disp-begin disp) (point)))
+ ;; Is the last line on display in scroll region?
+ (when (< scroll-begin (eat--t-disp-width disp))
+ ;; Go to the end of scroll region (before deleting or moving
+ ;; texts).
+ (eat--t-goto-bol (- (1+ (- scroll-end scroll-begin)) n))
+ ;; If there is anything after the scroll region, insert
+ ;; newlines to keep that text unmoved.
+ (when (< (point) (point-max))
+ (eat--t-repeated-insert ?\n n))))
+ ;; Recalculate point if needed.
+ (let ((recalc-point
+ (<= scroll-begin (eat--t-cur-y cursor) scroll-end)))
+ ;; If recalc-point is non-nil, and AS-SIDE-EFFECT is non-nil,
+ ;; update cursor position so that it is unmoved relative to
+ ;; surrounding text and reconsider point recalculation.
+ (when (and recalc-point as-side-effect)
+ (setq recalc-point (< (- (eat--t-cur-y cursor) n)
+ scroll-begin))
+ (setf (eat--t-cur-y cursor)
+ (max (- (eat--t-cur-y cursor) n) scroll-begin)))
+ (when recalc-point
+ ;; Recalculate point.
+ (let ((y (eat--t-cur-y cursor))
+ (x (eat--t-cur-x cursor)))
+ (eat--t-goto 1 1)
+ (eat--t-goto y x)))))))
+
+(defun eat--t-scroll-down (&optional n)
+ "Scroll down N lines, preserving cursor position.
+
+N default to 1."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ (scroll-begin (eat--t-term-scroll-begin eat--t-term))
+ (scroll-end (eat--t-term-scroll-end eat--t-term))
+ ;; N shouldn't be more more than the number of lines in
+ ;; scroll region.
+ (n (min (max (or n 1) 0) (1+ (- scroll-end scroll-begin)))))
+ ;; Make sure that N is positive.
+ (unless (zerop n)
+ ;; Move to the beginning of scroll region.
+ (goto-char (eat--t-disp-begin disp))
+ (eat--t-goto-bol (1- scroll-begin))
+ ;; Insert newlines to push text downwards.
+ (eat--t-repeated-insert ?\n n)
+ ;; Go to the end scroll region (after inserting newlines).
+ (eat--t-goto-eol (- (1+ (- scroll-end scroll-begin)) (1+ n)))
+ ;; Delete the text that was pushed out of scroll region.
+ (when (< (point) (point-max))
+ (delete-region (point) (car (eat--t-eol n))))
+ ;; The cursor mustn't move, so we have to recalculate point.
+ (let ((y (eat--t-cur-y cursor))
+ (x (eat--t-cur-x cursor)))
+ (eat--t-goto 1 1)
+ (eat--t-goto y x)))))
+
+(defun eat--t-goto (&optional y x)
+ "Go to Xth column of Yth line of display.
+
+Y and X default to 1. Y and X are one-based. If Y and/or X are out
+of range, place cursor at the edge of display."
+ ;; Important special case: if Y and X are both one, move to the
+ ;; display beginning.
+ (if (and (or (not y) (eql y 1))
+ (or (not x) (eql x 1)))
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp)))
+ (goto-char (eat--t-disp-begin disp))
+ (1value (setf (eat--t-cur-y cursor) 1))
+ (1value (setf (eat--t-cur-x cursor) 1)))
+ ;; Move to column one, go to Yth line and move to Xth column.
+ ;; REVIEW: We move relative to cursor position, which faster for
+ ;; positions near the point (usually the case), but slower for
+ ;; positions far away from the point. There are only two cursor
+ ;; positions whose exact position is known beforehand, the cursor
+ ;; (whose position is (point)) and (1, 1) (the display beginning).
+ ;; There are almost always some points which are at more distance
+ ;; from current position than from the display beginning (the only
+ ;; exception is when the cursor is at the display beginning). So
+ ;; first moving to the display beginning and then moving to those
+ ;; point will be faster than moving from cursor (except a small
+ ;; (perhaps negligible) overhead of `goto-char'). What we don't
+ ;; have is a formula the calculate the distance between two
+ ;; positions.
+ (eat--t-cur-horizontal-abs 1)
+ (eat--t-cur-vertical-abs y)
+ (eat--t-cur-horizontal-abs x)))
+
+(defun eat--t-enable-auto-margin ()
+ "Enable automatic margin."
+ ;; Set the automatic margin flag to t, the rest of code will take
+ ;; care of the effects.
+ (1value (setf (eat--t-term-auto-margin eat--t-term) t)))
+
+(defun eat--t-disable-auto-margin ()
+ "Disable automatic margin."
+ ;; Set the automatic margin flag to nil, the rest of code will take
+ ;; care of the effects.
+ (1value (setf (eat--t-term-auto-margin eat--t-term) nil)))
+
+(defun eat--t-set-charset (slot charset)
+ "SLOT's character set to CHARSET."
+ (setf (alist-get slot (cdr (eat--t-term-charset eat--t-term)))
+ charset))
+
+(defun eat--t-change-charset (charset)
+ "Change character set to CHARSET.
+
+CHARSET should be one of `g0', `g1', `g2' and `g3'."
+ (setf (car (eat--t-term-charset eat--t-term)) charset))
+
+(defun eat--t-write (str)
+ "Write STR on display."
+ (let* ((str
+ ;; Convert STR to Unicode according to the current character
+ ;; set.
+ (pcase (alist-get (car (eat--t-term-charset eat--t-term))
+ (cdr (eat--t-term-charset eat--t-term)))
+ ;; For `us-ascii', the default, no conversion is
+ ;; necessary.
+ ('us-ascii
+ str)
+ ;; `dec-line-drawing' contains various characters useful
+ ;; for drawing line diagram, so it is a must. This is
+ ;; also possible with `us-ascii', thanks to Unicode, but
+ ;; the character set `dec-line-drawing' is usually less
+ ;; expensive in terms of bytes needed to transfer than
+ ;; `us-ascii'.
+ ('dec-line-drawing
+ (let ((s (copy-sequence str)))
+ (dotimes (i (length s))
+ (let ((replacement (alist-get (aref s i)
+ '((?+ . ?→)
+ (?, . ?←)
+ (?- . ?↑)
+ (?. . ?↓)
+ (?0 . ?█)
+ (?\` . ?�)
+ (?a . ?▒)
+ (?b . ?␉)
+ (?c . ?␌)
+ (?d . ?␍)
+ (?e . ?␊)
+ (?f . ?°)
+ (?g . ?±)
+ (?h . ?░)
+ (?i . ?#)
+ (?j . ?┘)
+ (?k . ?┐)
+ (?l . ?┌)
+ (?m . ?└)
+ (?n . ?┼)
+ (?o . ?⎺)
+ (?p . ?⎻)
+ (?q . ?─)
+ (?r . ?⎼)
+ (?s . ?⎽)
+ (?t . ?├)
+ (?u . ?┤)
+ (?v . ?┴)
+ (?w . ?┬)
+ (?x . ?│)
+ (?y . ?≤)
+ (?z . ?≥)
+ (?{ . ?π)
+ (?| . ?≠)
+ (?} . ?£)
+ (?~ . ?•)))))
+ (when replacement
+ (aset s i replacement))))
+ s))
+ (_
+ str)))
+ ;; Add `face' property.
+ (str (propertize str 'face
+ (eat--t-face-face
+ (eat--t-term-face eat--t-term)))))
+ ;; TODO: Comment.
+ ;; REVIEW: This probably needs to be updated.
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ (scroll-end (eat--t-term-scroll-end eat--t-term)))
+ (while (not (string-empty-p str))
+ (let ((ins-count (min (- (eat--t-disp-width disp)
+ (1- (eat--t-cur-x cursor)))
+ (length str))))
+ (insert (substring str 0 ins-count))
+ (setq str (substring str ins-count))
+ (cl-incf (eat--t-cur-x cursor) ins-count)
+ (if (eat--t-term-ins-mode eat--t-term)
+ (delete-region
+ (save-excursion
+ (eat--t-col-motion (- (eat--t-disp-width disp)
+ (1- (eat--t-cur-x cursor))))
+ (point))
+ (car (eat--t-eol)))
+ (delete-region (point) (min (+ ins-count (point))
+ (car (eat--t-eol)))))
+ (when (> (eat--t-cur-x cursor) (eat--t-disp-width disp))
+ (if (not (eat--t-term-auto-margin eat--t-term))
+ (eat--t-cur-left 1)
+ (unless (string-empty-p str)
+ (when (= (eat--t-cur-y cursor) scroll-end)
+ (eat--t-scroll-up 1 'as-side-effect))
+ (if (= (eat--t-cur-y cursor) scroll-end)
+ (eat--t-carriage-return)
+ (if (= (point) (point-max))
+ (insert (propertize "\n" 'eat--t-wrap-line t))
+ (put-text-property (point) (1+ (point))
+ 'eat--t-wrap-line t)
+ (forward-char))
+ (1value (setf (eat--t-cur-x cursor) 1))
+ (cl-incf (eat--t-cur-y cursor)))))))))))
+
+(defun eat--t-horizontal-tab (&optional n)
+ "Go to the Nth next tabulation stop.
+
+N default to 1."
+ (let* ((n (max (or n 1) 1)) ; N must be positive.
+ (disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp)))
+ ;; Do some math calculate the distance of the Nth next tabulation
+ ;; stop from cursor, and go there.
+ (eat--t-cur-right (+ (- 8 (mod (1- (eat--t-cur-x cursor)) 8))
+ (* (1- n) 8)))))
+
+(defun eat--t-horizontal-backtab (&optional n)
+ "Go to the Nth previous tabulation stop.
+
+N default to 1."
+ (let* ((n (max (or n 1) 1)) ; N must be positive.
+ (disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp)))
+ ;; Do some math calculate the distance of the Nth next tabulation
+ ;; stop from cursor, and go there.
+ (eat--t-cur-left (+ (1+ (mod (- (eat--t-cur-x cursor) 2) 8))
+ (* (1- n) 8)))))
+
+(defun eat--t-index ()
+ "Go to the next line preserving column, scrolling if necessary."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ (scroll-end (eat--t-term-scroll-end eat--t-term))
+ ;; Are we inside scroll region?
+ (in-scroll-region (<= (eat--t-cur-y cursor) scroll-end)))
+ ;; If this is the last line (of the scroll region or the display),
+ ;; scroll up, otherwise move cursor downward.
+ (if (= (if in-scroll-region scroll-end (eat--t-disp-height disp))
+ (eat--t-cur-y cursor))
+ (eat--t-scroll-up 1)
+ (eat--t-cur-down 1))))
+
+(defun eat--t-carriage-return ()
+ "Go to column one."
+ (eat--t-cur-horizontal-abs 1))
+
+(defun eat--t-line-feed ()
+ "Go to the first column of the next line, scrolling if necessary."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ (scroll-end (eat--t-term-scroll-end eat--t-term))
+ ;; Are we inside scroll region?
+ (in-scroll-region (<= (eat--t-cur-y cursor) scroll-end)))
+ ;; If we are at the very end of the terminal, we might have some
+ ;; optimizations.
+ (if (= (point) (point-max))
+ ;; If the cursor is above the last line of the scroll region
+ ;; (or the display, if we are outside the scroll region), we
+ ;; can simply insert a newline and update the cursor position.
+ (if (/= (if in-scroll-region
+ scroll-end
+ (eat--t-disp-height disp))
+ (eat--t-cur-y cursor))
+ (progn
+ (insert ?\n)
+ (setf (eat--t-cur-x cursor) 1)
+ (cl-incf (eat--t-cur-y cursor)))
+ ;; This is the last line. We need to scroll up.
+ (eat--t-scroll-up 1 'as-side-effect)
+ ;; If we're still at the last line (only happens when the
+ ;; display has only a single line), go to column one of it.
+ (if (= (if in-scroll-region
+ scroll-end
+ (eat--t-disp-height disp))
+ (eat--t-cur-y cursor))
+ (eat--t-carriage-return)
+ ;; If we are somehow moved from the end of terminal,
+ ;; `eat--t-beg-of-next-line' is the best option.
+ (if (/= (point) (point-max))
+ (eat--t-beg-of-next-line 1)
+ ;; We are still at the end! We can can simply insert a
+ ;; newline and update the cursor position.
+ (insert ?\n)
+ (setf (eat--t-cur-x cursor) 1)
+ (cl-incf (eat--t-cur-y cursor)))))
+ ;; We are not at the end of terminal. But we still have a last
+ ;; chance. `eat--t-beg-of-next-line' is usually faster than
+ ;; `eat--t-carriage-return' followed by `eat--t-index', so if
+ ;; there is at least a single line (in the scroll region, if the
+ ;; cursor in the scroll region, otherwise in the display)
+ ;; underneath the cursor, we can use `eat--t-beg-of-next-line'.
+ (if (/= (if in-scroll-region
+ scroll-end
+ (eat--t-disp-height disp))
+ (eat--t-cur-y cursor))
+ (eat--t-beg-of-next-line 1)
+ ;; We don't have any other option, so we must use the most
+ ;; time-expensive option.
+ (eat--t-carriage-return)
+ (eat--t-index)))))
+
+(defun eat--t-reverse-index ()
+ "Go to the previous line preserving column, scrolling if needed."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ (scroll-begin (eat--t-term-scroll-begin eat--t-term))
+ ;; Are we in the scroll region?
+ (in-scroll-region (<= scroll-begin (eat--t-cur-y cursor))))
+ ;; If this is the first line (of the scroll region or the
+ ;; display), scroll down, otherwise move cursor upward.
+ (if (= (if in-scroll-region scroll-begin 1)
+ (eat--t-cur-y cursor))
+ (eat--t-scroll-down 1)
+ (eat--t-cur-up 1))))
+
+(defun eat--t-bell ()
+ "Ring the bell."
+ ;; Call the UI's bell handler.
+ (funcall (eat--t-term-bell-fn eat--t-term) eat--t-term))
+
+(defun eat--t-form-feed ()
+ "Insert a vertical tab."
+ ;; Form feed is same as `eat--t-index'.
+ (eat--t-index))
+
+(defun eat--t-save-cur ()
+ "Save current cursor position."
+ (let ((disp (eat--t-term-display eat--t-term))
+ (saved-face (eat--t-copy-face
+ (eat--t-term-face eat--t-term))))
+ ;; Save cursor position.
+ (setf (eat--t-disp-saved-cursor disp)
+ (eat--t-copy-cur (eat--t-disp-cursor disp)))
+ ;; Save SGR attributes.
+ (setf (eat--t-term-saved-face eat--t-term) saved-face)
+ ;; We use side-effects, so make sure the saved face doesn't share
+ ;; structure with the current face.
+ (setf (eat--t-face-face saved-face)
+ (copy-tree (eat--t-face-face saved-face)))
+ (setf (eat--t-face-underline-color saved-face)
+ (copy-tree (eat--t-face-underline-color saved-face)))))
+
+(defun eat--t-restore-cur ()
+ "Restore previously save cursor position."
+ (let ((saved (eat--t-disp-saved-cursor
+ (eat--t-term-display eat--t-term))))
+ ;; Restore cursor position.
+ (eat--t-goto (eat--t-cur-y saved) (eat--t-cur-x saved))
+ ;; Restore SGR attributes.
+ (setf (eat--t-term-face eat--t-term)
+ (copy-tree (eat--t-term-saved-face eat--t-term)))
+ (setf (eat--t-face-underline-color (eat--t-term-face eat--t-term))
+ (copy-tree (eat--t-face-underline-color
+ (eat--t-term-face eat--t-term))))))
+
+(defun eat--t-erase-in-line (&optional n)
+ "Erase part of current line, but don't move cursor.
+
+N defaults to 0. When N is 0, erase cursor to end of line. When N is
+1, erase beginning of line to cursor. When N is 2, erase whole line."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (face (eat--t-term-face eat--t-term))
+ (cursor (eat--t-disp-cursor disp)))
+ (pcase n
+ ((or 0 'nil (pred (< 2)))
+ ;; Delete cursor position (inclusive) to end of line.
+ (delete-region (point) (car (eat--t-eol)))
+ ;; If the SGR background attribute is set, we need to fill the
+ ;; erased area with that background.
+ (when (eat--t-face-bg face)
+ (save-excursion
+ (eat--t-repeated-insert
+ ?\ (1+ (- (eat--t-disp-width disp)
+ (eat--t-cur-x cursor)))
+ (and (eat--t-face-bg face)
+ (eat--t-face-face face))))))
+ (1
+ ;; Delete beginning of line to cursor position (inclusive).
+ (delete-region (car (eat--t-bol))
+ (if (or (= (point) (point-max))
+ (= (char-after) ?\n))
+ (point)
+ (1+ (point))))
+ ;; Fill the region with spaces, use SGR background attribute
+ ;; if set.
+ (eat--t-repeated-insert ?\ (eat--t-cur-x cursor)
+ (and (eat--t-face-bg face)
+ (eat--t-face-face face)))
+ ;; We erased the character at the cursor position, so after
+ ;; fill with spaces we are still off by one column; so move a
+ ;; column backward.
+ (backward-char))
+ (2
+ ;; Delete whole line.
+ (delete-region (car (eat--t-bol)) (car (eat--t-eol)))
+ ;; Fill the region before cursor position with spaces, use SGR
+ ;; background attribute if set.
+ (eat--t-repeated-insert ?\ (1- (eat--t-cur-x cursor))
+ (and (eat--t-face-bg face)
+ (eat--t-face-face face)))
+ ;; If the SGR background attribute is set, we need to fill the
+ ;; erased area including and after cursor position with that
+ ;; background.
+ (when (eat--t-face-bg face)
+ (save-excursion
+ (eat--t-repeated-insert
+ ?\ (1+ (- (eat--t-disp-width disp)
+ (eat--t-cur-x cursor)))
+ (and (eat--t-face-bg face)
+ (eat--t-face-face face)))))))))
+
+(defun eat--t-erase-in-disp (&optional n)
+ "Erase part of display.
+
+N defaults to 0. When N is 0, erase cursor to end of display. When N
+is 1, erase beginning of display to cursor. In both on the previous
+cases, don't move cursor. When N is 2, erase display and reset cursor
+to (1, 1). When N is 3, also erase the scrollback."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (face (eat--t-term-face eat--t-term))
+ (cursor (eat--t-disp-cursor disp)))
+ (pcase n
+ ((or 0 'nil (pred (< 3)))
+ ;; Delete from cursor position (inclusive) to end of terminal.
+ (delete-region (point) (point-max))
+ ;; If the SGR background attribute is set, we need to fill the
+ ;; erased area with that background.
+ (when (eat--t-face-bg face)
+ ;; `save-excursion' probably uses marker to save point, which
+ ;; doesn't work in this case. So we the store the point as a
+ ;; integer.
+ (let ((pos (point)))
+ ;; Fill current line.
+ (eat--t-repeated-insert ?\ (1+ (- (eat--t-disp-width disp)
+ (eat--t-cur-x cursor)))
+ (eat--t-face-face face))
+ ;; Fill the following lines.
+ (dotimes (_ (- (eat--t-disp-height disp)
+ (eat--t-cur-y cursor)))
+ (insert ?\n)
+ (eat--t-repeated-insert ?\ (eat--t-disp-width disp)
+ (eat--t-face-face face)))
+ ;; Restore position.
+ (goto-char pos))))
+ (1
+ (let* ((y (eat--t-cur-y cursor))
+ (x (eat--t-cur-x cursor))
+ ;; Should we erase including the cursor position?
+ (incl-point (/= (point) (point-max))))
+ ;; Delete the region to be erased.
+ (delete-region (eat--t-disp-begin disp)
+ (if incl-point (1+ (point)) (point)))
+ ;; If the SGR background attribute isn't set, insert
+ ;; newlines, otherwise fill the erased area above the current
+ ;; line with background color.
+ (if (not (eat--t-face-bg face))
+ (eat--t-repeated-insert ?\n (1- y))
+ (dotimes (_ (1- y))
+ (eat--t-repeated-insert ?\ (eat--t-disp-width disp)
+ (eat--t-face-face face))
+ (insert ?\n)))
+ ;; Fill the current line to keep the cursor unmoved. Use
+ ;; background if the corresponding SGR attribute is set.
+ (eat--t-repeated-insert ?\ x (and (eat--t-face-bg face)
+ (eat--t-face-face face)))
+ ;; We are off by one column; so move a column backward.
+ (when incl-point
+ (backward-char))))
+ ((or 2 3)
+ ;; Move to the display beginning.
+ (eat--t-goto 1 1)
+ ;; Delete everything in the display, and if N is 3, also delete
+ ;; everything in the scrollback area.
+ (delete-region (if (= n 2) (point) (point-min))
+ (point-max))
+ ;; If the SGR background attribute is set, fill the display
+ ;; with that background.
+ (when (eat--t-face-bg face)
+ ;; `save-excursion' probably uses marker to save point, which
+ ;; doesn't work in this case. So we the store the point as a
+ ;; integer.
+ (let ((pos (point)))
+ (dotimes (i (eat--t-disp-height disp))
+ (unless (zerop i)
+ (insert ?\n))
+ (eat--t-repeated-insert ?\ (eat--t-disp-width disp)
+ (eat--t-face-face face)))
+ ;; Restore point.
+ (goto-char pos)))))))
+
+(defun eat--t-device-status-report ()
+ "Send the current Y and X coordinate to client."
+ ;; TODO: Is this really device status report function?
+ (let ((cursor (eat--t-disp-cursor
+ (eat--t-term-display eat--t-term))))
+ (funcall (eat--t-term-input-fn eat--t-term) eat--t-term
+ (format "\e[%i;%iR" (eat--t-cur-y cursor)
+ (eat--t-cur-x cursor)))))
+
+(defun eat--t-set-cursor-state (state)
+ "Set cursor state to STATE.
+
+STATE one of the `:default', `:invisible', `:very-visible'."
+ (unless (eq (eat--t-term-cur-state eat--t-term) state)
+ ;; Update state.
+ (setf (eat--t-term-cur-state eat--t-term) state)
+ ;; Inform the UI.
+ (funcall (eat--t-term-set-cursor-fn eat--t-term) eat--t-term
+ state)))
+
+(defun eat--t-default-cursor ()
+ "Set the cursor to its default state."
+ (eat--t-set-cursor-state
+ (if (eat--t-term-cur-blinking-p eat--t-term)
+ :very-visible
+ :default)))
+
+(defun eat--t-invisible-cursor ()
+ "Make the cursor invisible."
+ (eat--t-set-cursor-state :invisible))
+
+(defun eat--t-blinking-cursor ()
+ "Make the cursor blink."
+ (setf (eat--t-term-cur-blinking-p eat--t-term) t)
+ (when (eq (eat--t-term-cur-state eat--t-term) :default)
+ (eat--t-set-cursor-state :very-visible)))
+
+(defun eat--t-non-blinking-cursor ()
+ "Make the cursor not blink."
+ (setf (eat--t-term-cur-blinking-p eat--t-term) nil)
+ (when (eq (eat--t-term-cur-state eat--t-term) :very-visible)
+ (eat--t-set-cursor-state :default)))
+
+(defun eat--t-enable-bracketed-yank ()
+ "Enable bracketed yank mode."
+ (setf (eat--t-term-bracketed-yank eat--t-term) t))
+
+(defun eat--t-disable-bracketed-yank ()
+ "Disable bracketed yank mode."
+ (setf (eat--t-term-bracketed-yank eat--t-term) nil))
+
+(defun eat--t-enable-alt-disp ()
+ "Enable alternative display."
+ ;; Effective only when alternative display is enabled by user.
+ (when eat-enable-alternative-display
+ ;; Make sure we not already in the alternative display.
+ (unless (eat--t-term-main-display eat--t-term)
+ ;; Store the current display, including scrollback.
+ (let ((main-disp (eat--t-copy-disp
+ (eat--t-term-display eat--t-term))))
+ (setf (eat--t-disp-begin main-disp)
+ (- (eat--t-disp-begin main-disp) (point-min)))
+ (setf (eat--t-disp-old-begin main-disp)
+ (- (eat--t-disp-old-begin main-disp) (point-min)))
+ (setf (eat--t-disp-cursor main-disp)
+ (eat--t-copy-cur (eat--t-disp-cursor main-disp)))
+ (setf (eat--t-disp-saved-cursor main-disp)
+ (eat--t-copy-cur (eat--t-disp-saved-cursor main-disp)))
+ (setf (eat--t-cur-position (eat--t-disp-cursor main-disp))
+ (- (point) (point-min)))
+ (setf (eat--t-term-main-display eat--t-term)
+ (cons main-disp (buffer-string)))
+ ;; Delete everything, and move to the beginning of terminal.
+ (delete-region (point-min) (point-max))
+ (eat--t-goto 1 1)))))
+
+(defun eat--t-disable-alt-disp (&optional dont-move-cursor)
+ "Disable alternative display.
+
+If DONT-MOVE-CURSOR is non-nil, don't move cursor from current
+position."
+ ;; Make sure we in the alternative display.
+ (when (eat--t-term-main-display eat--t-term)
+ (let* ((main-disp (eat--t-term-main-display eat--t-term))
+ (old-y (eat--t-cur-y
+ (eat--t-disp-cursor
+ (eat--t-term-display eat--t-term))))
+ (old-x (eat--t-cur-x
+ (eat--t-disp-cursor
+ (eat--t-term-display eat--t-term))))
+ (width (eat--t-disp-width
+ (eat--t-term-display eat--t-term)))
+ (height (eat--t-disp-height
+ (eat--t-term-display eat--t-term))))
+ ;; Delete everything.
+ (delete-region (point-min) (point-max))
+ ;; Restore the main display.
+ (insert (cdr main-disp))
+ (setf (eat--t-disp-begin (car main-disp))
+ (copy-marker (+ (point-min)
+ (eat--t-disp-begin (car main-disp)))))
+ (setf (eat--t-disp-old-begin (car main-disp))
+ (copy-marker (+ (point-min)
+ (eat--t-disp-old-begin (car main-disp)))))
+ (setf (eat--t-cur-position (eat--t-disp-cursor (car main-disp)))
+ (copy-marker (+ (point-min)
+ (eat--t-cur-position
+ (eat--t-disp-cursor (car main-disp))))))
+ (setf (eat--t-term-display eat--t-term) (car main-disp))
+ (setf (eat--t-term-main-display eat--t-term) nil)
+ (goto-char (eat--t-cur-position
+ (eat--t-disp-cursor
+ (eat--t-term-display eat--t-term))))
+ ;; Maybe the terminal was resized after enabling alternative
+ ;; display, so we have to resize again.
+ (eat--t-resize width height)
+ ;; Restore cursor position if DONT-MOVE-CURSOR is non-nil.
+ (when dont-move-cursor
+ (eat--t-goto old-y old-x)))))
+
+(defun eat--t-insert-char (n)
+ "Insert N empty (space) characters, preserving cursor."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (face (eat--t-term-face eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ ;; Make sure N is positive. If N is more than the number of
+ ;; available columns available, set N to the maximum possible
+ ;; value.
+ (n (min (- (eat--t-disp-width disp)
+ (1- (eat--t-cur-x cursor)))
+ (max (or n 1) 1))))
+ ;; Return if N is zero.
+ (unless (zerop n)
+ (save-excursion
+ ;; Insert N spaces, with SGR background if that attribute is
+ ;; set.
+ (eat--t-repeated-insert ?\ n (and (eat--t-face-bg face)
+ (eat--t-face-face face)))
+ ;; Remove the characters that went beyond the edge of display.
+ (eat--t-col-motion (- (eat--t-disp-width disp)
+ (+ (1- (eat--t-cur-x cursor)) n)))
+ (delete-region (point) (car (eat--t-eol)))))))
+
+(defun eat--t-delete-char (n)
+ "Delete N characters, preserving cursor."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (face (eat--t-term-face eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ ;; Make sure N is positive. If N is more than the number of
+ ;; available columns available, set N to the maximum possible
+ ;; value.
+ (n (min (- (eat--t-disp-width disp)
+ (1- (eat--t-cur-x cursor)))
+ (max (or n 1) 1))))
+ ;; Return if N is zero.
+ (unless (zerop n)
+ (save-excursion
+ (let ((m (point)))
+ ;; Delete N character on current line.
+ (eat--t-col-motion n)
+ (delete-region m (point))
+ ;; If SGR background attribute is set, fill N characters at
+ ;; the right edge of display with that background.
+ (when (eat--t-face-bg face)
+ (save-excursion
+ (eat--t-goto-eol)
+ (let ((empty (1+ (- (eat--t-disp-width disp)
+ (eat--t-cur-x cursor)
+ (- (point) m)))))
+ ;; Reach the position from where to start filling.
+ ;; Use spaces if needed.
+ (when (> empty n)
+ (eat--t-repeated-insert ?\ (- empty n)))
+ ;; Fill with background.
+ (eat--t-repeated-insert
+ ?\ (min empty n) (eat--t-face-face face))))))))))
+
+(defun eat--t-erase-char (n)
+ "Make next N character cells empty, preserving cursor."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (face (eat--t-term-face eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ ;; Make sure N is positive. If N is more than the number of
+ ;; available columns available, set N to the maximum possible
+ ;; value.
+ (n (min (- (eat--t-disp-width disp)
+ (1- (eat--t-cur-x cursor)))
+ (max (or n 1) 1))))
+ ;; Return if N is zero.
+ (unless (zerop n)
+ (save-excursion
+ (let ((m (point)))
+ ;; Delete N character on current line.
+ (eat--t-col-motion n)
+ (delete-region m (point))
+ ;; Insert N spaces, with background if SGR background
+ ;; attribute is set.
+ (eat--t-repeated-insert
+ ?\ n (and (eat--t-face-bg face)
+ (eat--t-face-face face))))))))
+
+(defun eat--t-insert-line (n)
+ "Insert N empty lines, preserving cursor."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (face (eat--t-term-face eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ (scroll-begin (eat--t-term-scroll-begin eat--t-term))
+ (scroll-end (eat--t-term-scroll-end eat--t-term))
+ ;; N should be positive and shouldn't exceed the number of
+ ;; lines below cursor position and inside current scroll
+ ;; region.
+ (n (min (- (1+ (- scroll-end scroll-begin))
+ (1- (eat--t-cur-y cursor)))
+ (max (or n 1) 1))))
+ ;; Make sure we are in the scroll region and N is positive, return
+ ;; on failure.
+ (when (and (<= scroll-begin (eat--t-cur-y cursor) scroll-end)
+ (not (zerop n)))
+ (goto-char
+ (prog1
+ (progn
+ ;; This function doesn't move the cursor, but pushes all
+ ;; the line below and including current line. So to keep
+ ;; the cursor unmoved, go to the beginning of line and
+ ;; insert enough spaces to not move the cursor.
+ (eat--t-goto-bol)
+ (eat--t-repeated-insert ?\ (1- (eat--t-cur-x cursor))
+ (and (eat--t-face-bg face)
+ (eat--t-face-face face)))
+ (point))
+ ;; Insert N lines.
+ (if (not (eat--t-face-bg face))
+ (eat--t-repeated-insert ?\n n)
+ ;; SGR background attribute set, so fill the inserted lines
+ ;; with background.
+ (dotimes (i n)
+ ;; Fill a line.
+ (eat--t-repeated-insert
+ ?\ (if (not (zerop i))
+ (eat--t-disp-width disp)
+ ;; The first inserted line is already filled
+ ;; partially, so calculate the number columns left
+ ;; to fill.
+ (1+ (- (eat--t-disp-width disp)
+ (eat--t-cur-x cursor))))
+ (eat--t-face-face face))
+ ;; New line.
+ (insert ?\n)))
+ ;; Delete the lines that were just pushed beyond the end of
+ ;; scroll region.
+ (eat--t-goto-eol (- (1+ (- scroll-end scroll-begin))
+ (+ (- (eat--t-cur-y cursor)
+ (1- scroll-begin))
+ n)))
+ (delete-region (point) (car (eat--t-eol n))))))))
+
+(defun eat--t-delete-line (n)
+ "Delete N lines, preserving cursor."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (face (eat--t-term-face eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ (x (eat--t-cur-x cursor))
+ (scroll-begin (eat--t-term-scroll-begin eat--t-term))
+ (scroll-end (eat--t-term-scroll-end eat--t-term))
+ ;; N should be positive and shouldn't exceed the number of
+ ;; lines below cursor position and inside current scroll
+ ;; region.
+ (n (min (- (1+ (- scroll-end scroll-begin))
+ (1- (eat--t-cur-y cursor)))
+ (max (or n 1) 1))))
+ ;; Make sure we are in the scroll region and N is positive, return
+ ;; on failure.
+ (when (and (<= scroll-begin (eat--t-cur-y cursor) scroll-end)
+ (not (zerop n)))
+ ;; Delete N lines (including the current one).
+ (eat--t-goto-bol)
+ (save-excursion
+ (let ((m (point)))
+ (eat--t-goto-bol n)
+ (delete-region m (point))))
+ ;; Keep the lines beyond end of scroll region unmoved.
+ (when (or (< scroll-end (eat--t-disp-height disp))
+ (eat--t-face-bg face))
+ (let* ((pos (point))
+ (move (- (1+ (- scroll-end scroll-begin))
+ (- (+ (eat--t-cur-y cursor) n)
+ (1- scroll-begin))))
+ (moved (eat--t-goto-eol move)))
+ (when (or (/= (point) (point-max))
+ (eat--t-face-bg face))
+ ;; Move to the end of scroll region.
+ (eat--t-repeated-insert ?\n (- move moved))
+ ;; Insert enough new lines, fill them when SGR background
+ ;; attribute is set.
+ (if (not (eat--t-face-bg face))
+ (eat--t-repeated-insert ?\n n)
+ (dotimes (_ n)
+ (insert ?\n)
+ (eat--t-repeated-insert ?\ (eat--t-disp-width disp)
+ (eat--t-face-face face)))))
+ (goto-char pos)))
+ ;; Go to column where cursor is to preserve cursor position, use
+ ;; spaces if needed to reach the position.
+ (eat--t-repeated-insert
+ ?\ (- (1- x) (eat--t-col-motion (1- x)))))))
+
+(defun eat--t-repeat-last-char (&optional n)
+ "Repeat last character N times."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ ;; N must be at least one.
+ (n (max (or n 1) 1))
+ (char
+ ;; Get the character before cursor.
+ (when (< (eat--t-disp-begin disp) (point))
+ (if (get-text-property (1- (point)) 'eat--t-wrap-line)
+ ;; The character before cursor is a newline to break
+ ;; a long line, so use the character before that.
+ (when (< (eat--t-disp-begin disp) (1- (point)))
+ (char-before (1- (point))))
+ (char-before)))))
+ ;; Insert `char' N times. Make sure `char' is a non-nil and not
+ ;; a newline.
+ (when (and char (/= char ?\n))
+ (eat--t-write (make-string n char)))))
+
+(defun eat--t-change-scroll-region (&optional top bottom)
+ "Change the scroll region from lines TOP to BOTTOM (inclusive).
+
+TOP defaults to 1 and BOTTOM defaults to the height of the display."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (top (or top 1))
+ (bottom (or bottom (eat--t-disp-height disp))))
+ ;; According to DEC's documentation (found somewhere on the
+ ;; internet, but can't remember where), TOP and BOTTOM must be
+ ;; within display, and BOTTOM must be below TOP. Otherwise the
+ ;; control function is a nop.
+ (when (< 0 top bottom (1+ (eat--t-disp-height disp)))
+ (setf (eat--t-term-scroll-begin eat--t-term) top)
+ (setf (eat--t-term-scroll-end eat--t-term) bottom)
+ (eat--t-goto 1 1))))
+
+(defun eat--t-insert-mode ()
+ "Enable insert mode and disable replace mode."
+ (setf (eat--t-term-ins-mode eat--t-term) t))
+
+(defun eat--t-replace-mode ()
+ "Enable replace mode and disable insert mode."
+ (setf (eat--t-term-ins-mode eat--t-term) nil))
+
+(defun eat--t-set-sgr-params (params)
+ "Set SGR parameters PARAMS."
+ (let ((face (eat--t-term-face eat--t-term)))
+ ;; Set attributes.
+ (while params
+ (pcase (pop params)
+ (`(,(or 0 'nil))
+ (1value (setf (eat--t-face-fg face) nil))
+ (1value (setf (eat--t-face-bg face) nil))
+ (1value (setf (eat--t-face-intensity face) nil))
+ (1value (setf (eat--t-face-italic face) nil))
+ (1value (setf (eat--t-face-underline face) nil))
+ (1value (setf (eat--t-face-underline-color face) nil))
+ (1value (setf (eat--t-face-crossed face) nil))
+ (1value (setf (eat--t-face-conceal face) nil))
+ (1value (setf (eat--t-face-inverse face) nil))
+ (1value (setf (eat--t-face-blink face) nil))
+ (1value (setf (eat--t-face-font face) 'eat-term-font-0)))
+ ('(1)
+ (1value (setf (eat--t-face-intensity face) 'eat-term-bold)))
+ ('(2)
+ (1value (setf (eat--t-face-intensity face) 'eat-term-faint)))
+ ('(3)
+ (1value (setf (eat--t-face-italic face) 'eat-term-italic)))
+ ('(4)
+ (1value (setf (eat--t-face-underline face) 'line)))
+ ('(4 0)
+ (1value (setf (eat--t-face-underline face) nil)))
+ ('(4 1)
+ (1value (setf (eat--t-face-underline face) 'line)))
+ ('(4 2)
+ (1value (setf (eat--t-face-underline face) 'line)))
+ ('(4 3)
+ (1value (setf (eat--t-face-underline face) 'wave)))
+ ('(4 4)
+ (1value (setf (eat--t-face-underline face) 'wave)))
+ ('(4 5)
+ (1value (setf (eat--t-face-underline face) 'wave)))
+ ('(5)
+ (1value (setf (eat--t-face-blink face) 'eat-term-slow-blink)))
+ ('(6)
+ (setf (eat--t-face-blink face) 'eat-term-fast-blink))
+ ('(7)
+ (1value (1value (setf (eat--t-face-inverse face) t))))
+ ('(8)
+ (1value (setf (eat--t-face-conceal face) t)))
+ ('(9)
+ (1value (setf (eat--t-face-crossed face) t)))
+ (`(,(and (pred (lambda (font) (<= 10 font 19)))
+ font))
+ (setf (eat--t-face-font face)
+ (intern (format "eat-term-font-%i" (- font 10)))))
+ ('(21)
+ (1value (setf (eat--t-face-underline face) 'line)))
+ ('(22)
+ (1value (setf (eat--t-face-intensity face) nil)))
+ ('(23)
+ (1value (setf (eat--t-face-italic face) nil)))
+ ('(24)
+ (1value (setf (eat--t-face-underline face) nil)))
+ ('(25)
+ (1value (setf (eat--t-face-blink face) nil)))
+ ('(27)
+ (1value (setf (eat--t-face-inverse face) nil)))
+ ('(28)
+ (1value (setf (eat--t-face-conceal face) nil)))
+ ('(29)
+ (1value (setf (eat--t-face-crossed face) nil)))
+ (`(,(and (pred (lambda (color) (<= 30 color 37)))
+ color))
+ (setf (eat--t-face-fg face)
+ (face-foreground
+ (intern (format "eat-term-color-%i" (- color 30)))
+ nil t)))
+ ('(38)
+ (pcase (pop params)
+ ('(2)
+ (setf (eat--t-face-fg face)
+ (let ((r (car (pop params)))
+ (g (car (pop params)))
+ (b (car (pop params))))
+ (when (and r (<= 0 r 255)
+ g (<= 0 g 255)
+ b (<= 0 b 255))
+ (format "#%02x%02x%02x" r g b)))))
+ ('(5)
+ (let ((color (car (pop params))))
+ (setf (eat--t-face-fg face)
+ (when (and color (<= 0 color 255))
+ (face-foreground
+ (intern (format "eat-term-color-%i" color))
+ nil t)))))))
+ ('(39)
+ (1value (setf (eat--t-face-fg face) nil)))
+ (`(,(and (pred (lambda (color) (<= 40 color 47)))
+ color))
+ (setf (eat--t-face-bg face)
+ (face-foreground
+ (intern (format "eat-term-color-%i" (- color 40)))
+ nil t)))
+ ('(48)
+ (setf (eat--t-face-bg face)
+ (pcase (pop params)
+ ('(2)
+ (let ((r (car (pop params)))
+ (g (car (pop params)))
+ (b (car (pop params))))
+ (when (and r (<= 0 r 255)
+ g (<= 0 g 255)
+ b (<= 0 b 255))
+ (format "#%02x%02x%02x" r g b))))
+ ('(5)
+ (let ((color (car (pop params))))
+ (when (and color (<= 0 color 255))
+ (face-foreground
+ (intern (format "eat-term-color-%i" color))
+ nil t)))))))
+ ('(49)
+ (1value (setf (eat--t-face-bg face) nil)))
+ ('(58)
+ (setf (eat--t-face-underline-color face)
+ (pcase (pop params)
+ ('(2)
+ (let ((r (car (pop params)))
+ (g (car (pop params)))
+ (b (car (pop params))))
+ (when (and r (<= 0 r 255)
+ g (<= 0 g 255)
+ b (<= 0 b 255))
+ (format "#%02x%02x%02x" r g b))))
+ ('(5)
+ (let ((color (car (pop params))))
+ (when (and color (<= 0 color 255))
+ (face-foreground
+ (intern (format "eat-term-color-%i" color))
+ nil t)))))))
+ ('(59)
+ (1value (setf (eat--t-face-underline-color face) nil)))
+ (`(,(and (pred (lambda (color) (<= 90 color 97)))
+ color))
+ (setf (eat--t-face-fg face)
+ (face-foreground
+ (intern (format "eat-term-color-%i" (- color 82)))
+ nil t)))
+ (`(,(and (pred (lambda (color) (<= 100 color 107)))
+ color))
+ (setf (eat--t-face-bg face)
+ (face-foreground
+ (intern (format "eat-term-color-%i" (- color 92)))
+ nil t)))))
+ ;; Update face according to the attributes.
+ (setf (eat--t-face-face face)
+ `(,@(when-let ((fg (or (if (eat--t-face-conceal face)
+ (eat--t-face-bg face)
+ (eat--t-face-fg face))
+ (cond
+ ((eat--t-face-inverse face)
+ (face-foreground 'default))
+ ((eat--t-face-conceal face)
+ (face-background 'default))))))
+ (list (if (eat--t-face-inverse face)
+ :background
+ :foreground)
+ fg))
+ ,@(when-let ((bg (or (eat--t-face-bg face)
+ (and (eat--t-face-inverse face)
+ (face-background 'default)))))
+ (list (if (eat--t-face-inverse face)
+ :foreground
+ :background)
+ bg))
+ ,@(when-let ((underline (eat--t-face-underline face)))
+ (list
+ :underline
+ (list :color (eat--t-face-underline-color face)
+ :style underline)))
+ ,@(when-let ((crossed (eat--t-face-crossed face)))
+ ;; REVIEW: How about colors? No terminal supports
+ ;; crossed attribute with colors, so we'll need to be
+ ;; creative to add the feature.
+ `(:strike-through t))
+ :inherit
+ (,@(when-let ((intensity (eat--t-face-intensity face)))
+ (list intensity))
+ ,@(when-let ((italic (eat--t-face-italic face)))
+ (cl-assert (1value (eq (1value italic)
+ 'eat-term-italic)))
+ (list (1value italic)))
+ ,@(when-let ((blink (eat--t-face-blink face)))
+ (list blink))
+ ,(eat--t-face-font face))))))
+
+(defun eat--t-enable-keypad ()
+ "Enable keypad."
+ (1value (setf (eat--t-term-keypad-mode eat--t-term) t)))
+
+(defun eat--t-disable-keypad ()
+ "Disable keypad."
+ (1value (setf (eat--t-term-keypad-mode eat--t-term) nil)))
+
+(defun eat--t-enable-sgr-mouse-encoding ()
+ "Arrange that the following mouse events will be encoded like SGR."
+ (setf (eat--t-term-mouse-encoding eat--t-term) 'sgr))
+
+(defun eat--t-disable-sgr-mouse-encoding ()
+ "Arrange that the following mouse events won't be encoded like SGR."
+ (setf (eat--t-term-mouse-encoding eat--t-term) nil))
+
+(defun eat--t-set-mouse-mode (mode)
+ "Set current mouse mode to MODE.
+
+MODE should be one of nil and `x10', `normal', `button-event',
+`any-event'."
+ (setf (eat--t-term-mouse-mode eat--t-term) mode)
+ ;; When MODE is nil, disable mouse.
+ (unless mode
+ (eat--t-disable-sgr-mouse-encoding))
+ ;; `x10' mouse mode doesn't need to keep track of the mouse buttons
+ ;; pressed.
+ (when (or (not mode)
+ (eq mode 'x10))
+ (setf (eat--t-term-mouse-pressed eat--t-term) nil))
+ ;; Inform the UI.
+ (funcall (eat--t-term-grab-mouse-fn eat--t-term) eat--t-term
+ (pcase mode
+ ('x10 :click)
+ ('normal :modifier-click)
+ ('button-event :drag)
+ ('any-event :all))))
+
+(defun eat--t-enable-x10-mouse ()
+ "Enable X10 mouse tracking."
+ (eat--t-set-mouse-mode 'x10))
+
+(defun eat--t-enable-normal-mouse ()
+ "Enable normal mouse tracking."
+ (eat--t-set-mouse-mode 'normal))
+
+(defun eat--t-enable-button-event-mouse ()
+ "Enable button-event mouse tracking."
+ (eat--t-set-mouse-mode 'button-event))
+
+(defun eat--t-enable-any-event-mouse ()
+ "Enable any-event mouse tracking."
+ (eat--t-set-mouse-mode 'any-event))
+
+(defun eat--t-disable-mouse ()
+ "Disable mouse tracking."
+ (eat--t-set-mouse-mode nil))
+
+(defun eat--t-enable-focus-event ()
+ "Enable sending focus events."
+ (1value (setf (eat--t-term-focus-event-mode eat--t-term) t))
+ (funcall (eat--t-term-set-focus-ev-mode-fn eat--t-term) eat--t-term
+ t))
+
+(defun eat--t-disable-focus-event ()
+ "Disable sending focus events."
+ (1value (setf (eat--t-term-focus-event-mode eat--t-term) nil))
+ (funcall (eat--t-term-set-focus-ev-mode-fn eat--t-term) eat--t-term
+ nil))
+
+(defun eat--t-set-title (title)
+ "Set the title of terminal to TITLE."
+ ;; Update title.
+ (setf (eat--t-term-title eat--t-term) title)
+ ;; Inform the UI.
+ (funcall (eat--t-term-set-title-fn eat--t-term) eat--t-term title))
+
+(defun eat--t-send-device-attrs (params format)
+ "Return device attributes.
+
+PARAMS is the parameter list and FORMAT is the format of parameters in
+output."
+ (let ((params (or params '((0)))))
+ (pcase format
+ ('nil
+ (when (= (caar params) 0)
+ (funcall (eat--t-term-input-fn eat--t-term) eat--t-term
+ "\e[?1;2c")))
+ (?>
+ (when (= (caar params) 0)
+ (funcall (eat--t-term-input-fn eat--t-term) eat--t-term
+ "\e[>0;242;0c"))))))
+
+(defun eat--t-report-foreground-color ()
+ "Report the current default foreground color to the client."
+ (funcall
+ (eat--t-term-input-fn eat--t-term) eat--t-term
+ (let ((rgb (color-values (face-foreground 'default))))
+ (format "\e]10;%04x/%04x/%04x\e\\"
+ (pop rgb) (pop rgb) (pop rgb)))))
+
+(defun eat--t-report-background-color ()
+ "Report the current default background color to the client."
+ (funcall
+ (eat--t-term-input-fn eat--t-term) eat--t-term
+ (let ((rgb (color-values (face-background 'default))))
+ (format "\e]11;%04x/%04x/%04x\e\\"
+ (pop rgb) (pop rgb) (pop rgb)))))
+
+(defun eat--t-manipulate-selection (targets data)
+ "Set and send current selection.
+
+TARGETS is a string containing zero or more characters from the set
+`c', `p', `q', `s', `0', `1', `2', `3', `4', `5', `6', `7', `8', `9'.
+DATA is the selection data encoded in base64."
+ (when (string-empty-p targets)
+ (setq targets "s0"))
+ (if (string= data "?")
+ ;; The client is requesting for clipboard content, let's try to
+ ;; fulfill the request.
+ (funcall
+ (eat--t-term-input-fn eat--t-term) eat--t-term
+ (let ((str nil)
+ (source nil)
+ (n 0))
+ (while (and (not str) (< n (length targets)))
+ (setq
+ str
+ (pcase (aref targets n)
+ ;; c, p, q and s targets are handled by the UI, and they
+ ;; might refuse to give the clipboard content.
+ (?c
+ (funcall
+ (eat--t-term-manipulate-selection-fn eat--t-term)
+ eat--t-term :clipboard t))
+ (?p
+ (funcall
+ (eat--t-term-manipulate-selection-fn eat--t-term)
+ eat--t-term :primary t))
+ (?q
+ (funcall
+ (eat--t-term-manipulate-selection-fn eat--t-term)
+ eat--t-term :secondary t))
+ (?s
+ (funcall
+ (eat--t-term-manipulate-selection-fn eat--t-term)
+ eat--t-term :select t))
+ ;; 0 to 9 targets are handled by us, and always work.
+ ((and (pred (<= ?0))
+ (pred (>= ?9))
+ i)
+ (aref (eat--t-term-cut-buffers eat--t-term)
+ (- i ?0)))))
+ ;; If we got a string to send, record the source to inform
+ ;; the client.
+ (when str
+ (setq source (string (aref targets n))))
+ (cl-incf n))
+ ;; No string to send, so send an empty string and an empty
+ ;; target string meaning that we don't have any answer.
+ (unless str
+ (setq str "")
+ (setq source ""))
+ (format "\e]52;%s;%s\e\\" source
+ (base64-encode-string str))))
+ ;; The client is requesting to set clipboard content, let's try to
+ ;; fulfill the request.
+ (let ((str (ignore-error error
+ (decode-coding-string (base64-decode-string data)
+ locale-coding-system))))
+ (seq-doseq (target targets)
+ (pcase target
+ ;; c, p, q and s targets are handled by the UI, and they
+ ;; might reject the new clipboard content.
+ (?c
+ (funcall (eat--t-term-manipulate-selection-fn eat--t-term)
+ eat--t-term :clipboard str))
+ (?p
+ (funcall (eat--t-term-manipulate-selection-fn eat--t-term)
+ eat--t-term :primary str))
+ (?q
+ (funcall (eat--t-term-manipulate-selection-fn eat--t-term)
+ eat--t-term :secondary str))
+ (?s
+ (funcall (eat--t-term-manipulate-selection-fn eat--t-term)
+ eat--t-term :select str))
+ ;; 0 to 9 targets are handled by us, and always work.
+ ((and (pred (<= ?0))
+ (pred (>= ?9))
+ i)
+ (aset (eat--t-term-cut-buffers eat--t-term) (- i ?0)
+ str)))))))
+
+(defun eat--t-set-modes (params format)
+ "Set modes according to PARAMS in format FORMAT."
+ ;; Dispatch the request to appropriate function.
+ (pcase format
+ ('nil
+ (while params
+ (pcase (pop params)
+ ('(4)
+ (eat--t-insert-mode)))))
+ (??
+ (while params
+ (pcase (pop params)
+ ('(1)
+ (eat--t-enable-keypad))
+ ('(7)
+ (eat--t-enable-auto-margin))
+ ('(9)
+ (eat--t-enable-x10-mouse))
+ ('(12)
+ (eat--t-blinking-cursor))
+ ('(25)
+ (eat--t-default-cursor))
+ ('(1000)
+ (eat--t-enable-normal-mouse))
+ ('(1002)
+ (eat--t-enable-button-event-mouse))
+ ('(1003)
+ (eat--t-enable-any-event-mouse))
+ ('(1004)
+ (eat--t-enable-focus-event))
+ ('(1006)
+ (eat--t-enable-sgr-mouse-encoding))
+ ('(1048)
+ (eat--t-save-cur))
+ (`(,(or 1047 1049))
+ (eat--t-enable-alt-disp))
+ ('(2004)
+ (eat--t-enable-bracketed-yank)))))))
+
+(defun eat--t-reset-modes (params format)
+ "Reset modes according to PARAMS in format FORMAT."
+ ;; Dispatch the request to appropriate function.
+ (pcase format
+ ('nil
+ (while params
+ (pcase (pop params)
+ ('(4)
+ (eat--t-replace-mode)))))
+ (??
+ (while params
+ (pcase (pop params)
+ ('(1)
+ (eat--t-disable-keypad))
+ ('(7)
+ (eat--t-disable-auto-margin))
+ ('(12)
+ (eat--t-non-blinking-cursor))
+ ('(25)
+ (eat--t-invisible-cursor))
+ (`(,(or 9 1000 1002 1003))
+ (eat--t-disable-mouse))
+ ('(1004)
+ (eat--t-disable-focus-event))
+ ('(1006)
+ (eat--t-disable-sgr-mouse-encoding))
+ ('(1047)
+ (eat--t-disable-alt-disp 'dont-move-cursor))
+ ('(1048)
+ (eat--t-restore-cur))
+ ('(1049)
+ (eat--t-disable-alt-disp))
+ ('(2004)
+ (eat--t-disable-bracketed-yank)))))))
+
+(defun eat--t-handle-output (output)
+ "Parse and evaluate OUTPUT."
+ (let ((index 0))
+ (while (< index (length output))
+ (pcase (eat--t-term-parser-state eat--t-term)
+ ('nil
+ ;; Regular expression to find the end of plain text.
+ (let ((match (string-match
+ (1value (rx (or ?\0 ?\a ?\b ?\t ?\n ?\v
+ ?\f ?\r ?\C-n ?\C-o ?\e
+ #x7f)))
+ output index)))
+ (if (not match)
+ ;; The regex didn't match, so everything left to handle
+ ;; is just plain text.
+ (progn
+ (eat--t-write (substring output index))
+ (setq index (length output)))
+ (when (/= match index)
+ ;; The regex matched, and the position is after the
+ ;; current position. Process the plain text between
+ ;; them and advance to the control sequence.
+ (eat--t-write (substring output index match))
+ (setq index match))
+ ;; Dispatch control sequence.
+ (cl-incf index)
+ (pcase (aref output (1- index))
+ (?\a
+ (eat--t-bell))
+ (?\b
+ (eat--t-cur-left 1))
+ (?\t
+ (eat--t-horizontal-tab 1))
+ (?\n
+ (eat--t-line-feed))
+ (?\v
+ (eat--t-index))
+ (?\f
+ (eat--t-form-feed))
+ (?\r
+ ;; Avoid going to line home just before a line feed,
+ ;; we can just insert a new line if we are at the
+ ;; end of display.
+ (unless (and (/= index (length output))
+ (= (aref output index) ?\n))
+ (eat--t-carriage-return)))
+ (?\C-n
+ (eat--t-change-charset 'g1))
+ (?\C-o
+ (eat--t-change-charset 'g0))
+ (?\e
+ (1value (setf (eat--t-term-parser-state eat--t-term)
+ '(read-esc))))))))
+ ('(read-esc)
+ (let ((type (aref output index)))
+ (cl-incf index)
+ (1value (setf (eat--t-term-parser-state eat--t-term) nil))
+ ;; Dispatch control sequence.
+ (pcase type
+ ;; ESC (.
+ (?\(
+ (setf (eat--t-term-parser-state eat--t-term)
+ '(read-charset-standard g0 "")))
+ ;; ESC ).
+ (?\)
+ (setf (eat--t-term-parser-state eat--t-term)
+ '(read-charset-standard g1 "")))
+ ;; ESC *.
+ (?*
+ (setf (eat--t-term-parser-state eat--t-term)
+ '(read-charset-standard g2 "")))
+ ;; ESC +.
+ (?+
+ (setf (eat--t-term-parser-state eat--t-term)
+ '(read-charset-standard g3 "")))
+ ;; ESC -.
+ (?-
+ (setf (eat--t-term-parser-state eat--t-term)
+ '(read-charset-vt300 g1 "")))
+ ;; ESC ..
+ (?.
+ (setf (eat--t-term-parser-state eat--t-term)
+ '(read-charset-vt300 g2 "")))
+ ;; ESC /.
+ (?/
+ (setf (eat--t-term-parser-state eat--t-term)
+ '(read-charset-vt300 g3 "")))
+ ;; ESC 7.
+ (?7
+ (eat--t-save-cur))
+ ;; ESC 8.
+ (?8
+ (eat--t-restore-cur))
+ ;; ESC D.
+ (?D
+ (eat--t-index))
+ ;; ESC E.
+ (?E
+ (eat--t-line-feed))
+ ;; ESC M.
+ (?M
+ (eat--t-reverse-index))
+ ;; ESC P, or DCS.
+ (?P
+ (1value (setf (eat--t-term-parser-state eat--t-term)
+ '(read-dcs ""))))
+ ;; ESC X, or SOS.
+ (?X
+ (1value (setf (eat--t-term-parser-state eat--t-term)
+ '(read-sos ""))))
+ ;; ESC [, or CSI.
+ (?\[
+ (1value (setf (eat--t-term-parser-state eat--t-term)
+ '(read-csi ""))))
+ ;; ESC ], or OSC.
+ (?\]
+ (1value (setf (eat--t-term-parser-state eat--t-term)
+ '(read-osc ""))))
+ ;; ESC ^, or PM.
+ (?^
+ (1value (setf (eat--t-term-parser-state eat--t-term)
+ '(read-pm ""))))
+ ;; ESC _, or APC.
+ (?_
+ (1value (setf (eat--t-term-parser-state eat--t-term)
+ '(read-apc ""))))
+ ;; ESC c.
+ (?c
+ (eat--t-reset))
+ ;; ESC n.
+ (?n
+ (eat--t-change-charset 'g2))
+ ;; ESC o.
+ (?o
+ (eat--t-change-charset 'g3)))))
+ (`(read-csi ,buf)
+ (let ((match (string-match (rx (any (#x40 . #x7e)))
+ output index)))
+ (if (not match)
+ (progn
+ (setf (eat--t-term-parser-state eat--t-term)
+ `(read-csi ,(concat buf (substring output
+ index))))
+ (setq index (length output)))
+ (setf (eat--t-term-parser-state eat--t-term) nil)
+ (pcase
+ (let ((str (concat buf (substring output index
+ match)))
+ (format nil)
+ (intermediate-bytes ""))
+ (save-match-data
+ (when (string-match
+ (rx (zero-or-more (any (? . ?/)))
+ string-end)
+ str)
+ (setq str (substring
+ str 0 (match-beginning 0)))
+ (setq intermediate-bytes
+ (match-string 0 str))))
+ (when (and (not (string-empty-p str))
+ (= (aref str 0) ??))
+ (setq format ??)
+ (setq str (substring str 1)))
+ (when (and (not (string-empty-p str))
+ (= (aref str 0) ?>))
+ (setq format ?>)
+ (setq str (substring str 1)))
+ (when (and (not (string-empty-p str))
+ (= (aref str 0) ?=))
+ (setq format ?=)
+ (setq str (substring str 1)))
+ (setq index (match-end 0))
+ (list
+ (concat intermediate-bytes
+ (match-string 0 output))
+ format
+ (cond
+ ((string-empty-p str) '((nil)))
+ ((<= #x30 (aref str 0) #x3b)
+ (mapcar (lambda (p)
+ (mapcar (lambda (s)
+ (unless (string-empty-p s)
+ (string-to-number s)))
+ (split-string p ":")))
+ (split-string str ";")))
+ (t str))))
+ ;; CSI <n> @.
+ (`("@" nil ,(and (pred listp) params))
+ (eat--t-insert-char (caar params)))
+ ;; CSI <n> A.
+ ;; CSI <n> k.
+ (`(,(or "A" "k") nil ,(and (pred listp) params))
+ (eat--t-cur-up (caar params)))
+ ;; CSI <n> B.
+ ;; CSI <n> e.
+ (`(,(or "B" "e") nil ,(and (pred listp) params))
+ (eat--t-cur-down (caar params)))
+ ;; CSI <n> C.
+ ;; CSI <n> a.
+ (`(,(or "C" "a") nil ,(and (pred listp) params))
+ (eat--t-cur-right (caar params)))
+ ;; CSI <n> D.
+ ;; CSI <n> j.
+ (`(,(or "D" "j") nil ,(and (pred listp) params))
+ (eat--t-cur-left (caar params)))
+ ;; CSI <n> E.
+ (`("E" nil ,(and (pred listp) params))
+ (eat--t-beg-of-prev-line (caar params)))
+ ;; CSI <n> F.
+ (`("F" nil ,(and (pred listp) params))
+ (eat--t-beg-of-next-line (caar params)))
+ ;; CSI <n> G.
+ ;; CSI <n> `.
+ (`(,(or "G" "`") nil ,(and (pred listp) params))
+ (eat--t-cur-horizontal-abs (caar params)))
+ ;; CSI <n> ; <m> H
+ ;; CSI <n> ; <m> f
+ (`(,(or "H" "f") nil ,(and (pred listp) params))
+ (eat--t-goto (caar params) (caadr params)))
+ ;; CSI <n> I.
+ (`("I" nil ,(and (pred listp) params))
+ (eat--t-horizontal-tab (caar params)))
+ ;; CSI <n> J.
+ (`("J" nil ,(and (pred listp) params))
+ (eat--t-erase-in-disp (caar params)))
+ ;; CSI <n> K.
+ (`("K" nil ,(and (pred listp) params))
+ (eat--t-erase-in-line (caar params)))
+ ;; CSI <n> L.
+ (`("L" nil ,(and (pred listp) params))
+ (eat--t-insert-line (caar params)))
+ ;; CSI <n> M.
+ (`("M" nil ,(and (pred listp) params))
+ (eat--t-delete-line (caar params)))
+ ;; CSI <n> P.
+ (`("P" nil ,(and (pred listp) params))
+ (eat--t-delete-char (caar params)))
+ ;; CSI <n> S.
+ (`("S" nil ,(and (pred listp) params))
+ (eat--t-scroll-up (caar params)))
+ ;; CSI <n> T.
+ (`("T" nil ,(and (pred listp) params))
+ (eat--t-scroll-down (caar params)))
+ ;; CSI <n> X.
+ (`("X" nil ,(and (pred listp) params))
+ (eat--t-erase-char (caar params)))
+ ;; CSI <n> Z.
+ (`("Z" nil ,(and (pred listp) params))
+ (eat--t-horizontal-backtab (caar params)))
+ ;; CSI <n> b.
+ (`("b" nil ,(and (pred listp) params))
+ (eat--t-repeat-last-char (caar params)))
+ ;; CSI <n> c.
+ ;; CSI > <n> c.
+ (`("c" ,format ,(and (pred listp) params))
+ (eat--t-send-device-attrs params format))
+ ;; CSI <n> d.
+ (`("d" nil ,(and (pred listp) params))
+ (eat--t-cur-vertical-abs (caar params)))
+ ;; CSI ... h.
+ ;; CSI ? ... h.
+ (`("h" ,format ,(and (pred listp) params))
+ (eat--t-set-modes params format))
+ ;; CSI ... l.
+ ;; CSI ? ... l.
+ (`("l" ,format ,(and (pred listp) params))
+ (eat--t-reset-modes params format))
+ ;; CSI ... m.
+ (`("m" nil ,(and (pred listp) params))
+ (eat--t-set-sgr-params params))
+ ;; CSI 6 n.
+ ('("n" nil ((6)))
+ (eat--t-device-status-report))
+ ;; CSI <n> ; <n> r.
+ (`("r" nil ,(and (pred listp) params))
+ (eat--t-change-scroll-region (caar params)
+ (caadr params)))
+ ;; CSI s.
+ (`("s" nil nil)
+ (eat--t-save-cur))
+ ;; CSI u.
+ (`("u" nil nil)
+ (eat--t-restore-cur))))))
+ (`(,(and (or 'read-dcs 'read-sos 'read-osc 'read-pm 'read-apc)
+ state)
+ ,buf)
+ ;; Find the end of string.
+ (let ((match (string-match (if (eq state 'read-osc)
+ (rx (or ?\a ?\\))
+ (rx ?\\))
+ output index)))
+ (if (not match)
+ (progn
+ ;; Not found, store the text to process it later when
+ ;; we get the end of string.
+ (setf (eat--t-term-parser-state eat--t-term)
+ `(,state ,(concat buf (substring output
+ index))))
+ (setq index (length output)))
+ ;; Matched! Get the string from the output and previous
+ ;; runs.
+ (let ((str (concat buf (substring output index
+ match))))
+ (setq index (match-end 0))
+ ;; Is it really the end of string?
+ (if (and (= (aref output match) ?\\)
+ (not (or (zerop (length str))
+ (= (aref str (1- (length str)))
+ ?\e))))
+ ;; No. Push the '\' character to process later.
+ (setf (eat--t-term-parser-state eat--t-term)
+ `(,state ,(concat str "\\")))
+ ;; Yes! It's the end! We can parse it.
+ (when (= (aref output match) ?\\)
+ (setq str (substring str 0 (1- (length str)))))
+ (setf (eat--t-term-parser-state eat--t-term) nil)
+ ;; Dispatch control sequence.
+ (pcase state
+ ('read-osc
+ (pcase str
+ ;; OSC 0 ; <t> ST.
+ ;; OSC 2 ; <t> ST.
+ ((rx string-start (or ?0 ?2) ?\;
+ (let title (zero-or-more anything))
+ string-end)
+ (eat--t-set-title title))
+ ;; OSC 10 ; ? ST.
+ ("10;?"
+ (eat--t-report-foreground-color))
+ ;; OSC 11 ; ? ST.
+ ("11;?"
+ (eat--t-report-background-color))
+ ;; OSC 52 ; <t> ; <s> ST.
+ ((rx string-start "52;"
+ (let targets
+ (zero-or-more (any ?c ?p ?q ?s
+ (?0 . ?9))))
+ ";"
+ (let data (zero-or-more anything))
+ string-end)
+ (eat--t-manipulate-selection
+ targets data))))))))))
+ (`(read-charset-standard ,slot ,buf)
+ ;; Find the end.
+ (let ((match (string-match (rx (any ?0 ?2 ?4 ?5 ?6 ?7 ?9 ?<
+ ?= ?> ?? ?A ?B ?C ?E ?H
+ ?K ?Q ?R ?Y ?Z ?f))
+ output index)))
+ (if (not match)
+ (progn
+ ;; Not found, store the text to process it later when
+ ;; we find the end.
+ (setf (eat--t-term-parser-state eat--t-term)
+ `(read-charset-standard
+ ,slot ,(concat buf (substring
+ output index))))
+ (setq index (length output)))
+ ;; Got the end!
+ (let ((str (concat buf (substring output index
+ (match-end 0)))))
+ (setq index (match-end 0))
+ (setf (eat--t-term-parser-state eat--t-term) nil)
+ ;; Set the character set.
+ (eat--t-set-charset
+ slot
+ (pcase str
+ ;; ESC ( 0.
+ ;; ESC ) 0.
+ ;; ESC * 0.
+ ;; ESC + 0.
+ ("0" 'dec-line-drawing)
+ ;; ESC ( B.
+ ;; ESC ) B.
+ ;; ESC * B.
+ ;; ESC + B.
+ ("B" 'us-ascii)))))))
+ (`(read-charset-vt300 ,_slot)
+ (let ((_charset (aref output index)))
+ (cl-incf index)
+ (setf (eat--t-term-parser-state eat--t-term) nil)
+ (pcase charset
+ ;; TODO: Currently ignored. It is here just to recognize
+ ;; the control sequence.
+ )))))))
+
+(defun eat--t-resize (width height)
+ "Resize terminal to WIDTH x HEIGHT."
+ (let* ((disp (eat--t-term-display eat--t-term))
+ (cursor (eat--t-disp-cursor disp))
+ (old-width (eat--t-disp-width disp))
+ (old-height (eat--t-disp-height disp)))
+ ;; Don't do anything if size hasn't changed, or the new size is
+ ;; too small.
+ (when (and (not (and (eq old-width width)
+ (eq old-height height)))
+ (>= width 1)
+ (>= height 1))
+ ;; Update state.
+ (setf (eat--t-disp-width disp) width)
+ (setf (eat--t-disp-height disp) height)
+ (setf (eat--t-term-scroll-begin eat--t-term) 1)
+ (setf (eat--t-term-scroll-end eat--t-term)
+ (eat--t-disp-height disp))
+ (set-marker (eat--t-cur-position cursor) (point))
+ (if (eat--t-term-main-display eat--t-term)
+ ;; For alternative display, just delete the part of the
+ ;; display that went out of the edges. So if the terminal
+ ;; was enlarged, we don't have anything to do.
+ (when (or (< width old-width)
+ (< height old-height))
+ ;; Go to the beginning of display.
+ (goto-char (eat--t-disp-begin disp))
+ (dotimes (l height)
+ (eat--t-col-motion width)
+ (delete-region (point) (car (eat--t-eol)))
+ (if (< (1+ l) height)
+ (forward-char)
+ (delete-region (point) (point-max))
+ (let ((y (eat--t-cur-y cursor))
+ (x (eat--t-cur-x cursor)))
+ (eat--t-goto 1 1)
+ (eat--t-goto y x)))))
+ ;; REVIEW: This works, but it is very simple. Most
+ ;; terminals have more sophisticated mechanisms to do this.
+ ;; It would be nice thing have them here.
+ ;; Go to the beginning of display.
+ (goto-char (eat--t-disp-begin disp))
+ ;; Try to move to the end of previous line, maybe that's a
+ ;; part of a too long line.
+ (unless (bobp)
+ (backward-char))
+ ;; Join all long lines.
+ (while (not (eobp))
+ (eat--t-join-long-line))
+ ;; Go to display beginning again and break long lines.
+ (goto-char (eat--t-disp-begin disp))
+ (while (not (eobp))
+ (eat--t-break-long-line (eat--t-disp-width disp)))
+ ;; Calculate the beginning position of display.
+ (goto-char (point-max))
+ ;; TODO: This part needs explanation.
+ (let* ((disp-begin (car (eat--t-bol (- (1- height))))))
+ (when (< (eat--t-disp-begin disp) disp-begin)
+ (goto-char (max (- (eat--t-disp-begin disp) 1)
+ (point-min)))
+ (set-marker (eat--t-disp-begin disp) disp-begin)
+ (while (< (point) (1- (eat--t-disp-begin disp)))
+ (eat--t-join-long-line
+ (1- (eat--t-disp-begin disp))))))
+ ;; Update the cursor if needed.
+ (when (< (eat--t-cur-position cursor)
+ (eat--t-disp-begin disp))
+ (set-marker (eat--t-cur-position cursor)
+ (eat--t-disp-begin disp)))
+ ;; Update the coordinates of cursor.
+ (goto-char (eat--t-cur-position cursor))
+ (setf (eat--t-cur-x cursor) (1+ (eat--t-current-col)))
+ (goto-char (eat--t-disp-begin disp))
+ (setf (eat--t-cur-y cursor)
+ (let ((y 0))
+ (while (< (point) (eat--t-cur-position cursor))
+ (condition-case nil
+ (search-forward
+ "\n" (eat--t-cur-position cursor))
+ (search-failed
+ (goto-char (eat--t-cur-position cursor))))
+ (cl-incf y))
+ (when (or (= (point) (point-min))
+ (= (char-before) ?\n))
+ (cl-incf y))
+ (max y 1)))))))
+
+;;;###autoload
+(defun eat-term-make (buffer position)
+ "Make a Eat terminal at POSITION in BUFFER."
+ (eat--t-make-term
+ :buffer buffer
+ :begin (copy-marker position t)
+ :end (copy-marker position)
+ :display (eat--t-make-disp
+ :begin (copy-marker position)
+ :old-begin (copy-marker position)
+ :cursor (eat--t-make-cur
+ :position (copy-marker position)))))
+
+(defmacro eat--t-with-env (terminal &rest body)
+ "Setup the environment for TERMINAL and eval BODY in it."
+ (declare (indent 1))
+ `(let ((eat--t-term ,terminal))
+ (with-current-buffer (eat--t-term-buffer eat--t-term)
+ (save-excursion
+ (save-restriction
+ (narrow-to-region (eat--t-term-begin eat--t-term)
+ (eat--t-term-end eat--t-term))
+ (goto-char (eat--t-cur-position
+ (eat--t-disp-cursor
+ (eat--t-term-display eat--t-term))))
+ (unwind-protect
+ (progn ,@body)
+ (set-marker (eat--t-cur-position
+ (eat--t-disp-cursor
+ (eat--t-term-display eat--t-term)))
+ (point))
+ (set-marker (eat--t-term-begin eat--t-term) (point-min))
+ (set-marker (eat--t-term-end eat--t-term)
+ (point-max))))))))
+
+(defun eat-term-delete (terminal)
+ "Delete TERMINAL and do any cleanup to do."
+ (let ((inhibit-quit t)
+ (eat--t-term terminal))
+ (with-current-buffer (eat--t-term-buffer eat--t-term)
+ (save-excursion
+ (save-restriction
+ (narrow-to-region (eat--t-term-begin eat--t-term)
+ (eat--t-term-end eat--t-term))
+ (eat--t-set-cursor-state :default)
+ ;; Go to the beginning of display.
+ (goto-char (eat--t-disp-begin
+ (eat--t-term-display eat--t-term)))
+ ;; Join all long lines.
+ (unless (bobp)
+ (backward-char))
+ (while (not (eobp))
+ (eat--t-join-long-line)))))))
+
+(defun eat-term-reset (terminal)
+ "Reset TERMINAL."
+ (let ((inhibit-quit t))
+ (eat--t-with-env terminal
+ (eat--t-reset))))
+
+(defun eat-term-input-function (terminal)
+ "Return the function used to send input from TERMINAL.
+
+The function is called with two arguments, TERMINAL and the string to
+send. The function should not change point and buffer restriction.
+
+To set it, use (`setf' (`eat-term-input-function' TERMINAL) FUNCTION),
+where FUNCTION is the input function."
+ (eat--t-term-input-fn terminal))
+
+(gv-define-setter eat-term-input-function (function terminal)
+ `(setf (eat--t-term-input-fn ,terminal) ,function))
+
+(defun eat-term-cursor-type (terminal)
+ "Return the cursor state of TERMINAL.
+
+The return value can be one of the following:
+
+ `:default' Default cursor.
+ `:invisible' Invisible cursor.
+ `:very-visible' Very visible cursor."
+ (eat--t-term-cur-state terminal))
+
+(defun eat-term-set-cursor-function (terminal)
+ "Return the function used to set the cursor of TERMINAL.
+
+The function is called with two arguments, TERMINAL and a symbol STATE
+describing the new state of cursor. The function should not change
+point and buffer restriction. STATE can be one of the following:
+
+ `:default' Default cursor.
+ `:invisible' Invisible cursor.
+ `:very-visible' Very visible cursor. Can also be implemented
+ as blinking cursor.
+
+More possible values might be added in future. So in case the
+function doesn't know about a particular cursor state, it should reset
+the cursor to the default like the `:default' state.
+
+To set it, use (`setf' (`eat-term-set-cursor-function' TERMINAL)
+FUNCTION), where FUNCTION is the function to set cursor."
+ (eat--t-term-set-cursor-fn terminal))
+
+(gv-define-setter eat-term-set-cursor-function (function terminal)
+ `(setf (eat--t-term-set-cursor-fn ,terminal) ,function))
+
+(defun eat-term-title (terminal)
+ "Return the current title of TERMINAL."
+ (eat--t-term-title terminal))
+
+(defun eat-term-set-title-function (terminal)
+ "Return the function used to set the title of TERMINAL.
+
+The function is called with two arguments, TERMINAL and the new title
+of TERMINAL. The function should not change point and buffer
+restriction.
+
+To set it, use (`setf' (`eat-term-set-title-function' TERMINAL)
+FUNCTION), where FUNCTION is the function to set title."
+ (eat--t-term-set-title-fn terminal))
+
+(gv-define-setter eat-term-set-title-function (function terminal)
+ `(setf (eat--t-term-set-title-fn ,terminal) ,function))
+
+(defun eat-term-grab-mouse-function (terminal)
+ "Return the function used to grab the mouse.
+
+The function is called with two arguments, TERMINAL and a symbol MODE
+describing the new mouse mode MODE. The function should not change
+point and buffer restriction. MODE can be one of the following:
+
+ nil Disable mouse.
+ `:click' Pass `mouse-1', `mouse-2', and `mouse-3'
+ clicks.
+ `:modifier-click' Pass all mouse click events on both press and
+ release, including `control', `meta' and
+ `shift' modifiers.
+ `:drag' All of `:modifier-click', plus dragging
+ (moving mouse while pressed) information.
+ `:all' Pass all mouse events, including movement.
+
+More possible values might be added in future. So in case the
+function doesn't know about a particular mouse mode, it should behave
+as if MODE was nil and disable mouse.
+
+To set it, use (`setf' (`eat-term-set-mouse-mode-function' TERMINAL)
+FUNCTION), where FUNCTION is the function to set mouse mode."
+ (eat--t-term-grab-mouse-fn terminal))
+
+(gv-define-setter eat-term-grab-mouse-function (function terminal)
+ `(setf (eat--t-term-grab-mouse-fn ,terminal) ,function))
+
+(defun eat-term-grab-focus-events-function (terminal)
+ "Return the function used to grab focus in and out events.
+
+The function is called with two arguments, TERMINAL and a boolean
+describing the new grabbing mode. When the boolean is nil, don't send
+focus event, otherwise send focus events. The function should not
+change point and buffer restriction.
+
+To set it, use (`setf' (`eat-term-grab-focus-events-function'
+TERMINAL) FUNCTION), where FUNCTION is the function to grab focus
+events."
+ (eat--t-term-set-focus-ev-mode-fn terminal))
+
+(gv-define-setter eat-term-grab-focus-events-function
+ (function terminal)
+ `(setf (eat--t-term-set-focus-ev-mode-fn ,terminal) ,function))
+
+(defun eat-term-manipulate-selection-function (terminal)
+ "Return the function used to manipulate selection (or `kill-ring').
+
+The function is called with three arguments, TERMINAL, a symbol
+SELECTION describing the selection paramater and DATA, a string, or a
+boolean. The function should not change point and buffer restriction.
+SELECTION can be one of `:clipboard', `:primary', `:secondary',
+`:select'. When DATA is a string, it should set the selection to that
+string, when DATA is nil, it should unset the selection, and when DATA
+is t, it should return the selection, or nil if none.
+
+To set it, use (`setf' (`eat-term-manipulate-selection-function'
+TERMINAL) FUNCTION), where FUNCTION is the function to manipulate
+selection."
+ (eat--t-term-manipulate-selection-fn terminal))
+
+(gv-define-setter eat-term-manipulate-selection-function
+ (function terminal)
+ `(setf (eat--t-term-manipulate-selection-fn ,terminal) ,function))
+
+(defun eat-term-ring-bell-function (terminal)
+ "Return the function used to ring the bell.
+
+The function is called with a single argument TERMINAL.
+
+To set it, use (`setf' (`eat-term-ring-bell-function' TERMINAL)
+FUNCTION), where FUNCTION is the function to ring the bell."
+ (eat--t-term-manipulate-selection-fn terminal))
+
+(gv-define-setter eat-term-ring-bell-function (function terminal)
+ `(setf (eat--t-term-bell-fn ,terminal) ,function))
+
+(defun eat-term-size (terminal)
+ "Return the size of TERMINAL as (WIDTH . HEIGHT)."
+ (let ((disp (eat--t-term-display terminal)))
+ (cons (eat--t-disp-width disp) (eat--t-disp-height disp))))
+
+(defun eat-term-beginning (terminal)
+ "Return the beginning position of TERMINAL.
+
+Don't use markers to store the position, call this function whenever
+you need the position."
+ (eat--t-term-begin terminal))
+
+(defun eat-term-end (terminal)
+ "Return the end position of TERMINAL.
+
+This is also the end position of TERMINAL's display.
+
+Don't use markers to store the position, call this function whenever
+you need the position."
+ (eat--t-term-end terminal))
+
+(defun eat-term-display-beginning (terminal)
+ "Return the beginning position of TERMINAL's display."
+ (eat--t-disp-begin (eat--t-term-display terminal)))
+
+(defun eat-term-display-cursor (terminal)
+ "Return the cursor's current position on TERMINAL's display."
+ (let* ((disp (eat--t-term-display terminal))
+ (cursor (eat--t-disp-cursor disp)))
+ ;; The cursor might be after the edge of the display. But we
+ ;; don't want the UI to show that, so show cursor at the edge.
+ (if (> (eat--t-cur-x cursor) (eat--t-disp-width disp))
+ (1- (eat--t-cur-position cursor))
+ (eat--t-cur-position cursor))))
+
+(defun eat-term-process-output (terminal output)
+ "Process OUTPUT from client and show it on TERMINAL's display."
+ (let ((inhibit-quit t))
+ (eat--t-with-env terminal
+ (eat--t-handle-output output))))
+
+(defun eat-term-redisplay (terminal)
+ "Prepare TERMINAL for displaying."
+ (let ((inhibit-quit t))
+ (eat--t-with-env terminal
+ (let* ((disp (eat--t-term-display eat--t-term)))
+ (when (< (eat--t-disp-old-begin disp)
+ (eat--t-disp-begin disp))
+ ;; Join long lines.
+ (let ((limit (copy-marker (1- (eat--t-disp-begin disp)))))
+ (save-excursion
+ (goto-char (max (1- (eat--t-disp-old-begin disp))
+ (point-min)))
+ (while (< (point) limit)
+ (eat--t-join-long-line limit))))
+ ;; Truncate scrollback.
+ (when eat-term-scrollback-size
+ (delete-region
+ (point-min)
+ (max (point-min) (- (point) eat-term-scrollback-size))))
+ (set-marker (eat--t-disp-old-begin disp)
+ (eat--t-disp-begin disp)))))))
+
+(defun eat-term-resize (terminal width height)
+ "Resize TERMINAL to WIDTH x HEIGHT."
+ (let ((inhibit-quit t))
+ (eat--t-with-env terminal
+ (eat--t-resize width height))))
+
+(defun eat-term-in-alternative-display-p (terminal)
+ "Return non-nil when TERMINAL is in alternative display mode."
+ (eat--t-term-main-display terminal))
+
+(defun eat-term-input-event (terminal n event &optional ref-pos)
+ "Send EVENT as input N times to TERMINAL.
+
+EVENT should be a event. It can be any standard Emacs event, or a
+event list of any of the following forms:
+
+ (eat-focus-in)
+ Terminal just gained focus.
+
+ (eat-focus-out)
+ Terminal just lost focus.
+
+REF-POS is a mouse position list pointing to the start of terminal
+display satisfying the predicate `posnp'. It is used to calculate the
+position of mouse events and `eat-mouse-drag' events on terminal when
+given.
+
+For mouse events, events should be sent on both mouse button press and
+release unless the mouse grabing mode is `:click', otherwise the
+client process may get confused."
+ (let ((disp (eat--t-term-display terminal)))
+ (cl-flet ((send (str)
+ (funcall (eat--t-term-input-fn terminal)
+ terminal str)))
+ (dotimes (_ (or n 1))
+ (pcase event
+ ;; Arrow key, `insert', `delete', `deletechar', `home',
+ ;; `end', `prior', `next' and their modifier variants.
+ ((and (or 'up 'down 'right 'left
+ 'C-up 'C-down 'C-right 'C-left
+ 'M-up 'M-down 'M-right 'M-left
+ 'S-up 'S-down 'S-right 'S-left
+ 'C-M-up 'C-M-down 'C-M-right 'C-M-left
+ 'C-S-up 'C-S-down 'C-S-right 'C-S-left
+ 'M-S-up 'M-S-down 'M-S-right 'M-S-left
+ 'C-M-S-up 'C-M-S-down 'C-M-S-right 'C-M-S-left
+ 'insert 'C-insert 'M-insert 'S-insert 'C-M-insert
+ 'C-S-insert 'M-S-insert 'C-M-S-insert
+ 'delete 'C-delete 'M-delete 'S-delete 'C-M-delete
+ 'C-S-delete 'M-S-delete 'C-M-S-delete
+ 'deletechar 'C-deletechar 'M-deletechar
+ 'S-deletechar 'C-M-deletechar 'C-S-deletechar
+ 'M-S-deletechar 'C-M-S-deletechar
+ 'home 'C-home 'M-home 'S-home 'C-M-home 'C-S-home
+ 'M-S-home 'C-M-S-home
+ 'end 'C-end 'M-end 'S-end 'C-M-end 'C-S-end
+ 'M-S-end 'C-M-S-end
+ 'prior 'C-prior 'M-prior 'S-prior 'C-M-prior
+ 'C-S-prior 'M-S-prior 'C-M-S-prior
+ 'next 'C-next 'M-next 'S-next 'C-M-next 'C-S-next
+ 'M-S-next 'C-M-S-next)
+ ev)
+ (send
+ (format
+ "\e%s%c"
+ (if (not (or (memq 'control (event-modifiers ev))
+ (memq 'meta (event-modifiers ev))
+ (memq 'shift (event-modifiers ev))))
+ (pcase (event-basic-type ev)
+ ('insert "[2")
+ ((or 'delete 'deletechar) "[3")
+ ('prior "[5")
+ ('next "[6")
+ (_ (if (eat--t-term-keypad-mode terminal)
+ "O"
+ "[")))
+ (format
+ "[%c;%c"
+ (pcase (event-basic-type ev)
+ ('insert ?2)
+ ((or 'delete 'deletechar) ?3)
+ ('prior ?5)
+ ('next ?6)
+ (_ ?1))
+ (pcase (event-modifiers ev)
+ ((and (pred (memq 'control))
+ (pred (memq 'meta))
+ (pred (memq 'shift)))
+ ?8)
+ ((and (pred (memq 'control))
+ (pred (memq 'meta)))
+ ?7)
+ ((and (pred (memq 'control))
+ (pred (memq 'shift)))
+ ?6)
+ ((and (pred (memq 'meta))
+ (pred (memq 'shift)))
+ ?4)
+ ((pred (memq 'control))
+ ?5)
+ ((pred (memq 'meta))
+ ?3)
+ ((pred (memq 'shift))
+ ?2))))
+ (pcase (event-basic-type ev)
+ ('up ?A)
+ ('down ?B)
+ ('right ?C)
+ ('left ?D)
+ ('home ?H)
+ ('end ?F)
+ (_ ?~)))))
+ ('backspace
+ (send "\C-?"))
+ ('C-backspace
+ (send "\C-h"))
+ ;; Function keys.
+ ((and (pred symbolp)
+ fn-key
+ (let (rx string-start "f"
+ (let fn-num (one-or-more (any (?0 . ?9))))
+ string-end)
+ (symbol-name fn-key))
+ (let (and (pred (<= 1))
+ (pred (>= 63))
+ key)
+ (string-to-number fn-num)))
+ (send
+ (aref
+ ["\eOP" "\eOQ" "\eOR" "\eOS" "\e[15~" "\e[17~" "\e[18~"
+ "\e[19~" "\e[20~" "\e[21~" "\e[23~" "\e[24~" "\e[1;2P"
+ "\e[1;2Q" "\e[1;2R" "\e[1;2S" "\e[15;2~" "\e[17;2~"
+ "\e[18;2~" "\e[19;2~" "\e[20;2~" "\e[21;2~" "\e[23;2~"
+ "\e[24;2~" "\e[1;5P" "\e[1;5Q" "\e[1;5R" "\e[1;5S"
+ "\e[15;5~" "\e[17;5~" "\e[18;5~" "\e[19;5~" "\e[20;5~"
+ "\e[21;5~" "\e[23;5~" "\e[24;5~" "\e[1;6P" "\e[1;6Q"
+ "\e[1;6R" "\e[1;6S" "\e[15;6~" "\e[17;6~" "\e[18;6~"
+ "\e[19;6~" "\e[20;6~" "\e[21;6~" "\e[23;6~" "\e[24;6~"
+ "\e[1;3P" "\e[1;3Q" "\e[1;3R" "\e[1;3S" "\e[15;3~"
+ "\e[17;3~" "\e[18;3~" "\e[19;3~" "\e[20;3~" "\e[21;3~"
+ "\e[23;3~" "\e[24;3~" "\e[1;4P" "\e[1;4Q" "\e[1;4R"]
+ (1- key))))
+ ((and (or (pred numberp)
+ (pred symbolp))
+ char)
+ ;; Adapted from Term source.
+ (when (symbolp char)
+ ;; Convert `return' to C-m, etc.
+ (let ((tmp (get char 'event-symbol-elements)))
+ (if tmp (setq char (car tmp)))
+ (and (symbolp char)
+ (setq tmp (get char 'ascii-character))
+ (setq char tmp))))
+ (when (numberp char)
+ (let* ((base (event-basic-type char))
+ (mods (event-modifiers char)))
+ ;; Try to avoid event-convert-list if possible.
+ (if (and (characterp char)
+ (not (memq 'meta mods))
+ (not (and (memq 'control mods)
+ (memq 'shift mods))))
+ (send (format "%c" char))
+ (when (memq 'control mods)
+ (setq mods (delq 'shift mods)))
+ (let ((ch (pcase (event-convert-list
+ (append (remq 'meta mods)
+ (list base)))
+ (?\C-\ ?\C-@)
+ (?\C-/ ?\C-?)
+ (?\C-- ?\C-_)
+ (c c))))
+ (when (characterp ch)
+ (send (cond
+ ((and (memq 'meta mods)
+ (memq ch '(?\[ ?O)))
+ "\e")
+ (t
+ (format
+ (if (memq 'meta mods) "\e%c" "%c")
+ ch))))))))))
+ ;; Mouse handling.
+ ((and (guard (eat--t-term-mouse-mode terminal))
+ mouse
+ (pred eventp)
+ (or (and (let mouse-type (event-basic-type mouse))
+ (let (rx string-start "mouse-"
+ (let key-num (one-or-more
+ (any (?0 . ?9))))
+ string-end)
+ (symbol-name mouse-type))
+ (let (and (pred (<= 1))
+ (pred (>= 11))
+ mouse-num)
+ (string-to-number key-num)))
+ (and (let 'wheel-up (event-basic-type mouse))
+ (let mouse-num 4))
+ (and (let 'wheel-down (event-basic-type mouse))
+ (let mouse-num 5))
+ (and (let 'wheel-right (event-basic-type mouse))
+ (let mouse-num 6))
+ (and (let 'wheel-left (event-basic-type mouse))
+ (let mouse-num 7))))
+ (let* ((modifiers (event-modifiers mouse))
+ (pos (if (memq 'drag modifiers)
+ (event-end mouse)
+ (event-start mouse)))
+ (x-y (posn-col-row pos 'use-window))
+ (x (1+ (car x-y)))
+ (y (1+ (cdr x-y)))
+ (button
+ (let ((b (aref
+ [0 1 2 64 65 66 67 128 129 130 131]
+ (1- mouse-num))))
+ (when (memq 'shift modifiers)
+ (cl-incf b 4))
+ (when (memq 'meta modifiers)
+ (cl-incf b 8))
+ (when (memq 'control modifiers)
+ (cl-incf b 16))
+ b)))
+ (when ref-pos
+ (let ((ref-x-y (posn-col-row ref-pos 'use-window)))
+ (cl-decf x (car ref-x-y))
+ (cl-decf y (cdr ref-x-y))))
+ (when (and (<= 1 x (eat--t-disp-width disp))
+ (<= 1 y (eat--t-disp-height disp))
+ (or (eat--t-term-mouse-encoding terminal)
+ (and (<= x 95)
+ (<= y 95)
+ (<= button 95))))
+ (if (eq (eat--t-term-mouse-mode terminal) 'x10)
+ (when (and (< button 3)
+ (or (memq 'click modifiers)
+ (memq 'drag modifiers)))
+ (send
+ (if (eq (eat--t-term-mouse-encoding terminal)
+ 'sgr)
+ (format "\e[<%i;%i;%iM" button x y)
+ (format "\e[M%c%c%c" (+ button 32) (+ x 32)
+ (+ y 32)))))
+ (cond
+ ;; `down-mouse-1' and friends.
+ ((memq 'down modifiers)
+ ;; For `mouse-1', `mouse-2' and `mouse-3', keep
+ ;; track the button's state, we'll need it when
+ ;; button event mouse mode is enabled.
+ (when (< (logand button 3) 3)
+ (setf (eat--t-term-mouse-pressed terminal)
+ ;; In XTerm and Kitty, mouse-1 is
+ ;; prioritized over mouse-2, and mouse-2
+ ;; over mouse-3. However St doesn't keep
+ ;; track of multiple buttons.
+ (sort
+ (cons button (eat--t-term-mouse-pressed
+ terminal))
+ #'<)))
+ (send
+ (if (eq (eat--t-term-mouse-encoding terminal)
+ 'sgr)
+ (format "\e[<%i;%i;%iM" button x y)
+ (format "\e[M%c%c%c" (+ button 32) (+ x 32)
+ (+ y 32)))))
+ ;; `mouse-1', `mouse-2', `mouse-3', and their
+ ;; `drag'ged variants.
+ ((and (or (memq 'click modifiers)
+ (memq 'drag modifiers))
+ (<= mouse-num 3))
+ ;; For `mouse-1', `mouse-2' and `mouse-3', keep
+ ;; track the button's state, we'll need it when
+ ;; button event mouse mode is enabled.
+ (setf (eat--t-term-mouse-pressed terminal)
+ (cl-delete-if
+ (lambda (b)
+ (= (logand b 3) (logand button 3)))
+ (eat--t-term-mouse-pressed terminal)))
+ (send
+ (if (eq (eat--t-term-mouse-encoding terminal)
+ 'sgr)
+ (format "\e[<%i;%i;%im" button x y)
+ (format "\e[M%c%c%c" (+ (logior button 3) 32)
+ (+ x 32) (+ y 32)))))
+ ;; Mouse wheel, `mouse-4' and friends.
+ (t
+ (send
+ (if (eq (eat--t-term-mouse-encoding terminal)
+ 'sgr)
+ (format "\e[<%i;%i;%iM" button x y)
+ (format "\e[M%c%c%c" (+ button 32) (+ x 32)
+ (+ y 32))))))))))
+ ;; Mouse movement tracking.
+ ((and (guard (memq (eat--t-term-mouse-mode terminal)
+ '(button-event any-event)))
+ (pred mouse-movement-p)
+ movement)
+ (let* ((pos (event-start movement))
+ (x (1+ (car (posn-col-row pos))))
+ (y (1+ (cdr (posn-col-row pos))))
+ (button
+ (if (car (eat--t-term-mouse-pressed terminal))
+ (+ (car (eat--t-term-mouse-pressed terminal))
+ 32)
+ 35)))
+ (when ref-pos
+ (cl-decf x (car (posn-col-row ref-pos)))
+ (cl-decf y (cdr (posn-col-row ref-pos))))
+ (when (and (or (eq (eat--t-term-mouse-mode terminal)
+ 'any-event)
+ (/= button 35))
+ (<= 1 x (eat--t-disp-width disp))
+ (<= 1 y (eat--t-disp-height disp))
+ (or (eat--t-term-mouse-encoding terminal)
+ (and (<= x 95)
+ (<= y 95)
+ (<= button 95))))
+ (send
+ (if (eq (eat--t-term-mouse-encoding terminal)
+ 'sgr)
+ (format "\e[<%i;%i;%iM" button x y)
+ (format "\e[M%c%c%c" (+ button 32) (+ x 32)
+ (+ y 32)))))))
+ ;; Focus events.
+ ('(eat-focus-in)
+ (when (eat--t-term-focus-event-mode terminal)
+ (send "\e[I")))
+ ('(eat-focus-out)
+ (when (eat--t-term-focus-event-mode terminal)
+ (send "\e[O"))))))))
+
+(defun eat-send-string-as-yank (terminal args)
+ "Send ARGS to TERMINAL, honoring bracketed yank mode.
+
+Each argument in ARGS can be either string or character."
+ (funcall (eat--t-term-input-fn terminal) terminal
+ (let ((str (mapconcat (lambda (s)
+ (if (stringp s) s (string s)))
+ args "")))
+ (if (eat--t-term-bracketed-yank terminal)
+ ;; REVIEW: What if `str' itself contains these escape
+ ;; sequences? St doesn't care and just wraps the
+ ;; string with these magic escape sequences, while
+ ;; Kitty tries to be smart.
+ (format "\e[200~%s\e[201~" str)
+ str))))
+
+(defun eat-term-make-keymap (input-command categories exceptions)
+ "Make a keymap binding INPUT-COMMAND to the events of CATEGORIES.
+
+CATEGORIES is a list whose elements should be a one of the following
+keywords:
+
+ `:ascii' All self-insertable characters, plus
+ `backspace', `insert', `delete' and
+ `deletechar' keys, with all possible
+ modifiers.
+ `:arrow' Arrow keys with all possible modifiers.
+ `:navigation' Navigation keys: home, end, prior (or page up)
+ and next (or page down) with all possible
+ modifiers.
+ `:function' Function keys (f1 - f63).
+ `:mouse-click' `mouse-1', `mouse-2' and `mouse-3'.
+ `:mouse-modifier' All mouse events except mouse movement.
+ `:mouse-movement' Mouse movement.
+
+EXCEPTIONS is a list of key sequences to not bind. Don't use
+\"M-...\" key sequences in EXCEPTIONS, use \"ESC ...\" instead."
+ (let ((map (make-sparse-keymap)))
+ (cl-labels ((bind (key)
+ (unless (member key exceptions)
+ (define-key map key input-command))))
+ (when (memq :ascii categories)
+ ;; Bind ASCII and self-insertable characters except ESC and
+ ;; DEL.
+ (bind [remap self-insert-command])
+ (cl-loop
+ for i from ?\C-@ to ?~
+ do (unless (= i meta-prefix-char)
+ (bind `[,i])))
+ ;; Bind `backspace', `delete', `deletechar', and all modified
+ ;; variants.
+ (dolist (key '( backspace C-backspace
+ insert C-insert M-insert S-insert C-M-insert
+ C-S-insert M-S-insert C-M-S-insert
+ delete C-delete M-delete S-delete C-M-delete
+ C-S-delete M-S-delete C-M-S-delete
+ deletechar C-deletechar M-deletechar
+ S-deletechar C-M-deletechar C-S-deletechar
+ M-S-deletechar C-M-S-deletechar))
+ (bind `[,key]))
+ ;; Bind these non-encodable keys. They are translated.
+ (dolist (key '(?\C-- ?\C-? ?\C-\ ))
+ (bind `[,key]))
+ ;; Bind M-<ASCII> keys.
+ (unless (member `[,meta-prefix-char] exceptions)
+ (define-key map `[,meta-prefix-char] (make-sparse-keymap))
+ (cl-loop
+ for i from ?\C-@ to ?~
+ do (unless (memq i '(?O ?\[))
+ (bind `[,meta-prefix-char ,i])))
+ (bind `[,meta-prefix-char ,meta-prefix-char])))
+ (when (memq :arrow categories)
+ (dolist (key '( up down right left
+ C-up C-down C-right C-left
+ M-up M-down M-right M-left
+ S-up S-down S-right S-left
+ C-M-up C-M-down C-M-right C-M-left
+ C-S-up C-S-down C-S-right C-S-left
+ M-S-up M-S-down M-S-right M-S-left
+ C-M-S-up C-M-S-down C-M-S-right C-M-S-left))
+ (bind `[,key])))
+ (when (memq :navigation categories)
+ (dolist (key '( home C-home M-home S-home C-M-home C-S-home
+ M-S-home C-M-S-home
+ end C-end M-end S-end C-M-end C-S-end
+ M-S-end C-M-S-end
+ prior C-prior M-prior S-prior C-M-prior
+ C-S-prior M-S-prior C-M-S-prior
+ next C-next M-next S-next C-M-next C-S-next
+ M-S-next C-M-S-next))
+ (bind `[,key])))
+ (when (memq :function categories)
+ (cl-loop
+ for i from 1 to 63
+ do (let ((key (intern (format "f%i" i))))
+ (bind `[,key]))))
+ (when (memq :mouse-click categories)
+ (dolist (key '(mouse-1 mouse-2 mouse-3))
+ (bind `[,key])))
+ (when (memq :mouse-modifier categories)
+ (dolist (key
+ '( down-mouse-1 drag-mouse-1 down-mouse-2
+ drag-mouse-2 down-mouse-3 drag-mouse-3
+ C-down-mouse-1 C-drag-mouse-1 C-down-mouse-2
+ C-drag-mouse-2 C-down-mouse-3 C-drag-mouse-3
+ M-down-mouse-1 M-drag-mouse-1 M-down-mouse-2
+ M-drag-mouse-2 M-down-mouse-3 M-drag-mouse-3
+ S-down-mouse-1 S-drag-mouse-1 S-down-mouse-2
+ S-drag-mouse-2 S-down-mouse-3 S-drag-mouse-3
+ C-M-down-mouse-1 C-M-drag-mouse-1
+ C-M-down-mouse-2 C-M-drag-mouse-2
+ C-M-down-mouse-3 C-M-drag-mouse-3
+ C-S-down-mouse-1 C-S-drag-mouse-1
+ C-S-down-mouse-2 C-S-drag-mouse-2
+ C-S-down-mouse-3 C-S-drag-mouse-3
+ M-S-down-mouse-1 M-S-drag-mouse-1
+ M-S-down-mouse-2 M-S-drag-mouse-2
+ M-S-down-mouse-3 M-S-drag-mouse-3
+ C-M-S-down-mouse-1 C-M-S-drag-mouse-1
+ C-M-S-down-mouse-2 C-M-S-drag-mouse-2
+ C-M-S-down-mouse-3 C-M-S-drag-mouse-3 mouse-1
+ mouse-2 mouse-3 mouse-4 mouse-5 mouse-6 mouse-7
+ mouse-8 mouse-9 mouse-10 mouse-11 C-mouse-1
+ C-mouse-2 C-mouse-3 C-mouse-4 C-mouse-5
+ C-mouse-6 C-mouse-7 C-mouse-8 C-mouse-9
+ C-mouse-10 C-mouse-11 M-mouse-1 M-mouse-2
+ M-mouse-3 M-mouse-4 M-mouse-5 M-mouse-6
+ M-mouse-7 M-mouse-8 M-mouse-9 M-mouse-10
+ M-mouse-11 S-mouse-1 S-mouse-2 S-mouse-3
+ S-mouse-4 S-mouse-5 S-mouse-6 S-mouse-7
+ S-mouse-8 S-mouse-9 S-mouse-10 S-mouse-11
+ C-M-mouse-1 C-M-mouse-2 C-M-mouse-3 C-M-mouse-4
+ C-M-mouse-5 C-M-mouse-6 C-M-mouse-7 C-M-mouse-8
+ C-M-mouse-9 C-M-mouse-10 C-M-mouse-11
+ C-S-mouse-1 C-S-mouse-2 C-S-mouse-3 C-S-mouse-4
+ C-S-mouse-5 C-S-mouse-6 C-S-mouse-7 C-S-mouse-8
+ C-S-mouse-9 C-S-mouse-10 C-S-mouse-11
+ M-S-mouse-1 M-S-mouse-2 M-S-mouse-3 M-S-mouse-4
+ M-S-mouse-5 M-S-mouse-6 M-S-mouse-7 M-S-mouse-8
+ M-S-mouse-9 M-S-mouse-10 M-S-mouse-11
+ C-M-S-mouse-1 C-M-S-mouse-2 C-M-S-mouse-3
+ C-M-S-mouse-4 C-M-S-mouse-5 C-M-S-mouse-6
+ C-M-S-mouse-7 C-M-S-mouse-8 C-M-S-mouse-9
+ C-M-S-mouse-10 C-M-S-mouse-11 wheel-up
+ wheel-down wheel-right wheel-left C-wheel-up
+ C-wheel-down C-wheel-right C-wheel-left
+ M-wheel-up M-wheel-down M-wheel-right
+ M-wheel-left S-wheel-up S-wheel-down
+ S-wheel-right S-wheel-left C-M-wheel-up
+ C-M-wheel-down C-M-wheel-right C-M-wheel-left
+ C-S-wheel-up C-S-wheel-down C-S-wheel-right
+ C-S-wheel-left M-S-wheel-up M-S-wheel-down
+ M-S-wheel-right M-S-wheel-left C-M-S-wheel-up
+ C-M-S-wheel-down C-M-S-wheel-right
+ C-M-S-wheel-left))
+ (bind `[,key])))
+ (when (memq :mouse-movement categories)
+ (bind [mouse-movement])))
+ map))
+
+(defun eat-term-name ()
+ "Return the value of `TERM' environment variable for Eat."
+ (if (stringp eat-term-name)
+ eat-term-name
+ (funcall eat-term-name)))
+
+(defun eat-term-get-suitable-term-name (&optional display)
+ "Return the most suitable value for `TERM' for DISPLAY.
+
+If the number of colors supported by display (as returned by
+`display-color-cells') is more than 256, return \"eat-truecolor\", if
+it is more than 8 but less than or equal to 256, return
+\"eat-256color\", if is more than 1 but less than or equal to 8,
+return \"eat-color\", otherwise return \"eat-mono\"."
+ (let ((colors (display-color-cells display)))
+ (cond ((> colors 256) "eat-truecolor")
+ ((> colors 8) "eat-256color")
+ ((> colors 1) "eat-color")
+ (t "eat-mono"))))
+
+(defun eat-term-filter-string (string)
+ "Filter Eat's special text properties from STRING."
+ (with-temp-buffer
+ (insert string)
+ (goto-char (point-min))
+ (while (not (eobp))
+ (eat--t-join-long-line))
+ (buffer-string)))
+
+
+;;;; Blink mode.
+
+(defvar eat--slow-blink-state nil
+ "Current state of slowly blinking text, t means inverse video.")
+
+(defvar eat--fast-blink-state nil
+ "Current state of rapidly blinking text, t means inverse video.")
+
+(defvar eat--slow-blink-remap nil
+ "Face remapping cookie of slowly blinking face.")
+
+(defvar eat--fast-blink-remap nil
+ "Face remapping cookie of rapidly blinking face.")
+
+(defvar eat--slow-blink-timer nil
+ "Timer for blinking slowly blinking text.")
+
+(defvar eat--fast-blink-timer nil
+ "Timer for blinking rapidly blinking text.")
+
+(defun eat--flip-slow-blink-state ()
+ "Flip the state of slowly blinking text."
+ (declare-function face-remap-add-relative "face-remap"
+ (face &rest specs))
+ (declare-function face-remap-remove-relative "face-remap" (cookie))
+ (face-remap-remove-relative eat--slow-blink-remap)
+ (setq eat--slow-blink-remap
+ (face-remap-add-relative
+ 'eat-slow-blink
+ `(:box nil :inverse-video ,(not eat--slow-blink-state))))
+ (setq eat--slow-blink-state (not eat--slow-blink-state)))
+
+(defun eat--flip-fast-blink-state ()
+ "Flip the state of rapidly blinking text."
+ (declare-function face-remap-add-relative "face-remap"
+ (face &rest specs))
+ (declare-function face-remap-remove-relative "face-remap" (cookie))
+ (face-remap-remove-relative eat--fast-blink-remap)
+ (setq eat--fast-blink-remap
+ (face-remap-add-relative
+ 'eat-fast-blink
+ `(:box nil :inverse-video ,(not eat--fast-blink-state))))
+ (setq eat--fast-blink-state (not eat--fast-blink-state)))
+
+(defun eat--blink-stop-timers ()
+ "Start blinking timers."
+ (when eat--slow-blink-timer
+ (cancel-timer eat--slow-blink-timer)
+ (setq eat--slow-blink-timer nil))
+ (when eat--fast-blink-timer
+ (cancel-timer eat--fast-blink-timer)
+ (setq eat--fast-blink-timer nil)))
+
+(defun eat--blink-start-timers ()
+ "Start blinking timers."
+ (eat--blink-stop-timers)
+ (setq eat--slow-blink-timer
+ (run-with-timer t (/ (float eat-slow-blink-frequency))
+ #'eat--flip-slow-blink-state))
+ (setq eat--fast-blink-timer
+ (run-with-timer t (/ (float eat-fast-blink-frequency))
+ #'eat--flip-fast-blink-state)))
+
+(define-minor-mode eat-blink-mode
+ "Toggle blinking of text with blink attribute."
+ :lighter " Eat-Blink"
+ (declare-function face-remap-add-relative "face-remap"
+ (face &rest specs))
+ (cond
+ (eat-blink-mode
+ (setq eat-blink-mode nil)
+ (require 'face-remap)
+ (setq eat-blink-mode t)
+ (make-local-variable 'eat--slow-blink-state)
+ (make-local-variable 'eat--fast-blink-state)
+ (make-local-variable 'eat--slow-blink-remap)
+ (make-local-variable 'eat--fast-blink-remap)
+ (make-local-variable 'eat--slow-blink-timer)
+ (make-local-variable 'eat--fast-blink-timer)
+ (setq eat--slow-blink-state nil)
+ (setq eat--fast-blink-state nil)
+ (setq eat--slow-blink-remap
+ (face-remap-add-relative 'eat-term-slow-blink '(:box nil)))
+ (setq eat--fast-blink-remap
+ (face-remap-add-relative 'eat-term-fast-blink '(:box nil)))
+ (add-hook 'pre-command-hook #'eat--blink-stop-timers nil t)
+ (add-hook 'post-command-hook #'eat--blink-start-timers nil t))
+ (t
+ (eat--blink-stop-timers)
+ (face-remap-remove-relative eat--slow-blink-remap)
+ (face-remap-remove-relative eat--fast-blink-remap)
+ (remove-hook 'pre-command-hook #'eat--blink-stop-timers t)
+ (remove-hook 'post-command-hook #'eat--blink-start-timers t)
+ (kill-local-variable 'eat--slow-blink-state)
+ (kill-local-variable 'eat--fast-blink-state)
+ (kill-local-variable 'eat--slow-blink-remap)
+ (kill-local-variable 'eat--fast-blink-remap)
+ (kill-local-variable 'eat--slow-blink-timer)
+ (kill-local-variable 'eat--fast-blink-timer))))
+
+
+;;;; Buffer-local Cursor Blinking.
+
+(defvar eat--cursor-blink-type nil
+ "Type of blinking cursor.")
+
+(defvar eat--cursor-blink-state nil
+ "Current state of slowly blinking text, non-nil means on.")
+
+(defvar eat--cursor-blink-timer nil
+ "Timer for blinking slowly blinking text.")
+
+(defvar eat--cursor-blink-mode)
+
+(defun eat--flip-cursor-blink-state ()
+ "Flip the state of slowly blinking text."
+ (when (and eat--cursor-blink-mode
+ (display-graphic-p))
+ (setq-local cursor-type (if eat--cursor-blink-state
+ (caddr eat--cursor-blink-type)
+ (car eat--cursor-blink-type)))
+ (setq eat--cursor-blink-state (not eat--cursor-blink-state))
+ ;; REVIEW: This is expensive, and some causes flickering. Any
+ ;; better way?
+ (when-let ((window (get-buffer-window nil 'visible)))
+ (redraw-frame (window-frame window)))))
+
+(defun eat--cursor-blink-stop-timers ()
+ "Stop blinking timers."
+ (unless eat--cursor-blink-state
+ (eat--flip-cursor-blink-state))
+ (when eat--cursor-blink-timer
+ (cancel-timer eat--cursor-blink-timer)
+ (setq eat--cursor-blink-timer nil)))
+
+(defun eat--cursor-blink-start-timers ()
+ "Start blinking timers."
+ (eat--cursor-blink-stop-timers)
+ (setq eat--cursor-blink-timer
+ (run-with-timer t (/ (float (cadr eat--cursor-blink-type)))
+ #'eat--flip-cursor-blink-state)))
+
+(define-minor-mode eat--cursor-blink-mode
+ "Toggle blinking of cursor."
+ :interactive nil
+ (cond
+ (eat--cursor-blink-mode
+ (make-local-variable 'eat--cursor-blink-state)
+ (make-local-variable 'eat--cursor-blink-timer)
+ (setq eat--cursor-blink-state nil)
+ (setq eat--cursor-blink-timer nil)
+ (add-hook 'pre-command-hook #'eat--cursor-blink-stop-timers nil t)
+ (add-hook 'post-command-hook #'eat--cursor-blink-start-timers
+ nil t)
+ (when (current-idle-time)
+ (eat--cursor-blink-start-timers)))
+ (t
+ (eat--cursor-blink-stop-timers)
+ (remove-hook 'pre-command-hook #'eat--cursor-blink-stop-timers t)
+ (remove-hook 'post-command-hook #'eat--cursor-blink-start-timers
+ t)
+ (kill-local-variable 'eat--cursor-blink-state)
+ (kill-local-variable 'eat--cursor-blink-timer))))
+
+
+;;;; User Interface.
+
+(defvar eat--terminal nil
+ "The terminal object.")
+
+(defvar eat--synchronize-scroll-function nil
+ "Function to synchronize scrolling between terminal and window.")
+
+(defun eat-reset ()
+ "Perform a terminal reset."
+ (interactive)
+ (when eat--terminal
+ (let ((inhibit-read-only t))
+ (eat-term-reset eat--terminal)
+ (eat-term-redisplay eat--terminal))))
+
+(defun eat--set-cursor (_ state)
+ "Set cursor type according to STATE.
+
+ STATE can be one of the following:
+
+ `:default' Default cursor.
+ `:invisible' Invisible cursor.
+ `:very-visible' Very visible cursor. Can also be implemented
+ as blinking cursor.
+ Any other value Default cursor."
+ (setq-local eat--cursor-blink-type
+ (pcase state
+ (:invisible eat-invisible-cursor-type)
+ (:very-visible eat-very-visible-cursor-type)
+ (_ eat-default-cursor-type))) ; `:default'
+ (setq-local cursor-type (car eat--cursor-blink-type))
+ (eat--cursor-blink-mode (if (cadr eat--cursor-blink-type) +1 -1)))
+
+
+;;;;; Input.
+
+(defvar eat--mouse-grabbing-type nil
+ "Current mouse grabbing type/mode.")
+
+(defvar eat--mouse-pressed-buttons nil
+ "Mouse buttons currently pressed.")
+
+(defvar eat--mouse-last-position nil
+ "Last position of mouse, nil when not dragging.")
+
+(defvar eat--mouse-drag-transient-map-exit nil
+ "Function to exit mouse dragging transient map.")
+
+(defun eat-self-input (n &optional e)
+ "Send E as input N times.
+
+N defaults to 1 and E defaults to `last-command-event' and should be a
+event."
+ (interactive
+ (list (prefix-numeric-value current-prefix-arg)
+ (if (and (> (length (this-command-keys)) 1)
+ (eq (aref (this-command-keys)
+ (- (length (this-command-keys)) 2))
+ meta-prefix-char))
+ ;; HACK: Capture meta modifier (ESC prefix) in terminal.
+ (cond
+ ((eq last-command-event meta-prefix-char)
+ last-command-event)
+ ((characterp last-command-event)
+ (aref
+ (kbd (format "M-%c" last-command-event))
+ 0))
+ ((symbolp last-command-event)
+ (aref
+ (kbd (format "M-<%S>" last-command-event))
+ 0))
+ (t
+ last-command-event))
+ last-command-event)))
+ (when (memq (event-basic-type e)
+ '( mouse-1 mouse-2 mouse-3 mouse-4 mouse-5 mouse-6
+ mouse-7 mouse-8 mouse-9 mouse-10 mouse-11))
+ (select-window (posn-window (event-start e))))
+ (when eat--terminal
+ (unless (mouse-movement-p e)
+ (funcall eat--synchronize-scroll-function))
+ (if (memq (event-basic-type e)
+ '( mouse-1 mouse-2 mouse-3 mouse-4 mouse-5 mouse-6
+ mouse-7 mouse-8 mouse-9 mouse-10 mouse-11
+ mouse-movement))
+ (let ((disp-begin-posn
+ (posn-at-point
+ (eat-term-display-beginning eat--terminal)))
+ (e (if (or (not eat--mouse-last-position)
+ (eq (posn-window
+ (if (memq 'drag (event-modifiers e))
+ (event-end e)
+ (event-start e)))
+ (posn-window eat--mouse-last-position)))
+ e
+ (pcase e
+ (`(,type ,_)
+ `(,type ,eat--mouse-last-position))
+ (`(,type ,start ,_)
+ `(,type ,start ,eat--mouse-last-position))
+ (ev ev)))))
+ (if (not (mouse-movement-p e))
+ (eat-term-input-event eat--terminal n e disp-begin-posn)
+ (if (not eat--mouse-pressed-buttons)
+ (when (eq eat--mouse-grabbing-type :all)
+ (eat-term-input-event eat--terminal n e
+ disp-begin-posn))
+ (when (memq eat--mouse-grabbing-type '(:all :drag))
+ (eat-term-input-event eat--terminal n e
+ disp-begin-posn))
+ (setq eat--mouse-last-position (event-start e))))
+ (when (memq (event-basic-type e) '(mouse-1 mouse-2 mouse-3))
+ (when (or (memq 'click (event-modifiers e))
+ (memq 'drag (event-modifiers e)))
+ (setq eat--mouse-pressed-buttons
+ (delq (event-basic-type e)
+ eat--mouse-pressed-buttons))
+ (unless eat--mouse-pressed-buttons
+ (setq eat--mouse-last-position nil)
+ (when eat--mouse-drag-transient-map-exit
+ (funcall eat--mouse-drag-transient-map-exit)
+ (setq eat--mouse-drag-transient-map-exit nil))))
+ (when (memq 'down (event-modifiers e))
+ (push (event-basic-type e) eat--mouse-pressed-buttons)
+ (setq eat--mouse-last-position (event-start e))
+ (unless eat--mouse-drag-transient-map-exit
+ (let ((old-track-mouse track-mouse)
+ (buffer (current-buffer)))
+ (setq track-mouse 'dragging)
+ (setq eat--mouse-drag-transient-map-exit
+ (set-transient-map
+ (let ((map (eat-term-make-keymap
+ #'eat-self-input
+ '(:mouse-modifier
+ :mouse-movement)
+ nil)))
+ ;; Some of the events will of course end up
+ ;; looked up with a mode-line, header-line
+ ;; or vertical-line prefix ...
+ (define-key map [mode-line] map)
+ (define-key map [header-line] map)
+ (define-key map [tab-line] map)
+ (define-key map [vertical-line] map)
+ ;; ... and some maybe even with a right- or
+ ;; bottom-divider prefix.
+ (define-key map [right-divider] map)
+ (define-key map [bottom-divider] map))
+ #'always
+ (lambda ()
+ (with-current-buffer buffer
+ (setq track-mouse
+ old-track-mouse))))))))))
+ (eat-term-input-event eat--terminal n e))))
+
+(defun eat-quoted-input ()
+ "Read a char and send it as INPUT."
+ (declare (interactive-only "Use `eat-self-input' instead."))
+ (interactive)
+ ;; HACK: Quick hack to allow inputting `C-g'. Any better way to do
+ ;; this?
+ (eat-self-input 1 (let ((inhibit-quit t)
+ (quit-flag nil))
+ (read-event))))
+
+(defun eat-yank (&optional arg)
+ "Same as `yank', but for Eat.
+
+ARG is passed to `yank', which see."
+ (interactive "*P")
+ (when eat--terminal
+ (funcall eat--synchronize-scroll-function)
+ (cl-letf* ((inhibit-read-only t)
+ (insert-for-yank (symbol-function #'insert-for-yank))
+ ((symbol-function #'insert-for-yank)
+ (lambda (&rest args)
+ (cl-letf (((symbol-function #'insert)
+ (lambda (&rest args)
+ (eat-send-string-as-yank
+ eat--terminal
+ (mapconcat (lambda (arg)
+ (if (stringp arg)
+ arg
+ (string arg)))
+ args)))))
+ (apply insert-for-yank args)))))
+ (yank arg))))
+
+(defun eat-yank-pop (&optional arg)
+ "Same as `yank-pop', but for Eat.
+
+ARG is passed to `yank-pop', which see."
+ (interactive "p")
+ (when eat--terminal
+ (funcall eat--synchronize-scroll-function)
+ (cl-letf* ((inhibit-read-only t)
+ (insert-for-yank (symbol-function #'insert-for-yank))
+ ((symbol-function #'insert-for-yank)
+ (lambda (&rest args)
+ (cl-letf (((symbol-function #'insert)
+ (lambda (&rest args)
+ (eat-send-string-as-yank
+ eat--terminal
+ (mapconcat (lambda (arg)
+ (if (stringp arg)
+ arg
+ (string arg)))
+ args)))))
+ (apply insert-for-yank args)))))
+ (yank-pop arg))))
+
+;; When changing these keymaps, be sure to update the manual, README
+;; and commentary.
+(defvar eat-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map [?\C-c ?\M-d] #'eat-char-mode)
+ (define-key map [?\C-c ?\C-j] #'eat-semi-char-mode)
+ (define-key map [?\C-c ?\C-k] #'eat-kill-process)
+ map)
+ "Keymap for Eat mode.")
+
+(defvar eat-semi-char-mode-map
+ (let ((map (eat-term-make-keymap
+ #'eat-self-input
+ '(:ascii :arrow :navigation)
+ '( [?\C-\\] [?\C-q] [?\C-c] [?\C-x] [?\C-g] [?\C-h]
+ [?\e ?\C-c] [?\C-u] [?\C-q] [?\e ?x] [?\e ?:]
+ [?\e ?!] [?\e ?&] [?\C-y] [?\e ?y]))))
+ (define-key map [?\C-q] #'eat-quoted-input)
+ (define-key map [?\C-y] #'eat-yank)
+ (define-key map [?\M-y] #'eat-yank-pop)
+ (define-key map [?\C-c ?\C-c] #'eat-self-input)
+ (define-key map [?\C-c ?\C-e] #'eat-emacs-mode)
+ map)
+ "Keymap for Eat semi-char mode.")
+
+(defvar eat-char-mode-map
+ (let ((map (eat-term-make-keymap
+ #'eat-self-input
+ '(:ascii :arrow :navigation :function)
+ '([?\e ?\C-m]))))
+ (define-key map [?\C-\M-m] #'eat-semi-char-mode)
+ map)
+ "Keymap for Eat char mode.")
+
+(defvar eat--mouse-click-mode-map
+ (eat-term-make-keymap #'eat-self-input '(:mouse-click) nil)
+ "Keymap for `eat--mouse-click-mode'.")
+
+(defvar eat--mouse-modifier-click-mode-map
+ (eat-term-make-keymap #'eat-self-input '(:mouse-modifier) nil)
+ "Keymap for `eat--mouse-modifier-click-mode'.")
+
+(defvar eat--mouse-movement-mode-map
+ (eat-term-make-keymap #'eat-self-input '(:mouse-movement) nil)
+ "Keymap for `eat--mouse-movement-mode'.")
+
+(define-minor-mode eat--semi-char-mode
+ "Minor mode for semi-char mode keymap."
+ :interactive nil
+ :keymap eat-semi-char-mode-map)
+
+(define-minor-mode eat--char-mode
+ "Minor mode for char mode keymap."
+ :interactive nil
+ :keymap eat-char-mode-map)
+
+(define-minor-mode eat--mouse-click-mode
+ "Minor mode for mouse click keymap."
+ :interactive nil)
+
+(define-minor-mode eat--mouse-modifier-click-mode
+ "Minor mode for mouse click with modifiers keymap."
+ :interactive nil)
+
+(define-minor-mode eat--mouse-movement-mode
+ "Minor mode for mouse movement keymap."
+ :interactive nil)
+
+(defun eat-emacs-mode ()
+ "Switch to Emacs keybindings mode."
+ (interactive)
+ (eat--semi-char-mode -1)
+ (eat--char-mode -1)
+ (setq buffer-read-only t)
+ (eat--grab-mouse nil eat--mouse-grabbing-type)
+ (force-mode-line-update))
+
+(defun eat-semi-char-mode ()
+ "Switch to semi-char mode."
+ (interactive)
+ (if (not eat--terminal)
+ (error "Process not running")
+ (setq buffer-read-only nil)
+ (eat--char-mode -1)
+ (eat--semi-char-mode +1)
+ (eat--grab-mouse nil eat--mouse-grabbing-type)
+ (force-mode-line-update)))
+
+(defun eat-char-mode ()
+ "Switch to char mode."
+ (interactive)
+ (if (not eat--terminal)
+ (error "Process not running")
+ (setq buffer-read-only nil)
+ (eat--semi-char-mode -1)
+ (eat--char-mode +1)
+ (eat--grab-mouse nil eat--mouse-grabbing-type)
+ (force-mode-line-update)))
+
+(defvar eat--eshell-semi-char-mode)
+(defvar eat--eshell-char-mode)
+
+(defun eat--grab-mouse (_ mode)
+ "Grab mouse.
+
+MODE should one of:
+
+ nil Disable mouse.
+ `:click' Pass `mouse-1', `mouse-2', and `mouse-3'
+ clicks.
+ `:modifier-click' Pass all mouse clicks, including control,
+ meta and shift modifiers.
+ `:drag' All of :modifier-click, plus dragging
+ (moving mouse while pressed) information.
+ `:all' Pass all mouse events, including movement.
+ Any other value Disable mouse."
+ (setq eat--mouse-grabbing-type mode)
+ (pcase (and eat-enable-mouse
+ (or eat--semi-char-mode
+ eat--char-mode
+ eat--eshell-semi-char-mode
+ eat--eshell-char-mode)
+ mode)
+ (:all
+ (setq track-mouse t)
+ (eat--mouse-click-mode -1)
+ (eat--mouse-modifier-click-mode +1)
+ (eat--mouse-movement-mode +1))
+ ((or :modifier-click :drag)
+ (setq track-mouse nil)
+ (eat--mouse-click-mode -1)
+ (eat--mouse-movement-mode -1)
+ (eat--mouse-modifier-click-mode +1))
+ (:click
+ (setq track-mouse nil)
+ (eat--mouse-modifier-click-mode -1)
+ (eat--mouse-movement-mode -1)
+ (eat--mouse-click-mode +1))
+ (_
+ (setq track-mouse nil)
+ (eat--mouse-click-mode -1)
+ (eat--mouse-modifier-click-mode -1)
+ (eat--mouse-movement-mode -1))))
+
+(defun eat--manipulate-kill-ring (_ selection data)
+ "Manipulate `kill-ring'.
+
+SELECTION can be one of `:clipboard', `:primary', `:secondary',
+`:select'. When DATA is a string, set the selection to that string,
+when DATA is nil, unset the selection, and when DATA is t, return the
+selection, or nil if none."
+ (let ((inhibit-eol-conversion t)
+ (select-enable-clipboard (eq selection :clipboard))
+ (select-enable-primary (eq selection :primary)))
+ (pcase data
+ ('t
+ (when eat-enable-yank-to-terminal
+ (ignore-error error
+ (current-kill 0 'do-not-move))))
+ ((and (pred stringp)
+ str)
+ (when eat-enable-kill-from-terminal
+ (kill-new str))))))
+
+(defun eat--bell (_)
+ "Ring the bell."
+ (beep t))
+
+
+;;;;; Major Mode.
+
+(defun eat--synchronize-scroll ()
+ "Synchronize scrolling and point between terminal and window."
+ (when-let ((window (get-buffer-window (current-buffer))))
+ (set-window-start
+ window (eat-term-display-beginning eat--terminal)))
+ (goto-char (eat-term-display-cursor eat--terminal)))
+
+(defun eat--setup-glyphless-chars ()
+ "Setup the display of glyphless characters."
+ (setq-local glyphless-char-display
+ (copy-sequence (default-value 'glyphless-char-display)))
+ (set-char-table-extra-slot
+ glyphless-char-display 0
+ (if (display-graphic-p) 'empty-box 'thin-space)))
+
+(defun eat--filter-buffer-substring (begin end &optional delete)
+ "Filter buffer substring from BEGIN to END and return that.
+
+When DELETE is given and non-nil, delete the text between BEGIN and
+END if it's safe to do so."
+ (let ((str (eat-term-filter-string (buffer-substring begin end))))
+ (when (and delete
+ (or (not eat--terminal)
+ (and (<= (eat-term-end eat--terminal) begin)
+ (<= (eat-term-end eat--terminal) end))
+ (and (<= begin (eat-term-beginning eat--terminal))
+ (<= end (eat-term-beginning eat--terminal)))))
+ (delete-region begin end))
+ str))
+
+(define-derived-mode eat-mode fundamental-mode "Eat"
+ "Major mode for Eat."
+ :group 'eat-ui
+ (make-local-variable 'buffer-read-only)
+ (make-local-variable 'buffer-undo-list)
+ (make-local-variable 'filter-buffer-substring-function)
+ (make-local-variable 'mode-line-process)
+ (make-local-variable 'mode-line-buffer-identification)
+ (make-local-variable 'glyphless-char-display)
+ (make-local-variable 'cursor-type)
+ (make-local-variable 'track-mouse)
+ (make-local-variable 'eat--terminal)
+ (make-local-variable 'eat--process)
+ (make-local-variable 'eat--synchronize-scroll-function)
+ (make-local-variable 'eat--mouse-grabbing-type)
+ (make-local-variable 'eat--pending-output-chunks)
+ (make-local-variable 'eat--output-queue-first-chunk-time)
+ (make-local-variable 'eat--process-output-queue-timer)
+ ;; This is intended; input methods don't work on read-only buffers.
+ (setq buffer-read-only nil)
+ (setq buffer-undo-list t)
+ (setq eat--synchronize-scroll-function #'eat--synchronize-scroll)
+ (setq eat--mouse-grabbing-type nil)
+ (setq filter-buffer-substring-function
+ #'eat--filter-buffer-substring)
+ (setq mode-line-process
+ '(""
+ (:eval
+ (when eat--process
+ (cond
+ (eat--semi-char-mode
+ `("["
+ (:propertize
+ "semi-char"
+ help-echo
+ ,(concat "mouse-1: Switch to char mode, "
+ "mouse-3: Switch to emacs mode")
+ mouse-face mode-line-highlight
+ local-map
+ (keymap
+ (mode-line
+ . (keymap
+ (down-mouse-1 . eat-char-mode)
+ (down-mouse-3 . eat-emacs-mode)))))
+ "]"))
+ (eat--char-mode
+ '("["
+ (:propertize
+ "char"
+ help-echo
+ ,(concat "mouse-1: Switch to semi-char mode, "
+ "mouse-3: Switch to emacs mode")
+ mouse-face mode-line-highlight
+ local-map
+ (keymap
+ (mode-line
+ . (keymap
+ (down-mouse-1 . eat-semi-char-mode)
+ (down-mouse-3 . eat-emacs-mode)))))
+ "]"))
+ (t
+ `("["
+ (:propertize
+ "emacs"
+ help-echo
+ ,(concat "mouse-1: Switch to semi char mode, "
+ "mouse-3: Switch to char mode")
+ mouse-face mode-line-highlight
+ local-map
+ (keymap
+ (mode-line
+ . (keymap
+ (down-mouse-1 . eat-semi-char-mode)
+ (down-mouse-3 . eat-char-mode)))))
+ "]")))))
+ ":%s"))
+ (setq mode-line-buffer-identification
+ `(12 (""
+ ,(nconc
+ (propertized-buffer-identification "%b")
+ '(" "
+ (:propertize
+ (:eval
+ (when (and eat--terminal
+ (not (string-empty-p (eat-term-title
+ eat--terminal))))
+ (format "(%s)" (eat-term-title
+ eat--terminal))))
+ help-echo "Title"))))))
+ (eat-emacs-mode)
+ ;; Make sure glyphless character don't display a huge box glyph,
+ ;; that would break the display.
+ (eat--setup-glyphless-chars)
+ (when eat-enable-blinking-text
+ (eat-blink-mode +1)))
+
+
+;;;;; Process Handling.
+
+(defvar eat--process nil
+ "The running process.")
+
+(defvar eat--pending-output-chunks nil
+ "The list of pending output chunks.
+
+The output chunks are pushed, so last output appears first.")
+
+(defvar eat--output-queue-first-chunk-time nil
+ "Time when the first chunk in the current output queue was pushed.")
+
+(defvar eat--process-output-queue-timer nil
+ "Timer to process output queue.")
+
+(defun eat-kill-process ()
+ "Kill Eat process in current buffer."
+ (interactive)
+ (when eat--process
+ (kill-process eat--process)))
+
+(defun eat--send-string (process string)
+ "Send to PROCESS the contents of STRING as input.
+
+This is equivalent to `process-send-string', except that long input
+strings are broken up into chunks of size `eat-input-chunk-size'.
+Processes are given a chance to output between chunks. This can help
+prevent processes from hanging when you send them long inputs on some
+OS's."
+ (let ((i 0)
+ (j eat-input-chunk-size)
+ (l (length string)))
+ (while (< i l)
+ (process-send-string process (substring string i (min j l)))
+ (accept-process-output)
+ (cl-incf i eat-input-chunk-size)
+ (cl-incf j eat-input-chunk-size))))
+
+(defun eat--send-input (_ input)
+ "Send INPUT to subprocess."
+ (when eat--process
+ (eat--send-string eat--process input)))
+
+(defun eat--process-output-queue (buffer)
+ "Process the output queue on BUFFER."
+ (when (buffer-live-p buffer)
+ (with-current-buffer buffer
+ (let ((inhibit-quit t) ; Don't disturb!
+ (inhibit-read-only t)
+ (inhibit-modification-hooks t)
+ (synchronize-scroll
+ (or (= (eat-term-display-cursor eat--terminal) (point))
+ eat--char-mode)))
+ (when eat--process-output-queue-timer
+ (cancel-timer eat--process-output-queue-timer))
+ (setq eat--output-queue-first-chunk-time nil)
+ (let ((queue eat--pending-output-chunks))
+ (setq eat--pending-output-chunks nil)
+ (dolist (output (nreverse queue))
+ (eat-term-process-output eat--terminal output)))
+ (eat-term-redisplay eat--terminal)
+ ;; Truncate output of previous dead processes.
+ (when (and eat-term-scrollback-size
+ (< eat-term-scrollback-size
+ (- (point) (point-min))))
+ (delete-region
+ (point-min)
+ (max (point-min)
+ (- (eat-term-display-beginning eat--terminal)
+ eat-term-scrollback-size))))
+ (when synchronize-scroll
+ (funcall eat--synchronize-scroll-function))))))
+
+(defun eat--filter (process output)
+ "Handle OUTPUT from PROCESS."
+ (when (buffer-live-p (process-buffer process))
+ (with-current-buffer (process-buffer process)
+ (when eat--process-output-queue-timer
+ (cancel-timer eat--process-output-queue-timer))
+ (unless eat--output-queue-first-chunk-time
+ (setq eat--output-queue-first-chunk-time (current-time)))
+ (push output eat--pending-output-chunks)
+ (let ((time-left
+ (- eat-maximum-latency
+ (float-time
+ (time-subtract
+ nil eat--output-queue-first-chunk-time)))))
+ (if (<= time-left 0)
+ (eat--process-output-queue (current-buffer))
+ (setq eat--process-output-queue-timer
+ (run-with-timer
+ (min time-left eat-minimum-latency) nil
+ #'eat--process-output-queue (current-buffer))))))))
+
+(defun eat--sentinel (process message)
+ "Sentinel for Eat buffers.
+
+PROCESS is the process and MESSAGE is the description of what happened
+to it."
+ (let ((buffer (process-buffer process)))
+ (when (memq (process-status process) '(signal exit))
+ (if (buffer-live-p buffer)
+ (if eat-kill-buffer-on-exit
+ (kill-buffer buffer)
+ (with-current-buffer buffer
+ (let ((inhibit-read-only t))
+ (eat--process-output-queue (current-buffer))
+ (eat-emacs-mode)
+ (setq eat--process nil)
+ (delete-process process)
+ (eat-term-delete eat--terminal)
+ (setq eat--terminal nil)
+ (eat--set-cursor nil :default)
+ (eat--grab-mouse nil nil)
+ (goto-char (point-max))
+ (insert "\nProcess " (process-name process) " "
+ message)
+ (setq buffer-read-only nil))))
+ (set-process-buffer process nil)))))
+
+(defun eat--adjust-process-window-size (process windows)
+ "Resize process window and terminal. Return new dimensions.
+
+PROCESS is the process whose window to resize, and WINDOWS is the list
+of window displaying PROCESS's buffer."
+ (let* ((size (funcall window-adjust-process-window-size-function
+ process windows)))
+ (when size
+ (let ((width (max (car size) 1))
+ (height (max (cdr size) 1))
+ (inhibit-read-only t)
+ (synchronize-scroll
+ (and (<= (eat-term-display-beginning eat--terminal)
+ (point))
+ (or (< (point) (eat-term-end eat--terminal))
+ (= (point) (eat-term-end eat--terminal)
+ (point-max))))))
+ (eat-term-resize eat--terminal width height)
+ (eat-term-redisplay eat--terminal)
+ (when synchronize-scroll
+ (funcall eat--synchronize-scroll-function))))
+ size))
+
+;; Adapted from Term.
+(defun eat-exec (buffer name command startfile switches)
+ "Start up a process in BUFFER for Eat mode.
+
+Run COMMAND with SWITCHES. Set NAME as the name of the process.
+Blast any old process running in the buffer. Don't set the buffer
+mode. You can use this to cheaply run a series of processes in the
+same Eat buffer. The hook `eat-exec-hook' is run after each exec."
+ (with-current-buffer buffer
+ (let ((inhibit-read-only t))
+ (when eat--process
+ (let ((eat-kill-buffer-on-exit nil))
+ (delete-process eat--process)))
+ ;; Ensure final newline.
+ (goto-char (point-max))
+ (unless (or (= (point-min) (point-max))
+ (= (char-before (point-max)) ?\n))
+ (insert ?\n))
+ (unless (= (point-min) (point-max))
+ (insert "\n\n"))
+ (setq eat--terminal (eat-term-make buffer (point)))
+ (eat-semi-char-mode)
+ (when-let ((window (get-buffer-window nil t)))
+ (with-selected-window window
+ (eat-term-resize eat--terminal (window-max-chars-per-line)
+ (window-text-height))))
+ (setf (eat-term-input-function eat--terminal) #'eat--send-input)
+ (setf (eat-term-set-cursor-function eat--terminal)
+ #'eat--set-cursor)
+ (setf (eat-term-grab-mouse-function eat--terminal)
+ #'eat--grab-mouse)
+ (setf (eat-term-manipulate-selection-function eat--terminal)
+ #'eat--manipulate-kill-ring)
+ (setf (eat-term-ring-bell-function eat--terminal) #'eat--bell)
+ ;; Crank up a new process.
+ (let* ((size (eat-term-size eat--terminal))
+ (process-environment
+ (nconc
+ (list
+ (concat "TERM=" (eat-term-name))
+ (concat "TERMINFO=" eat-term-terminfo-directory)
+ (concat "INSIDE_EMACS=" eat-term-inside-emacs))
+ process-environment))
+ (process-connection-type t)
+ ;; We should suppress conversion of end-of-line format.
+ (inhibit-eol-conversion t)
+ (process
+ (make-process
+ :name name
+ :buffer buffer
+ :command `("/usr/bin/env" "sh" "-c"
+ ,(format "stty -nl echo rows %d columns \
+%d sane 2>%s ; if [ $1 = .. ]; then shift; fi; exec \"$@\""
+ (cdr size) (car size)
+ null-device)
+ ".."
+ ,command ,@switches)
+ :filter #'eat--filter
+ :sentinel #'eat--sentinel
+ :file-handler t)))
+ (process-put process 'adjust-window-size-function
+ #'eat--adjust-process-window-size)
+ ;; Jump to the end, and set the process mark.
+ (goto-char (point-max))
+ (set-marker (process-mark process) (point))
+ (setq eat--process process)
+ ;; Feed it the startfile.
+ (when startfile
+ ;;This is guaranteed to wait long enough
+ ;;but has bad results if the shell does not prompt at all
+ ;; (while (= size (buffer-size))
+ ;; (sleep-for 1))
+ ;;I hope 1 second is enough!
+ (sleep-for 1)
+ (goto-char (point-max))
+ (insert-file-contents startfile)
+ (process-send-string
+ process (delete-and-extract-region (point) (point-max)))))
+ (eat-term-redisplay eat--terminal)
+ (run-hooks 'eat-exec-hook)
+ buffer)))
+
+
+;;;;; Entry Points.
+
+(defun eat-make (name program &optional startfile &rest switches)
+ "Make a Eat process NAME in a buffer, running PROGRAM.
+
+The name of the buffer is made by surrounding NAME with `*'s. If
+there is already a running process in that buffer, it is not
+restarted. Optional third arg STARTFILE is the name of a file to send
+the contents of to the process. SWITCHES are the arguments to
+PROGRAM."
+ (let ((buffer (get-buffer-create (concat "*" name "*"))))
+ ;; If no process, or nuked process, crank up a new one and put
+ ;; buffer in Eat mode. Otherwise, leave buffer and existing
+ ;; process alone.
+ (when (not (let ((proc (get-buffer-process buffer)))
+ (and proc (memq (process-status proc)
+ '(run stop open listen connect)))))
+ (with-current-buffer buffer
+ (eat-mode))
+ (eat-exec buffer name program startfile switches))
+ buffer))
+
+;;;###autoload
+(defun eat (&optional program arg)
+ "Start a new Eat terminal emulator in a buffer.
+
+Start a new Eat session, or switch to an already active session.
+Return the buffer selected (or created).
+
+With a non-numeric prefix ARG, create a new session.
+
+With a numeric prefix ARG (like \\[universal-argument] 42 \\[eshell]),
+switch to the session with that number, or create it if it doesn't
+already exist.
+
+PROGRAM can be a shell command."
+ (interactive (list (read-shell-command "Run program: "
+ (or explicit-shell-file-name
+ (getenv "ESHELL")
+ shell-file-name))
+ current-prefix-arg))
+ (let ((program (or program (or explicit-shell-file-name
+ (getenv "ESHELL")
+ shell-file-name)))
+ (buffer
+ (cond
+ ((numberp arg)
+ (get-buffer-create (format "%s<%d>" eat-buffer-name arg)))
+ (arg
+ (generate-new-buffer eat-buffer-name))
+ (t
+ (get-buffer-create eat-buffer-name)))))
+ (with-current-buffer buffer
+ (unless (eq major-mode #'eat-mode)
+ (eat-mode))
+ (pop-to-buffer-same-window buffer)
+ (unless eat--process
+ (eat-exec buffer "eat" "/usr/bin/env" nil
+ `("sh" "-c" ,program)))
+ buffer)))
+
+
+;;;; Eshell integration.
+
+;;;;; Input.
+
+;; When changing these keymaps, be sure to update the manual, README
+;; and commentary.
+(defvar eat-eshell-emacs-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map [?\C-c ?\C-j] #'eat-eshell-semi-char-mode)
+ (define-key map [remap eshell-toggle-direct-send] ; C-c M-d
+ #'eat-eshell-char-mode)
+ map)
+ "Keymap for Eat Eshell when no process is running.")
+
+(defvar eat-eshell-semi-char-mode-map
+ (let ((map (eat-term-make-keymap
+ #'eat-self-input
+ '(:ascii :arrow :navigation)
+ '( [?\C-\\] [?\C-q] [?\C-c] [?\C-x] [?\C-g] [?\C-h]
+ [?\e ?\C-c] [?\C-u] [?\C-q] [?\e ?x] [?\e ?:]
+ [?\e ?!] [?\e ?&] [?\C-y] [?\e ?y]))))
+ (define-key map [?\C-q] #'eat-quoted-input)
+ (define-key map [?\C-y] #'eat-yank)
+ (define-key map [?\M-y] #'eat-yank-pop)
+ (define-key map [?\C-c ?\C-e] #'eat-eshell-emacs-mode)
+ map)
+ "Keymap for Eat Eshell semi-char mode.")
+
+(defvar eat-eshell-char-mode-map
+ (let ((map (eat-term-make-keymap
+ #'eat-self-input
+ '(:ascii :arrow :navigation :function)
+ '([?\e ?\C-m]))))
+ (define-key map [?\C-\M-m] #'eat-eshell-semi-char-mode)
+ map)
+ "Keymap for Eat Eshell char mode.")
+
+(define-minor-mode eat--eshell-semi-char-mode
+ "Minor mode for semi-char mode keymap."
+ :interactive nil
+ :keymap eat-eshell-semi-char-mode-map
+ ;; HACK: Some keys like `C-c' are overriden by other keymaps
+ ;; (possibly by the keymaps of other minor modes), so we also put
+ ;; the keymap to `minor-mode-overriding-map-alist' to make Emacs
+ ;; prioritize us.
+ (setq minor-mode-overriding-map-alist
+ (delete (cons #'eat--eshell-semi-char-mode
+ eat-eshell-semi-char-mode-map)
+ minor-mode-overriding-map-alist))
+ (when eat--eshell-semi-char-mode
+ (push (cons #'eat--eshell-semi-char-mode
+ eat-eshell-semi-char-mode-map)
+ minor-mode-overriding-map-alist)))
+
+(define-minor-mode eat--eshell-char-mode
+ "Minor mode for char mode keymap."
+ :interactive nil
+ :keymap eat-eshell-char-mode-map
+ ;; HACK: Some keys like `C-c' are overriden by other keymaps
+ ;; (possibly by the keymaps of other minor modes), so we also put
+ ;; the keymap to `minor-mode-overriding-map-alist' to make Emacs
+ ;; prioritize us.
+ (setq minor-mode-overriding-map-alist
+ (delete (cons #'eat--eshell-char-mode
+ eat-eshell-char-mode-map)
+ minor-mode-overriding-map-alist))
+ (when eat--eshell-char-mode
+ (push (cons #'eat--eshell-char-mode eat-eshell-char-mode-map)
+ minor-mode-overriding-map-alist)))
+
+(defun eat-eshell-emacs-mode ()
+ "Switch to Emacs keybindings mode."
+ (interactive)
+ (eat--eshell-semi-char-mode -1)
+ (eat--eshell-char-mode -1)
+ (setq buffer-read-only t)
+ (eat--grab-mouse nil eat--mouse-grabbing-type)
+ (force-mode-line-update))
+
+(defun eat-eshell-semi-char-mode ()
+ "Switch to semi-char mode."
+ (interactive)
+ (when eat--terminal
+ (setq buffer-read-only nil)
+ (eat--eshell-char-mode -1)
+ (eat--eshell-semi-char-mode +1)
+ (eat--grab-mouse nil eat--mouse-grabbing-type)
+ (force-mode-line-update)))
+
+(defun eat-eshell-char-mode ()
+ "Switch to char mode."
+ (interactive)
+ (when eat--terminal
+ (setq buffer-read-only nil)
+ (eat--eshell-semi-char-mode -1)
+ (eat--eshell-char-mode +1)
+ (eat--grab-mouse nil eat--mouse-grabbing-type)
+ (force-mode-line-update)))
+
+
+;;;;; Process Handling.
+
+(defun eat--eshell-output-filter ()
+ "Handle output from subprocess."
+ (defvar eshell-last-output-start) ; In `esh-mode'.
+ (defvar eshell-last-output-end) ; In `esh-mode'.
+ (let ((inhibit-quit t) ; Don't disturb!
+ (inhibit-read-only t)
+ (str (buffer-substring-no-properties
+ eshell-last-output-start
+ eshell-last-output-end)))
+ (delete-region eshell-last-output-start eshell-last-output-end)
+ (let ((synchronize-scroll
+ (or (= (eat-term-display-cursor eat--terminal) (point))
+ eat--eshell-char-mode)))
+ (eat-term-process-output eat--terminal str)
+ (eat-term-redisplay eat--terminal)
+ (when synchronize-scroll
+ (funcall eat--synchronize-scroll-function)))
+ (let ((end (eat-term-end eat--terminal)))
+ (set-marker eshell-last-output-start end)
+ (set-marker eshell-last-output-end end)
+ (set-marker (process-mark eat--process) end))))
+
+(defun eat--eshell-setup-proc-and-term (proc)
+ "Setup process PROC and a new terminal for it."
+ (unless (or eat--terminal eat--process)
+ (setq eat--process proc)
+ (process-put proc 'adjust-window-size-function
+ #'eat--adjust-process-window-size)
+ (setq eat--terminal (eat-term-make (current-buffer)
+ (process-mark proc)))
+ (set-marker (process-mark proc) (eat-term-end eat--terminal))
+ (setf (eat-term-input-function eat--terminal) #'eat--send-input)
+ (setf (eat-term-set-cursor-function eat--terminal)
+ #'eat--set-cursor)
+ (setf (eat-term-grab-mouse-function eat--terminal)
+ #'eat--grab-mouse)
+ (setf (eat-term-manipulate-selection-function eat--terminal)
+ #'eat--manipulate-kill-ring)
+ (setf (eat-term-ring-bell-function eat--terminal) #'eat--bell)
+ (when-let ((window (get-buffer-window nil t)))
+ (with-selected-window window
+ (eat-term-resize eat--terminal (window-max-chars-per-line)
+ (window-text-height))))
+ (eat-term-redisplay eat--terminal)
+ (make-local-variable 'eshell-output-filter-functions)
+ (setq eshell-output-filter-functions '(eat--eshell-output-filter))
+ (eat-eshell-semi-char-mode)))
+
+(defun eat--eshell-cleanup ()
+ "Cleanup everything."
+ (defvar eshell-last-output-start) ; In `esh-mode'.
+ (defvar eshell-last-output-end) ; In `esh-mode'.
+ (when eat--terminal
+ (let ((inhibit-read-only t))
+ (goto-char (eat-term-end eat--terminal))
+ (unless (or (= (point) (point-min))
+ (= (char-before) ?\n))
+ (insert ?\n))
+ (set-marker eshell-last-output-start (point))
+ (set-marker eshell-last-output-end (point))
+ (eat--cursor-blink-mode -1)
+ (eat--grab-mouse nil nil)
+ (eat-term-delete eat--terminal)
+ (set-process-filter eat--process #'eshell-output-filter)
+ (setq eat--terminal nil)
+ (setq eat--process nil)
+ (kill-local-variable 'eshell-output-filter-functions)
+ (eat--eshell-semi-char-mode -1)
+ (eat--eshell-char-mode -1)
+ (setq buffer-read-only nil))))
+
+(defun eat--eshell-process-output-queue (process buffer)
+ "Process the output queue on BUFFER from PROCESS."
+ (declare-function eshell-output-filter "esh-mode" (process string))
+ (when (buffer-live-p buffer)
+ (with-current-buffer buffer
+ (when eat--process-output-queue-timer
+ (cancel-timer eat--process-output-queue-timer))
+ (setq eat--output-queue-first-chunk-time nil)
+ (let ((queue eat--pending-output-chunks))
+ (setq eat--pending-output-chunks nil)
+ (combine-change-calls
+ (eat-term-beginning eat--terminal)
+ (eat-term-end eat--terminal)
+ ;; TODO: Is `string-join' OK or should we use a loop?
+ (eshell-output-filter
+ process (string-join (nreverse queue))))))))
+
+(defun eat--eshell-filter (process string)
+ "Process output STRING from PROCESS."
+ (when (buffer-live-p (process-buffer process))
+ (with-current-buffer (process-buffer process)
+ (when eat--process-output-queue-timer
+ (cancel-timer eat--process-output-queue-timer))
+ (unless eat--output-queue-first-chunk-time
+ (setq eat--output-queue-first-chunk-time (current-time)))
+ (push string eat--pending-output-chunks)
+ (let ((time-left
+ (- eat-maximum-latency
+ (float-time
+ (time-subtract
+ nil eat--output-queue-first-chunk-time)))))
+ (if (<= time-left 0)
+ (eat--eshell-process-output-queue
+ process (current-buffer))
+ (setq eat--process-output-queue-timer
+ (run-with-timer
+ (min time-left eat-minimum-latency) nil
+ #'eat--eshell-process-output-queue process
+ (current-buffer))))))))
+
+(defun eat--eshell-sentinel (process message)
+ "Process status message MESSAGE from PROCESS."
+ (declare-function eshell-sentinel "esh-proc" (proc string))
+ (when (buffer-live-p (process-buffer process))
+ (with-current-buffer (process-buffer process)
+ (cl-letf* ((process-send-string
+ (symbol-function #'process-send-string))
+ ((symbol-function #'process-send-string)
+ (lambda (proc string)
+ (when (or (not (eq proc process))
+ (process-live-p proc))
+ (funcall process-send-string proc string)))))
+ (eat--eshell-process-output-queue process (current-buffer)))
+ (when (memq (process-status process) '(signal exit))
+ (eat--eshell-cleanup))))
+ (eshell-sentinel process message))
+
+;; HACK: This is a dirty hack, it can break easily.
+(defun eat--eshell-adjust-make-process-args (fn command args)
+ "Setup an environment to adjust `make-process' arguments.
+
+Call FN with COMMAND and ARGS, and whenever `make-process' is called,
+modify its argument to change the filter, the sentinel and invoke
+`stty' from the new process."
+ (cl-letf*
+ ((make-process (symbol-function #'make-process))
+ ((symbol-function #'make-process)
+ (lambda (&rest plist)
+ ;; Make sure we don't attack wrong process.
+ (if (not (and (eq (plist-get plist :filter)
+ #'eshell-output-filter)
+ (eq (plist-get plist :sentinel)
+ #'eshell-sentinel)
+ (equal (plist-get plist :command)
+ (cons (file-local-name
+ (expand-file-name command))
+ args))))
+ (apply make-process plist)
+ (plist-put plist :filter #'eat--eshell-filter)
+ (plist-put plist :sentinel #'eat--eshell-sentinel)
+ (plist-put
+ plist :command
+ `("/usr/bin/env" "sh" "-c"
+ ,(format "stty -nl echo rows %d columns %d \
+sane 2>%s ; if [ $1 = .. ]; then shift; fi; exec \"$@\""
+ (window-text-height)
+ (window-max-chars-per-line) null-device)
+ ".."
+ ,@(plist-get plist :command)))
+ (let ((process (apply make-process plist)))
+ (eat--eshell-setup-proc-and-term process)
+ process)))))
+ (funcall fn command args)))
+
+
+;;;;; Minor Modes.
+
+(defun eat--eshell-synchronize-scroll ()
+ "Synchronize scrolling and point between terminal and window."
+ (when-let ((window (get-buffer-window (current-buffer))))
+ (set-window-start
+ window
+ (if (or (eat-term-in-alternative-display-p eat--terminal)
+ eat--eshell-char-mode)
+ (eat-term-display-beginning eat--terminal)
+ (save-restriction
+ (narrow-to-region
+ (eat-term-beginning eat--terminal)
+ (eat-term-end eat--terminal))
+ (let ((start-line (- (window-text-height window)
+ (line-number-at-pos (point-max)))))
+ (goto-char (point-min))
+ (widen)
+ (if (<= start-line 0)
+ (eat-term-display-beginning eat--terminal)
+ (vertical-motion (- start-line))
+ (point)))))))
+ (goto-char (eat-term-display-cursor eat--terminal)))
+
+(define-minor-mode eat--eshell-local-mode
+ "Toggle Eat terminal emulation is Eshell."
+ :interactive nil
+ :keymap eat-eshell-emacs-mode-map
+ (cond
+ (eat--eshell-local-mode
+ (make-local-variable 'cursor-type)
+ (make-local-variable 'glyphless-char-display)
+ (make-local-variable 'track-mouse)
+ (make-local-variable 'filter-buffer-substring-function)
+ (make-local-variable 'eat--terminal)
+ (make-local-variable 'eat--process)
+ (make-local-variable 'eat--synchronize-scroll-function)
+ (make-local-variable 'eat--mouse-grabbing-type)
+ (make-local-variable 'eat--pending-output-chunks)
+ (make-local-variable 'eat--output-queue-first-chunk-time)
+ (make-local-variable 'eat--process-output-queue-timer)
+ (setq eat--synchronize-scroll-function
+ #'eat--eshell-synchronize-scroll)
+ (setq filter-buffer-substring-function
+ #'eat--filter-buffer-substring)
+ ;; Make sure glyphless character don't display a huge box glyph,
+ ;; that would break the display.
+ (eat--setup-glyphless-chars)
+ (when eat-enable-blinking-text
+ (eat-blink-mode +1)))
+ (t
+ (when eat-enable-blinking-text
+ (eat-blink-mode -1))
+ (kill-local-variable 'cursor-type)
+ (kill-local-variable 'glyphless-char-display)
+ (kill-local-variable 'track-mouse)
+ (make-local-variable 'filter-buffer-substring-function)
+ (kill-local-variable 'eat--terminal)
+ (kill-local-variable 'eat--process)
+ (kill-local-variable 'eat--synchronize-scroll-function)
+ (kill-local-variable 'eat--mouse-grabbing-type)
+ (kill-local-variable 'eat--pending-output-chunks)
+ (kill-local-variable 'eat--output-queue-first-chunk-time)
+ (kill-local-variable 'eat--process-output-queue-timer))))
+
+;;;###autoload
+(define-minor-mode eat-eshell-mode
+ "Toggle Eat terminal emulation is Eshell."
+ :global t
+ :lighter (eat--eshell-local-mode
+ (" Eat-Eshell"
+ (:eval
+ (when eat--terminal
+ (cond
+ (eat--eshell-semi-char-mode
+ `("["
+ (:propertize
+ "semi-char"
+ help-echo
+ ,(concat "mouse-1: Switch to char mode, "
+ "mouse-3: Switch to emacs mode")
+ mouse-face mode-line-highlight
+ local-map
+ (keymap
+ (mode-line
+ . (keymap
+ (down-mouse-1 . eat-eshell-char-mode)
+ (down-mouse-3 . eat-eshell-emacs-mode)))))
+ "]"))
+ (eat--eshell-char-mode
+ '("["
+ (:propertize
+ "char"
+ help-echo
+ ,(concat "mouse-1: Switch to semi-char mode, "
+ "mouse-3: Switch to emacs mode")
+ mouse-face mode-line-highlight
+ local-map
+ (keymap
+ (mode-line
+ . (keymap
+ (down-mouse-1 . eat-eshell-semi-char-mode)
+ (down-mouse-3 . eat-eshell-emacs-mode)))))
+ "]"))
+ (t
+ `("["
+ (:propertize
+ "emacs"
+ help-echo
+ ,(concat "mouse-1: Switch to semi-char mode, "
+ "mouse-3: Switch to char mode")
+ mouse-face mode-line-highlight
+ local-map
+ (keymap
+ (mode-line
+ . (keymap
+ (down-mouse-1 . eat-eshell-semi-char-mode)
+ (down-mouse-3 . eat-eshell-char-mode)))))
+ "]")))))))
+ :group 'eat-ehell
+ (defvar eshell-variable-aliases-list) ; In `esh-var'.
+ (defvar eshell-last-async-procs) ; In `esh-cmd'.
+ (declare-function eshell-gather-process-output "esh-proc"
+ (command args))
+ (cond
+ (eat-eshell-mode
+ (let ((buffers nil))
+ (setq eat-eshell-mode nil)
+ (require 'esh-mode)
+ (require 'esh-proc)
+ (require 'esh-var)
+ (require 'esh-cmd)
+ (dolist (buffer (buffer-list))
+ (with-current-buffer buffer
+ (when (eq major-mode #'eshell-mode)
+ (when eshell-last-async-procs
+ (user-error
+ (concat "Can't toggle Eat Eshell mode while"
+ " any Eshell process is running")))
+ (push buffer buffers))))
+ (setq eat-eshell-mode t)
+ (dolist (buffer buffers)
+ (with-current-buffer buffer
+ (eat--eshell-local-mode +1))))
+ (add-hook 'eshell-mode-hook #'eat--eshell-local-mode)
+ (setq eshell-variable-aliases-list
+ `(("TERM" eat-term-name t t)
+ ("TERMINFO" eat-term-terminfo-directory t)
+ ("INSIDE_EMACS" eat-term-inside-emacs t)
+ ,@eshell-variable-aliases-list))
+ (advice-add #'eshell-gather-process-output :around
+ #'eat--eshell-adjust-make-process-args))
+ (t
+ (let ((buffers nil))
+ (setq eat-eshell-mode t)
+ (dolist (buffer (buffer-list))
+ (with-current-buffer buffer
+ (when (and (eq major-mode #'eshell-mode)
+ eat--eshell-local-mode)
+ (when eshell-last-async-procs
+ (user-error
+ (concat "Can't toggle Eat Eshell mode while"
+ " any Eshell process is running")))
+ (push buffer buffers))))
+ (setq eat-eshell-mode nil)
+ (dolist (buffer buffers)
+ (with-current-buffer buffer
+ (eat--eshell-local-mode -1))))
+ (remove-hook 'eshell-mode-hook #'eat--eshell-local-mode)
+ (setq eshell-variable-aliases-list
+ (cl-delete-if
+ (lambda (elem)
+ (member elem
+ '(("TERM" eat-term-name t t)
+ ("TERMINFO" eat-term-terminfo-directory t)
+ ("INSIDE_EMACS" eat-term-inside-emacs t))))
+ eshell-variable-aliases-list))
+ (advice-remove #'eshell-gather-process-output
+ #'eat--eshell-adjust-make-process-args))))
+
+
+;;;; Eshell Visual Command Handling.
+
+;; Adapted from `em-term'.
+(defun eat--eshell-visual-sentinel (proc _msg)
+ "Clean up the buffer visiting PROC.
+
+If `eshell-destroy-buffer-when-process-dies' is non-nil, destroy
+the buffer.
+
+MSG describes PROC's status."
+ (defvar eshell-destroy-buffer-when-process-dies) ; In `em-term'.
+ (when eshell-destroy-buffer-when-process-dies
+ (let ((proc-buf (process-buffer proc)))
+ (when (and proc-buf (buffer-live-p proc-buf)
+ (not (eq 'run (process-status proc)))
+ (= (process-exit-status proc) 0))
+ (if (eq (current-buffer) proc-buf)
+ (when-let ((buf (and (boundp 'eshell-parent-buffer)
+ (buffer-live-p eshell-parent-buffer)
+ eshell-parent-buffer)))
+ (switch-to-buffer buf)))
+ (kill-buffer proc-buf)))))
+
+(defun eat--eshell-exec-visual (&rest args)
+ "Run the specified PROGRAM in a terminal emulation buffer.
+
+ARGS are passed to the program. At the moment, no piping of input is
+allowed."
+ (declare-function eshell-find-interpreter "esh-ext"
+ (file args &optional no-examine-p))
+ (declare-function eshell-stringify-list "esh-util" (args))
+ (defvar eshell-interpreter-alist) ; In `esh-ext'.
+ (require 'esh-ext)
+ (require 'esh-util)
+ (let* (eshell-interpreter-alist
+ (interp (eshell-find-interpreter (car args) (cdr args)))
+ (program (car interp))
+ (args (flatten-tree
+ (eshell-stringify-list (append (cdr interp)
+ (cdr args)))))
+ (eat-buf
+ (generate-new-buffer
+ (concat "*" (file-name-nondirectory program) "*")))
+ (eshell-buf (current-buffer)))
+ (with-current-buffer eat-buf
+ (switch-to-buffer eat-buf)
+ (eat-mode)
+ (setq-local eshell-parent-buffer eshell-buf)
+ (setq-local eat-kill-buffer-on-exit nil)
+ (eat-exec eat-buf program program nil args)
+ (let ((proc (get-buffer-process eat-buf)))
+ (if (and proc (eq 'run (process-status proc)))
+ (let ((sentinel (process-sentinel proc)))
+ (add-function :after (var sentinel)
+ #'eat--eshell-visual-sentinel)
+ (set-process-sentinel proc sentinel))
+ (error "Failed to invoke visual command")))
+ (eat-semi-char-mode)))
+ nil)
+
+;;;###autoload
+(define-minor-mode eat-eshell-visual-command-mode
+ "Toggle running Eshell visual commands with Eat."
+ :group 'eat-eshell
+ :global t
+ (declare-function eshell-exec-visual "em-term" (&rest args))
+ (if eat-eshell-visual-command-mode
+ (advice-add #'eshell-exec-visual :override
+ #'eat--eshell-exec-visual)
+ (advice-remove #'eshell-exec-visual #'eat--eshell-exec-visual)))
+
+
+;;;; Project Integration.
+
+;;;###autoload
+(defun eat-project (&optional arg)
+ "Start Eat in the current project's root directory.
+
+Start a new Eat session, or switch to an already active session.
+Return the buffer selected (or created).
+
+With a non-numeric prefix ARG, create a new session.
+
+With a numeric prefix ARG (like \\[universal-argument] 42 \\[eshell]),
+switch to the session with that number, or create it if it doesn't
+already exist."
+ (interactive "P")
+ (declare-function project-root "project" (project))
+ (declare-function project-prefixed-buffer-name "project" (mode))
+ (require 'project)
+ (let* ((default-directory (project-root (project-current t)))
+ (eat-buffer-name (project-prefixed-buffer-name "eat")))
+ (eat nil arg)))
+
+
+;;;; Tracing.
+
+;;;;; Recording Trace Data.
+
+(defconst eat--trace-recorded-variables
+ '(eat-term-scrollback-size
+ eat-enable-alternative-display)
+ "The variable to record in trace output.")
+
+(defvar eat--trace-output-buffer nil
+ "Buffer where the trace data is written to.")
+
+(defun eat--trace-log (time operation &rest args)
+ "Log TIME, OPERATION and ARGS into trace output.
+
+TIME defaults to the current time.
+
+The current buffer should be the trace output buffer. Move the point
+to the end of (accessible portion of) buffer."
+ (goto-char (point-max))
+ ;; Hope that `float-time' won't roll over while tracing. ;-)
+ (insert (replace-regexp-in-string
+ "\n" "\\\\n"
+ (format "%S" `(,(float-time time) ,operation ,@args)))
+ ?\n))
+
+(defun eat--trace-stop ()
+ "Stop tracing the terminal in current buffer."
+ (when eat--trace-output-buffer
+ (with-current-buffer eat--trace-output-buffer
+ (eat--trace-log nil 'finish)))
+ (remove-hook 'kill-buffer-hook #'eat--trace-stop t)
+ (kill-local-variable 'eat--trace-output-buffer))
+
+(defun eat--trace-exec (fn buffer name command startfile switches)
+ "Trace `eat-exec'.
+
+BUFFER is the buffer and COMMAND and SWITCHES are the invocation
+command. BUFFER, NAME, COMMAND, STARTFILE and SWITCHES are passed to
+FN, `eat-exec', which see."
+ (let ((time (current-time)))
+ (prog1
+ (funcall fn buffer name command startfile switches)
+ (let ((buf (generate-new-buffer
+ (format "*eat-trace %s*: %s"
+ (buffer-name buffer)
+ (mapconcat #'shell-quote-argument
+ (cons command switches) " "))))
+ (width nil)
+ (height nil)
+ (variables nil))
+ (with-current-buffer buffer
+ (setq-local eat--trace-output-buffer buf)
+ (add-hook 'kill-buffer-hook #'eat--trace-stop nil t)
+ (let ((size (eat-term-size eat--terminal)))
+ (setq width (car size))
+ (setq height (cdr size)))
+ (dolist (var eat--trace-recorded-variables)
+ (push (cons var (symbol-value var)) variables)))
+ (with-current-buffer buf
+ (lisp-data-mode)
+ (insert ";; -*- lisp-data -*-\n")
+ (eat--trace-log time 'create 'eat width height
+ variables))))))
+
+(defun eat--trace-process-output-queue (fn buffer)
+ "Trace `eat--process-output-queue'.
+
+BUFFER is passed to FN, `eat--process-output-queue', which see."
+ (if (or (not (buffer-live-p buffer))
+ (not (buffer-local-value 'eat--trace-output-buffer buffer)))
+ (funcall fn buffer)
+ (cl-letf* ((eat-term-process-output
+ (symbol-function #'eat-term-process-output))
+ ((symbol-function #'eat-term-process-output)
+ (lambda (terminal output)
+ (when (buffer-live-p eat--trace-output-buffer)
+ (with-current-buffer eat--trace-output-buffer
+ (eat--trace-log nil 'output output)))
+ (funcall eat-term-process-output terminal output)))
+ (eat-term-redisplay
+ (symbol-function #'eat-term-redisplay))
+ ((symbol-function #'eat-term-redisplay)
+ (lambda (terminal)
+ (when (buffer-live-p eat--trace-output-buffer)
+ (with-current-buffer eat--trace-output-buffer
+ (eat--trace-log nil 'redisplay)))
+ (funcall eat-term-redisplay terminal))))
+ (funcall fn buffer))))
+
+(defun eat--trace-adjust-process-window-size (fn process windows)
+ "Trace `eat--adjust-process-window-size'.
+
+PROCESS and WINDOWS are passed to FN,
+`eat--adjust-process-window-size', which see."
+ (cl-letf*
+ ((eat-term-resize (symbol-function #'eat-term-resize))
+ ((symbol-function #'eat-term-resize)
+ (lambda (terminal width height)
+ (when (buffer-live-p eat--trace-output-buffer)
+ (with-current-buffer eat--trace-output-buffer
+ (eat--trace-log nil 'resize width height)))
+ (funcall eat-term-resize terminal width height)))
+ (eat-term-redisplay (symbol-function #'eat-term-redisplay))
+ ((symbol-function #'eat-term-redisplay)
+ (lambda (terminal)
+ (when (buffer-live-p eat--trace-output-buffer)
+ (with-current-buffer eat--trace-output-buffer
+ (eat--trace-log nil 'redisplay)))
+ (funcall eat-term-redisplay terminal))))
+ (funcall fn process windows)))
+
+(defun eat--trace-sentinel (fn &rest args)
+ "Trace `eat--sentinel'.
+
+Elements of ARGS are passed to FN, `eat--sentinel', which see."
+ (cl-letf* ((eat-term-delete (symbol-function #'eat-term-delete))
+ ((symbol-function #'eat-term-delete)
+ (lambda (terminal)
+ (when (buffer-live-p eat--trace-output-buffer)
+ (eat--trace-stop))
+ (funcall eat-term-delete terminal))))
+ (apply fn args)))
+
+(defun eat--trace-reset (fn)
+ "Trace `eat-reset'.
+
+FN is original definition of `eat-reset'."
+ (cl-letf*
+ ((eat-term-reset (symbol-function #'eat-term-reset))
+ ((symbol-function #'eat-term-reset)
+ (lambda (terminal)
+ (when (buffer-live-p eat--trace-output-buffer)
+ (with-current-buffer eat--trace-output-buffer
+ (eat--trace-log nil 'reset)))
+ (funcall eat-term-reset terminal)))
+ (eat-term-redisplay (symbol-function #'eat-term-redisplay))
+ ((symbol-function #'eat-term-redisplay)
+ (lambda (terminal)
+ (when (buffer-live-p eat--trace-output-buffer)
+ (with-current-buffer eat--trace-output-buffer
+ (eat--trace-log nil 'redisplay)))
+ (funcall eat-term-redisplay terminal))))
+ (funcall fn)))
+
+(defun eat--eshell-trace-adjust-make-process-args (fn &rest args)
+ "Trace `eat--eshell-adjust-make-process-args'.
+
+ARGS is passed to FN, `eat--eshell-adjust-make-process-args', which
+see."
+ (cl-letf*
+ ((command nil)
+ (make-process (symbol-function #'make-process))
+ ((symbol-function #'make-process)
+ (lambda (&rest plist)
+ (prog1
+ (apply make-process plist)
+ (setq command (nthcdr 5 (plist-get plist :command))))))
+ (eat--eshell-setup-proc-and-term
+ (symbol-function #'eat--eshell-setup-proc-and-term))
+ ((symbol-function #'eat--eshell-setup-proc-and-term)
+ (lambda (proc)
+ (let ((time (current-time)))
+ (prog1
+ (funcall eat--eshell-setup-proc-and-term proc)
+ (when (eq eat--process proc)
+ (let ((buf (generate-new-buffer
+ (format "*eat-trace %s*: %s"
+ (buffer-name)
+ (mapconcat
+ #'shell-quote-argument
+ command " "))))
+ (width nil)
+ (height nil)
+ (variables nil))
+ (setq-local eat--trace-output-buffer buf)
+ (add-hook 'kill-buffer-hook #'eat--trace-stop nil t)
+ (let ((size (eat-term-size eat--terminal)))
+ (setq width (car size))
+ (setq height (cdr size)))
+ (dolist (var eat--trace-recorded-variables)
+ (push (cons var (symbol-value var)) variables))
+ (with-current-buffer buf
+ (lisp-data-mode)
+ (insert ";; -*- lisp-data -*-\n")
+ (eat--trace-log time 'create 'eshell width height
+ variables)))))))))
+ (apply fn args)))
+
+(defun eat--eshell-trace-output-filter (fn)
+ "Trace `eat--eshell-output-filter'.
+
+FN is the original definition of `eat--eshell-output-filter', which
+see."
+ (if (not (buffer-live-p eat--trace-output-buffer))
+ (funcall fn)
+ (cl-letf* ((eat-term-process-output
+ (symbol-function #'eat-term-process-output))
+ ((symbol-function #'eat-term-process-output)
+ (lambda (terminal output)
+ (with-current-buffer eat--trace-output-buffer
+ (eat--trace-log nil 'output output))
+ (funcall eat-term-process-output terminal output)))
+ (eat-term-redisplay
+ (symbol-function #'eat-term-redisplay))
+ ((symbol-function #'eat-term-redisplay)
+ (lambda (terminal)
+ (with-current-buffer eat--trace-output-buffer
+ (eat--trace-log nil 'redisplay))
+ (funcall eat-term-redisplay terminal))))
+ (funcall fn))))
+
+(defun eat--eshell-trace-cleanup (fn)
+ "Trace `eat--eshell-cleanup'.
+
+FN is the original definition of `eat--eshell-cleanup', which see."
+ (if (not (buffer-live-p eat--trace-output-buffer))
+ (funcall fn)
+ (cl-letf* ((eat-term-delete (symbol-function #'eat-term-delete))
+ ((symbol-function #'eat-term-delete)
+ (lambda (terminal)
+ (eat--trace-stop)
+ (funcall eat-term-delete terminal))))
+ (funcall fn))))
+
+(define-minor-mode eat-trace-mode
+ "Toggle tracing Eat terminal."
+ :global t
+ :require 'eat
+ :lighter " Eat-Trace"
+ (if eat-trace-mode
+ (progn
+ (advice-add #'eat-exec :around #'eat--trace-exec)
+ (advice-add #'eat--process-output-queue :around
+ #'eat--trace-process-output-queue)
+ (advice-add #'eat--adjust-process-window-size :around
+ #'eat--trace-adjust-process-window-size)
+ (advice-add #'eat--sentinel :around #'eat--trace-sentinel)
+ (advice-add #'eat-reset :around #'eat--trace-reset)
+ (advice-add #'eat--eshell-adjust-make-process-args :around
+ #'eat--eshell-trace-adjust-make-process-args)
+ (advice-add #'eat--eshell-output-filter :around
+ #'eat--eshell-trace-output-filter)
+ (advice-add #'eat--eshell-cleanup :around
+ #'eat--eshell-trace-cleanup))
+ (advice-remove #'eat-exec #'eat--trace-exec)
+ (advice-remove #'eat--process-output-queue
+ #'eat--trace-process-output-queue)
+ (advice-remove #'eat--adjust-process-window-size
+ #'eat--trace-adjust-process-window-size)
+ (advice-remove #'eat--sentinel #'eat--trace-sentinel)
+ (advice-remove #'eat-reset #'eat--trace-reset)
+ (advice-remove #'eat--eshell-adjust-make-process-args
+ #'eat--eshell-trace-adjust-make-process-args)
+ (advice-remove #'eat--eshell-output-filter
+ #'eat--eshell-trace-output-filter)
+ (advice-remove #'eat--eshell-cleanup
+ #'eat--eshell-trace-cleanup)
+ (dolist (buffer (buffer-list))
+ (when (buffer-local-value 'eat--trace-output-buffer buffer)
+ (with-current-buffer buffer
+ (setq-local eat--trace-output-buffer nil))))))
+
+
+;;;;; Trace Data Replay.
+
+(defvar eat--trace-replay-buffer nil
+ "The buffer replaying the trace data in current buffer.")
+
+(defvar eat--trace-replay-marker nil
+ "The point from where to read the next sexp.")
+
+(defvar eat--trace-replay-current-sexp-overlay nil
+ "Overlay indicating the current sexp.")
+
+(defvar eat--trace-replay-source-buffer nil
+ "The source buffer containing the trace output.")
+
+(defvar eat--trace-replay-recording-start-time 0.0
+ "Time when recording was started.")
+
+(defvar eat--trace-replay-frame-count 0
+ "The number of the frames in the trace output.")
+
+(defvar eat--trace-replay-progress-frame 0
+ "The number of the frames before the current position.")
+
+(defvar eat--trace-replay-progress nil
+ "The number of seconds of trace output was shown.")
+
+(defun eat--trace-replay-eval (data)
+ "Evalulate DATA as trace output."
+ (let ((inhibit-read-only t))
+ (setq eat--trace-replay-progress
+ (- (car data) eat--trace-replay-recording-start-time))
+ (pcase data
+ (`(,time create ,_ui ,width ,height ,variables)
+ (setq eat--trace-replay-recording-start-time time)
+ (setq eat--trace-replay-progress 0)
+ (dolist (var eat--trace-recorded-variables)
+ (set (make-local-variable var) (alist-get var variables)))
+ (setq eat--terminal (eat-term-make (current-buffer) (point)))
+ (setf (eat-term-set-cursor-function eat--terminal)
+ #'eat--set-cursor)
+ (setf (eat-term-ring-bell-function eat--terminal) #'eat--bell)
+ (eat-term-resize eat--terminal width height)
+ (eat-term-redisplay eat--terminal))
+ (`(,_time output ,string)
+ (eat-term-process-output eat--terminal string))
+ (`(,_time redisplay)
+ (eat-term-redisplay eat--terminal))
+ (`(,_time resize ,width ,height)
+ (eat-term-resize eat--terminal width height))
+ (`(,_time reset)
+ (eat-term-reset eat--terminal))
+ (`(,_time finish)
+ (eat-term-delete eat--terminal)))
+ (eat--synchronize-scroll)))
+
+(defun eat--trace-replay-eval-next ()
+ "Evaluate next sexp in trace output."
+ (with-current-buffer eat--trace-replay-source-buffer
+ (goto-char eat--trace-replay-marker)
+ (ignore-error end-of-file
+ (let ((data (read (current-buffer))))
+ (set-marker eat--trace-replay-marker (point))
+ (backward-list)
+ (move-overlay eat--trace-replay-current-sexp-overlay
+ (point) (point))
+ (when-let ((window (get-buffer-window)))
+ (set-window-point window (point)))
+ (with-current-buffer eat--trace-replay-buffer
+ (cl-incf eat--trace-replay-progress-frame)
+ (eat--trace-replay-eval data))))))
+
+(defun eat-trace-replay ()
+ "Replay terminal according to trace output in current buffer."
+ (interactive)
+ (unless (buffer-live-p eat--trace-replay-buffer)
+ (setq-local eat--trace-replay-buffer
+ (generate-new-buffer
+ (format "*eat-trace-replay*: %s" (buffer-name))))
+ (setq-local eat--trace-replay-marker (point-min-marker))
+ (let ((ov (make-overlay (point-min) (point-min))))
+ (overlay-put ov 'before-string
+ (propertize " " 'display
+ '(left-fringe right-triangle)))
+ (setq-local eat--trace-replay-current-sexp-overlay ov))
+ (goto-char (point-min))
+ (let ((source (current-buffer))
+ (frame-count 0))
+ (ignore-error end-of-file
+ (while (read (current-buffer))
+ (cl-incf frame-count)))
+ (goto-char (point-min))
+ (with-current-buffer eat--trace-replay-buffer
+ (eat-trace-replay-mode)
+ (setq eat--trace-replay-source-buffer source)
+ (setq eat--trace-replay-frame-count frame-count))))
+ (display-buffer eat--trace-replay-buffer))
+
+(defun eat-trace-replay-next-frame (&optional n)
+ "Show the Nth next frame.
+
+N defaults to 1. Interactively, N is the prefix argument."
+ (interactive "p")
+ (dotimes (_ n)
+ (eat--trace-replay-eval-next)))
+
+(defun eat-trace--cleanup ()
+ "Clean up the source buffer before the terminal being killed."
+ (when (buffer-live-p eat--trace-replay-source-buffer)
+ (with-current-buffer eat--trace-replay-source-buffer
+ (setq eat--trace-replay-buffer nil)
+ (setq eat--trace-replay-marker nil)
+ (delete-overlay eat--trace-replay-current-sexp-overlay))))
+
+(defvar eat-trace-replay-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "n") #'eat-trace-replay-next-frame)
+ (define-key map (kbd "<down>") #'eat-trace-replay-next-frame)
+ (define-key map (kbd "q") #'quit-window)
+ map)
+ "Keymap for Eat-Trace-Replay mode.")
+
+(define-derived-mode eat-trace-replay-mode special-mode
+ "Eat-Trace-Replay"
+ "Major mode for replaying terminal according to trace output."
+ (make-local-variable 'eat--terminal)
+ (make-local-variable 'eat--trace-replay-source-buffer)
+ (make-local-variable 'eat--trace-replay-recording-start-time)
+ (make-local-variable 'eat--trace-replay-progress)
+ (make-local-variable 'eat--trace-replay-frame-count)
+ (make-local-variable 'eat--trace-replay-progress-frame)
+ (setq-local
+ mode-line-process
+ '("[" (:eval (number-to-string eat--trace-replay-progress-frame))
+ "/" (:eval (number-to-string eat--trace-replay-frame-count))
+ "]"))
+ (add-hook 'kill-buffer-hook #'eat-trace--cleanup nil t))
+
+(provide 'eat)
+;;; eat.el ends here
diff --git a/eat.texi b/eat.texi
new file mode 100644
index 0000000000..aa5e128e8f
--- /dev/null
+++ b/eat.texi
@@ -0,0 +1,1001 @@
+\input texinfo
+
+@comment %**start of header
+@setfilename eat.info
+@set UPDATED 17 November 2022
+@set EDITION 0.1snapshot
+@set VERSION 0.1snapshot
+@documentencoding UTF-8
+@codequotebacktick on
+@codequoteundirected on
+@syncodeindex fn cp
+@syncodeindex vr cp
+@syncodeindex ky cp
+@settitle Eat User Manual, version @value{VERSION}
+@comment %**end of header
+
+@copying
+This manual is for Eat (version @value{VERSION}, @value{UPDATED}), a
+terminal emulator for Emacs.
+
+Copyright @copyright{} 2022 Akib Azmain Turja.
+
+@quotation
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.3 or
+any later version published by the Free Software Foundation; with no
+Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A
+copy of the license is included in the section entitled ``GNU Free
+Documentation License''.
+@end quotation
+@end copying
+
+@dircategory Emacs
+@direntry
+* Eat: (eat). Emulate A Terminal.
+@end direntry
+
+@titlepage
+@title Eat User Manual
+@subtitle For version @value{VERSION}
+@author Akib Azmain Turja
+@page
+@vskip 0pt plus 1filll
+@insertcopying
+@end titlepage
+
+@contents
+
+@ifnottex
+@node Top
+@top Eat Manual
+@insertcopying
+@end ifnottex
+
+@menu
+Introduction
+* Intro:: What is Eat?
+* Hello Terminal:: Starting Eat's terminal for the first time.
+* Project-local Terminal:: One project, one terminal.
+* Eshell Terminal:: Eat's terminal emulation in Eshell.
+
+Basic Operations
+* Keyboard:: Most keyboard keys are captured by Eat.
+* Mouse:: Eat supports mouse.
+* Input Modes:: Input modes for various use-cases.
+
+Advanced Customizations
+* Display:: The region where everything is shown.
+* Scrollback:: Region for things that went out of display.
+* Cursor Types:: Cursor can displayed in many forms.
+* Mouse Tracking:: Eat tracks mouse, but this can be changed.
+* Clipboard:: Integrating kill ring with terminal.
+* Colors:: Eat can show more than sixteen million colors.
+* Fonts:: Eat can show up to sixty font different fonts.
+* Blinking Text:: Annoying blinking texts.
+* Performance Tuning:: Fine tuning to maximize performance.
+
+Recovering from Problems
+* Common Problems:: Common problem, and fixes.
+* Reporting Bugs:: How to report problems?
+* Tracing the Terminal:: Gathering some crucial information to
+ reproduce a bug.
+
+Appendices
+* GNU General Public License:: Copying condition for Eat.
+* GNU Free Documentation License:: Copying conditions of this manual.
+* Index:: A list of various things.
+@end menu
+
+@part Part I:@* Introduction
+
+@node Intro
+@cindex introduction
+@chapter Introduction
+
+@abbr{Eat, Emulate A Terminal} is a terminal emulator for Emacs. It
+emulates a XTerm-like terminal, just like many other terminal
+emulators. But it has some key features that make Eat distinct from
+other terminal emulators.
+
+Firstly, it's in Emacs, which means you don't need to leave the
+comfort of Emacs to use Emacs.
+
+Secondly, it's easy and convenient to use. It is tries to stay out of
+your way, allow you to maximize your productivity.
+
+Finally, special care has been taken while designing the keybindings,
+so that the terminal doesn't conflict with Emacs default keybindings
+on both graphical display and text display, while still allowing you
+to run full screen programs like Emacs within the terminal.
+
+@node Hello Terminal
+@cindex hello terminal
+@cindex terminal, hello
+@chapter Hello Terminal
+
+@findex eat
+The terminal can be started with @kbd{M-x eat}. It would prompt you
+for the program to run in the terminal, with a reasonable default
+value already filled in. You can specify a program path, or a shell
+command. After you hit @kbd{@key{RET}}, you will see a terminal, and
+the program you chose will be run within the terminal in the current
+directory. You can interact with it. If you started a shell, you
+should get a shell prompt and you should be able to write commands and
+execute them. Full screen programs like @samp{htop}, @samp{lynx} and
+Emacs will work inside it, just like other any other terminal.
+
+If an Eat terminal already exists, @kbd{M-x eat} will switch to it.
+To create a new terminal, call it with a prefix argument like this,
+@kbd{C-u M-x eat}.
+
+If you give it a numeric argument N, for example @kbd{C-u 42 M-x eat},
+it'll switch to a terminal in @file{*eat*<N>}, @file{*eat*<42>} for
+example, and it'll create a new terminal if that doesn't exist.
+
+@node Project-local Terminal
+@cindex project-local terminal
+@cindex terminal, project-local
+@cindex project's terminal
+@cindex project terminal
+@cindex terminal, project
+@chapter Project-local Terminal
+
+@findex eat-project
+Usually, you don't use a single terminal for everything, instead you
+open a terminal for each project that needs it. So there is command
+named @command{eat-project}. It opens a new terminal in project root
+directory, or switches to a already existing project terminal. It too
+accepts prefix argument, just like the ordinary @command{eat} command.
+
+@node Eshell Terminal
+@cindex eshell terminal
+@cindex terminal, eshell
+@cindex eshell terminal emulation
+@cindex terminal emulation, eshell
+@cindex eat, eshell
+@cindex eshell, eat
+@cindex eshell
+@chapter Eshell Terminal Emulation
+
+Eat also supports terminal emulation outside Eat's terminal. So you
+can emulate terminal in Eshell (@pxref{Top,,, eshell, Eshell manual})
+with Eat. After configuring Eshell to use Eat for terminal emulation,
+you can run any full screen terminal program in Eshell.
+
+@findex eat-eshell-mode
+To enable terminal emulation in Eshell, enable the global minor mode
+@command{eat-eshell-mode}. It will enable Eat's terminal emulation in
+Eshell. To disable the terminal emulation, disable the minor mode.
+
+You can't toggle the global minor mode while any Eshell command is
+running, so terminate any Eshell command or wait them to finish before
+toggling the mode.
+
+Unless stated otherwise, everything described in this manual about
+Eat terminal also applies to Eshell terminal emulation.
+
+@part Part II:@* Basic Operations
+
+@node Keyboard
+@cindex keyboard
+@chapter Keyboard
+
+Just like any other text terminal, the primary interaction device with
+Eat terminal is the keyboard. Eat forwards all supported keyboard
+events like @kbd{a}, @kbd{E}, @kbd{RET}, @kbd{C-a} to the terminal.
+
+However, this conflict with Emacs keybinding conventions, and makes it
+almost impossible to call any other Emacs command. So, by default,
+Eat doesn't intercept the key sequences beginning with the following
+keys and lets Emacs to handle them: @kbd{C-\}, @kbd{C-c}, @kbd{C-x},
+@kbd{C-g}, @kbd{C-h}, @kbd{C-M-c}, @kbd{C-u}, @kbd{C-q}, @kbd{M-x},
+@kbd{M-:}, @kbd{M-!} and @kbd{M-&}.
+
+To input the above key sequences, prefix them with @kbd{C-q}.
+@kbd{C-q} reads the next event and sends to directly to the terminal.
+For example, to input @kbd{M-:}, use the key sequence @kbd{C-q M-:}.
+
+For an alternative way to input these exceptional characters,
+@pxref{Char Mode}.
+
+@node Mouse
+@cindex mouse
+@chapter Mouse
+
+Eat supports mouse tracking. That means in programs like Emacs,
+@samp{htop}, etc, that support mouse, you can hover and click on
+text and buttons. You can also use your mouse wheel to scroll text,
+if the program supports it.
+
+@xref{Mouse Tracking} to configure mouse tracking.
+
+@node Input Modes
+@cindex input modes
+@cindex modes, input
+@cindex keybinding modes
+@cindex modes, keybinding
+@chapter Input Modes
+
+By default, Eat forwards all supported keys to terminals, except some
+exceptions. It is possible to input them with @kbd{C-q}, but it is
+not very convenient.
+
+To conveniently input those character, they should be bound to input
+themselves to the terminal (i.e. pressing @kbd{M-x} will input
+@kbd{M-x}, bypassing Emacs). But this is conflicts with Emacs's
+default keybindings, so this can't done, at least by default.
+
+To overcome the problem, Eat implements several ``input modes''. Each
+input mode has a different set of keybindings for different
+applications.
+
+@anchor{Semi-char Mode}
+@cindex semi-char mode
+@cindex mode, semi-char
+@cindex keybindings, semi-char mode
+@cindex keybinding mode, semi-char
+@cindex input mode, semi-char
+@section Semi-char Mode
+
+``Semi-char mode'' is the default input mode of Eat. This works for
+most inputs. It forwards all keys, except @kbd{C-\}, @kbd{C-c},
+@kbd{C-x}, @kbd{C-g}, @kbd{C-h}, @kbd{C-M-c}, @kbd{C-u}, @kbd{M-x},
+@kbd{C-q}, @kbd{M-:}, @kbd{M-!} and @kbd{M-&}, Emacs handle them.
+
+@cindex inputting exceptional characters
+@kindex C-q @r{(``semi-char mode'')}
+To input these exceptions, there is a key @kbd{C-q}. This reads the
+next input event and sends that as the input. For example, the key
+sequences @kbd{C-q M-:} inputs @kbd{M-:}, @kbd{C-q C-g} inputs
+@kbd{C-g}.
+
+Input methods (@pxref{Input Methods,,, emacs, GNU Emacs Manual}) work
+in this mode, so, unlike Term (@pxref{Terminal emulator, Emacs
+Terminal Emulator, Emacs Terminal Emulator, emacs, GNU Emacs Manual}),
+Emacs built-in terminal emulator, you can still input any character.
+
+@kindex C-c C-c @r{(``semi-char mode'')}
+@kindex C-c C-k @r{(``semi-char mode'')}
+In ``semi-char mode'', @kbd{C-c C-c} sends a @kbd{C-c}, just for
+convenience, and @kbd{C-c C-k} kills the terminal program.
+
+@anchor{Char Mode}
+@cindex char mode
+@cindex mode, char
+@cindex keybindings, char mode
+@cindex keybinding mode, char
+@cindex input mode, char
+@section Char Mode
+
+By default, Eat is in ``semi-char mode''. In this input mode, Eat
+forwards all supported keys to terminals, except some exceptions,
+@pxref{Semi-char Mode}. It is possible to input them with @kbd{C-q},
+but it is not very convenient.
+
+@kindex C-c M-d @r{(``semi-char mode'')}
+To overcome this problem, Eat implements another input mode called
+``char mode''. To switch to ``char mode'', press @kbd{C-c M-d} in
+``semi-char mode''. In Eshell, the command
+@command{eshell-toggle-direct-send} is remapped to enable
+``char-mode'', which is usually bound to @kbd{C-c M-d}.
+
+In this input mode, Eat forwards all supported keys. However, input
+methods still work in this mode, so you can still input keys that are
+not on your keyboard.
+
+@kindex C-M-m @r{(``char mode'')}
+@kindex M-RET @r{(``char mode'')}
+To get out of ``char mode'', press @kbd{C-M-m} or @kbd{M-@key{RET}},
+this switches back to ``semi-char mode''.
+
+@anchor{Emacs Mode}
+@cindex emacs mode
+@cindex mode, emacs
+@cindex keybindings, emacs mode
+@cindex keybinding mode, emacs
+@cindex input mode, emacs
+@section Emacs Mode
+
+In ``emacs mode'', no input events are send to the terminal. In this
+mode, you can interact with the terminal buffer just like a regular
+buffer. However, you are not allowed to change the buffer contents.
+
+@kindex C-c C-e @r{(``semi-char mode'')}
+To switch to ``emacs mode'', press @kbd{C-c C-e} from ``semi-char
+mode''.
+
+@kindex C-c C-k @r{(``emacs mode'')}
+In this mode, @kbd{C-c C-k} kills the terminal program like in
+``semi-char mode''.
+
+@kindex C-c C-j @r{(``emacs mode'')}
+@kindex C-c M-d @r{(``emacs mode'')}
+From ``emacs mode'', you can switch to ``semi-char mode'' with
+@kbd{C-c C-j} and to ``char mode'' with @kbd{C-c M-d}. In Eshell, the
+command @command{eshell-toggle-direct-send} is remapped to enable
+``char-mode'', which is usually bound to @kbd{C-c M-d}.
+
+@part Part III:@* Advanced Customizations
+
+@node Display
+@cindex display
+@chapter Display
+
+Display is the region you see on the terminal. The program writes to
+the display and manipulates the text on the display. The display can
+be of any size. The cursor is always on the display (though it might
+be invisible sometimes, @pxref{Cursor Types}).
+
+@vindex window-adjust-process-window-size-function
+You can resize the display by resizing the terminal window. The
+display size is controlled by the Emacs user option
+@code{window-adjust-process-window-size-function}. @xref{Process
+Buffers,,, elisp, GNU Emacs Lisp Reference Manual} for the possible
+values of the user option.
+
+@node Scrollback
+@cindex scrollback
+@chapter Scrollback
+
+When you go too downward on the terminal, the terminal starts to
+``scroll''. This causes the line at the upper side of the terminal to
+go out of the display and become hidden. But these line are not
+deleted, they are just put in the scrollback region.
+
+Scrollback region is a region just above the display of the terminal.
+This contains the lines that went out of display due to scrolling up.
+
+Scrollback region is not unlimited by default, to avoid using too much
+memory. You can change the limit, or remove it altogether.
+
+@vindex eat-term-scrollback-size
+@defopt eat-term-scrollback-size
+This controls the size of scrollback region. It is expressed in
+character. If set to @var{size}, Eat won't store more than @var{size}
+characters in the scrollback region. If set to @code{nil}, the
+scrollback region is unlimited.
+@end defopt
+
+@node Cursor Types
+@cindex cursor types
+@cindex types, cursor
+@cindex changing cursor
+@cindex customizing cursor
+@cindex cursor blinking
+@cindex blinking cursor
+@chapter Cursor Types
+
+In terminal, cursor can be of up to three type: ``visible'',
+``invisible'' and ``very visible''. ``Visible'' is the default cursor
+type, which is the cursor you usually see in a shell (unless the shell
+changes the cursor type). ``Invisible'' is, as the name suggests,
+invisible, you can't see it. ``Very visible'' cursor is a blinking
+cursor, programs use this to help you not lose the cursor.
+
+The cursor type can customized with three user options for the three
+types of cursor. Each of the user options share the same format.
+
+@vindex eat-default-cursor-type
+@defopt eat-default-cursor-type
+This control the cursor shape of ``visible'' cursor type.
+@end defopt
+
+@vindex eat-invisible-cursor-type
+@defopt eat-invisible-cursor-type
+This control the cursor shape of ``invisible'' cursor type.
+@end defopt
+
+@vindex eat-very-visible-cursor-type
+@defopt eat-very-visible-cursor-type
+This control the cursor shape of ``very visible'' cursor type. This
+cursor blinks, switching between the default cursor shape and a hollow
+box.
+@end defopt
+
+The value type of these user options is a list. The list is of form
+(@var{cursor-on} @var{blinking-frequency} @var{cursor-off}).
+@var{blinking-frequency} is the frequency of blinking of cursor. It
+is a number, controlling how many times the cursor will blink a
+second. This can also be @code{nil}, this will disable cursor
+blinking. @var{cursor-on} is the default cursor shape, only this
+shape is shown on the display when blinking is disabled. This uses
+the same format as Emacs's @code{cursor-type} user option
+(@pxref{Cursor Display,,, emacs, GNU Emacs Manual}). When
+@var{blinking-frequency} is a number, Eat will consult to the third
+element of the list, @var{cursor-off}, whose format same as
+@var{cursor-on}. The blinking cursor switches between @var{cursor-on}
+and @var{cursor-off} cursor shape.
+
+@node Mouse Tracking
+@cindex mouse tracking
+@cindex tracking mouse
+@chapter Mouse Tracking
+
+Eat tracks mouse by default, when the program supports mouse. But
+sometimes, you may want to avoid using mouse, or you might not have a
+mouse at all. So mouse tracking can be toggled.
+
+@vindex eat-enable-mouse
+@defopt eat-enable-mouse
+This user option controls mouse tracking. When set to non-@code{nil},
+mouse tracking is enabled. Set to this to @code{nil} to disable mouse
+tracking. This is enabled by default.
+@end defopt
+
+@node Clipboard
+@cindex clipboard
+@chapter Clipboard
+
+@cindex yanking
+@findex eat-yank
+@kindex C-y @r{(``semi-char mode'')}
+@findex eat-yank-pop
+@kindex M-y @r{(``semi-char mode'')}
+Just like any other buffer, you can yank text in terminal with
+@kbd{C-y} (bound to @command{eat-yank}) or @kbd{M-y} (bound to
+@command{eat-yank-pop}) in ``semi-char mode''.
+
+@cindex clipboard integration
+Programs can also request to the terminal to kill (@pxref{Killing,,,
+emacs, GNU Emacs Manual}) something. It is up to Eat whether the
+request will be fulfilled or not. By default, Eat fulfills the
+request and kills the text. This can sometimes be annoying, when the
+program automatically kills text without user interaction. This
+killing can be configured with the following user option:
+
+@vindex eat-enable-kill-from-terminal
+@defopt eat-enable-kill-from-terminal
+This controls killing texts from terminal. When set to
+non-@code{nil}, killing something from terminal add the text to
+Emacs's kill ring (@pxref{Kill Ring,,, emacs, GNU Emacs Manual}).
+This is enabled by default.
+@end defopt
+
+Programs can also request the text in kill ring. Again, this is up to
+Eat whether the request will be fulfilled or not. You can customize
+the following user option to configure this:
+
+@vindex eat-enable-yank-to-terminal
+@defopt eat-enable-yank-to-terminal
+This controls sending kill ring texts to terminal. When set to
+non-@code{nil}, programs can receive the kill ring contents. This is
+disabled by default for security reasons.
+@end defopt
+
+@node Colors
+@cindex colors
+@cindex customizing colors
+@chapter Colors
+
+Eat can show more than 16 million colors (16,777,216 colors exactly).
+Eat has also a palette of 256 colors, which is more than enough for
+most applications. Programs usually use this color palette. Each of
+these 256 colors can customized.
+
+There are 256 faces for the 256 colors, one face for each color. They
+are named like @code{eat-term-color-@var{n}}, which corresponds to
+color @var{n}, and @var{n} can be any number between 0 and 255
+(inclusive). For example, color 42 is can be changed by customizing
+@code{eat-term-color-42}.
+
+The foreground attribute contains the color value to use for the
+corresponding color. Other attributes are currently ignored and
+reserved for future changes.
+
+@cindex color aliases
+@cindex face aliases
+@cindex aliases, face
+@cindex aliases, color
+Each of the first 16 colors, from @code{eat-term-color-0} to
+@code{eat-term-color-15} also have a alias. They are respectively
+@code{eat-term-color-black},
+@code{eat-term-color-red},
+@code{eat-term-color-green},
+@code{eat-term-color-yellow},
+@code{eat-term-color-blue},
+@code{eat-term-color-magenta},
+@code{eat-term-color-cyan},
+@code{eat-term-color-white},
+@code{eat-term-color-bright-black},
+@code{eat-term-color-bright-red},
+@code{eat-term-color-bright-green},
+@code{eat-term-color-bright-yellow},
+@code{eat-term-color-bright-blue},
+@code{eat-term-color-bright-magenta},
+@code{eat-term-color-bright-cyan}
+and @code{eat-term-color-bright-white}.
+
+Eat also supports 24-bit colors, or so called ``truecolor''. Programs
+like Emacs can give a RGB triplet to use as the color of some text.
+As the programs directly specify the color in this case, you can't
+customize these color. But you may configure the program sending the
+color codes.
+
+@cindex color advertisement
+@cindex advertising colors
+Eat doesn't always advertise color support depending on the display
+Eat is running. For example, if you are on a Linux console which
+supports only eight colors, Eat will advertise eight color support to
+the programs, while on graphical displays with 24-bit color support,
+Eat will report 24-bit color support. This is because Eat supports
+more colors, the display doesn't always support them.
+
+@vindex TERM
+@cindex @env{TERM} environment variable
+@cindex environment variable, @env{TERM}
+Eat does the trick by setting the @env{TERM} environment variable of
+the program. The value of @env{TERM} depends on the number of the
+available colors on the display. This environment variable is
+controlled by the following user option:
+
+@vindex eat-term-name
+@defopt eat-term-name
+The value of @env{TERM} environment variable as a string. The value
+can also be a function taking no arguments, that function should
+return a string which used as the value of @env{TERM}. The default
+value is @code{eat-term-get-suitable-term-name}, which is responsible
+for the behavior described above.
+@end defopt
+
+@node Fonts
+@cindex fonts
+@chapter Fonts
+
+Programs may request the terminal to change the text font. It can
+change text weight, use italic text, or even change the font family
+altogether.
+
+@cindex bold text
+@cindex text, bold
+@vindex eat-term-bold
+Programs may request the terminal to show some text bolder than
+normal. Bold text uses the face @code{eat-term-bold}.
+
+@cindex faint text
+@cindex text, faint
+@vindex eat-term-bold
+Programs may also request the terminal to show some text fainter than
+normal. Faint text uses the face @code{eat-term-faint}.
+
+@cindex italic text
+@cindex slant text
+@cindex text, italic
+@cindex text, slant
+@vindex eat-term-italic
+Programs may request the terminal to show italic text too. Italic
+text uses the customizable face @code{eat-term-faint}.
+
+@cindex font family
+@cindex text, font family
+The number of available fonts is ten. Most of the programs doesn't
+change the font. Following many other terminal emulator, Eat actually
+uses the same font, the default font, regardless of the font requested
+by the program, by default.
+
+@cindex customizing font families
+There are ten faces for ten fonts, one face for each. They are named
+like @code{eat-term-font-@var{n}}, which corresponds to color @var{n},
+and @var{n} can be any number between 0 and 9 (inclusive). For
+example, the font 6 is can be changed by customizing
+@code{eat-term-font-6}. Font 0 is the default font.
+
+@node Blinking Text
+@cindex blinking text
+@cindex text, blinking
+@chapter Blinking Text
+
+Programs can request the terminal to blink some text. This helps to
+get user attention. But however, often this annoying to many people
+and also has accessiblity problems. So this is disabled by default.
+
+@vindex eat-enable-blinking-text
+@defopt eat-enable-blinking-text
+This controls the blinking of text with blink attribute. When set to
+non-@code{nil}, Eat arranges that text with blink attribute will
+blink at a certain interval.
+@end defopt
+
+@findex eat-blink-mode
+You can toggle blinking temporarily by toggle the buffer-local minor
+mode @command{eat-blink-mode}. This is only effective in the buffer
+where the mode is toggled.
+
+By default, @code{eat-enable-blinking-text} is set to @code{nil}.
+This disables text blinking and causes the text with blink attribute
+to be displayed in inverse video (swapped foreground and background).
+
+@vindex eat-term-slow-blink
+@vindex eat-term-fast-blink
+Programs may also request to blink some text more rapidly that other
+blinking text. When blinking is disabled, the face
+@code{eat-term-slow-blink} is used for slowly blinking text, and
+@code{eat-term-fast-blink} for rapidly blinking text.
+
+When blinking is enabled, by setting @code{eat-enable-blinking-text}
+to non-@code{nil} value, the following user options can be customized
+to change the rate of blinking:
+
+@vindex eat-slow-blink-frequency
+@defopt eat-slow-blink-frequency
+The blinking rate of slowly blinking text. When set to a number N,
+it causes slowly blinking text to blink N times a second. The value
+can also be a floating point number. The default value is 2, meaning
+that the slowing text will blink two times a second.
+@end defopt
+
+@vindex eat-fast-blink-frequency
+@defopt eat-fast-blink-frequency
+The blinking rate of rapidly blinking text. When set to a number N,
+it causes rapidly blinking text to blink N times a second. The value
+can also be a floating point number as well. The default value is 3,
+meaning that the slowing text will blink three times a second.
+@end defopt
+
+@node Performance Tuning
+@cindex performance tuning
+@cindex tuning performance
+@chapter Performance Tuning
+
+Eat tries to be as fast as possible. So Eat employs some techniques
+to maximize performance.
+
+Some program choke and hang when given too much input at once. So Eat
+divides large input to smaller chunks and sends the chunks one at a
+time. The maximum size of a input chunk is controlled by
+@code{eat-input-chunk-size}.
+
+@vindex eat-input-chunk-size
+@defopt eat-input-chunk-size
+The value is a integer. Eat treat input larger than this many
+character as large and breaks it into chunks of at most this size
+before sending the input.
+@end defopt
+
+@cindex flickering
+@cindex reason behind flickering
+@cindex cause of flickering
+Programs also break large output into smaller chunks before sending
+it to the terminal, for same reason. Eat doesn't suffer from the
+problem, but there isn't any standard way to inform programs about
+this, and usually there are other obstructions sending large amount of
+data at once. These small chunks create another problem for Eat,
+flickering. When updating the whole display, the output is usually
+pretty large and the programs break them into smaller chunks. Each of
+the chunks update the display partially. After receiving the last
+chunk, the update is complete and the display can be updated. But it
+is impossible for Eat to guess the last chunk, so Eat has to redisplay
+or update the display after receiving each chunk. This is the reason
+why sometimes the terminal shows some old contents and some new. This
+only lasts for a fraction of a second until the next chunk is received
+and processed. This is flickering. This also degrades performance,
+because redisplay is an expensive process and takes some time.
+
+@cindex fixing flickering
+@cindex flickering fix
+@cindex latency
+Fixing the flickering completely is not possible. Eat tries to
+decrease flickering by deferring redisplay. After receiving a chunk,
+Eat waits for a tiny fraction of a second. If another chunk arrives
+within the time, the redisplay is postponed. Then Eat waits for the
+same amount of time and this goes on. When timeout occurs, Eat
+processing the output and displays the output. This causes a small
+latency between output arrive and redisplay, but this is usually not
+long enough for human eyes to catch it. This waiting time can be
+configured with the following user option:
+
+@vindex eat-minimum-latency
+@defopt eat-minimum-latency
+The value is the time in seconds to wait for the next chunk to arrive.
+This is the minimum latency between the first chunk after a redisplay
+and the next redisplay. For example, if you press @kbd{@key{RET}} in
+an empty Bash prompt, the next prompt won't appear before this much
+time.
+
+You should set the time to something comfortable for you. You can
+also set this to zero is disable waiting and showing the output
+instantly, but this would likely cause a lot of flickering.
+@end defopt
+
+However, this waiting raises another problem. What if you execute the
+POSIX command @samp{yes} in the terminal? It will write infinite
+``y''s in the terminal without any delay between them anywhere. Eat
+will wait indefinitely for a delay between two chunks, which will
+never happen, unless the program is executed remotely and the
+connection is slow enough. So Eat has a limit for waiting, the
+display will be always be updated after this time. This limit also
+customizable:
+
+@vindex eat-maximum-latency
+@defopt eat-maximum-latency
+The value is the time in seconds to wait at most for chunk. In case
+of large burst of output, redisplay is never deferred more than this
+many seconds, and cause a latency of up to this many seconds.
+
+You should set the time to something comfortable for you. You can
+also set this to zero is disable waiting and showing the output
+instantly, but this would likely cause a lot of flickering.
+@end defopt
+
+The user option described in this chapter have reasonable default
+values, but they may change anytime.
+
+@part Part IV:@* Recovering from Problems
+
+@node Common Problems
+@cindex common problems
+@cindex problem, common
+@chapter Common Problems
+
+This chapter describe how to recognize and handle situations in which
+Eat does something unexpected, such as hangs, garbled text, etc.
+
+@menu
+* Not Recognized:: The program can't recognize Eat.
+* Garbled Text:: When you get garbage on your terminal.
+* Not Responding:: What to do if Eat is unresponsive.
+* Signaled an Error:: The worst and the most unlikely bug.
+* Bugs in Manual:: What if there are problems in this manual?
+@end menu
+
+@node Not Recognized
+@cindex not recognized
+@cindex problem, not recognized
+@cindex terminal not recognized
+@cindex problem, terminal not recognized
+@section Terminal Not Recognized
+
+If your program says that it can't recognize the terminal, probably
+the @env{TERM} environment has a wrong value.
+
+Check the value of @env{TERM}, if it's not set to something like
+@samp{eat-...}, check the user option @code{eat-term-name}. If that's
+correct that your shell might be changing the @env{TERM} environment
+variable. If @code{eat-term-name} isn't correct, customize to a
+suitable value and try again, your problem should be fixed.
+
+If @env{TERM} has the correct value, then probably the Terminfo
+entries of Eat are missing. This can happen if you manually install
+Eat. Check that whether the values of the environment value
+@env{TERM} and the user option @code{eat-term-terminfo-directory}
+match. If they match, customize @code{eat-term-terminfo-directory} to
+the directory that contains the Terminfo database, the program should
+now recognize Eat. If they don't match, then your shell is probably
+responsible for the problem.
+
+@node Garbled Text
+@cindex garbled text
+@cindex problem, garbled text
+@cindex problem, text garbled
+@section Garbled Text on Terminal
+
+If the text on the terminal looks wrong, first check out the value of
+@env{TERM}. Usually @env{TERM} has a wrong value set, making programs
+send invalid escape sequences.
+
+First, @pxref{Not Recognized}; the problem is most likely because the
+problem doesn't recognize Eat, and it stays silent instead of
+reporting that.
+
+If the problem isn't resolved after following the instructions in the
+previous section, may be your program is blindly assuming that the
+terminal is XTerm-compatible. If so, what you are seeing is the
+current state of ``XTerm-compliance''. Though it's not really a bug,
+we definite want to know what's problem so that we can fix it.
+@xref{Reporting Bugs} for instructions on sending bug reports or
+feature request.
+
+The other potential reason is that Eat is not working. This is
+definitely a bug, so please report it.
+
+@node Not Responding
+@cindex not responding, eat
+@cindex not responding, emacs
+@cindex eat not responding
+@cindex emacs not responding
+@cindex problem, eat not responding
+@cindex problem, emacs not responding
+@cindex problem, not responding, eat
+@cindex problem, not responding, emacs
+@section Emacs or Eat Not Responding
+
+If you run something that outputs huge amount of data, your Emacs may
+not respond, and even quitting may not work. Quitting doesn't work
+while doing something terminal related (output processing, for
+example), and that's intentional, because quitting would mess up the
+terminal.
+
+The best way to fix it is to stop the program, so that Eat is not
+overloaded. To avoid the problem in future, it is recommended to run
+those programs in faster terminals like bare Eshell (i.e. without
+Eat-Eshell), Comint, or external terminal emulators.
+
+@node Signaled an Error
+@cindex signaled an error
+@cindex eat signaled an error
+@cindex problem, signaled an error
+@cindex problem, eat signaled an error
+@section Eat Signaled an Error
+
+The worst thing that happen is that Eat might signal an error. It is
+the worst thing possible, because it messes up the terminal, and also
+a security hole. Fortunately, this is very rare. If you ever find
+any such bug, you should report the bug (@pxref{Reporting Bugs}) as
+soon as possible with as much information as possible.
+
+@findex eat-reset
+Once the error signaled, your best option is to delete the terminal
+and start a new one. But if you don't want to delete the terminal,
+you can try invoking the command @command{reset} from your shell. If
+for some reason you can't do that, invoke the Emacs command
+@command{eat-reset}. This will reset most of the terminal state and
+give you a clean terminal to work with. However, it mayn't work if
+you're really unlucky, in that case deleting the terminal and starting
+a new one is your only option.
+
+@node Bugs in Manual
+@cindex bugs in manual
+@cindex typos in manual
+@cindex bugs, manual
+@cindex typos, manual
+@cindex manual, bugs
+@cindex manual, typos
+@cindex problems, bugs in manual
+@cindex problems, typos in manual
+@cindex problems, bugs, manual
+@cindex problems, typos, manual
+@cindex problems, manual, bugs
+@cindex problems, manual, typos
+@section Bugs in Manual
+
+Human makes mistake, and we are no exceptions. But we are trying hard
+to improve Eat and it's manual.
+
+If you don't understand something even after a careful rereading,
+that's a bug.
+
+If you find anything unclear in this manual, that's a bug.
+
+This manual's job is make everything clear, so failing to do that
+indicates a bug.
+
+If the built-in documentation and this manual don't agree, one of them
+must be wrong, and that's a bug.
+
+If you Eat doesn't behave as this manual describes, that's a bug.
+
+If you find any typing mistakes, that's a bug.
+
+If you find a bug, please report it. @xref{Reporting Bugs} for
+instruction on how to report it.
+
+@node Reporting Bugs
+@cindex reporting bugs
+@cindex bug reporting
+@cindex problem, reporting
+@chapter Reporting Bugs
+
+We welcome bug reports and feature request. If you think you have
+found a bug, please report it. If you're doubt, please send it
+anyway. We can't promise that we'll fix the bug or implement your
+feature idea, or always agree that it's a bug, but we always want to
+hear from you. Please report bugs at
+@url{https://codeberg.org/akib/emacs-eat/issues/new}. You may send
+the bug report by emailing to the maintain
+(@kbd{M-x describe-package RET eat RET} would show the email address),
+but we prefer the former method, since the report is visible to
+everyone immediately.
+
+The most important principle in reporting a bug is to report
+@emph{facts}. Hypotheses and verbal descriptions are useful when they
+are more guesses, but in no way substitute for detailed raw data. You
+are encouraged to send you finding about bug, but please make sure to
+send the raw data needed to reproduce the bug.
+
+For bug reports, please try to reproduce the bug with @samp{emacs -Q}.
+This will disable loading your Emacs configuration, ruling out the
+potential bugs in your customizations. Please include enough
+information for us to reproduce the bug with @samp{emacs -Q}, so that
+we have (or can get) enough information about the bug to fix it. Some
+bugs are hard to reproduce with @samp{emacs -Q}, and some are not
+easily reproducible at all, in that case please give us the as much
+information as possible about Emacs configuration. Generally
+speaking, enough information includes (but not limited to):
+
+@itemize @bullet
+@item
+A description of the bug.
+
+@item
+The version of Eat you're running.
+(@kbd{M-x describe-package RET eat RET} would show the version.)
+
+@item
+The version of Emacs you're running.
+
+@item
+Your Eat configuration, precisely.
+
+@item
+If you didn't or can't reproduce the bug with @code{emacs -Q}, as much
+information as possible about Emacs configuration.
+
+@item
+Precisely what you did.
+
+@item
+Trace output, if you can generate it. (@xref{Tracing the Terminal}
+for instruction to generate it.)
+
+@item
+Hardware and operating system names and versions.
+
+@item
+Anything else that you think would be helpful.
+@end itemize
+
+When in doubt whether to include something or not, please include it.
+It is better to include many useless information than to leave out
+something useful.
+
+It is critical to send enough information to reproduce the bug. What
+is not critical to ``narrow down'' the example to the smallest
+possible -- anything that reproduces the bug will suffice. (Of
+course, if you like doing experiments, the smaller the example, the
+better.)
+
+@node Tracing the Terminal
+@cindex tracing the terminal
+@cindex terminal tracing
+@cindex terminal recording
+@chapter Tracing the Terminal
+
+When you run into a bug and want to report it, you'll want to trace
+the terminal. Tracing means recording all the terminal activity,
+including creation, output, resizing and deleting.
+
+@findex eat-trace-mode
+To enable tracing, enable the global minor mode
+@command{eat-trace-mode}. This will trace all new terminals,
+including the terminal created inside Eshell.
+
+Trace output for each command will be output in a buffer named
+@samp{*eat-trace @var{buffer-name}*: @var{command}}, where
+@var{buffer-name} is the buffer showing the terminal, and
+@var{command} is the command run in the terminal.
+
+Only the terminals created after the trace mode is enabled are traced.
+So if you don't have the mode enabled when you have found a bug,
+tracing can't give you any information (as tracing is disabled,
+nothing has been recorded).
+
+While submitting bug reports, please include the whole output in the
+trace output buffer. This contains many crucial information required
+to reproduce your bug.
+
+@findex eat-trace-replay
+You can replay the terminal by executing the command
+@command{eat-trace-replay} is the trace output buffer. You can use
+the key @kbd{n} or the key @kbd{@key{down}} to show the next frame.
+This is not intended for ordinary users, it's documented here only to
+help you debug Eat. You mustn't rely on the behavior of this
+functionality to do anything else.
+
+@part Part V:@* Appendices
+
+@node GNU General Public License
+@appendix GNU General Public License
+
+@include gpl.texi
+
+@node GNU Free Documentation License
+@appendix GNU Free Documentation License
+
+@include fdl.texi
+
+@node Index
+@appendix Index
+
+@printindex cp
+
+@bye
diff --git a/eat.ti b/eat.ti
new file mode 100644
index 0000000000..7f80e0dc40
--- /dev/null
+++ b/eat.ti
@@ -0,0 +1,206 @@
+# The code here is forced by the interface, and is not subject to
+# copyright, constituting the only possible expression of the
+# algorithm in this format.
+#
+# When updating this file, files in e/ should be regenerated by
+# running in "make" or "make terminfo" here.
+
+eat-mono|Emacs Eat without colors,
+ cols#80,
+ lines#24,
+ am,
+ clear=\e[2J,
+ cr=\r,
+ bel=^G,
+ home=\e[H,
+ cub1=\b,
+ cuf1=\e[C,
+ cuu1=\e[A,
+ cud1=^K,
+ ind=\n,
+ indn=\e[%p1%dS,
+ ri=\eM,
+ rin=\e[%p1%dT,
+ nel=\eE,
+ cup=\e[%i%p1%d;%p2%dH,
+ hpa=\e[%i%p1%dG,
+ vpa=\e[%i%p1%dd,
+ cub=\e[%p1%dD,
+ cuf=\e[%p1%dC,
+ cuu=\e[%p1%dA,
+ cud=\e[%p1%dB,
+ smcup=\e[?1049h,
+ rmcup=\e[?1049l,
+ el=\e[K,
+ el1=\e[1K,
+ ed=\e[J,
+ Ed=\e[J,
+ il1=\e[L,
+ il=\e[%p1%dL,
+ dl1=\e[M,
+ dl=\e[%p1%dM,
+ csr=\e[%i%p1%d;%p2%dr,
+ sc=\e7,
+ rc=\e8,
+ smir=\e[4h,
+ rmir=\e[4l,
+# ich1=\e[@,
+ ich=\e[%p1%d@,
+ mir,
+ dch1=\e[P,
+ dch=\e[%p1%dP,
+ ech=\e[%p1%dX,
+ smso=\e[7m,
+ rmso=\e[27m,
+ smul=\e[4m,
+ rmul=\e[24m,
+ sitm=\e[3m,
+ ritm=\e[23m,
+ blink=\e[5m,
+ bold=\e[1m,
+ dim=\e[2m,
+ invis=\e[8m,
+ rev=\e[7m,
+ sgr0=\e(B\e[m,
+ sgr=%?%p9%t\e(0%e\e(B%;
+ \e[0%?%p6%t;1%;
+ %?%p5%t;2%;
+ %?%p2%t;4%;
+ %?%p1%p3%|%t;7%;
+ %?%p4%t;5%;
+ %?%p7%t;8%;m,
+ msgr,
+ cvvis=\e[?12;25h,
+ civis=\e[?25l,
+ cnorm=\e[?12l\e[?25h,
+ smkx=\e[?1h,
+ rmkx=\e[?1l,
+ ht=\t,
+ cbt=\e[Z,
+ it#8,
+ rs1=\e\\\ec,
+ hs,
+ tsl=\e]2;,
+ fsl=\e\\,
+ dsl=\e]2;\e\\,
+ xenl,
+ acsc=++\,\,--..00``aaffggiihhjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
+ smacs=\e(0,
+ rmacs=\e(B,
+ npc,
+ rep=%p1%c\E[%p2%{1}%-%db,
+ u6=\e[%i%d;%dR,
+ u7=\e[6n,
+ smam=\e[?7h,
+ rmam=\e[?7l,
+ smxx=\e[9m,
+ rmxx=\e[29m,
+ kbs=^?,
+ kcuu1=\eOA,
+ kri=\e[1;2A,
+ kcud1=\eOB,
+ kind=\e[1;2B,
+ kcuf1=\eOC,
+ kRIT=\e[1;2C,
+ kcub1=\eOD,
+ kLFT=\e[1;2D,
+ kich1=\e[2~,
+ kIC=\e[2;2~,
+ kdch1=\e[3~,
+ kDC=\e[3;2~,
+ khome=\eOH,
+ kHOM=\e[1;2H,
+ kend=\eOF,
+ kEND=\e[1;2F,
+ kpp=\e[5~,
+ kPRV=\e[5;2~,
+ knp=\e[6~,
+ kNXT=\e[6;2~,
+ kf1=\eOP,
+ kf2=\eOQ,
+ kf3=\eOR,
+ kf4=\eOS,
+ kf5=\e[15~,
+ kf6=\e[17~,
+ kf7=\e[18~,
+ kf8=\e[19~,
+ kf9=\e[20~,
+ kf10=\e[21~,
+ kf11=\e[23~,
+ kf12=\e[24~,
+ kf13=\e[1;2P,
+ kf14=\e[1;2Q,
+ kf15=\e[1;2R,
+ kf16=\e[1;2S,
+ kf17=\e[15;2~,
+ kf18=\e[17;2~,
+ kf19=\e[18;2~,
+ kf20=\e[19;2~,
+ kf21=\e[20;2~,
+ kf22=\e[21;2~,
+ kf23=\e[23;2~,
+ kf24=\e[24;2~,
+ kf25=\e[1;5P,
+ kf26=\e[1;5Q,
+ kf27=\e[1;5R,
+ kf28=\e[1;5S,
+ kf29=\e[15;5~,
+ kf30=\e[17;5~,
+ kf31=\e[18;5~,
+ kf32=\e[19;5~,
+ kf33=\e[20;5~,
+ kf34=\e[21;5~,
+ kf35=\e[23;5~,
+ kf36=\e[24;5~,
+ kf37=\e[1;6P,
+ kf38=\e[1;6Q,
+ kf39=\e[1;6R,
+ kf40=\e[1;6S,
+ kf41=\e[15;6~,
+ kf42=\e[17;6~,
+ kf43=\e[18;6~,
+ kf44=\e[19;6~,
+ kf45=\e[20;6~,
+ kf46=\e[21;6~,
+ kf47=\e[23;6~,
+ kf48=\e[24;6~,
+ kf49=\e[1;3P,
+ kf50=\e[1;3Q,
+ kf51=\e[1;3R,
+ kf52=\e[1;3S,
+ kf53=\e[15;3~,
+ kf54=\e[17;3~,
+ kf55=\e[18;3~,
+ kf56=\e[19;3~,
+ kf57=\e[20;3~,
+ kf58=\e[21;3~,
+ kf59=\e[23;3~,
+ kf60=\e[24;3~,
+ kf61=\e[1;4P,
+ kf62=\e[1;4Q,
+ kf63=\e[1;4R,
+ kmous=\e[M,
+
+eat-color|Emacs Eat with eight colors,
+ use=eat-mono,
+ colors#0x8,
+ pairs#0x40,
+ bce,
+ op=\e[39;49m,
+ setab=\e[4%p1%dm,
+ setaf=\e[3%p1%dm,
+ setb=\e[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m,
+ setf=\e[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m,
+
+eat-256color|Emacs Eat with 256 colors,
+ use=eat-color,
+ colors#0x100,
+ pairs#0x10000,
+ setab=\e[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m,
+ setaf=\e[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m,
+ setb@,
+ setf@,
+
+eat-truecolor|Emacs Eat with truecolor,
+ use=eat-256color,
+ Tc,
diff --git a/fdl.texi b/fdl.texi
new file mode 100644
index 0000000000..eaf3da0e92
--- /dev/null
+++ b/fdl.texi
@@ -0,0 +1,505 @@
+@c The GNU Free Documentation License.
+@center Version 1.3, 3 November 2008
+
+@c This file is intended to be included within another document,
+@c hence no sectioning command or @node.
+
+@display
+Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation,
Inc.
+@uref{https://fsf.org/}
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+@end display
+
+@enumerate 0
+@item
+PREAMBLE
+
+The purpose of this License is to make a manual, textbook, or other
+functional and useful document @dfn{free} in the sense of freedom: to
+assure everyone the effective freedom to copy and redistribute it,
+with or without modifying it, either commercially or noncommercially.
+Secondarily, this License preserves for the author and publisher a way
+to get credit for their work, while not being considered responsible
+for modifications made by others.
+
+This License is a kind of ``copyleft'', which means that derivative
+works of the document must themselves be free in the same sense. It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does. But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book. We recommend this License
+principally for works whose purpose is instruction or reference.
+
+@item
+APPLICABILITY AND DEFINITIONS
+
+This License applies to any manual or other work, in any medium, that
+contains a notice placed by the copyright holder saying it can be
+distributed under the terms of this License. Such a notice grants a
+world-wide, royalty-free license, unlimited in duration, to use that
+work under the conditions stated herein. The ``Document'', below,
+refers to any such manual or work. Any member of the public is a
+licensee, and is addressed as ``you''. You accept the license if you
+copy, modify or distribute the work in a way requiring permission
+under copyright law.
+
+A ``Modified Version'' of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A ``Secondary Section'' is a named appendix or a front-matter section
+of the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall
+subject (or to related matters) and contains nothing that could fall
+directly within that overall subject. (Thus, if the Document is in
+part a textbook of mathematics, a Secondary Section may not explain
+any mathematics.) The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The ``Invariant Sections'' are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License. If a
+section does not fit the above definition of Secondary then it is not
+allowed to be designated as Invariant. The Document may contain zero
+Invariant Sections. If the Document does not identify any Invariant
+Sections then there are none.
+
+The ``Cover Texts'' are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License. A Front-Cover Text may
+be at most 5 words, and a Back-Cover Text may be at most 25 words.
+
+A ``Transparent'' copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, that is suitable for revising the document
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters. A copy made in an otherwise Transparent file
+format whose markup, or absence of markup, has been arranged to thwart
+or discourage subsequent modification by readers is not Transparent.
+An image format is not Transparent if used for any substantial amount
+of text. A copy that is not ``Transparent'' is called ``Opaque''.
+
+Examples of suitable formats for Transparent copies include plain
+ASCII without markup, Texinfo input format, La@TeX{} input
+format, SGML or XML using a publicly available
+DTD, and standard-conforming simple HTML,
+PostScript or PDF designed for human modification. Examples
+of transparent image formats include PNG, XCF and
+JPG@. Opaque formats include proprietary formats that can be
+read and edited only by proprietary word processors, SGML or
+XML for which the DTD and/or processing tools are
+not generally available, and the machine-generated HTML,
+PostScript or PDF produced by some word processors for
+output purposes only.
+
+The ``Title Page'' means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page. For works in
+formats which do not have any title page as such, ``Title Page'' means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+The ``publisher'' means any person or entity that distributes copies
+of the Document to the public.
+
+A section ``Entitled XYZ'' means a named subunit of the Document whose
+title either is precisely XYZ or contains XYZ in parentheses following
+text that translates XYZ in another language. (Here XYZ stands for a
+specific section name mentioned below, such as ``Acknowledgements'',
+``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title''
+of such a section when you modify the Document means that it remains a
+section ``Entitled XYZ'' according to this definition.
+
+The Document may include Warranty Disclaimers next to the notice which
+states that this License applies to the Document. These Warranty
+Disclaimers are considered to be included by reference in this
+License, but only as regards disclaiming warranties: any other
+implication that these Warranty Disclaimers may have is void and has
+no effect on the meaning of this License.
+
+@item
+VERBATIM COPYING
+
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no other
+conditions whatsoever to those of this License. You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute. However, you may accept
+compensation in exchange for copies. If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+@item
+COPYING IN QUANTITY
+
+If you publish printed copies (or copies in media that commonly have
+printed covers) of the Document, numbering more than 100, and the
+Document's license notice requires Cover Texts, you must enclose the
+copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover. Both covers must also clearly and legibly identify
+you as the publisher of these copies. The front cover must present
+the full title with all words of the title equally prominent and
+visible. You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a computer-network location from which the general network-using
+public has access to download using public-standard network protocols
+a complete Transparent copy of the Document, free of added material.
+If you use the latter option, you must take reasonably prudent steps,
+when you begin distribution of Opaque copies in quantity, to ensure
+that this Transparent copy will remain thus accessible at the stated
+location until at least one year after the last time you distribute an
+Opaque copy (directly or through your agents or retailers) of that
+edition to the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to give
+them a chance to provide you with an updated version of the Document.
+
+@item
+MODIFICATIONS
+
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it. In addition, you must do these things in the Modified Version:
+
+@enumerate A
+@item
+Use in the Title Page (and on the covers, if any) a title distinct
+from that of the Document, and from those of previous versions
+(which should, if there were any, be listed in the History section
+of the Document). You may use the same title as a previous version
+if the original publisher of that version gives permission.
+
+@item
+List on the Title Page, as authors, one or more persons or entities
+responsible for authorship of the modifications in the Modified
+Version, together with at least five of the principal authors of the
+Document (all of its principal authors, if it has fewer than five),
+unless they release you from this requirement.
+
+@item
+State on the Title page the name of the publisher of the
+Modified Version, as the publisher.
+
+@item
+Preserve all the copyright notices of the Document.
+
+@item
+Add an appropriate copyright notice for your modifications
+adjacent to the other copyright notices.
+
+@item
+Include, immediately after the copyright notices, a license notice
+giving the public permission to use the Modified Version under the
+terms of this License, in the form shown in the Addendum below.
+
+@item
+Preserve in that license notice the full lists of Invariant Sections
+and required Cover Texts given in the Document's license notice.
+
+@item
+Include an unaltered copy of this License.
+
+@item
+Preserve the section Entitled ``History'', Preserve its Title, and add
+to it an item stating at least the title, year, new authors, and
+publisher of the Modified Version as given on the Title Page. If
+there is no section Entitled ``History'' in the Document, create one
+stating the title, year, authors, and publisher of the Document as
+given on its Title Page, then add an item describing the Modified
+Version as stated in the previous sentence.
+
+@item
+Preserve the network location, if any, given in the Document for
+public access to a Transparent copy of the Document, and likewise
+the network locations given in the Document for previous versions
+it was based on. These may be placed in the ``History'' section.
+You may omit a network location for a work that was published at
+least four years before the Document itself, or if the original
+publisher of the version it refers to gives permission.
+
+@item
+For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve
+the Title of the section, and preserve in the section all the
+substance and tone of each of the contributor acknowledgements and/or
+dedications given therein.
+
+@item
+Preserve all the Invariant Sections of the Document,
+unaltered in their text and in their titles. Section numbers
+or the equivalent are not considered part of the section titles.
+
+@item
+Delete any section Entitled ``Endorsements''. Such a section
+may not be included in the Modified Version.
+
+@item
+Do not retitle any existing section to be Entitled ``Endorsements'' or
+to conflict in title with any Invariant Section.
+
+@item
+Preserve any Warranty Disclaimers.
+@end enumerate
+
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant. To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section Entitled ``Endorsements'', provided it contains
+nothing but endorsements of your Modified Version by various
+parties---for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version. Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity. If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+@item
+COMBINING DOCUMENTS
+
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice, and that you preserve all their Warranty Disclaimers.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy. If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections Entitled ``History''
+in the various original documents, forming one section Entitled
+``History''; likewise combine any sections Entitled ``Acknowledgements'',
+and any sections Entitled ``Dedications''. You must delete all
+sections Entitled ``Endorsements.''
+
+@item
+COLLECTIONS OF DOCUMENTS
+
+You may make a collection consisting of the Document and other documents
+released under this License, and replace the individual copies of this
+License in the various documents with a single copy that is included in
+the collection, provided that you follow the rules of this License for
+verbatim copying of each of the documents in all other respects.
+
+You may extract a single document from such a collection, and distribute
+it individually under this License, provided you insert a copy of this
+License into the extracted document, and follow this License in all
+other respects regarding verbatim copying of that document.
+
+@item
+AGGREGATION WITH INDEPENDENT WORKS
+
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, is called an ``aggregate'' if the copyright
+resulting from the compilation is not used to limit the legal rights
+of the compilation's users beyond what the individual works permit.
+When the Document is included in an aggregate, this License does not
+apply to the other works in the aggregate which are not themselves
+derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one half of
+the entire aggregate, the Document's Cover Texts may be placed on
+covers that bracket the Document within the aggregate, or the
+electronic equivalent of covers if the Document is in electronic form.
+Otherwise they must appear on printed covers that bracket the whole
+aggregate.
+
+@item
+TRANSLATION
+
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections. You may include a
+translation of this License, and all the license notices in the
+Document, and any Warranty Disclaimers, provided that you also include
+the original English version of this License and the original versions
+of those notices and disclaimers. In case of a disagreement between
+the translation and the original version of this License or a notice
+or disclaimer, the original version will prevail.
+
+If a section in the Document is Entitled ``Acknowledgements'',
+``Dedications'', or ``History'', the requirement (section 4) to Preserve
+its Title (section 1) will typically require changing the actual
+title.
+
+@item
+TERMINATION
+
+You may not copy, modify, sublicense, or distribute the Document
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense, or distribute it is void, and
+will automatically terminate your rights under this License.
+
+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, receipt of a copy of some or all of the same material does
+not give you any rights to use it.
+
+@item
+FUTURE REVISIONS OF THIS LICENSE
+
+The Free Software Foundation may publish new, revised versions
+of the GNU Free Documentation 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. See
+@uref{https://www.gnu.org/licenses/}.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License ``or any later version'' applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation. If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation. If the Document
+specifies that a proxy can decide which future versions of this
+License can be used, that proxy's public statement of acceptance of a
+version permanently authorizes you to choose that version for the
+Document.
+
+@item
+RELICENSING
+
+``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any
+World Wide Web server that publishes copyrightable works and also
+provides prominent facilities for anybody to edit those works. A
+public wiki that anybody can edit is an example of such a server. A
+``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the
+site means any set of copyrightable works thus published on the MMC
+site.
+
+``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0
+license published by Creative Commons Corporation, a not-for-profit
+corporation with a principal place of business in San Francisco,
+California, as well as future copyleft versions of that license
+published by that same organization.
+
+``Incorporate'' means to publish or republish a Document, in whole or
+in part, as part of another Document.
+
+An MMC is ``eligible for relicensing'' if it is licensed under this
+License, and if all works that were first published under this License
+somewhere other than this MMC, and subsequently incorporated in whole
+or in part into the MMC, (1) had no cover texts or invariant sections,
+and (2) were thus incorporated prior to November 1, 2008.
+
+The operator of an MMC Site may republish an MMC contained in the site
+under CC-BY-SA on the same site at any time before August 1, 2009,
+provided the MMC is eligible for relicensing.
+
+@end enumerate
+
+@page
+@heading ADDENDUM: How to use this License for your documents
+
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+@smallexample
+@group
+ Copyright (C) @var{year} @var{your name}.
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.3
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+ Texts. A copy of the license is included in the section entitled ``GNU
+ Free Documentation License''.
+@end group
+@end smallexample
+
+If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
+replace the ``with@dots{}Texts.''@: line with this:
+
+@smallexample
+@group
+ with the Invariant Sections being @var{list their titles}, with
+ the Front-Cover Texts being @var{list}, and with the Back-Cover Texts
+ being @var{list}.
+@end group
+@end smallexample
+
+If you have Invariant Sections without Cover Texts, or some other
+combination of the three, merge those two alternatives to suit the
+situation.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
+
+@c Local Variables:
+@c ispell-local-pdict: "ispell-dict"
+@c End:
diff --git a/gitlog-to-changelog b/gitlog-to-changelog
new file mode 100755
index 0000000000..82d9f97336
--- /dev/null
+++ b/gitlog-to-changelog
@@ -0,0 +1,516 @@
+#!/bin/sh
+#! -*-perl-*-
+
+# Convert git log output to ChangeLog format.
+
+# Copyright (C) 2008-2022 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 <https://www.gnu.org/licenses/>.
+#
+# Written by Jim Meyering
+
+# This is a prologue that allows to run a perl script as an executable
+# on systems that are compliant to a POSIX version before POSIX:2017.
+# On such systems, the usual invocation of an executable through execlp()
+# or execvp() fails with ENOEXEC if it is a script that does not start
+# with a #! line. The script interpreter mentioned in the #! line has
+# to be /bin/sh, because on GuixSD systems that is the only program that
+# has a fixed file name. The second line is essential for perl and is
+# also useful for editing this file in Emacs. The next two lines below
+# are valid code in both sh and perl. When executed by sh, they re-execute
+# the script through the perl program found in $PATH. The '-x' option
+# is essential as well; without it, perl would re-execute the script
+# through /bin/sh. When executed by perl, the next two lines are a no-op.
+eval 'exec perl -wSx "$0" "$@"'
+ if 0;
+
+my $VERSION = '2022-01-27 18:49'; # UTC
+# The definition above must lie within the first 8 lines in order
+# for the Emacs time-stamp write hook (at end) to update it.
+# If you change this file with Emacs, please let the write hook
+# do its job. Otherwise, update this string manually.
+
+use strict;
+use warnings;
+use Getopt::Long;
+use POSIX qw(strftime);
+
+(my $ME = $0) =~ s|.*/||;
+
+# use File::Coda; # https://meyering.net/code/Coda/
+END {
+ defined fileno STDOUT or return;
+ close STDOUT and return;
+ warn "$ME: failed to close standard output: $!\n";
+ $? ||= 1;
+}
+
+sub usage ($)
+{
+ my ($exit_code) = @_;
+ my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
+ if ($exit_code != 0)
+ {
+ print $STREAM "Try '$ME --help' for more information.\n";
+ }
+ else
+ {
+ print $STREAM <<EOF;
+Usage: $ME [OPTIONS] [ARGS]
+
+Convert git log output to ChangeLog format. If present, any ARGS
+are passed to "git log". To avoid ARGS being parsed as options to
+$ME, they may be preceded by '--'.
+
+OPTIONS:
+
+ --amend=FILE FILE maps from an SHA1 to perl code (i.e., s/old/new/) that
+ makes a change to SHA1's commit log text or metadata.
+ --append-dot append a dot to the first line of each commit message if
+ there is no other punctuation or blank at the end.
+ --no-cluster never cluster commit messages under the same date/author
+ header; the default is to cluster adjacent commit messages
+ if their headers are the same and neither commit message
+ contains multiple paragraphs.
+ --srcdir=DIR the root of the source tree, from which the .git/
+ directory can be derived.
+ --since=DATE convert only the logs since DATE;
+ the default is to convert all log entries.
+ --until=DATE convert only the logs older than DATE.
+ --ignore-matching=PAT ignore commit messages whose first lines match PAT.
+ --ignore-line=PAT ignore lines of commit messages that match PAT.
+ --format=FMT set format string for commit subject and body;
+ see 'man git-log' for the list of format metacharacters;
+ the default is '%s%n%b%n'
+ --strip-tab remove one additional leading TAB from commit message lines.
+ --strip-cherry-pick remove data inserted by "git cherry-pick";
+ this includes the "cherry picked from commit ..." line,
+ and the possible final "Conflicts:" paragraph.
+ --help display this help and exit
+ --version output version information and exit
+
+EXAMPLE:
+
+ $ME --since=2008-01-01 > ChangeLog
+ $ME -- -n 5 foo > last-5-commits-to-branch-foo
+
+SPECIAL SYNTAX:
+
+The following types of strings are interpreted specially when they appear
+at the beginning of a log message line. They are not copied to the output.
+
+ Copyright-paperwork-exempt: Yes
+ Append the "(tiny change)" notation to the usual "date name email"
+ ChangeLog header to mark a change that does not require a copyright
+ assignment.
+ Co-authored-by: Joe User <user\@example.com>
+ List the specified name and email address on a second
+ ChangeLog header, denoting a co-author.
+ Signed-off-by: Joe User <user\@example.com>
+ These lines are simply elided.
+
+In a FILE specified via --amend, comment lines (starting with "#") are ignored.
+FILE must consist of <SHA,CODE+> pairs where SHA is a 40-byte SHA1 (alone on
+a line) referring to a commit in the current project, and CODE refers to one
+or more consecutive lines of Perl code. Pairs must be separated by one or
+more blank line.
+
+Here is sample input for use with --amend=FILE, from coreutils:
+
+3a169f4c5d9159283548178668d2fae6fced3030
+# fix typo in title:
+s/all tile types/all file types/
+
+1379ed974f1fa39b12e2ffab18b3f7a607082202
+# Due to a bug in vc-dwim, I mis-attributed a patch by Paul to myself.
+# Change the author to be Paul. Note the escaped "@":
+s,Jim .*>,Paul Eggert <eggert\\\@cs.ucla.edu>,
+
+EOF
+ }
+ exit $exit_code;
+}
+
+# If the string $S is a well-behaved file name, simply return it.
+# If it contains white space, quotes, etc., quote it, and return the new
string.
+sub shell_quote($)
+{
+ my ($s) = @_;
+ if ($s =~ m![^\w+/.,-]!)
+ {
+ # Convert each single quote to '\''
+ $s =~ s/\'/\'\\\'\'/g;
+ # Then single quote the string.
+ $s = "'$s'";
+ }
+ return $s;
+}
+
+sub quoted_cmd(@)
+{
+ return join (' ', map {shell_quote $_} @_);
+}
+
+# Parse file F.
+# Comment lines (starting with "#") are ignored.
+# F must consist of <SHA,CODE+> pairs where SHA is a 40-byte SHA1
+# (alone on a line) referring to a commit in the current project, and
+# CODE refers to one or more consecutive lines of Perl code.
+# Pairs must be separated by one or more blank line.
+sub parse_amend_file($)
+{
+ my ($f) = @_;
+
+ open F, '<', $f
+ or die "$ME: $f: failed to open for reading: $!\n";
+
+ my $fail;
+ my $h = {};
+ my $in_code = 0;
+ my $sha;
+ while (defined (my $line = <F>))
+ {
+ $line =~ /^\#/
+ and next;
+ chomp $line;
+ $line eq ''
+ and $in_code = 0, next;
+
+ if (!$in_code)
+ {
+ $line =~ /^([[:xdigit:]]{40})$/
+ or (warn "$ME: $f:$.: invalid line; expected an SHA1\n"),
+ $fail = 1, next;
+ $sha = lc $1;
+ $in_code = 1;
+ exists $h->{$sha}
+ and (warn "$ME: $f:$.: duplicate SHA1\n"),
+ $fail = 1, next;
+ }
+ else
+ {
+ $h->{$sha} ||= '';
+ $h->{$sha} .= "$line\n";
+ }
+ }
+ close F;
+
+ $fail
+ and exit 1;
+
+ return $h;
+}
+
+# git_dir_option $SRCDIR
+#
+# From $SRCDIR, the --git-dir option to pass to git (none if $SRCDIR
+# is undef). Return as a list (0 or 1 element).
+sub git_dir_option($)
+{
+ my ($srcdir) = @_;
+ my @res = ();
+ if (defined $srcdir)
+ {
+ my $qdir = shell_quote $srcdir;
+ my $cmd = "cd $qdir && git rev-parse --show-toplevel";
+ my $qcmd = shell_quote $cmd;
+ my $git_dir = qx($cmd);
+ defined $git_dir
+ or die "$ME: cannot run $qcmd: $!\n";
+ $? == 0
+ or die "$ME: $qcmd had unexpected exit code or signal ($?)\n";
+ chomp $git_dir;
+ push @res, "--git-dir=$git_dir/.git";
+ }
+ @res;
+}
+
+{
+ my $since_date;
+ my $until_date;
+ my $format_string = '%s%n%b%n';
+ my $amend_file;
+ my $append_dot = 0;
+ my $cluster = 1;
+ my $ignore_matching;
+ my $ignore_line;
+ my $strip_tab = 0;
+ my $strip_cherry_pick = 0;
+ my $srcdir;
+ GetOptions
+ (
+ help => sub { usage 0 },
+ version => sub { print "$ME version $VERSION\n"; exit },
+ 'since=s' => \$since_date,
+ 'until=s' => \$until_date,
+ 'format=s' => \$format_string,
+ 'amend=s' => \$amend_file,
+ 'append-dot' => \$append_dot,
+ 'cluster!' => \$cluster,
+ 'ignore-matching=s' => \$ignore_matching,
+ 'ignore-line=s' => \$ignore_line,
+ 'strip-tab' => \$strip_tab,
+ 'strip-cherry-pick' => \$strip_cherry_pick,
+ 'srcdir=s' => \$srcdir,
+ ) or usage 1;
+
+ defined $since_date
+ and unshift @ARGV, "--since=$since_date";
+ defined $until_date
+ and unshift @ARGV, "--until=$until_date";
+
+ # This is a hash that maps an SHA1 to perl code (i.e., s/old/new/)
+ # that makes a correction in the log or attribution of that commit.
+ my $amend_code = defined $amend_file ? parse_amend_file $amend_file : {};
+
+ my @cmd = ('git',
+ git_dir_option $srcdir,
+ qw(log --log-size),
+ '--pretty=format:%H:%ct %an <%ae>%n%n'.$format_string, @ARGV);
+ open PIPE, '-|', @cmd
+ or die ("$ME: failed to run '". quoted_cmd (@cmd) ."': $!\n"
+ . "(Is your Git too old? Version 1.5.1 or later is required.)\n");
+
+ my $prev_multi_paragraph;
+ my $prev_date_line = '';
+ my @prev_coauthors = ();
+ my @skipshas = ();
+ while (1)
+ {
+ defined (my $in = <PIPE>)
+ or last;
+ $in =~ /^log size (\d+)$/
+ or die "$ME:$.: Invalid line (expected log size):\n$in";
+ my $log_nbytes = $1;
+
+ my $log;
+ my $n_read = read PIPE, $log, $log_nbytes;
+ $n_read == $log_nbytes
+ or die "$ME:$.: unexpected EOF\n";
+
+ # Extract leading hash.
+ my ($sha, $rest) = split ':', $log, 2;
+ defined $sha
+ or die "$ME:$.: malformed log entry\n";
+ $sha =~ /^[[:xdigit:]]{40}$/
+ or die "$ME:$.: invalid SHA1: $sha\n";
+
+ my $skipflag = 0;
+ if (@skipshas)
+ {
+ foreach(@skipshas)
+ {
+ if ($sha =~ /^$_/)
+ {
+ $skipflag = $_;
+ last;
+ }
+ }
+ }
+
+ # If this commit's log requires any transformation, do it now.
+ my $code = $amend_code->{$sha};
+ if (defined $code)
+ {
+ eval 'use Safe';
+ my $s = new Safe;
+ # Put the unpreprocessed entry into "$_".
+ $_ = $rest;
+
+ # Let $code operate on it, safely.
+ my $r = $s->reval("$code")
+ or die "$ME:$.:$sha: failed to eval \"$code\":\n$@\n";
+
+ # Note that we've used this entry.
+ delete $amend_code->{$sha};
+
+ # Update $rest upon success.
+ $rest = $_;
+ }
+
+ # Remove lines inserted by "git cherry-pick".
+ if ($strip_cherry_pick)
+ {
+ $rest =~ s/^\s*Conflicts:\n.*//sm;
+ $rest =~ s/^\s*\(cherry picked from commit [\da-f]+\)\n//m;
+ }
+
+ my @line = split /[ \t]*\n/, $rest;
+ my $author_line = shift @line;
+ defined $author_line
+ or die "$ME:$.: unexpected EOF\n";
+ $author_line =~ /^(\d+) (.*>)$/
+ or die "$ME:$.: Invalid line "
+ . "(expected date/author/email):\n$author_line\n";
+
+ # Format 'Copyright-paperwork-exempt: Yes' as a standard ChangeLog
+ # `(tiny change)' annotation.
+ my $tiny = (grep
(/^(?:Copyright-paperwork-exempt|Tiny-change):\s+[Yy]es$/, @line)
+ ? ' (tiny change)' : '');
+
+ my $date_line = sprintf "%s %s$tiny\n",
+ strftime ("%Y-%m-%d", localtime ($1)), $2;
+
+ my @coauthors = grep /^Co-authored-by:.*$/, @line;
+ # Omit meta-data lines we've already interpreted.
+ @line = grep !/^(?:Signed-off-by:[ ].*>$
+ |Co-authored-by:[ ]
+ |Copyright-paperwork-exempt:[ ]
+ |Tiny-change:[ ]
+ )/x, @line;
+
+ # Remove leading and trailing blank lines.
+ if (@line)
+ {
+ while ($line[0] =~ /^\s*$/) { shift @line; }
+ while ($line[$#line] =~ /^\s*$/) { pop @line; }
+ }
+
+ # Handle Emacs gitmerge.el "skipped" commits.
+ # Yes, this should be controlled by an option. So sue me.
+ if ( grep /^(; )?Merge from /, @line )
+ {
+ my $found = 0;
+ foreach (@line)
+ {
+ if (grep /^The following commit.*skipped:$/, $_)
+ {
+ $found = 1;
+ ## Reset at each merge to reduce chance of false matches.
+ @skipshas = ();
+ next;
+ }
+ if ($found && $_ =~ /^([[:xdigit:]]{7,}) [^ ]/)
+ {
+ push ( @skipshas, $1 );
+ }
+ }
+ }
+
+ # Ignore commits that match the --ignore-matching pattern, if specified.
+ if (defined $ignore_matching && @line && $line[0] =~ /$ignore_matching/)
+ {
+ $skipflag = 1;
+ }
+ elsif ($skipflag)
+ {
+ ## Perhaps only warn if a pattern matches more than once?
+ warn "$ME: warning: skipping $sha due to $skipflag\n";
+ }
+
+ if (! $skipflag)
+ {
+ if (defined $ignore_line && @line)
+ {
+ @line = grep ! /$ignore_line/, @line;
+ while ($line[$#line] =~ /^\s*$/) { pop @line; }
+ }
+
+ # Record whether there are two or more paragraphs.
+ my $multi_paragraph = grep /^\s*$/, @line;
+
+ # Format 'Co-authored-by: A U Thor <email@example.com>' lines in
+ # standard multi-author ChangeLog format.
+ for (@coauthors)
+ {
+ s/^Co-authored-by:\s*/\t /;
+ s/\s*</ </;
+
+ /<.*?@.*\..*>/
+ or warn "$ME: warning: missing email address for "
+ . substr ($_, 5) . "\n";
+ }
+
+ # If clustering of commit messages has been disabled, if this header
+ # would be different from the previous date/name/etc. header,
+ # or if this or the previous entry consists of two or more
paragraphs,
+ # then print the header.
+ if ( ! $cluster
+ || $date_line ne $prev_date_line
+ || "@coauthors" ne "@prev_coauthors"
+ || $multi_paragraph
+ || $prev_multi_paragraph)
+ {
+ $prev_date_line eq ''
+ or print "\n";
+ print $date_line;
+ @coauthors
+ and print join ("\n", @coauthors), "\n";
+ }
+ $prev_date_line = $date_line;
+ @prev_coauthors = @coauthors;
+ $prev_multi_paragraph = $multi_paragraph;
+
+ # If there were any lines
+ if (@line == 0)
+ {
+ warn "$ME: warning: empty commit message:\n"
+ . " commit $sha\n $date_line\n";
+ }
+ else
+ {
+ if ($append_dot)
+ {
+ # If the first line of the message has enough room, then
+ if (length $line[0] < 72)
+ {
+ # append a dot if there is no other punctuation or blank
+ # at the end.
+ $line[0] =~ /[[:punct:]\s]$/
+ or $line[0] .= '.';
+ }
+ }
+
+ # Remove one additional leading TAB from each line.
+ $strip_tab
+ and map { s/^\t// } @line;
+
+ # Prefix each non-empty line with a TAB.
+ @line = map { length $_ ? "\t$_" : '' } @line;
+
+ print "\n", join ("\n", @line), "\n";
+ }
+ }
+
+ defined ($in = <PIPE>)
+ or last;
+ $in ne "\n"
+ and die "$ME:$.: unexpected line:\n$in";
+ }
+
+ close PIPE
+ or die "$ME: error closing pipe from " . quoted_cmd (@cmd) . "\n";
+ # FIXME-someday: include $PROCESS_STATUS in the diagnostic
+
+ # Complain about any unused entry in the --amend=F specified file.
+ my $fail = 0;
+ foreach my $sha (keys %$amend_code)
+ {
+ warn "$ME:$amend_file: unused entry: $sha\n";
+ $fail = 1;
+ }
+
+ exit $fail;
+}
+
+# Local Variables:
+# mode: perl
+# indent-tabs-mode: nil
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-line-limit: 50
+# time-stamp-start: "my $VERSION = '"
+# time-stamp-format: "%:y-%02m-%02d %02H:%02M"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "'; # UTC"
+# End:
diff --git a/gpl.texi b/gpl.texi
new file mode 100644
index 0000000000..c007dc0696
--- /dev/null
+++ b/gpl.texi
@@ -0,0 +1,717 @@
+@c The GNU General Public License.
+@center Version 3, 29 June 2007
+
+@c This file is intended to be included within another document,
+@c hence no sectioning command or @node.
+
+@display
+Copyright @copyright{} 2007 Free Software Foundation, Inc.
@url{https://fsf.org/}
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+@end display
+
+@heading 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.
+
+@heading TERMS AND CONDITIONS
+
+@enumerate 0
+@item 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.
+
+@item 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.
+
+@item 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.
+
+@item 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.
+
+@item 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.
+
+@item 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:
+
+@enumerate a
+@item
+The work must carry prominent notices stating that you modified it,
+and giving a relevant date.
+
+@item
+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''.
+
+@item
+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.
+
+@item
+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.
+@end enumerate
+
+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.
+
+@item 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:
+
+@enumerate a
+@item
+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.
+
+@item
+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.
+
+@item
+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.
+
+@item
+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.
+
+@item
+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.
+
+@end enumerate
+
+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.
+
+@item 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:
+
+@enumerate a
+@item
+Disclaiming warranty or limiting liability differently from the terms
+of sections 15 and 16 of this License; or
+
+@item
+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
+
+@item
+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
+
+@item
+Limiting the use for publicity purposes of names of licensors or
+authors of the material; or
+
+@item
+Declining to grant rights under trademark law for use of some trade
+names, trademarks, or service marks; or
+
+@item
+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.
+@end enumerate
+
+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.
+
+@item 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.
+
+@item 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.
+
+@item 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.
+
+@item 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.
+
+@item 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.
+
+@item 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.
+
+@item 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.
+
+@item 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.
+
+@item 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.
+
+@item 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 enumerate
+
+@heading END OF TERMS AND CONDITIONS
+
+@heading 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.
+
+@smallexample
+@var{one line to give the program's name and a brief idea of what it does.}
+Copyright (C) @var{year} @var{name of author}
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE@. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see @url{https://www.gnu.org/licenses/}.
+@end smallexample
+
+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:
+
+@smallexample
+@var{program} Copyright (C) @var{year} @var{name of author}
+This program comes with ABSOLUTELY NO WARRANTY; for details type @samp{show w}.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type @samp{show c} for details.
+@end smallexample
+
+The hypothetical commands @samp{show w} and @samp{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
+@url{https://www.gnu.org/licenses/}.
+
+The GNU General Public License does not permit incorporating your
+program into proprietary programs. If your program is a subroutine
+library, you may consider it more useful to permit linking proprietary
+applications with the library. If this is what you want to do, use
+the GNU Lesser General Public License instead of this License. But
+first, please read @url{https://www.gnu.org/licenses/why-not-lgpl.html}.
diff --git a/make-changelog b/make-changelog
new file mode 100755
index 0000000000..b5aa113a0c
--- /dev/null
+++ b/make-changelog
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+./gitlog-to-changelog --ignore-matching='^; ' --ignore-line='^; ' \
+ --format='%B' >ChangeLog
+
+# Find the years covered by the generated ChangeLog, so that
+# a proper copyright notice can be output.
+years=$(sed -n 's/^\([0-9][0-9]*\).*/\1/p' ChangeLog | sort -nu)
+start_year=$(echo "$years" | head)
+end_year=$(echo "$years" | tail)
+
+if test "$start_year" = "$end_year"; then
+ year_range=$start_year
+else
+ year_range=$start_year-$end_year
+fi
+
+copyright_notice="
+;; Local Variables:
+;; coding: utf-8
+;; End:
+
+ Copyright (C) $year_range Akib Azmain Turja.
+
+ This file is not part of GNU Emacs.
+
+ GNU Emacs 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.
+
+ GNU Emacs 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>."
+
+echo "$copyright_notice" >>ChangeLog
diff --git a/texinfo.tex b/texinfo.tex
new file mode 100644
index 0000000000..deca599187
--- /dev/null
+++ b/texinfo.tex
@@ -0,0 +1,11614 @@
+% texinfo.tex -- TeX macros to handle Texinfo files.
+%
+% Load plain if necessary, i.e., if running under initex.
+\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi
+%
+\def\texinfoversion{2020-02-11.09}
+%
+% Copyright 1985, 1986, 1988, 1990-2019 Free Software Foundation, Inc.
+%
+% This texinfo.tex file 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 texinfo.tex file 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 <https://www.gnu.org/licenses/>.
+%
+% As a special exception, when this file is read by TeX when processing
+% a Texinfo source document, you may use the result without
+% restriction. This Exception is an additional permission under section 7
+% of the GNU General Public License, version 3 ("GPLv3").
+%
+% Please try the latest version of texinfo.tex before submitting bug
+% reports; you can get the latest version from:
+% https://ftp.gnu.org/gnu/texinfo/ (the Texinfo release area), or
+% https://ftpmirror.gnu.org/texinfo/ (same, via a mirror), or
+% https://www.gnu.org/software/texinfo/ (the Texinfo home page)
+% The texinfo.tex in any given distribution could well be out
+% of date, so if that's what you're using, please check.
+%
+% Send bug reports to bug-texinfo@gnu.org. Please include including a
+% complete document in each bug report with which we can reproduce the
+% problem. Patches are, of course, greatly appreciated.
+%
+% To process a Texinfo manual with TeX, it's most reliable to use the
+% texi2dvi shell script that comes with the distribution. For a simple
+% manual foo.texi, however, you can get away with this:
+% tex foo.texi
+% texindex foo.??
+% tex foo.texi
+% tex foo.texi
+% dvips foo.dvi -o # or whatever; this makes foo.ps.
+% The extra TeX runs get the cross-reference information correct.
+% Sometimes one run after texindex suffices, and sometimes you need more
+% than two; texi2dvi does it as many times as necessary.
+%
+% It is possible to adapt texinfo.tex for other languages, to some
+% extent. You can get the existing language-specific files from the
+% full Texinfo distribution.
+%
+% The GNU Texinfo home page is https://www.gnu.org/software/texinfo.
+
+
+\message{Loading texinfo [version \texinfoversion]:}
+
+% If in a .fmt file, print the version number
+% and turn on active characters that we couldn't do earlier because
+% they might have appeared in the input file name.
+\everyjob{\message{[Texinfo version \texinfoversion]}%
+ \catcode`+=\active \catcode`\_=\active}
+
+% LaTeX's \typeout. This ensures that the messages it is used for
+% are identical in format to the corresponding ones from latex/pdflatex.
+\def\typeout{\immediate\write17}%
+
+\chardef\other=12
+
+% We never want plain's \outer definition of \+ in Texinfo.
+% For @tex, we can use \tabalign.
+\let\+ = \relax
+
+% Save some plain tex macros whose names we will redefine.
+\let\ptexb=\b
+\let\ptexbullet=\bullet
+\let\ptexc=\c
+\let\ptexcomma=\,
+\let\ptexdot=\.
+\let\ptexdots=\dots
+\let\ptexend=\end
+\let\ptexequiv=\equiv
+\let\ptexexclam=\!
+\let\ptexfootnote=\footnote
+\let\ptexgtr=>
+\let\ptexhat=^
+\let\ptexi=\i
+\let\ptexindent=\indent
+\let\ptexinsert=\insert
+\let\ptexlbrace=\{
+\let\ptexless=<
+\let\ptexnewwrite\newwrite
+\let\ptexnoindent=\noindent
+\let\ptexplus=+
+\let\ptexraggedright=\raggedright
+\let\ptexrbrace=\}
+\let\ptexslash=\/
+\let\ptexsp=\sp
+\let\ptexstar=\*
+\let\ptexsup=\sup
+\let\ptext=\t
+\let\ptextop=\top
+{\catcode`\'=\active \global\let\ptexquoteright'}% active in plain's math mode
+
+% If this character appears in an error message or help string, it
+% starts a new line in the output.
+\newlinechar = `^^J
+
+% Use TeX 3.0's \inputlineno to get the line number, for better error
+% messages, but if we're using an old version of TeX, don't do anything.
+%
+\ifx\inputlineno\thisisundefined
+ \let\linenumber = \empty % Pre-3.0.
+\else
+ \def\linenumber{l.\the\inputlineno:\space}
+\fi
+
+% Set up fixed words for English if not already set.
+\ifx\putwordAppendix\undefined \gdef\putwordAppendix{Appendix}\fi
+\ifx\putwordChapter\undefined \gdef\putwordChapter{Chapter}\fi
+\ifx\putworderror\undefined \gdef\putworderror{error}\fi
+\ifx\putwordfile\undefined \gdef\putwordfile{file}\fi
+\ifx\putwordin\undefined \gdef\putwordin{in}\fi
+\ifx\putwordIndexIsEmpty\undefined \gdef\putwordIndexIsEmpty{(Index is
empty)}\fi
+\ifx\putwordIndexNonexistent\undefined \gdef\putwordIndexNonexistent{(Index
is nonexistent)}\fi
+\ifx\putwordInfo\undefined \gdef\putwordInfo{Info}\fi
+\ifx\putwordInstanceVariableof\undefined
\gdef\putwordInstanceVariableof{Instance Variable of}\fi
+\ifx\putwordMethodon\undefined \gdef\putwordMethodon{Method on}\fi
+\ifx\putwordNoTitle\undefined \gdef\putwordNoTitle{No Title}\fi
+\ifx\putwordof\undefined \gdef\putwordof{of}\fi
+\ifx\putwordon\undefined \gdef\putwordon{on}\fi
+\ifx\putwordpage\undefined \gdef\putwordpage{page}\fi
+\ifx\putwordsection\undefined \gdef\putwordsection{section}\fi
+\ifx\putwordSection\undefined \gdef\putwordSection{Section}\fi
+\ifx\putwordsee\undefined \gdef\putwordsee{see}\fi
+\ifx\putwordSee\undefined \gdef\putwordSee{See}\fi
+\ifx\putwordShortTOC\undefined \gdef\putwordShortTOC{Short Contents}\fi
+\ifx\putwordTOC\undefined \gdef\putwordTOC{Table of Contents}\fi
+%
+\ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi
+\ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi
+\ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi
+\ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi
+\ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi
+\ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi
+\ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi
+\ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi
+\ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi
+\ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi
+\ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi
+\ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi
+%
+\ifx\putwordDefmac\undefined \gdef\putwordDefmac{Macro}\fi
+\ifx\putwordDefspec\undefined \gdef\putwordDefspec{Special Form}\fi
+\ifx\putwordDefvar\undefined \gdef\putwordDefvar{Variable}\fi
+\ifx\putwordDefopt\undefined \gdef\putwordDefopt{User Option}\fi
+\ifx\putwordDeffunc\undefined \gdef\putwordDeffunc{Function}\fi
+
+% Give the space character the catcode for a space.
+\def\spaceisspace{\catcode`\ =10\relax}
+
+% Likewise for ^^M, the end of line character.
+\def\endlineisspace{\catcode13=10\relax}
+
+\chardef\dashChar = `\-
+\chardef\slashChar = `\/
+\chardef\underChar = `\_
+
+% Ignore a token.
+%
+\def\gobble#1{}
+
+% The following is used inside several \edef's.
+\def\makecsname#1{\expandafter\noexpand\csname#1\endcsname}
+
+% Hyphenation fixes.
+\hyphenation{
+ Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script
+ ap-pen-dix bit-map bit-maps
+ data-base data-bases eshell fall-ing half-way long-est man-u-script
+ man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm
+ par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces
+ spell-ing spell-ings
+ stand-alone strong-est time-stamp time-stamps which-ever white-space
+ wide-spread wrap-around
+}
+
+% Sometimes it is convenient to have everything in the transcript file
+% and nothing on the terminal. We don't just call \tracingall here,
+% since that produces some useless output on the terminal. We also make
+% some effort to order the tracing commands to reduce output in the log
+% file; cf. trace.sty in LaTeX.
+%
+\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}%
+\def\loggingall{%
+ \tracingstats2
+ \tracingpages1
+ \tracinglostchars2 % 2 gives us more in etex
+ \tracingparagraphs1
+ \tracingoutput1
+ \tracingmacros2
+ \tracingrestores1
+ \showboxbreadth\maxdimen \showboxdepth\maxdimen
+ \ifx\eTeXversion\thisisundefined\else % etex gives us more logging
+ \tracingscantokens1
+ \tracingifs1
+ \tracinggroups1
+ \tracingnesting2
+ \tracingassigns1
+ \fi
+ \tracingcommands3 % 3 gives us more in etex
+ \errorcontextlines16
+}%
+
+% @errormsg{MSG}. Do the index-like expansions on MSG, but if things
+% aren't perfect, it's not the end of the world, being an error message,
+% after all.
+%
+\def\errormsg{\begingroup \indexnofonts \doerrormsg}
+\def\doerrormsg#1{\errmessage{#1}}
+
+% add check for \lastpenalty to plain's definitions. If the last thing
+% we did was a \nobreak, we don't want to insert more space.
+%
+\def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount
+ \removelastskip\penalty-50\smallskip\fi\fi}
+\def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount
+ \removelastskip\penalty-100\medskip\fi\fi}
+\def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount
+ \removelastskip\penalty-200\bigskip\fi\fi}
+
+% Output routine
+%
+
+% For a final copy, take out the rectangles
+% that mark overfull boxes (in case you have decided
+% that the text looks ok even though it passes the margin).
+%
+\def\finalout{\overfullrule=0pt }
+
+\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines
+\newdimen\topandbottommargin \topandbottommargin=.75in
+
+% Output a mark which sets \thischapter, \thissection and \thiscolor.
+% We dump everything together because we only have one kind of mark.
+% This works because we only use \botmark / \topmark, not \firstmark.
+%
+% A mark contains a subexpression of the \ifcase ... \fi construct.
+% \get*marks macros below extract the needed part using \ifcase.
+%
+% Another complication is to let the user choose whether \thischapter
+% (\thissection) refers to the chapter (section) in effect at the top
+% of a page, or that at the bottom of a page.
+
+% \domark is called twice inside \chapmacro, to add one
+% mark before the section break, and one after.
+% In the second call \prevchapterdefs is the same as \currentchapterdefs,
+% and \prevsectiondefs is the same as \currentsectiondefs.
+% Then if the page is not broken at the mark, some of the previous
+% section appears on the page, and we can get the name of this section
+% from \firstmark for @everyheadingmarks top.
+% @everyheadingmarks bottom uses \botmark.
+%
+% See page 260 of The TeXbook.
+\def\domark{%
+ \toks0=\expandafter{\currentchapterdefs}%
+ \toks2=\expandafter{\currentsectiondefs}%
+ \toks4=\expandafter{\prevchapterdefs}%
+ \toks6=\expandafter{\prevsectiondefs}%
+ \toks8=\expandafter{\currentcolordefs}%
+ \mark{%
+ \the\toks0 \the\toks2 % 0: marks for @everyheadingmarks top
+ \noexpand\or \the\toks4 \the\toks6 % 1: for @everyheadingmarks bottom
+ \noexpand\else \the\toks8 % 2: color marks
+ }%
+}
+
+% \gettopheadingmarks, \getbottomheadingmarks,
+% \getcolormarks - extract needed part of mark.
+%
+% \topmark doesn't work for the very first chapter (after the title
+% page or the contents), so we use \firstmark there -- this gets us
+% the mark with the chapter defs, unless the user sneaks in, e.g.,
+% @setcolor (or @url, or @link, etc.) between @contents and the very
+% first @chapter.
+\def\gettopheadingmarks{%
+ \ifcase0\the\savedtopmark\fi
+ \ifx\thischapter\empty \ifcase0\firstmark\fi \fi
+}
+\def\getbottomheadingmarks{\ifcase1\botmark\fi}
+\def\getcolormarks{\ifcase2\the\savedtopmark\fi}
+
+% Avoid "undefined control sequence" errors.
+\def\currentchapterdefs{}
+\def\currentsectiondefs{}
+\def\currentsection{}
+\def\prevchapterdefs{}
+\def\prevsectiondefs{}
+\def\currentcolordefs{}
+
+% Margin to add to right of even pages, to left of odd pages.
+\newdimen\bindingoffset
+\newdimen\normaloffset
+\newdimen\txipagewidth \newdimen\txipageheight
+
+% Main output routine.
+%
+\chardef\PAGE = 255
+\newtoks\defaultoutput
+\defaultoutput = {\savetopmark\onepageout{\pagecontents\PAGE}}
+\output=\expandafter{\the\defaultoutput}
+
+\newbox\headlinebox
+\newbox\footlinebox
+
+% When outputting the double column layout for indices, an output routine
+% is run several times, which hides the original value of \topmark. This
+% can lead to a page heading being output and duplicating the chapter heading
+% of the index. Hence, save the contents of \topmark at the beginning of
+% the output routine. The saved contents are valid until we actually
+% \shipout a page.
+%
+% (We used to run a short output routine to actually set \topmark and
+% \firstmark to the right values, but if this was called with an empty page
+% containing whatsits for writing index entries, the whatsits would be thrown
+% away and the index auxiliary file would remain empty.)
+%
+\newtoks\savedtopmark
+\newif\iftopmarksaved
+\topmarksavedtrue
+\def\savetopmark{%
+ \iftopmarksaved\else
+ \global\savedtopmark=\expandafter{\topmark}%
+ \global\topmarksavedtrue
+ \fi
+}
+
+% \onepageout takes a vbox as an argument.
+% \shipout a vbox for a single page, adding an optional header, footer
+% and footnote. This also causes index entries for this page to be written
+% to the auxiliary files.
+%
+\def\onepageout#1{%
+ \hoffset=\normaloffset
+ %
+ \ifodd\pageno \advance\hoffset by \bindingoffset
+ \else \advance\hoffset by -\bindingoffset\fi
+ %
+ % Retrieve the information for the headings from the marks in the page,
+ % and call Plain TeX's \makeheadline and \makefootline, which use the
+ % values in \headline and \footline.
+ %
+ % This is used to check if we are on the first page of a chapter.
+ \ifcase1\the\savedtopmark\fi
+ \let\prevchaptername\thischaptername
+ \ifcase0\firstmark\fi
+ \let\curchaptername\thischaptername
+ %
+ \ifodd\pageno \getoddheadingmarks \else \getevenheadingmarks \fi
+ %
+ \ifx\curchaptername\prevchaptername
+ \let\thischapterheading\thischapter
+ \else
+ % \thischapterheading is the same as \thischapter except it is blank
+ % for the first page of a chapter. This is to prevent the chapter name
+ % being shown twice.
+ \def\thischapterheading{}%
+ \fi
+ %
+ % Common context changes for both heading and footing.
+ % Do this outside of the \shipout so @code etc. will be expanded in
+ % the headline as they should be, not taken literally (outputting ''code).
+ \def\commonheadfootline{\let\hsize=\txipagewidth \texinfochars}
+ %
+ \global\setbox\headlinebox = \vbox{\commonheadfootline \makeheadline}%
+ %
+ \ifodd\pageno \getoddfootingmarks \else \getevenfootingmarks \fi
+ \global\setbox\footlinebox = \vbox{\commonheadfootline \makefootline}%
+ %
+ {%
+ % Set context for writing to auxiliary files like index files.
+ % Have to do this stuff outside the \shipout because we want it to
+ % take effect in \write's, yet the group defined by the \vbox ends
+ % before the \shipout runs.
+ %
+ \atdummies % don't expand commands in the output.
+ \turnoffactive
+ \shipout\vbox{%
+ % Do this early so pdf references go to the beginning of the page.
+ \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi
+ %
+ \unvbox\headlinebox
+ \pagebody{#1}%
+ \ifdim\ht\footlinebox > 0pt
+ % Only leave this space if the footline is nonempty.
+ % (We lessened \vsize for it in \oddfootingyyy.)
+ % The \baselineskip=24pt in plain's \makefootline has no effect.
+ \vskip 24pt
+ \unvbox\footlinebox
+ \fi
+ %
+ }%
+ }%
+ \global\topmarksavedfalse
+ \advancepageno
+ \ifnum\outputpenalty>-20000 \else\dosupereject\fi
+}
+
+\newinsert\margin \dimen\margin=\maxdimen
+
+% Main part of page, including any footnotes
+\def\pagebody#1{\vbox to\txipageheight{\boxmaxdepth=\maxdepth #1}}
+{\catcode`\@ =11
+\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi
+% marginal hacks, juha@viisa.uucp (Juha Takala)
+\ifvoid\margin\else % marginal info is present
+ \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi
+\dimen@=\dp#1\relax \unvbox#1\relax
+\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi
+\ifr@ggedbottom \kern-\dimen@ \vfil \fi}
+}
+
+
+% Argument parsing
+
+% Parse an argument, then pass it to #1. The argument is the rest of
+% the input line (except we remove a trailing comment). #1 should be a
+% macro which expects an ordinary undelimited TeX argument.
+% For example, \def\foo{\parsearg\fooxxx}.
+%
+\def\parsearg{\parseargusing{}}
+\def\parseargusing#1#2{%
+ \def\argtorun{#2}%
+ \begingroup
+ \obeylines
+ \spaceisspace
+ #1%
+ \parseargline\empty% Insert the \empty token, see \finishparsearg below.
+}
+
+{\obeylines %
+ \gdef\parseargline#1^^M{%
+ \endgroup % End of the group started in \parsearg.
+ \argremovecomment #1\comment\ArgTerm%
+ }%
+}
+
+% First remove any @comment, then any @c comment. Pass the result on to
+% \argcheckspaces.
+\def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm}
+\def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm}
+
+% Each occurrence of `\^^M' or `<space>\^^M' is replaced by a single space.
+%
+% \argremovec might leave us with trailing space, e.g.,
+% @end itemize @c foo
+% This space token undergoes the same procedure and is eventually removed
+% by \finishparsearg.
+%
+\def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M}
+\def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M}
+\def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{%
+ \def\temp{#3}%
+ \ifx\temp\empty
+ % Do not use \next, perhaps the caller of \parsearg uses it; reuse \temp:
+ \let\temp\finishparsearg
+ \else
+ \let\temp\argcheckspaces
+ \fi
+ % Put the space token in:
+ \temp#1 #3\ArgTerm
+}
+
+% If a _delimited_ argument is enclosed in braces, they get stripped; so
+% to get _exactly_ the rest of the line, we had to prevent such situation.
+% We prepended an \empty token at the very beginning and we expand it now,
+% just before passing the control to \argtorun.
+% (Similarly, we have to think about #3 of \argcheckspacesY above: it is
+% either the null string, or it ends with \^^M---thus there is no danger
+% that a pair of braces would be stripped.
+%
+% But first, we have to remove the trailing space token.
+%
+\def\finishparsearg#1 \ArgTerm{\expandafter\argtorun\expandafter{#1}}
+
+
+% \parseargdef - define a command taking an argument on the line
+%
+% \parseargdef\foo{...}
+% is roughly equivalent to
+% \def\foo{\parsearg\Xfoo}
+% \def\Xfoo#1{...}
+\def\parseargdef#1{%
+ \expandafter \doparseargdef \csname\string#1\endcsname #1%
+}
+\def\doparseargdef#1#2{%
+ \def#2{\parsearg#1}%
+ \def#1##1%
+}
+
+% Several utility definitions with active space:
+{
+ \obeyspaces
+ \gdef\obeyedspace{ }
+
+ % Make each space character in the input produce a normal interword
+ % space in the output. Don't allow a line break at this space, as this
+ % is used only in environments like @example, where each line of input
+ % should produce a line of output anyway.
+ %
+ \gdef\sepspaces{\obeyspaces\let =\tie}
+
+ % If an index command is used in an @example environment, any spaces
+ % therein should become regular spaces in the raw index file, not the
+ % expansion of \tie (\leavevmode \penalty \@M \ ).
+ \gdef\unsepspaces{\let =\space}
+}
+
+
+\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next}
+
+% Define the framework for environments in texinfo.tex. It's used like this:
+%
+% \envdef\foo{...}
+% \def\Efoo{...}
+%
+% It's the responsibility of \envdef to insert \begingroup before the
+% actual body; @end closes the group after calling \Efoo. \envdef also
+% defines \thisenv, so the current environment is known; @end checks
+% whether the environment name matches. The \checkenv macro can also be
+% used to check whether the current environment is the one expected.
+%
+% Non-false conditionals (@iftex, @ifset) don't fit into this, so they
+% are not treated as environments; they don't open a group. (The
+% implementation of @end takes care not to call \endgroup in this
+% special case.)
+
+
+% At run-time, environments start with this:
+\def\startenvironment#1{\begingroup\def\thisenv{#1}}
+% initialize
+\let\thisenv\empty
+
+% ... but they get defined via ``\envdef\foo{...}'':
+\long\def\envdef#1#2{\def#1{\startenvironment#1#2}}
+\def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}}
+
+% Check whether we're in the right environment:
+\def\checkenv#1{%
+ \def\temp{#1}%
+ \ifx\thisenv\temp
+ \else
+ \badenverr
+ \fi
+}
+
+% Environment mismatch, #1 expected:
+\def\badenverr{%
+ \errhelp = \EMsimple
+ \errmessage{This command can appear only \inenvironment\temp,
+ not \inenvironment\thisenv}%
+}
+\def\inenvironment#1{%
+ \ifx#1\empty
+ outside of any environment%
+ \else
+ in environment \expandafter\string#1%
+ \fi
+}
+
+% @end foo executes the definition of \Efoo.
+% But first, it executes a specialized version of \checkenv
+%
+\parseargdef\end{%
+ \if 1\csname iscond.#1\endcsname
+ \else
+ % The general wording of \badenverr may not be ideal.
+ \expandafter\checkenv\csname#1\endcsname
+ \csname E#1\endcsname
+ \endgroup
+ \fi
+}
+
+\newhelp\EMsimple{Press RETURN to continue.}
+
+
+% Be sure we're in horizontal mode when doing a tie, since we make space
+% equivalent to this in @example-like environments. Otherwise, a space
+% at the beginning of a line will start with \penalty -- and
+% since \penalty is valid in vertical mode, we'd end up putting the
+% penalty on the vertical list instead of in the new paragraph.
+{\catcode`@ = 11
+ % Avoid using \@M directly, because that causes trouble
+ % if the definition is written into an index file.
+ \global\let\tiepenalty = \@M
+ \gdef\tie{\leavevmode\penalty\tiepenalty\ }
+}
+
+% @: forces normal size whitespace following.
+\def\:{\spacefactor=1000 }
+
+% @* forces a line break.
+\def\*{\unskip\hfil\break\hbox{}\ignorespaces}
+
+% @/ allows a line break.
+\let\/=\allowbreak
+
+% @. is an end-of-sentence period.
+\def\.{.\spacefactor=\endofsentencespacefactor\space}
+
+% @! is an end-of-sentence bang.
+\def\!{!\spacefactor=\endofsentencespacefactor\space}
+
+% @? is an end-of-sentence query.
+\def\?{?\spacefactor=\endofsentencespacefactor\space}
+
+% @frenchspacing on|off says whether to put extra space after punctuation.
+%
+\def\onword{on}
+\def\offword{off}
+%
+\parseargdef\frenchspacing{%
+ \def\temp{#1}%
+ \ifx\temp\onword \plainfrenchspacing
+ \else\ifx\temp\offword \plainnonfrenchspacing
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @frenchspacing option `\temp', must be on|off}%
+ \fi\fi
+}
+
+% @w prevents a word break. Without the \leavevmode, @w at the
+% beginning of a paragraph, when TeX is still in vertical mode, would
+% produce a whole line of output instead of starting the paragraph.
+\def\w#1{\leavevmode\hbox{#1}}
+
+% @group ... @end group forces ... to be all on one page, by enclosing
+% it in a TeX vbox. We use \vtop instead of \vbox to construct the box
+% to keep its height that of a normal line. According to the rules for
+% \topskip (p.114 of the TeXbook), the glue inserted is
+% max (\topskip - \ht (first item), 0). If that height is large,
+% therefore, no glue is inserted, and the space between the headline and
+% the text is small, which looks bad.
+%
+% Another complication is that the group might be very large. This can
+% cause the glue on the previous page to be unduly stretched, because it
+% does not have much material. In this case, it's better to add an
+% explicit \vfill so that the extra space is at the bottom. The
+% threshold for doing this is if the group is more than \vfilllimit
+% percent of a page (\vfilllimit can be changed inside of @tex).
+%
+\newbox\groupbox
+\def\vfilllimit{0.7}
+%
+\envdef\group{%
+ \ifnum\catcode`\^^M=\active \else
+ \errhelp = \groupinvalidhelp
+ \errmessage{@group invalid in context where filling is enabled}%
+ \fi
+ \startsavinginserts
+ %
+ \setbox\groupbox = \vtop\bgroup
+ % Do @comment since we are called inside an environment such as
+ % @example, where each end-of-line in the input causes an
+ % end-of-line in the output. We don't want the end-of-line after
+ % the `@group' to put extra space in the output. Since @group
+ % should appear on a line by itself (according to the Texinfo
+ % manual), we don't worry about eating any user text.
+ \comment
+}
+%
+% The \vtop produces a box with normal height and large depth; thus, TeX puts
+% \baselineskip glue before it, and (when the next line of text is done)
+% \lineskip glue after it. Thus, space below is not quite equal to space
+% above. But it's pretty close.
+\def\Egroup{%
+ % To get correct interline space between the last line of the group
+ % and the first line afterwards, we have to propagate \prevdepth.
+ \endgraf % Not \par, as it may have been set to \lisppar.
+ \global\dimen1 = \prevdepth
+ \egroup % End the \vtop.
+ \addgroupbox
+ \prevdepth = \dimen1
+ \checkinserts
+}
+
+\def\addgroupbox{
+ % \dimen0 is the vertical size of the group's box.
+ \dimen0 = \ht\groupbox \advance\dimen0 by \dp\groupbox
+ % \dimen2 is how much space is left on the page (more or less).
+ \dimen2 = \txipageheight \advance\dimen2 by -\pagetotal
+ % if the group doesn't fit on the current page, and it's a big big
+ % group, force a page break.
+ \ifdim \dimen0 > \dimen2
+ \ifdim \pagetotal < \vfilllimit\txipageheight
+ \page
+ \fi
+ \fi
+ \box\groupbox
+}
+
+%
+% TeX puts in an \escapechar (i.e., `@') at the beginning of the help
+% message, so this ends up printing `@group can only ...'.
+%
+\newhelp\groupinvalidhelp{%
+group can only be used in environments such as @example,^^J%
+where each line of input produces a line of output.}
+
+% @need space-in-mils
+% forces a page break if there is not space-in-mils remaining.
+
+\newdimen\mil \mil=0.001in
+
+\parseargdef\need{%
+ % Ensure vertical mode, so we don't make a big box in the middle of a
+ % paragraph.
+ \par
+ %
+ % If the @need value is less than one line space, it's useless.
+ \dimen0 = #1\mil
+ \dimen2 = \ht\strutbox
+ \advance\dimen2 by \dp\strutbox
+ \ifdim\dimen0 > \dimen2
+ %
+ % Do a \strut just to make the height of this box be normal, so the
+ % normal leading is inserted relative to the preceding line.
+ % And a page break here is fine.
+ \vtop to #1\mil{\strut\vfil}%
+ %
+ % TeX does not even consider page breaks if a penalty added to the
+ % main vertical list is 10000 or more. But in order to see if the
+ % empty box we just added fits on the page, we must make it consider
+ % page breaks. On the other hand, we don't want to actually break the
+ % page after the empty box. So we use a penalty of 9999.
+ %
+ % There is an extremely small chance that TeX will actually break the
+ % page at this \penalty, if there are no other feasible breakpoints in
+ % sight. (If the user is using lots of big @group commands, which
+ % almost-but-not-quite fill up a page, TeX will have a hard time doing
+ % good page breaking, for example.) However, I could not construct an
+ % example where a page broke at this \penalty; if it happens in a real
+ % document, then we can reconsider our strategy.
+ \penalty9999
+ %
+ % Back up by the size of the box, whether we did a page break or not.
+ \kern -#1\mil
+ %
+ % Do not allow a page break right after this kern.
+ \nobreak
+ \fi
+}
+
+% @br forces paragraph break (and is undocumented).
+
+\let\br = \par
+
+% @page forces the start of a new page.
+%
+\def\page{\par\vfill\supereject}
+
+% @exdent text....
+% outputs text on separate line in roman font, starting at standard page margin
+
+% This records the amount of indent in the innermost environment.
+% That's how much \exdent should take out.
+\newskip\exdentamount
+
+% This defn is used inside fill environments such as @defun.
+\parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}
+
+% This defn is used inside nofill environments such as @example.
+\parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount
+ \leftline{\hskip\leftskip{\rm#1}}}}
+
+% @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current
+% paragraph. For more general purposes, use the \margin insertion
+% class. WHICH is `l' or `r'. Not documented, written for gawk manual.
+%
+\newskip\inmarginspacing \inmarginspacing=1cm
+\def\strutdepth{\dp\strutbox}
+%
+\def\doinmargin#1#2{\strut\vadjust{%
+ \nobreak
+ \kern-\strutdepth
+ \vtop to \strutdepth{%
+ \baselineskip=\strutdepth
+ \vss
+ % if you have multiple lines of stuff to put here, you'll need to
+ % make the vbox yourself of the appropriate size.
+ \ifx#1l%
+ \llap{\ignorespaces #2\hskip\inmarginspacing}%
+ \else
+ \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}%
+ \fi
+ \null
+ }%
+}}
+\def\inleftmargin{\doinmargin l}
+\def\inrightmargin{\doinmargin r}
+%
+% @inmargin{TEXT [, RIGHT-TEXT]}
+% (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right;
+% else use TEXT for both).
+%
+\def\inmargin#1{\parseinmargin #1,,\finish}
+\def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing.
+ \setbox0 = \hbox{\ignorespaces #2}%
+ \ifdim\wd0 > 0pt
+ \def\lefttext{#1}% have both texts
+ \def\righttext{#2}%
+ \else
+ \def\lefttext{#1}% have only one text
+ \def\righttext{#1}%
+ \fi
+ %
+ \ifodd\pageno
+ \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin
+ \else
+ \def\temp{\inleftmargin\lefttext}%
+ \fi
+ \temp
+}
+
+% @include FILE -- \input text of FILE.
+%
+\def\include{\parseargusing\filenamecatcodes\includezzz}
+\def\includezzz#1{%
+ \pushthisfilestack
+ \def\thisfile{#1}%
+ {%
+ \makevalueexpandable % we want to expand any @value in FILE.
+ \turnoffactive % and allow special characters in the expansion
+ \indexnofonts % Allow `@@' and other weird things in file names.
+ \wlog{texinfo.tex: doing @include of #1^^J}%
+ \edef\temp{\noexpand\input #1 }%
+ %
+ % This trickery is to read FILE outside of a group, in case it makes
+ % definitions, etc.
+ \expandafter
+ }\temp
+ \popthisfilestack
+}
+\def\filenamecatcodes{%
+ \catcode`\\=\other
+ \catcode`~=\other
+ \catcode`^=\other
+ \catcode`_=\other
+ \catcode`|=\other
+ \catcode`<=\other
+ \catcode`>=\other
+ \catcode`+=\other
+ \catcode`-=\other
+ \catcode`\`=\other
+ \catcode`\'=\other
+}
+
+\def\pushthisfilestack{%
+ \expandafter\pushthisfilestackX\popthisfilestack\StackTerm
+}
+\def\pushthisfilestackX{%
+ \expandafter\pushthisfilestackY\thisfile\StackTerm
+}
+\def\pushthisfilestackY #1\StackTerm #2\StackTerm {%
+ \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}%
+}
+
+\def\popthisfilestack{\errthisfilestackempty}
+\def\errthisfilestackempty{\errmessage{Internal error:
+ the stack of filenames is empty.}}
+%
+\def\thisfile{}
+
+% @center line
+% outputs that line, centered.
+%
+\parseargdef\center{%
+ \ifhmode
+ \let\centersub\centerH
+ \else
+ \let\centersub\centerV
+ \fi
+ \centersub{\hfil \ignorespaces#1\unskip \hfil}%
+ \let\centersub\relax % don't let the definition persist, just in case
+}
+\def\centerH#1{{%
+ \hfil\break
+ \advance\hsize by -\leftskip
+ \advance\hsize by -\rightskip
+ \line{#1}%
+ \break
+}}
+%
+\newcount\centerpenalty
+\def\centerV#1{%
+ % The idea here is the same as in \startdefun, \cartouche, etc.: if
+ % @center is the first thing after a section heading, we need to wipe
+ % out the negative parskip inserted by \sectionheading, but still
+ % prevent a page break here.
+ \centerpenalty = \lastpenalty
+ \ifnum\centerpenalty>10000 \vskip\parskip \fi
+ \ifnum\centerpenalty>9999 \penalty\centerpenalty \fi
+ \line{\kern\leftskip #1\kern\rightskip}%
+}
+
+% @sp n outputs n lines of vertical space
+%
+\parseargdef\sp{\vskip #1\baselineskip}
+
+% @comment ...line which is ignored...
+% @c is the same as @comment
+% @ignore ... @end ignore is another way to write a comment
+
+
+\def\c{\begingroup \catcode`\^^M=\active%
+\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other%
+\cxxx}
+{\catcode`\^^M=\active \gdef\cxxx#1^^M{\endgroup}}
+%
+\let\comment\c
+
+% @paragraphindent NCHARS
+% We'll use ems for NCHARS, close enough.
+% NCHARS can also be the word `asis' or `none'.
+% We cannot feasibly implement @paragraphindent asis, though.
+%
+\def\asisword{asis} % no translation, these are keywords
+\def\noneword{none}
+%
+\parseargdef\paragraphindent{%
+ \def\temp{#1}%
+ \ifx\temp\asisword
+ \else
+ \ifx\temp\noneword
+ \defaultparindent = 0pt
+ \else
+ \defaultparindent = #1em
+ \fi
+ \fi
+ \parindent = \defaultparindent
+}
+
+% @exampleindent NCHARS
+% We'll use ems for NCHARS like @paragraphindent.
+% It seems @exampleindent asis isn't necessary, but
+% I preserve it to make it similar to @paragraphindent.
+\parseargdef\exampleindent{%
+ \def\temp{#1}%
+ \ifx\temp\asisword
+ \else
+ \ifx\temp\noneword
+ \lispnarrowing = 0pt
+ \else
+ \lispnarrowing = #1em
+ \fi
+ \fi
+}
+
+% @firstparagraphindent WORD
+% If WORD is `none', then suppress indentation of the first paragraph
+% after a section heading. If WORD is `insert', then do indent at such
+% paragraphs.
+%
+% The paragraph indentation is suppressed or not by calling
+% \suppressfirstparagraphindent, which the sectioning commands do.
+% We switch the definition of this back and forth according to WORD.
+% By default, we suppress indentation.
+%
+\def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent}
+\def\insertword{insert}
+%
+\parseargdef\firstparagraphindent{%
+ \def\temp{#1}%
+ \ifx\temp\noneword
+ \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent
+ \else\ifx\temp\insertword
+ \let\suppressfirstparagraphindent = \relax
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @firstparagraphindent option `\temp'}%
+ \fi\fi
+}
+
+% Here is how we actually suppress indentation. Redefine \everypar to
+% \kern backwards by \parindent, and then reset itself to empty.
+%
+% We also make \indent itself not actually do anything until the next
+% paragraph.
+%
+\gdef\dosuppressfirstparagraphindent{%
+ \gdef\indent {\restorefirstparagraphindent \indent}%
+ \gdef\noindent{\restorefirstparagraphindent \noindent}%
+ \global\everypar = {\kern -\parindent \restorefirstparagraphindent}%
+}
+%
+\gdef\restorefirstparagraphindent{%
+ \global\let\indent = \ptexindent
+ \global\let\noindent = \ptexnoindent
+ \global\everypar = {}%
+}
+
+
+% @refill is a no-op.
+\let\refill=\relax
+
+% @setfilename INFO-FILENAME - ignored
+\let\setfilename=\comment
+
+% @bye.
+\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend}
+
+
+\message{pdf,}
+% adobe `portable' document format
+\newcount\tempnum
+\newcount\lnkcount
+\newtoks\filename
+\newcount\filenamelength
+\newcount\pgn
+\newtoks\toksA
+\newtoks\toksB
+\newtoks\toksC
+\newtoks\toksD
+\newbox\boxA
+\newbox\boxB
+\newcount\countA
+\newif\ifpdf
+\newif\ifpdfmakepagedest
+
+%
+% For LuaTeX
+%
+
+\newif\iftxiuseunicodedestname
+\txiuseunicodedestnamefalse % For pdfTeX etc.
+
+\ifx\luatexversion\thisisundefined
+\else
+ % Use Unicode destination names
+ \txiuseunicodedestnametrue
+ % Escape PDF strings with converting UTF-16 from UTF-8
+ \begingroup
+ \catcode`\%=12
+ \directlua{
+ function UTF16oct(str)
+ tex.sprint(string.char(0x5c) .. '376' .. string.char(0x5c) .. '377')
+ for c in string.utfvalues(str) do
+ if c < 0x10000 then
+ tex.sprint(
+ string.format(string.char(0x5c) .. string.char(0x25) .. '03o' ..
+ string.char(0x5c) .. string.char(0x25) .. '03o',
+ math.floor(c / 256), math.floor(c % 256)))
+ else
+ c = c - 0x10000
+ local c_hi = c / 1024 + 0xd800
+ local c_lo = c % 1024 + 0xdc00
+ tex.sprint(
+ string.format(string.char(0x5c) .. string.char(0x25) .. '03o' ..
+ string.char(0x5c) .. string.char(0x25) .. '03o' ..
+ string.char(0x5c) .. string.char(0x25) .. '03o' ..
+ string.char(0x5c) .. string.char(0x25) .. '03o',
+ math.floor(c_hi / 256), math.floor(c_hi % 256),
+ math.floor(c_lo / 256), math.floor(c_lo % 256)))
+ end
+ end
+ end
+ }
+ \endgroup
+ \def\pdfescapestrutfsixteen#1{\directlua{UTF16oct('\luaescapestring{#1}')}}
+ % Escape PDF strings without converting
+ \begingroup
+ \directlua{
+ function PDFescstr(str)
+ for c in string.bytes(str) do
+ if c <= 0x20 or c >= 0x80 or c == 0x28 or c == 0x29 or c == 0x5c then
+ tex.sprint(-2,
+ string.format(string.char(0x5c) .. string.char(0x25) .. '03o',
+ c))
+ else
+ tex.sprint(-2, string.char(c))
+ end
+ end
+ end
+ }
+ % The -2 in the arguments here gives all the input to TeX catcode 12
+ % (other) or 10 (space), preventing undefined control sequence errors. See
+ % https://lists.gnu.org/archive/html/bug-texinfo/2019-08/msg00031.html
+ %
+ \endgroup
+ \def\pdfescapestring#1{\directlua{PDFescstr('\luaescapestring{#1}')}}
+ \ifnum\luatexversion>84
+ % For LuaTeX >= 0.85
+ \def\pdfdest{\pdfextension dest}
+ \let\pdfoutput\outputmode
+ \def\pdfliteral{\pdfextension literal}
+ \def\pdfcatalog{\pdfextension catalog}
+ \def\pdftexversion{\numexpr\pdffeedback version\relax}
+ \let\pdfximage\saveimageresource
+ \let\pdfrefximage\useimageresource
+ \let\pdflastximage\lastsavedimageresourceindex
+ \def\pdfendlink{\pdfextension endlink\relax}
+ \def\pdfoutline{\pdfextension outline}
+ \def\pdfstartlink{\pdfextension startlink}
+ \def\pdffontattr{\pdfextension fontattr}
+ \def\pdfobj{\pdfextension obj}
+ \def\pdflastobj{\numexpr\pdffeedback lastobj\relax}
+ \let\pdfpagewidth\pagewidth
+ \let\pdfpageheight\pageheight
+ \edef\pdfhorigin{\pdfvariable horigin}
+ \edef\pdfvorigin{\pdfvariable vorigin}
+ \fi
+\fi
+
+% when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1
+% can be set). So we test for \relax and 0 as well as being undefined.
+\ifx\pdfoutput\thisisundefined
+\else
+ \ifx\pdfoutput\relax
+ \else
+ \ifcase\pdfoutput
+ \else
+ \pdftrue
+ \fi
+ \fi
+\fi
+
+\newif\ifpdforxetex
+\pdforxetexfalse
+\ifpdf
+ \pdforxetextrue
+\fi
+\ifx\XeTeXrevision\thisisundefined\else
+ \pdforxetextrue
+\fi
+
+
+% PDF uses PostScript string constants for the names of xref targets,
+% for display in the outlines, and in other places. Thus, we have to
+% double any backslashes. Otherwise, a name like "\node" will be
+% interpreted as a newline (\n), followed by o, d, e. Not good.
+%
+% See http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html and
+% related messages. The final outcome is that it is up to the TeX user
+% to double the backslashes and otherwise make the string valid, so
+% that's what we do. pdftex 1.30.0 (ca.2005) introduced a primitive to
+% do this reliably, so we use it.
+
+% #1 is a control sequence in which to do the replacements,
+% which we \xdef.
+\def\txiescapepdf#1{%
+ \ifx\pdfescapestring\thisisundefined
+ % No primitive available; should we give a warning or log?
+ % Many times it won't matter.
+ \xdef#1{#1}%
+ \else
+ % The expandable \pdfescapestring primitive escapes parentheses,
+ % backslashes, and other special chars.
+ \xdef#1{\pdfescapestring{#1}}%
+ \fi
+}
+\def\txiescapepdfutfsixteen#1{%
+ \ifx\pdfescapestrutfsixteen\thisisundefined
+ % No UTF-16 converting macro available.
+ \txiescapepdf{#1}%
+ \else
+ \xdef#1{\pdfescapestrutfsixteen{#1}}%
+ \fi
+}
+
+\newhelp\nopdfimagehelp{Texinfo supports .png, .jpg, .jpeg, and .pdf images
+with PDF output, and none of those formats could be found. (.eps cannot
+be supported due to the design of the PDF format; use regular TeX (DVI
+output) for that.)}
+
+\ifpdf
+ %
+ % Color manipulation macros using ideas from pdfcolor.tex,
+ % except using rgb instead of cmyk; the latter is said to render as a
+ % very dark gray on-screen and a very dark halftone in print, instead
+ % of actual black. The dark red here is dark enough to print on paper as
+ % nearly black, but still distinguishable for online viewing. We use
+ % black by default, though.
+ \def\rgbDarkRed{0.50 0.09 0.12}
+ \def\rgbBlack{0 0 0}
+ %
+ % rg sets the color for filling (usual text, etc.);
+ % RG sets the color for stroking (thin rules, e.g., normal _'s).
+ \def\pdfsetcolor#1{\pdfliteral{#1 rg #1 RG}}
+ %
+ % Set color, and create a mark which defines \thiscolor accordingly,
+ % so that \makeheadline knows which color to restore.
+ \def\setcolor#1{%
+ \xdef\currentcolordefs{\gdef\noexpand\thiscolor{#1}}%
+ \domark
+ \pdfsetcolor{#1}%
+ }
+ %
+ \def\maincolor{\rgbBlack}
+ \pdfsetcolor{\maincolor}
+ \edef\thiscolor{\maincolor}
+ \def\currentcolordefs{}
+ %
+ \def\makefootline{%
+ \baselineskip24pt
+ \line{\pdfsetcolor{\maincolor}\the\footline}%
+ }
+ %
+ \def\makeheadline{%
+ \vbox to 0pt{%
+ \vskip-22.5pt
+ \line{%
+ \vbox to8.5pt{}%
+ % Extract \thiscolor definition from the marks.
+ \getcolormarks
+ % Typeset the headline with \maincolor, then restore the color.
+ \pdfsetcolor{\maincolor}\the\headline\pdfsetcolor{\thiscolor}%
+ }%
+ \vss
+ }%
+ \nointerlineskip
+ }
+ %
+ %
+ \pdfcatalog{/PageMode /UseOutlines}
+ %
+ % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto).
+ \def\dopdfimage#1#2#3{%
+ \def\pdfimagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}%
+ \def\pdfimageheight{#3}\setbox2 = \hbox{\ignorespaces #3}%
+ %
+ % pdftex (and the PDF format) support .pdf, .png, .jpg (among
+ % others). Let's try in that order, PDF first since if
+ % someone has a scalable image, presumably better to use that than a
+ % bitmap.
+ \let\pdfimgext=\empty
+ \begingroup
+ \openin 1 #1.pdf \ifeof 1
+ \openin 1 #1.PDF \ifeof 1
+ \openin 1 #1.png \ifeof 1
+ \openin 1 #1.jpg \ifeof 1
+ \openin 1 #1.jpeg \ifeof 1
+ \openin 1 #1.JPG \ifeof 1
+ \errhelp = \nopdfimagehelp
+ \errmessage{Could not find image file #1 for pdf}%
+ \else \gdef\pdfimgext{JPG}%
+ \fi
+ \else \gdef\pdfimgext{jpeg}%
+ \fi
+ \else \gdef\pdfimgext{jpg}%
+ \fi
+ \else \gdef\pdfimgext{png}%
+ \fi
+ \else \gdef\pdfimgext{PDF}%
+ \fi
+ \else \gdef\pdfimgext{pdf}%
+ \fi
+ \closein 1
+ \endgroup
+ %
+ % without \immediate, ancient pdftex seg faults when the same image is
+ % included twice. (Version 3.14159-pre-1.0-unofficial-20010704.)
+ \ifnum\pdftexversion < 14
+ \immediate\pdfimage
+ \else
+ \immediate\pdfximage
+ \fi
+ \ifdim \wd0 >0pt width \pdfimagewidth \fi
+ \ifdim \wd2 >0pt height \pdfimageheight \fi
+ \ifnum\pdftexversion<13
+ #1.\pdfimgext
+ \else
+ {#1.\pdfimgext}%
+ \fi
+ \ifnum\pdftexversion < 14 \else
+ \pdfrefximage \pdflastximage
+ \fi}
+ %
+ \def\setpdfdestname#1{{%
+ % We have to set dummies so commands such as @code, and characters
+ % such as \, aren't expanded when present in a section title.
+ \indexnofonts
+ \makevalueexpandable
+ \turnoffactive
+ \iftxiuseunicodedestname
+ \ifx \declaredencoding \latone
+ % Pass through Latin-1 characters.
+ % LuaTeX with byte wise I/O converts Latin-1 characters to Unicode.
+ \else
+ \ifx \declaredencoding \utfeight
+ % Pass through Unicode characters.
+ \else
+ % Use ASCII approximations in destination names.
+ \passthroughcharsfalse
+ \fi
+ \fi
+ \else
+ % Use ASCII approximations in destination names.
+ \passthroughcharsfalse
+ \fi
+ \def\pdfdestname{#1}%
+ \txiescapepdf\pdfdestname
+ }}
+ %
+ \def\setpdfoutlinetext#1{{%
+ \indexnofonts
+ \makevalueexpandable
+ \turnoffactive
+ \ifx \declaredencoding \latone
+ % The PDF format can use an extended form of Latin-1 in bookmark
+ % strings. See Appendix D of the PDF Reference, Sixth Edition, for
+ % the "PDFDocEncoding".
+ \passthroughcharstrue
+ % Pass through Latin-1 characters.
+ % LuaTeX: Convert to Unicode
+ % pdfTeX: Use Latin-1 as PDFDocEncoding
+ \def\pdfoutlinetext{#1}%
+ \else
+ \ifx \declaredencoding \utfeight
+ \ifx\luatexversion\thisisundefined
+ % For pdfTeX with UTF-8.
+ % TODO: the PDF format can use UTF-16 in bookmark strings,
+ % but the code for this isn't done yet.
+ % Use ASCII approximations.
+ \passthroughcharsfalse
+ \def\pdfoutlinetext{#1}%
+ \else
+ % For LuaTeX with UTF-8.
+ % Pass through Unicode characters for title texts.
+ \passthroughcharstrue
+ \def\pdfoutlinetext{#1}%
+ \fi
+ \else
+ % For non-Latin-1 or non-UTF-8 encodings.
+ % Use ASCII approximations.
+ \passthroughcharsfalse
+ \def\pdfoutlinetext{#1}%
+ \fi
+ \fi
+ % LuaTeX: Convert to UTF-16
+ % pdfTeX: Use Latin-1 as PDFDocEncoding
+ \txiescapepdfutfsixteen\pdfoutlinetext
+ }}
+ %
+ \def\pdfmkdest#1{%
+ \setpdfdestname{#1}%
+ \safewhatsit{\pdfdest name{\pdfdestname} xyz}%
+ }
+ %
+ % used to mark target names; must be expandable.
+ \def\pdfmkpgn#1{#1}
+ %
+ % by default, use black for everything.
+ \def\urlcolor{\rgbBlack}
+ \def\linkcolor{\rgbBlack}
+ \def\endlink{\setcolor{\maincolor}\pdfendlink}
+ %
+ % Adding outlines to PDF; macros for calculating structure of outlines
+ % come from Petr Olsak
+ \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0%
+ \else \csname#1\endcsname \fi}
+ \def\advancenumber#1{\tempnum=\expnumber{#1}\relax
+ \advance\tempnum by 1
+ \expandafter\xdef\csname#1\endcsname{\the\tempnum}}
+ %
+ % #1 is the section text, which is what will be displayed in the
+ % outline by the pdf viewer. #2 is the pdf expression for the number
+ % of subentries (or empty, for subsubsections). #3 is the node text,
+ % which might be empty if this toc entry had no corresponding node.
+ % #4 is the page number
+ %
+ \def\dopdfoutline#1#2#3#4{%
+ % Generate a link to the node text if that exists; else, use the
+ % page number. We could generate a destination for the section
+ % text in the case where a section has no node, but it doesn't
+ % seem worth the trouble, since most documents are normally structured.
+ \setpdfoutlinetext{#1}
+ \setpdfdestname{#3}
+ \ifx\pdfdestname\empty
+ \def\pdfdestname{#4}%
+ \fi
+ %
+ \pdfoutline goto name{\pdfmkpgn{\pdfdestname}}#2{\pdfoutlinetext}%
+ }
+ %
+ \def\pdfmakeoutlines{%
+ \begingroup
+ % Read toc silently, to get counts of subentries for \pdfoutline.
+ \def\partentry##1##2##3##4{}% ignore parts in the outlines
+ \def\numchapentry##1##2##3##4{%
+ \def\thischapnum{##2}%
+ \def\thissecnum{0}%
+ \def\thissubsecnum{0}%
+ }%
+ \def\numsecentry##1##2##3##4{%
+ \advancenumber{chap\thischapnum}%
+ \def\thissecnum{##2}%
+ \def\thissubsecnum{0}%
+ }%
+ \def\numsubsecentry##1##2##3##4{%
+ \advancenumber{sec\thissecnum}%
+ \def\thissubsecnum{##2}%
+ }%
+ \def\numsubsubsecentry##1##2##3##4{%
+ \advancenumber{subsec\thissubsecnum}%
+ }%
+ \def\thischapnum{0}%
+ \def\thissecnum{0}%
+ \def\thissubsecnum{0}%
+ %
+ % use \def rather than \let here because we redefine \chapentry et
+ % al. a second time, below.
+ \def\appentry{\numchapentry}%
+ \def\appsecentry{\numsecentry}%
+ \def\appsubsecentry{\numsubsecentry}%
+ \def\appsubsubsecentry{\numsubsubsecentry}%
+ \def\unnchapentry{\numchapentry}%
+ \def\unnsecentry{\numsecentry}%
+ \def\unnsubsecentry{\numsubsecentry}%
+ \def\unnsubsubsecentry{\numsubsubsecentry}%
+ \readdatafile{toc}%
+ %
+ % Read toc second time, this time actually producing the outlines.
+ % The `-' means take the \expnumber as the absolute number of
+ % subentries, which we calculated on our first read of the .toc above.
+ %
+ % We use the node names as the destinations.
+ \def\numchapentry##1##2##3##4{%
+ \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}%
+ \def\numsecentry##1##2##3##4{%
+ \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}%
+ \def\numsubsecentry##1##2##3##4{%
+ \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}%
+ \def\numsubsubsecentry##1##2##3##4{% count is always zero
+ \dopdfoutline{##1}{}{##3}{##4}}%
+ %
+ % PDF outlines are displayed using system fonts, instead of
+ % document fonts. Therefore we cannot use special characters,
+ % since the encoding is unknown. For example, the eogonek from
+ % Latin 2 (0xea) gets translated to a | character. Info from
+ % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100.
+ %
+ % TODO this right, we have to translate 8-bit characters to
+ % their "best" equivalent, based on the @documentencoding. Too
+ % much work for too little return. Just use the ASCII equivalents
+ % we use for the index sort strings.
+ %
+ \indexnofonts
+ \setupdatafile
+ % We can have normal brace characters in the PDF outlines, unlike
+ % Texinfo index files. So set that up.
+ \def\{{\lbracecharliteral}%
+ \def\}{\rbracecharliteral}%
+ \catcode`\\=\active \otherbackslash
+ \input \tocreadfilename
+ \endgroup
+ }
+ {\catcode`[=1 \catcode`]=2
+ \catcode`{=\other \catcode`}=\other
+ \gdef\lbracecharliteral[{]%
+ \gdef\rbracecharliteral[}]%
+ ]
+ %
+ \def\skipspaces#1{\def\PP{#1}\def\D{|}%
+ \ifx\PP\D\let\nextsp\relax
+ \else\let\nextsp\skipspaces
+ \addtokens{\filename}{\PP}%
+ \advance\filenamelength by 1
+ \fi
+ \nextsp}
+ \def\getfilename#1{%
+ \filenamelength=0
+ % If we don't expand the argument now, \skipspaces will get
+ % snagged on things like "@value{foo}".
+ \edef\temp{#1}%
+ \expandafter\skipspaces\temp|\relax
+ }
+ \ifnum\pdftexversion < 14
+ \let \startlink \pdfannotlink
+ \else
+ \let \startlink \pdfstartlink
+ \fi
+ % make a live url in pdf output.
+ \def\pdfurl#1{%
+ \begingroup
+ % it seems we really need yet another set of dummies; have not
+ % tried to figure out what each command should do in the context
+ % of @url. for now, just make @/ a no-op, that's the only one
+ % people have actually reported a problem with.
+ %
+ \normalturnoffactive
+ \def\@{@}%
+ \let\/=\empty
+ \makevalueexpandable
+ % do we want to go so far as to use \indexnofonts instead of just
+ % special-casing \var here?
+ \def\var##1{##1}%
+ %
+ \leavevmode\setcolor{\urlcolor}%
+ \startlink attr{/Border [0 0 0]}%
+ user{/Subtype /Link /A << /S /URI /URI (#1) >>}%
+ \endgroup}
+ % \pdfgettoks - Surround page numbers in #1 with @pdflink. #1 may
+ % be a simple number, or a list of numbers in the case of an index
+ % entry.
+ \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}}
+ \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks}
+ \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks}
+ \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}}
+ \def\maketoks{%
+ \expandafter\poptoks\the\toksA|ENDTOKS|\relax
+ \ifx\first0\adn0
+ \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3
+ \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6
+ \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9
+ \else
+ \ifnum0=\countA\else\makelink\fi
+ \ifx\first.\let\next=\done\else
+ \let\next=\maketoks
+ \addtokens{\toksB}{\the\toksD}
+ \ifx\first,\addtokens{\toksB}{\space}\fi
+ \fi
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+ \next}
+ \def\makelink{\addtokens{\toksB}%
+ {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0}
+ \def\pdflink#1{%
+ \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}}
+ \setcolor{\linkcolor}#1\endlink}
+ \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st}
+\else
+ % non-pdf mode
+ \let\pdfmkdest = \gobble
+ \let\pdfurl = \gobble
+ \let\endlink = \relax
+ \let\setcolor = \gobble
+ \let\pdfsetcolor = \gobble
+ \let\pdfmakeoutlines = \relax
+\fi % \ifx\pdfoutput
+
+%
+% For XeTeX
+%
+\ifx\XeTeXrevision\thisisundefined
+\else
+ %
+ % XeTeX version check
+ %
+ \ifnum\strcmp{\the\XeTeXversion\XeTeXrevision}{0.99996}>-1
+ % TeX Live 2016 contains XeTeX 0.99996 and xdvipdfmx 20160307.
+ % It can use the `dvipdfmx:config' special (from TeX Live SVN r40941).
+ % For avoiding PDF destination name replacement, we use this special
+ % instead of xdvipdfmx's command line option `-C 0x0010'.
+ \special{dvipdfmx:config C 0x0010}
+ % XeTeX 0.99995+ comes with xdvipdfmx 20160307+.
+ % It can handle Unicode destination names for PDF.
+ \txiuseunicodedestnametrue
+ \else
+ % XeTeX < 0.99996 (TeX Live < 2016) cannot use the
+ % `dvipdfmx:config' special.
+ % So for avoiding PDF destination name replacement,
+ % xdvipdfmx's command line option `-C 0x0010' is necessary.
+ %
+ % XeTeX < 0.99995 can not handle Unicode destination names for PDF
+ % because xdvipdfmx 20150315 has a UTF-16 conversion issue.
+ % It is fixed by xdvipdfmx 20160106 (TeX Live SVN r39753).
+ \txiuseunicodedestnamefalse
+ \fi
+ %
+ % Color support
+ %
+ \def\rgbDarkRed{0.50 0.09 0.12}
+ \def\rgbBlack{0 0 0}
+ %
+ \def\pdfsetcolor#1{\special{pdf:scolor [#1]}}
+ %
+ % Set color, and create a mark which defines \thiscolor accordingly,
+ % so that \makeheadline knows which color to restore.
+ \def\setcolor#1{%
+ \xdef\currentcolordefs{\gdef\noexpand\thiscolor{#1}}%
+ \domark
+ \pdfsetcolor{#1}%
+ }
+ %
+ \def\maincolor{\rgbBlack}
+ \pdfsetcolor{\maincolor}
+ \edef\thiscolor{\maincolor}
+ \def\currentcolordefs{}
+ %
+ \def\makefootline{%
+ \baselineskip24pt
+ \line{\pdfsetcolor{\maincolor}\the\footline}%
+ }
+ %
+ \def\makeheadline{%
+ \vbox to 0pt{%
+ \vskip-22.5pt
+ \line{%
+ \vbox to8.5pt{}%
+ % Extract \thiscolor definition from the marks.
+ \getcolormarks
+ % Typeset the headline with \maincolor, then restore the color.
+ \pdfsetcolor{\maincolor}\the\headline\pdfsetcolor{\thiscolor}%
+ }%
+ \vss
+ }%
+ \nointerlineskip
+ }
+ %
+ % PDF outline support
+ %
+ % Emulate pdfTeX primitive
+ \def\pdfdest name#1 xyz{%
+ \special{pdf:dest (#1) [@thispage /XYZ @xpos @ypos null]}%
+ }
+ %
+ \def\setpdfdestname#1{{%
+ % We have to set dummies so commands such as @code, and characters
+ % such as \, aren't expanded when present in a section title.
+ \indexnofonts
+ \makevalueexpandable
+ \turnoffactive
+ \iftxiuseunicodedestname
+ % Pass through Unicode characters.
+ \else
+ % Use ASCII approximations in destination names.
+ \passthroughcharsfalse
+ \fi
+ \def\pdfdestname{#1}%
+ \txiescapepdf\pdfdestname
+ }}
+ %
+ \def\setpdfoutlinetext#1{{%
+ \turnoffactive
+ % Always use Unicode characters in title texts.
+ \def\pdfoutlinetext{#1}%
+ % For XeTeX, xdvipdfmx converts to UTF-16.
+ % So we do not convert.
+ \txiescapepdf\pdfoutlinetext
+ }}
+ %
+ \def\pdfmkdest#1{%
+ \setpdfdestname{#1}%
+ \safewhatsit{\pdfdest name{\pdfdestname} xyz}%
+ }
+ %
+ % by default, use black for everything.
+ \def\urlcolor{\rgbBlack}
+ \def\linkcolor{\rgbBlack}
+ \def\endlink{\setcolor{\maincolor}\pdfendlink}
+ %
+ \def\dopdfoutline#1#2#3#4{%
+ \setpdfoutlinetext{#1}
+ \setpdfdestname{#3}
+ \ifx\pdfdestname\empty
+ \def\pdfdestname{#4}%
+ \fi
+ %
+ \special{pdf:out [-] #2 << /Title (\pdfoutlinetext) /A
+ << /S /GoTo /D (\pdfdestname) >> >> }%
+ }
+ %
+ \def\pdfmakeoutlines{%
+ \begingroup
+ %
+ % For XeTeX, counts of subentries are not necessary.
+ % Therefore, we read toc only once.
+ %
+ % We use node names as destinations.
+ \def\partentry##1##2##3##4{}% ignore parts in the outlines
+ \def\numchapentry##1##2##3##4{%
+ \dopdfoutline{##1}{1}{##3}{##4}}%
+ \def\numsecentry##1##2##3##4{%
+ \dopdfoutline{##1}{2}{##3}{##4}}%
+ \def\numsubsecentry##1##2##3##4{%
+ \dopdfoutline{##1}{3}{##3}{##4}}%
+ \def\numsubsubsecentry##1##2##3##4{%
+ \dopdfoutline{##1}{4}{##3}{##4}}%
+ %
+ \let\appentry\numchapentry%
+ \let\appsecentry\numsecentry%
+ \let\appsubsecentry\numsubsecentry%
+ \let\appsubsubsecentry\numsubsubsecentry%
+ \let\unnchapentry\numchapentry%
+ \let\unnsecentry\numsecentry%
+ \let\unnsubsecentry\numsubsecentry%
+ \let\unnsubsubsecentry\numsubsubsecentry%
+ %
+ % For XeTeX, xdvipdfmx converts strings to UTF-16.
+ % Therefore, the encoding and the language may not be considered.
+ %
+ \indexnofonts
+ \setupdatafile
+ % We can have normal brace characters in the PDF outlines, unlike
+ % Texinfo index files. So set that up.
+ \def\{{\lbracecharliteral}%
+ \def\}{\rbracecharliteral}%
+ \catcode`\\=\active \otherbackslash
+ \input \tocreadfilename
+ \endgroup
+ }
+ {\catcode`[=1 \catcode`]=2
+ \catcode`{=\other \catcode`}=\other
+ \gdef\lbracecharliteral[{]%
+ \gdef\rbracecharliteral[}]%
+ ]
+
+ \special{pdf:docview << /PageMode /UseOutlines >> }
+ % ``\special{pdf:tounicode ...}'' is not necessary
+ % because xdvipdfmx converts strings from UTF-8 to UTF-16 without it.
+ % However, due to a UTF-16 conversion issue of xdvipdfmx 20150315,
+ % ``\special{pdf:dest ...}'' cannot handle non-ASCII strings.
+ % It is fixed by xdvipdfmx 20160106 (TeX Live SVN r39753).
+%
+ \def\skipspaces#1{\def\PP{#1}\def\D{|}%
+ \ifx\PP\D\let\nextsp\relax
+ \else\let\nextsp\skipspaces
+ \addtokens{\filename}{\PP}%
+ \advance\filenamelength by 1
+ \fi
+ \nextsp}
+ \def\getfilename#1{%
+ \filenamelength=0
+ % If we don't expand the argument now, \skipspaces will get
+ % snagged on things like "@value{foo}".
+ \edef\temp{#1}%
+ \expandafter\skipspaces\temp|\relax
+ }
+ % make a live url in pdf output.
+ \def\pdfurl#1{%
+ \begingroup
+ % it seems we really need yet another set of dummies; have not
+ % tried to figure out what each command should do in the context
+ % of @url. for now, just make @/ a no-op, that's the only one
+ % people have actually reported a problem with.
+ %
+ \normalturnoffactive
+ \def\@{@}%
+ \let\/=\empty
+ \makevalueexpandable
+ % do we want to go so far as to use \indexnofonts instead of just
+ % special-casing \var here?
+ \def\var##1{##1}%
+ %
+ \leavevmode\setcolor{\urlcolor}%
+ \special{pdf:bann << /Border [0 0 0]
+ /Subtype /Link /A << /S /URI /URI (#1) >> >>}%
+ \endgroup}
+ \def\endlink{\setcolor{\maincolor}\special{pdf:eann}}
+ \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}}
+ \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks}
+ \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks}
+ \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}}
+ \def\maketoks{%
+ \expandafter\poptoks\the\toksA|ENDTOKS|\relax
+ \ifx\first0\adn0
+ \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3
+ \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6
+ \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9
+ \else
+ \ifnum0=\countA\else\makelink\fi
+ \ifx\first.\let\next=\done\else
+ \let\next=\maketoks
+ \addtokens{\toksB}{\the\toksD}
+ \ifx\first,\addtokens{\toksB}{\space}\fi
+ \fi
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+ \next}
+ \def\makelink{\addtokens{\toksB}%
+ {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0}
+ \def\pdflink#1{%
+ \special{pdf:bann << /Border [0 0 0]
+ /Type /Annot /Subtype /Link /A << /S /GoTo /D (#1) >> >>}%
+ \setcolor{\linkcolor}#1\endlink}
+ \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st}
+%
+ %
+ % @image support
+ %
+ % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto).
+ \def\doxeteximage#1#2#3{%
+ \def\xeteximagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}%
+ \def\xeteximageheight{#3}\setbox2 = \hbox{\ignorespaces #3}%
+ %
+ % XeTeX (and the PDF format) supports .pdf, .png, .jpg (among
+ % others). Let's try in that order, PDF first since if
+ % someone has a scalable image, presumably better to use that than a
+ % bitmap.
+ \let\xeteximgext=\empty
+ \begingroup
+ \openin 1 #1.pdf \ifeof 1
+ \openin 1 #1.PDF \ifeof 1
+ \openin 1 #1.png \ifeof 1
+ \openin 1 #1.jpg \ifeof 1
+ \openin 1 #1.jpeg \ifeof 1
+ \openin 1 #1.JPG \ifeof 1
+ \errmessage{Could not find image file #1 for XeTeX}%
+ \else \gdef\xeteximgext{JPG}%
+ \fi
+ \else \gdef\xeteximgext{jpeg}%
+ \fi
+ \else \gdef\xeteximgext{jpg}%
+ \fi
+ \else \gdef\xeteximgext{png}%
+ \fi
+ \else \gdef\xeteximgext{PDF}%
+ \fi
+ \else \gdef\xeteximgext{pdf}%
+ \fi
+ \closein 1
+ \endgroup
+ %
+ \def\xetexpdfext{pdf}%
+ \ifx\xeteximgext\xetexpdfext
+ \XeTeXpdffile "#1".\xeteximgext ""
+ \else
+ \def\xetexpdfext{PDF}%
+ \ifx\xeteximgext\xetexpdfext
+ \XeTeXpdffile "#1".\xeteximgext ""
+ \else
+ \XeTeXpicfile "#1".\xeteximgext ""
+ \fi
+ \fi
+ \ifdim \wd0 >0pt width \xeteximagewidth \fi
+ \ifdim \wd2 >0pt height \xeteximageheight \fi \relax
+ }
+\fi
+
+
+%
+\message{fonts,}
+
+% Set the baselineskip to #1, and the lineskip and strut size
+% correspondingly. There is no deep meaning behind these magic numbers
+% used as factors; they just match (closely enough) what Knuth defined.
+%
+\def\lineskipfactor{.08333}
+\def\strutheightpercent{.70833}
+\def\strutdepthpercent {.29167}
+%
+% can get a sort of poor man's double spacing by redefining this.
+\def\baselinefactor{1}
+%
+\newdimen\textleading
+\def\setleading#1{%
+ \dimen0 = #1\relax
+ \normalbaselineskip = \baselinefactor\dimen0
+ \normallineskip = \lineskipfactor\normalbaselineskip
+ \normalbaselines
+ \setbox\strutbox =\hbox{%
+ \vrule width0pt height\strutheightpercent\baselineskip
+ depth \strutdepthpercent \baselineskip
+ }%
+}
+
+% PDF CMaps. See also LaTeX's t1.cmap.
+%
+% do nothing with this by default.
+\expandafter\let\csname cmapOT1\endcsname\gobble
+\expandafter\let\csname cmapOT1IT\endcsname\gobble
+\expandafter\let\csname cmapOT1TT\endcsname\gobble
+
+% if we are producing pdf, and we have \pdffontattr, then define cmaps.
+% (\pdffontattr was introduced many years ago, but people still run
+% older pdftex's; it's easy to conditionalize, so we do.)
+\ifpdf \ifx\pdffontattr\thisisundefined \else
+ \begingroup
+ \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char.
+ \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap
+%%DocumentNeededResources: ProcSet (CIDInit)
+%%IncludeResource: ProcSet (CIDInit)
+%%BeginResource: CMap (TeX-OT1-0)
+%%Title: (TeX-OT1-0 TeX OT1 0)
+%%Version: 1.000
+%%EndComments
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (TeX)
+/Ordering (OT1)
+/Supplement 0
+>> def
+/CMapName /TeX-OT1-0 def
+/CMapType 2 def
+1 begincodespacerange
+<00> <7F>
+endcodespacerange
+8 beginbfrange
+<00> <01> <0393>
+<09> <0A> <03A8>
+<23> <26> <0023>
+<28> <3B> <0028>
+<3F> <5B> <003F>
+<5D> <5E> <005D>
+<61> <7A> <0061>
+<7B> <7C> <2013>
+endbfrange
+40 beginbfchar
+<02> <0398>
+<03> <039B>
+<04> <039E>
+<05> <03A0>
+<06> <03A3>
+<07> <03D2>
+<08> <03A6>
+<0B> <00660066>
+<0C> <00660069>
+<0D> <0066006C>
+<0E> <006600660069>
+<0F> <00660066006C>
+<10> <0131>
+<11> <0237>
+<12> <0060>
+<13> <00B4>
+<14> <02C7>
+<15> <02D8>
+<16> <00AF>
+<17> <02DA>
+<18> <00B8>
+<19> <00DF>
+<1A> <00E6>
+<1B> <0153>
+<1C> <00F8>
+<1D> <00C6>
+<1E> <0152>
+<1F> <00D8>
+<21> <0021>
+<22> <201D>
+<27> <2019>
+<3C> <00A1>
+<3D> <003D>
+<3E> <00BF>
+<5C> <201C>
+<5F> <02D9>
+<60> <2018>
+<7D> <02DD>
+<7E> <007E>
+<7F> <00A8>
+endbfchar
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+%%EndResource
+%%EOF
+ }\endgroup
+ \expandafter\edef\csname cmapOT1\endcsname#1{%
+ \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}%
+ }%
+%
+% \cmapOT1IT
+ \begingroup
+ \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char.
+ \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap
+%%DocumentNeededResources: ProcSet (CIDInit)
+%%IncludeResource: ProcSet (CIDInit)
+%%BeginResource: CMap (TeX-OT1IT-0)
+%%Title: (TeX-OT1IT-0 TeX OT1IT 0)
+%%Version: 1.000
+%%EndComments
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (TeX)
+/Ordering (OT1IT)
+/Supplement 0
+>> def
+/CMapName /TeX-OT1IT-0 def
+/CMapType 2 def
+1 begincodespacerange
+<00> <7F>
+endcodespacerange
+8 beginbfrange
+<00> <01> <0393>
+<09> <0A> <03A8>
+<25> <26> <0025>
+<28> <3B> <0028>
+<3F> <5B> <003F>
+<5D> <5E> <005D>
+<61> <7A> <0061>
+<7B> <7C> <2013>
+endbfrange
+42 beginbfchar
+<02> <0398>
+<03> <039B>
+<04> <039E>
+<05> <03A0>
+<06> <03A3>
+<07> <03D2>
+<08> <03A6>
+<0B> <00660066>
+<0C> <00660069>
+<0D> <0066006C>
+<0E> <006600660069>
+<0F> <00660066006C>
+<10> <0131>
+<11> <0237>
+<12> <0060>
+<13> <00B4>
+<14> <02C7>
+<15> <02D8>
+<16> <00AF>
+<17> <02DA>
+<18> <00B8>
+<19> <00DF>
+<1A> <00E6>
+<1B> <0153>
+<1C> <00F8>
+<1D> <00C6>
+<1E> <0152>
+<1F> <00D8>
+<21> <0021>
+<22> <201D>
+<23> <0023>
+<24> <00A3>
+<27> <2019>
+<3C> <00A1>
+<3D> <003D>
+<3E> <00BF>
+<5C> <201C>
+<5F> <02D9>
+<60> <2018>
+<7D> <02DD>
+<7E> <007E>
+<7F> <00A8>
+endbfchar
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+%%EndResource
+%%EOF
+ }\endgroup
+ \expandafter\edef\csname cmapOT1IT\endcsname#1{%
+ \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}%
+ }%
+%
+% \cmapOT1TT
+ \begingroup
+ \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char.
+ \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap
+%%DocumentNeededResources: ProcSet (CIDInit)
+%%IncludeResource: ProcSet (CIDInit)
+%%BeginResource: CMap (TeX-OT1TT-0)
+%%Title: (TeX-OT1TT-0 TeX OT1TT 0)
+%%Version: 1.000
+%%EndComments
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (TeX)
+/Ordering (OT1TT)
+/Supplement 0
+>> def
+/CMapName /TeX-OT1TT-0 def
+/CMapType 2 def
+1 begincodespacerange
+<00> <7F>
+endcodespacerange
+5 beginbfrange
+<00> <01> <0393>
+<09> <0A> <03A8>
+<21> <26> <0021>
+<28> <5F> <0028>
+<61> <7E> <0061>
+endbfrange
+32 beginbfchar
+<02> <0398>
+<03> <039B>
+<04> <039E>
+<05> <03A0>
+<06> <03A3>
+<07> <03D2>
+<08> <03A6>
+<0B> <2191>
+<0C> <2193>
+<0D> <0027>
+<0E> <00A1>
+<0F> <00BF>
+<10> <0131>
+<11> <0237>
+<12> <0060>
+<13> <00B4>
+<14> <02C7>
+<15> <02D8>
+<16> <00AF>
+<17> <02DA>
+<18> <00B8>
+<19> <00DF>
+<1A> <00E6>
+<1B> <0153>
+<1C> <00F8>
+<1D> <00C6>
+<1E> <0152>
+<1F> <00D8>
+<20> <2423>
+<27> <2019>
+<60> <2018>
+<7F> <00A8>
+endbfchar
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+%%EndResource
+%%EOF
+ }\endgroup
+ \expandafter\edef\csname cmapOT1TT\endcsname#1{%
+ \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}%
+ }%
+\fi\fi
+
+
+% Set the font macro #1 to the font named \fontprefix#2.
+% #3 is the font's design size, #4 is a scale factor, #5 is the CMap
+% encoding (only OT1, OT1IT and OT1TT are allowed, or empty to omit).
+% Example:
+% #1 = \textrm
+% #2 = \rmshape
+% #3 = 10
+% #4 = \mainmagstep
+% #5 = OT1
+%
+\def\setfont#1#2#3#4#5{%
+ \font#1=\fontprefix#2#3 scaled #4
+ \csname cmap#5\endcsname#1%
+}
+% This is what gets called when #5 of \setfont is empty.
+\let\cmap\gobble
+%
+% (end of cmaps)
+
+% Use cm as the default font prefix.
+% To specify the font prefix, you must define \fontprefix
+% before you read in texinfo.tex.
+\ifx\fontprefix\thisisundefined
+\def\fontprefix{cm}
+\fi
+% Support font families that don't use the same naming scheme as CM.
+\def\rmshape{r}
+\def\rmbshape{bx} % where the normal face is bold
+\def\bfshape{b}
+\def\bxshape{bx}
+\def\ttshape{tt}
+\def\ttbshape{tt}
+\def\ttslshape{sltt}
+\def\itshape{ti}
+\def\itbshape{bxti}
+\def\slshape{sl}
+\def\slbshape{bxsl}
+\def\sfshape{ss}
+\def\sfbshape{ss}
+\def\scshape{csc}
+\def\scbshape{csc}
+
+% Definitions for a main text size of 11pt. (The default in Texinfo.)
+%
+\def\definetextfontsizexi{%
+% Text fonts (11.2pt, magstep1).
+\def\textnominalsize{11pt}
+\edef\mainmagstep{\magstephalf}
+\setfont\textrm\rmshape{10}{\mainmagstep}{OT1}
+\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT}
+\setfont\textbf\bfshape{10}{\mainmagstep}{OT1}
+\setfont\textit\itshape{10}{\mainmagstep}{OT1IT}
+\setfont\textsl\slshape{10}{\mainmagstep}{OT1}
+\setfont\textsf\sfshape{10}{\mainmagstep}{OT1}
+\setfont\textsc\scshape{10}{\mainmagstep}{OT1}
+\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT}
+\font\texti=cmmi10 scaled \mainmagstep
+\font\textsy=cmsy10 scaled \mainmagstep
+\def\textecsize{1095}
+
+% A few fonts for @defun names and args.
+\setfont\defbf\bfshape{10}{\magstep1}{OT1}
+\setfont\deftt\ttshape{10}{\magstep1}{OT1TT}
+\setfont\defsl\slshape{10}{\magstep1}{OT1}
+\setfont\defttsl\ttslshape{10}{\magstep1}{OT1TT}
+\def\df{\let\ttfont=\deftt \let\bffont = \defbf
+\let\ttslfont=\defttsl \let\slfont=\defsl \bf}
+
+% Fonts for indices, footnotes, small examples (9pt).
+\def\smallnominalsize{9pt}
+\setfont\smallrm\rmshape{9}{1000}{OT1}
+\setfont\smalltt\ttshape{9}{1000}{OT1TT}
+\setfont\smallbf\bfshape{10}{900}{OT1}
+\setfont\smallit\itshape{9}{1000}{OT1IT}
+\setfont\smallsl\slshape{9}{1000}{OT1}
+\setfont\smallsf\sfshape{9}{1000}{OT1}
+\setfont\smallsc\scshape{10}{900}{OT1}
+\setfont\smallttsl\ttslshape{10}{900}{OT1TT}
+\font\smalli=cmmi9
+\font\smallsy=cmsy9
+\def\smallecsize{0900}
+
+% Fonts for small examples (8pt).
+\def\smallernominalsize{8pt}
+\setfont\smallerrm\rmshape{8}{1000}{OT1}
+\setfont\smallertt\ttshape{8}{1000}{OT1TT}
+\setfont\smallerbf\bfshape{10}{800}{OT1}
+\setfont\smallerit\itshape{8}{1000}{OT1IT}
+\setfont\smallersl\slshape{8}{1000}{OT1}
+\setfont\smallersf\sfshape{8}{1000}{OT1}
+\setfont\smallersc\scshape{10}{800}{OT1}
+\setfont\smallerttsl\ttslshape{10}{800}{OT1TT}
+\font\smalleri=cmmi8
+\font\smallersy=cmsy8
+\def\smallerecsize{0800}
+
+% Fonts for math mode superscripts (7pt).
+\def\sevennominalsize{7pt}
+\setfont\sevenrm\rmshape{7}{1000}{OT1}
+\setfont\seventt\ttshape{10}{700}{OT1TT}
+\setfont\sevenbf\bfshape{10}{700}{OT1}
+\setfont\sevenit\itshape{7}{1000}{OT1IT}
+\setfont\sevensl\slshape{10}{700}{OT1}
+\setfont\sevensf\sfshape{10}{700}{OT1}
+\setfont\sevensc\scshape{10}{700}{OT1}
+\setfont\seventtsl\ttslshape{10}{700}{OT1TT}
+\font\seveni=cmmi7
+\font\sevensy=cmsy7
+\def\sevenecsize{0700}
+
+% Fonts for title page (20.4pt):
+\def\titlenominalsize{20pt}
+\setfont\titlerm\rmbshape{12}{\magstep3}{OT1}
+\setfont\titleit\itbshape{10}{\magstep4}{OT1IT}
+\setfont\titlesl\slbshape{10}{\magstep4}{OT1}
+\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT}
+\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT}
+\setfont\titlesf\sfbshape{17}{\magstep1}{OT1}
+\let\titlebf=\titlerm
+\setfont\titlesc\scbshape{10}{\magstep4}{OT1}
+\font\titlei=cmmi12 scaled \magstep3
+\font\titlesy=cmsy10 scaled \magstep4
+\def\titleecsize{2074}
+
+% Chapter (and unnumbered) fonts (17.28pt).
+\def\chapnominalsize{17pt}
+\setfont\chaprm\rmbshape{12}{\magstep2}{OT1}
+\setfont\chapit\itbshape{10}{\magstep3}{OT1IT}
+\setfont\chapsl\slbshape{10}{\magstep3}{OT1}
+\setfont\chaptt\ttbshape{12}{\magstep2}{OT1TT}
+\setfont\chapttsl\ttslshape{10}{\magstep3}{OT1TT}
+\setfont\chapsf\sfbshape{17}{1000}{OT1}
+\let\chapbf=\chaprm
+\setfont\chapsc\scbshape{10}{\magstep3}{OT1}
+\font\chapi=cmmi12 scaled \magstep2
+\font\chapsy=cmsy10 scaled \magstep3
+\def\chapecsize{1728}
+
+% Section fonts (14.4pt).
+\def\secnominalsize{14pt}
+\setfont\secrm\rmbshape{12}{\magstep1}{OT1}
+\setfont\secrmnotbold\rmshape{12}{\magstep1}{OT1}
+\setfont\secit\itbshape{10}{\magstep2}{OT1IT}
+\setfont\secsl\slbshape{10}{\magstep2}{OT1}
+\setfont\sectt\ttbshape{12}{\magstep1}{OT1TT}
+\setfont\secttsl\ttslshape{10}{\magstep2}{OT1TT}
+\setfont\secsf\sfbshape{12}{\magstep1}{OT1}
+\let\secbf\secrm
+\setfont\secsc\scbshape{10}{\magstep2}{OT1}
+\font\seci=cmmi12 scaled \magstep1
+\font\secsy=cmsy10 scaled \magstep2
+\def\sececsize{1440}
+
+% Subsection fonts (13.15pt).
+\def\ssecnominalsize{13pt}
+\setfont\ssecrm\rmbshape{12}{\magstephalf}{OT1}
+\setfont\ssecit\itbshape{10}{1315}{OT1IT}
+\setfont\ssecsl\slbshape{10}{1315}{OT1}
+\setfont\ssectt\ttbshape{12}{\magstephalf}{OT1TT}
+\setfont\ssecttsl\ttslshape{10}{1315}{OT1TT}
+\setfont\ssecsf\sfbshape{12}{\magstephalf}{OT1}
+\let\ssecbf\ssecrm
+\setfont\ssecsc\scbshape{10}{1315}{OT1}
+\font\sseci=cmmi12 scaled \magstephalf
+\font\ssecsy=cmsy10 scaled 1315
+\def\ssececsize{1200}
+
+% Reduced fonts for @acronym in text (10pt).
+\def\reducednominalsize{10pt}
+\setfont\reducedrm\rmshape{10}{1000}{OT1}
+\setfont\reducedtt\ttshape{10}{1000}{OT1TT}
+\setfont\reducedbf\bfshape{10}{1000}{OT1}
+\setfont\reducedit\itshape{10}{1000}{OT1IT}
+\setfont\reducedsl\slshape{10}{1000}{OT1}
+\setfont\reducedsf\sfshape{10}{1000}{OT1}
+\setfont\reducedsc\scshape{10}{1000}{OT1}
+\setfont\reducedttsl\ttslshape{10}{1000}{OT1TT}
+\font\reducedi=cmmi10
+\font\reducedsy=cmsy10
+\def\reducedecsize{1000}
+
+\textleading = 13.2pt % line spacing for 11pt CM
+\textfonts % reset the current fonts
+\rm
+} % end of 11pt text font size definitions, \definetextfontsizexi
+
+
+% Definitions to make the main text be 10pt Computer Modern, with
+% section, chapter, etc., sizes following suit. This is for the GNU
+% Press printing of the Emacs 22 manual. Maybe other manuals in the
+% future. Used with @smallbook, which sets the leading to 12pt.
+%
+\def\definetextfontsizex{%
+% Text fonts (10pt).
+\def\textnominalsize{10pt}
+\edef\mainmagstep{1000}
+\setfont\textrm\rmshape{10}{\mainmagstep}{OT1}
+\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT}
+\setfont\textbf\bfshape{10}{\mainmagstep}{OT1}
+\setfont\textit\itshape{10}{\mainmagstep}{OT1IT}
+\setfont\textsl\slshape{10}{\mainmagstep}{OT1}
+\setfont\textsf\sfshape{10}{\mainmagstep}{OT1}
+\setfont\textsc\scshape{10}{\mainmagstep}{OT1}
+\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT}
+\font\texti=cmmi10 scaled \mainmagstep
+\font\textsy=cmsy10 scaled \mainmagstep
+\def\textecsize{1000}
+
+% A few fonts for @defun names and args.
+\setfont\defbf\bfshape{10}{\magstephalf}{OT1}
+\setfont\deftt\ttshape{10}{\magstephalf}{OT1TT}
+\setfont\defsl\slshape{10}{\magstephalf}{OT1}
+\setfont\defttsl\ttslshape{10}{\magstephalf}{OT1TT}
+\def\df{\let\ttfont=\deftt \let\bffont = \defbf
+\let\slfont=\defsl \let\ttslfont=\defttsl \bf}
+
+% Fonts for indices, footnotes, small examples (9pt).
+\def\smallnominalsize{9pt}
+\setfont\smallrm\rmshape{9}{1000}{OT1}
+\setfont\smalltt\ttshape{9}{1000}{OT1TT}
+\setfont\smallbf\bfshape{10}{900}{OT1}
+\setfont\smallit\itshape{9}{1000}{OT1IT}
+\setfont\smallsl\slshape{9}{1000}{OT1}
+\setfont\smallsf\sfshape{9}{1000}{OT1}
+\setfont\smallsc\scshape{10}{900}{OT1}
+\setfont\smallttsl\ttslshape{10}{900}{OT1TT}
+\font\smalli=cmmi9
+\font\smallsy=cmsy9
+\def\smallecsize{0900}
+
+% Fonts for small examples (8pt).
+\def\smallernominalsize{8pt}
+\setfont\smallerrm\rmshape{8}{1000}{OT1}
+\setfont\smallertt\ttshape{8}{1000}{OT1TT}
+\setfont\smallerbf\bfshape{10}{800}{OT1}
+\setfont\smallerit\itshape{8}{1000}{OT1IT}
+\setfont\smallersl\slshape{8}{1000}{OT1}
+\setfont\smallersf\sfshape{8}{1000}{OT1}
+\setfont\smallersc\scshape{10}{800}{OT1}
+\setfont\smallerttsl\ttslshape{10}{800}{OT1TT}
+\font\smalleri=cmmi8
+\font\smallersy=cmsy8
+\def\smallerecsize{0800}
+
+% Fonts for math mode superscripts (7pt).
+\def\sevennominalsize{7pt}
+\setfont\sevenrm\rmshape{7}{1000}{OT1}
+\setfont\seventt\ttshape{10}{700}{OT1TT}
+\setfont\sevenbf\bfshape{10}{700}{OT1}
+\setfont\sevenit\itshape{7}{1000}{OT1IT}
+\setfont\sevensl\slshape{10}{700}{OT1}
+\setfont\sevensf\sfshape{10}{700}{OT1}
+\setfont\sevensc\scshape{10}{700}{OT1}
+\setfont\seventtsl\ttslshape{10}{700}{OT1TT}
+\font\seveni=cmmi7
+\font\sevensy=cmsy7
+\def\sevenecsize{0700}
+
+% Fonts for title page (20.4pt):
+\def\titlenominalsize{20pt}
+\setfont\titlerm\rmbshape{12}{\magstep3}{OT1}
+\setfont\titleit\itbshape{10}{\magstep4}{OT1IT}
+\setfont\titlesl\slbshape{10}{\magstep4}{OT1}
+\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT}
+\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT}
+\setfont\titlesf\sfbshape{17}{\magstep1}{OT1}
+\let\titlebf=\titlerm
+\setfont\titlesc\scbshape{10}{\magstep4}{OT1}
+\font\titlei=cmmi12 scaled \magstep3
+\font\titlesy=cmsy10 scaled \magstep4
+\def\titleecsize{2074}
+
+% Chapter fonts (14.4pt).
+\def\chapnominalsize{14pt}
+\setfont\chaprm\rmbshape{12}{\magstep1}{OT1}
+\setfont\chapit\itbshape{10}{\magstep2}{OT1IT}
+\setfont\chapsl\slbshape{10}{\magstep2}{OT1}
+\setfont\chaptt\ttbshape{12}{\magstep1}{OT1TT}
+\setfont\chapttsl\ttslshape{10}{\magstep2}{OT1TT}
+\setfont\chapsf\sfbshape{12}{\magstep1}{OT1}
+\let\chapbf\chaprm
+\setfont\chapsc\scbshape{10}{\magstep2}{OT1}
+\font\chapi=cmmi12 scaled \magstep1
+\font\chapsy=cmsy10 scaled \magstep2
+\def\chapecsize{1440}
+
+% Section fonts (12pt).
+\def\secnominalsize{12pt}
+\setfont\secrm\rmbshape{12}{1000}{OT1}
+\setfont\secit\itbshape{10}{\magstep1}{OT1IT}
+\setfont\secsl\slbshape{10}{\magstep1}{OT1}
+\setfont\sectt\ttbshape{12}{1000}{OT1TT}
+\setfont\secttsl\ttslshape{10}{\magstep1}{OT1TT}
+\setfont\secsf\sfbshape{12}{1000}{OT1}
+\let\secbf\secrm
+\setfont\secsc\scbshape{10}{\magstep1}{OT1}
+\font\seci=cmmi12
+\font\secsy=cmsy10 scaled \magstep1
+\def\sececsize{1200}
+
+% Subsection fonts (10pt).
+\def\ssecnominalsize{10pt}
+\setfont\ssecrm\rmbshape{10}{1000}{OT1}
+\setfont\ssecit\itbshape{10}{1000}{OT1IT}
+\setfont\ssecsl\slbshape{10}{1000}{OT1}
+\setfont\ssectt\ttbshape{10}{1000}{OT1TT}
+\setfont\ssecttsl\ttslshape{10}{1000}{OT1TT}
+\setfont\ssecsf\sfbshape{10}{1000}{OT1}
+\let\ssecbf\ssecrm
+\setfont\ssecsc\scbshape{10}{1000}{OT1}
+\font\sseci=cmmi10
+\font\ssecsy=cmsy10
+\def\ssececsize{1000}
+
+% Reduced fonts for @acronym in text (9pt).
+\def\reducednominalsize{9pt}
+\setfont\reducedrm\rmshape{9}{1000}{OT1}
+\setfont\reducedtt\ttshape{9}{1000}{OT1TT}
+\setfont\reducedbf\bfshape{10}{900}{OT1}
+\setfont\reducedit\itshape{9}{1000}{OT1IT}
+\setfont\reducedsl\slshape{9}{1000}{OT1}
+\setfont\reducedsf\sfshape{9}{1000}{OT1}
+\setfont\reducedsc\scshape{10}{900}{OT1}
+\setfont\reducedttsl\ttslshape{10}{900}{OT1TT}
+\font\reducedi=cmmi9
+\font\reducedsy=cmsy9
+\def\reducedecsize{0900}
+
+\divide\parskip by 2 % reduce space between paragraphs
+\textleading = 12pt % line spacing for 10pt CM
+\textfonts % reset the current fonts
+\rm
+} % end of 10pt text font size definitions, \definetextfontsizex
+
+% Fonts for short table of contents.
+\setfont\shortcontrm\rmshape{12}{1000}{OT1}
+\setfont\shortcontbf\bfshape{10}{\magstep1}{OT1} % no cmb12
+\setfont\shortcontsl\slshape{12}{1000}{OT1}
+\setfont\shortconttt\ttshape{12}{1000}{OT1TT}
+
+
+% We provide the user-level command
+% @fonttextsize 10
+% (or 11) to redefine the text font size. pt is assumed.
+%
+\def\xiword{11}
+\def\xword{10}
+\def\xwordpt{10pt}
+%
+\parseargdef\fonttextsize{%
+ \def\textsizearg{#1}%
+ %\wlog{doing @fonttextsize \textsizearg}%
+ %
+ % Set \globaldefs so that documents can use this inside @tex, since
+ % makeinfo 4.8 does not support it, but we need it nonetheless.
+ %
+ \begingroup \globaldefs=1
+ \ifx\textsizearg\xword \definetextfontsizex
+ \else \ifx\textsizearg\xiword \definetextfontsizexi
+ \else
+ \errhelp=\EMsimple
+ \errmessage{@fonttextsize only supports `10' or `11', not `\textsizearg'}
+ \fi\fi
+ \endgroup
+}
+
+%
+% Change the current font style to #1, remembering it in \curfontstyle.
+% For now, we do not accumulate font styles: @b{@i{foo}} prints foo in
+% italics, not bold italics.
+%
+\def\setfontstyle#1{%
+ \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd.
+ \csname #1font\endcsname % change the current font
+}
+
+\def\rm{\fam=0 \setfontstyle{rm}}
+\def\it{\fam=\itfam \setfontstyle{it}}
+\def\sl{\fam=\slfam \setfontstyle{sl}}
+\def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf}
+\def\tt{\fam=\ttfam \setfontstyle{tt}}
+
+% Texinfo sort of supports the sans serif font style, which plain TeX does not.
+% So we set up a \sf.
+\newfam\sffam
+\def\sf{\fam=\sffam \setfontstyle{sf}}
+
+% We don't need math for this font style.
+\def\ttsl{\setfontstyle{ttsl}}
+
+
+% In order for the font changes to affect most math symbols and letters,
+% we have to define the \textfont of the standard families.
+% We don't bother to reset \scriptscriptfont; awaiting user need.
+%
+\def\resetmathfonts{%
+ \textfont0=\rmfont \textfont1=\ifont \textfont2=\syfont
+ \textfont\itfam=\itfont \textfont\slfam=\slfont \textfont\bffam=\bffont
+ \textfont\ttfam=\ttfont \textfont\sffam=\sffont
+ %
+ % Fonts for superscript. Note that the 7pt fonts are used regardless
+ % of the current font size.
+ \scriptfont0=\sevenrm \scriptfont1=\seveni \scriptfont2=\sevensy
+ \scriptfont\itfam=\sevenit \scriptfont\slfam=\sevensl
+ \scriptfont\bffam=\sevenbf \scriptfont\ttfam=\seventt
+ \scriptfont\sffam=\sevensf
+}
+
+%
+
+% The font-changing commands (all called \...fonts) redefine the meanings
+% of \STYLEfont, instead of just \STYLE. We do this because \STYLE needs
+% to also set the current \fam for math mode. Our \STYLE (e.g., \rm)
+% commands hardwire \STYLEfont to set the current font.
+%
+% The fonts used for \ifont are for "math italics" (\itfont is for italics
+% in regular text). \syfont is also used in math mode only.
+%
+% Each font-changing command also sets the names \lsize (one size lower)
+% and \lllsize (three sizes lower). These relative commands are used
+% in, e.g., the LaTeX logo and acronyms.
+%
+% This all needs generalizing, badly.
+%
+
+\def\assignfonts#1{%
+ \expandafter\let\expandafter\rmfont\csname #1rm\endcsname
+ \expandafter\let\expandafter\itfont\csname #1it\endcsname
+ \expandafter\let\expandafter\slfont\csname #1sl\endcsname
+ \expandafter\let\expandafter\bffont\csname #1bf\endcsname
+ \expandafter\let\expandafter\ttfont\csname #1tt\endcsname
+ \expandafter\let\expandafter\smallcaps\csname #1sc\endcsname
+ \expandafter\let\expandafter\sffont \csname #1sf\endcsname
+ \expandafter\let\expandafter\ifont \csname #1i\endcsname
+ \expandafter\let\expandafter\syfont \csname #1sy\endcsname
+ \expandafter\let\expandafter\ttslfont\csname #1ttsl\endcsname
+}
+
+\newif\ifrmisbold
+
+% Select smaller font size with the current style. Used to change font size
+% in, e.g., the LaTeX logo and acronyms. If we are using bold fonts for
+% normal roman text, also use bold fonts for roman text in the smaller size.
+\def\switchtolllsize{%
+ \expandafter\assignfonts\expandafter{\lllsize}%
+ \ifrmisbold
+ \let\rmfont\bffont
+ \fi
+ \csname\curfontstyle\endcsname
+}%
+
+\def\switchtolsize{%
+ \expandafter\assignfonts\expandafter{\lsize}%
+ \ifrmisbold
+ \let\rmfont\bffont
+ \fi
+ \csname\curfontstyle\endcsname
+}%
+
+\def\definefontsetatsize#1#2#3#4#5{%
+\expandafter\def\csname #1fonts\endcsname{%
+ \def\curfontsize{#1}%
+ \def\lsize{#2}\def\lllsize{#3}%
+ \csname rmisbold#5\endcsname
+ \assignfonts{#1}%
+ \resetmathfonts
+ \setleading{#4}%
+}}
+
+\definefontsetatsize{text} {reduced}{smaller}{\textleading}{false}
+\definefontsetatsize{title} {chap} {subsec} {27pt} {true}
+\definefontsetatsize{chap} {sec} {text} {19pt} {true}
+\definefontsetatsize{sec} {subsec} {reduced}{17pt} {true}
+\definefontsetatsize{ssec} {text} {small} {15pt} {true}
+\definefontsetatsize{reduced}{small} {smaller}{10.5pt}{false}
+\definefontsetatsize{small} {smaller}{smaller}{10.5pt}{false}
+\definefontsetatsize{smaller}{smaller}{smaller}{9.5pt} {false}
+
+\def\titlefont#1{{\titlefonts\rm #1}}
+\let\subsecfonts = \ssecfonts
+\let\subsubsecfonts = \ssecfonts
+
+% Define these just so they can be easily changed for other fonts.
+\def\angleleft{$\langle$}
+\def\angleright{$\rangle$}
+
+% Set the fonts to use with the @small... environments.
+\let\smallexamplefonts = \smallfonts
+
+% About \smallexamplefonts. If we use \smallfonts (9pt), @smallexample
+% can fit this many characters:
+% 8.5x11=86 smallbook=72 a4=90 a5=69
+% If we use \scriptfonts (8pt), then we can fit this many characters:
+% 8.5x11=90+ smallbook=80 a4=90+ a5=77
+% For me, subjectively, the few extra characters that fit aren't worth
+% the additional smallness of 8pt. So I'm making the default 9pt.
+%
+% By the way, for comparison, here's what fits with @example (10pt):
+% 8.5x11=71 smallbook=60 a4=75 a5=58
+% --karl, 24jan03.
+
+% Set up the default fonts, so we can use them for creating boxes.
+%
+\definetextfontsizexi
+
+
+\message{markup,}
+
+% Check if we are currently using a typewriter font. Since all the
+% Computer Modern typewriter fonts have zero interword stretch (and
+% shrink), and it is reasonable to expect all typewriter fonts to have
+% this property, we can check that font parameter.
+%
+\def\ifmonospace{\ifdim\fontdimen3\font=0pt }
+
+% Markup style infrastructure. \defmarkupstylesetup\INITMACRO will
+% define and register \INITMACRO to be called on markup style changes.
+% \INITMACRO can check \currentmarkupstyle for the innermost
+% style.
+
+\let\currentmarkupstyle\empty
+
+\def\setupmarkupstyle#1{%
+ \def\currentmarkupstyle{#1}%
+ \markupstylesetup
+}
+
+\let\markupstylesetup\empty
+
+\def\defmarkupstylesetup#1{%
+ \expandafter\def\expandafter\markupstylesetup
+ \expandafter{\markupstylesetup #1}%
+ \def#1%
+}
+
+% Markup style setup for left and right quotes.
+\defmarkupstylesetup\markupsetuplq{%
+ \expandafter\let\expandafter \temp
+ \csname markupsetuplq\currentmarkupstyle\endcsname
+ \ifx\temp\relax \markupsetuplqdefault \else \temp \fi
+}
+
+\defmarkupstylesetup\markupsetuprq{%
+ \expandafter\let\expandafter \temp
+ \csname markupsetuprq\currentmarkupstyle\endcsname
+ \ifx\temp\relax \markupsetuprqdefault \else \temp \fi
+}
+
+{
+\catcode`\'=\active
+\catcode`\`=\active
+
+\gdef\markupsetuplqdefault{\let`\lq}
+\gdef\markupsetuprqdefault{\let'\rq}
+
+\gdef\markupsetcodequoteleft{\let`\codequoteleft}
+\gdef\markupsetcodequoteright{\let'\codequoteright}
+}
+
+\let\markupsetuplqcode \markupsetcodequoteleft
+\let\markupsetuprqcode \markupsetcodequoteright
+%
+\let\markupsetuplqexample \markupsetcodequoteleft
+\let\markupsetuprqexample \markupsetcodequoteright
+%
+\let\markupsetuplqkbd \markupsetcodequoteleft
+\let\markupsetuprqkbd \markupsetcodequoteright
+%
+\let\markupsetuplqsamp \markupsetcodequoteleft
+\let\markupsetuprqsamp \markupsetcodequoteright
+%
+\let\markupsetuplqverb \markupsetcodequoteleft
+\let\markupsetuprqverb \markupsetcodequoteright
+%
+\let\markupsetuplqverbatim \markupsetcodequoteleft
+\let\markupsetuprqverbatim \markupsetcodequoteright
+
+% Allow an option to not use regular directed right quote/apostrophe
+% (char 0x27), but instead the undirected quote from cmtt (char 0x0d).
+% The undirected quote is ugly, so don't make it the default, but it
+% works for pasting with more pdf viewers (at least evince), the
+% lilypond developers report. xpdf does work with the regular 0x27.
+%
+\def\codequoteright{%
+ \ifmonospace
+ \expandafter\ifx\csname SETtxicodequoteundirected\endcsname\relax
+ \expandafter\ifx\csname SETcodequoteundirected\endcsname\relax
+ '%
+ \else \char'15 \fi
+ \else \char'15 \fi
+ \else
+ '%
+ \fi
+}
+%
+% and a similar option for the left quote char vs. a grave accent.
+% Modern fonts display ASCII 0x60 as a grave accent, so some people like
+% the code environments to do likewise.
+%
+\def\codequoteleft{%
+ \ifmonospace
+ \expandafter\ifx\csname SETtxicodequotebacktick\endcsname\relax
+ \expandafter\ifx\csname SETcodequotebacktick\endcsname\relax
+ % [Knuth] pp. 380,381,391
+ % \relax disables Spanish ligatures ?` and !` of \tt font.
+ \relax`%
+ \else \char'22 \fi
+ \else \char'22 \fi
+ \else
+ \relax`%
+ \fi
+}
+
+% Commands to set the quote options.
+%
+\parseargdef\codequoteundirected{%
+ \def\temp{#1}%
+ \ifx\temp\onword
+ \expandafter\let\csname SETtxicodequoteundirected\endcsname
+ = t%
+ \else\ifx\temp\offword
+ \expandafter\let\csname SETtxicodequoteundirected\endcsname
+ = \relax
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @codequoteundirected value `\temp', must be on|off}%
+ \fi\fi
+}
+%
+\parseargdef\codequotebacktick{%
+ \def\temp{#1}%
+ \ifx\temp\onword
+ \expandafter\let\csname SETtxicodequotebacktick\endcsname
+ = t%
+ \else\ifx\temp\offword
+ \expandafter\let\csname SETtxicodequotebacktick\endcsname
+ = \relax
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @codequotebacktick value `\temp', must be on|off}%
+ \fi\fi
+}
+
+% [Knuth] pp. 380,381,391, disable Spanish ligatures ?` and !` of \tt font.
+\def\noligaturesquoteleft{\relax\lq}
+
+% Count depth in font-changes, for error checks
+\newcount\fontdepth \fontdepth=0
+
+% Font commands.
+
+% #1 is the font command (\sl or \it), #2 is the text to slant.
+% If we are in a monospaced environment, however, 1) always use \ttsl,
+% and 2) do not add an italic correction.
+\def\dosmartslant#1#2{%
+ \ifusingtt
+ {{\ttsl #2}\let\next=\relax}%
+ {\def\next{{#1#2}\futurelet\next\smartitaliccorrection}}%
+ \next
+}
+\def\smartslanted{\dosmartslant\sl}
+\def\smartitalic{\dosmartslant\it}
+
+% Output an italic correction unless \next (presumed to be the following
+% character) is such as not to need one.
+\def\smartitaliccorrection{%
+ \ifx\next,%
+ \else\ifx\next-%
+ \else\ifx\next.%
+ \else\ifx\next\.%
+ \else\ifx\next\comma%
+ \else\ptexslash
+ \fi\fi\fi\fi\fi
+ \aftersmartic
+}
+
+% Unconditional use \ttsl, and no ic. @var is set to this for defuns.
+\def\ttslanted#1{{\ttsl #1}}
+
+% @cite is like \smartslanted except unconditionally use \sl. We never want
+% ttsl for book titles, do we?
+\def\cite#1{{\sl #1}\futurelet\next\smartitaliccorrection}
+
+\def\aftersmartic{}
+\def\var#1{%
+ \let\saveaftersmartic = \aftersmartic
+ \def\aftersmartic{\null\let\aftersmartic=\saveaftersmartic}%
+ \smartslanted{#1}%
+}
+
+\let\i=\smartitalic
+\let\slanted=\smartslanted
+\let\dfn=\smartslanted
+\let\emph=\smartitalic
+
+% Explicit font changes: @r, @sc, undocumented @ii.
+\def\r#1{{\rm #1}} % roman font
+\def\sc#1{{\smallcaps#1}} % smallcaps font
+\def\ii#1{{\it #1}} % italic font
+
+% @b, explicit bold. Also @strong.
+\def\b#1{{\bf #1}}
+\let\strong=\b
+
+% @sansserif, explicit sans.
+\def\sansserif#1{{\sf #1}}
+
+% We can't just use \exhyphenpenalty, because that only has effect at
+% the end of a paragraph. Restore normal hyphenation at the end of the
+% group within which \nohyphenation is presumably called.
+%
+\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation}
+\def\restorehyphenation{\hyphenchar\font = `- }
+
+% Set sfcode to normal for the chars that usually have another value.
+% Can't use plain's \frenchspacing because it uses the `\x notation, and
+% sometimes \x has an active definition that messes things up.
+%
+\catcode`@=11
+ \def\plainfrenchspacing{%
+ \sfcode`\.=\@m \sfcode`\?=\@m \sfcode`\!=\@m
+ \sfcode`\:=\@m \sfcode`\;=\@m \sfcode`\,=\@m
+ \def\endofsentencespacefactor{1000}% for @. and friends
+ }
+ \def\plainnonfrenchspacing{%
+ \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000
+ \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250
+ \def\endofsentencespacefactor{3000}% for @. and friends
+ }
+\catcode`@=\other
+\def\endofsentencespacefactor{3000}% default
+
+% @t, explicit typewriter.
+\def\t#1{%
+ {\tt \plainfrenchspacing #1}%
+ \null
+}
+
+% @samp.
+\def\samp#1{{\setupmarkupstyle{samp}\lq\tclose{#1}\rq\null}}
+
+% @indicateurl is \samp, that is, with quotes.
+\let\indicateurl=\samp
+
+% @code (and similar) prints in typewriter, but with spaces the same
+% size as normal in the surrounding text, without hyphenation, etc.
+% This is a subroutine for that.
+\def\tclose#1{%
+ {%
+ % Change normal interword space to be same as for the current font.
+ \spaceskip = \fontdimen2\font
+ %
+ % Switch to typewriter.
+ \tt
+ %
+ % But `\ ' produces the large typewriter interword space.
+ \def\ {{\spaceskip = 0pt{} }}%
+ %
+ % Turn off hyphenation.
+ \nohyphenation
+ %
+ \plainfrenchspacing
+ #1%
+ }%
+ \null % reset spacefactor to 1000
+}
+
+% We *must* turn on hyphenation at `-' and `_' in @code.
+% (But see \codedashfinish below.)
+% Otherwise, it is too hard to avoid overfull hboxes
+% in the Emacs manual, the Library manual, etc.
+%
+% Unfortunately, TeX uses one parameter (\hyphenchar) to control
+% both hyphenation at - and hyphenation within words.
+% We must therefore turn them both off (\tclose does that)
+% and arrange explicitly to hyphenate at a dash. -- rms.
+{
+ \catcode`\-=\active \catcode`\_=\active
+ \catcode`\'=\active \catcode`\`=\active
+ \global\let'=\rq \global\let`=\lq % default definitions
+ %
+ \global\def\code{\begingroup
+ \setupmarkupstyle{code}%
+ % The following should really be moved into \setupmarkupstyle handlers.
+ \catcode\dashChar=\active \catcode\underChar=\active
+ \ifallowcodebreaks
+ \let-\codedash
+ \let_\codeunder
+ \else
+ \let-\normaldash
+ \let_\realunder
+ \fi
+ % Given -foo (with a single dash), we do not want to allow a break
+ % after the hyphen.
+ \global\let\codedashprev=\codedash
+ %
+ \codex
+ }
+ %
+ \gdef\codedash{\futurelet\next\codedashfinish}
+ \gdef\codedashfinish{%
+ \normaldash % always output the dash character itself.
+ %
+ % Now, output a discretionary to allow a line break, unless
+ % (a) the next character is a -, or
+ % (b) the preceding character is a -.
+ % E.g., given --posix, we do not want to allow a break after either -.
+ % Given --foo-bar, we do want to allow a break between the - and the b.
+ \ifx\next\codedash \else
+ \ifx\codedashprev\codedash
+ \else \discretionary{}{}{}\fi
+ \fi
+ % we need the space after the = for the case when \next itself is a
+ % space token; it would get swallowed otherwise. As in @code{- a}.
+ \global\let\codedashprev= \next
+ }
+}
+\def\normaldash{-}
+%
+\def\codex #1{\tclose{#1}\endgroup}
+
+\def\codeunder{%
+ % this is all so @math{@code{var_name}+1} can work. In math mode, _
+ % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.)
+ % will therefore expand the active definition of _, which is us
+ % (inside @code that is), therefore an endless loop.
+ \ifusingtt{\ifmmode
+ \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_.
+ \else\normalunderscore \fi
+ \discretionary{}{}{}}%
+ {\_}%
+}
+
+% An additional complication: the above will allow breaks after, e.g.,
+% each of the four underscores in __typeof__. This is bad.
+% @allowcodebreaks provides a document-level way to turn breaking at -
+% and _ on and off.
+%
+\newif\ifallowcodebreaks \allowcodebreakstrue
+
+\def\keywordtrue{true}
+\def\keywordfalse{false}
+
+\parseargdef\allowcodebreaks{%
+ \def\txiarg{#1}%
+ \ifx\txiarg\keywordtrue
+ \allowcodebreakstrue
+ \else\ifx\txiarg\keywordfalse
+ \allowcodebreaksfalse
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @allowcodebreaks option `\txiarg', must be true|false}%
+ \fi\fi
+}
+
+% For @command, @env, @file, @option quotes seem unnecessary,
+% so use \code rather than \samp.
+\let\command=\code
+\let\env=\code
+\let\file=\code
+\let\option=\code
+
+% @uref (abbreviation for `urlref') aka @url takes an optional
+% (comma-separated) second argument specifying the text to display and
+% an optional third arg as text to display instead of (rather than in
+% addition to) the url itself. First (mandatory) arg is the url.
+
+% TeX-only option to allow changing PDF output to show only the second
+% arg (if given), and not the url (which is then just the link target).
+\newif\ifurefurlonlylink
+
+% The main macro is \urefbreak, which allows breaking at expected
+% places within the url. (There used to be another version, which
+% didn't support automatic breaking.)
+\def\urefbreak{\begingroup \urefcatcodes \dourefbreak}
+\let\uref=\urefbreak
+%
+\def\dourefbreak#1{\urefbreakfinish #1,,,\finish}
+\def\urefbreakfinish#1,#2,#3,#4\finish{% doesn't work in @example
+ \unsepspaces
+ \pdfurl{#1}%
+ \setbox0 = \hbox{\ignorespaces #3}%
+ \ifdim\wd0 > 0pt
+ \unhbox0 % third arg given, show only that
+ \else
+ \setbox0 = \hbox{\ignorespaces #2}% look for second arg
+ \ifdim\wd0 > 0pt
+ \ifpdf
+ % For pdfTeX and LuaTeX
+ \ifurefurlonlylink
+ % PDF plus option to not display url, show just arg
+ \unhbox0
+ \else
+ % PDF, normally display both arg and url for consistency,
+ % visibility, if the pdf is eventually used to print, etc.
+ \unhbox0\ (\urefcode{#1})%
+ \fi
+ \else
+ \ifx\XeTeXrevision\thisisundefined
+ \unhbox0\ (\urefcode{#1})% DVI, always show arg and url
+ \else
+ % For XeTeX
+ \ifurefurlonlylink
+ % PDF plus option to not display url, show just arg
+ \unhbox0
+ \else
+ % PDF, normally display both arg and url for consistency,
+ % visibility, if the pdf is eventually used to print, etc.
+ \unhbox0\ (\urefcode{#1})%
+ \fi
+ \fi
+ \fi
+ \else
+ \urefcode{#1}% only url given, so show it
+ \fi
+ \fi
+ \endlink
+\endgroup}
+
+% Allow line breaks around only a few characters (only).
+\def\urefcatcodes{%
+ \catcode`\&=\active \catcode`\.=\active
+ \catcode`\#=\active \catcode`\?=\active
+ \catcode`\/=\active
+}
+{
+ \urefcatcodes
+ %
+ \global\def\urefcode{\begingroup
+ \setupmarkupstyle{code}%
+ \urefcatcodes
+ \let&\urefcodeamp
+ \let.\urefcodedot
+ \let#\urefcodehash
+ \let?\urefcodequest
+ \let/\urefcodeslash
+ \codex
+ }
+ %
+ % By default, they are just regular characters.
+ \global\def&{\normalamp}
+ \global\def.{\normaldot}
+ \global\def#{\normalhash}
+ \global\def?{\normalquest}
+ \global\def/{\normalslash}
+}
+
+\def\urefcodeamp{\urefprebreak \&\urefpostbreak}
+\def\urefcodedot{\urefprebreak .\urefpostbreak}
+\def\urefcodehash{\urefprebreak \#\urefpostbreak}
+\def\urefcodequest{\urefprebreak ?\urefpostbreak}
+\def\urefcodeslash{\futurelet\next\urefcodeslashfinish}
+{
+ \catcode`\/=\active
+ \global\def\urefcodeslashfinish{%
+ \urefprebreak \slashChar
+ % Allow line break only after the final / in a sequence of
+ % slashes, to avoid line break between the slashes in http://.
+ \ifx\next/\else \urefpostbreak \fi
+ }
+}
+
+% By default we'll break after the special characters, but some people like to
+% break before the special chars, so allow that. Also allow no breaking at
+% all, for manual control.
+%
+\parseargdef\urefbreakstyle{%
+ \def\txiarg{#1}%
+ \ifx\txiarg\wordnone
+ \def\urefprebreak{\nobreak}\def\urefpostbreak{\nobreak}
+ \else\ifx\txiarg\wordbefore
+ \def\urefprebreak{\urefallowbreak}\def\urefpostbreak{\nobreak}
+ \else\ifx\txiarg\wordafter
+ \def\urefprebreak{\nobreak}\def\urefpostbreak{\urefallowbreak}
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @urefbreakstyle setting `\txiarg'}%
+ \fi\fi\fi
+}
+\def\wordafter{after}
+\def\wordbefore{before}
+\def\wordnone{none}
+
+% Allow a ragged right output to aid breaking long URL's. There can
+% be a break at the \allowbreak with no extra glue (if the existing stretch in
+% the line is sufficent), a break at the \penalty100 with extra glue added
+% at the end of the line, or no break at all here.
+% Changing the value of the penalty and/or the amount of stretch affects how
+% preferrable one choice is over the other.
+\def\urefallowbreak{%
+ \allowbreak
+ \hskip 0pt plus 2 em\relax
+ \penalty300
+ \hskip 0pt plus -2 em\relax
+}
+
+\urefbreakstyle after
+
+% @url synonym for @uref, since that's how everyone uses it.
+%
+\let\url=\uref
+
+% rms does not like angle brackets --karl, 17may97.
+% So now @email is just like @uref, unless we are pdf.
+%
+%\def\email#1{\angleleft{\tt #1}\angleright}
+\ifpdforxetex
+ \def\email#1{\doemail#1,,\finish}
+ \def\doemail#1,#2,#3\finish{\begingroup
+ \unsepspaces
+ \pdfurl{mailto:#1}%
+ \setbox0 = \hbox{\ignorespaces #2}%
+ \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi
+ \endlink
+ \endgroup}
+\else
+ \let\email=\uref
+\fi
+
+% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always),
+% `example' (@kbd uses ttsl only inside of @example and friends),
+% or `code' (@kbd uses normal tty font always).
+\parseargdef\kbdinputstyle{%
+ \def\txiarg{#1}%
+ \ifx\txiarg\worddistinct
+ \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}%
+ \else\ifx\txiarg\wordexample
+ \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}%
+ \else\ifx\txiarg\wordcode
+ \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}%
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @kbdinputstyle setting `\txiarg'}%
+ \fi\fi\fi
+}
+\def\worddistinct{distinct}
+\def\wordexample{example}
+\def\wordcode{code}
+
+% Default is `distinct'.
+\kbdinputstyle distinct
+
+% @kbd is like @code, except that if the argument is just one @key command,
+% then @kbd has no effect.
+\def\kbd#1{{\def\look{#1}\expandafter\kbdsub\look??\par}}
+
+\def\xkey{\key}
+\def\kbdsub#1#2#3\par{%
+ \def\one{#1}\def\three{#3}\def\threex{??}%
+ \ifx\one\xkey\ifx\threex\three \key{#2}%
+ \else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi
+ \else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi
+}
+
+% definition of @key that produces a lozenge. Doesn't adjust to text size.
+%\setfont\keyrm\rmshape{8}{1000}{OT1}
+%\font\keysy=cmsy9
+%\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{%
+% \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{%
+% \vbox{\hrule\kern-0.4pt
+% \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}%
+% \kern-0.4pt\hrule}%
+% \kern-.06em\raise0.4pt\hbox{\angleright}}}}
+
+% definition of @key with no lozenge. If the current font is already
+% monospace, don't change it; that way, we respect @kbdinputstyle. But
+% if it isn't monospace, then use \tt.
+%
+\def\key#1{{\setupmarkupstyle{key}%
+ \nohyphenation
+ \ifmonospace\else\tt\fi
+ #1}\null}
+
+% @clicksequence{File @click{} Open ...}
+\def\clicksequence#1{\begingroup #1\endgroup}
+
+% @clickstyle @arrow (by default)
+\parseargdef\clickstyle{\def\click{#1}}
+\def\click{\arrow}
+
+% Typeset a dimension, e.g., `in' or `pt'. The only reason for the
+% argument is to make the input look right: @dmn{pt} instead of @dmn{}pt.
+%
+\def\dmn#1{\thinspace #1}
+
+% @acronym for "FBI", "NATO", and the like.
+% We print this one point size smaller, since it's intended for
+% all-uppercase.
+%
+\def\acronym#1{\doacronym #1,,\finish}
+\def\doacronym#1,#2,#3\finish{%
+ {\switchtolsize #1}%
+ \def\temp{#2}%
+ \ifx\temp\empty \else
+ \space ({\unsepspaces \ignorespaces \temp \unskip})%
+ \fi
+ \null % reset \spacefactor=1000
+}
+
+% @abbr for "Comput. J." and the like.
+% No font change, but don't do end-of-sentence spacing.
+%
+\def\abbr#1{\doabbr #1,,\finish}
+\def\doabbr#1,#2,#3\finish{%
+ {\plainfrenchspacing #1}%
+ \def\temp{#2}%
+ \ifx\temp\empty \else
+ \space ({\unsepspaces \ignorespaces \temp \unskip})%
+ \fi
+ \null % reset \spacefactor=1000
+}
+
+% @asis just yields its argument. Used with @table, for example.
+%
+\def\asis#1{#1}
+
+% @math outputs its argument in math mode.
+%
+% One complication: _ usually means subscripts, but it could also mean
+% an actual _ character, as in @math{@var{some_variable} + 1}. So make
+% _ active, and distinguish by seeing if the current family is \slfam,
+% which is what @var uses.
+{
+ \catcode`\_ = \active
+ \gdef\mathunderscore{%
+ \catcode`\_=\active
+ \def_{\ifnum\fam=\slfam \_\else\sb\fi}%
+ }
+}
+% Another complication: we want \\ (and @\) to output a math (or tt) \.
+% FYI, plain.tex uses \\ as a temporary control sequence (for no
+% particular reason), but this is not advertised and we don't care.
+%
+% The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\.
+\def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi}
+%
+\def\math{%
+ \ifmmode\else % only go into math if not in math mode already
+ \tex
+ \mathunderscore
+ \let\\ = \mathbackslash
+ \mathactive
+ % make the texinfo accent commands work in math mode
+ \let\"=\ddot
+ \let\'=\acute
+ \let\==\bar
+ \let\^=\hat
+ \let\`=\grave
+ \let\u=\breve
+ \let\v=\check
+ \let\~=\tilde
+ \let\dotaccent=\dot
+ % have to provide another name for sup operator
+ \let\mathopsup=\sup
+ $\expandafter\finishmath\fi
+}
+\def\finishmath#1{#1$\endgroup} % Close the group opened by \tex.
+
+% Some active characters (such as <) are spaced differently in math.
+% We have to reset their definitions in case the @math was an argument
+% to a command which sets the catcodes (such as @item or @section).
+%
+{
+ \catcode`^ = \active
+ \catcode`< = \active
+ \catcode`> = \active
+ \catcode`+ = \active
+ \catcode`' = \active
+ \gdef\mathactive{%
+ \let^ = \ptexhat
+ \let< = \ptexless
+ \let> = \ptexgtr
+ \let+ = \ptexplus
+ \let' = \ptexquoteright
+ }
+}
+
+% for @sub and @sup, if in math mode, just do a normal sub/superscript.
+% If in text, use math to place as sub/superscript, but switch
+% into text mode, with smaller fonts. This is a different font than the
+% one used for real math sub/superscripts (8pt vs. 7pt), but let's not
+% fix it (significant additions to font machinery) until someone notices.
+%
+\def\sub{\ifmmode \expandafter\sb \else \expandafter\finishsub\fi}
+\def\finishsub#1{$\sb{\hbox{\switchtolllsize #1}}$}%
+%
+\def\sup{\ifmmode \expandafter\ptexsp \else \expandafter\finishsup\fi}
+\def\finishsup#1{$\ptexsp{\hbox{\switchtolllsize #1}}$}%
+
+% @inlinefmt{FMTNAME,PROCESSED-TEXT} and @inlineraw{FMTNAME,RAW-TEXT}.
+% Ignore unless FMTNAME == tex; then it is like @iftex and @tex,
+% except specified as a normal braced arg, so no newlines to worry about.
+%
+\def\outfmtnametex{tex}
+%
+\long\def\inlinefmt#1{\doinlinefmt #1,\finish}
+\long\def\doinlinefmt#1,#2,\finish{%
+ \def\inlinefmtname{#1}%
+ \ifx\inlinefmtname\outfmtnametex \ignorespaces #2\fi
+}
+%
+% @inlinefmtifelse{FMTNAME,THEN-TEXT,ELSE-TEXT} expands THEN-TEXT if
+% FMTNAME is tex, else ELSE-TEXT.
+\long\def\inlinefmtifelse#1{\doinlinefmtifelse #1,,,\finish}
+\long\def\doinlinefmtifelse#1,#2,#3,#4,\finish{%
+ \def\inlinefmtname{#1}%
+ \ifx\inlinefmtname\outfmtnametex \ignorespaces #2\else \ignorespaces #3\fi
+}
+%
+% For raw, must switch into @tex before parsing the argument, to avoid
+% setting catcodes prematurely. Doing it this way means that, for
+% example, @inlineraw{html, foo{bar} gets a parse error instead of being
+% ignored. But this isn't important because if people want a literal
+% *right* brace they would have to use a command anyway, so they may as
+% well use a command to get a left brace too. We could re-use the
+% delimiter character idea from \verb, but it seems like overkill.
+%
+\long\def\inlineraw{\tex \doinlineraw}
+\long\def\doinlineraw#1{\doinlinerawtwo #1,\finish}
+\def\doinlinerawtwo#1,#2,\finish{%
+ \def\inlinerawname{#1}%
+ \ifx\inlinerawname\outfmtnametex \ignorespaces #2\fi
+ \endgroup % close group opened by \tex.
+}
+
+% @inlineifset{VAR, TEXT} expands TEXT if VAR is @set.
+%
+\long\def\inlineifset#1{\doinlineifset #1,\finish}
+\long\def\doinlineifset#1,#2,\finish{%
+ \def\inlinevarname{#1}%
+ \expandafter\ifx\csname SET\inlinevarname\endcsname\relax
+ \else\ignorespaces#2\fi
+}
+
+% @inlineifclear{VAR, TEXT} expands TEXT if VAR is not @set.
+%
+\long\def\inlineifclear#1{\doinlineifclear #1,\finish}
+\long\def\doinlineifclear#1,#2,\finish{%
+ \def\inlinevarname{#1}%
+ \expandafter\ifx\csname SET\inlinevarname\endcsname\relax \ignorespaces#2\fi
+}
+
+
+\message{glyphs,}
+% and logos.
+
+% @@ prints an @, as does @atchar{}.
+\def\@{\char64 }
+\let\atchar=\@
+
+% @{ @} @lbracechar{} @rbracechar{} all generate brace characters.
+\def\lbracechar{{\ifmonospace\char123\else\ensuremath\lbrace\fi}}
+\def\rbracechar{{\ifmonospace\char125\else\ensuremath\rbrace\fi}}
+\let\{=\lbracechar
+\let\}=\rbracechar
+
+% @comma{} to avoid , parsing problems.
+\let\comma = ,
+
+% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent
+% Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H.
+\let\, = \ptexc
+\let\dotaccent = \ptexdot
+\def\ringaccent#1{{\accent23 #1}}
+\let\tieaccent = \ptext
+\let\ubaraccent = \ptexb
+\let\udotaccent = \d
+
+% Other special characters: @questiondown @exclamdown @ordf @ordm
+% Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss.
+\def\questiondown{?`}
+\def\exclamdown{!`}
+\def\ordf{\leavevmode\raise1ex\hbox{\switchtolllsize \underbar{a}}}
+\def\ordm{\leavevmode\raise1ex\hbox{\switchtolllsize \underbar{o}}}
+
+% Dotless i and dotless j, used for accents.
+\def\imacro{i}
+\def\jmacro{j}
+\def\dotless#1{%
+ \def\temp{#1}%
+ \ifx\temp\imacro \ifmmode\imath \else\ptexi \fi
+ \else\ifx\temp\jmacro \ifmmode\jmath \else\j \fi
+ \else \errmessage{@dotless can be used only with i or j}%
+ \fi\fi
+}
+
+% The \TeX{} logo, as in plain, but resetting the spacing so that a
+% period following counts as ending a sentence. (Idea found in latex.)
+%
+\edef\TeX{\TeX \spacefactor=1000 }
+
+% @LaTeX{} logo. Not quite the same results as the definition in
+% latex.ltx, since we use a different font for the raised A; it's most
+% convenient for us to use an explicitly smaller font, rather than using
+% the \scriptstyle font (since we don't reset \scriptstyle and
+% \scriptscriptstyle).
+%
+\def\LaTeX{%
+ L\kern-.36em
+ {\setbox0=\hbox{T}%
+ \vbox to \ht0{\hbox{%
+ \ifx\textnominalsize\xwordpt
+ % for 10pt running text, lllsize (8pt) is too small for the A in LaTeX.
+ % Revert to plain's \scriptsize, which is 7pt.
+ \count255=\the\fam $\fam\count255 \scriptstyle A$%
+ \else
+ % For 11pt, we can use our lllsize.
+ \switchtolllsize A%
+ \fi
+ }%
+ \vss
+ }}%
+ \kern-.15em
+ \TeX
+}
+
+% Some math mode symbols. Define \ensuremath to switch into math mode
+% unless we are already there. Expansion tricks may not be needed here,
+% but safer, and can't hurt.
+\def\ensuremath{\ifmmode \expandafter\asis \else\expandafter\ensuredmath \fi}
+\def\ensuredmath#1{$\relax#1$}
+%
+\def\bullet{\ensuremath\ptexbullet}
+\def\geq{\ensuremath\ge}
+\def\leq{\ensuremath\le}
+\def\minus{\ensuremath-}
+
+% @dots{} outputs an ellipsis using the current font.
+% We do .5em per period so that it has the same spacing in the cm
+% typewriter fonts as three actual period characters; on the other hand,
+% in other typewriter fonts three periods are wider than 1.5em. So do
+% whichever is larger.
+%
+\def\dots{%
+ \leavevmode
+ \setbox0=\hbox{...}% get width of three periods
+ \ifdim\wd0 > 1.5em
+ \dimen0 = \wd0
+ \else
+ \dimen0 = 1.5em
+ \fi
+ \hbox to \dimen0{%
+ \hskip 0pt plus.25fil
+ .\hskip 0pt plus1fil
+ .\hskip 0pt plus1fil
+ .\hskip 0pt plus.5fil
+ }%
+}
+
+% @enddots{} is an end-of-sentence ellipsis.
+%
+\def\enddots{%
+ \dots
+ \spacefactor=\endofsentencespacefactor
+}
+
+% @point{}, @result{}, @expansion{}, @print{}, @equiv{}.
+%
+% Since these characters are used in examples, they should be an even number of
+% \tt widths. Each \tt character is 1en, so two makes it 1em.
+%
+\def\point{$\star$}
+\def\arrow{\leavevmode\raise.05ex\hbox to 1em{\hfil$\rightarrow$\hfil}}
+\def\result{\leavevmode\raise.05ex\hbox to 1em{\hfil$\Rightarrow$\hfil}}
+\def\expansion{\leavevmode\hbox to 1em{\hfil$\mapsto$\hfil}}
+\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}}
+\def\equiv{\leavevmode\hbox to 1em{\hfil$\ptexequiv$\hfil}}
+
+% The @error{} command.
+% Adapted from the TeXbook's \boxit.
+%
+\newbox\errorbox
+%
+{\ttfont \global\dimen0 = 3em}% Width of the box.
+\dimen2 = .55pt % Thickness of rules
+% The text. (`r' is open on the right, `e' somewhat less so on the left.)
+\setbox0 = \hbox{\kern-.75pt \reducedsf \putworderror\kern-1.5pt}
+%
+\setbox\errorbox=\hbox to \dimen0{\hfil
+ \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right.
+ \advance\hsize by -2\dimen2 % Rules.
+ \vbox{%
+ \hrule height\dimen2
+ \hbox{\vrule width\dimen2 \kern3pt % Space to left of text.
+ \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below.
+ \kern3pt\vrule width\dimen2}% Space to right.
+ \hrule height\dimen2}
+ \hfil}
+%
+\def\error{\leavevmode\lower.7ex\copy\errorbox}
+
+% @pounds{} is a sterling sign, which Knuth put in the CM italic font.
+%
+\def\pounds{{\it\$}}
+
+% @euro{} comes from a separate font, depending on the current style.
+% We use the free feym* fonts from the eurosym package by Henrik
+% Theiling, which support regular, slanted, bold and bold slanted (and
+% "outlined" (blackboard board, sort of) versions, which we don't need).
+% It is available from http://www.ctan.org/tex-archive/fonts/eurosym.
+%
+% Although only regular is the truly official Euro symbol, we ignore
+% that. The Euro is designed to be slightly taller than the regular
+% font height.
+%
+% feymr - regular
+% feymo - slanted
+% feybr - bold
+% feybo - bold slanted
+%
+% There is no good (free) typewriter version, to my knowledge.
+% A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide.
+% Hmm.
+%
+% Also doesn't work in math. Do we need to do math with euro symbols?
+% Hope not.
+%
+%
+\def\euro{{\eurofont e}}
+\def\eurofont{%
+ % We set the font at each command, rather than predefining it in
+ % \textfonts and the other font-switching commands, so that
+ % installations which never need the symbol don't have to have the
+ % font installed.
+ %
+ % There is only one designed size (nominal 10pt), so we always scale
+ % that to the current nominal size.
+ %
+ % By the way, simply using "at 1em" works for cmr10 and the like, but
+ % does not work for cmbx10 and other extended/shrunken fonts.
+ %
+ \def\eurosize{\csname\curfontsize nominalsize\endcsname}%
+ %
+ \ifx\curfontstyle\bfstylename
+ % bold:
+ \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize
+ \else
+ % regular:
+ \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize
+ \fi
+ \thiseurofont
+}
+
+% Glyphs from the EC fonts. We don't use \let for the aliases, because
+% sometimes we redefine the original macro, and the alias should reflect
+% the redefinition.
+%
+% Use LaTeX names for the Icelandic letters.
+\def\DH{{\ecfont \char"D0}} % Eth
+\def\dh{{\ecfont \char"F0}} % eth
+\def\TH{{\ecfont \char"DE}} % Thorn
+\def\th{{\ecfont \char"FE}} % thorn
+%
+\def\guillemetleft{{\ecfont \char"13}}
+\def\guillemotleft{\guillemetleft}
+\def\guillemetright{{\ecfont \char"14}}
+\def\guillemotright{\guillemetright}
+\def\guilsinglleft{{\ecfont \char"0E}}
+\def\guilsinglright{{\ecfont \char"0F}}
+\def\quotedblbase{{\ecfont \char"12}}
+\def\quotesinglbase{{\ecfont \char"0D}}
+%
+% This positioning is not perfect (see the ogonek LaTeX package), but
+% we have the precomposed glyphs for the most common cases. We put the
+% tests to use those glyphs in the single \ogonek macro so we have fewer
+% dummy definitions to worry about for index entries, etc.
+%
+% ogonek is also used with other letters in Lithuanian (IOU), but using
+% the precomposed glyphs for those is not so easy since they aren't in
+% the same EC font.
+\def\ogonek#1{{%
+ \def\temp{#1}%
+ \ifx\temp\macrocharA\Aogonek
+ \else\ifx\temp\macrochara\aogonek
+ \else\ifx\temp\macrocharE\Eogonek
+ \else\ifx\temp\macrochare\eogonek
+ \else
+ \ecfont \setbox0=\hbox{#1}%
+ \ifdim\ht0=1ex\accent"0C #1%
+ \else\ooalign{\unhbox0\crcr\hidewidth\char"0C \hidewidth}%
+ \fi
+ \fi\fi\fi\fi
+ }%
+}
+\def\Aogonek{{\ecfont \char"81}}\def\macrocharA{A}
+\def\aogonek{{\ecfont \char"A1}}\def\macrochara{a}
+\def\Eogonek{{\ecfont \char"86}}\def\macrocharE{E}
+\def\eogonek{{\ecfont \char"A6}}\def\macrochare{e}
+%
+% Use the European Computer Modern fonts (cm-super in outline format)
+% for non-CM glyphs. That is ec* for regular text and tc* for the text
+% companion symbols (LaTeX TS1 encoding). Both are part of the ec
+% package and follow the same conventions.
+%
+\def\ecfont{\etcfont{e}}
+\def\tcfont{\etcfont{t}}
+%
+\def\etcfont#1{%
+ % We can't distinguish serif/sans and italic/slanted, but this
+ % is used for crude hacks anyway (like adding French and German
+ % quotes to documents typeset with CM, where we lose kerning), so
+ % hopefully nobody will notice/care.
+ \edef\ecsize{\csname\curfontsize ecsize\endcsname}%
+ \edef\nominalsize{\csname\curfontsize nominalsize\endcsname}%
+ \ifmonospace
+ % typewriter:
+ \font\thisecfont = #1ctt\ecsize \space at \nominalsize
+ \else
+ \ifx\curfontstyle\bfstylename
+ % bold:
+ \font\thisecfont = #1cb\ifusingit{i}{x}\ecsize \space at \nominalsize
+ \else
+ % regular:
+ \font\thisecfont = #1c\ifusingit{ti}{rm}\ecsize \space at \nominalsize
+ \fi
+ \fi
+ \thisecfont
+}
+
+% @registeredsymbol - R in a circle. The font for the R should really
+% be smaller yet, but lllsize is the best we can do for now.
+% Adapted from the plain.tex definition of \copyright.
+%
+\def\registeredsymbol{%
+ $^{{\ooalign{\hfil\raise.07ex\hbox{\switchtolllsize R}%
+ \hfil\crcr\Orb}}%
+ }$%
+}
+
+% @textdegree - the normal degrees sign.
+%
+\def\textdegree{$^\circ$}
+
+% Laurent Siebenmann reports \Orb undefined with:
+% Textures 1.7.7 (preloaded format=plain 93.10.14) (68K) 16 APR 2004 02:38
+% so we'll define it if necessary.
+%
+\ifx\Orb\thisisundefined
+\def\Orb{\mathhexbox20D}
+\fi
+
+% Quotes.
+\chardef\quotedblleft="5C
+\chardef\quotedblright=`\"
+\chardef\quoteleft=`\`
+\chardef\quoteright=`\'
+
+
+\message{page headings,}
+
+\newskip\titlepagetopglue \titlepagetopglue = 1.5in
+\newskip\titlepagebottomglue \titlepagebottomglue = 2pc
+
+% First the title page. Must do @settitle before @titlepage.
+\newif\ifseenauthor
+\newif\iffinishedtitlepage
+
+% @setcontentsaftertitlepage used to do an implicit @contents or
+% @shortcontents after @end titlepage, but it is now obsolete.
+\def\setcontentsaftertitlepage{%
+ \errmessage{@setcontentsaftertitlepage has been removed as a Texinfo
+ command; move your @contents command if you want the contents
+ after the title page.}}%
+\def\setshortcontentsaftertitlepage{%
+ \errmessage{@setshortcontentsaftertitlepage has been removed as a Texinfo
+ command; move your @shortcontents and @contents commands if you
+ want the contents after the title page.}}%
+
+\parseargdef\shorttitlepage{%
+ \begingroup \hbox{}\vskip 1.5in \chaprm \centerline{#1}%
+ \endgroup\page\hbox{}\page}
+
+\envdef\titlepage{%
+ % Open one extra group, as we want to close it in the middle of \Etitlepage.
+ \begingroup
+ \parindent=0pt \textfonts
+ % Leave some space at the very top of the page.
+ \vglue\titlepagetopglue
+ % No rule at page bottom unless we print one at the top with @title.
+ \finishedtitlepagetrue
+ %
+ % Most title ``pages'' are actually two pages long, with space
+ % at the top of the second. We don't want the ragged left on the second.
+ \let\oldpage = \page
+ \def\page{%
+ \iffinishedtitlepage\else
+ \finishtitlepage
+ \fi
+ \let\page = \oldpage
+ \page
+ \null
+ }%
+}
+
+\def\Etitlepage{%
+ \iffinishedtitlepage\else
+ \finishtitlepage
+ \fi
+ % It is important to do the page break before ending the group,
+ % because the headline and footline are only empty inside the group.
+ % If we use the new definition of \page, we always get a blank page
+ % after the title page, which we certainly don't want.
+ \oldpage
+ \endgroup
+ %
+ % Need this before the \...aftertitlepage checks so that if they are
+ % in effect the toc pages will come out with page numbers.
+ \HEADINGSon
+}
+
+\def\finishtitlepage{%
+ \vskip4pt \hrule height 2pt width \hsize
+ \vskip\titlepagebottomglue
+ \finishedtitlepagetrue
+}
+
+% Settings used for typesetting titles: no hyphenation, no indentation,
+% don't worry much about spacing, ragged right. This should be used
+% inside a \vbox, and fonts need to be set appropriately first. \par should
+% be specified before the end of the \vbox, since a vbox is a group.
+%
+\def\raggedtitlesettings{%
+ \rm
+ \hyphenpenalty=10000
+ \parindent=0pt
+ \tolerance=5000
+ \ptexraggedright
+}
+
+% Macros to be used within @titlepage:
+
+\let\subtitlerm=\rmfont
+\def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}
+
+\parseargdef\title{%
+ \checkenv\titlepage
+ \vbox{\titlefonts \raggedtitlesettings #1\par}%
+ % print a rule at the page bottom also.
+ \finishedtitlepagefalse
+ \vskip4pt \hrule height 4pt width \hsize \vskip4pt
+}
+
+\parseargdef\subtitle{%
+ \checkenv\titlepage
+ {\subtitlefont \rightline{#1}}%
+}
+
+% @author should come last, but may come many times.
+% It can also be used inside @quotation.
+%
+\parseargdef\author{%
+ \def\temp{\quotation}%
+ \ifx\thisenv\temp
+ \def\quotationauthor{#1}% printed in \Equotation.
+ \else
+ \checkenv\titlepage
+ \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi
+ {\secfonts\rm \leftline{#1}}%
+ \fi
+}
+
+
+% Set up page headings and footings.
+
+\let\thispage=\folio
+
+\newtoks\evenheadline % headline on even pages
+\newtoks\oddheadline % headline on odd pages
+\newtoks\evenfootline % footline on even pages
+\newtoks\oddfootline % footline on odd pages
+
+% Now make \makeheadline and \makefootline in Plain TeX use those variables
+\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline
+ \else \the\evenheadline \fi}}
+\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline
+ \else \the\evenfootline \fi}\HEADINGShook}
+\let\HEADINGShook=\relax
+
+% Commands to set those variables.
+% For example, this is what @headings on does
+% @evenheading @thistitle|@thispage|@thischapter
+% @oddheading @thischapter|@thispage|@thistitle
+% @evenfooting @thisfile||
+% @oddfooting ||@thisfile
+
+
+\def\evenheading{\parsearg\evenheadingxxx}
+\def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish}
+\def\evenheadingyyy #1\|#2\|#3\|#4\finish{%
+\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\def\oddheading{\parsearg\oddheadingxxx}
+\def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish}
+\def\oddheadingyyy #1\|#2\|#3\|#4\finish{%
+\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}%
+
+\def\evenfooting{\parsearg\evenfootingxxx}
+\def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish}
+\def\evenfootingyyy #1\|#2\|#3\|#4\finish{%
+\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\def\oddfooting{\parsearg\oddfootingxxx}
+\def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish}
+\def\oddfootingyyy #1\|#2\|#3\|#4\finish{%
+ \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}%
+ %
+ % Leave some space for the footline. Hopefully ok to assume
+ % @evenfooting will not be used by itself.
+ \global\advance\txipageheight by -12pt
+ \global\advance\vsize by -12pt
+}
+
+\parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}}
+
+% @evenheadingmarks top \thischapter <- chapter at the top of a page
+% @evenheadingmarks bottom \thischapter <- chapter at the bottom of a page
+%
+% The same set of arguments for:
+%
+% @oddheadingmarks
+% @evenfootingmarks
+% @oddfootingmarks
+% @everyheadingmarks
+% @everyfootingmarks
+
+% These define \getoddheadingmarks, \getevenheadingmarks,
+% \getoddfootingmarks, and \getevenfootingmarks, each to one of
+% \gettopheadingmarks, \getbottomheadingmarks.
+%
+\def\evenheadingmarks{\headingmarks{even}{heading}}
+\def\oddheadingmarks{\headingmarks{odd}{heading}}
+\def\evenfootingmarks{\headingmarks{even}{footing}}
+\def\oddfootingmarks{\headingmarks{odd}{footing}}
+\parseargdef\everyheadingmarks{\headingmarks{even}{heading}{#1}
+ \headingmarks{odd}{heading}{#1} }
+\parseargdef\everyfootingmarks{\headingmarks{even}{footing}{#1}
+ \headingmarks{odd}{footing}{#1} }
+% #1 = even/odd, #2 = heading/footing, #3 = top/bottom.
+\def\headingmarks#1#2#3 {%
+ \expandafter\let\expandafter\temp \csname get#3headingmarks\endcsname
+ \global\expandafter\let\csname get#1#2marks\endcsname \temp
+}
+
+\everyheadingmarks bottom
+\everyfootingmarks bottom
+
+% @headings double turns headings on for double-sided printing.
+% @headings single turns headings on for single-sided printing.
+% @headings off turns them off.
+% @headings on same as @headings double, retained for compatibility.
+% @headings after turns on double-sided headings after this page.
+% @headings doubleafter turns on double-sided headings after this page.
+% @headings singleafter turns on single-sided headings after this page.
+% By default, they are off at the start of a document,
+% and turned `on' after @end titlepage.
+
+\parseargdef\headings{\csname HEADINGS#1\endcsname}
+
+\def\headingsoff{% non-global headings elimination
+ \evenheadline={\hfil}\evenfootline={\hfil}%
+ \oddheadline={\hfil}\oddfootline={\hfil}%
+}
+
+\def\HEADINGSoff{{\globaldefs=1 \headingsoff}} % global setting
+\HEADINGSoff % it's the default
+
+% When we turn headings on, set the page number to 1.
+% For double-sided printing, put current file name in lower left corner,
+% chapter name on inside top of right hand pages, document
+% title on inside top of left hand pages, and page numbers on outside top
+% edge of all pages.
+\def\HEADINGSdouble{%
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\global\oddheadline={\line{\thischapterheading\hfil\folio}}
+\global\let\contentsalignmacro = \chapoddpage
+}
+\let\contentsalignmacro = \chappager
+
+% For single-sided printing, chapter title goes across top left of page,
+% page number on top right.
+\def\HEADINGSsingle{%
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\thischapterheading\hfil\folio}}
+\global\oddheadline={\line{\thischapterheading\hfil\folio}}
+\global\let\contentsalignmacro = \chappager
+}
+\def\HEADINGSon{\HEADINGSdouble}
+
+\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex}
+\let\HEADINGSdoubleafter=\HEADINGSafter
+\def\HEADINGSdoublex{%
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\global\oddheadline={\line{\thischapterheading\hfil\folio}}
+\global\let\contentsalignmacro = \chapoddpage
+}
+
+\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex}
+\def\HEADINGSsinglex{%
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\thischapterheading\hfil\folio}}
+\global\oddheadline={\line{\thischapterheading\hfil\folio}}
+\global\let\contentsalignmacro = \chappager
+}
+
+% Subroutines used in generating headings
+% This produces Day Month Year style of output.
+% Only define if not already defined, in case a txi-??.tex file has set
+% up a different format (e.g., txi-cs.tex does this).
+\ifx\today\thisisundefined
+\def\today{%
+ \number\day\space
+ \ifcase\month
+ \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr
+ \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug
+ \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec
+ \fi
+ \space\number\year}
+\fi
+
+% @settitle line... specifies the title of the document, for headings.
+% It generates no output of its own.
+\def\thistitle{\putwordNoTitle}
+\def\settitle{\parsearg{\gdef\thistitle}}
+
+
+\message{tables,}
+% Tables -- @table, @ftable, @vtable, @item(x).
+
+% default indentation of table text
+\newdimen\tableindent \tableindent=.8in
+% default indentation of @itemize and @enumerate text
+\newdimen\itemindent \itemindent=.3in
+% margin between end of table item and start of table text.
+\newdimen\itemmargin \itemmargin=.1in
+
+% used internally for \itemindent minus \itemmargin
+\newdimen\itemmax
+
+% Note @table, @ftable, and @vtable define @item, @itemx, etc., with
+% these defs.
+% They also define \itemindex
+% to index the item name in whatever manner is desired (perhaps none).
+
+\newif\ifitemxneedsnegativevskip
+
+\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi}
+
+\def\internalBitem{\smallbreak \parsearg\itemzzz}
+\def\internalBitemx{\itemxpar \parsearg\itemzzz}
+
+\def\itemzzz #1{\begingroup %
+ \advance\hsize by -\rightskip
+ \advance\hsize by -\tableindent
+ \setbox0=\hbox{\itemindicate{#1}}%
+ \itemindex{#1}%
+ \nobreak % This prevents a break before @itemx.
+ %
+ % If the item text does not fit in the space we have, put it on a line
+ % by itself, and do not allow a page break either before or after that
+ % line. We do not start a paragraph here because then if the next
+ % command is, e.g., @kindex, the whatsit would get put into the
+ % horizontal list on a line by itself, resulting in extra blank space.
+ \ifdim \wd0>\itemmax
+ %
+ % Make this a paragraph so we get the \parskip glue and wrapping,
+ % but leave it ragged-right.
+ \begingroup
+ \advance\leftskip by-\tableindent
+ \advance\hsize by\tableindent
+ \advance\rightskip by0pt plus1fil\relax
+ \leavevmode\unhbox0\par
+ \endgroup
+ %
+ % We're going to be starting a paragraph, but we don't want the
+ % \parskip glue -- logically it's part of the @item we just started.
+ \nobreak \vskip-\parskip
+ %
+ % Stop a page break at the \parskip glue coming up. However, if
+ % what follows is an environment such as @example, there will be no
+ % \parskip glue; then the negative vskip we just inserted would
+ % cause the example and the item to crash together. So we use this
+ % bizarre value of 10001 as a signal to \aboveenvbreak to insert
+ % \parskip glue after all. Section titles are handled this way also.
+ %
+ \penalty 10001
+ \endgroup
+ \itemxneedsnegativevskipfalse
+ \else
+ % The item text fits into the space. Start a paragraph, so that the
+ % following text (if any) will end up on the same line.
+ \noindent
+ % Do this with kerns and \unhbox so that if there is a footnote in
+ % the item text, it can migrate to the main vertical list and
+ % eventually be printed.
+ \nobreak\kern-\tableindent
+ \dimen0 = \itemmax \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0
+ \unhbox0
+ \nobreak\kern\dimen0
+ \endgroup
+ \itemxneedsnegativevskiptrue
+ \fi
+}
+
+\def\item{\errmessage{@item while not in a list environment}}
+\def\itemx{\errmessage{@itemx while not in a list environment}}
+
+% @table, @ftable, @vtable.
+\envdef\table{%
+ \let\itemindex\gobble
+ \tablecheck{table}%
+}
+\envdef\ftable{%
+ \def\itemindex ##1{\doind {fn}{\code{##1}}}%
+ \tablecheck{ftable}%
+}
+\envdef\vtable{%
+ \def\itemindex ##1{\doind {vr}{\code{##1}}}%
+ \tablecheck{vtable}%
+}
+\def\tablecheck#1{%
+ \ifnum \the\catcode`\^^M=\active
+ \endgroup
+ \errmessage{This command won't work in this context; perhaps the problem is
+ that we are \inenvironment\thisenv}%
+ \def\next{\doignore{#1}}%
+ \else
+ \let\next\tablex
+ \fi
+ \next
+}
+\def\tablex#1{%
+ \def\itemindicate{#1}%
+ \parsearg\tabley
+}
+\def\tabley#1{%
+ {%
+ \makevalueexpandable
+ \edef\temp{\noexpand\tablez #1\space\space\space}%
+ \expandafter
+ }\temp \endtablez
+}
+\def\tablez #1 #2 #3 #4\endtablez{%
+ \aboveenvbreak
+ \ifnum 0#1>0 \advance \leftskip by #1\mil \fi
+ \ifnum 0#2>0 \tableindent=#2\mil \fi
+ \ifnum 0#3>0 \advance \rightskip by #3\mil \fi
+ \itemmax=\tableindent
+ \advance \itemmax by -\itemmargin
+ \advance \leftskip by \tableindent
+ \exdentamount=\tableindent
+ \parindent = 0pt
+ \parskip = \smallskipamount
+ \ifdim \parskip=0pt \parskip=2pt \fi
+ \let\item = \internalBitem
+ \let\itemx = \internalBitemx
+}
+\def\Etable{\endgraf\afterenvbreak}
+\let\Eftable\Etable
+\let\Evtable\Etable
+\let\Eitemize\Etable
+\let\Eenumerate\Etable
+
+% This is the counter used by @enumerate, which is really @itemize
+
+\newcount \itemno
+
+\envdef\itemize{\parsearg\doitemize}
+
+\def\doitemize#1{%
+ \aboveenvbreak
+ \itemmax=\itemindent
+ \advance\itemmax by -\itemmargin
+ \advance\leftskip by \itemindent
+ \exdentamount=\itemindent
+ \parindent=0pt
+ \parskip=\smallskipamount
+ \ifdim\parskip=0pt \parskip=2pt \fi
+ %
+ % Try typesetting the item mark so that if the document erroneously says
+ % something like @itemize @samp (intending @table), there's an error
+ % right away at the @itemize. It's not the best error message in the
+ % world, but it's better than leaving it to the @item. This means if
+ % the user wants an empty mark, they have to say @w{} not just @w.
+ \def\itemcontents{#1}%
+ \setbox0 = \hbox{\itemcontents}%
+ %
+ % @itemize with no arg is equivalent to @itemize @bullet.
+ \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi
+ %
+ \let\item=\itemizeitem
+}
+
+% Definition of @item while inside @itemize and @enumerate.
+%
+\def\itemizeitem{%
+ \advance\itemno by 1 % for enumerations
+ {\let\par=\endgraf \smallbreak}% reasonable place to break
+ {%
+ % If the document has an @itemize directly after a section title, a
+ % \nobreak will be last on the list, and \sectionheading will have
+ % done a \vskip-\parskip. In that case, we don't want to zero
+ % parskip, or the item text will crash with the heading. On the
+ % other hand, when there is normal text preceding the item (as there
+ % usually is), we do want to zero parskip, or there would be too much
+ % space. In that case, we won't have a \nobreak before. At least
+ % that's the theory.
+ \ifnum\lastpenalty<10000 \parskip=0in \fi
+ \noindent
+ \hbox to 0pt{\hss \itemcontents \kern\itemmargin}%
+ %
+ \ifinner\else
+ \vadjust{\penalty 1200}% not good to break after first line of item.
+ \fi
+ % We can be in inner vertical mode in a footnote, although an
+ % @itemize looks awful there.
+ }%
+ \flushcr
+}
+
+% \splitoff TOKENS\endmark defines \first to be the first token in
+% TOKENS, and \rest to be the remainder.
+%
+\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}%
+
+% Allow an optional argument of an uppercase letter, lowercase letter,
+% or number, to specify the first label in the enumerated list. No
+% argument is the same as `1'.
+%
+\envparseargdef\enumerate{\enumeratey #1 \endenumeratey}
+\def\enumeratey #1 #2\endenumeratey{%
+ % If we were given no argument, pretend we were given `1'.
+ \def\thearg{#1}%
+ \ifx\thearg\empty \def\thearg{1}\fi
+ %
+ % Detect if the argument is a single token. If so, it might be a
+ % letter. Otherwise, the only valid thing it can be is a number.
+ % (We will always have one token, because of the test we just made.
+ % This is a good thing, since \splitoff doesn't work given nothing at
+ % all -- the first parameter is undelimited.)
+ \expandafter\splitoff\thearg\endmark
+ \ifx\rest\empty
+ % Only one token in the argument. It could still be anything.
+ % A ``lowercase letter'' is one whose \lccode is nonzero.
+ % An ``uppercase letter'' is one whose \lccode is both nonzero, and
+ % not equal to itself.
+ % Otherwise, we assume it's a number.
+ %
+ % We need the \relax at the end of the \ifnum lines to stop TeX from
+ % continuing to look for a <number>.
+ %
+ \ifnum\lccode\expandafter`\thearg=0\relax
+ \numericenumerate % a number (we hope)
+ \else
+ % It's a letter.
+ \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax
+ \lowercaseenumerate % lowercase letter
+ \else
+ \uppercaseenumerate % uppercase letter
+ \fi
+ \fi
+ \else
+ % Multiple tokens in the argument. We hope it's a number.
+ \numericenumerate
+ \fi
+}
+
+% An @enumerate whose labels are integers. The starting integer is
+% given in \thearg.
+%
+\def\numericenumerate{%
+ \itemno = \thearg
+ \startenumeration{\the\itemno}%
+}
+
+% The starting (lowercase) letter is in \thearg.
+\def\lowercaseenumerate{%
+ \itemno = \expandafter`\thearg
+ \startenumeration{%
+ % Be sure we're not beyond the end of the alphabet.
+ \ifnum\itemno=0
+ \errmessage{No more lowercase letters in @enumerate; get a bigger
+ alphabet}%
+ \fi
+ \char\lccode\itemno
+ }%
+}
+
+% The starting (uppercase) letter is in \thearg.
+\def\uppercaseenumerate{%
+ \itemno = \expandafter`\thearg
+ \startenumeration{%
+ % Be sure we're not beyond the end of the alphabet.
+ \ifnum\itemno=0
+ \errmessage{No more uppercase letters in @enumerate; get a bigger
+ alphabet}
+ \fi
+ \char\uccode\itemno
+ }%
+}
+
+% Call \doitemize, adding a period to the first argument and supplying the
+% common last two arguments. Also subtract one from the initial value in
+% \itemno, since @item increments \itemno.
+%
+\def\startenumeration#1{%
+ \advance\itemno by -1
+ \doitemize{#1.}\flushcr
+}
+
+% @alphaenumerate and @capsenumerate are abbreviations for giving an arg
+% to @enumerate.
+%
+\def\alphaenumerate{\enumerate{a}}
+\def\capsenumerate{\enumerate{A}}
+\def\Ealphaenumerate{\Eenumerate}
+\def\Ecapsenumerate{\Eenumerate}
+
+
+% @multitable macros
+% Amy Hendrickson, 8/18/94, 3/6/96
+%
+% @multitable ... @end multitable will make as many columns as desired.
+% Contents of each column will wrap at width given in preamble. Width
+% can be specified either with sample text given in a template line,
+% or in percent of \hsize, the current width of text on page.
+
+% Table can continue over pages but will only break between lines.
+
+% To make preamble:
+%
+% Either define widths of columns in terms of percent of \hsize:
+% @multitable @columnfractions .25 .3 .45
+% @item ...
+%
+% Numbers following @columnfractions are the percent of the total
+% current hsize to be used for each column. You may use as many
+% columns as desired.
+
+
+% Or use a template:
+% @multitable {Column 1 template} {Column 2 template} {Column 3 template}
+% @item ...
+% using the widest term desired in each column.
+
+% Each new table line starts with @item, each subsequent new column
+% starts with @tab. Empty columns may be produced by supplying @tab's
+% with nothing between them for as many times as empty columns are needed,
+% ie, @tab@tab@tab will produce two empty columns.
+
+% @item, @tab do not need to be on their own lines, but it will not hurt
+% if they are.
+
+% Sample multitable:
+
+% @multitable {Column 1 template} {Column 2 template} {Column 3 template}
+% @item first col stuff @tab second col stuff @tab third col
+% @item
+% first col stuff
+% @tab
+% second col stuff
+% @tab
+% third col
+% @item first col stuff @tab second col stuff
+% @tab Many paragraphs of text may be used in any column.
+%
+% They will wrap at the width determined by the template.
+% @item@tab@tab This will be in third column.
+% @end multitable
+
+% Default dimensions may be reset by user.
+% @multitableparskip is vertical space between paragraphs in table.
+% @multitableparindent is paragraph indent in table.
+% @multitablecolmargin is horizontal space to be left between columns.
+% @multitablelinespace is space to leave between table items, baseline
+% to baseline.
+% 0pt means it depends on current normal line spacing.
+%
+\newskip\multitableparskip
+\newskip\multitableparindent
+\newdimen\multitablecolspace
+\newskip\multitablelinespace
+\multitableparskip=0pt
+\multitableparindent=6pt
+\multitablecolspace=12pt
+\multitablelinespace=0pt
+
+% Macros used to set up halign preamble:
+%
+\let\endsetuptable\relax
+\def\xendsetuptable{\endsetuptable}
+\let\columnfractions\relax
+\def\xcolumnfractions{\columnfractions}
+\newif\ifsetpercent
+
+% #1 is the @columnfraction, usually a decimal number like .5, but might
+% be just 1. We just use it, whatever it is.
+%
+\def\pickupwholefraction#1 {%
+ \global\advance\colcount by 1
+ \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}%
+ \setuptable
+}
+
+\newcount\colcount
+\def\setuptable#1{%
+ \def\firstarg{#1}%
+ \ifx\firstarg\xendsetuptable
+ \let\go = \relax
+ \else
+ \ifx\firstarg\xcolumnfractions
+ \global\setpercenttrue
+ \else
+ \ifsetpercent
+ \let\go\pickupwholefraction
+ \else
+ \global\advance\colcount by 1
+ \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a
+ % separator; typically that is always in the input, anyway.
+ \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}%
+ \fi
+ \fi
+ \ifx\go\pickupwholefraction
+ % Put the argument back for the \pickupwholefraction call, so
+ % we'll always have a period there to be parsed.
+ \def\go{\pickupwholefraction#1}%
+ \else
+ \let\go = \setuptable
+ \fi%
+ \fi
+ \go
+}
+
+% multitable-only commands.
+%
+% @headitem starts a heading row, which we typeset in bold. Assignments
+% have to be global since we are inside the implicit group of an
+% alignment entry. \everycr below resets \everytab so we don't have to
+% undo it ourselves.
+\def\headitemfont{\b}% for people to use in the template row; not changeable
+\def\headitem{%
+ \checkenv\multitable
+ \crcr
+ \gdef\headitemcrhook{\nobreak}% attempt to avoid page break after headings
+ \global\everytab={\bf}% can't use \headitemfont since the parsing differs
+ \the\everytab % for the first item
+}%
+%
+% default for tables with no headings.
+\let\headitemcrhook=\relax
+%
+% A \tab used to include \hskip1sp. But then the space in a template
+% line is not enough. That is bad. So let's go back to just `&' until
+% we again encounter the problem the 1sp was intended to solve.
+% --karl, nathan@acm.org, 20apr99.
+\def\tab{\checkenv\multitable &\the\everytab}%
+
+% @multitable ... @end multitable definitions:
+%
+\newtoks\everytab % insert after every tab.
+%
+\envdef\multitable{%
+ \vskip\parskip
+ \startsavinginserts
+ %
+ % @item within a multitable starts a normal row.
+ % We use \def instead of \let so that if one of the multitable entries
+ % contains an @itemize, we don't choke on the \item (seen as \crcr aka
+ % \endtemplate) expanding \doitemize.
+ \def\item{\crcr}%
+ %
+ \tolerance=9500
+ \hbadness=9500
+ \setmultitablespacing
+ \parskip=\multitableparskip
+ \parindent=\multitableparindent
+ \overfullrule=0pt
+ \global\colcount=0
+ %
+ \everycr = {%
+ \noalign{%
+ \global\everytab={}% Reset from possible headitem.
+ \global\colcount=0 % Reset the column counter.
+ %
+ % Check for saved footnotes, etc.:
+ \checkinserts
+ %
+ % Perhaps a \nobreak, then reset:
+ \headitemcrhook
+ \global\let\headitemcrhook=\relax
+ }%
+ }%
+ %
+ \parsearg\domultitable
+}
+\def\domultitable#1{%
+ % To parse everything between @multitable and @item:
+ \setuptable#1 \endsetuptable
+ %
+ % This preamble sets up a generic column definition, which will
+ % be used as many times as user calls for columns.
+ % \vtop will set a single line and will also let text wrap and
+ % continue for many paragraphs if desired.
+ \halign\bgroup &%
+ \global\advance\colcount by 1
+ \multistrut
+ \vtop{%
+ % Use the current \colcount to find the correct column width:
+ \hsize=\expandafter\csname col\the\colcount\endcsname
+ %
+ % In order to keep entries from bumping into each other
+ % we will add a \leftskip of \multitablecolspace to all columns after
+ % the first one.
+ %
+ % If a template has been used, we will add \multitablecolspace
+ % to the width of each template entry.
+ %
+ % If the user has set preamble in terms of percent of \hsize we will
+ % use that dimension as the width of the column, and the \leftskip
+ % will keep entries from bumping into each other. Table will start at
+ % left margin and final column will justify at right margin.
+ %
+ % Make sure we don't inherit \rightskip from the outer environment.
+ \rightskip=0pt
+ \ifnum\colcount=1
+ % The first column will be indented with the surrounding text.
+ \advance\hsize by\leftskip
+ \else
+ \ifsetpercent \else
+ % If user has not set preamble in terms of percent of \hsize
+ % we will advance \hsize by \multitablecolspace.
+ \advance\hsize by \multitablecolspace
+ \fi
+ % In either case we will make \leftskip=\multitablecolspace:
+ \leftskip=\multitablecolspace
+ \fi
+ % Ignoring space at the beginning and end avoids an occasional spurious
+ % blank line, when TeX decides to break the line at the space before the
+ % box from the multistrut, so the strut ends up on a line by itself.
+ % For example:
+ % @multitable @columnfractions .11 .89
+ % @item @code{#}
+ % @tab Legal holiday which is valid in major parts of the whole country.
+ % Is automatically provided with highlighting sequences respectively
+ % marking characters.
+ \noindent\ignorespaces##\unskip\multistrut
+ }\cr
+}
+\def\Emultitable{%
+ \crcr
+ \egroup % end the \halign
+ \global\setpercentfalse
+}
+
+\def\setmultitablespacing{%
+ \def\multistrut{\strut}% just use the standard line spacing
+ %
+ % Compute \multitablelinespace (if not defined by user) for use in
+ % \multitableparskip calculation. We used define \multistrut based on
+ % this, but (ironically) that caused the spacing to be off.
+ % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100.
+\ifdim\multitablelinespace=0pt
+\setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip
+\global\advance\multitablelinespace by-\ht0
+\fi
+% Test to see if parskip is larger than space between lines of
+% table. If not, do nothing.
+% If so, set to same dimension as multitablelinespace.
+\ifdim\multitableparskip>\multitablelinespace
+\global\multitableparskip=\multitablelinespace
+\global\advance\multitableparskip-7pt % to keep parskip somewhat smaller
+ % than skip between lines in the table.
+\fi%
+\ifdim\multitableparskip=0pt
+\global\multitableparskip=\multitablelinespace
+\global\advance\multitableparskip-7pt % to keep parskip somewhat smaller
+ % than skip between lines in the table.
+\fi}
+
+
+\message{conditionals,}
+
+% @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext,
+% @ifnotxml always succeed. They currently do nothing; we don't
+% attempt to check whether the conditionals are properly nested. But we
+% have to remember that they are conditionals, so that @end doesn't
+% attempt to close an environment group.
+%
+\def\makecond#1{%
+ \expandafter\let\csname #1\endcsname = \relax
+ \expandafter\let\csname iscond.#1\endcsname = 1
+}
+\makecond{iftex}
+\makecond{ifnotdocbook}
+\makecond{ifnothtml}
+\makecond{ifnotinfo}
+\makecond{ifnotplaintext}
+\makecond{ifnotxml}
+
+% Ignore @ignore, @ifhtml, @ifinfo, and the like.
+%
+\def\direntry{\doignore{direntry}}
+\def\documentdescription{\doignore{documentdescription}}
+\def\docbook{\doignore{docbook}}
+\def\html{\doignore{html}}
+\def\ifdocbook{\doignore{ifdocbook}}
+\def\ifhtml{\doignore{ifhtml}}
+\def\ifinfo{\doignore{ifinfo}}
+\def\ifnottex{\doignore{ifnottex}}
+\def\ifplaintext{\doignore{ifplaintext}}
+\def\ifxml{\doignore{ifxml}}
+\def\ignore{\doignore{ignore}}
+\def\menu{\doignore{menu}}
+\def\xml{\doignore{xml}}
+
+% Ignore text until a line `@end #1', keeping track of nested conditionals.
+%
+% A count to remember the depth of nesting.
+\newcount\doignorecount
+
+\def\doignore#1{\begingroup
+ % Scan in ``verbatim'' mode:
+ \obeylines
+ \catcode`\@ = \other
+ \catcode`\{ = \other
+ \catcode`\} = \other
+ %
+ % Make sure that spaces turn into tokens that match what \doignoretext wants.
+ \spaceisspace
+ %
+ % Count number of #1's that we've seen.
+ \doignorecount = 0
+ %
+ % Swallow text until we reach the matching `@end #1'.
+ \dodoignore{#1}%
+}
+
+{ \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source.
+ \obeylines %
+ %
+ \gdef\dodoignore#1{%
+ % #1 contains the command name as a string, e.g., `ifinfo'.
+ %
+ % Define a command to find the next `@end #1'.
+ \long\def\doignoretext##1^^M@end #1{%
+ \doignoretextyyy##1^^M@#1\_STOP_}%
+ %
+ % And this command to find another #1 command, at the beginning of a
+ % line. (Otherwise, we would consider a line `@c @ifset', for
+ % example, to count as an @ifset for nesting.)
+ \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}%
+ %
+ % And now expand that command.
+ \doignoretext ^^M%
+ }%
+}
+
+\def\doignoreyyy#1{%
+ \def\temp{#1}%
+ \ifx\temp\empty % Nothing found.
+ \let\next\doignoretextzzz
+ \else % Found a nested condition, ...
+ \advance\doignorecount by 1
+ \let\next\doignoretextyyy % ..., look for another.
+ % If we're here, #1 ends with ^^M\ifinfo (for example).
+ \fi
+ \next #1% the token \_STOP_ is present just after this macro.
+}
+
+% We have to swallow the remaining "\_STOP_".
+%
+\def\doignoretextzzz#1{%
+ \ifnum\doignorecount = 0 % We have just found the outermost @end.
+ \let\next\enddoignore
+ \else % Still inside a nested condition.
+ \advance\doignorecount by -1
+ \let\next\doignoretext % Look for the next @end.
+ \fi
+ \next
+}
+
+% Finish off ignored text.
+{ \obeylines%
+ % Ignore anything after the last `@end #1'; this matters in verbatim
+ % environments, where otherwise the newline after an ignored conditional
+ % would result in a blank line in the output.
+ \gdef\enddoignore#1^^M{\endgroup\ignorespaces}%
+}
+
+
+% @set VAR sets the variable VAR to an empty value.
+% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE.
+%
+% Since we want to separate VAR from REST-OF-LINE (which might be
+% empty), we can't just use \parsearg; we have to insert a space of our
+% own to delimit the rest of the line, and then take it out again if we
+% didn't need it.
+% We rely on the fact that \parsearg sets \catcode`\ =10.
+%
+\parseargdef\set{\setyyy#1 \endsetyyy}
+\def\setyyy#1 #2\endsetyyy{%
+ {%
+ \makevalueexpandable
+ \def\temp{#2}%
+ \edef\next{\gdef\makecsname{SET#1}}%
+ \ifx\temp\empty
+ \next{}%
+ \else
+ \setzzz#2\endsetzzz
+ \fi
+ }%
+}
+% Remove the trailing space \setxxx inserted.
+\def\setzzz#1 \endsetzzz{\next{#1}}
+
+% @clear VAR clears (i.e., unsets) the variable VAR.
+%
+\parseargdef\clear{%
+ {%
+ \makevalueexpandable
+ \global\expandafter\let\csname SET#1\endcsname=\relax
+ }%
+}
+
+% @value{foo} gets the text saved in variable foo.
+\def\value{\begingroup\makevalueexpandable\valuexxx}
+\def\valuexxx#1{\expandablevalue{#1}\endgroup}
+{
+ \catcode`\-=\active \catcode`\_=\active
+ %
+ \gdef\makevalueexpandable{%
+ \let\value = \expandablevalue
+ % We don't want these characters active, ...
+ \catcode`\-=\other \catcode`\_=\other
+ % ..., but we might end up with active ones in the argument if
+ % we're called from @code, as @code{@value{foo-bar_}}, though.
+ % So \let them to their normal equivalents.
+ \let-\normaldash \let_\normalunderscore
+ }
+}
+
+\def\expandablevalue#1{%
+ \expandafter\ifx\csname SET#1\endcsname\relax
+ {[No value for ``#1'']}%
+ \message{Variable `#1', used in @value, is not set.}%
+ \else
+ \csname SET#1\endcsname
+ \fi
+}
+
+% Like \expandablevalue, but completely expandable (the \message in the
+% definition above operates at the execution level of TeX). Used when
+% writing to auxiliary files, due to the expansion that \write does.
+% If flag is undefined, pass through an unexpanded @value command: maybe it
+% will be set by the time it is read back in.
+%
+% NB flag names containing - or _ may not work here.
+\def\dummyvalue#1{%
+ \expandafter\ifx\csname SET#1\endcsname\relax
+ \string\value{#1}%
+ \else
+ \csname SET#1\endcsname
+ \fi
+}
+
+% Used for @value's in index entries to form the sort key: expand the @value
+% if possible, otherwise sort late.
+\def\indexnofontsvalue#1{%
+ \expandafter\ifx\csname SET#1\endcsname\relax
+ ZZZZZZZ%
+ \else
+ \csname SET#1\endcsname
+ \fi
+}
+
+% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined
+% with @set.
+%
+% To get the special treatment we need for `@end ifset,' we call
+% \makecond and then redefine.
+%
+\makecond{ifset}
+\def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}}
+\def\doifset#1#2{%
+ {%
+ \makevalueexpandable
+ \let\next=\empty
+ \expandafter\ifx\csname SET#2\endcsname\relax
+ #1% If not set, redefine \next.
+ \fi
+ \expandafter
+ }\next
+}
+\def\ifsetfail{\doignore{ifset}}
+
+% @ifclear VAR ... @end executes the `...' iff VAR has never been
+% defined with @set, or has been undefined with @clear.
+%
+% The `\else' inside the `\doifset' parameter is a trick to reuse the
+% above code: if the variable is not set, do nothing, if it is set,
+% then redefine \next to \ifclearfail.
+%
+\makecond{ifclear}
+\def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}}
+\def\ifclearfail{\doignore{ifclear}}
+
+% @ifcommandisdefined CMD ... @end executes the `...' if CMD (written
+% without the @) is in fact defined. We can only feasibly check at the
+% TeX level, so something like `mathcode' is going to considered
+% defined even though it is not a Texinfo command.
+%
+\makecond{ifcommanddefined}
+\def\ifcommanddefined{\parsearg{\doifcmddefined{\let\next=\ifcmddefinedfail}}}
+%
+\def\doifcmddefined#1#2{{%
+ \makevalueexpandable
+ \let\next=\empty
+ \expandafter\ifx\csname #2\endcsname\relax
+ #1% If not defined, \let\next as above.
+ \fi
+ \expandafter
+ }\next
+}
+\def\ifcmddefinedfail{\doignore{ifcommanddefined}}
+
+% @ifcommandnotdefined CMD ... handled similar to @ifclear above.
+\makecond{ifcommandnotdefined}
+\def\ifcommandnotdefined{%
+ \parsearg{\doifcmddefined{\else \let\next=\ifcmdnotdefinedfail}}}
+\def\ifcmdnotdefinedfail{\doignore{ifcommandnotdefined}}
+
+% Set the `txicommandconditionals' variable, so documents have a way to
+% test if the @ifcommand...defined conditionals are available.
+\set txicommandconditionals
+
+% @dircategory CATEGORY -- specify a category of the dir file
+% which this file should belong to. Ignore this in TeX.
+\let\dircategory=\comment
+
+% @defininfoenclose.
+\let\definfoenclose=\comment
+
+
+\message{indexing,}
+% Index generation facilities
+
+% Define \newwrite to be identical to plain tex's \newwrite
+% except not \outer, so it can be used within macros and \if's.
+\edef\newwrite{\makecsname{ptexnewwrite}}
+
+% \newindex {foo} defines an index named IX.
+% It automatically defines \IXindex such that
+% \IXindex ...rest of line... puts an entry in the index IX.
+% It also defines \IXindfile to be the number of the output channel for
+% the file that accumulates this index. The file's extension is IX.
+% The name of an index should be no more than 2 characters long
+% for the sake of vms.
+%
+\def\newindex#1{%
+ \expandafter\chardef\csname#1indfile\endcsname=0
+ \expandafter\xdef\csname#1index\endcsname{% % Define @#1index
+ \noexpand\doindex{#1}}
+}
+
+% @defindex foo == \newindex{foo}
+%
+\def\defindex{\parsearg\newindex}
+
+% Define @defcodeindex, like @defindex except put all entries in @code.
+%
+\def\defcodeindex{\parsearg\newcodeindex}
+%
+\def\newcodeindex#1{%
+ \expandafter\chardef\csname#1indfile\endcsname=0
+ \expandafter\xdef\csname#1index\endcsname{%
+ \noexpand\docodeindex{#1}}%
+}
+
+% The default indices:
+\newindex{cp}% concepts,
+\newcodeindex{fn}% functions,
+\newcodeindex{vr}% variables,
+\newcodeindex{tp}% types,
+\newcodeindex{ky}% keys
+\newcodeindex{pg}% and programs.
+
+
+% @synindex foo bar makes index foo feed into index bar.
+% Do this instead of @defindex foo if you don't want it as a separate index.
+%
+% @syncodeindex foo bar similar, but put all entries made for index foo
+% inside @code.
+%
+\def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}}
+\def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}}
+
+% #1 is \doindex or \docodeindex, #2 the index getting redefined (foo),
+% #3 the target index (bar).
+\def\dosynindex#1#2#3{%
+ \requireopenindexfile{#3}%
+ % redefine \fooindfile:
+ \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname
+ \expandafter\let\csname#2indfile\endcsname=\temp
+ % redefine \fooindex:
+ \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}%
+}
+
+% Define \doindex, the driver for all index macros.
+% Argument #1 is generated by the calling \fooindex macro,
+% and it is the two-letter name of the index.
+
+\def\doindex#1{\edef\indexname{#1}\parsearg\doindexxxx}
+\def\doindexxxx #1{\doind{\indexname}{#1}}
+
+% like the previous two, but they put @code around the argument.
+\def\docodeindex#1{\edef\indexname{#1}\parsearg\docodeindexxxx}
+\def\docodeindexxxx #1{\doind{\indexname}{\code{#1}}}
+
+
+% Used for the aux, toc and index files to prevent expansion of Texinfo
+% commands.
+%
+\def\atdummies{%
+ \definedummyletter\@%
+ \definedummyletter\ %
+ \definedummyletter\{%
+ \definedummyletter\}%
+ \definedummyletter\&%
+ %
+ % Do the redefinitions.
+ \definedummies
+ \otherbackslash
+}
+
+% \definedummyword defines \#1 as \string\#1\space, thus effectively
+% preventing its expansion. This is used only for control words,
+% not control letters, because the \space would be incorrect for
+% control characters, but is needed to separate the control word
+% from whatever follows.
+%
+% These can be used both for control words that take an argument and
+% those that do not. If it is followed by {arg} in the input, then
+% that will dutifully get written to the index (or wherever).
+%
+% For control letters, we have \definedummyletter, which omits the
+% space.
+%
+\def\definedummyword #1{\def#1{\string#1\space}}%
+\def\definedummyletter#1{\def#1{\string#1}}%
+\let\definedummyaccent\definedummyletter
+
+% Called from \atdummies to prevent the expansion of commands.
+%
+\def\definedummies{%
+ %
+ \let\commondummyword\definedummyword
+ \let\commondummyletter\definedummyletter
+ \let\commondummyaccent\definedummyaccent
+ \commondummiesnofonts
+ %
+ \definedummyletter\_%
+ \definedummyletter\-%
+ %
+ % Non-English letters.
+ \definedummyword\AA
+ \definedummyword\AE
+ \definedummyword\DH
+ \definedummyword\L
+ \definedummyword\O
+ \definedummyword\OE
+ \definedummyword\TH
+ \definedummyword\aa
+ \definedummyword\ae
+ \definedummyword\dh
+ \definedummyword\exclamdown
+ \definedummyword\l
+ \definedummyword\o
+ \definedummyword\oe
+ \definedummyword\ordf
+ \definedummyword\ordm
+ \definedummyword\questiondown
+ \definedummyword\ss
+ \definedummyword\th
+ %
+ % Although these internal commands shouldn't show up, sometimes they do.
+ \definedummyword\bf
+ \definedummyword\gtr
+ \definedummyword\hat
+ \definedummyword\less
+ \definedummyword\sf
+ \definedummyword\sl
+ \definedummyword\tclose
+ \definedummyword\tt
+ %
+ \definedummyword\LaTeX
+ \definedummyword\TeX
+ %
+ % Assorted special characters.
+ \definedummyword\ampchar
+ \definedummyword\atchar
+ \definedummyword\arrow
+ \definedummyword\backslashchar
+ \definedummyword\bullet
+ \definedummyword\comma
+ \definedummyword\copyright
+ \definedummyword\registeredsymbol
+ \definedummyword\dots
+ \definedummyword\enddots
+ \definedummyword\entrybreak
+ \definedummyword\equiv
+ \definedummyword\error
+ \definedummyword\euro
+ \definedummyword\expansion
+ \definedummyword\geq
+ \definedummyword\guillemetleft
+ \definedummyword\guillemetright
+ \definedummyword\guilsinglleft
+ \definedummyword\guilsinglright
+ \definedummyword\lbracechar
+ \definedummyword\leq
+ \definedummyword\mathopsup
+ \definedummyword\minus
+ \definedummyword\ogonek
+ \definedummyword\pounds
+ \definedummyword\point
+ \definedummyword\print
+ \definedummyword\quotedblbase
+ \definedummyword\quotedblleft
+ \definedummyword\quotedblright
+ \definedummyword\quoteleft
+ \definedummyword\quoteright
+ \definedummyword\quotesinglbase
+ \definedummyword\rbracechar
+ \definedummyword\result
+ \definedummyword\sub
+ \definedummyword\sup
+ \definedummyword\textdegree
+ %
+ \definedummyword\subentry
+ %
+ % We want to disable all macros so that they are not expanded by \write.
+ \macrolist
+ \let\value\dummyvalue
+ %
+ \normalturnoffactive
+}
+
+% \commondummiesnofonts: common to \definedummies and \indexnofonts.
+% Define \commondummyletter, \commondummyaccent and \commondummyword before
+% using. Used for accents, font commands, and various control letters.
+%
+\def\commondummiesnofonts{%
+ % Control letters and accents.
+ \commondummyletter\!%
+ \commondummyaccent\"%
+ \commondummyaccent\'%
+ \commondummyletter\*%
+ \commondummyaccent\,%
+ \commondummyletter\.%
+ \commondummyletter\/%
+ \commondummyletter\:%
+ \commondummyaccent\=%
+ \commondummyletter\?%
+ \commondummyaccent\^%
+ \commondummyaccent\`%
+ \commondummyaccent\~%
+ \commondummyword\u
+ \commondummyword\v
+ \commondummyword\H
+ \commondummyword\dotaccent
+ \commondummyword\ogonek
+ \commondummyword\ringaccent
+ \commondummyword\tieaccent
+ \commondummyword\ubaraccent
+ \commondummyword\udotaccent
+ \commondummyword\dotless
+ %
+ % Texinfo font commands.
+ \commondummyword\b
+ \commondummyword\i
+ \commondummyword\r
+ \commondummyword\sansserif
+ \commondummyword\sc
+ \commondummyword\slanted
+ \commondummyword\t
+ %
+ % Commands that take arguments.
+ \commondummyword\abbr
+ \commondummyword\acronym
+ \commondummyword\anchor
+ \commondummyword\cite
+ \commondummyword\code
+ \commondummyword\command
+ \commondummyword\dfn
+ \commondummyword\dmn
+ \commondummyword\email
+ \commondummyword\emph
+ \commondummyword\env
+ \commondummyword\file
+ \commondummyword\image
+ \commondummyword\indicateurl
+ \commondummyword\inforef
+ \commondummyword\kbd
+ \commondummyword\key
+ \commondummyword\math
+ \commondummyword\option
+ \commondummyword\pxref
+ \commondummyword\ref
+ \commondummyword\samp
+ \commondummyword\strong
+ \commondummyword\tie
+ \commondummyword\U
+ \commondummyword\uref
+ \commondummyword\url
+ \commondummyword\var
+ \commondummyword\verb
+ \commondummyword\w
+ \commondummyword\xref
+}
+
+\let\indexlbrace\relax
+\let\indexrbrace\relax
+\let\indexatchar\relax
+\let\indexbackslash\relax
+
+{\catcode`\@=0
+\catcode`\\=13
+ @gdef@backslashdisappear{@def\{}}
+}
+
+{
+\catcode`\<=13
+\catcode`\-=13
+\catcode`\`=13
+ \gdef\indexnonalnumdisappear{%
+ \expandafter\ifx\csname SETtxiindexlquoteignore\endcsname\relax\else
+ % @set txiindexlquoteignore makes us ignore left quotes in the sort term.
+ % (Introduced for FSFS 2nd ed.)
+ \let`=\empty
+ \fi
+ %
+ \expandafter\ifx\csname SETtxiindexbackslashignore\endcsname\relax\else
+ \backslashdisappear
+ \fi
+ %
+ \expandafter\ifx\csname SETtxiindexhyphenignore\endcsname\relax\else
+ \def-{}%
+ \fi
+ \expandafter\ifx\csname SETtxiindexlessthanignore\endcsname\relax\else
+ \def<{}%
+ \fi
+ \expandafter\ifx\csname SETtxiindexatsignignore\endcsname\relax\else
+ \def\@{}%
+ \fi
+ }
+
+ \gdef\indexnonalnumreappear{%
+ \let-\normaldash
+ \let<\normalless
+ }
+}
+
+
+% \indexnofonts is used when outputting the strings to sort the index
+% by, and when constructing control sequence names. It eliminates all
+% control sequences and just writes whatever the best ASCII sort string
+% would be for a given command (usually its argument).
+%
+\def\indexnofonts{%
+ % Accent commands should become @asis.
+ \def\commondummyaccent##1{\let##1\asis}%
+ % We can just ignore other control letters.
+ \def\commondummyletter##1{\let##1\empty}%
+ % All control words become @asis by default; overrides below.
+ \let\commondummyword\commondummyaccent
+ \commondummiesnofonts
+ %
+ % Don't no-op \tt, since it isn't a user-level command
+ % and is used in the definitions of the active chars like <, >, |, etc.
+ % Likewise with the other plain tex font commands.
+ %\let\tt=\asis
+ %
+ \def\ { }%
+ \def\@{@}%
+ \def\_{\normalunderscore}%
+ \def\-{}% @- shouldn't affect sorting
+ %
+ \uccode`\1=`\{ \uppercase{\def\{{1}}%
+ \uccode`\1=`\} \uppercase{\def\}{1}}%
+ \let\lbracechar\{%
+ \let\rbracechar\}%
+ %
+ % Non-English letters.
+ \def\AA{AA}%
+ \def\AE{AE}%
+ \def\DH{DZZ}%
+ \def\L{L}%
+ \def\OE{OE}%
+ \def\O{O}%
+ \def\TH{TH}%
+ \def\aa{aa}%
+ \def\ae{ae}%
+ \def\dh{dzz}%
+ \def\exclamdown{!}%
+ \def\l{l}%
+ \def\oe{oe}%
+ \def\ordf{a}%
+ \def\ordm{o}%
+ \def\o{o}%
+ \def\questiondown{?}%
+ \def\ss{ss}%
+ \def\th{th}%
+ %
+ \def\LaTeX{LaTeX}%
+ \def\TeX{TeX}%
+ %
+ % Assorted special characters. \defglyph gives the control sequence a
+ % definition that removes the {} that follows its use.
+ \defglyph\atchar{@}%
+ \defglyph\arrow{->}%
+ \defglyph\bullet{bullet}%
+ \defglyph\comma{,}%
+ \defglyph\copyright{copyright}%
+ \defglyph\dots{...}%
+ \defglyph\enddots{...}%
+ \defglyph\equiv{==}%
+ \defglyph\error{error}%
+ \defglyph\euro{euro}%
+ \defglyph\expansion{==>}%
+ \defglyph\geq{>=}%
+ \defglyph\guillemetleft{<<}%
+ \defglyph\guillemetright{>>}%
+ \defglyph\guilsinglleft{<}%
+ \defglyph\guilsinglright{>}%
+ \defglyph\leq{<=}%
+ \defglyph\lbracechar{\{}%
+ \defglyph\minus{-}%
+ \defglyph\point{.}%
+ \defglyph\pounds{pounds}%
+ \defglyph\print{-|}%
+ \defglyph\quotedblbase{"}%
+ \defglyph\quotedblleft{"}%
+ \defglyph\quotedblright{"}%
+ \defglyph\quoteleft{`}%
+ \defglyph\quoteright{'}%
+ \defglyph\quotesinglbase{,}%
+ \defglyph\rbracechar{\}}%
+ \defglyph\registeredsymbol{R}%
+ \defglyph\result{=>}%
+ \defglyph\textdegree{o}%
+ %
+ % We need to get rid of all macros, leaving only the arguments (if present).
+ % Of course this is not nearly correct, but it is the best we can do for now.
+ % makeinfo does not expand macros in the argument to @deffn, which ends up
+ % writing an index entry, and texindex isn't prepared for an index sort entry
+ % that starts with \.
+ %
+ % Since macro invocations are followed by braces, we can just redefine them
+ % to take a single TeX argument. The case of a macro invocation that
+ % goes to end-of-line is not handled.
+ %
+ \macrolist
+ \let\value\indexnofontsvalue
+}
+\def\defglyph#1#2{\def#1##1{#2}} % see above
+
+
+
+
+% #1 is the index name, #2 is the entry text.
+\def\doind#1#2{%
+ \iflinks
+ {%
+ %
+ \requireopenindexfile{#1}%
+ \edef\writeto{\csname#1indfile\endcsname}%
+ %
+ \def\indextext{#2}%
+ \safewhatsit\doindwrite
+ }%
+ \fi
+}
+
+% Check if an index file has been opened, and if not, open it.
+\def\requireopenindexfile#1{%
+\ifnum\csname #1indfile\endcsname=0
+ \expandafter\newwrite \csname#1indfile\endcsname
+ \edef\suffix{#1}%
+ % A .fls suffix would conflict with the file extension for the output
+ % of -recorder, so use .f1s instead.
+ \ifx\suffix\indexisfl\def\suffix{f1}\fi
+ % Open the file
+ \immediate\openout\csname#1indfile\endcsname \jobname.\suffix
+ % Using \immediate above here prevents an object entering into the current
+ % box, which could confound checks such as those in \safewhatsit for
+ % preceding skips.
+ \typeout{Writing index file \jobname.\suffix}%
+\fi}
+\def\indexisfl{fl}
+
+% Definition for writing index entry sort key.
+{
+\catcode`\-=13
+\gdef\indexwritesortas{%
+ \begingroup
+ \indexnonalnumreappear
+ \indexwritesortasxxx}
+\gdef\indexwritesortasxxx#1{%
+ \xdef\indexsortkey{#1}\endgroup}
+}
+
+\def\indexwriteseealso#1{
+ \gdef\pagenumbertext{\string\seealso{#1}}%
+}
+\def\indexwriteseeentry#1{
+ \gdef\pagenumbertext{\string\seeentry{#1}}%
+}
+
+% The default definitions
+\def\sortas#1{}%
+\def\seealso#1{\i{\putwordSeeAlso}\ #1}% for sorted index file only
+\def\putwordSeeAlso{See also}
+\def\seeentry#1{\i{\putwordSee}\ #1}% for sorted index file only
+
+
+% Given index entry text like "aaa @subentry bbb @sortas{ZZZ}":
+% * Set \bracedtext to "{aaa}{bbb}"
+% * Set \fullindexsortkey to "aaa @subentry ZZZ"
+% * If @seealso occurs, set \pagenumbertext
+%
+\def\splitindexentry#1{%
+ \gdef\fullindexsortkey{}%
+ \xdef\bracedtext{}%
+ \def\sep{}%
+ \def\seealso##1{}%
+ \def\seeentry##1{}%
+ \expandafter\doindexsegment#1\subentry\finish\subentry
+}
+
+% append the results from the next segment
+\def\doindexsegment#1\subentry{%
+ \def\segment{#1}%
+ \ifx\segment\isfinish
+ \else
+ %
+ % Fully expand the segment, throwing away any @sortas directives, and
+ % trim spaces.
+ \edef\trimmed{\segment}%
+ \edef\trimmed{\expandafter\eatspaces\expandafter{\trimmed}}%
+ %
+ \xdef\bracedtext{\bracedtext{\trimmed}}%
+ %
+ % Get the string to sort by. Process the segment with all
+ % font commands turned off.
+ \bgroup
+ \let\sortas\indexwritesortas
+ \let\seealso\indexwriteseealso
+ \let\seeentry\indexwriteseeentry
+ \indexnofonts
+ % The braces around the commands are recognized by texindex.
+ \def\lbracechar{{\string\indexlbrace}}%
+ \def\rbracechar{{\string\indexrbrace}}%
+ \let\{=\lbracechar
+ \let\}=\rbracechar
+ \def\@{{\string\indexatchar}}%
+ \def\atchar##1{\@}%
+ \def\backslashchar{{\string\indexbackslash}}%
+ \uccode`\~=`\\ \uppercase{\let~\backslashchar}%
+ %
+ \let\indexsortkey\empty
+ \global\let\pagenumbertext\empty
+ % Execute the segment and throw away the typeset output. This executes
+ % any @sortas or @seealso commands in this segment.
+ \setbox\dummybox = \hbox{\segment}%
+ \ifx\indexsortkey\empty{%
+ \indexnonalnumdisappear
+ \xdef\trimmed{\segment}%
+ \xdef\trimmed{\expandafter\eatspaces\expandafter{\trimmed}}%
+ \xdef\indexsortkey{\trimmed}%
+ \ifx\indexsortkey\empty\xdef\indexsortkey{ }\fi
+ }\fi
+ %
+ % Append to \fullindexsortkey.
+ \edef\tmp{\gdef\noexpand\fullindexsortkey{%
+ \fullindexsortkey\sep\indexsortkey}}%
+ \tmp
+ \egroup
+ \def\sep{\subentry}%
+ %
+ \expandafter\doindexsegment
+ \fi
+}
+\def\isfinish{\finish}%
+\newbox\dummybox % used above
+
+\let\subentry\relax
+
+% Use \ instead of @ in index files. To support old texi2dvi and texindex.
+% This works without changing the escape character used in the toc or aux
+% files because the index entries are fully expanded here, and \string uses
+% the current value of \escapechar.
+\def\escapeisbackslash{\escapechar=`\\}
+
+% Use \ in index files by default. texi2dvi didn't support @ as the escape
+% character (as it checked for "\entry" in the files, and not "@entry"). When
+% the new version of texi2dvi has had a chance to become more prevalent, then
+% the escape character can change back to @ again. This should be an easy
+% change to make now because both @ and \ are only used as escape characters in
+% index files, never standing for themselves.
+%
+\set txiindexescapeisbackslash
+
+% Write the entry in \indextext to the index file.
+%
+\def\doindwrite{%
+ \maybemarginindex
+ %
+ \atdummies
+ %
+ \expandafter\ifx\csname SETtxiindexescapeisbackslash\endcsname\relax\else
+ \escapeisbackslash
+ \fi
+ %
+ % For texindex which always views { and } as separators.
+ \def\{{\lbracechar{}}%
+ \def\}{\rbracechar{}}%
+ \uccode`\~=`\\ \uppercase{\def~{\backslashchar{}}}%
+ %
+ % Split the entry into primary entry and any subentries, and get the index
+ % sort key.
+ \splitindexentry\indextext
+ %
+ % Set up the complete index entry, with both the sort key and
+ % the original text, including any font commands. We write
+ % three arguments to \entry to the .?? file (four in the
+ % subentry case), texindex reduces to two when writing the .??s
+ % sorted result.
+ %
+ \edef\temp{%
+ \write\writeto{%
+ \string\entry{\fullindexsortkey}%
+ {\ifx\pagenumbertext\empty\noexpand\folio\else\pagenumbertext\fi}%
+ \bracedtext}%
+ }%
+ \temp
+}
+
+% Put the index entry in the margin if desired (undocumented).
+\def\maybemarginindex{%
+ \ifx\SETmarginindex\relax\else
+ \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \relax\indextext}}%
+ \fi
+}
+\let\SETmarginindex=\relax
+
+
+% Take care of unwanted page breaks/skips around a whatsit:
+%
+% If a skip is the last thing on the list now, preserve it
+% by backing up by \lastskip, doing the \write, then inserting
+% the skip again. Otherwise, the whatsit generated by the
+% \write or \pdfdest will make \lastskip zero. The result is that
+% sequences like this:
+% @end defun
+% @tindex whatever
+% @defun ...
+% will have extra space inserted, because the \medbreak in the
+% start of the @defun won't see the skip inserted by the @end of
+% the previous defun.
+%
+% But don't do any of this if we're not in vertical mode. We
+% don't want to do a \vskip and prematurely end a paragraph.
+%
+% Avoid page breaks due to these extra skips, too.
+%
+% But wait, there is a catch there:
+% We'll have to check whether \lastskip is zero skip. \ifdim is not
+% sufficient for this purpose, as it ignores stretch and shrink parts
+% of the skip. The only way seems to be to check the textual
+% representation of the skip.
+%
+% The following is almost like \def\zeroskipmacro{0.0pt} except that
+% the ``p'' and ``t'' characters have catcode \other, not 11 (letter).
+%
+\edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname}
+%
+\newskip\whatsitskip
+\newcount\whatsitpenalty
+%
+% ..., ready, GO:
+%
+\def\safewhatsit#1{\ifhmode
+ #1%
+ \else
+ % \lastskip and \lastpenalty cannot both be nonzero simultaneously.
+ \whatsitskip = \lastskip
+ \edef\lastskipmacro{\the\lastskip}%
+ \whatsitpenalty = \lastpenalty
+ %
+ % If \lastskip is nonzero, that means the last item was a
+ % skip. And since a skip is discardable, that means this
+ % -\whatsitskip glue we're inserting is preceded by a
+ % non-discardable item, therefore it is not a potential
+ % breakpoint, therefore no \nobreak needed.
+ \ifx\lastskipmacro\zeroskipmacro
+ \else
+ \vskip-\whatsitskip
+ \fi
+ %
+ #1%
+ %
+ \ifx\lastskipmacro\zeroskipmacro
+ % If \lastskip was zero, perhaps the last item was a penalty, and
+ % perhaps it was >=10000, e.g., a \nobreak. In that case, we want
+ % to re-insert the same penalty (values >10000 are used for various
+ % signals); since we just inserted a non-discardable item, any
+ % following glue (such as a \parskip) would be a breakpoint. For example:
+ % @deffn deffn-whatever
+ % @vindex index-whatever
+ % Description.
+ % would allow a break between the index-whatever whatsit
+ % and the "Description." paragraph.
+ \ifnum\whatsitpenalty>9999 \penalty\whatsitpenalty \fi
+ \else
+ % On the other hand, if we had a nonzero \lastskip,
+ % this make-up glue would be preceded by a non-discardable item
+ % (the whatsit from the \write), so we must insert a \nobreak.
+ \nobreak\vskip\whatsitskip
+ \fi
+\fi}
+
+% The index entry written in the file actually looks like
+% \entry {sortstring}{page}{topic}
+% or
+% \entry {sortstring}{page}{topic}{subtopic}
+% The texindex program reads in these files and writes files
+% containing these kinds of lines:
+% \initial {c}
+% before the first topic whose initial is c
+% \entry {topic}{pagelist}
+% for a topic that is used without subtopics
+% \primary {topic}
+% \entry {topic}{}
+% for the beginning of a topic that is used with subtopics
+% \secondary {subtopic}{pagelist}
+% for each subtopic.
+% \secondary {subtopic}{}
+% for a subtopic with sub-subtopics
+% \tertiary {subtopic}{subsubtopic}{pagelist}
+% for each sub-subtopic.
+
+% Define the user-accessible indexing commands
+% @findex, @vindex, @kindex, @cindex.
+
+\def\findex {\fnindex}
+\def\kindex {\kyindex}
+\def\cindex {\cpindex}
+\def\vindex {\vrindex}
+\def\tindex {\tpindex}
+\def\pindex {\pgindex}
+
+% Define the macros used in formatting output of the sorted index material.
+
+% @printindex causes a particular index (the ??s file) to get printed.
+% It does not print any chapter heading (usually an @unnumbered).
+%
+\parseargdef\printindex{\begingroup
+ \dobreak \chapheadingskip{10000}%
+ %
+ \smallfonts \rm
+ \tolerance = 9500
+ \plainfrenchspacing
+ \everypar = {}% don't want the \kern\-parindent from indentation suppression.
+ %
+ % See comment in \requireopenindexfile.
+ \def\indexname{#1}\ifx\indexname\indexisfl\def\indexname{f1}\fi
+ %
+ % See if the index file exists and is nonempty.
+ \openin 1 \jobname.\indexname s
+ \ifeof 1
+ % \enddoublecolumns gets confused if there is no text in the index,
+ % and it loses the chapter title and the aux file entries for the
+ % index. The easiest way to prevent this problem is to make sure
+ % there is some text.
+ \putwordIndexNonexistent
+ \typeout{No file \jobname.\indexname s.}%
+ \else
+ % If the index file exists but is empty, then \openin leaves \ifeof
+ % false. We have to make TeX try to read something from the file, so
+ % it can discover if there is anything in it.
+ \read 1 to \thisline
+ \ifeof 1
+ \putwordIndexIsEmpty
+ \else
+ \expandafter\printindexzz\thisline\relax\relax\finish%
+ \fi
+ \fi
+ \closein 1
+\endgroup}
+
+% If the index file starts with a backslash, forgo reading the index
+% file altogether. If somebody upgrades texinfo.tex they may still have
+% old index files using \ as the escape character. Reading this would
+% at best lead to typesetting garbage, at worst a TeX syntax error.
+\def\printindexzz#1#2\finish{%
+ \expandafter\ifx\csname SETtxiindexescapeisbackslash\endcsname\relax
+ \uccode`\~=`\\ \uppercase{\if\noexpand~}\noexpand#1
+ \expandafter\ifx\csname SETtxiskipindexfileswithbackslash\endcsname\relax
+\errmessage{%
+ERROR: A sorted index file in an obsolete format was skipped.
+To fix this problem, please upgrade your version of 'texi2dvi'
+or 'texi2pdf' to that at <https://ftp.gnu.org/gnu/texinfo>.
+If you are using an old version of 'texindex' (part of the Texinfo
+distribution), you may also need to upgrade to a newer version (at least 6.0).
+You may be able to typeset the index if you run
+'texindex \jobname.\indexname' yourself.
+You could also try setting the 'txiindexescapeisbackslash' flag by
+running a command like
+'texi2dvi -t "@set txiindexescapeisbackslash" \jobname.texi'. If you do
+this, Texinfo will try to use index files in the old format.
+If you continue to have problems, deleting the index files and starting again
+might help (with 'rm \jobname.?? \jobname.??s')%
+}%
+ \else
+ (Skipped sorted index file in obsolete format)
+ \fi
+ \else
+ \begindoublecolumns
+ \input \jobname.\indexname s
+ \enddoublecolumns
+ \fi
+ \else
+ \begindoublecolumns
+ \catcode`\\=0\relax
+ \catcode`\@=12\relax
+ \input \jobname.\indexname s
+ \enddoublecolumns
+ \fi
+}
+
+% These macros are used by the sorted index file itself.
+% Change them to control the appearance of the index.
+
+{\catcode`\/=13 \catcode`\-=13 \catcode`\^=13 \catcode`\~=13 \catcode`\_=13
+\catcode`\|=13 \catcode`\<=13 \catcode`\>=13 \catcode`\+=13 \catcode`\"=13
+\catcode`\$=3
+\gdef\initialglyphs{%
+ % special control sequences used in the index sort key
+ \let\indexlbrace\{%
+ \let\indexrbrace\}%
+ \let\indexatchar\@%
+ \def\indexbackslash{\math{\backslash}}%
+ %
+ % Some changes for non-alphabetic characters. Using the glyphs from the
+ % math fonts looks more consistent than the typewriter font used elsewhere
+ % for these characters.
+ \uccode`\~=`\\ \uppercase{\def~{\math{\backslash}}}
+ %
+ % In case @\ is used for backslash
+ \uppercase{\let\\=~}
+ % Can't get bold backslash so don't use bold forward slash
+ \catcode`\/=13
+ \def/{{\secrmnotbold \normalslash}}%
+ \def-{{\normaldash\normaldash}}% en dash `--'
+ \def^{{\chapbf \normalcaret}}%
+ \def~{{\chapbf \normaltilde}}%
+ \def\_{%
+ \leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em }%
+ \def|{$\vert$}%
+ \def<{$\less$}%
+ \def>{$\gtr$}%
+ \def+{$\normalplus$}%
+}}
+
+\def\initial{%
+ \bgroup
+ \initialglyphs
+ \initialx
+}
+
+\def\initialx#1{%
+ % Remove any glue we may have, we'll be inserting our own.
+ \removelastskip
+ %
+ % We like breaks before the index initials, so insert a bonus.
+ % The glue before the bonus allows a little bit of space at the
+ % bottom of a column to reduce an increase in inter-line spacing.
+ \nobreak
+ \vskip 0pt plus 5\baselineskip
+ \penalty -300
+ \vskip 0pt plus -5\baselineskip
+ %
+ % Typeset the initial. Making this add up to a whole number of
+ % baselineskips increases the chance of the dots lining up from column
+ % to column. It still won't often be perfect, because of the stretch
+ % we need before each entry, but it's better.
+ %
+ % No shrink because it confuses \balancecolumns.
+ \vskip 1.67\baselineskip plus 1\baselineskip
+ \leftline{\secfonts \kern-0.05em \secbf #1}%
+ % \secfonts is inside the argument of \leftline so that the change of
+ % \baselineskip will not affect any glue inserted before the vbox that
+ % \leftline creates.
+ % Do our best not to break after the initial.
+ \nobreak
+ \vskip .33\baselineskip plus .1\baselineskip
+ \egroup % \initialglyphs
+}
+
+\newdimen\entryrightmargin
+\entryrightmargin=0pt
+
+% \entry typesets a paragraph consisting of the text (#1), dot leaders, and
+% then page number (#2) flushed to the right margin. It is used for index
+% and table of contents entries. The paragraph is indented by \leftskip.
+%
+\def\entry{%
+ \begingroup
+ %
+ % Start a new paragraph if necessary, so our assignments below can't
+ % affect previous text.
+ \par
+ %
+ % No extra space above this paragraph.
+ \parskip = 0in
+ %
+ % When reading the text of entry, convert explicit line breaks
+ % from @* into spaces. The user might give these in long section
+ % titles, for instance.
+ \def\*{\unskip\space\ignorespaces}%
+ \def\entrybreak{\hfil\break}% An undocumented command
+ %
+ % Swallow the left brace of the text (first parameter):
+ \afterassignment\doentry
+ \let\temp =
+}
+\def\entrybreak{\unskip\space\ignorespaces}%
+\def\doentry{%
+ % Save the text of the entry
+ \global\setbox\boxA=\hbox\bgroup
+ \bgroup % Instead of the swallowed brace.
+ \noindent
+ \aftergroup\finishentry
+ % And now comes the text of the entry.
+ % Not absorbing as a macro argument reduces the chance of problems
+ % with catcodes occurring.
+}
+{\catcode`\@=11
+\gdef\finishentry#1{%
+ \egroup % end box A
+ \dimen@ = \wd\boxA % Length of text of entry
+ \global\setbox\boxA=\hbox\bgroup
+ \unhbox\boxA
+ % #1 is the page number.
+ %
+ % Get the width of the page numbers, and only use
+ % leaders if they are present.
+ \global\setbox\boxB = \hbox{#1}%
+ \ifdim\wd\boxB = 0pt
+ \null\nobreak\hfill\ %
+ \else
+ %
+ \null\nobreak\indexdotfill % Have leaders before the page number.
+ %
+ \ifpdforxetex
+ \pdfgettoks#1.%
+ \hskip\skip\thinshrinkable\the\toksA
+ \else
+ \hskip\skip\thinshrinkable #1%
+ \fi
+ \fi
+ \egroup % end \boxA
+ \ifdim\wd\boxB = 0pt
+ \noindent\unhbox\boxA\par
+ \nobreak
+ \else\bgroup
+ % We want the text of the entries to be aligned to the left, and the
+ % page numbers to be aligned to the right.
+ %
+ \parindent = 0pt
+ \advance\leftskip by 0pt plus 1fil
+ \advance\leftskip by 0pt plus -1fill
+ \rightskip = 0pt plus -1fil
+ \advance\rightskip by 0pt plus 1fill
+ % Cause last line, which could consist of page numbers on their own
+ % if the list of page numbers is long, to be aligned to the right.
+ \parfillskip=0pt plus -1fill
+ %
+ \advance\rightskip by \entryrightmargin
+ % Determine how far we can stretch into the margin.
+ % This allows, e.g., "Appendix H GNU Free Documentation License" to
+ % fit on one line in @letterpaper format.
+ \ifdim\entryrightmargin>2.1em
+ \dimen@i=2.1em
+ \else
+ \dimen@i=0em
+ \fi
+ \advance \parfillskip by 0pt minus 1\dimen@i
+ %
+ \dimen@ii = \hsize
+ \advance\dimen@ii by -1\leftskip
+ \advance\dimen@ii by -1\entryrightmargin
+ \advance\dimen@ii by 1\dimen@i
+ \ifdim\wd\boxA > \dimen@ii % If the entry doesn't fit in one line
+ \ifdim\dimen@ > 0.8\dimen@ii % due to long index text
+ % Try to split the text roughly evenly. \dimen@ will be the length of
+ % the first line.
+ \dimen@ = 0.7\dimen@
+ \dimen@ii = \hsize
+ \ifnum\dimen@>\dimen@ii
+ % If the entry is too long (for example, if it needs more than
+ % two lines), use all the space in the first line.
+ \dimen@ = \dimen@ii
+ \fi
+ \advance\leftskip by 0pt plus 1fill % ragged right
+ \advance \dimen@ by 1\rightskip
+ \parshape = 2 0pt \dimen@ 0em \dimen@ii
+ % Ideally we'd add a finite glue at the end of the first line only,
+ % instead of using \parshape with explicit line lengths, but TeX
+ % doesn't seem to provide a way to do such a thing.
+ %
+ % Indent all lines but the first one.
+ \advance\leftskip by 1em
+ \advance\parindent by -1em
+ \fi\fi
+ \indent % start paragraph
+ \unhbox\boxA
+ %
+ % Do not prefer a separate line ending with a hyphen to fewer lines.
+ \finalhyphendemerits = 0
+ %
+ % Word spacing - no stretch
+ \spaceskip=\fontdimen2\font minus \fontdimen4\font
+ %
+ \linepenalty=1000 % Discourage line breaks.
+ \hyphenpenalty=5000 % Discourage hyphenation.
+ %
+ \par % format the paragraph
+ \egroup % The \vbox
+ \fi
+ \endgroup
+}}
+
+\newskip\thinshrinkable
+\skip\thinshrinkable=.15em minus .15em
+
+% Like plain.tex's \dotfill, except uses up at least 1 em.
+% The filll stretch here overpowers both the fil and fill stretch to push
+% the page number to the right.
+\def\indexdotfill{\cleaders
+ \hbox{$\mathsurround=0pt \mkern1.5mu.\mkern1.5mu$}\hskip 1em plus 1filll}
+
+
+\def\primary #1{\line{#1\hfil}}
+
+\def\secondary{\indententry{0.5cm}}
+\def\tertiary{\indententry{1cm}}
+
+\def\indententry#1#2#3{%
+ \bgroup
+ \leftskip=#1
+ \entry{#2}{#3}%
+ \egroup
+}
+
+% Define two-column mode, which we use to typeset indexes.
+% Adapted from the TeXbook, page 416, which is to say,
+% the manmac.tex format used to print the TeXbook itself.
+\catcode`\@=11 % private names
+
+\newbox\partialpage
+\newdimen\doublecolumnhsize
+
+\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns
+ % If not much space left on page, start a new page.
+ \ifdim\pagetotal>0.8\vsize\vfill\eject\fi
+ %
+ % Grab any single-column material above us.
+ \output = {%
+ \savetopmark
+ %
+ \global\setbox\partialpage = \vbox{%
+ % Unvbox the main output page.
+ \unvbox\PAGE
+ \kern-\topskip \kern\baselineskip
+ }%
+ }%
+ \eject % run that output routine to set \partialpage
+ %
+ % Use the double-column output routine for subsequent pages.
+ \output = {\doublecolumnout}%
+ %
+ % Change the page size parameters. We could do this once outside this
+ % routine, in each of @smallbook, @afourpaper, and the default 8.5x11
+ % format, but then we repeat the same computation. Repeating a couple
+ % of assignments once per index is clearly meaningless for the
+ % execution time, so we may as well do it in one place.
+ %
+ % First we halve the line length, less a little for the gutter between
+ % the columns. We compute the gutter based on the line length, so it
+ % changes automatically with the paper format. The magic constant
+ % below is chosen so that the gutter has the same value (well, +-<1pt)
+ % as it did when we hard-coded it.
+ %
+ % We put the result in a separate register, \doublecolumhsize, so we
+ % can restore it in \pagesofar, after \hsize itself has (potentially)
+ % been clobbered.
+ %
+ \doublecolumnhsize = \hsize
+ \advance\doublecolumnhsize by -.04154\hsize
+ \divide\doublecolumnhsize by 2
+ \hsize = \doublecolumnhsize
+ %
+ % Get the available space for the double columns -- the normal
+ % (undoubled) page height minus any material left over from the
+ % previous page.
+ \advance\vsize by -\ht\partialpage
+ \vsize = 2\vsize
+ %
+ % For the benefit of balancing columns
+ \advance\baselineskip by 0pt plus 0.5pt
+}
+
+% The double-column output routine for all double-column pages except
+% the last, which is done by \balancecolumns.
+%
+\def\doublecolumnout{%
+ %
+ \savetopmark
+ \splittopskip=\topskip \splitmaxdepth=\maxdepth
+ \dimen@ = \vsize
+ \divide\dimen@ by 2
+ %
+ % box0 will be the left-hand column, box2 the right.
+ \setbox0=\vsplit\PAGE to\dimen@ \setbox2=\vsplit\PAGE to\dimen@
+ \global\advance\vsize by 2\ht\partialpage
+ \onepageout\pagesofar % empty except for the first time we are called
+ \unvbox\PAGE
+ \penalty\outputpenalty
+}
+%
+% Re-output the contents of the output page -- any previous material,
+% followed by the two boxes we just split, in box0 and box2.
+\def\pagesofar{%
+ \unvbox\partialpage
+ %
+ \hsize = \doublecolumnhsize
+ \wd0=\hsize \wd2=\hsize
+ \hbox to\txipagewidth{\box0\hfil\box2}%
+}
+
+
+% Finished with double columns.
+\def\enddoublecolumns{%
+ % The following penalty ensures that the page builder is exercised
+ % _before_ we change the output routine. This is necessary in the
+ % following situation:
+ %
+ % The last section of the index consists only of a single entry.
+ % Before this section, \pagetotal is less than \pagegoal, so no
+ % break occurs before the last section starts. However, the last
+ % section, consisting of \initial and the single \entry, does not
+ % fit on the page and has to be broken off. Without the following
+ % penalty the page builder will not be exercised until \eject
+ % below, and by that time we'll already have changed the output
+ % routine to the \balancecolumns version, so the next-to-last
+ % double-column page will be processed with \balancecolumns, which
+ % is wrong: The two columns will go to the main vertical list, with
+ % the broken-off section in the recent contributions. As soon as
+ % the output routine finishes, TeX starts reconsidering the page
+ % break. The two columns and the broken-off section both fit on the
+ % page, because the two columns now take up only half of the page
+ % goal. When TeX sees \eject from below which follows the final
+ % section, it invokes the new output routine that we've set after
+ % \balancecolumns below; \onepageout will try to fit the two columns
+ % and the final section into the vbox of \txipageheight (see
+ % \pagebody), causing an overfull box.
+ %
+ % Note that glue won't work here, because glue does not exercise the
+ % page builder, unlike penalties (see The TeXbook, pp. 280-281).
+ \penalty0
+ %
+ \output = {%
+ % Split the last of the double-column material.
+ \savetopmark
+ \balancecolumns
+ }%
+ \eject % call the \output just set
+ \ifdim\pagetotal=0pt
+ % Having called \balancecolumns once, we do not
+ % want to call it again. Therefore, reset \output to its normal
+ % definition right away.
+ \global\output=\expandafter{\the\defaultoutput}
+ %
+ \endgroup % started in \begindoublecolumns
+ % Leave the double-column material on the current page, no automatic
+ % page break.
+ \box\balancedcolumns
+ %
+ % \pagegoal was set to the doubled \vsize above, since we restarted
+ % the current page. We're now back to normal single-column
+ % typesetting, so reset \pagegoal to the normal \vsize.
+ \global\vsize = \txipageheight %
+ \pagegoal = \txipageheight %
+ \else
+ % We had some left-over material. This might happen when \doublecolumnout
+ % is called in \balancecolumns. Try again.
+ \expandafter\enddoublecolumns
+ \fi
+}
+\newbox\balancedcolumns
+\setbox\balancedcolumns=\vbox{shouldnt see this}%
+%
+% Only called for the last of the double column material. \doublecolumnout
+% does the others.
+\def\balancecolumns{%
+ \setbox0 = \vbox{\unvbox\PAGE}% like \box255 but more efficient, see p.120.
+ \dimen@ = \ht0
+ \ifdim\dimen@<7\baselineskip
+ % Don't split a short final column in two.
+ \setbox2=\vbox{}%
+ \global\setbox\balancedcolumns=\vbox{\pagesofar}%
+ \else
+ % double the leading vertical space
+ \advance\dimen@ by \topskip
+ \advance\dimen@ by-\baselineskip
+ \divide\dimen@ by 2 % target to split to
+ \dimen@ii = \dimen@
+ \splittopskip = \topskip
+ % Loop until left column is at least as high as the right column.
+ {%
+ \vbadness = 10000
+ \loop
+ \global\setbox3 = \copy0
+ \global\setbox1 = \vsplit3 to \dimen@
+ \ifdim\ht1<\ht3
+ \global\advance\dimen@ by 1pt
+ \repeat
+ }%
+ % Now the left column is in box 1, and the right column in box 3.
+ %
+ % Check whether the left column has come out higher than the page itself.
+ % (Note that we have doubled \vsize for the double columns, so
+ % the actual height of the page is 0.5\vsize).
+ \ifdim2\ht1>\vsize
+ % It appears that we have been called upon to balance too much material.
+ % Output some of it with \doublecolumnout, leaving the rest on the page.
+ \setbox\PAGE=\box0
+ \doublecolumnout
+ \else
+ % Compare the heights of the two columns.
+ \ifdim4\ht1>5\ht3
+ % Column heights are too different, so don't make their bottoms
+ % flush with each other.
+ \setbox2=\vbox to \ht1 {\unvbox3\vfill}%
+ \setbox0=\vbox to \ht1 {\unvbox1\vfill}%
+ \else
+ % Make column bottoms flush with each other.
+ \setbox2=\vbox to\ht1{\unvbox3\unskip}%
+ \setbox0=\vbox to\ht1{\unvbox1\unskip}%
+ \fi
+ \global\setbox\balancedcolumns=\vbox{\pagesofar}%
+ \fi
+ \fi
+ %
+}
+\catcode`\@ = \other
+
+
+\message{sectioning,}
+% Chapters, sections, etc.
+
+% Let's start with @part.
+\outer\parseargdef\part{\partzzz{#1}}
+\def\partzzz#1{%
+ \chapoddpage
+ \null
+ \vskip.3\vsize % move it down on the page a bit
+ \begingroup
+ \noindent \titlefonts\rm #1\par % the text
+ \let\lastnode=\empty % no node to associate with
+ \writetocentry{part}{#1}{}% but put it in the toc
+ \headingsoff % no headline or footline on the part page
+ % This outputs a mark at the end of the page that clears \thischapter
+ % and \thissection, as is done in \startcontents.
+ \let\pchapsepmacro\relax
+ \chapmacro{}{Yomitfromtoc}{}%
+ \chapoddpage
+ \endgroup
+}
+
+% \unnumberedno is an oxymoron. But we count the unnumbered
+% sections so that we can refer to them unambiguously in the pdf
+% outlines by their "section number". We avoid collisions with chapter
+% numbers by starting them at 10000. (If a document ever has 10000
+% chapters, we're in trouble anyway, I'm sure.)
+\newcount\unnumberedno \unnumberedno = 10000
+\newcount\chapno
+\newcount\secno \secno=0
+\newcount\subsecno \subsecno=0
+\newcount\subsubsecno \subsubsecno=0
+
+% This counter is funny since it counts through charcodes of letters A, B, ...
+\newcount\appendixno \appendixno = `\@
+%
+% \def\appendixletter{\char\the\appendixno}
+% We do the following ugly conditional instead of the above simple
+% construct for the sake of pdftex, which needs the actual
+% letter in the expansion, not just typeset.
+%
+\def\appendixletter{%
+ \ifnum\appendixno=`A A%
+ \else\ifnum\appendixno=`B B%
+ \else\ifnum\appendixno=`C C%
+ \else\ifnum\appendixno=`D D%
+ \else\ifnum\appendixno=`E E%
+ \else\ifnum\appendixno=`F F%
+ \else\ifnum\appendixno=`G G%
+ \else\ifnum\appendixno=`H H%
+ \else\ifnum\appendixno=`I I%
+ \else\ifnum\appendixno=`J J%
+ \else\ifnum\appendixno=`K K%
+ \else\ifnum\appendixno=`L L%
+ \else\ifnum\appendixno=`M M%
+ \else\ifnum\appendixno=`N N%
+ \else\ifnum\appendixno=`O O%
+ \else\ifnum\appendixno=`P P%
+ \else\ifnum\appendixno=`Q Q%
+ \else\ifnum\appendixno=`R R%
+ \else\ifnum\appendixno=`S S%
+ \else\ifnum\appendixno=`T T%
+ \else\ifnum\appendixno=`U U%
+ \else\ifnum\appendixno=`V V%
+ \else\ifnum\appendixno=`W W%
+ \else\ifnum\appendixno=`X X%
+ \else\ifnum\appendixno=`Y Y%
+ \else\ifnum\appendixno=`Z Z%
+ % The \the is necessary, despite appearances, because \appendixletter is
+ % expanded while writing the .toc file. \char\appendixno is not
+ % expandable, thus it is written literally, thus all appendixes come out
+ % with the same letter (or @) in the toc without it.
+ \else\char\the\appendixno
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi}
+
+% Each @chapter defines these (using marks) as the number+name, number
+% and name of the chapter. Page headings and footings can use
+% these. @section does likewise.
+\def\thischapter{}
+\def\thischapternum{}
+\def\thischaptername{}
+\def\thissection{}
+\def\thissectionnum{}
+\def\thissectionname{}
+
+\newcount\absseclevel % used to calculate proper heading level
+\newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count
+
+% @raisesections: treat @section as chapter, @subsection as section, etc.
+\def\raisesections{\global\advance\secbase by -1}
+
+% @lowersections: treat @chapter as section, @section as subsection, etc.
+\def\lowersections{\global\advance\secbase by 1}
+
+% we only have subsub.
+\chardef\maxseclevel = 3
+%
+% A numbered section within an unnumbered changes to unnumbered too.
+% To achieve this, remember the "biggest" unnum. sec. we are currently in:
+\chardef\unnlevel = \maxseclevel
+%
+% Trace whether the current chapter is an appendix or not:
+% \chapheadtype is "N" or "A", unnumbered chapters are ignored.
+\def\chapheadtype{N}
+
+% Choose a heading macro
+% #1 is heading type
+% #2 is heading level
+% #3 is text for heading
+\def\genhead#1#2#3{%
+ % Compute the abs. sec. level:
+ \absseclevel=#2
+ \advance\absseclevel by \secbase
+ % Make sure \absseclevel doesn't fall outside the range:
+ \ifnum \absseclevel < 0
+ \absseclevel = 0
+ \else
+ \ifnum \absseclevel > 3
+ \absseclevel = 3
+ \fi
+ \fi
+ % The heading type:
+ \def\headtype{#1}%
+ \if \headtype U%
+ \ifnum \absseclevel < \unnlevel
+ \chardef\unnlevel = \absseclevel
+ \fi
+ \else
+ % Check for appendix sections:
+ \ifnum \absseclevel = 0
+ \edef\chapheadtype{\headtype}%
+ \else
+ \if \headtype A\if \chapheadtype N%
+ \errmessage{@appendix... within a non-appendix chapter}%
+ \fi\fi
+ \fi
+ % Check for numbered within unnumbered:
+ \ifnum \absseclevel > \unnlevel
+ \def\headtype{U}%
+ \else
+ \chardef\unnlevel = 3
+ \fi
+ \fi
+ % Now print the heading:
+ \if \headtype U%
+ \ifcase\absseclevel
+ \unnumberedzzz{#3}%
+ \or \unnumberedseczzz{#3}%
+ \or \unnumberedsubseczzz{#3}%
+ \or \unnumberedsubsubseczzz{#3}%
+ \fi
+ \else
+ \if \headtype A%
+ \ifcase\absseclevel
+ \appendixzzz{#3}%
+ \or \appendixsectionzzz{#3}%
+ \or \appendixsubseczzz{#3}%
+ \or \appendixsubsubseczzz{#3}%
+ \fi
+ \else
+ \ifcase\absseclevel
+ \chapterzzz{#3}%
+ \or \seczzz{#3}%
+ \or \numberedsubseczzz{#3}%
+ \or \numberedsubsubseczzz{#3}%
+ \fi
+ \fi
+ \fi
+ \suppressfirstparagraphindent
+}
+
+% an interface:
+\def\numhead{\genhead N}
+\def\apphead{\genhead A}
+\def\unnmhead{\genhead U}
+
+% @chapter, @appendix, @unnumbered. Increment top-level counter, reset
+% all lower-level sectioning counters to zero.
+%
+% Also set \chaplevelprefix, which we prepend to @float sequence numbers
+% (e.g., figures), q.v. By default (before any chapter), that is empty.
+\let\chaplevelprefix = \empty
+%
+\outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz
+\def\chapterzzz#1{%
+ % section resetting is \global in case the chapter is in a group, such
+ % as an @include file.
+ \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+ \global\advance\chapno by 1
+ %
+ % Used for \float.
+ \gdef\chaplevelprefix{\the\chapno.}%
+ \resetallfloatnos
+ %
+ % \putwordChapter can contain complex things in translations.
+ \toks0=\expandafter{\putwordChapter}%
+ \message{\the\toks0 \space \the\chapno}%
+ %
+ % Write the actual heading.
+ \chapmacro{#1}{Ynumbered}{\the\chapno}%
+ %
+ % So @section and the like are numbered underneath this chapter.
+ \global\let\section = \numberedsec
+ \global\let\subsection = \numberedsubsec
+ \global\let\subsubsection = \numberedsubsubsec
+}
+
+\outer\parseargdef\appendix{\apphead0{#1}} % normally calls appendixzzz
+%
+\def\appendixzzz#1{%
+ \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+ \global\advance\appendixno by 1
+ \gdef\chaplevelprefix{\appendixletter.}%
+ \resetallfloatnos
+ %
+ % \putwordAppendix can contain complex things in translations.
+ \toks0=\expandafter{\putwordAppendix}%
+ \message{\the\toks0 \space \appendixletter}%
+ %
+ \chapmacro{#1}{Yappendix}{\appendixletter}%
+ %
+ \global\let\section = \appendixsec
+ \global\let\subsection = \appendixsubsec
+ \global\let\subsubsection = \appendixsubsubsec
+}
+
+% normally unnmhead0 calls unnumberedzzz:
+\outer\parseargdef\unnumbered{\unnmhead0{#1}}
+\def\unnumberedzzz#1{%
+ \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+ \global\advance\unnumberedno by 1
+ %
+ % Since an unnumbered has no number, no prefix for figures.
+ \global\let\chaplevelprefix = \empty
+ \resetallfloatnos
+ %
+ % This used to be simply \message{#1}, but TeX fully expands the
+ % argument to \message. Therefore, if #1 contained @-commands, TeX
+ % expanded them. For example, in `@unnumbered The @cite{Book}', TeX
+ % expanded @cite (which turns out to cause errors because \cite is meant
+ % to be executed, not expanded).
+ %
+ % Anyway, we don't want the fully-expanded definition of @cite to appear
+ % as a result of the \message, we just want `@cite' itself. We use
+ % \the<toks register> to achieve this: TeX expands \the<toks> only once,
+ % simply yielding the contents of <toks register>. (We also do this for
+ % the toc entries.)
+ \toks0 = {#1}%
+ \message{(\the\toks0)}%
+ %
+ \chapmacro{#1}{Ynothing}{\the\unnumberedno}%
+ %
+ \global\let\section = \unnumberedsec
+ \global\let\subsection = \unnumberedsubsec
+ \global\let\subsubsection = \unnumberedsubsubsec
+}
+
+% @centerchap is like @unnumbered, but the heading is centered.
+\outer\parseargdef\centerchap{%
+ \let\centerparametersmaybe = \centerparameters
+ \unnmhead0{#1}%
+ \let\centerparametersmaybe = \relax
+}
+
+% @top is like @unnumbered.
+\let\top\unnumbered
+
+% Sections.
+%
+\outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz
+\def\seczzz#1{%
+ \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1
+ \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}%
+}
+
+% normally calls appendixsectionzzz:
+\outer\parseargdef\appendixsection{\apphead1{#1}}
+\def\appendixsectionzzz#1{%
+ \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1
+ \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}%
+}
+\let\appendixsec\appendixsection
+
+% normally calls unnumberedseczzz:
+\outer\parseargdef\unnumberedsec{\unnmhead1{#1}}
+\def\unnumberedseczzz#1{%
+ \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1
+ \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}%
+}
+
+% Subsections.
+%
+% normally calls numberedsubseczzz:
+\outer\parseargdef\numberedsubsec{\numhead2{#1}}
+\def\numberedsubseczzz#1{%
+ \global\subsubsecno=0 \global\advance\subsecno by 1
+ \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}%
+}
+
+% normally calls appendixsubseczzz:
+\outer\parseargdef\appendixsubsec{\apphead2{#1}}
+\def\appendixsubseczzz#1{%
+ \global\subsubsecno=0 \global\advance\subsecno by 1
+ \sectionheading{#1}{subsec}{Yappendix}%
+ {\appendixletter.\the\secno.\the\subsecno}%
+}
+
+% normally calls unnumberedsubseczzz:
+\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}}
+\def\unnumberedsubseczzz#1{%
+ \global\subsubsecno=0 \global\advance\subsecno by 1
+ \sectionheading{#1}{subsec}{Ynothing}%
+ {\the\unnumberedno.\the\secno.\the\subsecno}%
+}
+
+% Subsubsections.
+%
+% normally numberedsubsubseczzz:
+\outer\parseargdef\numberedsubsubsec{\numhead3{#1}}
+\def\numberedsubsubseczzz#1{%
+ \global\advance\subsubsecno by 1
+ \sectionheading{#1}{subsubsec}{Ynumbered}%
+ {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+% normally appendixsubsubseczzz:
+\outer\parseargdef\appendixsubsubsec{\apphead3{#1}}
+\def\appendixsubsubseczzz#1{%
+ \global\advance\subsubsecno by 1
+ \sectionheading{#1}{subsubsec}{Yappendix}%
+ {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+% normally unnumberedsubsubseczzz:
+\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}}
+\def\unnumberedsubsubseczzz#1{%
+ \global\advance\subsubsecno by 1
+ \sectionheading{#1}{subsubsec}{Ynothing}%
+ {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+% These macros control what the section commands do, according
+% to what kind of chapter we are in (ordinary, appendix, or unnumbered).
+% Define them by default for a numbered chapter.
+\let\section = \numberedsec
+\let\subsection = \numberedsubsec
+\let\subsubsection = \numberedsubsubsec
+
+% Define @majorheading, @heading and @subheading
+
+\def\majorheading{%
+ {\advance\chapheadingskip by 10pt \chapbreak }%
+ \parsearg\chapheadingzzz
+}
+
+\def\chapheading{\chapbreak \parsearg\chapheadingzzz}
+\def\chapheadingzzz#1{%
+ \vbox{\chapfonts \raggedtitlesettings #1\par}%
+ \nobreak\bigskip \nobreak
+ \suppressfirstparagraphindent
+}
+
+% @heading, @subheading, @subsubheading.
+\parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{}
+ \suppressfirstparagraphindent}
+\parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{}
+ \suppressfirstparagraphindent}
+\parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{}
+ \suppressfirstparagraphindent}
+
+% These macros generate a chapter, section, etc. heading only
+% (including whitespace, linebreaking, etc. around it),
+% given all the information in convenient, parsed form.
+
+% Args are the skip and penalty (usually negative)
+\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi}
+
+% Parameter controlling skip before chapter headings (if needed)
+\newskip\chapheadingskip
+
+% Define plain chapter starts, and page on/off switching for it.
+\def\chapbreak{\dobreak \chapheadingskip {-4000}}
+
+% Start a new page
+\def\chappager{\par\vfill\supereject}
+
+% \chapoddpage - start on an odd page for a new chapter
+% Because \domark is called before \chapoddpage, the filler page will
+% get the headings for the next chapter, which is wrong. But we don't
+% care -- we just disable all headings on the filler page.
+\def\chapoddpage{%
+ \chappager
+ \ifodd\pageno \else
+ \begingroup
+ \headingsoff
+ \null
+ \chappager
+ \endgroup
+ \fi
+}
+
+\parseargdef\setchapternewpage{\csname CHAPPAG#1\endcsname}
+
+\def\CHAPPAGoff{%
+\global\let\contentsalignmacro = \chappager
+\global\let\pchapsepmacro=\chapbreak
+\global\let\pagealignmacro=\chappager}
+
+\def\CHAPPAGon{%
+\global\let\contentsalignmacro = \chappager
+\global\let\pchapsepmacro=\chappager
+\global\let\pagealignmacro=\chappager
+\global\def\HEADINGSon{\HEADINGSsingle}}
+
+\def\CHAPPAGodd{%
+\global\let\contentsalignmacro = \chapoddpage
+\global\let\pchapsepmacro=\chapoddpage
+\global\let\pagealignmacro=\chapoddpage
+\global\def\HEADINGSon{\HEADINGSdouble}}
+
+\CHAPPAGon
+
+% \chapmacro - Chapter opening.
+%
+% #1 is the text, #2 is the section type (Ynumbered, Ynothing,
+% Yappendix, Yomitfromtoc), #3 the chapter number.
+% Not used for @heading series.
+%
+% To test against our argument.
+\def\Ynothingkeyword{Ynothing}
+\def\Yappendixkeyword{Yappendix}
+\def\Yomitfromtockeyword{Yomitfromtoc}
+%
+\def\chapmacro#1#2#3{%
+ \expandafter\ifx\thisenv\titlepage\else
+ \checkenv{}% chapters, etc., should not start inside an environment.
+ \fi
+ % Insert the first mark before the heading break (see notes for \domark).
+ \let\prevchapterdefs=\currentchapterdefs
+ \let\prevsectiondefs=\currentsectiondefs
+ \gdef\currentsectiondefs{\gdef\thissectionname{}\gdef\thissectionnum{}%
+ \gdef\thissection{}}%
+ %
+ \def\temptype{#2}%
+ \ifx\temptype\Ynothingkeyword
+ \gdef\currentchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}%
+ \gdef\thischapter{\thischaptername}}%
+ \else\ifx\temptype\Yomitfromtockeyword
+ \gdef\currentchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}%
+ \gdef\thischapter{}}%
+ \else\ifx\temptype\Yappendixkeyword
+ \toks0={#1}%
+ \xdef\currentchapterdefs{%
+ \gdef\noexpand\thischaptername{\the\toks0}%
+ \gdef\noexpand\thischapternum{\appendixletter}%
+ % \noexpand\putwordAppendix avoids expanding indigestible
+ % commands in some of the translations.
+ \gdef\noexpand\thischapter{\noexpand\putwordAppendix{}
+ \noexpand\thischapternum:
+ \noexpand\thischaptername}%
+ }%
+ \else
+ \toks0={#1}%
+ \xdef\currentchapterdefs{%
+ \gdef\noexpand\thischaptername{\the\toks0}%
+ \gdef\noexpand\thischapternum{\the\chapno}%
+ % \noexpand\putwordChapter avoids expanding indigestible
+ % commands in some of the translations.
+ \gdef\noexpand\thischapter{\noexpand\putwordChapter{}
+ \noexpand\thischapternum:
+ \noexpand\thischaptername}%
+ }%
+ \fi\fi\fi
+ %
+ % Output the mark. Pass it through \safewhatsit, to take care of
+ % the preceding space.
+ \safewhatsit\domark
+ %
+ % Insert the chapter heading break.
+ \pchapsepmacro
+ %
+ % Now the second mark, after the heading break. No break points
+ % between here and the heading.
+ \let\prevchapterdefs=\currentchapterdefs
+ \let\prevsectiondefs=\currentsectiondefs
+ \domark
+ %
+ {%
+ \chapfonts \rm
+ \let\footnote=\errfootnoteheading % give better error message
+ %
+ % Have to define \currentsection before calling \donoderef, because the
+ % xref code eventually uses it. On the other hand, it has to be called
+ % after \pchapsepmacro, or the headline will change too soon.
+ \gdef\currentsection{#1}%
+ %
+ % Only insert the separating space if we have a chapter/appendix
+ % number, and don't print the unnumbered ``number''.
+ \ifx\temptype\Ynothingkeyword
+ \setbox0 = \hbox{}%
+ \def\toctype{unnchap}%
+ \else\ifx\temptype\Yomitfromtockeyword
+ \setbox0 = \hbox{}% contents like unnumbered, but no toc entry
+ \def\toctype{omit}%
+ \else\ifx\temptype\Yappendixkeyword
+ \setbox0 = \hbox{\putwordAppendix{} #3\enspace}%
+ \def\toctype{app}%
+ \else
+ \setbox0 = \hbox{#3\enspace}%
+ \def\toctype{numchap}%
+ \fi\fi\fi
+ %
+ % Write the toc entry for this chapter. Must come before the
+ % \donoderef, because we include the current node name in the toc
+ % entry, and \donoderef resets it to empty.
+ \writetocentry{\toctype}{#1}{#3}%
+ %
+ % For pdftex, we have to write out the node definition (aka, make
+ % the pdfdest) after any page break, but before the actual text has
+ % been typeset. If the destination for the pdf outline is after the
+ % text, then jumping from the outline may wind up with the text not
+ % being visible, for instance under high magnification.
+ \donoderef{#2}%
+ %
+ % Typeset the actual heading.
+ \nobreak % Avoid page breaks at the interline glue.
+ \vbox{\raggedtitlesettings \hangindent=\wd0 \centerparametersmaybe
+ \unhbox0 #1\par}%
+ }%
+ \nobreak\bigskip % no page break after a chapter title
+ \nobreak
+}
+
+% @centerchap -- centered and unnumbered.
+\let\centerparametersmaybe = \relax
+\def\centerparameters{%
+ \advance\rightskip by 3\rightskip
+ \leftskip = \rightskip
+ \parfillskip = 0pt
+}
+
+
+% Section titles. These macros combine the section number parts and
+% call the generic \sectionheading to do the printing.
+%
+\newskip\secheadingskip
+\def\secheadingbreak{\dobreak \secheadingskip{-1000}}
+
+% Subsection titles.
+\newskip\subsecheadingskip
+\def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}}
+
+% Subsubsection titles.
+\def\subsubsecheadingskip{\subsecheadingskip}
+\def\subsubsecheadingbreak{\subsecheadingbreak}
+
+
+% Print any size, any type, section title.
+%
+% #1 is the text of the title,
+% #2 is the section level (sec/subsec/subsubsec),
+% #3 is the section type (Ynumbered, Ynothing, Yappendix, Yomitfromtoc),
+% #4 is the section number.
+%
+\def\seckeyword{sec}
+%
+\def\sectionheading#1#2#3#4{%
+ {%
+ \def\sectionlevel{#2}%
+ \def\temptype{#3}%
+ %
+ % It is ok for the @heading series commands to appear inside an
+ % environment (it's been historically allowed, though the logic is
+ % dubious), but not the others.
+ \ifx\temptype\Yomitfromtockeyword\else
+ \checkenv{}% non-@*heading should not be in an environment.
+ \fi
+ \let\footnote=\errfootnoteheading
+ %
+ % Switch to the right set of fonts.
+ \csname #2fonts\endcsname \rm
+ %
+ % Insert first mark before the heading break (see notes for \domark).
+ \let\prevsectiondefs=\currentsectiondefs
+ \ifx\temptype\Ynothingkeyword
+ \ifx\sectionlevel\seckeyword
+
\gdef\currentsectiondefs{\gdef\thissectionname{#1}\gdef\thissectionnum{}%
+ \gdef\thissection{\thissectionname}}%
+ \fi
+ \else\ifx\temptype\Yomitfromtockeyword
+ % Don't redefine \thissection.
+ \else\ifx\temptype\Yappendixkeyword
+ \ifx\sectionlevel\seckeyword
+ \toks0={#1}%
+ \xdef\currentsectiondefs{%
+ \gdef\noexpand\thissectionname{\the\toks0}%
+ \gdef\noexpand\thissectionnum{#4}%
+ % \noexpand\putwordSection avoids expanding indigestible
+ % commands in some of the translations.
+ \gdef\noexpand\thissection{\noexpand\putwordSection{}
+ \noexpand\thissectionnum:
+ \noexpand\thissectionname}%
+ }%
+ \fi
+ \else
+ \ifx\sectionlevel\seckeyword
+ \toks0={#1}%
+ \xdef\currentsectiondefs{%
+ \gdef\noexpand\thissectionname{\the\toks0}%
+ \gdef\noexpand\thissectionnum{#4}%
+ % \noexpand\putwordSection avoids expanding indigestible
+ % commands in some of the translations.
+ \gdef\noexpand\thissection{\noexpand\putwordSection{}
+ \noexpand\thissectionnum:
+ \noexpand\thissectionname}%
+ }%
+ \fi
+ \fi\fi\fi
+ %
+ % Go into vertical mode. Usually we'll already be there, but we
+ % don't want the following whatsit to end up in a preceding paragraph
+ % if the document didn't happen to have a blank line.
+ \par
+ %
+ % Output the mark. Pass it through \safewhatsit, to take care of
+ % the preceding space.
+ \safewhatsit\domark
+ %
+ % Insert space above the heading.
+ \csname #2headingbreak\endcsname
+ %
+ % Now the second mark, after the heading break. No break points
+ % between here and the heading.
+ \global\let\prevsectiondefs=\currentsectiondefs
+ \domark
+ %
+ % Only insert the space after the number if we have a section number.
+ \ifx\temptype\Ynothingkeyword
+ \setbox0 = \hbox{}%
+ \def\toctype{unn}%
+ \gdef\currentsection{#1}%
+ \else\ifx\temptype\Yomitfromtockeyword
+ % for @headings -- no section number, don't include in toc,
+ % and don't redefine \currentsection.
+ \setbox0 = \hbox{}%
+ \def\toctype{omit}%
+ \let\sectionlevel=\empty
+ \else\ifx\temptype\Yappendixkeyword
+ \setbox0 = \hbox{#4\enspace}%
+ \def\toctype{app}%
+ \gdef\currentsection{#1}%
+ \else
+ \setbox0 = \hbox{#4\enspace}%
+ \def\toctype{num}%
+ \gdef\currentsection{#1}%
+ \fi\fi\fi
+ %
+ % Write the toc entry (before \donoderef). See comments in \chapmacro.
+ \writetocentry{\toctype\sectionlevel}{#1}{#4}%
+ %
+ % Write the node reference (= pdf destination for pdftex).
+ % Again, see comments in \chapmacro.
+ \donoderef{#3}%
+ %
+ % Interline glue will be inserted when the vbox is completed.
+ % That glue will be a valid breakpoint for the page, since it'll be
+ % preceded by a whatsit (usually from the \donoderef, or from the
+ % \writetocentry if there was no node). We don't want to allow that
+ % break, since then the whatsits could end up on page n while the
+ % section is on page n+1, thus toc/etc. are wrong. Debian bug 276000.
+ \nobreak
+ %
+ % Output the actual section heading.
+ \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright
+ \hangindent=\wd0 % zero if no section number
+ \unhbox0 #1}%
+ }%
+ % Add extra space after the heading -- half of whatever came above it.
+ % Don't allow stretch, though.
+ \kern .5 \csname #2headingskip\endcsname
+ %
+ % Do not let the kern be a potential breakpoint, as it would be if it
+ % was followed by glue.
+ \nobreak
+ %
+ % We'll almost certainly start a paragraph next, so don't let that
+ % glue accumulate. (Not a breakpoint because it's preceded by a
+ % discardable item.) However, when a paragraph is not started next
+ % (\startdefun, \cartouche, \center, etc.), this needs to be wiped out
+ % or the negative glue will cause weirdly wrong output, typically
+ % obscuring the section heading with something else.
+ \vskip-\parskip
+ %
+ % This is so the last item on the main vertical list is a known
+ % \penalty > 10000, so \startdefun, etc., can recognize the situation
+ % and do the needful.
+ \penalty 10001
+}
+
+
+\message{toc,}
+% Table of contents.
+\newwrite\tocfile
+
+% Write an entry to the toc file, opening it if necessary.
+% Called from @chapter, etc.
+%
+% Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno}
+% We append the current node name (if any) and page number as additional
+% arguments for the \{chap,sec,...}entry macros which will eventually
+% read this. The node name is used in the pdf outlines as the
+% destination to jump to.
+%
+% We open the .toc file for writing here instead of at @setfilename (or
+% any other fixed time) so that @contents can be anywhere in the document.
+% But if #1 is `omit', then we don't do anything. This is used for the
+% table of contents chapter openings themselves.
+%
+\newif\iftocfileopened
+\def\omitkeyword{omit}%
+%
+\def\writetocentry#1#2#3{%
+ \edef\writetoctype{#1}%
+ \ifx\writetoctype\omitkeyword \else
+ \iftocfileopened\else
+ \immediate\openout\tocfile = \jobname.toc
+ \global\tocfileopenedtrue
+ \fi
+ %
+ \iflinks
+ {\atdummies
+ \edef\temp{%
+ \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}%
+ \temp
+ }%
+ \fi
+ \fi
+ %
+ % Tell \shipout to create a pdf destination on each page, if we're
+ % writing pdf. These are used in the table of contents. We can't
+ % just write one on every page because the title pages are numbered
+ % 1 and 2 (the page numbers aren't printed), and so are the first
+ % two pages of the document. Thus, we'd have two destinations named
+ % `1', and two named `2'.
+ \ifpdforxetex
+ \global\pdfmakepagedesttrue
+ \fi
+}
+
+
+% These characters do not print properly in the Computer Modern roman
+% fonts, so we must take special care. This is more or less redundant
+% with the Texinfo input format setup at the end of this file.
+%
+\def\activecatcodes{%
+ \catcode`\"=\active
+ \catcode`\$=\active
+ \catcode`\<=\active
+ \catcode`\>=\active
+ \catcode`\\=\active
+ \catcode`\^=\active
+ \catcode`\_=\active
+ \catcode`\|=\active
+ \catcode`\~=\active
+}
+
+
+% Read the toc file, which is essentially Texinfo input.
+\def\readtocfile{%
+ \setupdatafile
+ \activecatcodes
+ \input \tocreadfilename
+}
+
+\newskip\contentsrightmargin \contentsrightmargin=1in
+\newcount\savepageno
+\newcount\lastnegativepageno \lastnegativepageno = -1
+
+% Prepare to read what we've written to \tocfile.
+%
+\def\startcontents#1{%
+ % If @setchapternewpage on, and @headings double, the contents should
+ % start on an odd page, unlike chapters. Thus, we maintain
+ % \contentsalignmacro in parallel with \pagealignmacro.
+ % From: Torbjorn Granlund <tege@matematik.su.se>
+ \contentsalignmacro
+ \immediate\closeout\tocfile
+ %
+ % Don't need to put `Contents' or `Short Contents' in the headline.
+ % It is abundantly clear what they are.
+ \chapmacro{#1}{Yomitfromtoc}{}%
+ %
+ \savepageno = \pageno
+ \begingroup % Set up to handle contents files properly.
+ \raggedbottom % Worry more about breakpoints than the bottom.
+ \entryrightmargin=\contentsrightmargin % Don't use the full line length.
+ %
+ % Roman numerals for page numbers.
+ \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi
+}
+
+% redefined for the two-volume lispref. We always output on
+% \jobname.toc even if this is redefined.
+%
+\def\tocreadfilename{\jobname.toc}
+
+% Normal (long) toc.
+%
+\def\contents{%
+ \startcontents{\putwordTOC}%
+ \openin 1 \tocreadfilename\space
+ \ifeof 1 \else
+ \readtocfile
+ \fi
+ \vfill \eject
+ \contentsalignmacro % in case @setchapternewpage odd is in effect
+ \ifeof 1 \else
+ \pdfmakeoutlines
+ \fi
+ \closein 1
+ \endgroup
+ \lastnegativepageno = \pageno
+ \global\pageno = \savepageno
+}
+
+% And just the chapters.
+\def\summarycontents{%
+ \startcontents{\putwordShortTOC}%
+ %
+ \let\partentry = \shortpartentry
+ \let\numchapentry = \shortchapentry
+ \let\appentry = \shortchapentry
+ \let\unnchapentry = \shortunnchapentry
+ % We want a true roman here for the page numbers.
+ \secfonts
+ \let\rm=\shortcontrm \let\bf=\shortcontbf
+ \let\sl=\shortcontsl \let\tt=\shortconttt
+ \rm
+ \hyphenpenalty = 10000
+ \advance\baselineskip by 1pt % Open it up a little.
+ \def\numsecentry##1##2##3##4{}
+ \let\appsecentry = \numsecentry
+ \let\unnsecentry = \numsecentry
+ \let\numsubsecentry = \numsecentry
+ \let\appsubsecentry = \numsecentry
+ \let\unnsubsecentry = \numsecentry
+ \let\numsubsubsecentry = \numsecentry
+ \let\appsubsubsecentry = \numsecentry
+ \let\unnsubsubsecentry = \numsecentry
+ \openin 1 \tocreadfilename\space
+ \ifeof 1 \else
+ \readtocfile
+ \fi
+ \closein 1
+ \vfill \eject
+ \contentsalignmacro % in case @setchapternewpage odd is in effect
+ \endgroup
+ \lastnegativepageno = \pageno
+ \global\pageno = \savepageno
+}
+\let\shortcontents = \summarycontents
+
+% Typeset the label for a chapter or appendix for the short contents.
+% The arg is, e.g., `A' for an appendix, or `3' for a chapter.
+%
+\def\shortchaplabel#1{%
+ % This space should be enough, since a single number is .5em, and the
+ % widest letter (M) is 1em, at least in the Computer Modern fonts.
+ % But use \hss just in case.
+ % (This space doesn't include the extra space that gets added after
+ % the label; that gets put in by \shortchapentry above.)
+ %
+ % We'd like to right-justify chapter numbers, but that looks strange
+ % with appendix letters. And right-justifying numbers and
+ % left-justifying letters looks strange when there is less than 10
+ % chapters. Have to read the whole toc once to know how many chapters
+ % there are before deciding ...
+ \hbox to 1em{#1\hss}%
+}
+
+% These macros generate individual entries in the table of contents.
+% The first argument is the chapter or section name.
+% The last argument is the page number.
+% The arguments in between are the chapter number, section number, ...
+
+% Parts, in the main contents. Replace the part number, which doesn't
+% exist, with an empty box. Let's hope all the numbers have the same width.
+% Also ignore the page number, which is conventionally not printed.
+\def\numeralbox{\setbox0=\hbox{8}\hbox to \wd0{\hfil}}
+\def\partentry#1#2#3#4{%
+ % Add stretch and a bonus for breaking the page before the part heading.
+ % This reduces the chance of the page being broken immediately after the
+ % part heading, before a following chapter heading.
+ \vskip 0pt plus 5\baselineskip
+ \penalty-300
+ \vskip 0pt plus -5\baselineskip
+ \dochapentry{\numeralbox\labelspace#1}{}%
+}
+%
+% Parts, in the short toc.
+\def\shortpartentry#1#2#3#4{%
+ \penalty-300
+ \vskip.5\baselineskip plus.15\baselineskip minus.1\baselineskip
+ \shortchapentry{{\bf #1}}{\numeralbox}{}{}%
+}
+
+% Chapters, in the main contents.
+\def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}}
+
+% Chapters, in the short toc.
+% See comments in \dochapentry re vbox and related settings.
+\def\shortchapentry#1#2#3#4{%
+ \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}%
+}
+
+% Appendices, in the main contents.
+% Need the word Appendix, and a fixed-size box.
+%
+\def\appendixbox#1{%
+ % We use M since it's probably the widest letter.
+ \setbox0 = \hbox{\putwordAppendix{} M}%
+ \hbox to \wd0{\putwordAppendix{} #1\hss}}
+%
+\def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\hskip.7em#1}{#4}}
+
+% Unnumbered chapters.
+\def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}}
+\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}}
+
+% Sections.
+\def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}}
+\let\appsecentry=\numsecentry
+\def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}}
+
+% Subsections.
+\def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}}
+\let\appsubsecentry=\numsubsecentry
+\def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}}
+
+% And subsubsections.
+\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}}
+\let\appsubsubsecentry=\numsubsubsecentry
+\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}}
+
+% This parameter controls the indentation of the various levels.
+% Same as \defaultparindent.
+\newdimen\tocindent \tocindent = 15pt
+
+% Now for the actual typesetting. In all these, #1 is the text and #2 is the
+% page number.
+%
+% If the toc has to be broken over pages, we want it to be at chapters
+% if at all possible; hence the \penalty.
+\def\dochapentry#1#2{%
+ \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip
+ \begingroup
+ % Move the page numbers slightly to the right
+ \advance\entryrightmargin by -0.05em
+ \chapentryfonts
+ \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+ \endgroup
+ \nobreak\vskip .25\baselineskip plus.1\baselineskip
+}
+
+\def\dosecentry#1#2{\begingroup
+ \secentryfonts \leftskip=\tocindent
+ \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+\def\dosubsecentry#1#2{\begingroup
+ \subsecentryfonts \leftskip=2\tocindent
+ \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+\def\dosubsubsecentry#1#2{\begingroup
+ \subsubsecentryfonts \leftskip=3\tocindent
+ \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+% We use the same \entry macro as for the index entries.
+\let\tocentry = \entry
+
+% Space between chapter (or whatever) number and the title.
+\def\labelspace{\hskip1em \relax}
+
+\def\dopageno#1{{\rm #1}}
+\def\doshortpageno#1{{\rm #1}}
+
+\def\chapentryfonts{\secfonts \rm}
+\def\secentryfonts{\textfonts}
+\def\subsecentryfonts{\textfonts}
+\def\subsubsecentryfonts{\textfonts}
+
+
+\message{environments,}
+% @foo ... @end foo.
+
+% @tex ... @end tex escapes into raw TeX temporarily.
+% One exception: @ is still an escape character, so that @end tex works.
+% But \@ or @@ will get a plain @ character.
+
+\envdef\tex{%
+ \setupmarkupstyle{tex}%
+ \catcode `\\=0 \catcode `\{=1 \catcode `\}=2
+ \catcode `\$=3 \catcode `\&=4 \catcode `\#=6
+ \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie
+ \catcode `\%=14
+ \catcode `\+=\other
+ \catcode `\"=\other
+ \catcode `\|=\other
+ \catcode `\<=\other
+ \catcode `\>=\other
+ \catcode `\`=\other
+ \catcode `\'=\other
+ %
+ % ' is active in math mode (mathcode"8000). So reset it, and all our
+ % other math active characters (just in case), to plain's definitions.
+ \mathactive
+ %
+ % Inverse of the list at the beginning of the file.
+ \let\b=\ptexb
+ \let\bullet=\ptexbullet
+ \let\c=\ptexc
+ \let\,=\ptexcomma
+ \let\.=\ptexdot
+ \let\dots=\ptexdots
+ \let\equiv=\ptexequiv
+ \let\!=\ptexexclam
+ \let\i=\ptexi
+ \let\indent=\ptexindent
+ \let\noindent=\ptexnoindent
+ \let\{=\ptexlbrace
+ \let\+=\tabalign
+ \let\}=\ptexrbrace
+ \let\/=\ptexslash
+ \let\sp=\ptexsp
+ \let\*=\ptexstar
+ %\let\sup=\ptexsup % do not redefine, we want @sup to work in math mode
+ \let\t=\ptext
+ \expandafter \let\csname top\endcsname=\ptextop % we've made it outer
+ \let\frenchspacing=\plainfrenchspacing
+ %
+ \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}%
+ \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}%
+ \def\@{@}%
+}
+% There is no need to define \Etex.
+
+% Define @lisp ... @end lisp.
+% @lisp environment forms a group so it can rebind things,
+% including the definition of @end lisp (which normally is erroneous).
+
+% Amount to narrow the margins by for @lisp.
+\newskip\lispnarrowing \lispnarrowing=0.4in
+
+% This is the definition that ^^M gets inside @lisp, @example, and other
+% such environments. \null is better than a space, since it doesn't
+% have any width.
+\def\lisppar{\null\endgraf}
+
+% This space is always present above and below environments.
+\newskip\envskipamount \envskipamount = 0pt
+
+% Make spacing and below environment symmetrical. We use \parskip here
+% to help in doing that, since in @example-like environments \parskip
+% is reset to zero; thus the \afterenvbreak inserts no space -- but the
+% start of the next paragraph will insert \parskip.
+%
+\def\aboveenvbreak{{%
+ % =10000 instead of <10000 because of a special case in \itemzzz and
+ % \sectionheading, q.v.
+ \ifnum \lastpenalty=10000 \else
+ \advance\envskipamount by \parskip
+ \endgraf
+ \ifdim\lastskip<\envskipamount
+ \removelastskip
+ \ifnum\lastpenalty<10000
+ % Penalize breaking before the environment, because preceding text
+ % often leads into it.
+ \penalty100
+ \fi
+ \vskip\envskipamount
+ \fi
+ \fi
+}}
+
+\def\afterenvbreak{{%
+ % =10000 instead of <10000 because of a special case in \itemzzz and
+ % \sectionheading, q.v.
+ \ifnum \lastpenalty=10000 \else
+ \advance\envskipamount by \parskip
+ \endgraf
+ \ifdim\lastskip<\envskipamount
+ \removelastskip
+ % it's not a good place to break if the last penalty was \nobreak
+ % or better ...
+ \ifnum\lastpenalty<10000 \penalty-50 \fi
+ \vskip\envskipamount
+ \fi
+ \fi
+}}
+
+% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins; it will
+% also clear it, so that its embedded environments do the narrowing again.
+\let\nonarrowing=\relax
+
+% @cartouche ... @end cartouche: draw rectangle w/rounded corners around
+% environment contents.
+
+%
+\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth
+\def\ctr{{\hskip 6pt\circle\char'010}}
+\def\cbl{{\circle\char'012\hskip -6pt}}
+\def\cbr{{\hskip 6pt\circle\char'011}}
+\def\carttop{\hbox to \cartouter{\hskip\lskip
+ \ctl\leaders\hrule height\circthick\hfil\ctr
+ \hskip\rskip}}
+\def\cartbot{\hbox to \cartouter{\hskip\lskip
+ \cbl\leaders\hrule height\circthick\hfil\cbr
+ \hskip\rskip}}
+%
+\newskip\lskip\newskip\rskip
+
+% only require the font if @cartouche is actually used
+\def\cartouchefontdefs{%
+ \font\circle=lcircle10\relax
+ \circthick=\fontdimen8\circle
+}
+\newdimen\circthick
+\newdimen\cartouter\newdimen\cartinner
+\newskip\normbskip\newskip\normpskip\newskip\normlskip
+
+
+\envdef\cartouche{%
+ \cartouchefontdefs
+ \ifhmode\par\fi % can't be in the midst of a paragraph.
+ \startsavinginserts
+ \lskip=\leftskip \rskip=\rightskip
+ \leftskip=0pt\rightskip=0pt % we want these *outside*.
+ \cartinner=\hsize \advance\cartinner by-\lskip
+ \advance\cartinner by-\rskip
+ \cartouter=\hsize
+ \advance\cartouter by 18.4pt % allow for 3pt kerns on either
+ % side, and for 6pt waste from
+ % each corner char, and rule thickness
+ \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip
+ %
+ % If this cartouche directly follows a sectioning command, we need the
+ % \parskip glue (backspaced over by default) or the cartouche can
+ % collide with the section heading.
+ \ifnum\lastpenalty>10000 \vskip\parskip \penalty\lastpenalty \fi
+ %
+ \setbox\groupbox=\vbox\bgroup
+ \baselineskip=0pt\parskip=0pt\lineskip=0pt
+ \carttop
+ \hbox\bgroup
+ \hskip\lskip
+ \vrule\kern3pt
+ \vbox\bgroup
+ \kern3pt
+ \hsize=\cartinner
+ \baselineskip=\normbskip
+ \lineskip=\normlskip
+ \parskip=\normpskip
+ \vskip -\parskip
+ \comment % For explanation, see the end of def\group.
+}
+\def\Ecartouche{%
+ \ifhmode\par\fi
+ \kern3pt
+ \egroup
+ \kern3pt\vrule
+ \hskip\rskip
+ \egroup
+ \cartbot
+ \egroup
+ \addgroupbox
+ \checkinserts
+}
+
+
+% This macro is called at the beginning of all the @example variants,
+% inside a group.
+\newdimen\nonfillparindent
+\def\nonfillstart{%
+ \aboveenvbreak
+ \ifdim\hfuzz < 12pt \hfuzz = 12pt \fi % Don't be fussy
+ \sepspaces % Make spaces be word-separators rather than space tokens.
+ \let\par = \lisppar % don't ignore blank lines
+ \obeylines % each line of input is a line of output
+ \parskip = 0pt
+ % Turn off paragraph indentation but redefine \indent to emulate
+ % the normal \indent.
+ \nonfillparindent=\parindent
+ \parindent = 0pt
+ \let\indent\nonfillindent
+ %
+ \emergencystretch = 0pt % don't try to avoid overfull boxes
+ \ifx\nonarrowing\relax
+ \advance \leftskip by \lispnarrowing
+ \exdentamount=\lispnarrowing
+ \else
+ \let\nonarrowing = \relax
+ \fi
+ \let\exdent=\nofillexdent
+}
+
+\begingroup
+\obeyspaces
+% We want to swallow spaces (but not other tokens) after the fake
+% @indent in our nonfill-environments, where spaces are normally
+% active and set to @tie, resulting in them not being ignored after
+% @indent.
+\gdef\nonfillindent{\futurelet\temp\nonfillindentcheck}%
+\gdef\nonfillindentcheck{%
+\ifx\temp %
+\expandafter\nonfillindentgobble%
+\else%
+\leavevmode\nonfillindentbox%
+\fi%
+}%
+\endgroup
+\def\nonfillindentgobble#1{\nonfillindent}
+\def\nonfillindentbox{\hbox to \nonfillparindent{\hss}}
+
+% If you want all examples etc. small: @set dispenvsize small.
+% If you want even small examples the full size: @set dispenvsize nosmall.
+% This affects the following displayed environments:
+% @example, @display, @format, @lisp
+%
+\def\smallword{small}
+\def\nosmallword{nosmall}
+\let\SETdispenvsize\relax
+\def\setnormaldispenv{%
+ \ifx\SETdispenvsize\smallword
+ % end paragraph for sake of leading, in case document has no blank
+ % line. This is redundant with what happens in \aboveenvbreak, but
+ % we need to do it before changing the fonts, and it's inconvenient
+ % to change the fonts afterward.
+ \ifnum \lastpenalty=10000 \else \endgraf \fi
+ \smallexamplefonts \rm
+ \fi
+}
+\def\setsmalldispenv{%
+ \ifx\SETdispenvsize\nosmallword
+ \else
+ \ifnum \lastpenalty=10000 \else \endgraf \fi
+ \smallexamplefonts \rm
+ \fi
+}
+
+% We often define two environments, @foo and @smallfoo.
+% Let's do it in one command. #1 is the env name, #2 the definition.
+\def\makedispenvdef#1#2{%
+ \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2}%
+ \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2}%
+ \expandafter\let\csname E#1\endcsname \afterenvbreak
+ \expandafter\let\csname Esmall#1\endcsname \afterenvbreak
+}
+
+% Define two environment synonyms (#1 and #2) for an environment.
+\def\maketwodispenvdef#1#2#3{%
+ \makedispenvdef{#1}{#3}%
+ \makedispenvdef{#2}{#3}%
+}
+%
+% @lisp: indented, narrowed, typewriter font;
+% @example: same as @lisp.
+%
+% @smallexample and @smalllisp: use smaller fonts.
+% Originally contributed by Pavel@xerox.
+%
+\maketwodispenvdef{lisp}{example}{%
+ \nonfillstart
+ \tt\setupmarkupstyle{example}%
+ \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special.
+ \gobble % eat return
+}
+% @display/@smalldisplay: same as @lisp except keep current font.
+%
+\makedispenvdef{display}{%
+ \nonfillstart
+ \gobble
+}
+
+% @format/@smallformat: same as @display except don't narrow margins.
+%
+\makedispenvdef{format}{%
+ \let\nonarrowing = t%
+ \nonfillstart
+ \gobble
+}
+
+% @flushleft: same as @format, but doesn't obey \SETdispenvsize.
+\envdef\flushleft{%
+ \let\nonarrowing = t%
+ \nonfillstart
+ \gobble
+}
+\let\Eflushleft = \afterenvbreak
+
+% @flushright.
+%
+\envdef\flushright{%
+ \let\nonarrowing = t%
+ \nonfillstart
+ \advance\leftskip by 0pt plus 1fill\relax
+ \gobble
+}
+\let\Eflushright = \afterenvbreak
+
+
+% @raggedright does more-or-less normal line breaking but no right
+% justification. From plain.tex.
+\envdef\raggedright{%
+ \rightskip0pt plus2.4em \spaceskip.3333em \xspaceskip.5em\relax
+}
+\let\Eraggedright\par
+
+\envdef\raggedleft{%
+ \parindent=0pt \leftskip0pt plus2em
+ \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt
+ \hbadness=10000 % Last line will usually be underfull, so turn off
+ % badness reporting.
+}
+\let\Eraggedleft\par
+
+\envdef\raggedcenter{%
+ \parindent=0pt \rightskip0pt plus1em \leftskip0pt plus1em
+ \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt
+ \hbadness=10000 % Last line will usually be underfull, so turn off
+ % badness reporting.
+}
+\let\Eraggedcenter\par
+
+
+% @quotation does normal linebreaking (hence we can't use \nonfillstart)
+% and narrows the margins. We keep \parskip nonzero in general, since
+% we're doing normal filling. So, when using \aboveenvbreak and
+% \afterenvbreak, temporarily make \parskip 0.
+%
+\makedispenvdef{quotation}{\quotationstart}
+%
+\def\quotationstart{%
+ \indentedblockstart % same as \indentedblock, but increase right margin too.
+ \ifx\nonarrowing\relax
+ \advance\rightskip by \lispnarrowing
+ \fi
+ \parsearg\quotationlabel
+}
+
+% We have retained a nonzero parskip for the environment, since we're
+% doing normal filling.
+%
+\def\Equotation{%
+ \par
+ \ifx\quotationauthor\thisisundefined\else
+ % indent a bit.
+ \leftline{\kern 2\leftskip \sl ---\quotationauthor}%
+ \fi
+ {\parskip=0pt \afterenvbreak}%
+}
+\def\Esmallquotation{\Equotation}
+
+% If we're given an argument, typeset it in bold with a colon after.
+\def\quotationlabel#1{%
+ \def\temp{#1}%
+ \ifx\temp\empty \else
+ {\bf #1: }%
+ \fi
+}
+
+% @indentedblock is like @quotation, but indents only on the left and
+% has no optional argument.
+%
+\makedispenvdef{indentedblock}{\indentedblockstart}
+%
+\def\indentedblockstart{%
+ {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip
+ \parindent=0pt
+ %
+ % @cartouche defines \nonarrowing to inhibit narrowing at next level down.
+ \ifx\nonarrowing\relax
+ \advance\leftskip by \lispnarrowing
+ \exdentamount = \lispnarrowing
+ \else
+ \let\nonarrowing = \relax
+ \fi
+}
+
+% Keep a nonzero parskip for the environment, since we're doing normal filling.
+%
+\def\Eindentedblock{%
+ \par
+ {\parskip=0pt \afterenvbreak}%
+}
+\def\Esmallindentedblock{\Eindentedblock}
+
+
+% LaTeX-like @verbatim...@end verbatim and @verb{<char>...<char>}
+% If we want to allow any <char> as delimiter,
+% we need the curly braces so that makeinfo sees the @verb command, eg:
+% `@verbx...x' would look like the '@verbx' command. --janneke@gnu.org
+%
+% [Knuth]: Donald Ervin Knuth, 1996. The TeXbook.
+%
+% [Knuth] p.344; only we need to do the other characters Texinfo sets
+% active too. Otherwise, they get lost as the first character on a
+% verbatim line.
+\def\dospecials{%
+ \do\ \do\\\do\{\do\}\do\$\do\&%
+ \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~%
+ \do\<\do\>\do\|\do\@\do+\do\"%
+ % Don't do the quotes -- if we do, @set txicodequoteundirected and
+ % @set txicodequotebacktick will not have effect on @verb and
+ % @verbatim, and ?` and !` ligatures won't get disabled.
+ %\do\`\do\'%
+}
+%
+% [Knuth] p. 380
+\def\uncatcodespecials{%
+ \def\do##1{\catcode`##1=\other}\dospecials}
+%
+% Setup for the @verb command.
+%
+% Eight spaces for a tab
+\begingroup
+ \catcode`\^^I=\active
+ \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }}
+\endgroup
+%
+\def\setupverb{%
+ \tt % easiest (and conventionally used) font for verbatim
+ \def\par{\leavevmode\endgraf}%
+ \setupmarkupstyle{verb}%
+ \tabeightspaces
+ % Respect line breaks,
+ % print special symbols as themselves, and
+ % make each space count
+ % must do in this order:
+ \obeylines \uncatcodespecials \sepspaces
+}
+
+% Setup for the @verbatim environment
+%
+% Real tab expansion.
+\newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount
+%
+% We typeset each line of the verbatim in an \hbox, so we can handle
+% tabs. The \global is in case the verbatim line starts with an accent,
+% or some other command that starts with a begin-group. Otherwise, the
+% entire \verbbox would disappear at the corresponding end-group, before
+% it is typeset. Meanwhile, we can't have nested verbatim commands
+% (can we?), so the \global won't be overwriting itself.
+\newbox\verbbox
+\def\starttabbox{\global\setbox\verbbox=\hbox\bgroup}
+%
+\begingroup
+ \catcode`\^^I=\active
+ \gdef\tabexpand{%
+ \catcode`\^^I=\active
+ \def^^I{\leavevmode\egroup
+ \dimen\verbbox=\wd\verbbox % the width so far, or since the previous tab
+ \divide\dimen\verbbox by\tabw
+ \multiply\dimen\verbbox by\tabw % compute previous multiple of \tabw
+ \advance\dimen\verbbox by\tabw % advance to next multiple of \tabw
+ \wd\verbbox=\dimen\verbbox \box\verbbox \starttabbox
+ }%
+ }
+\endgroup
+
+% start the verbatim environment.
+\def\setupverbatim{%
+ \let\nonarrowing = t%
+ \nonfillstart
+ \tt % easiest (and conventionally used) font for verbatim
+ % The \leavevmode here is for blank lines. Otherwise, we would
+ % never \starttabbox and the \egroup would end verbatim mode.
+ \def\par{\leavevmode\egroup\box\verbbox\endgraf}%
+ \tabexpand
+ \setupmarkupstyle{verbatim}%
+ % Respect line breaks,
+ % print special symbols as themselves, and
+ % make each space count.
+ % Must do in this order:
+ \obeylines \uncatcodespecials \sepspaces
+ \everypar{\starttabbox}%
+}
+
+% Do the @verb magic: verbatim text is quoted by unique
+% delimiter characters. Before first delimiter expect a
+% right brace, after last delimiter expect closing brace:
+%
+% \def\doverb'{'<char>#1<char>'}'{#1}
+%
+% [Knuth] p. 382; only eat outer {}
+\begingroup
+ \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other
+ \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next]
+\endgroup
+%
+\def\verb{\begingroup\setupverb\doverb}
+%
+%
+% Do the @verbatim magic: define the macro \doverbatim so that
+% the (first) argument ends when '@end verbatim' is reached, ie:
+%
+% \def\doverbatim#1@end verbatim{#1}
+%
+% For Texinfo it's a lot easier than for LaTeX,
+% because texinfo's \verbatim doesn't stop at '\end{verbatim}':
+% we need not redefine '\', '{' and '}'.
+%
+% Inspired by LaTeX's verbatim command set [latex.ltx]
+%
+\begingroup
+ \catcode`\ =\active
+ \obeylines %
+ % ignore everything up to the first ^^M, that's the newline at the end
+ % of the @verbatim input line itself. Otherwise we get an extra blank
+ % line in the output.
+ \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}%
+ % We really want {...\end verbatim} in the body of the macro, but
+ % without the active space; thus we have to use \xdef and \gobble.
+\endgroup
+%
+\envdef\verbatim{%
+ \setupverbatim\doverbatim
+}
+\let\Everbatim = \afterenvbreak
+
+
+% @verbatiminclude FILE - insert text of file in verbatim environment.
+%
+\def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude}
+%
+\def\doverbatiminclude#1{%
+ {%
+ \makevalueexpandable
+ \setupverbatim
+ {%
+ \indexnofonts % Allow `@@' and other weird things in file names.
+ \wlog{texinfo.tex: doing @verbatiminclude of #1^^J}%
+ \edef\tmp{\noexpand\input #1 }
+ \expandafter
+ }\tmp
+ \afterenvbreak
+ }%
+}
+
+% @copying ... @end copying.
+% Save the text away for @insertcopying later.
+%
+% We save the uninterpreted tokens, rather than creating a box.
+% Saving the text in a box would be much easier, but then all the
+% typesetting commands (@smallbook, font changes, etc.) have to be done
+% beforehand -- and a) we want @copying to be done first in the source
+% file; b) letting users define the frontmatter in as flexible order as
+% possible is desirable.
+%
+\def\copying{\checkenv{}\begingroup\scanargctxt\docopying}
+\def\docopying#1@end copying{\endgroup\def\copyingtext{#1}}
+%
+\def\insertcopying{%
+ \begingroup
+ \parindent = 0pt % paragraph indentation looks wrong on title page
+ \scanexp\copyingtext
+ \endgroup
+}
+
+
+\message{defuns,}
+% @defun etc.
+
+\newskip\defbodyindent \defbodyindent=.4in
+\newskip\defargsindent \defargsindent=50pt
+\newskip\deflastargmargin \deflastargmargin=18pt
+\newcount\defunpenalty
+
+% Start the processing of @deffn:
+\def\startdefun{%
+ \ifnum\lastpenalty<10000
+ \medbreak
+ \defunpenalty=10003 % Will keep this @deffn together with the
+ % following @def command, see below.
+ \else
+ % If there are two @def commands in a row, we'll have a \nobreak,
+ % which is there to keep the function description together with its
+ % header. But if there's nothing but headers, we need to allow a
+ % break somewhere. Check specifically for penalty 10002, inserted
+ % by \printdefunline, instead of 10000, since the sectioning
+ % commands also insert a nobreak penalty, and we don't want to allow
+ % a break between a section heading and a defun.
+ %
+ % As a further refinement, we avoid "club" headers by signalling
+ % with penalty of 10003 after the very first @deffn in the
+ % sequence (see above), and penalty of 10002 after any following
+ % @def command.
+ \ifnum\lastpenalty=10002 \penalty2000 \else \defunpenalty=10002 \fi
+ %
+ % Similarly, after a section heading, do not allow a break.
+ % But do insert the glue.
+ \medskip % preceded by discardable penalty, so not a breakpoint
+ \fi
+ %
+ \parindent=0in
+ \advance\leftskip by \defbodyindent
+ \exdentamount=\defbodyindent
+}
+
+\def\dodefunx#1{%
+ % First, check whether we are in the right environment:
+ \checkenv#1%
+ %
+ % As above, allow line break if we have multiple x headers in a row.
+ % It's not a great place, though.
+ \ifnum\lastpenalty=10002 \penalty3000 \else \defunpenalty=10002 \fi
+ %
+ % And now, it's time to reuse the body of the original defun:
+ \expandafter\gobbledefun#1%
+}
+\def\gobbledefun#1\startdefun{}
+
+% \printdefunline \deffnheader{text}
+%
+\def\printdefunline#1#2{%
+ \begingroup
+ % call \deffnheader:
+ #1#2 \endheader
+ % common ending:
+ \interlinepenalty = 10000
+ \advance\rightskip by 0pt plus 1fil\relax
+ \endgraf
+ \nobreak\vskip -\parskip
+ \penalty\defunpenalty % signal to \startdefun and \dodefunx
+ % Some of the @defun-type tags do not enable magic parentheses,
+ % rendering the following check redundant. But we don't optimize.
+ \checkparencounts
+ \endgroup
+}
+
+\def\Edefun{\endgraf\medbreak}
+
+% \makedefun{deffn} creates \deffn, \deffnx and \Edeffn;
+% the only thing remaining is to define \deffnheader.
+%
+\def\makedefun#1{%
+ \expandafter\let\csname E#1\endcsname = \Edefun
+ \edef\temp{\noexpand\domakedefun
+ \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}%
+ \temp
+}
+
+% \domakedefun \deffn \deffnx \deffnheader { (defn. of \deffnheader) }
+%
+% Define \deffn and \deffnx, without parameters.
+% \deffnheader has to be defined explicitly.
+%
+\def\domakedefun#1#2#3{%
+ \envdef#1{%
+ \startdefun
+ \doingtypefnfalse % distinguish typed functions from all else
+ \parseargusing\activeparens{\printdefunline#3}%
+ }%
+ \def#2{\dodefunx#1}%
+ \def#3%
+}
+
+\newif\ifdoingtypefn % doing typed function?
+\newif\ifrettypeownline % typeset return type on its own line?
+
+% @deftypefnnewline on|off says whether the return type of typed functions
+% are printed on their own line. This affects @deftypefn, @deftypefun,
+% @deftypeop, and @deftypemethod.
+%
+\parseargdef\deftypefnnewline{%
+ \def\temp{#1}%
+ \ifx\temp\onword
+ \expandafter\let\csname SETtxideftypefnnl\endcsname
+ = \empty
+ \else\ifx\temp\offword
+ \expandafter\let\csname SETtxideftypefnnl\endcsname
+ = \relax
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @txideftypefnnl value `\temp',
+ must be on|off}%
+ \fi\fi
+}
+
+% \dosubind {index}{topic}{subtopic}
+%
+% If SUBTOPIC is present, precede it with a space, and call \doind.
+% (At some time during the 20th century, this made a two-level entry in an
+% index such as the operation index. Nobody seemed to notice the change in
+% behaviour though.)
+\def\dosubind#1#2#3{%
+ \def\thirdarg{#3}%
+ \ifx\thirdarg\empty
+ \doind{#1}{#2}%
+ \else
+ \doind{#1}{#2\space#3}%
+ \fi
+}
+
+% Untyped functions:
+
+% @deffn category name args
+\makedefun{deffn}{\deffngeneral{}}
+
+% @deffn category class name args
+\makedefun{defop}#1 {\defopon{#1\ \putwordon}}
+
+% \defopon {category on}class name args
+\def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} }
+
+% \deffngeneral {subind}category name args
+%
+\def\deffngeneral#1#2 #3 #4\endheader{%
+ \dosubind{fn}{\code{#3}}{#1}%
+ \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}%
+}
+
+% Typed functions:
+
+% @deftypefn category type name args
+\makedefun{deftypefn}{\deftypefngeneral{}}
+
+% @deftypeop category class type name args
+\makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}}
+
+% \deftypeopon {category on}class type name args
+\def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} }
+
+% \deftypefngeneral {subind}category type name args
+%
+\def\deftypefngeneral#1#2 #3 #4 #5\endheader{%
+ \dosubind{fn}{\code{#4}}{#1}%
+ \doingtypefntrue
+ \defname{#2}{#3}{#4}\defunargs{#5\unskip}%
+}
+
+% Typed variables:
+
+% @deftypevr category type var args
+\makedefun{deftypevr}{\deftypecvgeneral{}}
+
+% @deftypecv category class type var args
+\makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}}
+
+% \deftypecvof {category of}class type var args
+\def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} }
+
+% \deftypecvgeneral {subind}category type var args
+%
+\def\deftypecvgeneral#1#2 #3 #4 #5\endheader{%
+ \dosubind{vr}{\code{#4}}{#1}%
+ \defname{#2}{#3}{#4}\defunargs{#5\unskip}%
+}
+
+% Untyped variables:
+
+% @defvr category var args
+\makedefun{defvr}#1 {\deftypevrheader{#1} {} }
+
+% @defcv category class var args
+\makedefun{defcv}#1 {\defcvof{#1\ \putwordof}}
+
+% \defcvof {category of}class var args
+\def\defcvof#1#2 {\deftypecvof{#1}#2 {} }
+
+% Types:
+
+% @deftp category name args
+\makedefun{deftp}#1 #2 #3\endheader{%
+ \doind{tp}{\code{#2}}%
+ \defname{#1}{}{#2}\defunargs{#3\unskip}%
+}
+
+% Remaining @defun-like shortcuts:
+\makedefun{defun}{\deffnheader{\putwordDeffunc} }
+\makedefun{defmac}{\deffnheader{\putwordDefmac} }
+\makedefun{defspec}{\deffnheader{\putwordDefspec} }
+\makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} }
+\makedefun{defvar}{\defvrheader{\putwordDefvar} }
+\makedefun{defopt}{\defvrheader{\putwordDefopt} }
+\makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} }
+\makedefun{defmethod}{\defopon\putwordMethodon}
+\makedefun{deftypemethod}{\deftypeopon\putwordMethodon}
+\makedefun{defivar}{\defcvof\putwordInstanceVariableof}
+\makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof}
+
+% \defname, which formats the name of the @def (not the args).
+% #1 is the category, such as "Function".
+% #2 is the return type, if any.
+% #3 is the function name.
+%
+% We are followed by (but not passed) the arguments, if any.
+%
+\def\defname#1#2#3{%
+ \par
+ % Get the values of \leftskip and \rightskip as they were outside the @def...
+ \advance\leftskip by -\defbodyindent
+ %
+ % Determine if we are typesetting the return type of a typed function
+ % on a line by itself.
+ \rettypeownlinefalse
+ \ifdoingtypefn % doing a typed function specifically?
+ % then check user option for putting return type on its own line:
+ \expandafter\ifx\csname SETtxideftypefnnl\endcsname\relax \else
+ \rettypeownlinetrue
+ \fi
+ \fi
+ %
+ % How we'll format the category name. Putting it in brackets helps
+ % distinguish it from the body text that may end up on the next line
+ % just below it.
+ \def\temp{#1}%
+ \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi}
+ %
+ % Figure out line sizes for the paragraph shape. We'll always have at
+ % least two.
+ \tempnum = 2
+ %
+ % The first line needs space for \box0; but if \rightskip is nonzero,
+ % we need only space for the part of \box0 which exceeds it:
+ \dimen0=\hsize \advance\dimen0 by -\wd0 \advance\dimen0 by \rightskip
+ %
+ % If doing a return type on its own line, we'll have another line.
+ \ifrettypeownline
+ \advance\tempnum by 1
+ \def\maybeshapeline{0in \hsize}%
+ \else
+ \def\maybeshapeline{}%
+ \fi
+ %
+ % The continuations:
+ \dimen2=\hsize \advance\dimen2 by -\defargsindent
+ %
+ % The final paragraph shape:
+ \parshape \tempnum 0in \dimen0 \maybeshapeline \defargsindent \dimen2
+ %
+ % Put the category name at the right margin.
+ \noindent
+ \hbox to 0pt{%
+ \hfil\box0 \kern-\hsize
+ % \hsize has to be shortened this way:
+ \kern\leftskip
+ % Intentionally do not respect \rightskip, since we need the space.
+ }%
+ %
+ % Allow all lines to be underfull without complaint:
+ \tolerance=10000 \hbadness=10000
+ \exdentamount=\defbodyindent
+ {%
+ % defun fonts. We use typewriter by default (used to be bold) because:
+ % . we're printing identifiers, they should be in tt in principle.
+ % . in languages with many accents, such as Czech or French, it's
+ % common to leave accents off identifiers. The result looks ok in
+ % tt, but exceedingly strange in rm.
+ % . we don't want -- and --- to be treated as ligatures.
+ % . this still does not fix the ?` and !` ligatures, but so far no
+ % one has made identifiers using them :).
+ \df \tt
+ \def\temp{#2}% text of the return type
+ \ifx\temp\empty\else
+ \tclose{\temp}% typeset the return type
+ \ifrettypeownline
+ % put return type on its own line; prohibit line break following:
+ \hfil\vadjust{\nobreak}\break
+ \else
+ \space % type on same line, so just followed by a space
+ \fi
+ \fi % no return type
+ #3% output function name
+ }%
+ {\rm\enskip}% hskip 0.5 em of \rmfont
+ %
+ \boldbrax
+ % arguments will be output next, if any.
+}
+
+% Print arguments in slanted roman (not ttsl), inconsistently with using
+% tt for the name. This is because literal text is sometimes needed in
+% the argument list (groff manual), and ttsl and tt are not very
+% distinguishable. Prevent hyphenation at `-' chars.
+%
+\def\defunargs#1{%
+ % use sl by default (not ttsl),
+ % tt for the names.
+ \df \sl \hyphenchar\font=0
+ %
+ % On the other hand, if an argument has two dashes (for instance), we
+ % want a way to get ttsl. We used to recommend @var for that, so
+ % leave the code in, but it's strange for @var to lead to typewriter.
+ % Nowadays we recommend @code, since the difference between a ttsl hyphen
+ % and a tt hyphen is pretty tiny. @code also disables ?` !`.
+ \def\var##1{{\setupmarkupstyle{var}\ttslanted{##1}}}%
+ #1%
+ \sl\hyphenchar\font=45
+}
+
+% We want ()&[] to print specially on the defun line.
+%
+\def\activeparens{%
+ \catcode`\(=\active \catcode`\)=\active
+ \catcode`\[=\active \catcode`\]=\active
+ \catcode`\&=\active
+}
+
+% Make control sequences which act like normal parenthesis chars.
+\let\lparen = ( \let\rparen = )
+
+% Be sure that we always have a definition for `(', etc. For example,
+% if the fn name has parens in it, \boldbrax will not be in effect yet,
+% so TeX would otherwise complain about undefined control sequence.
+{
+ \activeparens
+ \global\let(=\lparen \global\let)=\rparen
+ \global\let[=\lbrack \global\let]=\rbrack
+ \global\let& = \&
+
+ \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb}
+ \gdef\magicamp{\let&=\amprm}
+}
+\let\ampchar\&
+
+\newcount\parencount
+
+% If we encounter &foo, then turn on ()-hacking afterwards
+\newif\ifampseen
+\def\amprm#1 {\ampseentrue{\bf\ }}
+
+\def\parenfont{%
+ \ifampseen
+ % At the first level, print parens in roman,
+ % otherwise use the default font.
+ \ifnum \parencount=1 \rm \fi
+ \else
+ % The \sf parens (in \boldbrax) actually are a little bolder than
+ % the contained text. This is especially needed for [ and ] .
+ \sf
+ \fi
+}
+\def\infirstlevel#1{%
+ \ifampseen
+ \ifnum\parencount=1
+ #1%
+ \fi
+ \fi
+}
+\def\bfafterword#1 {#1 \bf}
+
+\def\opnr{%
+ \global\advance\parencount by 1
+ {\parenfont(}%
+ \infirstlevel \bfafterword
+}
+\def\clnr{%
+ {\parenfont)}%
+ \infirstlevel \sl
+ \global\advance\parencount by -1
+}
+
+\newcount\brackcount
+\def\lbrb{%
+ \global\advance\brackcount by 1
+ {\bf[}%
+}
+\def\rbrb{%
+ {\bf]}%
+ \global\advance\brackcount by -1
+}
+
+\def\checkparencounts{%
+ \ifnum\parencount=0 \else \badparencount \fi
+ \ifnum\brackcount=0 \else \badbrackcount \fi
+}
+% these should not use \errmessage; the glibc manual, at least, actually
+% has such constructs (when documenting function pointers).
+\def\badparencount{%
+ \message{Warning: unbalanced parentheses in @def...}%
+ \global\parencount=0
+}
+\def\badbrackcount{%
+ \message{Warning: unbalanced square brackets in @def...}%
+ \global\brackcount=0
+}
+
+
+\message{macros,}
+% @macro.
+
+% To do this right we need a feature of e-TeX, \scantokens,
+% which we arrange to emulate with a temporary file in ordinary TeX.
+\ifx\eTeXversion\thisisundefined
+ \newwrite\macscribble
+ \def\scantokens#1{%
+ \toks0={#1}%
+ \immediate\openout\macscribble=\jobname.tmp
+ \immediate\write\macscribble{\the\toks0}%
+ \immediate\closeout\macscribble
+ \input \jobname.tmp
+ }
+\fi
+
+% Used at the time of macro expansion.
+% Argument is macro body with arguments substituted
+\def\scanmacro#1{%
+ \newlinechar`\^^M
+ \def\xeatspaces{\eatspaces}%
+ %
+ % Process the macro body under the current catcode regime.
+ \scantokens{#1@comment}%
+ %
+ % The \comment is to remove the \newlinechar added by \scantokens, and
+ % can be noticed by \parsearg. Note \c isn't used because this means cedilla
+ % in math mode.
+}
+
+% Used for copying and captions
+\def\scanexp#1{%
+ \expandafter\scanmacro\expandafter{#1}%
+}
+
+\newcount\paramno % Count of parameters
+\newtoks\macname % Macro name
+\newif\ifrecursive % Is it recursive?
+
+% List of all defined macros in the form
+% \commondummyword\macro1\commondummyword\macro2...
+% Currently is also contains all @aliases; the list can be split
+% if there is a need.
+\def\macrolist{}
+
+% Add the macro to \macrolist
+\def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname}
+\def\addtomacrolistxxx#1{%
+ \toks0 = \expandafter{\macrolist\commondummyword#1}%
+ \xdef\macrolist{\the\toks0}%
+}
+
+% Utility routines.
+% This does \let #1 = #2, with \csnames; that is,
+% \let \csname#1\endcsname = \csname#2\endcsname
+% (except of course we have to play expansion games).
+%
+\def\cslet#1#2{%
+ \expandafter\let
+ \csname#1\expandafter\endcsname
+ \csname#2\endcsname
+}
+
+% Trim leading and trailing spaces off a string.
+% Concepts from aro-bend problem 15 (see CTAN).
+{\catcode`\@=11
+\gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }}
+\gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@}
+\gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @}
+\def\unbrace#1{#1}
+\unbrace{\gdef\trim@@@ #1 } #2@{#1}
+}
+
+% Trim a single trailing ^^M off a string.
+{\catcode`\^^M=\other \catcode`\Q=3%
+\gdef\eatcr #1{\eatcra #1Q^^MQ}%
+\gdef\eatcra#1^^MQ{\eatcrb#1Q}%
+\gdef\eatcrb#1Q#2Q{#1}%
+}
+
+% Macro bodies are absorbed as an argument in a context where
+% all characters are catcode 10, 11 or 12, except \ which is active
+% (as in normal texinfo). It is necessary to change the definition of \
+% to recognize macro arguments; this is the job of \mbodybackslash.
+%
+% Non-ASCII encodings make 8-bit characters active, so un-activate
+% them to avoid their expansion. Must do this non-globally, to
+% confine the change to the current group.
+%
+% It's necessary to have hard CRs when the macro is executed. This is
+% done by making ^^M (\endlinechar) catcode 12 when reading the macro
+% body, and then making it the \newlinechar in \scanmacro.
+%
+\def\scanctxt{% used as subroutine
+ \catcode`\"=\other
+ \catcode`\+=\other
+ \catcode`\<=\other
+ \catcode`\>=\other
+ \catcode`\^=\other
+ \catcode`\_=\other
+ \catcode`\|=\other
+ \catcode`\~=\other
+ \passthroughcharstrue
+}
+
+\def\scanargctxt{% used for copying and captions, not macros.
+ \scanctxt
+ \catcode`\@=\other
+ \catcode`\\=\other
+ \catcode`\^^M=\other
+}
+
+\def\macrobodyctxt{% used for @macro definitions
+ \scanctxt
+ \catcode`\ =\other
+ \catcode`\@=\other
+ \catcode`\{=\other
+ \catcode`\}=\other
+ \catcode`\^^M=\other
+ \usembodybackslash
+}
+
+% Used when scanning braced macro arguments. Note, however, that catcode
+% changes here are ineffectual if the macro invocation was nested inside
+% an argument to another Texinfo command.
+\def\macroargctxt{%
+ \scanctxt
+ \catcode`\ =\active
+ \catcode`\@=\other
+ \catcode`\^^M=\other
+ \catcode`\\=\active
+}
+
+\def\macrolineargctxt{% used for whole-line arguments without braces
+ \scanctxt
+ \catcode`\@=\other
+ \catcode`\{=\other
+ \catcode`\}=\other
+}
+
+% \mbodybackslash is the definition of \ in @macro bodies.
+% It maps \foo\ => \csname macarg.foo\endcsname => #N
+% where N is the macro parameter number.
+% We define \csname macarg.\endcsname to be \realbackslash, so
+% \\ in macro replacement text gets you a backslash.
+%
+{\catcode`@=0 @catcode`@\=@active
+ @gdef@usembodybackslash{@let\=@mbodybackslash}
+ @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname}
+}
+\expandafter\def\csname macarg.\endcsname{\realbackslash}
+
+\def\margbackslash#1{\char`\#1 }
+
+\def\macro{\recursivefalse\parsearg\macroxxx}
+\def\rmacro{\recursivetrue\parsearg\macroxxx}
+
+\def\macroxxx#1{%
+ \getargs{#1}% now \macname is the macname and \argl the arglist
+ \ifx\argl\empty % no arguments
+ \paramno=0\relax
+ \else
+ \expandafter\parsemargdef \argl;%
+ \if\paramno>256\relax
+ \ifx\eTeXversion\thisisundefined
+ \errhelp = \EMsimple
+ \errmessage{You need eTeX to compile a file with macros with more
than 256 arguments}
+ \fi
+ \fi
+ \fi
+ \if1\csname ismacro.\the\macname\endcsname
+ \message{Warning: redefining \the\macname}%
+ \else
+ \expandafter\ifx\csname \the\macname\endcsname \relax
+ \else \errmessage{Macro name \the\macname\space already defined}\fi
+ \global\cslet{macsave.\the\macname}{\the\macname}%
+ \global\expandafter\let\csname ismacro.\the\macname\endcsname=1%
+ \addtomacrolist{\the\macname}%
+ \fi
+ \begingroup \macrobodyctxt
+ \ifrecursive \expandafter\parsermacbody
+ \else \expandafter\parsemacbody
+ \fi}
+
+\parseargdef\unmacro{%
+ \if1\csname ismacro.#1\endcsname
+ \global\cslet{#1}{macsave.#1}%
+ \global\expandafter\let \csname ismacro.#1\endcsname=0%
+ % Remove the macro name from \macrolist:
+ \begingroup
+ \expandafter\let\csname#1\endcsname \relax
+ \let\commondummyword\unmacrodo
+ \xdef\macrolist{\macrolist}%
+ \endgroup
+ \else
+ \errmessage{Macro #1 not defined}%
+ \fi
+}
+
+% Called by \do from \dounmacro on each macro. The idea is to omit any
+% macro definitions that have been changed to \relax.
+%
+\def\unmacrodo#1{%
+ \ifx #1\relax
+ % remove this
+ \else
+ \noexpand\commondummyword \noexpand#1%
+ \fi
+}
+
+% \getargs -- Parse the arguments to a @macro line. Set \macname to
+% the name of the macro, and \argl to the braced argument list.
+\def\getargs#1{\getargsxxx#1{}}
+\def\getargsxxx#1#{\getmacname #1 \relax\getmacargs}
+\def\getmacname#1 #2\relax{\macname={#1}}
+\def\getmacargs#1{\def\argl{#1}}
+% This made use of the feature that if the last token of a
+% <parameter list> is #, then the preceding argument is delimited by
+% an opening brace, and that opening brace is not consumed.
+
+% Parse the optional {params} list to @macro or @rmacro.
+% Set \paramno to the number of arguments,
+% and \paramlist to a parameter text for the macro (e.g. #1,#2,#3 for a
+% three-param macro.) Define \macarg.BLAH for each BLAH in the params
+% list to some hook where the argument is to be expanded. If there are
+% less than 10 arguments that hook is to be replaced by ##N where N
+% is the position in that list, that is to say the macro arguments are to be
+% defined `a la TeX in the macro body.
+%
+% That gets used by \mbodybackslash (above).
+%
+% If there are 10 or more arguments, a different technique is used: see
+% \parsemmanyargdef.
+%
+\def\parsemargdef#1;{%
+ \paramno=0\def\paramlist{}%
+ \let\hash\relax
+ % \hash is redefined to `#' later to get it into definitions
+ \let\xeatspaces\relax
+ \parsemargdefxxx#1,;,%
+ \ifnum\paramno<10\relax\else
+ \paramno0\relax
+ \parsemmanyargdef@@#1,;,% 10 or more arguments
+ \fi
+}
+\def\parsemargdefxxx#1,{%
+ \if#1;\let\next=\relax
+ \else \let\next=\parsemargdefxxx
+ \advance\paramno by 1
+ \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname
+ {\xeatspaces{\hash\the\paramno}}%
+ \edef\paramlist{\paramlist\hash\the\paramno,}%
+ \fi\next}
+
+% \parsemacbody, \parsermacbody
+%
+% Read recursive and nonrecursive macro bodies. (They're different since
+% rec and nonrec macros end differently.)
+%
+% We are in \macrobodyctxt, and the \xdef causes backslashshes in the macro
+% body to be transformed.
+% Set \macrobody to the body of the macro, and call \defmacro.
+%
+{\catcode`\ =\other\long\gdef\parsemacbody#1@end macro{%
+\xdef\macrobody{\eatcr{#1}}\endgroup\defmacro}}%
+{\catcode`\ =\other\long\gdef\parsermacbody#1@end rmacro{%
+\xdef\macrobody{\eatcr{#1}}\endgroup\defmacro}}%
+
+% Make @ a letter, so that we can make private-to-Texinfo macro names.
+\edef\texiatcatcode{\the\catcode`\@}
+\catcode `@=11\relax
+
+%%%%%%%%%%%%%% Code for > 10 arguments only %%%%%%%%%%%%%%%%%%
+
+% If there are 10 or more arguments, a different technique is used, where the
+% hook remains in the body, and when macro is to be expanded the body is
+% processed again to replace the arguments.
+%
+% In that case, the hook is \the\toks N-1, and we simply set \toks N-1 to the
+% argument N value and then \edef the body (nothing else will expand because of
+% the catcode regime under which the body was input).
+%
+% If you compile with TeX (not eTeX), and you have macros with 10 or more
+% arguments, no macro can have more than 256 arguments (else error).
+%
+% In case that there are 10 or more arguments we parse again the arguments
+% list to set new definitions for the \macarg.BLAH macros corresponding to
+% each BLAH argument. It was anyhow needed to parse already once this list
+% in order to count the arguments, and as macros with at most 9 arguments
+% are by far more frequent than macro with 10 or more arguments, defining
+% twice the \macarg.BLAH macros does not cost too much processing power.
+\def\parsemmanyargdef@@#1,{%
+ \if#1;\let\next=\relax
+ \else
+ \let\next=\parsemmanyargdef@@
+ \edef\tempb{\eatspaces{#1}}%
+ \expandafter\def\expandafter\tempa
+ \expandafter{\csname macarg.\tempb\endcsname}%
+ % Note that we need some extra \noexpand\noexpand, this is because we
+ % don't want \the to be expanded in the \parsermacbody as it uses an
+ % \xdef .
+ \expandafter\edef\tempa
+ {\noexpand\noexpand\noexpand\the\toks\the\paramno}%
+ \advance\paramno by 1\relax
+ \fi\next}
+
+
+\let\endargs@\relax
+\let\nil@\relax
+\def\nilm@{\nil@}%
+\long\def\nillm@{\nil@}%
+
+% This macro is expanded during the Texinfo macro expansion, not during its
+% definition. It gets all the arguments' values and assigns them to macros
+% macarg.ARGNAME
+%
+% #1 is the macro name
+% #2 is the list of argument names
+% #3 is the list of argument values
+\def\getargvals@#1#2#3{%
+ \def\macargdeflist@{}%
+ \def\saveparamlist@{#2}% Need to keep a copy for parameter expansion.
+ \def\paramlist{#2,\nil@}%
+ \def\macroname{#1}%
+ \begingroup
+ \macroargctxt
+ \def\argvaluelist{#3,\nil@}%
+ \def\@tempa{#3}%
+ \ifx\@tempa\empty
+ \setemptyargvalues@
+ \else
+ \getargvals@@
+ \fi
+}
+\def\getargvals@@{%
+ \ifx\paramlist\nilm@
+ % Some sanity check needed here that \argvaluelist is also empty.
+ \ifx\argvaluelist\nillm@
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Too many arguments in macro `\macroname'!}%
+ \fi
+ \let\next\macargexpandinbody@
+ \else
+ \ifx\argvaluelist\nillm@
+ % No more arguments values passed to macro. Set remaining named-arg
+ % macros to empty.
+ \let\next\setemptyargvalues@
+ \else
+ % pop current arg name into \@tempb
+ \def\@tempa##1{\pop@{\@tempb}{\paramlist}##1\endargs@}%
+ \expandafter\@tempa\expandafter{\paramlist}%
+ % pop current argument value into \@tempc
+ \def\@tempa##1{\longpop@{\@tempc}{\argvaluelist}##1\endargs@}%
+ \expandafter\@tempa\expandafter{\argvaluelist}%
+ % Here \@tempb is the current arg name and \@tempc is the current arg
value.
+ % First place the new argument macro definition into \@tempd
+ \expandafter\macname\expandafter{\@tempc}%
+ \expandafter\let\csname macarg.\@tempb\endcsname\relax
+ \expandafter\def\expandafter\@tempe\expandafter{%
+ \csname macarg.\@tempb\endcsname}%
+ \edef\@tempd{\long\def\@tempe{\the\macname}}%
+ \push@\@tempd\macargdeflist@
+ \let\next\getargvals@@
+ \fi
+ \fi
+ \next
+}
+
+\def\push@#1#2{%
+ \expandafter\expandafter\expandafter\def
+ \expandafter\expandafter\expandafter#2%
+ \expandafter\expandafter\expandafter{%
+ \expandafter#1#2}%
+}
+
+% Replace arguments by their values in the macro body, and place the result
+% in macro \@tempa.
+%
+\def\macvalstoargs@{%
+ % To do this we use the property that token registers that are \the'ed
+ % within an \edef expand only once. So we are going to place all argument
+ % values into respective token registers.
+ %
+ % First we save the token context, and initialize argument numbering.
+ \begingroup
+ \paramno0\relax
+ % Then, for each argument number #N, we place the corresponding argument
+ % value into a new token list register \toks#N
+ \expandafter\putargsintokens@\saveparamlist@,;,%
+ % Then, we expand the body so that argument are replaced by their
+ % values. The trick for values not to be expanded themselves is that they
+ % are within tokens and that tokens expand only once in an \edef .
+ \edef\@tempc{\csname mac.\macroname .body\endcsname}%
+ % Now we restore the token stack pointer to free the token list registers
+ % which we have used, but we make sure that expanded body is saved after
+ % group.
+ \expandafter
+ \endgroup
+ \expandafter\def\expandafter\@tempa\expandafter{\@tempc}%
+ }
+
+% Define the named-macro outside of this group and then close this group.
+%
+\def\macargexpandinbody@{%
+ \expandafter
+ \endgroup
+ \macargdeflist@
+ % First the replace in body the macro arguments by their values, the result
+ % is in \@tempa .
+ \macvalstoargs@
+ % Then we point at the \norecurse or \gobble (for recursive) macro value
+ % with \@tempb .
+ \expandafter\let\expandafter\@tempb\csname mac.\macroname .recurse\endcsname
+ % Depending on whether it is recursive or not, we need some tailing
+ % \egroup .
+ \ifx\@tempb\gobble
+ \let\@tempc\relax
+ \else
+ \let\@tempc\egroup
+ \fi
+ % And now we do the real job:
+
\edef\@tempd{\noexpand\@tempb{\macroname}\noexpand\scanmacro{\@tempa}\@tempc}%
+ \@tempd
+}
+
+\def\putargsintokens@#1,{%
+ \if#1;\let\next\relax
+ \else
+ \let\next\putargsintokens@
+ % First we allocate the new token list register, and give it a temporary
+ % alias \@tempb .
+ \toksdef\@tempb\the\paramno
+ % Then we place the argument value into that token list register.
+ \expandafter\let\expandafter\@tempa\csname macarg.#1\endcsname
+ \expandafter\@tempb\expandafter{\@tempa}%
+ \advance\paramno by 1\relax
+ \fi
+ \next
+}
+
+% Trailing missing arguments are set to empty.
+%
+\def\setemptyargvalues@{%
+ \ifx\paramlist\nilm@
+ \let\next\macargexpandinbody@
+ \else
+ \expandafter\setemptyargvaluesparser@\paramlist\endargs@
+ \let\next\setemptyargvalues@
+ \fi
+ \next
+}
+
+\def\setemptyargvaluesparser@#1,#2\endargs@{%
+ \expandafter\def\expandafter\@tempa\expandafter{%
+ \expandafter\def\csname macarg.#1\endcsname{}}%
+ \push@\@tempa\macargdeflist@
+ \def\paramlist{#2}%
+}
+
+% #1 is the element target macro
+% #2 is the list macro
+% #3,#4\endargs@ is the list value
+\def\pop@#1#2#3,#4\endargs@{%
+ \def#1{#3}%
+ \def#2{#4}%
+}
+\long\def\longpop@#1#2#3,#4\endargs@{%
+ \long\def#1{#3}%
+ \long\def#2{#4}%
+}
+
+
+%%%%%%%%%%%%%% End of code for > 10 arguments %%%%%%%%%%%%%%%%%%
+
+
+% This defines a Texinfo @macro or @rmacro, called by \parsemacbody.
+% \macrobody has the body of the macro in it, with placeholders for
+% its parameters, looking like "\xeatspaces{\hash 1}".
+% \paramno is the number of parameters
+% \paramlist is a TeX parameter text, e.g. "#1,#2,#3,"
+% There are four cases: macros of zero, one, up to nine, and many arguments.
+% \xdef is used so that macro definitions will survive the file
+% they're defined in: @include reads the file inside a group.
+%
+\def\defmacro{%
+ \let\hash=##% convert placeholders to macro parameter chars
+ \ifnum\paramno=1
+ \def\xeatspaces##1{##1}%
+ % This removes the pair of braces around the argument. We don't
+ % use \eatspaces, because this can cause ends of lines to be lost
+ % when the argument to \eatspaces is read, leading to line-based
+ % commands like "@itemize" not being read correctly.
+ \else
+ \let\xeatspaces\relax % suppress expansion
+ \fi
+ \ifcase\paramno
+ % 0
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \bgroup
+ \noexpand\spaceisspace
+ \noexpand\endlineisspace
+ \noexpand\expandafter % skip any whitespace after the macro name.
+ \expandafter\noexpand\csname\the\macname @@@\endcsname}%
+ \expandafter\xdef\csname\the\macname @@@\endcsname{%
+ \egroup
+ \noexpand\scanmacro{\macrobody}}%
+ \or % 1
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \bgroup
+ \noexpand\braceorline
+ \expandafter\noexpand\csname\the\macname @@@\endcsname}%
+ \expandafter\xdef\csname\the\macname @@@\endcsname##1{%
+ \egroup
+ \noexpand\scanmacro{\macrobody}%
+ }%
+ \else % at most 9
+ \ifnum\paramno<10\relax
+ % @MACNAME sets the context for reading the macro argument
+ % @MACNAME@@ gets the argument, processes backslashes and appends a
+ % comma.
+ % @MACNAME@@@ removes braces surrounding the argument list.
+ % @MACNAME@@@@ scans the macro body with arguments substituted.
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \bgroup
+ \noexpand\expandafter % This \expandafter skip any spaces after the
+ \noexpand\macroargctxt % macro before we change the catcode of space.
+ \noexpand\expandafter
+ \expandafter\noexpand\csname\the\macname @@\endcsname}%
+ \expandafter\xdef\csname\the\macname @@\endcsname##1{%
+ \noexpand\passargtomacro
+ \expandafter\noexpand\csname\the\macname @@@\endcsname{##1,}}%
+ \expandafter\xdef\csname\the\macname @@@\endcsname##1{%
+ \expandafter\noexpand\csname\the\macname @@@@\endcsname ##1}%
+ \expandafter\expandafter
+ \expandafter\xdef
+ \expandafter\expandafter
+ \csname\the\macname @@@@\endcsname\paramlist{%
+ \egroup\noexpand\scanmacro{\macrobody}}%
+ \else % 10 or more:
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \noexpand\getargvals@{\the\macname}{\argl}%
+ }%
+ \global\expandafter\let\csname mac.\the\macname .body\endcsname\macrobody
+ \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\gobble
+ \fi
+ \fi}
+
+\catcode `\@\texiatcatcode\relax % end private-to-Texinfo catcodes
+
+\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+{\catcode`\@=0 \catcode`\\=13 % We need to manipulate \ so use @ as escape
+@catcode`@_=11 % private names
+@catcode`@!=11 % used as argument separator
+
+% \passargtomacro#1#2 -
+% Call #1 with a list of tokens #2, with any doubled backslashes in #2
+% compressed to one.
+%
+% This implementation works by expansion, and not execution (so we cannot use
+% \def or similar). This reduces the risk of this failing in contexts where
+% complete expansion is done with no execution (for example, in writing out to
+% an auxiliary file for an index entry).
+%
+% State is kept in the input stream: the argument passed to
+% @look_ahead, @gobble_and_check_finish and @add_segment is
+%
+% THE_MACRO ARG_RESULT ! {PENDING_BS} NEXT_TOKEN (... rest of input)
+%
+% where:
+% THE_MACRO - name of the macro we want to call
+% ARG_RESULT - argument list we build to pass to that macro
+% PENDING_BS - either a backslash or nothing
+% NEXT_TOKEN - used to look ahead in the input stream to see what's coming next
+
+@gdef@passargtomacro#1#2{%
+ @add_segment #1!{}@relax#2\@_finish\%
+}
+@gdef@_finish{@_finishx} @global@let@_finishx@relax
+
+% #1 - THE_MACRO ARG_RESULT
+% #2 - PENDING_BS
+% #3 - NEXT_TOKEN
+% #4 used to look ahead
+%
+% If the next token is not a backslash, process the rest of the argument;
+% otherwise, remove the next token.
+@gdef@look_ahead#1!#2#3#4{%
+ @ifx#4\%
+ @expandafter@gobble_and_check_finish
+ @else
+ @expandafter@add_segment
+ @fi#1!{#2}#4#4%
+}
+
+% #1 - THE_MACRO ARG_RESULT
+% #2 - PENDING_BS
+% #3 - NEXT_TOKEN
+% #4 should be a backslash, which is gobbled.
+% #5 looks ahead
+%
+% Double backslash found. Add a single backslash, and look ahead.
+@gdef@gobble_and_check_finish#1!#2#3#4#5{%
+ @add_segment#1\!{}#5#5%
+}
+
+@gdef@is_fi{@fi}
+
+% #1 - THE_MACRO ARG_RESULT
+% #2 - PENDING_BS
+% #3 - NEXT_TOKEN
+% #4 is input stream until next backslash
+%
+% Input stream is either at the start of the argument, or just after a
+% backslash sequence, either a lone backslash, or a doubled backslash.
+% NEXT_TOKEN contains the first token in the input stream: if it is \finish,
+% finish; otherwise, append to ARG_RESULT the segment of the argument up until
+% the next backslash. PENDING_BACKSLASH contains a backslash to represent
+% a backslash just before the start of the input stream that has not been
+% added to ARG_RESULT.
+@gdef@add_segment#1!#2#3#4\{%
+@ifx#3@_finish
+ @call_the_macro#1!%
+@else
+ % append the pending backslash to the result, followed by the next segment
+ @expandafter@is_fi@look_ahead#1#2#4!{\}@fi
+ % this @fi is discarded by @look_ahead.
+ % we can't get rid of it with \expandafter because we don't know how
+ % long #4 is.
+}
+
+% #1 - THE_MACRO
+% #2 - ARG_RESULT
+% #3 discards the res of the conditional in @add_segment, and @is_fi ends the
+% conditional.
+@gdef@call_the_macro#1#2!#3@fi{@is_fi #1{#2}}
+
+}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% \braceorline MAC is used for a one-argument macro MAC. It checks
+% whether the next non-whitespace character is a {. It sets the context
+% for reading the argument (slightly different in the two cases). Then,
+% to read the argument, in the whole-line case, it then calls the regular
+% \parsearg MAC; in the lbrace case, it calls \passargtomacro MAC.
+%
+\def\braceorline#1{\let\macnamexxx=#1\futurelet\nchar\braceorlinexxx}
+\def\braceorlinexxx{%
+ \ifx\nchar\bgroup
+ \macroargctxt
+ \expandafter\passargtomacro
+ \else
+ \macrolineargctxt\expandafter\parsearg
+ \fi \macnamexxx}
+
+
+% @alias.
+% We need some trickery to remove the optional spaces around the equal
+% sign. Make them active and then expand them all to nothing.
+%
+\def\alias{\parseargusing\obeyspaces\aliasxxx}
+\def\aliasxxx #1{\aliasyyy#1\relax}
+\def\aliasyyy #1=#2\relax{%
+ {%
+ \expandafter\let\obeyedspace=\empty
+ \addtomacrolist{#1}%
+ \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}%
+ }%
+ \next
+}
+
+
+\message{cross references,}
+
+\newwrite\auxfile
+\newif\ifhavexrefs % True if xref values are known.
+\newif\ifwarnedxrefs % True if we warned once that they aren't known.
+
+% @inforef is relatively simple.
+\def\inforef #1{\inforefzzz #1,,,,**}
+\def\inforefzzz #1,#2,#3,#4**{%
+ \putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}},
+ node \samp{\ignorespaces#1{}}}
+
+% @node's only job in TeX is to define \lastnode, which is used in
+% cross-references. The @node line might or might not have commas, and
+% might or might not have spaces before the first comma, like:
+% @node foo , bar , ...
+% We don't want such trailing spaces in the node name.
+%
+\parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse}
+%
+% also remove a trailing comma, in case of something like this:
+% @node Help-Cross, , , Cross-refs
+\def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse}
+\def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}\omittopnode}
+
+% Used so that the @top node doesn't have to be wrapped in an @ifnottex
+% conditional.
+% \doignore goes to more effort to skip nested conditionals but we don't need
+% that here.
+\def\omittopnode{%
+ \ifx\lastnode\wordTop
+ \expandafter\ignorenode\fi
+}
+\def\wordTop{Top}
+
+% Until the next @node or @bye command, divert output to a box that is not
+% output.
+\def\ignorenode{\setbox\dummybox\vbox\bgroup\def\node{\egroup\node}%
+\ignorenodebye
+}
+
+{\let\bye\relax
+\gdef\ignorenodebye{\let\bye\ignorenodebyedef}
+\gdef\ignorenodebyedef{\egroup(`Top' node ignored)\bye}}
+% The redefinition of \bye here is because it is declared \outer
+
+\let\lastnode=\empty
+
+% Write a cross-reference definition for the current node. #1 is the
+% type (Ynumbered, Yappendix, Ynothing).
+%
+\def\donoderef#1{%
+ \ifx\lastnode\empty\else
+ \setref{\lastnode}{#1}%
+ \global\let\lastnode=\empty
+ \fi
+}
+
+% @anchor{NAME} -- define xref target at arbitrary point.
+%
+\newcount\savesfregister
+%
+\def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi}
+\def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi}
+\def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces}
+
+% \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an
+% anchor), which consists of three parts:
+% 1) NAME-title - the current sectioning name taken from \currentsection,
+% or the anchor name.
+% 2) NAME-snt - section number and type, passed as the SNT arg, or
+% empty for anchors.
+% 3) NAME-pg - the page number.
+%
+% This is called from \donoderef, \anchor, and \dofloat. In the case of
+% floats, there is an additional part, which is not written here:
+% 4) NAME-lof - the text as it should appear in a @listoffloats.
+%
+\def\setref#1#2{%
+ \pdfmkdest{#1}%
+ \iflinks
+ {%
+ \requireauxfile
+ \atdummies % preserve commands, but don't expand them
+ % match definition in \xrdef, \refx, \xrefX.
+ \def\value##1{##1}%
+ \edef\writexrdef##1##2{%
+ \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef
+ ##1}{##2}}% these are parameters of \writexrdef
+ }%
+ \toks0 = \expandafter{\currentsection}%
+ \immediate \writexrdef{title}{\the\toks0 }%
+ \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc.
+ \safewhatsit{\writexrdef{pg}{\folio}}% will be written later, at \shipout
+ }%
+ \fi
+}
+
+% @xrefautosectiontitle on|off says whether @section(ing) names are used
+% automatically in xrefs, if the third arg is not explicitly specified.
+% This was provided as a "secret" @set xref-automatic-section-title
+% variable, now it's official.
+%
+\parseargdef\xrefautomaticsectiontitle{%
+ \def\temp{#1}%
+ \ifx\temp\onword
+ \expandafter\let\csname SETxref-automatic-section-title\endcsname
+ = \empty
+ \else\ifx\temp\offword
+ \expandafter\let\csname SETxref-automatic-section-title\endcsname
+ = \relax
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @xrefautomaticsectiontitle value `\temp',
+ must be on|off}%
+ \fi\fi
+}
+
+%
+% @xref, @pxref, and @ref generate cross-references. For \xrefX, #1 is
+% the node name, #2 the name of the Info cross-reference, #3 the printed
+% node name, #4 the name of the Info file, #5 the name of the printed
+% manual. All but the node name can be omitted.
+%
+\def\pxref{\putwordsee{} \xrefXX}
+\def\xref{\putwordSee{} \xrefXX}
+\def\ref{\xrefXX}
+
+\def\xrefXX#1{\def\xrefXXarg{#1}\futurelet\tokenafterxref\xrefXXX}
+\def\xrefXXX{\expandafter\xrefX\expandafter[\xrefXXarg,,,,,,,]}
+%
+\newbox\toprefbox
+\newbox\printedrefnamebox
+\newbox\infofilenamebox
+\newbox\printedmanualbox
+%
+\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup
+ \unsepspaces
+ %
+ % Get args without leading/trailing spaces.
+ \def\printedrefname{\ignorespaces #3}%
+ \setbox\printedrefnamebox = \hbox{\printedrefname\unskip}%
+ %
+ \def\infofilename{\ignorespaces #4}%
+ \setbox\infofilenamebox = \hbox{\infofilename\unskip}%
+ %
+ \def\printedmanual{\ignorespaces #5}%
+ \setbox\printedmanualbox = \hbox{\printedmanual\unskip}%
+ %
+ % If the printed reference name (arg #3) was not explicitly given in
+ % the @xref, figure out what we want to use.
+ \ifdim \wd\printedrefnamebox = 0pt
+ % No printed node name was explicitly given.
+ \expandafter\ifx\csname SETxref-automatic-section-title\endcsname \relax
+ % Not auto section-title: use node name inside the square brackets.
+ \def\printedrefname{\ignorespaces #1}%
+ \else
+ % Auto section-title: use chapter/section title inside
+ % the square brackets if we have it.
+ \ifdim \wd\printedmanualbox > 0pt
+ % It is in another manual, so we don't have it; use node name.
+ \def\printedrefname{\ignorespaces #1}%
+ \else
+ \ifhavexrefs
+ % We (should) know the real title if we have the xref values.
+ \def\printedrefname{\refx{#1-title}{}}%
+ \else
+ % Otherwise just copy the Info node name.
+ \def\printedrefname{\ignorespaces #1}%
+ \fi%
+ \fi
+ \fi
+ \fi
+ %
+ % Make link in pdf output.
+ \ifpdf
+ % For pdfTeX and LuaTeX
+ {\indexnofonts
+ \makevalueexpandable
+ \turnoffactive
+ % This expands tokens, so do it after making catcode changes, so _
+ % etc. don't get their TeX definitions. This ignores all spaces in
+ % #4, including (wrongly) those in the middle of the filename.
+ \getfilename{#4}%
+ %
+ % This (wrongly) does not take account of leading or trailing
+ % spaces in #1, which should be ignored.
+ \setpdfdestname{#1}%
+ %
+ \ifx\pdfdestname\empty
+ \def\pdfdestname{Top}% no empty targets
+ \fi
+ %
+ \leavevmode
+ \startlink attr{/Border [0 0 0]}%
+ \ifnum\filenamelength>0
+ goto file{\the\filename.pdf} name{\pdfdestname}%
+ \else
+ goto name{\pdfmkpgn{\pdfdestname}}%
+ \fi
+ }%
+ \setcolor{\linkcolor}%
+ \else
+ \ifx\XeTeXrevision\thisisundefined
+ \else
+ % For XeTeX
+ {\indexnofonts
+ \makevalueexpandable
+ \turnoffactive
+ % This expands tokens, so do it after making catcode changes, so _
+ % etc. don't get their TeX definitions. This ignores all spaces in
+ % #4, including (wrongly) those in the middle of the filename.
+ \getfilename{#4}%
+ %
+ % This (wrongly) does not take account of leading or trailing
+ % spaces in #1, which should be ignored.
+ \setpdfdestname{#1}%
+ %
+ \ifx\pdfdestname\empty
+ \def\pdfdestname{Top}% no empty targets
+ \fi
+ %
+ \leavevmode
+ \ifnum\filenamelength>0
+ % With default settings,
+ % XeTeX (xdvipdfmx) replaces link destination names with integers.
+ % In this case, the replaced destination names of
+ % remote PDFs are no longer known. In order to avoid a replacement,
+ % you can use xdvipdfmx's command line option `-C 0x0010'.
+ % If you use XeTeX 0.99996+ (TeX Live 2016+),
+ % this command line option is no longer necessary
+ % because we can use the `dvipdfmx:config' special.
+ \special{pdf:bann << /Border [0 0 0] /Type /Annot /Subtype /Link /A
+ << /S /GoToR /F (\the\filename.pdf) /D (\pdfdestname) >> >>}%
+ \else
+ \special{pdf:bann << /Border [0 0 0] /Type /Annot /Subtype /Link /A
+ << /S /GoTo /D (\pdfdestname) >> >>}%
+ \fi
+ }%
+ \setcolor{\linkcolor}%
+ \fi
+ \fi
+ {%
+ % Have to otherify everything special to allow the \csname to
+ % include an _ in the xref name, etc.
+ \indexnofonts
+ \turnoffactive
+ \def\value##1{##1}%
+ \expandafter\global\expandafter\let\expandafter\Xthisreftitle
+ \csname XR#1-title\endcsname
+ }%
+ %
+ % Float references are printed completely differently: "Figure 1.2"
+ % instead of "[somenode], p.3". \iffloat distinguishes them by
+ % \Xthisreftitle being set to a magic string.
+ \iffloat\Xthisreftitle
+ % If the user specified the print name (third arg) to the ref,
+ % print it instead of our usual "Figure 1.2".
+ \ifdim\wd\printedrefnamebox = 0pt
+ \refx{#1-snt}{}%
+ \else
+ \printedrefname
+ \fi
+ %
+ % If the user also gave the printed manual name (fifth arg), append
+ % "in MANUALNAME".
+ \ifdim \wd\printedmanualbox > 0pt
+ \space \putwordin{} \cite{\printedmanual}%
+ \fi
+ \else
+ % node/anchor (non-float) references.
+ %
+ % If we use \unhbox to print the node names, TeX does not insert
+ % empty discretionaries after hyphens, which means that it will not
+ % find a line break at a hyphen in a node names. Since some manuals
+ % are best written with fairly long node names, containing hyphens,
+ % this is a loss. Therefore, we give the text of the node name
+ % again, so it is as if TeX is seeing it for the first time.
+ %
+ \ifdim \wd\printedmanualbox > 0pt
+ % Cross-manual reference with a printed manual name.
+ %
+ \crossmanualxref{\cite{\printedmanual\unskip}}%
+ %
+ \else\ifdim \wd\infofilenamebox > 0pt
+ % Cross-manual reference with only an info filename (arg 4), no
+ % printed manual name (arg 5). This is essentially the same as
+ % the case above; we output the filename, since we have nothing else.
+ %
+ \crossmanualxref{\code{\infofilename\unskip}}%
+ %
+ \else
+ % Reference within this manual.
+ %
+ % Only output a following space if the -snt ref is nonempty; for
+ % @unnumbered and @anchor, it won't be.
+ \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}%
+ \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi
+ %
+ % output the `[mynode]' via the macro below so it can be overridden.
+ \xrefprintnodename\printedrefname
+ %
+ % But we always want a comma and a space:
+ ,\space
+ %
+ % output the `page 3'.
+ \turnoffactive \putwordpage\tie\refx{#1-pg}{}%
+ % Add a , if xref followed by a space
+ \if\space\noexpand\tokenafterxref ,%
+ \else\ifx\ \tokenafterxref ,% @TAB
+ \else\ifx\*\tokenafterxref ,% @*
+ \else\ifx\ \tokenafterxref ,% @SPACE
+ \else\ifx\
+ \tokenafterxref ,% @NL
+ \else\ifx\tie\tokenafterxref ,% @tie
+ \fi\fi\fi\fi\fi\fi
+ \fi\fi
+ \fi
+ \endlink
+\endgroup}
+
+% Output a cross-manual xref to #1. Used just above (twice).
+%
+% Only include the text "Section ``foo'' in" if the foo is neither
+% missing or Top. Thus, @xref{,,,foo,The Foo Manual} outputs simply
+% "see The Foo Manual", the idea being to refer to the whole manual.
+%
+% But, this being TeX, we can't easily compare our node name against the
+% string "Top" while ignoring the possible spaces before and after in
+% the input. By adding the arbitrary 7sp below, we make it much less
+% likely that a real node name would have the same width as "Top" (e.g.,
+% in a monospaced font). Hopefully it will never happen in practice.
+%
+% For the same basic reason, we retypeset the "Top" at every
+% reference, since the current font is indeterminate.
+%
+\def\crossmanualxref#1{%
+ \setbox\toprefbox = \hbox{Top\kern7sp}%
+ \setbox2 = \hbox{\ignorespaces \printedrefname \unskip \kern7sp}%
+ \ifdim \wd2 > 7sp % nonempty?
+ \ifdim \wd2 = \wd\toprefbox \else % same as Top?
+ \putwordSection{} ``\printedrefname'' \putwordin{}\space
+ \fi
+ \fi
+ #1%
+}
+
+% This macro is called from \xrefX for the `[nodename]' part of xref
+% output. It's a separate macro only so it can be changed more easily,
+% since square brackets don't work well in some documents. Particularly
+% one that Bob is working on :).
+%
+\def\xrefprintnodename#1{[#1]}
+
+% Things referred to by \setref.
+%
+\def\Ynothing{}
+\def\Yomitfromtoc{}
+\def\Ynumbered{%
+ \ifnum\secno=0
+ \putwordChapter@tie \the\chapno
+ \else \ifnum\subsecno=0
+ \putwordSection@tie \the\chapno.\the\secno
+ \else \ifnum\subsubsecno=0
+ \putwordSection@tie \the\chapno.\the\secno.\the\subsecno
+ \else
+ \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno
+ \fi\fi\fi
+}
+\def\Yappendix{%
+ \ifnum\secno=0
+ \putwordAppendix@tie @char\the\appendixno{}%
+ \else \ifnum\subsecno=0
+ \putwordSection@tie @char\the\appendixno.\the\secno
+ \else \ifnum\subsubsecno=0
+ \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno
+ \else
+ \putwordSection@tie
+ @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno
+ \fi\fi\fi
+}
+
+% \refx{NAME}{SUFFIX} - reference a cross-reference string named NAME. SUFFIX
+% is output afterwards if non-empty.
+\def\refx#1#2{%
+ \requireauxfile
+ {%
+ \indexnofonts
+ \turnoffactive
+ \def\value##1{##1}%
+ \expandafter\global\expandafter\let\expandafter\thisrefX
+ \csname XR#1\endcsname
+ }%
+ \ifx\thisrefX\relax
+ % If not defined, say something at least.
+ \angleleft un\-de\-fined\angleright
+ \iflinks
+ \ifhavexrefs
+ {\toks0 = {#1}% avoid expansion of possibly-complex value
+ \message{\linenumber Undefined cross reference `\the\toks0'.}}%
+ \else
+ \ifwarnedxrefs\else
+ \global\warnedxrefstrue
+ \message{Cross reference values unknown; you must run TeX again.}%
+ \fi
+ \fi
+ \fi
+ \else
+ % It's defined, so just use it.
+ \thisrefX
+ \fi
+ #2% Output the suffix in any case.
+}
+
+% This is the macro invoked by entries in the aux file. Define a control
+% sequence for a cross-reference target (we prepend XR to the control sequence
+% name to avoid collisions). The value is the page number. If this is a float
+% type, we have more work to do.
+%
+\def\xrdef#1#2{%
+ {% Expand the node or anchor name to remove control sequences.
+ % \turnoffactive stops 8-bit characters being changed to commands
+ % like @'e. \refx does the same to retrieve the value in the definition.
+ \indexnofonts
+ \turnoffactive
+ \def\value##1{##1}%
+ \xdef\safexrefname{#1}%
+ }%
+ %
+ \bgroup
+ \expandafter\gdef\csname XR\safexrefname\endcsname{#2}%
+ \egroup
+ % We put the \gdef inside a group to avoid the definitions building up on
+ % TeX's save stack, which can cause it to run out of space for aux files with
+ % thousands of lines. \gdef doesn't use the save stack, but \csname does
+ % when it defines an unknown control sequence as \relax.
+ %
+ % Was that xref control sequence that we just defined for a float?
+ \expandafter\iffloat\csname XR\safexrefname\endcsname
+ % it was a float, and we have the (safe) float type in \iffloattype.
+ \expandafter\let\expandafter\floatlist
+ \csname floatlist\iffloattype\endcsname
+ %
+ % Is this the first time we've seen this float type?
+ \expandafter\ifx\floatlist\relax
+ \toks0 = {\do}% yes, so just \do
+ \else
+ % had it before, so preserve previous elements in list.
+ \toks0 = \expandafter{\floatlist\do}%
+ \fi
+ %
+ % Remember this xref in the control sequence \floatlistFLOATTYPE,
+ % for later use in \listoffloats.
+ \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0
+ {\safexrefname}}%
+ \fi
+}
+
+% If working on a large document in chapters, it is convenient to
+% be able to disable indexing, cross-referencing, and contents, for test runs.
+% This is done with @novalidate at the beginning of the file.
+%
+\newif\iflinks \linkstrue % by default we want the aux files.
+\let\novalidate = \linksfalse
+
+% Used when writing to the aux file, or when using data from it.
+\def\requireauxfile{%
+ \iflinks
+ \tryauxfile
+ % Open the new aux file. TeX will close it automatically at exit.
+ \immediate\openout\auxfile=\jobname.aux
+ \fi
+ \global\let\requireauxfile=\relax % Only do this once.
+}
+
+% Read the last existing aux file, if any. No error if none exists.
+%
+\def\tryauxfile{%
+ \openin 1 \jobname.aux
+ \ifeof 1 \else
+ \readdatafile{aux}%
+ \global\havexrefstrue
+ \fi
+ \closein 1
+}
+
+\def\setupdatafile{%
+ \catcode`\^^@=\other
+ \catcode`\^^A=\other
+ \catcode`\^^B=\other
+ \catcode`\^^C=\other
+ \catcode`\^^D=\other
+ \catcode`\^^E=\other
+ \catcode`\^^F=\other
+ \catcode`\^^G=\other
+ \catcode`\^^H=\other
+ \catcode`\^^K=\other
+ \catcode`\^^L=\other
+ \catcode`\^^N=\other
+ \catcode`\^^P=\other
+ \catcode`\^^Q=\other
+ \catcode`\^^R=\other
+ \catcode`\^^S=\other
+ \catcode`\^^T=\other
+ \catcode`\^^U=\other
+ \catcode`\^^V=\other
+ \catcode`\^^W=\other
+ \catcode`\^^X=\other
+ \catcode`\^^Z=\other
+ \catcode`\^^[=\other
+ \catcode`\^^\=\other
+ \catcode`\^^]=\other
+ \catcode`\^^^=\other
+ \catcode`\^^_=\other
+ \catcode`\^=\other
+ %
+ % Special characters. Should be turned off anyway, but...
+ \catcode`\~=\other
+ \catcode`\[=\other
+ \catcode`\]=\other
+ \catcode`\"=\other
+ \catcode`\_=\other
+ \catcode`\|=\other
+ \catcode`\<=\other
+ \catcode`\>=\other
+ \catcode`\$=\other
+ \catcode`\#=\other
+ \catcode`\&=\other
+ \catcode`\%=\other
+ \catcode`+=\other % avoid \+ for paranoia even though we've turned it off
+ %
+ \catcode`\\=\active
+ %
+ % @ is our escape character in .aux files, and we need braces.
+ \catcode`\{=1
+ \catcode`\}=2
+ \catcode`\@=0
+}
+
+\def\readdatafile#1{%
+\begingroup
+ \setupdatafile
+ \input\jobname.#1
+\endgroup}
+
+
+\message{insertions,}
+% including footnotes.
+
+\newcount \footnoteno
+
+% The trailing space in the following definition for supereject is
+% vital for proper filling; pages come out unaligned when you do a
+% pagealignmacro call if that space before the closing brace is
+% removed. (Generally, numeric constants should always be followed by a
+% space to prevent strange expansion errors.)
+\def\supereject{\par\penalty -20000\footnoteno =0 }
+
+% @footnotestyle is meaningful for Info output only.
+\let\footnotestyle=\comment
+
+{\catcode `\@=11
+%
+% Auto-number footnotes. Otherwise like plain.
+\gdef\footnote{%
+ \global\advance\footnoteno by \@ne
+ \edef\thisfootno{$^{\the\footnoteno}$}%
+ %
+ % In case the footnote comes at the end of a sentence, preserve the
+ % extra spacing after we do the footnote number.
+ \let\@sf\empty
+ \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi
+ %
+ % Remove inadvertent blank space before typesetting the footnote number.
+ \unskip
+ \thisfootno\@sf
+ \dofootnote
+}%
+
+% Don't bother with the trickery in plain.tex to not require the
+% footnote text as a parameter. Our footnotes don't need to be so general.
+%
+% Oh yes, they do; otherwise, @ifset (and anything else that uses
+% \parseargline) fails inside footnotes because the tokens are fixed when
+% the footnote is read. --karl, 16nov96.
+%
+\gdef\dofootnote{%
+ \insert\footins\bgroup
+ %
+ % Nested footnotes are not supported in TeX, that would take a lot
+ % more work. (\startsavinginserts does not suffice.)
+ \let\footnote=\errfootnotenest
+ %
+ % We want to typeset this text as a normal paragraph, even if the
+ % footnote reference occurs in (for example) a display environment.
+ % So reset some parameters.
+ \hsize=\txipagewidth
+ \interlinepenalty\interfootnotelinepenalty
+ \splittopskip\ht\strutbox % top baseline for broken footnotes
+ \splitmaxdepth\dp\strutbox
+ \floatingpenalty\@MM
+ \leftskip\z@skip
+ \rightskip\z@skip
+ \spaceskip\z@skip
+ \xspaceskip\z@skip
+ \parindent\defaultparindent
+ %
+ \smallfonts \rm
+ %
+ % Because we use hanging indentation in footnotes, a @noindent appears
+ % to exdent this text, so make it be a no-op. makeinfo does not use
+ % hanging indentation so @noindent can still be needed within footnote
+ % text after an @example or the like (not that this is good style).
+ \let\noindent = \relax
+ %
+ % Hang the footnote text off the number. Use \everypar in case the
+ % footnote extends for more than one paragraph.
+ \everypar = {\hang}%
+ \textindent{\thisfootno}%
+ %
+ % Don't crash into the line above the footnote text. Since this
+ % expands into a box, it must come within the paragraph, lest it
+ % provide a place where TeX can split the footnote.
+ \footstrut
+ %
+ % Invoke rest of plain TeX footnote routine.
+ \futurelet\next\fo@t
+}
+}%end \catcode `\@=11
+
+\def\errfootnotenest{%
+ \errhelp=\EMsimple
+ \errmessage{Nested footnotes not supported in texinfo.tex,
+ even though they work in makeinfo; sorry}
+}
+
+\def\errfootnoteheading{%
+ \errhelp=\EMsimple
+ \errmessage{Footnotes in chapters, sections, etc., are not supported}
+}
+
+% In case a @footnote appears in a vbox, save the footnote text and create
+% the real \insert just after the vbox finished. Otherwise, the insertion
+% would be lost.
+% Similarly, if a @footnote appears inside an alignment, save the footnote
+% text to a box and make the \insert when a row of the table is finished.
+% And the same can be done for other insert classes. --kasal, 16nov03.
+%
+% Replace the \insert primitive by a cheating macro.
+% Deeper inside, just make sure that the saved insertions are not spilled
+% out prematurely.
+%
+\def\startsavinginserts{%
+ \ifx \insert\ptexinsert
+ \let\insert\saveinsert
+ \else
+ \let\checkinserts\relax
+ \fi
+}
+
+% This \insert replacement works for both \insert\footins{foo} and
+% \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}.
+%
+\def\saveinsert#1{%
+ \edef\next{\noexpand\savetobox \makeSAVEname#1}%
+ \afterassignment\next
+ % swallow the left brace
+ \let\temp =
+}
+\def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}}
+\def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1}
+
+\def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi}
+
+\def\placesaveins#1{%
+ \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname
+ {\box#1}%
+}
+
+% eat @SAVE -- beware, all of them have catcode \other:
+{
+ \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials % ;-)
+ \gdef\gobblesave @SAVE{}
+}
+
+% initialization:
+\def\newsaveins #1{%
+ \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}%
+ \next
+}
+\def\newsaveinsX #1{%
+ \csname newbox\endcsname #1%
+ \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts
+ \checksaveins #1}%
+}
+
+% initialize:
+\let\checkinserts\empty
+\newsaveins\footins
+\newsaveins\margin
+
+
+% @image. We use the macros from epsf.tex to support this.
+% If epsf.tex is not installed and @image is used, we complain.
+%
+% Check for and read epsf.tex up front. If we read it only at @image
+% time, we might be inside a group, and then its definitions would get
+% undone and the next image would fail.
+\openin 1 = epsf.tex
+\ifeof 1 \else
+ % Do not bother showing banner with epsf.tex v2.7k (available in
+ % doc/epsf.tex and on ctan).
+ \def\epsfannounce{\toks0 = }%
+ \input epsf.tex
+\fi
+\closein 1
+%
+% We will only complain once about lack of epsf.tex.
+\newif\ifwarnednoepsf
+\newhelp\noepsfhelp{epsf.tex must be installed for images to
+ work. It is also included in the Texinfo distribution, or you can get
+ it from https://ctan.org/texarchive/macros/texinfo/texinfo/doc/epsf.tex.}
+%
+\def\image#1{%
+ \ifx\epsfbox\thisisundefined
+ \ifwarnednoepsf \else
+ \errhelp = \noepsfhelp
+ \errmessage{epsf.tex not found, images will be ignored}%
+ \global\warnednoepsftrue
+ \fi
+ \else
+ \imagexxx #1,,,,,\finish
+ \fi
+}
+%
+% Arguments to @image:
+% #1 is (mandatory) image filename; we tack on .eps extension.
+% #2 is (optional) width, #3 is (optional) height.
+% #4 is (ignored optional) html alt text.
+% #5 is (ignored optional) extension.
+% #6 is just the usual extra ignored arg for parsing stuff.
+\newif\ifimagevmode
+\def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup
+ \catcode`\^^M = 5 % in case we're inside an example
+ \normalturnoffactive % allow _ et al. in names
+ \def\xprocessmacroarg{\eatspaces}% in case we are being used via a macro
+ % If the image is by itself, center it.
+ \ifvmode
+ \imagevmodetrue
+ \else \ifx\centersub\centerV
+ % for @center @image, we need a vbox so we can have our vertical space
+ \imagevmodetrue
+ \vbox\bgroup % vbox has better behavior than vtop herev
+ \fi\fi
+ %
+ \ifimagevmode
+ \nobreak\medskip
+ % Usually we'll have text after the image which will insert
+ % \parskip glue, so insert it here too to equalize the space
+ % above and below.
+ \nobreak\vskip\parskip
+ \nobreak
+ \fi
+ %
+ % Leave vertical mode so that indentation from an enclosing
+ % environment such as @quotation is respected.
+ % However, if we're at the top level, we don't want the
+ % normal paragraph indentation.
+ % On the other hand, if we are in the case of @center @image, we don't
+ % want to start a paragraph, which will create a hsize-width box and
+ % eradicate the centering.
+ \ifx\centersub\centerV\else \noindent \fi
+ %
+ % Output the image.
+ \ifpdf
+ % For pdfTeX and LuaTeX <= 0.80
+ \dopdfimage{#1}{#2}{#3}%
+ \else
+ \ifx\XeTeXrevision\thisisundefined
+ % For epsf.tex
+ % \epsfbox itself resets \epsf?size at each figure.
+ \setbox0 = \hbox{\ignorespaces #2}%
+ \ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi
+ \setbox0 = \hbox{\ignorespaces #3}%
+ \ifdim\wd0 > 0pt \epsfysize=#3\relax \fi
+ \epsfbox{#1.eps}%
+ \else
+ % For XeTeX
+ \doxeteximage{#1}{#2}{#3}%
+ \fi
+ \fi
+ %
+ \ifimagevmode
+ \medskip % space after a standalone image
+ \fi
+ \ifx\centersub\centerV \egroup \fi
+\endgroup}
+
+
+% @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables,
+% etc. We don't actually implement floating yet, we always include the
+% float "here". But it seemed the best name for the future.
+%
+\envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish}
+
+% There may be a space before second and/or third parameter; delete it.
+\def\eatcommaspace#1, {#1,}
+
+% #1 is the optional FLOATTYPE, the text label for this float, typically
+% "Figure", "Table", "Example", etc. Can't contain commas. If omitted,
+% this float will not be numbered and cannot be referred to.
+%
+% #2 is the optional xref label. Also must be present for the float to
+% be referable.
+%
+% #3 is the optional positioning argument; for now, it is ignored. It
+% will somehow specify the positions allowed to float to (here, top, bottom).
+%
+% We keep a separate counter for each FLOATTYPE, which we reset at each
+% chapter-level command.
+\let\resetallfloatnos=\empty
+%
+\def\dofloat#1,#2,#3,#4\finish{%
+ \let\thiscaption=\empty
+ \let\thisshortcaption=\empty
+ %
+ % don't lose footnotes inside @float.
+ %
+ % BEWARE: when the floats start float, we have to issue warning whenever an
+ % insert appears inside a float which could possibly float. --kasal, 26may04
+ %
+ \startsavinginserts
+ %
+ % We can't be used inside a paragraph.
+ \par
+ %
+ \vtop\bgroup
+ \def\floattype{#1}%
+ \def\floatlabel{#2}%
+ \def\floatloc{#3}% we do nothing with this yet.
+ %
+ \ifx\floattype\empty
+ \let\safefloattype=\empty
+ \else
+ {%
+ % the floattype might have accents or other special characters,
+ % but we need to use it in a control sequence name.
+ \indexnofonts
+ \turnoffactive
+ \xdef\safefloattype{\floattype}%
+ }%
+ \fi
+ %
+ % If label is given but no type, we handle that as the empty type.
+ \ifx\floatlabel\empty \else
+ % We want each FLOATTYPE to be numbered separately (Figure 1,
+ % Table 1, Figure 2, ...). (And if no label, no number.)
+ %
+ \expandafter\getfloatno\csname\safefloattype floatno\endcsname
+ \global\advance\floatno by 1
+ %
+ {%
+ % This magic value for \currentsection is output by \setref as the
+ % XREFLABEL-title value. \xrefX uses it to distinguish float
+ % labels (which have a completely different output format) from
+ % node and anchor labels. And \xrdef uses it to construct the
+ % lists of floats.
+ %
+ \edef\currentsection{\floatmagic=\safefloattype}%
+ \setref{\floatlabel}{Yfloat}%
+ }%
+ \fi
+ %
+ % start with \parskip glue, I guess.
+ \vskip\parskip
+ %
+ % Don't suppress indentation if a float happens to start a section.
+ \restorefirstparagraphindent
+}
+
+% we have these possibilities:
+% @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap
+% @float Foo,lbl & no caption: Foo 1.1
+% @float Foo & @caption{Cap}: Foo: Cap
+% @float Foo & no caption: Foo
+% @float ,lbl & Caption{Cap}: 1.1: Cap
+% @float ,lbl & no caption: 1.1
+% @float & @caption{Cap}: Cap
+% @float & no caption:
+%
+\def\Efloat{%
+ \let\floatident = \empty
+ %
+ % In all cases, if we have a float type, it comes first.
+ \ifx\floattype\empty \else \def\floatident{\floattype}\fi
+ %
+ % If we have an xref label, the number comes next.
+ \ifx\floatlabel\empty \else
+ \ifx\floattype\empty \else % if also had float type, need tie first.
+ \appendtomacro\floatident{\tie}%
+ \fi
+ % the number.
+ \appendtomacro\floatident{\chaplevelprefix\the\floatno}%
+ \fi
+ %
+ % Start the printed caption with what we've constructed in
+ % \floatident, but keep it separate; we need \floatident again.
+ \let\captionline = \floatident
+ %
+ \ifx\thiscaption\empty \else
+ \ifx\floatident\empty \else
+ \appendtomacro\captionline{: }% had ident, so need a colon between
+ \fi
+ %
+ % caption text.
+ \appendtomacro\captionline{\scanexp\thiscaption}%
+ \fi
+ %
+ % If we have anything to print, print it, with space before.
+ % Eventually this needs to become an \insert.
+ \ifx\captionline\empty \else
+ \vskip.5\parskip
+ \captionline
+ %
+ % Space below caption.
+ \vskip\parskip
+ \fi
+ %
+ % If have an xref label, write the list of floats info. Do this
+ % after the caption, to avoid chance of it being a breakpoint.
+ \ifx\floatlabel\empty \else
+ % Write the text that goes in the lof to the aux file as
+ % \floatlabel-lof. Besides \floatident, we include the short
+ % caption if specified, else the full caption if specified, else nothing.
+ {%
+ \requireauxfile
+ \atdummies
+ %
+ \ifx\thisshortcaption\empty
+ \def\gtemp{\thiscaption}%
+ \else
+ \def\gtemp{\thisshortcaption}%
+ \fi
+ \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident
+ \ifx\gtemp\empty \else : \gtemp \fi}}%
+ }%
+ \fi
+ \egroup % end of \vtop
+ %
+ \checkinserts
+}
+
+% Append the tokens #2 to the definition of macro #1, not expanding either.
+%
+\def\appendtomacro#1#2{%
+ \expandafter\def\expandafter#1\expandafter{#1#2}%
+}
+
+% @caption, @shortcaption
+%
+\def\caption{\docaption\thiscaption}
+\def\shortcaption{\docaption\thisshortcaption}
+\def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption}
+\def\defcaption#1#2{\egroup \def#1{#2}}
+
+% The parameter is the control sequence identifying the counter we are
+% going to use. Create it if it doesn't exist and assign it to \floatno.
+\def\getfloatno#1{%
+ \ifx#1\relax
+ % Haven't seen this figure type before.
+ \csname newcount\endcsname #1%
+ %
+ % Remember to reset this floatno at the next chap.
+ \expandafter\gdef\expandafter\resetallfloatnos
+ \expandafter{\resetallfloatnos #1=0 }%
+ \fi
+ \let\floatno#1%
+}
+
+% \setref calls this to get the XREFLABEL-snt value. We want an @xref
+% to the FLOATLABEL to expand to "Figure 3.1". We call \setref when we
+% first read the @float command.
+%
+\def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}%
+
+% Magic string used for the XREFLABEL-title value, so \xrefX can
+% distinguish floats from other xref types.
+\def\floatmagic{!!float!!}
+
+% #1 is the control sequence we are passed; we expand into a conditional
+% which is true if #1 represents a float ref. That is, the magic
+% \currentsection value which we \setref above.
+%
+\def\iffloat#1{\expandafter\doiffloat#1==\finish}
+%
+% #1 is (maybe) the \floatmagic string. If so, #2 will be the
+% (safe) float type for this float. We set \iffloattype to #2.
+%
+\def\doiffloat#1=#2=#3\finish{%
+ \def\temp{#1}%
+ \def\iffloattype{#2}%
+ \ifx\temp\floatmagic
+}
+
+% @listoffloats FLOATTYPE - print a list of floats like a table of contents.
+%
+\parseargdef\listoffloats{%
+ \def\floattype{#1}% floattype
+ {%
+ % the floattype might have accents or other special characters,
+ % but we need to use it in a control sequence name.
+ \indexnofonts
+ \turnoffactive
+ \xdef\safefloattype{\floattype}%
+ }%
+ %
+ % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE.
+ \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax
+ \ifhavexrefs
+ % if the user said @listoffloats foo but never @float foo.
+ \message{\linenumber No `\safefloattype' floats to list.}%
+ \fi
+ \else
+ \begingroup
+ \leftskip=\tocindent % indent these entries like a toc
+ \let\do=\listoffloatsdo
+ \csname floatlist\safefloattype\endcsname
+ \endgroup
+ \fi
+}
+
+% This is called on each entry in a list of floats. We're passed the
+% xref label, in the form LABEL-title, which is how we save it in the
+% aux file. We strip off the -title and look up \XRLABEL-lof, which
+% has the text we're supposed to typeset here.
+%
+% Figures without xref labels will not be included in the list (since
+% they won't appear in the aux file).
+%
+\def\listoffloatsdo#1{\listoffloatsdoentry#1\finish}
+\def\listoffloatsdoentry#1-title\finish{{%
+ % Can't fully expand XR#1-lof because it can contain anything. Just
+ % pass the control sequence. On the other hand, XR#1-pg is just the
+ % page number, and we want to fully expand that so we can get a link
+ % in pdf output.
+ \toksA = \expandafter{\csname XR#1-lof\endcsname}%
+ %
+ % use the same \entry macro we use to generate the TOC and index.
+ \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}%
+ \writeentry
+}}
+
+
+\message{localization,}
+
+% For single-language documents, @documentlanguage is usually given very
+% early, just after @documentencoding. Single argument is the language
+% (de) or locale (de_DE) abbreviation.
+%
+{
+ \catcode`\_ = \active
+ \globaldefs=1
+\parseargdef\documentlanguage{%
+ \tex % read txi-??.tex file in plain TeX.
+ % Read the file by the name they passed if it exists.
+ \let_ = \normalunderscore % normal _ character for filename test
+ \openin 1 txi-#1.tex
+ \ifeof 1
+ \documentlanguagetrywithoutunderscore #1_\finish
+ \else
+ \globaldefs = 1 % everything in the txi-LL files needs to persist
+ \input txi-#1.tex
+ \fi
+ \closein 1
+ \endgroup % end raw TeX
+}
+%
+% If they passed de_DE, and txi-de_DE.tex doesn't exist,
+% try txi-de.tex.
+%
+\gdef\documentlanguagetrywithoutunderscore#1_#2\finish{%
+ \openin 1 txi-#1.tex
+ \ifeof 1
+ \errhelp = \nolanghelp
+ \errmessage{Cannot read language file txi-#1.tex}%
+ \else
+ \globaldefs = 1 % everything in the txi-LL files needs to persist
+ \input txi-#1.tex
+ \fi
+ \closein 1
+}
+}% end of special _ catcode
+%
+\newhelp\nolanghelp{The given language definition file cannot be found or
+is empty. Maybe you need to install it? Putting it in the current
+directory should work if nowhere else does.}
+
+% This macro is called from txi-??.tex files; the first argument is the
+% \language name to set (without the "\lang@" prefix), the second and
+% third args are \{left,right}hyphenmin.
+%
+% The language names to pass are determined when the format is built.
+% See the etex.log file created at that time, e.g.,
+% /usr/local/texlive/2008/texmf-var/web2c/pdftex/etex.log.
+%
+% With TeX Live 2008, etex now includes hyphenation patterns for all
+% available languages. This means we can support hyphenation in
+% Texinfo, at least to some extent. (This still doesn't solve the
+% accented characters problem.)
+%
+\catcode`@=11
+\def\txisetlanguage#1#2#3{%
+ % do not set the language if the name is undefined in the current TeX.
+ \expandafter\ifx\csname lang@#1\endcsname \relax
+ \message{no patterns for #1}%
+ \else
+ \global\language = \csname lang@#1\endcsname
+ \fi
+ % but there is no harm in adjusting the hyphenmin values regardless.
+ \global\lefthyphenmin = #2\relax
+ \global\righthyphenmin = #3\relax
+}
+
+% XeTeX and LuaTeX can handle Unicode natively.
+% Their default I/O uses UTF-8 sequences instead of a byte-wise operation.
+% Other TeX engines' I/O (pdfTeX, etc.) is byte-wise.
+%
+\newif\iftxinativeunicodecapable
+\newif\iftxiusebytewiseio
+
+\ifx\XeTeXrevision\thisisundefined
+ \ifx\luatexversion\thisisundefined
+ \txinativeunicodecapablefalse
+ \txiusebytewiseiotrue
+ \else
+ \txinativeunicodecapabletrue
+ \txiusebytewiseiofalse
+ \fi
+\else
+ \txinativeunicodecapabletrue
+ \txiusebytewiseiofalse
+\fi
+
+% Set I/O by bytes instead of UTF-8 sequence for XeTeX and LuaTex
+% for non-UTF-8 (byte-wise) encodings.
+%
+\def\setbytewiseio{%
+ \ifx\XeTeXrevision\thisisundefined
+ \else
+ \XeTeXdefaultencoding "bytes" % For subsequent files to be read
+ \XeTeXinputencoding "bytes" % For document root file
+ % Unfortunately, there seems to be no corresponding XeTeX command for
+ % output encoding. This is a problem for auxiliary index and TOC files.
+ % The only solution would be perhaps to write out @U{...} sequences in
+ % place of non-ASCII characters.
+ \fi
+
+ \ifx\luatexversion\thisisundefined
+ \else
+ \directlua{
+ local utf8_char, byte, gsub = unicode.utf8.char, string.byte, string.gsub
+ local function convert_char (char)
+ return utf8_char(byte(char))
+ end
+
+ local function convert_line (line)
+ return gsub(line, ".", convert_char)
+ end
+
+ callback.register("process_input_buffer", convert_line)
+
+ local function convert_line_out (line)
+ local line_out = ""
+ for c in string.utfvalues(line) do
+ line_out = line_out .. string.char(c)
+ end
+ return line_out
+ end
+
+ callback.register("process_output_buffer", convert_line_out)
+ }
+ \fi
+
+ \txiusebytewiseiotrue
+}
+
+
+% Helpers for encodings.
+% Set the catcode of characters 128 through 255 to the specified number.
+%
+\def\setnonasciicharscatcode#1{%
+ \count255=128
+ \loop\ifnum\count255<256
+ \global\catcode\count255=#1\relax
+ \advance\count255 by 1
+ \repeat
+}
+
+\def\setnonasciicharscatcodenonglobal#1{%
+ \count255=128
+ \loop\ifnum\count255<256
+ \catcode\count255=#1\relax
+ \advance\count255 by 1
+ \repeat
+}
+
+% @documentencoding sets the definition of non-ASCII characters
+% according to the specified encoding.
+%
+\def\documentencoding{\parseargusing\filenamecatcodes\documentencodingzzz}
+\def\documentencodingzzz#1{%
+ %
+ % Encoding being declared for the document.
+ \def\declaredencoding{\csname #1.enc\endcsname}%
+ %
+ % Supported encodings: names converted to tokens in order to be able
+ % to compare them with \ifx.
+ \def\ascii{\csname US-ASCII.enc\endcsname}%
+ \def\latnine{\csname ISO-8859-15.enc\endcsname}%
+ \def\latone{\csname ISO-8859-1.enc\endcsname}%
+ \def\lattwo{\csname ISO-8859-2.enc\endcsname}%
+ \def\utfeight{\csname UTF-8.enc\endcsname}%
+ %
+ \ifx \declaredencoding \ascii
+ \asciichardefs
+ %
+ \else \ifx \declaredencoding \lattwo
+ \iftxinativeunicodecapable
+ \setbytewiseio
+ \fi
+ \setnonasciicharscatcode\active
+ \lattwochardefs
+ %
+ \else \ifx \declaredencoding \latone
+ \iftxinativeunicodecapable
+ \setbytewiseio
+ \fi
+ \setnonasciicharscatcode\active
+ \latonechardefs
+ %
+ \else \ifx \declaredencoding \latnine
+ \iftxinativeunicodecapable
+ \setbytewiseio
+ \fi
+ \setnonasciicharscatcode\active
+ \latninechardefs
+ %
+ \else \ifx \declaredencoding \utfeight
+ \iftxinativeunicodecapable
+ % For native Unicode handling (XeTeX and LuaTeX)
+ \nativeunicodechardefs
+ \else
+ % For treating UTF-8 as byte sequences (TeX, eTeX and pdfTeX)
+ \setnonasciicharscatcode\active
+ % since we already invoked \utfeightchardefs at the top level
+ % (below), do not re-invoke it, otherwise our check for duplicated
+ % definitions gets triggered. Making non-ascii chars active is
+ % sufficient.
+ \fi
+ %
+ \else
+ \message{Ignoring unknown document encoding: #1.}%
+ %
+ \fi % utfeight
+ \fi % latnine
+ \fi % latone
+ \fi % lattwo
+ \fi % ascii
+ %
+ \ifx\XeTeXrevision\thisisundefined
+ \else
+ \ifx \declaredencoding \utfeight
+ \else
+ \ifx \declaredencoding \ascii
+ \else
+ \message{Warning: XeTeX with non-UTF-8 encodings cannot handle %
+ non-ASCII characters in auxiliary files.}%
+ \fi
+ \fi
+ \fi
+}
+
+% emacs-page
+% A message to be logged when using a character that isn't available
+% the default font encoding (OT1).
+%
+\def\missingcharmsg#1{\message{Character missing, sorry: #1.}}
+
+% Take account of \c (plain) vs. \, (Texinfo) difference.
+\def\cedilla#1{\ifx\c\ptexc\c{#1}\else\,{#1}\fi}
+
+% First, make active non-ASCII characters in order for them to be
+% correctly categorized when TeX reads the replacement text of
+% macros containing the character definitions.
+\setnonasciicharscatcode\active
+%
+
+\def\gdefchar#1#2{%
+\gdef#1{%
+ \ifpassthroughchars
+ \string#1%
+ \else
+ #2%
+ \fi
+}}
+
+% Latin1 (ISO-8859-1) character definitions.
+\def\latonechardefs{%
+ \gdefchar^^a0{\tie}
+ \gdefchar^^a1{\exclamdown}
+ \gdefchar^^a2{{\tcfont \char162}} % cent
+ \gdefchar^^a3{\pounds{}}
+ \gdefchar^^a4{{\tcfont \char164}} % currency
+ \gdefchar^^a5{{\tcfont \char165}} % yen
+ \gdefchar^^a6{{\tcfont \char166}} % broken bar
+ \gdefchar^^a7{\S}
+ \gdefchar^^a8{\"{}}
+ \gdefchar^^a9{\copyright{}}
+ \gdefchar^^aa{\ordf}
+ \gdefchar^^ab{\guillemetleft{}}
+ \gdefchar^^ac{\ensuremath\lnot}
+ \gdefchar^^ad{\-}
+ \gdefchar^^ae{\registeredsymbol{}}
+ \gdefchar^^af{\={}}
+ %
+ \gdefchar^^b0{\textdegree}
+ \gdefchar^^b1{$\pm$}
+ \gdefchar^^b2{$^2$}
+ \gdefchar^^b3{$^3$}
+ \gdefchar^^b4{\'{}}
+ \gdefchar^^b5{$\mu$}
+ \gdefchar^^b6{\P}
+ \gdefchar^^b7{\ensuremath\cdot}
+ \gdefchar^^b8{\cedilla\ }
+ \gdefchar^^b9{$^1$}
+ \gdefchar^^ba{\ordm}
+ \gdefchar^^bb{\guillemetright{}}
+ \gdefchar^^bc{$1\over4$}
+ \gdefchar^^bd{$1\over2$}
+ \gdefchar^^be{$3\over4$}
+ \gdefchar^^bf{\questiondown}
+ %
+ \gdefchar^^c0{\`A}
+ \gdefchar^^c1{\'A}
+ \gdefchar^^c2{\^A}
+ \gdefchar^^c3{\~A}
+ \gdefchar^^c4{\"A}
+ \gdefchar^^c5{\ringaccent A}
+ \gdefchar^^c6{\AE}
+ \gdefchar^^c7{\cedilla C}
+ \gdefchar^^c8{\`E}
+ \gdefchar^^c9{\'E}
+ \gdefchar^^ca{\^E}
+ \gdefchar^^cb{\"E}
+ \gdefchar^^cc{\`I}
+ \gdefchar^^cd{\'I}
+ \gdefchar^^ce{\^I}
+ \gdefchar^^cf{\"I}
+ %
+ \gdefchar^^d0{\DH}
+ \gdefchar^^d1{\~N}
+ \gdefchar^^d2{\`O}
+ \gdefchar^^d3{\'O}
+ \gdefchar^^d4{\^O}
+ \gdefchar^^d5{\~O}
+ \gdefchar^^d6{\"O}
+ \gdefchar^^d7{$\times$}
+ \gdefchar^^d8{\O}
+ \gdefchar^^d9{\`U}
+ \gdefchar^^da{\'U}
+ \gdefchar^^db{\^U}
+ \gdefchar^^dc{\"U}
+ \gdefchar^^dd{\'Y}
+ \gdefchar^^de{\TH}
+ \gdefchar^^df{\ss}
+ %
+ \gdefchar^^e0{\`a}
+ \gdefchar^^e1{\'a}
+ \gdefchar^^e2{\^a}
+ \gdefchar^^e3{\~a}
+ \gdefchar^^e4{\"a}
+ \gdefchar^^e5{\ringaccent a}
+ \gdefchar^^e6{\ae}
+ \gdefchar^^e7{\cedilla c}
+ \gdefchar^^e8{\`e}
+ \gdefchar^^e9{\'e}
+ \gdefchar^^ea{\^e}
+ \gdefchar^^eb{\"e}
+ \gdefchar^^ec{\`{\dotless i}}
+ \gdefchar^^ed{\'{\dotless i}}
+ \gdefchar^^ee{\^{\dotless i}}
+ \gdefchar^^ef{\"{\dotless i}}
+ %
+ \gdefchar^^f0{\dh}
+ \gdefchar^^f1{\~n}
+ \gdefchar^^f2{\`o}
+ \gdefchar^^f3{\'o}
+ \gdefchar^^f4{\^o}
+ \gdefchar^^f5{\~o}
+ \gdefchar^^f6{\"o}
+ \gdefchar^^f7{$\div$}
+ \gdefchar^^f8{\o}
+ \gdefchar^^f9{\`u}
+ \gdefchar^^fa{\'u}
+ \gdefchar^^fb{\^u}
+ \gdefchar^^fc{\"u}
+ \gdefchar^^fd{\'y}
+ \gdefchar^^fe{\th}
+ \gdefchar^^ff{\"y}
+}
+
+% Latin9 (ISO-8859-15) encoding character definitions.
+\def\latninechardefs{%
+ % Encoding is almost identical to Latin1.
+ \latonechardefs
+ %
+ \gdefchar^^a4{\euro{}}
+ \gdefchar^^a6{\v S}
+ \gdefchar^^a8{\v s}
+ \gdefchar^^b4{\v Z}
+ \gdefchar^^b8{\v z}
+ \gdefchar^^bc{\OE}
+ \gdefchar^^bd{\oe}
+ \gdefchar^^be{\"Y}
+}
+
+% Latin2 (ISO-8859-2) character definitions.
+\def\lattwochardefs{%
+ \gdefchar^^a0{\tie}
+ \gdefchar^^a1{\ogonek{A}}
+ \gdefchar^^a2{\u{}}
+ \gdefchar^^a3{\L}
+ \gdefchar^^a4{\missingcharmsg{CURRENCY SIGN}}
+ \gdefchar^^a5{\v L}
+ \gdefchar^^a6{\'S}
+ \gdefchar^^a7{\S}
+ \gdefchar^^a8{\"{}}
+ \gdefchar^^a9{\v S}
+ \gdefchar^^aa{\cedilla S}
+ \gdefchar^^ab{\v T}
+ \gdefchar^^ac{\'Z}
+ \gdefchar^^ad{\-}
+ \gdefchar^^ae{\v Z}
+ \gdefchar^^af{\dotaccent Z}
+ %
+ \gdefchar^^b0{\textdegree{}}
+ \gdefchar^^b1{\ogonek{a}}
+ \gdefchar^^b2{\ogonek{ }}
+ \gdefchar^^b3{\l}
+ \gdefchar^^b4{\'{}}
+ \gdefchar^^b5{\v l}
+ \gdefchar^^b6{\'s}
+ \gdefchar^^b7{\v{}}
+ \gdefchar^^b8{\cedilla\ }
+ \gdefchar^^b9{\v s}
+ \gdefchar^^ba{\cedilla s}
+ \gdefchar^^bb{\v t}
+ \gdefchar^^bc{\'z}
+ \gdefchar^^bd{\H{}}
+ \gdefchar^^be{\v z}
+ \gdefchar^^bf{\dotaccent z}
+ %
+ \gdefchar^^c0{\'R}
+ \gdefchar^^c1{\'A}
+ \gdefchar^^c2{\^A}
+ \gdefchar^^c3{\u A}
+ \gdefchar^^c4{\"A}
+ \gdefchar^^c5{\'L}
+ \gdefchar^^c6{\'C}
+ \gdefchar^^c7{\cedilla C}
+ \gdefchar^^c8{\v C}
+ \gdefchar^^c9{\'E}
+ \gdefchar^^ca{\ogonek{E}}
+ \gdefchar^^cb{\"E}
+ \gdefchar^^cc{\v E}
+ \gdefchar^^cd{\'I}
+ \gdefchar^^ce{\^I}
+ \gdefchar^^cf{\v D}
+ %
+ \gdefchar^^d0{\DH}
+ \gdefchar^^d1{\'N}
+ \gdefchar^^d2{\v N}
+ \gdefchar^^d3{\'O}
+ \gdefchar^^d4{\^O}
+ \gdefchar^^d5{\H O}
+ \gdefchar^^d6{\"O}
+ \gdefchar^^d7{$\times$}
+ \gdefchar^^d8{\v R}
+ \gdefchar^^d9{\ringaccent U}
+ \gdefchar^^da{\'U}
+ \gdefchar^^db{\H U}
+ \gdefchar^^dc{\"U}
+ \gdefchar^^dd{\'Y}
+ \gdefchar^^de{\cedilla T}
+ \gdefchar^^df{\ss}
+ %
+ \gdefchar^^e0{\'r}
+ \gdefchar^^e1{\'a}
+ \gdefchar^^e2{\^a}
+ \gdefchar^^e3{\u a}
+ \gdefchar^^e4{\"a}
+ \gdefchar^^e5{\'l}
+ \gdefchar^^e6{\'c}
+ \gdefchar^^e7{\cedilla c}
+ \gdefchar^^e8{\v c}
+ \gdefchar^^e9{\'e}
+ \gdefchar^^ea{\ogonek{e}}
+ \gdefchar^^eb{\"e}
+ \gdefchar^^ec{\v e}
+ \gdefchar^^ed{\'{\dotless{i}}}
+ \gdefchar^^ee{\^{\dotless{i}}}
+ \gdefchar^^ef{\v d}
+ %
+ \gdefchar^^f0{\dh}
+ \gdefchar^^f1{\'n}
+ \gdefchar^^f2{\v n}
+ \gdefchar^^f3{\'o}
+ \gdefchar^^f4{\^o}
+ \gdefchar^^f5{\H o}
+ \gdefchar^^f6{\"o}
+ \gdefchar^^f7{$\div$}
+ \gdefchar^^f8{\v r}
+ \gdefchar^^f9{\ringaccent u}
+ \gdefchar^^fa{\'u}
+ \gdefchar^^fb{\H u}
+ \gdefchar^^fc{\"u}
+ \gdefchar^^fd{\'y}
+ \gdefchar^^fe{\cedilla t}
+ \gdefchar^^ff{\dotaccent{}}
+}
+
+% UTF-8 character definitions.
+%
+% This code to support UTF-8 is based on LaTeX's utf8.def, with some
+% changes for Texinfo conventions. It is included here under the GPL by
+% permission from Frank Mittelbach and the LaTeX team.
+%
+\newcount\countUTFx
+\newcount\countUTFy
+\newcount\countUTFz
+
+\gdef\UTFviiiTwoOctets#1#2{\expandafter
+ \UTFviiiDefined\csname u8:#1\string #2\endcsname}
+%
+\gdef\UTFviiiThreeOctets#1#2#3{\expandafter
+ \UTFviiiDefined\csname u8:#1\string #2\string #3\endcsname}
+%
+\gdef\UTFviiiFourOctets#1#2#3#4{\expandafter
+ \UTFviiiDefined\csname u8:#1\string #2\string #3\string #4\endcsname}
+
+\gdef\UTFviiiDefined#1{%
+ \ifx #1\relax
+ \message{\linenumber Unicode char \string #1 not defined for Texinfo}%
+ \else
+ \expandafter #1%
+ \fi
+}
+
+% Give non-ASCII bytes the active definitions for processing UTF-8 sequences
+\begingroup
+ \catcode`\~13
+ \catcode`\$12
+ \catcode`\"12
+
+ % Loop from \countUTFx to \countUTFy, performing \UTFviiiTmp
+ % substituting ~ and $ with a character token of that value.
+ \def\UTFviiiLoop{%
+ \global\catcode\countUTFx\active
+ \uccode`\~\countUTFx
+ \uccode`\$\countUTFx
+ \uppercase\expandafter{\UTFviiiTmp}%
+ \advance\countUTFx by 1
+ \ifnum\countUTFx < \countUTFy
+ \expandafter\UTFviiiLoop
+ \fi}
+
+ % For bytes other than the first in a UTF-8 sequence. Not expected to
+ % be expanded except when writing to auxiliary files.
+ \countUTFx = "80
+ \countUTFy = "C2
+ \def\UTFviiiTmp{%
+ \gdef~{%
+ \ifpassthroughchars $\fi}}%
+ \UTFviiiLoop
+
+ \countUTFx = "C2
+ \countUTFy = "E0
+ \def\UTFviiiTmp{%
+ \gdef~{%
+ \ifpassthroughchars $%
+ \else\expandafter\UTFviiiTwoOctets\expandafter$\fi}}%
+ \UTFviiiLoop
+
+ \countUTFx = "E0
+ \countUTFy = "F0
+ \def\UTFviiiTmp{%
+ \gdef~{%
+ \ifpassthroughchars $%
+ \else\expandafter\UTFviiiThreeOctets\expandafter$\fi}}%
+ \UTFviiiLoop
+
+ \countUTFx = "F0
+ \countUTFy = "F4
+ \def\UTFviiiTmp{%
+ \gdef~{%
+ \ifpassthroughchars $%
+ \else\expandafter\UTFviiiFourOctets\expandafter$\fi
+ }}%
+ \UTFviiiLoop
+\endgroup
+
+\def\globallet{\global\let} % save some \expandafter's below
+
+% @U{xxxx} to produce U+xxxx, if we support it.
+\def\U#1{%
+ \expandafter\ifx\csname uni:#1\endcsname \relax
+ \iftxinativeunicodecapable
+ % All Unicode characters can be used if native Unicode handling is
+ % active. However, if the font does not have the glyph,
+ % letters are missing.
+ \begingroup
+ \uccode`\.="#1\relax
+ \uppercase{.}
+ \endgroup
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unicode character U+#1 not supported, sorry}%
+ \fi
+ \else
+ \csname uni:#1\endcsname
+ \fi
+}
+
+% These macros are used here to construct the name of a control
+% sequence to be defined.
+\def\UTFviiiTwoOctetsName#1#2{%
+ \csname u8:#1\string #2\endcsname}%
+\def\UTFviiiThreeOctetsName#1#2#3{%
+ \csname u8:#1\string #2\string #3\endcsname}%
+\def\UTFviiiFourOctetsName#1#2#3#4{%
+ \csname u8:#1\string #2\string #3\string #4\endcsname}%
+
+% For UTF-8 byte sequences (TeX, e-TeX and pdfTeX),
+% provide a definition macro to replace a Unicode character;
+% this gets used by the @U command
+%
+\begingroup
+ \catcode`\"=12
+ \catcode`\<=12
+ \catcode`\.=12
+ \catcode`\,=12
+ \catcode`\;=12
+ \catcode`\!=12
+ \catcode`\~=13
+ \gdef\DeclareUnicodeCharacterUTFviii#1#2{%
+ \countUTFz = "#1\relax
+ \begingroup
+ \parseXMLCharref
+
+ % Give \u8:... its definition. The sequence of seven \expandafter's
+ % expands after the \gdef three times, e.g.
+ %
+ % 1. \UTFviiTwoOctetsName B1 B2
+ % 2. \csname u8:B1 \string B2 \endcsname
+ % 3. \u8: B1 B2 (a single control sequence token)
+ %
+ \expandafter\expandafter
+ \expandafter\expandafter
+ \expandafter\expandafter
+ \expandafter\gdef \UTFviiiTmp{#2}%
+ %
+ \expandafter\ifx\csname uni:#1\endcsname \relax \else
+ \message{Internal error, already defined: #1}%
+ \fi
+ %
+ % define an additional control sequence for this code point.
+ \expandafter\globallet\csname uni:#1\endcsname \UTFviiiTmp
+ \endgroup}
+ %
+ % Given the value in \countUTFz as a Unicode code point, set \UTFviiiTmp
+ % to the corresponding UTF-8 sequence.
+ \gdef\parseXMLCharref{%
+ \ifnum\countUTFz < "A0\relax
+ \errhelp = \EMsimple
+ \errmessage{Cannot define Unicode char value < 00A0}%
+ \else\ifnum\countUTFz < "800\relax
+ \parseUTFviiiA,%
+ \parseUTFviiiB C\UTFviiiTwoOctetsName.,%
+ \else\ifnum\countUTFz < "10000\relax
+ \parseUTFviiiA;%
+ \parseUTFviiiA,%
+ \parseUTFviiiB E\UTFviiiThreeOctetsName.{,;}%
+ \else
+ \parseUTFviiiA;%
+ \parseUTFviiiA,%
+ \parseUTFviiiA!%
+ \parseUTFviiiB F\UTFviiiFourOctetsName.{!,;}%
+ \fi\fi\fi
+ }
+
+ % Extract a byte from the end of the UTF-8 representation of \countUTFx.
+ % It must be a non-initial byte in the sequence.
+ % Change \uccode of #1 for it to be used in \parseUTFviiiB as one
+ % of the bytes.
+ \gdef\parseUTFviiiA#1{%
+ \countUTFx = \countUTFz
+ \divide\countUTFz by 64
+ \countUTFy = \countUTFz % Save to be the future value of \countUTFz.
+ \multiply\countUTFz by 64
+
+ % \countUTFz is now \countUTFx with the last 5 bits cleared. Subtract
+ % in order to get the last five bits.
+ \advance\countUTFx by -\countUTFz
+
+ % Convert this to the byte in the UTF-8 sequence.
+ \advance\countUTFx by 128
+ \uccode `#1\countUTFx
+ \countUTFz = \countUTFy}
+
+ % Used to put a UTF-8 byte sequence into \UTFviiiTmp
+ % #1 is the increment for \countUTFz to yield a the first byte of the UTF-8
+ % sequence.
+ % #2 is one of the \UTFviii*OctetsName macros.
+ % #3 is always a full stop (.)
+ % #4 is a template for the other bytes in the sequence. The values for these
+ % bytes is substituted in here with \uppercase using the \uccode's.
+ \gdef\parseUTFviiiB#1#2#3#4{%
+ \advance\countUTFz by "#10\relax
+ \uccode `#3\countUTFz
+ \uppercase{\gdef\UTFviiiTmp{#2#3#4}}}
+\endgroup
+
+% For native Unicode handling (XeTeX and LuaTeX),
+% provide a definition macro that sets a catcode to `other' non-globally
+%
+\def\DeclareUnicodeCharacterNativeOther#1#2{%
+ \catcode"#1=\other
+}
+
+% https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_M
+% U+0000..U+007F = https://en.wikipedia.org/wiki/Basic_Latin_(Unicode_block)
+% U+0080..U+00FF =
https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)
+% U+0100..U+017F = https://en.wikipedia.org/wiki/Latin_Extended-A
+% U+0180..U+024F = https://en.wikipedia.org/wiki/Latin_Extended-B
+%
+% Many of our renditions are less than wonderful, and all the missing
+% characters are available somewhere. Loading the necessary fonts
+% awaits user request. We can't truly support Unicode without
+% reimplementing everything that's been done in LaTeX for many years,
+% plus probably using luatex or xetex, and who knows what else.
+% We won't be doing that here in this simple file. But we can try to at
+% least make most of the characters not bomb out.
+%
+\def\unicodechardefs{%
+ \DeclareUnicodeCharacter{00A0}{\tie}%
+ \DeclareUnicodeCharacter{00A1}{\exclamdown}%
+ \DeclareUnicodeCharacter{00A2}{{\tcfont \char162}}% 0242=cent
+ \DeclareUnicodeCharacter{00A3}{\pounds{}}%
+ \DeclareUnicodeCharacter{00A4}{{\tcfont \char164}}% 0244=currency
+ \DeclareUnicodeCharacter{00A5}{{\tcfont \char165}}% 0245=yen
+ \DeclareUnicodeCharacter{00A6}{{\tcfont \char166}}% 0246=brokenbar
+ \DeclareUnicodeCharacter{00A7}{\S}%
+ \DeclareUnicodeCharacter{00A8}{\"{ }}%
+ \DeclareUnicodeCharacter{00A9}{\copyright{}}%
+ \DeclareUnicodeCharacter{00AA}{\ordf}%
+ \DeclareUnicodeCharacter{00AB}{\guillemetleft{}}%
+ \DeclareUnicodeCharacter{00AC}{\ensuremath\lnot}%
+ \DeclareUnicodeCharacter{00AD}{\-}%
+ \DeclareUnicodeCharacter{00AE}{\registeredsymbol{}}%
+ \DeclareUnicodeCharacter{00AF}{\={ }}%
+ %
+ \DeclareUnicodeCharacter{00B0}{\ringaccent{ }}%
+ \DeclareUnicodeCharacter{00B1}{\ensuremath\pm}%
+ \DeclareUnicodeCharacter{00B2}{$^2$}%
+ \DeclareUnicodeCharacter{00B3}{$^3$}%
+ \DeclareUnicodeCharacter{00B4}{\'{ }}%
+ \DeclareUnicodeCharacter{00B5}{$\mu$}%
+ \DeclareUnicodeCharacter{00B6}{\P}%
+ \DeclareUnicodeCharacter{00B7}{\ensuremath\cdot}%
+ \DeclareUnicodeCharacter{00B8}{\cedilla{ }}%
+ \DeclareUnicodeCharacter{00B9}{$^1$}%
+ \DeclareUnicodeCharacter{00BA}{\ordm}%
+ \DeclareUnicodeCharacter{00BB}{\guillemetright{}}%
+ \DeclareUnicodeCharacter{00BC}{$1\over4$}%
+ \DeclareUnicodeCharacter{00BD}{$1\over2$}%
+ \DeclareUnicodeCharacter{00BE}{$3\over4$}%
+ \DeclareUnicodeCharacter{00BF}{\questiondown}%
+ %
+ \DeclareUnicodeCharacter{00C0}{\`A}%
+ \DeclareUnicodeCharacter{00C1}{\'A}%
+ \DeclareUnicodeCharacter{00C2}{\^A}%
+ \DeclareUnicodeCharacter{00C3}{\~A}%
+ \DeclareUnicodeCharacter{00C4}{\"A}%
+ \DeclareUnicodeCharacter{00C5}{\AA}%
+ \DeclareUnicodeCharacter{00C6}{\AE}%
+ \DeclareUnicodeCharacter{00C7}{\cedilla{C}}%
+ \DeclareUnicodeCharacter{00C8}{\`E}%
+ \DeclareUnicodeCharacter{00C9}{\'E}%
+ \DeclareUnicodeCharacter{00CA}{\^E}%
+ \DeclareUnicodeCharacter{00CB}{\"E}%
+ \DeclareUnicodeCharacter{00CC}{\`I}%
+ \DeclareUnicodeCharacter{00CD}{\'I}%
+ \DeclareUnicodeCharacter{00CE}{\^I}%
+ \DeclareUnicodeCharacter{00CF}{\"I}%
+ %
+ \DeclareUnicodeCharacter{00D0}{\DH}%
+ \DeclareUnicodeCharacter{00D1}{\~N}%
+ \DeclareUnicodeCharacter{00D2}{\`O}%
+ \DeclareUnicodeCharacter{00D3}{\'O}%
+ \DeclareUnicodeCharacter{00D4}{\^O}%
+ \DeclareUnicodeCharacter{00D5}{\~O}%
+ \DeclareUnicodeCharacter{00D6}{\"O}%
+ \DeclareUnicodeCharacter{00D7}{\ensuremath\times}%
+ \DeclareUnicodeCharacter{00D8}{\O}%
+ \DeclareUnicodeCharacter{00D9}{\`U}%
+ \DeclareUnicodeCharacter{00DA}{\'U}%
+ \DeclareUnicodeCharacter{00DB}{\^U}%
+ \DeclareUnicodeCharacter{00DC}{\"U}%
+ \DeclareUnicodeCharacter{00DD}{\'Y}%
+ \DeclareUnicodeCharacter{00DE}{\TH}%
+ \DeclareUnicodeCharacter{00DF}{\ss}%
+ %
+ \DeclareUnicodeCharacter{00E0}{\`a}%
+ \DeclareUnicodeCharacter{00E1}{\'a}%
+ \DeclareUnicodeCharacter{00E2}{\^a}%
+ \DeclareUnicodeCharacter{00E3}{\~a}%
+ \DeclareUnicodeCharacter{00E4}{\"a}%
+ \DeclareUnicodeCharacter{00E5}{\aa}%
+ \DeclareUnicodeCharacter{00E6}{\ae}%
+ \DeclareUnicodeCharacter{00E7}{\cedilla{c}}%
+ \DeclareUnicodeCharacter{00E8}{\`e}%
+ \DeclareUnicodeCharacter{00E9}{\'e}%
+ \DeclareUnicodeCharacter{00EA}{\^e}%
+ \DeclareUnicodeCharacter{00EB}{\"e}%
+ \DeclareUnicodeCharacter{00EC}{\`{\dotless{i}}}%
+ \DeclareUnicodeCharacter{00ED}{\'{\dotless{i}}}%
+ \DeclareUnicodeCharacter{00EE}{\^{\dotless{i}}}%
+ \DeclareUnicodeCharacter{00EF}{\"{\dotless{i}}}%
+ %
+ \DeclareUnicodeCharacter{00F0}{\dh}%
+ \DeclareUnicodeCharacter{00F1}{\~n}%
+ \DeclareUnicodeCharacter{00F2}{\`o}%
+ \DeclareUnicodeCharacter{00F3}{\'o}%
+ \DeclareUnicodeCharacter{00F4}{\^o}%
+ \DeclareUnicodeCharacter{00F5}{\~o}%
+ \DeclareUnicodeCharacter{00F6}{\"o}%
+ \DeclareUnicodeCharacter{00F7}{\ensuremath\div}%
+ \DeclareUnicodeCharacter{00F8}{\o}%
+ \DeclareUnicodeCharacter{00F9}{\`u}%
+ \DeclareUnicodeCharacter{00FA}{\'u}%
+ \DeclareUnicodeCharacter{00FB}{\^u}%
+ \DeclareUnicodeCharacter{00FC}{\"u}%
+ \DeclareUnicodeCharacter{00FD}{\'y}%
+ \DeclareUnicodeCharacter{00FE}{\th}%
+ \DeclareUnicodeCharacter{00FF}{\"y}%
+ %
+ \DeclareUnicodeCharacter{0100}{\=A}%
+ \DeclareUnicodeCharacter{0101}{\=a}%
+ \DeclareUnicodeCharacter{0102}{\u{A}}%
+ \DeclareUnicodeCharacter{0103}{\u{a}}%
+ \DeclareUnicodeCharacter{0104}{\ogonek{A}}%
+ \DeclareUnicodeCharacter{0105}{\ogonek{a}}%
+ \DeclareUnicodeCharacter{0106}{\'C}%
+ \DeclareUnicodeCharacter{0107}{\'c}%
+ \DeclareUnicodeCharacter{0108}{\^C}%
+ \DeclareUnicodeCharacter{0109}{\^c}%
+ \DeclareUnicodeCharacter{010A}{\dotaccent{C}}%
+ \DeclareUnicodeCharacter{010B}{\dotaccent{c}}%
+ \DeclareUnicodeCharacter{010C}{\v{C}}%
+ \DeclareUnicodeCharacter{010D}{\v{c}}%
+ \DeclareUnicodeCharacter{010E}{\v{D}}%
+ \DeclareUnicodeCharacter{010F}{d'}%
+ %
+ \DeclareUnicodeCharacter{0110}{\DH}%
+ \DeclareUnicodeCharacter{0111}{\dh}%
+ \DeclareUnicodeCharacter{0112}{\=E}%
+ \DeclareUnicodeCharacter{0113}{\=e}%
+ \DeclareUnicodeCharacter{0114}{\u{E}}%
+ \DeclareUnicodeCharacter{0115}{\u{e}}%
+ \DeclareUnicodeCharacter{0116}{\dotaccent{E}}%
+ \DeclareUnicodeCharacter{0117}{\dotaccent{e}}%
+ \DeclareUnicodeCharacter{0118}{\ogonek{E}}%
+ \DeclareUnicodeCharacter{0119}{\ogonek{e}}%
+ \DeclareUnicodeCharacter{011A}{\v{E}}%
+ \DeclareUnicodeCharacter{011B}{\v{e}}%
+ \DeclareUnicodeCharacter{011C}{\^G}%
+ \DeclareUnicodeCharacter{011D}{\^g}%
+ \DeclareUnicodeCharacter{011E}{\u{G}}%
+ \DeclareUnicodeCharacter{011F}{\u{g}}%
+ %
+ \DeclareUnicodeCharacter{0120}{\dotaccent{G}}%
+ \DeclareUnicodeCharacter{0121}{\dotaccent{g}}%
+ \DeclareUnicodeCharacter{0122}{\cedilla{G}}%
+ \DeclareUnicodeCharacter{0123}{\cedilla{g}}%
+ \DeclareUnicodeCharacter{0124}{\^H}%
+ \DeclareUnicodeCharacter{0125}{\^h}%
+ \DeclareUnicodeCharacter{0126}{\missingcharmsg{H WITH STROKE}}%
+ \DeclareUnicodeCharacter{0127}{\missingcharmsg{h WITH STROKE}}%
+ \DeclareUnicodeCharacter{0128}{\~I}%
+ \DeclareUnicodeCharacter{0129}{\~{\dotless{i}}}%
+ \DeclareUnicodeCharacter{012A}{\=I}%
+ \DeclareUnicodeCharacter{012B}{\={\dotless{i}}}%
+ \DeclareUnicodeCharacter{012C}{\u{I}}%
+ \DeclareUnicodeCharacter{012D}{\u{\dotless{i}}}%
+ \DeclareUnicodeCharacter{012E}{\ogonek{I}}%
+ \DeclareUnicodeCharacter{012F}{\ogonek{i}}%
+ %
+ \DeclareUnicodeCharacter{0130}{\dotaccent{I}}%
+ \DeclareUnicodeCharacter{0131}{\dotless{i}}%
+ \DeclareUnicodeCharacter{0132}{IJ}%
+ \DeclareUnicodeCharacter{0133}{ij}%
+ \DeclareUnicodeCharacter{0134}{\^J}%
+ \DeclareUnicodeCharacter{0135}{\^{\dotless{j}}}%
+ \DeclareUnicodeCharacter{0136}{\cedilla{K}}%
+ \DeclareUnicodeCharacter{0137}{\cedilla{k}}%
+ \DeclareUnicodeCharacter{0138}{\ensuremath\kappa}%
+ \DeclareUnicodeCharacter{0139}{\'L}%
+ \DeclareUnicodeCharacter{013A}{\'l}%
+ \DeclareUnicodeCharacter{013B}{\cedilla{L}}%
+ \DeclareUnicodeCharacter{013C}{\cedilla{l}}%
+ \DeclareUnicodeCharacter{013D}{L'}% should kern
+ \DeclareUnicodeCharacter{013E}{l'}% should kern
+ \DeclareUnicodeCharacter{013F}{L\U{00B7}}%
+ %
+ \DeclareUnicodeCharacter{0140}{l\U{00B7}}%
+ \DeclareUnicodeCharacter{0141}{\L}%
+ \DeclareUnicodeCharacter{0142}{\l}%
+ \DeclareUnicodeCharacter{0143}{\'N}%
+ \DeclareUnicodeCharacter{0144}{\'n}%
+ \DeclareUnicodeCharacter{0145}{\cedilla{N}}%
+ \DeclareUnicodeCharacter{0146}{\cedilla{n}}%
+ \DeclareUnicodeCharacter{0147}{\v{N}}%
+ \DeclareUnicodeCharacter{0148}{\v{n}}%
+ \DeclareUnicodeCharacter{0149}{'n}%
+ \DeclareUnicodeCharacter{014A}{\missingcharmsg{ENG}}%
+ \DeclareUnicodeCharacter{014B}{\missingcharmsg{eng}}%
+ \DeclareUnicodeCharacter{014C}{\=O}%
+ \DeclareUnicodeCharacter{014D}{\=o}%
+ \DeclareUnicodeCharacter{014E}{\u{O}}%
+ \DeclareUnicodeCharacter{014F}{\u{o}}%
+ %
+ \DeclareUnicodeCharacter{0150}{\H{O}}%
+ \DeclareUnicodeCharacter{0151}{\H{o}}%
+ \DeclareUnicodeCharacter{0152}{\OE}%
+ \DeclareUnicodeCharacter{0153}{\oe}%
+ \DeclareUnicodeCharacter{0154}{\'R}%
+ \DeclareUnicodeCharacter{0155}{\'r}%
+ \DeclareUnicodeCharacter{0156}{\cedilla{R}}%
+ \DeclareUnicodeCharacter{0157}{\cedilla{r}}%
+ \DeclareUnicodeCharacter{0158}{\v{R}}%
+ \DeclareUnicodeCharacter{0159}{\v{r}}%
+ \DeclareUnicodeCharacter{015A}{\'S}%
+ \DeclareUnicodeCharacter{015B}{\'s}%
+ \DeclareUnicodeCharacter{015C}{\^S}%
+ \DeclareUnicodeCharacter{015D}{\^s}%
+ \DeclareUnicodeCharacter{015E}{\cedilla{S}}%
+ \DeclareUnicodeCharacter{015F}{\cedilla{s}}%
+ %
+ \DeclareUnicodeCharacter{0160}{\v{S}}%
+ \DeclareUnicodeCharacter{0161}{\v{s}}%
+ \DeclareUnicodeCharacter{0162}{\cedilla{T}}%
+ \DeclareUnicodeCharacter{0163}{\cedilla{t}}%
+ \DeclareUnicodeCharacter{0164}{\v{T}}%
+ \DeclareUnicodeCharacter{0165}{\v{t}}%
+ \DeclareUnicodeCharacter{0166}{\missingcharmsg{H WITH STROKE}}%
+ \DeclareUnicodeCharacter{0167}{\missingcharmsg{h WITH STROKE}}%
+ \DeclareUnicodeCharacter{0168}{\~U}%
+ \DeclareUnicodeCharacter{0169}{\~u}%
+ \DeclareUnicodeCharacter{016A}{\=U}%
+ \DeclareUnicodeCharacter{016B}{\=u}%
+ \DeclareUnicodeCharacter{016C}{\u{U}}%
+ \DeclareUnicodeCharacter{016D}{\u{u}}%
+ \DeclareUnicodeCharacter{016E}{\ringaccent{U}}%
+ \DeclareUnicodeCharacter{016F}{\ringaccent{u}}%
+ %
+ \DeclareUnicodeCharacter{0170}{\H{U}}%
+ \DeclareUnicodeCharacter{0171}{\H{u}}%
+ \DeclareUnicodeCharacter{0172}{\ogonek{U}}%
+ \DeclareUnicodeCharacter{0173}{\ogonek{u}}%
+ \DeclareUnicodeCharacter{0174}{\^W}%
+ \DeclareUnicodeCharacter{0175}{\^w}%
+ \DeclareUnicodeCharacter{0176}{\^Y}%
+ \DeclareUnicodeCharacter{0177}{\^y}%
+ \DeclareUnicodeCharacter{0178}{\"Y}%
+ \DeclareUnicodeCharacter{0179}{\'Z}%
+ \DeclareUnicodeCharacter{017A}{\'z}%
+ \DeclareUnicodeCharacter{017B}{\dotaccent{Z}}%
+ \DeclareUnicodeCharacter{017C}{\dotaccent{z}}%
+ \DeclareUnicodeCharacter{017D}{\v{Z}}%
+ \DeclareUnicodeCharacter{017E}{\v{z}}%
+ \DeclareUnicodeCharacter{017F}{\missingcharmsg{LONG S}}%
+ %
+ \DeclareUnicodeCharacter{01C4}{D\v{Z}}%
+ \DeclareUnicodeCharacter{01C5}{D\v{z}}%
+ \DeclareUnicodeCharacter{01C6}{d\v{z}}%
+ \DeclareUnicodeCharacter{01C7}{LJ}%
+ \DeclareUnicodeCharacter{01C8}{Lj}%
+ \DeclareUnicodeCharacter{01C9}{lj}%
+ \DeclareUnicodeCharacter{01CA}{NJ}%
+ \DeclareUnicodeCharacter{01CB}{Nj}%
+ \DeclareUnicodeCharacter{01CC}{nj}%
+ \DeclareUnicodeCharacter{01CD}{\v{A}}%
+ \DeclareUnicodeCharacter{01CE}{\v{a}}%
+ \DeclareUnicodeCharacter{01CF}{\v{I}}%
+ %
+ \DeclareUnicodeCharacter{01D0}{\v{\dotless{i}}}%
+ \DeclareUnicodeCharacter{01D1}{\v{O}}%
+ \DeclareUnicodeCharacter{01D2}{\v{o}}%
+ \DeclareUnicodeCharacter{01D3}{\v{U}}%
+ \DeclareUnicodeCharacter{01D4}{\v{u}}%
+ %
+ \DeclareUnicodeCharacter{01E2}{\={\AE}}%
+ \DeclareUnicodeCharacter{01E3}{\={\ae}}%
+ \DeclareUnicodeCharacter{01E6}{\v{G}}%
+ \DeclareUnicodeCharacter{01E7}{\v{g}}%
+ \DeclareUnicodeCharacter{01E8}{\v{K}}%
+ \DeclareUnicodeCharacter{01E9}{\v{k}}%
+ %
+ \DeclareUnicodeCharacter{01F0}{\v{\dotless{j}}}%
+ \DeclareUnicodeCharacter{01F1}{DZ}%
+ \DeclareUnicodeCharacter{01F2}{Dz}%
+ \DeclareUnicodeCharacter{01F3}{dz}%
+ \DeclareUnicodeCharacter{01F4}{\'G}%
+ \DeclareUnicodeCharacter{01F5}{\'g}%
+ \DeclareUnicodeCharacter{01F8}{\`N}%
+ \DeclareUnicodeCharacter{01F9}{\`n}%
+ \DeclareUnicodeCharacter{01FC}{\'{\AE}}%
+ \DeclareUnicodeCharacter{01FD}{\'{\ae}}%
+ \DeclareUnicodeCharacter{01FE}{\'{\O}}%
+ \DeclareUnicodeCharacter{01FF}{\'{\o}}%
+ %
+ \DeclareUnicodeCharacter{021E}{\v{H}}%
+ \DeclareUnicodeCharacter{021F}{\v{h}}%
+ %
+ \DeclareUnicodeCharacter{0226}{\dotaccent{A}}%
+ \DeclareUnicodeCharacter{0227}{\dotaccent{a}}%
+ \DeclareUnicodeCharacter{0228}{\cedilla{E}}%
+ \DeclareUnicodeCharacter{0229}{\cedilla{e}}%
+ \DeclareUnicodeCharacter{022E}{\dotaccent{O}}%
+ \DeclareUnicodeCharacter{022F}{\dotaccent{o}}%
+ %
+ \DeclareUnicodeCharacter{0232}{\=Y}%
+ \DeclareUnicodeCharacter{0233}{\=y}%
+ \DeclareUnicodeCharacter{0237}{\dotless{j}}%
+ %
+ \DeclareUnicodeCharacter{02DB}{\ogonek{ }}%
+ %
+ % Greek letters upper case
+ \DeclareUnicodeCharacter{0391}{{\it A}}%
+ \DeclareUnicodeCharacter{0392}{{\it B}}%
+ \DeclareUnicodeCharacter{0393}{\ensuremath{\mit\Gamma}}%
+ \DeclareUnicodeCharacter{0394}{\ensuremath{\mit\Delta}}%
+ \DeclareUnicodeCharacter{0395}{{\it E}}%
+ \DeclareUnicodeCharacter{0396}{{\it Z}}%
+ \DeclareUnicodeCharacter{0397}{{\it H}}%
+ \DeclareUnicodeCharacter{0398}{\ensuremath{\mit\Theta}}%
+ \DeclareUnicodeCharacter{0399}{{\it I}}%
+ \DeclareUnicodeCharacter{039A}{{\it K}}%
+ \DeclareUnicodeCharacter{039B}{\ensuremath{\mit\Lambda}}%
+ \DeclareUnicodeCharacter{039C}{{\it M}}%
+ \DeclareUnicodeCharacter{039D}{{\it N}}%
+ \DeclareUnicodeCharacter{039E}{\ensuremath{\mit\Xi}}%
+ \DeclareUnicodeCharacter{039F}{{\it O}}%
+ \DeclareUnicodeCharacter{03A0}{\ensuremath{\mit\Pi}}%
+ \DeclareUnicodeCharacter{03A1}{{\it P}}%
+ %\DeclareUnicodeCharacter{03A2}{} % none - corresponds to final sigma
+ \DeclareUnicodeCharacter{03A3}{\ensuremath{\mit\Sigma}}%
+ \DeclareUnicodeCharacter{03A4}{{\it T}}%
+ \DeclareUnicodeCharacter{03A5}{\ensuremath{\mit\Upsilon}}%
+ \DeclareUnicodeCharacter{03A6}{\ensuremath{\mit\Phi}}%
+ \DeclareUnicodeCharacter{03A7}{{\it X}}%
+ \DeclareUnicodeCharacter{03A8}{\ensuremath{\mit\Psi}}%
+ \DeclareUnicodeCharacter{03A9}{\ensuremath{\mit\Omega}}%
+ %
+ % Vowels with accents
+ \DeclareUnicodeCharacter{0390}{\ensuremath{\ddot{\acute\iota}}}%
+ \DeclareUnicodeCharacter{03AC}{\ensuremath{\acute\alpha}}%
+ \DeclareUnicodeCharacter{03AD}{\ensuremath{\acute\epsilon}}%
+ \DeclareUnicodeCharacter{03AE}{\ensuremath{\acute\eta}}%
+ \DeclareUnicodeCharacter{03AF}{\ensuremath{\acute\iota}}%
+ \DeclareUnicodeCharacter{03B0}{\ensuremath{\acute{\ddot\upsilon}}}%
+ %
+ % Standalone accent
+ \DeclareUnicodeCharacter{0384}{\ensuremath{\acute{\ }}}%
+ %
+ % Greek letters lower case
+ \DeclareUnicodeCharacter{03B1}{\ensuremath\alpha}%
+ \DeclareUnicodeCharacter{03B2}{\ensuremath\beta}%
+ \DeclareUnicodeCharacter{03B3}{\ensuremath\gamma}%
+ \DeclareUnicodeCharacter{03B4}{\ensuremath\delta}%
+ \DeclareUnicodeCharacter{03B5}{\ensuremath\epsilon}%
+ \DeclareUnicodeCharacter{03B6}{\ensuremath\zeta}%
+ \DeclareUnicodeCharacter{03B7}{\ensuremath\eta}%
+ \DeclareUnicodeCharacter{03B8}{\ensuremath\theta}%
+ \DeclareUnicodeCharacter{03B9}{\ensuremath\iota}%
+ \DeclareUnicodeCharacter{03BA}{\ensuremath\kappa}%
+ \DeclareUnicodeCharacter{03BB}{\ensuremath\lambda}%
+ \DeclareUnicodeCharacter{03BC}{\ensuremath\mu}%
+ \DeclareUnicodeCharacter{03BD}{\ensuremath\nu}%
+ \DeclareUnicodeCharacter{03BE}{\ensuremath\xi}%
+ \DeclareUnicodeCharacter{03BF}{{\it o}}% omicron
+ \DeclareUnicodeCharacter{03C0}{\ensuremath\pi}%
+ \DeclareUnicodeCharacter{03C1}{\ensuremath\rho}%
+ \DeclareUnicodeCharacter{03C2}{\ensuremath\varsigma}%
+ \DeclareUnicodeCharacter{03C3}{\ensuremath\sigma}%
+ \DeclareUnicodeCharacter{03C4}{\ensuremath\tau}%
+ \DeclareUnicodeCharacter{03C5}{\ensuremath\upsilon}%
+ \DeclareUnicodeCharacter{03C6}{\ensuremath\phi}%
+ \DeclareUnicodeCharacter{03C7}{\ensuremath\chi}%
+ \DeclareUnicodeCharacter{03C8}{\ensuremath\psi}%
+ \DeclareUnicodeCharacter{03C9}{\ensuremath\omega}%
+ %
+ % More Greek vowels with accents
+ \DeclareUnicodeCharacter{03CA}{\ensuremath{\ddot\iota}}%
+ \DeclareUnicodeCharacter{03CB}{\ensuremath{\ddot\upsilon}}%
+ \DeclareUnicodeCharacter{03CC}{\ensuremath{\acute o}}%
+ \DeclareUnicodeCharacter{03CD}{\ensuremath{\acute\upsilon}}%
+ \DeclareUnicodeCharacter{03CE}{\ensuremath{\acute\omega}}%
+ %
+ % Variant Greek letters
+ \DeclareUnicodeCharacter{03D1}{\ensuremath\vartheta}%
+ \DeclareUnicodeCharacter{03D6}{\ensuremath\varpi}%
+ \DeclareUnicodeCharacter{03F1}{\ensuremath\varrho}%
+ %
+ \DeclareUnicodeCharacter{1E02}{\dotaccent{B}}%
+ \DeclareUnicodeCharacter{1E03}{\dotaccent{b}}%
+ \DeclareUnicodeCharacter{1E04}{\udotaccent{B}}%
+ \DeclareUnicodeCharacter{1E05}{\udotaccent{b}}%
+ \DeclareUnicodeCharacter{1E06}{\ubaraccent{B}}%
+ \DeclareUnicodeCharacter{1E07}{\ubaraccent{b}}%
+ \DeclareUnicodeCharacter{1E0A}{\dotaccent{D}}%
+ \DeclareUnicodeCharacter{1E0B}{\dotaccent{d}}%
+ \DeclareUnicodeCharacter{1E0C}{\udotaccent{D}}%
+ \DeclareUnicodeCharacter{1E0D}{\udotaccent{d}}%
+ \DeclareUnicodeCharacter{1E0E}{\ubaraccent{D}}%
+ \DeclareUnicodeCharacter{1E0F}{\ubaraccent{d}}%
+ %
+ \DeclareUnicodeCharacter{1E1E}{\dotaccent{F}}%
+ \DeclareUnicodeCharacter{1E1F}{\dotaccent{f}}%
+ %
+ \DeclareUnicodeCharacter{1E20}{\=G}%
+ \DeclareUnicodeCharacter{1E21}{\=g}%
+ \DeclareUnicodeCharacter{1E22}{\dotaccent{H}}%
+ \DeclareUnicodeCharacter{1E23}{\dotaccent{h}}%
+ \DeclareUnicodeCharacter{1E24}{\udotaccent{H}}%
+ \DeclareUnicodeCharacter{1E25}{\udotaccent{h}}%
+ \DeclareUnicodeCharacter{1E26}{\"H}%
+ \DeclareUnicodeCharacter{1E27}{\"h}%
+ %
+ \DeclareUnicodeCharacter{1E30}{\'K}%
+ \DeclareUnicodeCharacter{1E31}{\'k}%
+ \DeclareUnicodeCharacter{1E32}{\udotaccent{K}}%
+ \DeclareUnicodeCharacter{1E33}{\udotaccent{k}}%
+ \DeclareUnicodeCharacter{1E34}{\ubaraccent{K}}%
+ \DeclareUnicodeCharacter{1E35}{\ubaraccent{k}}%
+ \DeclareUnicodeCharacter{1E36}{\udotaccent{L}}%
+ \DeclareUnicodeCharacter{1E37}{\udotaccent{l}}%
+ \DeclareUnicodeCharacter{1E3A}{\ubaraccent{L}}%
+ \DeclareUnicodeCharacter{1E3B}{\ubaraccent{l}}%
+ \DeclareUnicodeCharacter{1E3E}{\'M}%
+ \DeclareUnicodeCharacter{1E3F}{\'m}%
+ %
+ \DeclareUnicodeCharacter{1E40}{\dotaccent{M}}%
+ \DeclareUnicodeCharacter{1E41}{\dotaccent{m}}%
+ \DeclareUnicodeCharacter{1E42}{\udotaccent{M}}%
+ \DeclareUnicodeCharacter{1E43}{\udotaccent{m}}%
+ \DeclareUnicodeCharacter{1E44}{\dotaccent{N}}%
+ \DeclareUnicodeCharacter{1E45}{\dotaccent{n}}%
+ \DeclareUnicodeCharacter{1E46}{\udotaccent{N}}%
+ \DeclareUnicodeCharacter{1E47}{\udotaccent{n}}%
+ \DeclareUnicodeCharacter{1E48}{\ubaraccent{N}}%
+ \DeclareUnicodeCharacter{1E49}{\ubaraccent{n}}%
+ %
+ \DeclareUnicodeCharacter{1E54}{\'P}%
+ \DeclareUnicodeCharacter{1E55}{\'p}%
+ \DeclareUnicodeCharacter{1E56}{\dotaccent{P}}%
+ \DeclareUnicodeCharacter{1E57}{\dotaccent{p}}%
+ \DeclareUnicodeCharacter{1E58}{\dotaccent{R}}%
+ \DeclareUnicodeCharacter{1E59}{\dotaccent{r}}%
+ \DeclareUnicodeCharacter{1E5A}{\udotaccent{R}}%
+ \DeclareUnicodeCharacter{1E5B}{\udotaccent{r}}%
+ \DeclareUnicodeCharacter{1E5E}{\ubaraccent{R}}%
+ \DeclareUnicodeCharacter{1E5F}{\ubaraccent{r}}%
+ %
+ \DeclareUnicodeCharacter{1E60}{\dotaccent{S}}%
+ \DeclareUnicodeCharacter{1E61}{\dotaccent{s}}%
+ \DeclareUnicodeCharacter{1E62}{\udotaccent{S}}%
+ \DeclareUnicodeCharacter{1E63}{\udotaccent{s}}%
+ \DeclareUnicodeCharacter{1E6A}{\dotaccent{T}}%
+ \DeclareUnicodeCharacter{1E6B}{\dotaccent{t}}%
+ \DeclareUnicodeCharacter{1E6C}{\udotaccent{T}}%
+ \DeclareUnicodeCharacter{1E6D}{\udotaccent{t}}%
+ \DeclareUnicodeCharacter{1E6E}{\ubaraccent{T}}%
+ \DeclareUnicodeCharacter{1E6F}{\ubaraccent{t}}%
+ %
+ \DeclareUnicodeCharacter{1E7C}{\~V}%
+ \DeclareUnicodeCharacter{1E7D}{\~v}%
+ \DeclareUnicodeCharacter{1E7E}{\udotaccent{V}}%
+ \DeclareUnicodeCharacter{1E7F}{\udotaccent{v}}%
+ %
+ \DeclareUnicodeCharacter{1E80}{\`W}%
+ \DeclareUnicodeCharacter{1E81}{\`w}%
+ \DeclareUnicodeCharacter{1E82}{\'W}%
+ \DeclareUnicodeCharacter{1E83}{\'w}%
+ \DeclareUnicodeCharacter{1E84}{\"W}%
+ \DeclareUnicodeCharacter{1E85}{\"w}%
+ \DeclareUnicodeCharacter{1E86}{\dotaccent{W}}%
+ \DeclareUnicodeCharacter{1E87}{\dotaccent{w}}%
+ \DeclareUnicodeCharacter{1E88}{\udotaccent{W}}%
+ \DeclareUnicodeCharacter{1E89}{\udotaccent{w}}%
+ \DeclareUnicodeCharacter{1E8A}{\dotaccent{X}}%
+ \DeclareUnicodeCharacter{1E8B}{\dotaccent{x}}%
+ \DeclareUnicodeCharacter{1E8C}{\"X}%
+ \DeclareUnicodeCharacter{1E8D}{\"x}%
+ \DeclareUnicodeCharacter{1E8E}{\dotaccent{Y}}%
+ \DeclareUnicodeCharacter{1E8F}{\dotaccent{y}}%
+ %
+ \DeclareUnicodeCharacter{1E90}{\^Z}%
+ \DeclareUnicodeCharacter{1E91}{\^z}%
+ \DeclareUnicodeCharacter{1E92}{\udotaccent{Z}}%
+ \DeclareUnicodeCharacter{1E93}{\udotaccent{z}}%
+ \DeclareUnicodeCharacter{1E94}{\ubaraccent{Z}}%
+ \DeclareUnicodeCharacter{1E95}{\ubaraccent{z}}%
+ \DeclareUnicodeCharacter{1E96}{\ubaraccent{h}}%
+ \DeclareUnicodeCharacter{1E97}{\"t}%
+ \DeclareUnicodeCharacter{1E98}{\ringaccent{w}}%
+ \DeclareUnicodeCharacter{1E99}{\ringaccent{y}}%
+ %
+ \DeclareUnicodeCharacter{1EA0}{\udotaccent{A}}%
+ \DeclareUnicodeCharacter{1EA1}{\udotaccent{a}}%
+ %
+ \DeclareUnicodeCharacter{1EB8}{\udotaccent{E}}%
+ \DeclareUnicodeCharacter{1EB9}{\udotaccent{e}}%
+ \DeclareUnicodeCharacter{1EBC}{\~E}%
+ \DeclareUnicodeCharacter{1EBD}{\~e}%
+ %
+ \DeclareUnicodeCharacter{1ECA}{\udotaccent{I}}%
+ \DeclareUnicodeCharacter{1ECB}{\udotaccent{i}}%
+ \DeclareUnicodeCharacter{1ECC}{\udotaccent{O}}%
+ \DeclareUnicodeCharacter{1ECD}{\udotaccent{o}}%
+ %
+ \DeclareUnicodeCharacter{1EE4}{\udotaccent{U}}%
+ \DeclareUnicodeCharacter{1EE5}{\udotaccent{u}}%
+ %
+ \DeclareUnicodeCharacter{1EF2}{\`Y}%
+ \DeclareUnicodeCharacter{1EF3}{\`y}%
+ \DeclareUnicodeCharacter{1EF4}{\udotaccent{Y}}%
+ %
+ \DeclareUnicodeCharacter{1EF8}{\~Y}%
+ \DeclareUnicodeCharacter{1EF9}{\~y}%
+ %
+ % Punctuation
+ \DeclareUnicodeCharacter{2013}{--}%
+ \DeclareUnicodeCharacter{2014}{---}%
+ \DeclareUnicodeCharacter{2018}{\quoteleft{}}%
+ \DeclareUnicodeCharacter{2019}{\quoteright{}}%
+ \DeclareUnicodeCharacter{201A}{\quotesinglbase{}}%
+ \DeclareUnicodeCharacter{201C}{\quotedblleft{}}%
+ \DeclareUnicodeCharacter{201D}{\quotedblright{}}%
+ \DeclareUnicodeCharacter{201E}{\quotedblbase{}}%
+ \DeclareUnicodeCharacter{2020}{\ensuremath\dagger}%
+ \DeclareUnicodeCharacter{2021}{\ensuremath\ddagger}%
+ \DeclareUnicodeCharacter{2022}{\bullet{}}%
+ \DeclareUnicodeCharacter{202F}{\thinspace}%
+ \DeclareUnicodeCharacter{2026}{\dots{}}%
+ \DeclareUnicodeCharacter{2039}{\guilsinglleft{}}%
+ \DeclareUnicodeCharacter{203A}{\guilsinglright{}}%
+ %
+ \DeclareUnicodeCharacter{20AC}{\euro{}}%
+ %
+ \DeclareUnicodeCharacter{2192}{\expansion{}}%
+ \DeclareUnicodeCharacter{21D2}{\result{}}%
+ %
+ % Mathematical symbols
+ \DeclareUnicodeCharacter{2200}{\ensuremath\forall}%
+ \DeclareUnicodeCharacter{2203}{\ensuremath\exists}%
+ \DeclareUnicodeCharacter{2208}{\ensuremath\in}%
+ \DeclareUnicodeCharacter{2212}{\minus{}}%
+ \DeclareUnicodeCharacter{2217}{\ast}%
+ \DeclareUnicodeCharacter{221E}{\ensuremath\infty}%
+ \DeclareUnicodeCharacter{2225}{\ensuremath\parallel}%
+ \DeclareUnicodeCharacter{2227}{\ensuremath\wedge}%
+ \DeclareUnicodeCharacter{2229}{\ensuremath\cap}%
+ \DeclareUnicodeCharacter{2261}{\equiv{}}%
+ \DeclareUnicodeCharacter{2264}{\ensuremath\leq}%
+ \DeclareUnicodeCharacter{2265}{\ensuremath\geq}%
+ \DeclareUnicodeCharacter{2282}{\ensuremath\subset}%
+ \DeclareUnicodeCharacter{2287}{\ensuremath\supseteq}%
+ %
+ \DeclareUnicodeCharacter{2016}{\ensuremath\Vert}%
+ \DeclareUnicodeCharacter{2032}{\ensuremath\prime}%
+ \DeclareUnicodeCharacter{210F}{\ensuremath\hbar}%
+ \DeclareUnicodeCharacter{2111}{\ensuremath\Im}%
+ \DeclareUnicodeCharacter{2113}{\ensuremath\ell}%
+ \DeclareUnicodeCharacter{2118}{\ensuremath\wp}%
+ \DeclareUnicodeCharacter{211C}{\ensuremath\Re}%
+ \DeclareUnicodeCharacter{2135}{\ensuremath\aleph}%
+ \DeclareUnicodeCharacter{2190}{\ensuremath\leftarrow}%
+ \DeclareUnicodeCharacter{2191}{\ensuremath\uparrow}%
+ \DeclareUnicodeCharacter{2193}{\ensuremath\downarrow}%
+ \DeclareUnicodeCharacter{2194}{\ensuremath\leftrightarrow}%
+ \DeclareUnicodeCharacter{2195}{\ensuremath\updownarrow}%
+ \DeclareUnicodeCharacter{2196}{\ensuremath\nwarrow}%
+ \DeclareUnicodeCharacter{2197}{\ensuremath\nearrow}%
+ \DeclareUnicodeCharacter{2198}{\ensuremath\searrow}%
+ \DeclareUnicodeCharacter{2199}{\ensuremath\swarrow}%
+ \DeclareUnicodeCharacter{21A6}{\ensuremath\mapsto}%
+ \DeclareUnicodeCharacter{21A9}{\ensuremath\hookleftarrow}%
+ \DeclareUnicodeCharacter{21AA}{\ensuremath\hookrightarrow}%
+ \DeclareUnicodeCharacter{21BC}{\ensuremath\leftharpoonup}%
+ \DeclareUnicodeCharacter{21BD}{\ensuremath\leftharpoondown}%
+ \DeclareUnicodeCharacter{21C0}{\ensuremath\rightharpoonup}%
+ \DeclareUnicodeCharacter{21C1}{\ensuremath\rightharpoondown}%
+ \DeclareUnicodeCharacter{21CC}{\ensuremath\rightleftharpoons}%
+ \DeclareUnicodeCharacter{21D0}{\ensuremath\Leftarrow}%
+ \DeclareUnicodeCharacter{21D1}{\ensuremath\Uparrow}%
+ \DeclareUnicodeCharacter{21D3}{\ensuremath\Downarrow}%
+ \DeclareUnicodeCharacter{21D4}{\ensuremath\Leftrightarrow}%
+ \DeclareUnicodeCharacter{21D5}{\ensuremath\Updownarrow}%
+ \DeclareUnicodeCharacter{2202}{\ensuremath\partial}%
+ \DeclareUnicodeCharacter{2205}{\ensuremath\emptyset}%
+ \DeclareUnicodeCharacter{2207}{\ensuremath\nabla}%
+ \DeclareUnicodeCharacter{2209}{\ensuremath\notin}%
+ \DeclareUnicodeCharacter{220B}{\ensuremath\owns}%
+ \DeclareUnicodeCharacter{220F}{\ensuremath\prod}%
+ \DeclareUnicodeCharacter{2210}{\ensuremath\coprod}%
+ \DeclareUnicodeCharacter{2211}{\ensuremath\sum}%
+ \DeclareUnicodeCharacter{2213}{\ensuremath\mp}%
+ \DeclareUnicodeCharacter{2218}{\ensuremath\circ}%
+ \DeclareUnicodeCharacter{221A}{\ensuremath\surd}%
+ \DeclareUnicodeCharacter{221D}{\ensuremath\propto}%
+ \DeclareUnicodeCharacter{2220}{\ensuremath\angle}%
+ \DeclareUnicodeCharacter{2223}{\ensuremath\mid}%
+ \DeclareUnicodeCharacter{2228}{\ensuremath\vee}%
+ \DeclareUnicodeCharacter{222A}{\ensuremath\cup}%
+ \DeclareUnicodeCharacter{222B}{\ensuremath\smallint}%
+ \DeclareUnicodeCharacter{222E}{\ensuremath\oint}%
+ \DeclareUnicodeCharacter{223C}{\ensuremath\sim}%
+ \DeclareUnicodeCharacter{2240}{\ensuremath\wr}%
+ \DeclareUnicodeCharacter{2243}{\ensuremath\simeq}%
+ \DeclareUnicodeCharacter{2245}{\ensuremath\cong}%
+ \DeclareUnicodeCharacter{2248}{\ensuremath\approx}%
+ \DeclareUnicodeCharacter{224D}{\ensuremath\asymp}%
+ \DeclareUnicodeCharacter{2250}{\ensuremath\doteq}%
+ \DeclareUnicodeCharacter{2260}{\ensuremath\neq}%
+ \DeclareUnicodeCharacter{226A}{\ensuremath\ll}%
+ \DeclareUnicodeCharacter{226B}{\ensuremath\gg}%
+ \DeclareUnicodeCharacter{227A}{\ensuremath\prec}%
+ \DeclareUnicodeCharacter{227B}{\ensuremath\succ}%
+ \DeclareUnicodeCharacter{2283}{\ensuremath\supset}%
+ \DeclareUnicodeCharacter{2286}{\ensuremath\subseteq}%
+ \DeclareUnicodeCharacter{228E}{\ensuremath\uplus}%
+ \DeclareUnicodeCharacter{2291}{\ensuremath\sqsubseteq}%
+ \DeclareUnicodeCharacter{2292}{\ensuremath\sqsupseteq}%
+ \DeclareUnicodeCharacter{2293}{\ensuremath\sqcap}%
+ \DeclareUnicodeCharacter{2294}{\ensuremath\sqcup}%
+ \DeclareUnicodeCharacter{2295}{\ensuremath\oplus}%
+ \DeclareUnicodeCharacter{2296}{\ensuremath\ominus}%
+ \DeclareUnicodeCharacter{2297}{\ensuremath\otimes}%
+ \DeclareUnicodeCharacter{2298}{\ensuremath\oslash}%
+ \DeclareUnicodeCharacter{2299}{\ensuremath\odot}%
+ \DeclareUnicodeCharacter{22A2}{\ensuremath\vdash}%
+ \DeclareUnicodeCharacter{22A3}{\ensuremath\dashv}%
+ \DeclareUnicodeCharacter{22A4}{\ensuremath\ptextop}%
+ \DeclareUnicodeCharacter{22A5}{\ensuremath\bot}%
+ \DeclareUnicodeCharacter{22A8}{\ensuremath\models}%
+ \DeclareUnicodeCharacter{22C0}{\ensuremath\bigwedge}%
+ \DeclareUnicodeCharacter{22C1}{\ensuremath\bigvee}%
+ \DeclareUnicodeCharacter{22C2}{\ensuremath\bigcap}%
+ \DeclareUnicodeCharacter{22C3}{\ensuremath\bigcup}%
+ \DeclareUnicodeCharacter{22C4}{\ensuremath\diamond}%
+ \DeclareUnicodeCharacter{22C5}{\ensuremath\cdot}%
+ \DeclareUnicodeCharacter{22C6}{\ensuremath\star}%
+ \DeclareUnicodeCharacter{22C8}{\ensuremath\bowtie}%
+ \DeclareUnicodeCharacter{2308}{\ensuremath\lceil}%
+ \DeclareUnicodeCharacter{2309}{\ensuremath\rceil}%
+ \DeclareUnicodeCharacter{230A}{\ensuremath\lfloor}%
+ \DeclareUnicodeCharacter{230B}{\ensuremath\rfloor}%
+ \DeclareUnicodeCharacter{2322}{\ensuremath\frown}%
+ \DeclareUnicodeCharacter{2323}{\ensuremath\smile}%
+ %
+ \DeclareUnicodeCharacter{25B3}{\ensuremath\triangle}%
+ \DeclareUnicodeCharacter{25B7}{\ensuremath\triangleright}%
+ \DeclareUnicodeCharacter{25BD}{\ensuremath\bigtriangledown}%
+ \DeclareUnicodeCharacter{25C1}{\ensuremath\triangleleft}%
+ \DeclareUnicodeCharacter{25C7}{\ensuremath\diamond}%
+ \DeclareUnicodeCharacter{2660}{\ensuremath\spadesuit}%
+ \DeclareUnicodeCharacter{2661}{\ensuremath\heartsuit}%
+ \DeclareUnicodeCharacter{2662}{\ensuremath\diamondsuit}%
+ \DeclareUnicodeCharacter{2663}{\ensuremath\clubsuit}%
+ \DeclareUnicodeCharacter{266D}{\ensuremath\flat}%
+ \DeclareUnicodeCharacter{266E}{\ensuremath\natural}%
+ \DeclareUnicodeCharacter{266F}{\ensuremath\sharp}%
+ \DeclareUnicodeCharacter{26AA}{\ensuremath\bigcirc}%
+ \DeclareUnicodeCharacter{27B9}{\ensuremath\rangle}%
+ \DeclareUnicodeCharacter{27C2}{\ensuremath\perp}%
+ \DeclareUnicodeCharacter{27E8}{\ensuremath\langle}%
+ \DeclareUnicodeCharacter{27F5}{\ensuremath\longleftarrow}%
+ \DeclareUnicodeCharacter{27F6}{\ensuremath\longrightarrow}%
+ \DeclareUnicodeCharacter{27F7}{\ensuremath\longleftrightarrow}%
+ \DeclareUnicodeCharacter{27FC}{\ensuremath\longmapsto}%
+ \DeclareUnicodeCharacter{29F5}{\ensuremath\setminus}%
+ \DeclareUnicodeCharacter{2A00}{\ensuremath\bigodot}%
+ \DeclareUnicodeCharacter{2A01}{\ensuremath\bigoplus}%
+ \DeclareUnicodeCharacter{2A02}{\ensuremath\bigotimes}%
+ \DeclareUnicodeCharacter{2A04}{\ensuremath\biguplus}%
+ \DeclareUnicodeCharacter{2A06}{\ensuremath\bigsqcup}%
+ \DeclareUnicodeCharacter{2A3F}{\ensuremath\amalg}%
+ \DeclareUnicodeCharacter{2AAF}{\ensuremath\preceq}%
+ \DeclareUnicodeCharacter{2AB0}{\ensuremath\succeq}%
+ %
+ \global\mathchardef\checkmark="1370% actually the square root sign
+ \DeclareUnicodeCharacter{2713}{\ensuremath\checkmark}%
+}% end of \unicodechardefs
+
+% UTF-8 byte sequence (pdfTeX) definitions (replacing and @U command)
+% It makes the setting that replace UTF-8 byte sequence.
+\def\utfeightchardefs{%
+ \let\DeclareUnicodeCharacter\DeclareUnicodeCharacterUTFviii
+ \unicodechardefs
+}
+
+% Whether the active definitions of non-ASCII characters expand to
+% non-active tokens with the same character code. This is used to
+% write characters literally, instead of using active definitions for
+% printing the correct glyphs.
+\newif\ifpassthroughchars
+\passthroughcharsfalse
+
+% For native Unicode handling (XeTeX and LuaTeX),
+% provide a definition macro to replace/pass-through a Unicode character
+%
+\def\DeclareUnicodeCharacterNative#1#2{%
+ \catcode"#1=\active
+ \def\dodeclareunicodecharacternative##1##2##3{%
+ \begingroup
+ \uccode`\~="##2\relax
+ \uppercase{\gdef~}{%
+ \ifpassthroughchars
+ ##1%
+ \else
+ ##3%
+ \fi
+ }
+ \endgroup
+ }
+ \begingroup
+ \uccode`\.="#1\relax
+ \uppercase{\def\UTFNativeTmp{.}}%
+ \expandafter\dodeclareunicodecharacternative\UTFNativeTmp{#1}{#2}%
+ \endgroup
+}
+
+% Native Unicode handling (XeTeX and LuaTeX) character replacing definition.
+% It activates the setting that replaces Unicode characters.
+\def\nativeunicodechardefs{%
+ \let\DeclareUnicodeCharacter\DeclareUnicodeCharacterNative
+ \unicodechardefs
+}
+
+% For native Unicode handling (XeTeX and LuaTeX),
+% make the character token expand
+% to the sequences given in \unicodechardefs for printing.
+\def\DeclareUnicodeCharacterNativeAtU#1#2{%
+ \def\UTFAtUTmp{#2}
+ \expandafter\globallet\csname uni:#1\endcsname \UTFAtUTmp
+}
+
+% @U command definitions for native Unicode handling (XeTeX and LuaTeX).
+\def\nativeunicodechardefsatu{%
+ \let\DeclareUnicodeCharacter\DeclareUnicodeCharacterNativeAtU
+ \unicodechardefs
+}
+
+% US-ASCII character definitions.
+\def\asciichardefs{% nothing need be done
+ \relax
+}
+
+% Define all Unicode characters we know about. This makes UTF-8 the default
+% input encoding and allows @U to work.
+\iftxinativeunicodecapable
+ \nativeunicodechardefsatu
+\else
+ \utfeightchardefs
+\fi
+
+\message{formatting,}
+
+\newdimen\defaultparindent \defaultparindent = 15pt
+
+\chapheadingskip = 15pt plus 4pt minus 2pt
+\secheadingskip = 12pt plus 3pt minus 2pt
+\subsecheadingskip = 9pt plus 2pt minus 2pt
+
+% Prevent underfull vbox error messages.
+\vbadness = 10000
+
+% Don't be very finicky about underfull hboxes, either.
+\hbadness = 6666
+
+% Following George Bush, get rid of widows and orphans.
+\widowpenalty=10000
+\clubpenalty=10000
+
+% Use TeX 3.0's \emergencystretch to help line breaking, but if we're
+% using an old version of TeX, don't do anything. We want the amount of
+% stretch added to depend on the line length, hence the dependence on
+% \hsize. We call this whenever the paper size is set.
+%
+\def\setemergencystretch{%
+ \ifx\emergencystretch\thisisundefined
+ % Allow us to assign to \emergencystretch anyway.
+ \def\emergencystretch{\dimen0}%
+ \else
+ \emergencystretch = .15\hsize
+ \fi
+}
+
+% Parameters in order: 1) textheight; 2) textwidth;
+% 3) voffset; 4) hoffset; 5) binding offset; 6) topskip;
+% 7) physical page height; 8) physical page width.
+%
+% We also call \setleading{\textleading}, so the caller should define
+% \textleading. The caller should also set \parskip.
+%
+\def\internalpagesizes#1#2#3#4#5#6#7#8{%
+ \voffset = #3\relax
+ \topskip = #6\relax
+ \splittopskip = \topskip
+ %
+ \vsize = #1\relax
+ \advance\vsize by \topskip
+ \outervsize = \vsize
+ \advance\outervsize by 2\topandbottommargin
+ \txipageheight = \vsize
+ %
+ \hsize = #2\relax
+ \outerhsize = \hsize
+ \advance\outerhsize by 0.5in
+ \txipagewidth = \hsize
+ %
+ \normaloffset = #4\relax
+ \bindingoffset = #5\relax
+ %
+ \ifpdf
+ \pdfpageheight #7\relax
+ \pdfpagewidth #8\relax
+ % if we don't reset these, they will remain at "1 true in" of
+ % whatever layout pdftex was dumped with.
+ \pdfhorigin = 1 true in
+ \pdfvorigin = 1 true in
+ \else
+ \ifx\XeTeXrevision\thisisundefined
+ \special{papersize=#8,#7}%
+ \else
+ \pdfpageheight #7\relax
+ \pdfpagewidth #8\relax
+ % XeTeX does not have \pdfhorigin and \pdfvorigin.
+ \fi
+ \fi
+ %
+ \setleading{\textleading}
+ %
+ \parindent = \defaultparindent
+ \setemergencystretch
+}
+
+% @letterpaper (the default).
+\def\letterpaper{{\globaldefs = 1
+ \parskip = 3pt plus 2pt minus 1pt
+ \textleading = 13.2pt
+ %
+ % If page is nothing but text, make it come out even.
+ \internalpagesizes{607.2pt}{6in}% that's 46 lines
+ {\voffset}{.25in}%
+ {\bindingoffset}{36pt}%
+ {11in}{8.5in}%
+}}
+
+% Use @smallbook to reset parameters for 7x9.25 trim size.
+\def\smallbook{{\globaldefs = 1
+ \parskip = 2pt plus 1pt
+ \textleading = 12pt
+ %
+ \internalpagesizes{7.5in}{5in}%
+ {-.2in}{0in}%
+ {\bindingoffset}{16pt}%
+ {9.25in}{7in}%
+ %
+ \lispnarrowing = 0.3in
+ \tolerance = 700
+ \contentsrightmargin = 0pt
+ \defbodyindent = .5cm
+}}
+
+% Use @smallerbook to reset parameters for 6x9 trim size.
+% (Just testing, parameters still in flux.)
+\def\smallerbook{{\globaldefs = 1
+ \parskip = 1.5pt plus 1pt
+ \textleading = 12pt
+ %
+ \internalpagesizes{7.4in}{4.8in}%
+ {-.2in}{-.4in}%
+ {0pt}{14pt}%
+ {9in}{6in}%
+ %
+ \lispnarrowing = 0.25in
+ \tolerance = 700
+ \contentsrightmargin = 0pt
+ \defbodyindent = .4cm
+}}
+
+% Use @afourpaper to print on European A4 paper.
+\def\afourpaper{{\globaldefs = 1
+ \parskip = 3pt plus 2pt minus 1pt
+ \textleading = 13.2pt
+ %
+ % Double-side printing via postscript on Laserjet 4050
+ % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm.
+ % To change the settings for a different printer or situation, adjust
+ % \normaloffset until the front-side and back-side texts align. Then
+ % do the same for \bindingoffset. You can set these for testing in
+ % your texinfo source file like this:
+ % @tex
+ % \global\normaloffset = -6mm
+ % \global\bindingoffset = 10mm
+ % @end tex
+ \internalpagesizes{673.2pt}{160mm}% that's 51 lines
+ {\voffset}{\hoffset}%
+ {\bindingoffset}{44pt}%
+ {297mm}{210mm}%
+ %
+ \tolerance = 700
+ \contentsrightmargin = 0pt
+ \defbodyindent = 5mm
+}}
+
+% Use @afivepaper to print on European A5 paper.
+% From romildo@urano.iceb.ufop.br, 2 July 2000.
+% He also recommends making @example and @lisp be small.
+\def\afivepaper{{\globaldefs = 1
+ \parskip = 2pt plus 1pt minus 0.1pt
+ \textleading = 12.5pt
+ %
+ \internalpagesizes{160mm}{120mm}%
+ {\voffset}{\hoffset}%
+ {\bindingoffset}{8pt}%
+ {210mm}{148mm}%
+ %
+ \lispnarrowing = 0.2in
+ \tolerance = 800
+ \contentsrightmargin = 0pt
+ \defbodyindent = 2mm
+ \tableindent = 12mm
+}}
+
+% A specific text layout, 24x15cm overall, intended for A4 paper.
+\def\afourlatex{{\globaldefs = 1
+ \afourpaper
+ \internalpagesizes{237mm}{150mm}%
+ {\voffset}{4.6mm}%
+ {\bindingoffset}{7mm}%
+ {297mm}{210mm}%
+ %
+ % Must explicitly reset to 0 because we call \afourpaper.
+ \globaldefs = 0
+}}
+
+% Use @afourwide to print on A4 paper in landscape format.
+\def\afourwide{{\globaldefs = 1
+ \afourpaper
+ \internalpagesizes{241mm}{165mm}%
+ {\voffset}{-2.95mm}%
+ {\bindingoffset}{7mm}%
+ {297mm}{210mm}%
+ \globaldefs = 0
+}}
+
+% @pagesizes TEXTHEIGHT[,TEXTWIDTH]
+% Perhaps we should allow setting the margins, \topskip, \parskip,
+% and/or leading, also. Or perhaps we should compute them somehow.
+%
+\parseargdef\pagesizes{\pagesizesyyy #1,,\finish}
+\def\pagesizesyyy#1,#2,#3\finish{{%
+ \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi
+ \globaldefs = 1
+ %
+ \parskip = 3pt plus 2pt minus 1pt
+ \setleading{\textleading}%
+ %
+ \dimen0 = #1\relax
+ \advance\dimen0 by \voffset
+ \advance\dimen0 by 1in % reference point for DVI is 1 inch from top of page
+ %
+ \dimen2 = \hsize
+ \advance\dimen2 by \normaloffset
+ \advance\dimen2 by 1in % reference point is 1 inch from left edge of page
+ %
+ \internalpagesizes{#1}{\hsize}%
+ {\voffset}{\normaloffset}%
+ {\bindingoffset}{44pt}%
+ {\dimen0}{\dimen2}%
+}}
+
+% Set default to letter.
+%
+\letterpaper
+
+% Default value of \hfuzz, for suppressing warnings about overfull hboxes.
+\hfuzz = 1pt
+
+
+\message{and turning on texinfo input format.}
+
+\def^^L{\par} % remove \outer, so ^L can appear in an @comment
+
+% DEL is a comment character, in case @c does not suffice.
+\catcode`\^^? = 14
+
+% Define macros to output various characters with catcode for normal text.
+\catcode`\"=\other \def\normaldoublequote{"}
+\catcode`\$=\other \def\normaldollar{$}%$ font-lock fix
+\catcode`\+=\other \def\normalplus{+}
+\catcode`\<=\other \def\normalless{<}
+\catcode`\>=\other \def\normalgreater{>}
+\catcode`\^=\other \def\normalcaret{^}
+\catcode`\_=\other \def\normalunderscore{_}
+\catcode`\|=\other \def\normalverticalbar{|}
+\catcode`\~=\other \def\normaltilde{~}
+
+% This macro is used to make a character print one way in \tt
+% (where it can probably be output as-is), and another way in other fonts,
+% where something hairier probably needs to be done.
+%
+% #1 is what to print if we are indeed using \tt; #2 is what to print
+% otherwise. Since all the Computer Modern typewriter fonts have zero
+% interword stretch (and shrink), and it is reasonable to expect all
+% typewriter fonts to have this, we can check that font parameter.
+%
+\def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi}
+
+% Same as above, but check for italic font. Actually this also catches
+% non-italic slanted fonts since it is impossible to distinguish them from
+% italic fonts. But since this is only used by $ and it uses \sl anyway
+% this is not a problem.
+\def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi}
+
+% Set catcodes for Texinfo file
+
+% Active characters for printing the wanted glyph.
+% Most of these we simply print from the \tt font, but for some, we can
+% use math or other variants that look better in normal text.
+%
+\catcode`\"=\active
+\def\activedoublequote{{\tt\char34}}
+\let"=\activedoublequote
+\catcode`\~=\active \def\activetilde{{\tt\char126}} \let~ = \activetilde
+\chardef\hatchar=`\^
+\catcode`\^=\active \def\activehat{{\tt \hatchar}} \let^ = \activehat
+
+\catcode`\_=\active
+\def_{\ifusingtt\normalunderscore\_}
+\def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em }
+\let\realunder=_
+
+\catcode`\|=\active \def|{{\tt\char124}}
+
+\chardef \less=`\<
+\catcode`\<=\active \def\activeless{{\tt \less}}\let< = \activeless
+\chardef \gtr=`\>
+\catcode`\>=\active \def\activegtr{{\tt \gtr}}\let> = \activegtr
+\catcode`\+=\active \def+{{\tt \char 43}}
+\catcode`\$=\active \def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix
+\catcode`\-=\active \let-=\normaldash
+
+
+% used for headline/footline in the output routine, in case the page
+% breaks in the middle of an @tex block.
+\def\texinfochars{%
+ \let< = \activeless
+ \let> = \activegtr
+ \let~ = \activetilde
+ \let^ = \activehat
+ \markupsetuplqdefault \markupsetuprqdefault
+ \let\b = \strong
+ \let\i = \smartitalic
+ % in principle, all other definitions in \tex have to be undone too.
+}
+
+% Used sometimes to turn off (effectively) the active characters even after
+% parsing them.
+\def\turnoffactive{%
+ \normalturnoffactive
+ \otherbackslash
+}
+
+\catcode`\@=0
+
+% \backslashcurfont outputs one backslash character in current font,
+% as in \char`\\.
+\global\chardef\backslashcurfont=`\\
+
+% \realbackslash is an actual character `\' with catcode other.
+{\catcode`\\=\other @gdef@realbackslash{\}}
+
+% In Texinfo, backslash is an active character; it prints the backslash
+% in fixed width font.
+\catcode`\\=\active % @ for escape char from now on.
+
+% Print a typewriter backslash. For math mode, we can't simply use
+% \backslashcurfont: the story here is that in math mode, the \char
+% of \backslashcurfont ends up printing the roman \ from the math symbol
+% font (because \char in math mode uses the \mathcode, and plain.tex
+% sets \mathcode`\\="026E). Hence we use an explicit \mathchar,
+% which is the decimal equivalent of "715c (class 7, e.g., use \fam;
+% ignored family value; char position "5C). We can't use " for the
+% usual hex value because it has already been made active.
+
+@def@ttbackslash{{@tt @ifmmode @mathchar29020 @else @backslashcurfont @fi}}
+@let@backslashchar = @ttbackslash % @backslashchar{} is for user documents.
+
+% \otherbackslash defines an active \ to be a literal `\' character with
+% catcode other.
+@gdef@otherbackslash{@let\=@realbackslash}
+
+% Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of
+% the literal character `\'.
+%
+{@catcode`- = @active
+ @gdef@normalturnoffactive{%
+ @passthroughcharstrue
+ @let-=@normaldash
+ @let"=@normaldoublequote
+ @let$=@normaldollar %$ font-lock fix
+ @let+=@normalplus
+ @let<=@normalless
+ @let>=@normalgreater
+ @let^=@normalcaret
+ @let_=@normalunderscore
+ @let|=@normalverticalbar
+ @let~=@normaltilde
+ @let\=@ttbackslash
+ @markupsetuplqdefault
+ @markupsetuprqdefault
+ @unsepspaces
+ }
+}
+
+% If a .fmt file is being used, characters that might appear in a file
+% name cannot be active until we have parsed the command line.
+% So turn them off again, and have @fixbackslash turn them back on.
+@catcode`+=@other @catcode`@_=@other
+
+% \enablebackslashhack - allow file to begin `\input texinfo'
+%
+% If a .fmt file is being used, we don't want the `\input texinfo' to show up.
+% That is what \eatinput is for; after that, the `\' should revert to printing
+% a backslash.
+% If the file did not have a `\input texinfo', then it is turned off after
+% the first line; otherwise the first `\' in the file would cause an error.
+% This is used on the very last line of this file, texinfo.tex.
+% We also use @c to call @fixbackslash, in case ends of lines are hidden.
+{
+@catcode`@^=7
+@catcode`@^^M=13@gdef@enablebackslashhack{%
+ @global@let\ = @eatinput%
+ @catcode`@^^M=13%
+ @def@c{@fixbackslash@c}%
+ % Definition for the newline at the end of this file.
+ @def ^^M{@let^^M@secondlinenl}%
+ % Definition for a newline in the main Texinfo file.
+ @gdef @secondlinenl{@fixbackslash}%
+ % In case the first line has a whole-line command on it
+ @let@originalparsearg@parsearg
+ @def@parsearg{@fixbackslash@originalparsearg}
+}}
+
+{@catcode`@^=7 @catcode`@^^M=13%
+@gdef@eatinput input texinfo#1^^M{@fixbackslash}}
+
+% Emergency active definition of newline, in case an active newline token
+% appears by mistake.
+{@catcode`@^=7 @catcode13=13%
+@gdef@enableemergencynewline{%
+ @gdef^^M{%
+ @par%
+ %<warning: active newline>@par%
+}}}
+
+
+@gdef@fixbackslash{%
+ @ifx\@eatinput @let\ = @ttbackslash @fi
+ @catcode13=5 % regular end of line
+ @enableemergencynewline
+ @let@c=@comment
+ @let@parsearg@originalparsearg
+ % Also turn back on active characters that might appear in the input
+ % file name, in case not using a pre-dumped format.
+ @catcode`+=@active
+ @catcode`@_=@active
+ %
+ % If texinfo.cnf is present on the system, read it.
+ % Useful for site-wide @afourpaper, etc. This macro, @fixbackslash, gets
+ % called at the beginning of every Texinfo file. Not opening texinfo.cnf
+ % directly in this file, texinfo.tex, makes it possible to make a format
+ % file for Texinfo.
+ %
+ @openin 1 texinfo.cnf
+ @ifeof 1 @else @input texinfo.cnf @fi
+ @closein 1
+}
+
+
+% Say @foo, not \foo, in error messages.
+@escapechar = `@@
+
+% These (along with & and #) are made active for url-breaking, so need
+% active definitions as the normal characters.
+@def@normaldot{.}
+@def@normalquest{?}
+@def@normalslash{/}
+
+% These look ok in all fonts, so just make them not special.
+% @hashchar{} gets its own user-level command, because of #line.
+@catcode`@& = @other @def@normalamp{&}
+@catcode`@# = @other @def@normalhash{#}
+@catcode`@% = @other @def@normalpercent{%}
+
+@let @hashchar = @normalhash
+
+@c Finally, make ` and ' active, so that txicodequoteundirected and
+@c txicodequotebacktick work right in, e.g., @w{@code{`foo'}}. If we
+@c don't make ` and ' active, @code will not get them as active chars.
+@c Do this last of all since we use ` in the previous @catcode assignments.
+@catcode`@'=@active
+@catcode`@`=@active
+@markupsetuplqdefault
+@markupsetuprqdefault
+
+@c Local variables:
+@c eval: (add-hook 'before-save-hook 'time-stamp)
+@c page-delimiter: "^\\\\message\\|emacs-page"
+@c time-stamp-start: "def\\\\texinfoversion{"
+@c time-stamp-format: "%:y-%02m-%02d.%02H"
+@c time-stamp-end: "}"
+@c End:
+
+@c vim:sw=2:
+
+@enablebackslashhack
- [nongnu] branch elpa/eat created (now 999c779bfc), ELPA Syncer, 2022/11/27
- [nongnu] elpa/eat ae4322fb45 06/12: Add documentation file generation code to Makefile, ELPA Syncer, 2022/11/27
- [nongnu] elpa/eat ed4d4ea6de 05/12: ; Fix outdated key sequences in README.org, ELPA Syncer, 2022/11/27
- [nongnu] elpa/eat 06e45d68b9 01/12: Implement the terminal,
ELPA Syncer <=
- [nongnu] elpa/eat 469b457238 04/12: ; Fix typo in code in Quelpa section in README.org, ELPA Syncer, 2022/11/27
- [nongnu] elpa/eat f65a6b0188 11/12: ; * eat.el: Use ?\s everywhere as space character, ELPA Syncer, 2022/11/27
- [nongnu] elpa/eat 3464e6b68c 03/12: ; Fix typo in summany line, ELPA Syncer, 2022/11/27
- [nongnu] elpa/eat 99dacbbf52 02/12: Add proper dependencies to targets in Makefile, ELPA Syncer, 2022/11/27
- [nongnu] elpa/eat 1035d99185 07/12: * Makefile (all): Remove "check" and "changelog.", ELPA Syncer, 2022/11/27
- [nongnu] elpa/eat 9eadacb243 10/12: ; * .dir-locals.el: No tabs and 70 column limit, ELPA Syncer, 2022/11/27
- [nongnu] elpa/eat ebdbba0c4b 09/12: Use third argument of posn-col-row on Emacs >= 29, ELPA Syncer, 2022/11/27
- [nongnu] elpa/eat 0cf652a591 08/12: ; Fix :url in Quelpa recipe in README.org, ELPA Syncer, 2022/11/27
- [nongnu] elpa/eat 999c779bfc 12/12: * .elpaignore: New file., ELPA Syncer, 2022/11/27