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