pax_global_header 0000666 0000000 0000000 00000000064 14751164310 0014514 g ustar 00root root 0000000 0000000 52 comment=4a7ec2c3ae5e0a341c015d8e96b07247e26d5e14
ne-3.3.4/ 0000775 0000000 0000000 00000000000 14751164310 0012125 5 ustar 00root root 0000000 0000000 ne-3.3.4/CHANGES 0000664 0000000 0000000 00000016156 14751164310 0013131 0 ustar 00root root 0000000 0000000 (This file, CHANGES, lists fixes. See NEWS for new features and enhancements.)
3.3.4 2025-02-06
* Improved YAML style file.
3.3.3 2023-10-19
* Fixed document reordering in SelectDoc requester when first or last
document wraps around to the other end of the document list.
* After closing the current document, the most recently active adjacent
document is chosen as the new current document.
3.3.2 2022-09-13
* Fixed issue where autocomplete could fail/crash with UTF-8 characters.
* Fixed issue where file requester could return incorrect path
to the selected file.
* Fixed makefile so that it correctly supports parallel builds. Thanks
to Emily Lucy Ishikawa for contributing this fix.
3.3.1 2021-05-16
* ttysize() is now invoked at start, improving user experience
in the termcap/ANSI versions.
* Fix sh syntax highlighting in double-quoted strings with complex
variable expressions.
* Fix java syntax highlighting for strings containing percent
signs not in a format specifier context.
* Fixed possible document name issue with docs from similarly named
directories.
3.3.0 2020-02-27
* Fixed possible crash when closing documents from within the SelectDoc
requester.
* Fixed adjusted position of mark, bookmark on same line as and to the right
of multi-line pastes.
* Fixed cursor position after reformatting a document's final paragraph.
* Paragraph n where n>1 is atomic; i.e. a single Undo reverts changes.
3.2.1 2019-10-02
* Fixed reordering items on multi-page requesters.
3.2.0 2019-09-30
* Fixed typo for "Home" key in default.keys.
* Add backtick support for MarkDown syntax (md.jsf)
* Fixed potentially infinite backwards search-and-replace.
3.1.2 2018-10-06
* Fixed visual glitch when characters spanning multiple columns are only
partially displayed on the left side.
* Fixed PARAGRAPH command.
* DeleteNextWord now deletes through the end of the next N words.
* Undoing DeleteNextWord/DeletePrevWord also restores cursor position.
3.1.1 2017-06-04
* Fixed visual glitch when deleting characters from long lines.
* Fixed broken behavior in WordWrap with leading whitespace.
* On CloseDoc (^Q), the next document now becomes active. It used to make
the previous document active.
3.1.0 2017-04-29
* Comments in .yaml files are highlighted more correctly.
* Determine better whether Find and FindRegExp patterns need to be recompiled
when switching buffers. Fix possible lockup after cancelled Find.
* Fixed missing screen update after ABOUT (regression introduced in 3.0.1).
Thanks to Brian Callahan for reporting this bug.
* Help command uses its optional parameter again.
* PasteVert behaves beyond the right end of lines again.
* AutoComplete keeps words containing apostrophes as single words ("can't"
instead of "can" and "t").
* Home, End, Tab keys now work in menus.
* Backward FindRegExp works again.
* Resizing the window in inputs, requesters is more sane.
* Restored correct keyboard input for 8-bit character sets.
* Update syntax code for feature parity with current Joe.
3.0.1 2015-06-22
* Updated version of GNU regex library provides 64-bit regular
expressions.
* We no longer assign stdin, fixing a *BSD compatibility problem (thanks
to Brian Callahan for reporting this issue).
* Fixed missing screen update at startup when no default autoprefs are
available.
3.0 2015-06-18
* Allow remapping of character SEQuences in .keys files.
* Global macros are now loaded from NE_GLOBAL_DIR/macros rather than
NE_GLOBAL_DIR (/usr/local/share/ne/macros vs /usr/local/share/ne).
2.6 2015-04-17
* Ancient bug with vertical clip edges fixed.
* AutoComplete occasionally would omit some completions.
* Exit attempts to save all modified documents even if one cannot be saved.
(It used to give up on the first error.)
* Avoid crash when resizing window while command prompt is active.
* Avoid SaveMacro optimization before Undo, called macros.
2.5 2012-12-24
* Pathologically long-running PARAGRAPH commands are now stoppable (^-\).
* Moved modified flag '*' to right end of status line to be easier to see.
* COPY, CUT, ERASE copy the correct text to the clip and do not crash
any more when in free form mode and cursor or mark is beyond the
end of a line.
* ReplaceOnce was returning a generic error code instead of success,
thus stopping macros.
2.4 2012-04-10
* CLOSEDOC and QUIT (^Q and Alt-Q) now close string requesters just like Esc.
* Macro calling macros are now stopped at an arbitrary depth of 32 calls.
* Last command of a loaded macro w/o trailing new-line now works.
* Vertical block selections where mark is below cursor select correct text.
* Mark right of a tab no longer moves when you change tab size.
* MARK and MARKVERT with no parameters always set rather than toggle the mark.
* Cursor no longer goes to start of line when you change tab size.
* A couple of operations in free form mode (joining the first line with
the following one and deleting a block with an extreme beyond the end
of file) should not cause crashes anymore.
* AUTOCOMPLETE could sometimes insert an inadvertent trailing "*".
2.3 2011-10-28
* Fixed ridiculously old bug when copying a block and the marker is
after the cursor.
* Tweak to syntax file for java.
* Changed build date in "About" to ISO YYYY-MM-DD format.
* Tweak suspend to signal process group; reduce chance of apparent hang.
* Instantaneous window resize works again.
* Now we display an error message (instead of crashing or returning an
I/O error) when a file is too large (>=2GiB).
* We no longer set the buffer filename in case of I/O error.
2.2 2011-01-23
* Fixed memory allocation macros in regex code from glibc to work on systems
that return NULL on zero-sized allocations.
* In makefile, made explicit the dependencies for regex.o.
* Fixed bug in "AdjustView R" that could push current character off screen.
* Ensure tab size remains less than half the window width when the window
changes size.
* Fixed buggy out-of-memory handling when loading files.
* Fixed buggy HTML/CSS syntax highlighting.
* Fixed wrong background line colour when clearing to the end of line.
* ToUpper and ToLower now stop when they reach the end of a document.
2.1 2010-03-17
* Fixed efficiency bug introduced with syntax highlighting: ne should now
be much more responsive along slow connections.
* Fixed old, bad, shameful bug: complex assertions were compiled into
the code even for the non-debug version. As a result, ne was deadly
slow on large files.
* Fixed small mistakes in the keyboard sequences displayed in the menus,
and small discrepancies between default.keys/default.menus and reality
(thanks to John Gabriele for having pointed out this).
* In some cases after a keyboard timeout an ESC character was left in
the keyboard buffer, causing weird behaviours.
* Probably really (this time) fixed problems with regexps matching empty strings.
* BackSpace and Delete behave better in FreeForm mode.
ne-3.3.4/COPYING 0000664 0000000 0000000 00000104513 14751164310 0013164 0 ustar 00root root 0000000 0000000 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
.
ne-3.3.4/INSTALL.md 0000664 0000000 0000000 00000011070 14751164310 0013554 0 ustar 00root root 0000000 0000000 Build and install into `/usr/local`
-----------------------------------
cd ne-x.y.z
make build install
Note that you must have adequate privileges.
Build and install into some alternative location
------------------------------------------------
Set the PREFIX variable, for example:
cd ne-x.y.z
make PREFIX=/home//opt build install
Makefiles
---------
There are three makefiles provided with the distribution: a top-level
makefile for easy build and installation, a low-level makefile in the
src directory that just builds ne's executable and provides some more
flexibility, and another low-level makefile in the doc directory.
The top-level makefile provides targets "source" (builds the standard
source distribution), "cygwin" (builds the cygwin distribution), "build"
(builds ne) and "install" (installs ne). The PREFIX make variable (see
above) decides where ne will be installed and which will be its global
directory. The LIBS variable can be used to change the name of the curses
library: by default, the low-level makefile uses "-lcurses", but depending
on your system you might prefer to specify "LIBS=-lncurses",
"LIBS=-lncursesw" or even "LIBS=-lterminfo".
For installation (i.e., "make install"), a POSIX compliant machine with a
terminfo database should be sufficient. Note that terminfo might come
bundled in a package named "curses", "ncurses" or some variant of it, and
you may need to install the ncurses development files. Choose the simplest
variant of this package, as ne does not actually use curses (a virtual
screen library), but just the underlying terminfo database.
You may receive some errors from the install-info command if you do not
have write access to the system infodir.bak file; these can be ignored.
Note, however, that creating a source distribution with the "source"
target requires a complete build, and in particular the presence of a
number of tools that manipulate texinfo files, as some of the source files
are generated from the documentation.
By default the build target does not produce the documentation PDF since
that depends on pdftex. If you still want to build the PDF you can do that
using `make alldocs build install`.
By playing with the low-level src makefile you have more options (as you can
first build using the low-level makefile and then use the install target
of the top-level makefile). If you have a termcap database, you should
specify "NE_TERMCAP=1" (i.e., type "make NE_TERMCAP=1"). It uses the GNU
version of termcap, whose sources are included (no library is needed). In
general, if a compilation via a simple "make" fails you should try these
variations in order until one of them succeeds:
make
make NE_POSIX=1
make NE_TERMCAP=1
make NE_POSIX=1 NE_TERMCAP=1
They use slightly different #define's to overcome the slight differences
among systems. If you have a problem with the local compiler and you have the
GNU C compiler installed, try "CC=gcc", and possibly also "OPTS=-ansi".
If you are compiling under Cygwin or similar emulations of UN*X running
under other operating systems, you can specify "NE_ANSI=1" to build a copy
of ne that by default will use built-in ANSI terminal control sequences.
By combining "NE_ANSI=1" and "NE_TERMCAP=1" you will get a version of ne
that needs no library, and moreover starts by default in ANSI mode.
Regardless of how ne was built, you can always override this choice by
invoking ne with one of the command line options "--ansi" or "--no-ansi".
ne can handle UTF-8, and supports multiple-column characters. The latter
requires some support from the system: you can disable wide-character,
multiple-column support with "NE_NOWCHAR=1".
If you cannot install ne as root, you can change the position of the
global preferences directory with "NE_GLOBAL_DIR=directory" (this is
done automatically by the top-level makefile on the basis of the PREFIX
variable). The global directory should contain automatic preferences files
for common extensions, and must contain the syntax directory provided with
ne's distribution, which contains joe's syntax definition files. In any
case, if the NE_GLOBAL_DIR environment variable is set ne will use its
value instead. The value ne ultimately uses, whether compiled in
or from the environment, is displayed at startup if no file is open.
Compatibility problems are also discussed in the documentation. Don't be
alarmed if you get a lot of warnings about signed vs. unsigned values.
If something does not work, please feel free to email us.
* seba ()
* Todd ()
*
ne-3.3.4/NEWS 0000664 0000000 0000000 00000024761 14751164310 0012636 0 ustar 00root root 0000000 0000000 (This file, NEWS, lists new features and enhancements. See CHANGES for fixes.)
3.3.4 2025-02-06
* Bracketed paste now consults the AutoIndent flag to determine whether to
preserve the cursor's initial horizontal offset. It's like a block
level AutoIndent rather than a line-by-line (and therefore
cumulative) AutoIndent. Being created internally by the Shift command,
the introduced leading space also honors the Tabs and ShiftTabs flags.
Like leading space introduced by AutoIndent, a single Undo removes the
bracketed paste's auto indented space; a second Undo removes the pasted
text itself.
Bracketed pastes now act like "normal" pastes, setting the '<' and
'>' bookmarks, and leaving the cursor at the head of the paste.
3.3.3 2023-10-19
* BracketedPaste support has been added; turns off AutoIndent when
pasting text from the system clipboard.
3.3.2 2022-09-13
* Paste and PasteVert now set two bookmarks designated '<' and '>' which
are set to the start and end of the most recently pasted text. Accessed
by the "GotoBookmark <" and "GotoBookmark >" commands. Two associated
menu items have been added to the "Search" menu.
* Added toml.jsf syntax.
* Support '_' in numeric constants, binary and hex constants, imaginary
numbers in go.jsf syntax.
* Now there is an uninstall target that uninstalls ne.
* Additionally load key bindings from ~/.ne/.keys-${TERM}
3.3.1 2021-05-16
* The makefile has been made more distro friendly by making it possible
to override all compiler and linker options.
* A workaround has been implemented to make --ansi (mostly) work when
compiling with ncurses. A change to ncurses in 2012 made tgoto() not
working unless tgetent()/setupterm() are called first.
3.3.0 2020-02-27
* You can now Save (^A) named documents from within the
SelectDoc (F4) requester.
* The mark, if set, is visible.
* Warning about opening a document with the same name as an existing
document now considers the fully qualified canonical path and name
of each rather than just their basenames.
* Status bar Modified flag (*) may also be underlined, indicating the
corresponding file's modification time has changed since the document
was loaded or saved.
3.2.0 2019-09-30
* Macros: They now work across multiple documents. You can cancel macro
recording, and append new recorded commands to your current macro.
* New documents inherit their predecessor's current macro, search, and
replace strings.
* Saving of read-only documents requires confirmation.
* Word completion now does not consider the word the cursor is currently
in, which helps in disambiguating.
* The option -h is now an alias for --help. Thanks to Lonnie Abelbeck for
implementing this feature.
* Add asterisk.jsf syntax.
* FIND in string inputs brings up a requester with your input history.
Use Enter to replace, TAB to insert into your input line; Escape to exit.
* Enhanced commands: MoveEOW, NextWord, and PrevWord can now move to either
end of the indicated word via optional '<' and '>' parameter modifiers.
* Requesters make better use of white space.
3.1.2 2018-10-06
* RepeatLast now accepts "Find" or "Replace" after its optional number so
you can explicitly repeat either operation even if the other was most
recently performed.
* We now use the xenl property to detect whether we can write on the last
column of the last line, thus making the status bar complete.
3.1.1 2017-06-04
* You can now CloseDoc (^Q) unmodified documents from within the
SelectDoc (F4) requester.
3.1.0 2017-04-29
* Large files that do not fit into memory are memory-mapped transparently
if enough disk space is available.
* Determine a "virtual extension" based on document contents and patterns
from ~/.ne/.extensions. Allows overrides of specified extensions only.
If no match, use .default#ap prefs.
* Status flag "!" indicates the last line of document is not empty, thus
the last line of the resulting saved file will not be terminated.
* Requesters by default no longer remove non-matching entries as you
enter characters. Use Insert or Delete keys within a requester
to toggle between these behaviors.
* Documents loaded through file requesters now have relative instead of
absolute paths.
* New NameConvert command (NC for short) changes current document's file
name from relative to absolute path or absolute to relative path.
* "SetBookmark ?" and "GotoBookmark ?" now prompt for the bookmark you
want to set/goto. The prompt also indicates which bookmarks are set.
* Command line option "--read-only" opens next document in read-only mode.
* Options "--read-only" and "+N,M" can be applied to piped input:
ls -l | ne --read-only +3,5 -
* Warn if SaveAs would overwrite an existing file.
* Suspend (Crtl-Z) works in prompts, requesters.
* "KeyCode" now takes optional parameter.
* Immediately/only after "Not Found", RepeatLast wraps around to the
other end of the document.
3.0 2015-06-18
* ne is now fully 64-bit, and needs to be compiled by a C99-compliant
compiler. Files can be of any size, provided that enough core memory
is available.
* ne is able to read from named pipes. You can even pipe content into
ne: it will be opened as the first document.
* It is now possible to interrupt searches (it used to be possible just
to interrupt global replace actions).
* Word wrap has been reverted to pre-2.5: it preserves just whitespace,
and it doesn't reparagraph at each line split or join. Paragraph keeps
the usual smart features preserving comments, etc.
2.6 2015-04-17
* Requesters (filenames, help, autocompletions, etc.) do progressive
"fuzzy match" by typed characters, backspace.
* Document requester (F4) opens with cursor on the current document.
Docs with unsaved changes are bold, flagged with "*". F2/F3 reorder
documents.
* Tab in Syntax command displays requester of extant syntax recognizers.
* You can now use AutoComplete in command line prompts.
* New SaveAll command saves all modified documents.
* Warns before saving over a file that was modified since the buffer was
last loaded or saved.
* `' joins (), {}, [], and <> as character pairs known to MatchBracket.
* New example macros: aspell, DeleteSOL.
* Add % to special leading characters for re-wrapping Paragraphs.
* WordWrap no longer waits for the cursor to hit the right margin; it wraps
on insertions and deletions now.
* Updates to ruby, erb, python, conf, many other syntax recognizers.
Added these recognizers from the Joe project: ant batch
classic_pascal comment_todo csharp debian differences elixir erlang
git-commit go haml htmlerb ini iptables js json md powershell pp
prolog properties sieve typescript whitespace yaml
2.5 2012-12-24
* New DelTabs flag, function separated from Tabs flag.
* Reformatting with the Paragraph command is now aware of common
leading characters used in comments: > # / * and spaces.
* WordWrap preserves leading characters identically to Paragraph.
2.4 2012-04-10
* New Shift command indents/outdents selected lines.
* Recorded macros preserve comments; indicate other included macros.
* New AtomicUndo command groups changes to be undone/redone as a group
instead of individually.
* For commands that have key bindings, Help displays them.
* New syntax highlighting for texinfo files.
2.3 2011-10-28
* Takes 'SEQ "sequence" KEYCODE' in ~/.ne/.keys to bind character
sequences to key codes.
2.2 2011-01-23
* Now ne is distributed under the GPLv3.
* Added AutoMatchBracket mode to indicate visible matching of
{}, (), [], <> pairs. Mode is 1 (brightness) by default.
* Bookmarks now remember/restore their vertical offsets in the window.
* Bookmark commands take -1/+1 to cycle through your bookmarks;
Use "UnsetBookmark *" to unset all bookmarks.
* New --binary command line option loads next listed file in binary mode.
May appear multiple times on the command line.
* +[N[,M]] command line option moves to N-th line, M-th column of next file loaded.
May appear multiple times on the command line.
* Mention http://groups.google.com/group/niceeditor in splash screen.
* About now displays splash screen in addition to its status bar message.
* In FastGUI mode, when highlighting menu items the cursor is now
positioned on the border of the menu rather than on the first letter
of menu items.
* New "tabs" syntax definition makes tabs visibly distinct from spaces.
* Recognize C99 integer types from and highlight accordingly.
2.1 2010-03-17
* New commands: KeyCode, DeleteNextWord, DeletePrevWord, AutoComplete,
InsertTab, Tabs, RequestOrder.
* Now we set the syntax when a file is saved with a (different) name.
* Now we correctly highlight control characters in the command line.
* Display request lists by columns ("RequestOrder 1") or rows ("RequestOrder 0").
* Allow window resizing during requests (file selection, help, AutoComplete).
* Sort filenames in dictionary order ("aa", "Ab", "ac", ...).
* Enter in Help places you on the right command in the command list.
* Only prompt once about identical filenames on startup.
* Consider "_" as a word character for word-oriented commands.
* Built-in filename extension to syntax mapping updates:
dtx -> tex, latex -> tex, sage -> python.
* Include new txt2tags syntax file from http://txt2tags.sourceforge.net/
* Replace reports the number of replacements (again).
* Display the global directory on startup even if it is not found.
* Default global directory changed from /usr/local/lib/ne to /usr/local/share/ne.
* Aborting an OpenNew doesn't leave you in a new blank document.
* AdjustView now takes optional number of lines or columns to
adjust by; swapped meaning of AdjustView 'C' and 'M' parameters.
* Read .keys and possibly .menus files from NE_GLOBAL_DIR on startup.
* More defensive reading of lines/columns from terminfo to avoid crashes
with XTerm on Mac OS X.
* More portable and robust window-size change detection.
* More parsimonious status-bar updates.
* StatusBar, FastGUI, VerboseMacros, and RequestOrder are not buffer specific, are
only saved in ~/.ne/.default#ap.
* Current syntax name is buffer specific; only saved in autoprefs, not ~/.ne/.default#ap.
* The Amiga is officially no longer supported (well, not exactly an
improvement...).
ne-3.3.4/README.md 0000664 0000000 0000000 00000002140 14751164310 0013401 0 ustar 00root root 0000000 0000000 Welcome to ne, the nice editor.
-------------------------------
ne is a free (GPL'd) text editor based on the POSIX standard that runs (we
hope) on almost any UN*X machine. ne is easy to use for the beginner, but
powerful and fully configurable for the wizard, and most sparing in its
resource usage. See the [manual](https://ne.di.unimi.it/docs/index.html)
for some highlights of ne's features.
ne is distributed under the GNU Public License (see COPYING). The
INSTALL.md file contains detailed installation instructions. The version
of this distribution of ne can be found in src/version.h.
Documentation (in the "doc" directory) is provided in the form of a
texinfo file. It can be printed as a manual using TeX and GNU's
texinfo.tex macro package, or turned into a hypertext document using GNU's
makeinfo. The directory contains several pre-compiled printable and
hypertext versions of the documentation.
If something does not work, please feel free to email us, or write
to the mailing list.
* seba ()
* Todd ()
*
ne-3.3.4/doc/ 0000775 0000000 0000000 00000000000 14751164310 0012672 5 ustar 00root root 0000000 0000000 ne-3.3.4/doc/.gitignore 0000664 0000000 0000000 00000000205 14751164310 0014657 0 ustar 00root root 0000000 0000000 version.texinfo
html/
*.gz
ne.txt
ne.txt-e
ne.aux
ne.cm
ne.cp
ne.cms
ne.cps
ne.dvi
ne.fn
ne.ky
ne.log
ne.pg
ne.toc
ne.tp
ne.vr
*.pdf
ne-3.3.4/doc/default.keys 0000664 0000000 0000000 00000013225 14751164310 0015216 0 ustar 00root root 0000000 0000000 #
# This is a ready-made keyboard binding configuration file that replicates
# ne's built-in bindings. You can use it as a start for modifications.
#
# Note: the keycode numbers in this file are case-insensitive hexadecimal
# values as reported by the KeyCode command. The "0x" prefixes are
# optional and omitted. Valid range is from 0 to 1FF.
#
# Cursor/editing keys
#
# Cursor up
KEY 101 LineUp
# Cursor down
KEY 102 LineDown
# Cursor left
KEY 103 MoveLeft
# Cursor right
KEY 104 MoveRight
# Cursor home
KEY 105 MoveIncUp
# Cursor end
KEY 106 MoveIncDown
# Cursor next page
KEY 107 NextPage
# Cursor prev page
KEY 108 PrevPage
# Scroll forward
KEY 109 LineDown
# Scroll backward
KEY 10A LineUp
# Clear to end of line
KEY 110 DeleteEOL
# Clear to end of screen
KEY 111 NOP
# Backspace
KEY 112 BackSpace
# Delete line
KEY 113 DeleteLine
# Insert line
KEY 114 InsertLine
# Delete char
KEY 115 DeleteChar
# Insert char
KEY 116 Insert
# Exit insert mode
KEY 117 NOP
# Clear
KEY 118 Clear
# Keypad A1
KEY 120 MoveSOF
# Keypad A3
KEY 121 PrevPage
# Keypad B2
KEY 122 ToggleSEOL
# Keypad C1
KEY 123 MoveEOF
# Keypad C3
KEY 124 NextPage
#
KEY 125 Exec
# Clear all tabs
KEY 128 NOP
# Clear tab
KEY 129 NOP
# Set tab
KEY 12A NOP
#
# Function keys
#
# F0
KEY 140 Escape
# F1
KEY 141 Escape
# F2
KEY 142 NextDoc
# F3
KEY 143 PrevDoc
# F4
KEY 144 SelectDoc
# F5
KEY 145 Undo
# F6
KEY 146 Redo
# F7
KEY 147 PrevWord
# F8
KEY 148 NextWord
# F9
KEY 149 PlayOnce
# F10
KEY 14A Help
KEY 15F DeletePrevWord
KEY 160 DeleteNextWord
#
# CONTROL-letter shortcuts
#
# CONTROL-@
KEY 0 MarkVert
# CONTROL-a
KEY 1 MoveSOL
# CONTROL-b
KEY 2 Mark
# CONTROL-c
KEY 3 Copy
# CONTROL-d
KEY 4 NewDoc
# CONTROL-e
KEY 5 MoveEOL
# CONTROL-f
KEY 6 Find
# CONTROL-g
KEY 7 RepeatLast
# CONTROL-h
KEY 8 BackSpace
# CONTROL-i cannot be redefined (it's TAB)
# CONTROL-j
KEY A GotoLine
# CONTROL-k
KEY B Exec
# CONTROL-l
KEY C Refresh
# CONTROL-m cannot be redefined (it's RETURN)
# CONTROL-n
KEY E NextPage
# CONTROL-o
KEY F Open
# CONTROL-p
KEY 10 PrevPage
# CONTROL-q
KEY 11 CloseDoc
# CONTROL-r
KEY 12 Replace
# CONTROL-s
KEY 13 Save
# CONTROL-t
KEY 14 Record
# CONTROL-u
KEY 15 UndelLine
# CONTROL-v
KEY 16 Paste
# CONTROL-w
KEY 17 PasteVert
# CONTROL-x
KEY 18 Cut
# CONTROL-y
KEY 19 DeleteLine
# CONTROL-z
KEY 1A Suspend
# CONTROL-[ cannot be redefined (it's Escape)
# CONTROL-\ cannot be redefined (it's the interrupt character)
# CONTROL-]
KEY 1D MatchBracket
# CONTROL-^
KEY 1E AdjustView
# CONTROL-_
KEY 1F FindRegExp
# Delete
KEY 7F DeleteChar
KEY 1cf OpenClip
KEY 1d0 Paragraph
KEY 1d1 Quit
KEY 1d2 Redo
KEY 1d3 SaveClip
KEY 1d4 Through
KEY 1d5 Undo
KEY 1d6 ToUpper
KEY 1d7 WordWrap
KEY 1d8 Exit
KEY 1d9 DeleteEOL
KEY 1da CRLF
#
# The following bindings set up the same actions for a number of correlated
#codes. This guarantees that the user is able to use the '~' bindings with
# CTRL-META, META or just a prefixed ESC.
#
# CONTROL-META-a, META-a, ESC A and ESC a
KEY 81 MoveSOF
KEY 181 MoveSOF
KEY 1C1 MoveSOF
KEY 1E1 MoveSOF
# CONTROL-META-b, META-b, ESC B and ESC b
KEY 82 PrevWord
KEY 182 PrevWord
KEY 1C2 PrevWord
KEY 1E2 PrevWord
# CONTROL-META-c, META-c, ESC C and ESC c
KEY 83 AdjustView C
KEY 183 AdjustView C
KEY 1C3 AdjustView C
KEY 1E3 AdjustView C
# CONTROL-META-d, META-d, ESC D and ESC d
KEY 84 NextDoc
KEY 184 NextDoc
KEY 1C4 NextDoc
KEY 1E4 NextDoc
# CONTROL-META-e, META-e, ESC E and ESC e
KEY 85 MoveEOF
KEY 185 MoveEOF
KEY 1C5 MoveEOF
KEY 1E5 MoveEOF
# CONTROL-META-f, META-f, ESC F and ESC f
KEY 86 NextWord
KEY 186 NextWord
KEY 1C6 NextWord
KEY 1E6 NextWord
# CONTROL-META-g, META-g, ESC G and ESC g
KEY 87 GotoBookMark
KEY 187 GotoBookMark
KEY 1C7 GotoBookMark
KEY 1E7 GotoBookMark
# CONTROL-META-i, META-i, ESC I and ESC i
KEY 89 AutoComplete
KEY 189 AutoComplete
KEY 1C9 AutoComplete
KEY 1E9 AutoComplete
# CONTROL-META-j, META-j, ESC J and ESC j
KEY 8A GotoColumn
KEY 18A GotoColumn
KEY 1CA GotoColumn
KEY 1EA GotoColumn
# CONTROL-META-k, META-k, ESC K and ESC k
KEY 8B SetBookmark
KEY 18B SetBookmark
KEY 1CB SetBookmark
KEY 1EB SetBookmark
# CONTROL-META-l, META-l, ESC L and ESC l
KEY 8C ToLower
KEY 18C ToLower
KEY 1CC ToLower
KEY 1EC ToLower
# CONTROL-META-m, META-m, ESC M and ESC m
KEY 8D Play 1
KEY 18D Play 1
KEY 1CD Play 1
KEY 1ED Play 1
# CONTROL-META-n, META-n, ESC N and ESC n
KEY 8E OpenNew
KEY 18E OpenNew
KEY 1CE OpenNew
KEY 1EE OpenNew
# CONTROL-META-o, META-o, ESC O and ESC o
KEY 8F OpenClip
KEY 18F OpenClip
KEY 1CF OpenClip
KEY 1EF OpenClip
# CONTROL-META-p, META-p, ESC P and ESC p
KEY 90 Paragraph
KEY 190 Paragraph
KEY 1D0 Paragraph
KEY 1F0 Paragraph
# CONTROL-META-q, META-q, ESC Q and ESC q
KEY 91 Quit
KEY 191 Quit
KEY 1D1 Quit
KEY 1F1 Quit
# CONTROL-META-r, META-r, ESC R and ESC r
KEY 92 Redo
KEY 192 Redo
KEY 1D2 Redo
KEY 1F2 Redo
# CONTROL-META-s, META-s, ESC S and ESC s
KEY 93 SaveClip
KEY 193 SaveClip
KEY 1D3 SaveClip
KEY 1F3 SaveClip
# CONTROL-META-t, META-t, ESC T and ESC t
KEY 94 Through
KEY 194 Through
KEY 1D4 Through
KEY 1F4 Through
# CONTROL-META-u, META-u, ESC U and ESC u
KEY 95 Undo
KEY 195 Undo
KEY 1D5 Undo
KEY 1F5 Undo
# CONTROL-META-v, META-v, ESC V and ESC v
KEY 96 ToUpper
KEY 196 ToUpper
KEY 1D6 ToUpper
KEY 1F6 ToUpper
# CONTROL-META-w, META-w, ESC W and ESC w
KEY 97 WordWrap
KEY 197 WordWrap
KEY 1D7 WordWrap
KEY 1F7 WordWrap
# CONTROL-META-x, META-x, ESC X and ESC x
KEY 98 Exit
KEY 198 Exit
KEY 1D8 Exit
KEY 1F8 Exit
# CONTROL-META-y, META-y, ESC Y and ESC y
KEY 99 DeleteEOL
KEY 199 DeleteEOL
KEY 1D9 DeleteEOL
KEY 1F9 DeleteEOL
# CONTROL-META-z, META-z, ESC Z and ESC z
KEY 9A CRLF
KEY 19A CRLF
KEY 1DA CRLF
KEY 1FA CRLF
ne-3.3.4/doc/default.menus 0000664 0000000 0000000 00000006645 14751164310 0015402 0 ustar 00root root 0000000 0000000 #
# This is a ready-made menu configuration file that replicates ne's built-in
# menus.
#
MENU "File"
ITEM "Open... ^O" Open
ITEM "Open New... [N" OpenNew
ITEM "Save ^S" Save
ITEM "Save As... " SaveAs
ITEM "Quit Now [Q" Quit
ITEM "Save&Exit [X" Exit
ITEM "About " About
MENU "Documents"
ITEM "New ^D" NewDoc
ITEM "Clear " Clear
ITEM "Close ^Q" CloseDoc
ITEM "Next f2/[D" NextDoc
ITEM "Prev f3" PrevDoc
ITEM "Select... f4" SelectDoc
MENU "Edit"
ITEM "Mark Block ^B" Mark
ITEM "Cut ^X" Cut
ITEM "Copy ^C" Copy
ITEM "Paste ^V" Paste
ITEM "Mark Vert ^@" MarkVert
ITEM "Paste Vert ^W" PasteVert
ITEM "Through [T" Through
ITEM "Erase " Erase
ITEM "Delete EOL [Y" DeleteEOL
ITEM "Delete Line ^Y" DeleteLine
ITEM "Undel Line ^U" UndelLine
ITEM "Del Prev Word " DeletePrevWord
ITEM "Del Next Word " DeleteNextWord
ITEM "Open Clip [O" OpenClip
ITEM "Save Clip [S" SaveClip
MENU "Search"
ITEM "Find... ^F" Find
ITEM "Find RegExp... ^_" FindRegExp
ITEM "Replace... ^R" Replace
ITEM "Replace Once... " ReplaceOnce
ITEM "Replace All... " ReplaceAll
ITEM "Repeat Last ^G" RepeatLast
ITEM "Goto Line... ^J" GotoLine
ITEM "Goto Col... [J" GotoColumn
ITEM "Goto Mark " GotoMark
ITEM "Goto Start Of Paste" GotoBookmark <
ITEM "Goto End Of Paste " GotoBookmark >
ITEM "Match Bracket ^]" MatchBracket
ITEM "Set Bookmark [K" SetBookmark
ITEM "Goto Bookmark [G" GotoBookmark
MENU "Macros"
ITEM "Start/Stop Rec ^T" Record
ITEM "Play Once f9/[M" Play 1
ITEM "Play Many... " Play
ITEM "Play Macro... " Macro
ITEM "Open Macro... " OpenMacro
ITEM "Save Macro... " SaveMacro
MENU "Extras"
ITEM "Exec... ^K" Exec
ITEM "Suspend ^Z" Suspend
ITEM "Help... f10" Help
ITEM "Refresh ^L" Refresh
ITEM "Undo f5/[U" Undo
ITEM "Redo f6/[R" Redo
ITEM "Center " Center
ITEM "Shift Right " Shift
ITEM "Shift Left " Shift <
ITEM "Paragraph [P" Paragraph
ITEM "ToUpper [V" ToUpper
ITEM "ToLower [L" ToLower
ITEM "Capitalize " Capitalize
ITEM "AutoComplete [I" AutoComplete
ITEM "UTF-8 " UTF8
MENU "Navigation"
ITEM "Move Left " MoveLeft
ITEM "Move Right " MoveRight
ITEM "Line Up " LineUp
ITEM "Line Down " LineDown
ITEM "Prev Page ^P" PrevPage
ITEM "Next Page ^N" NextPage
ITEM "Page Up " PageUp
ITEM "Page Down " PageDown
ITEM "Start Of File [A" MoveSOF
ITEM "End Of File [E" MoveEOF
ITEM "Start Of Line ^A" MoveSOL
ITEM "End Of Line ^E" MoveEOL
ITEM "Top Of Screen " MoveTOS
ITEM "Bottom Of Screen" MoveBOS
ITEM "Adjust View ^^" AdjustView
ITEM "Middle View [C" AdjustView M
ITEM "Incr Up Home" MoveIncUp
ITEM "Incr Down End" MoveIncDown
ITEM "Prev Word f7/[B" PrevWord
ITEM "Next Word f8/[F" NextWord
MENU "Prefs"
ITEM "Tab Size... " TabSize
ITEM "Tabs/Spaces " Tabs
ITEM "Insert/Over Ins" Insert
ITEM "Free Form " FreeForm
ITEM "Status Bar " StatusBar
ITEM "Hex Code " HexCode
ITEM "Fast GUI " FastGUI
ITEM "Word Wrap [W" WordWrap
ITEM "Right Margin " RightMargin
ITEM "Auto Indent " AutoIndent
ITEM "Request Order " RequestOrder
ITEM "Preserve CR " PreserveCR
ITEM "Save CR/LF [Z" CRLF
ITEM "Load Prefs... " LoadPrefs
ITEM "Save Prefs... " SavePrefs
ITEM "Load Auto Prefs " LoadAutoPrefs
ITEM "Save Auto Prefs " SaveAutoPrefs
ITEM "Save Def Prefs " SaveDefPrefs
ne-3.3.4/doc/makefile 0000664 0000000 0000000 00000002554 14751164310 0014400 0 ustar 00root root 0000000 0000000 #
# This Makefile has the basic commands for converting
# ne.texinfo into a plain text file (ne.txt), an
# info document set (ne.info*), a set of HTML files (html/) and a PDF
# document (ne.pdf).
#
DOCS=ne.info.gz ne.txt html/index.html
docs: $(DOCS)
pdf: ne.pdf
install: docs
cp * ../../../doc
version.texinfo:
( cd .. ; $(MAKE) version )
ne.txt: ne.texinfo version.texinfo
(makeinfo --plaintext --no-headers ne.texinfo -o ne.txt || makeinfo --no-headers ne.texinfo -o ne.txt)
sed -i -e "s/\`/'/g" ne.txt
sed -i -e "s/''''/'\`''/g" ne.txt
sed -i -e "s/'ne'/ne/g" ne.txt
ne.info.gz: ne.texinfo version.texinfo
makeinfo ne.texinfo
sed -i -e "s/\`/'/g" ne.info
sed -i -e "s/''''/'\`''/g" ne.info
sed -i -e "s/'ne'/ne/g" ne.info
rm -f ne.info*gz
gzip -9 ne.info*
texinfo.cnf: texinfo.cnf.in
cp texinfo.cnf.in texinfo.cnf
if locale -c height | grep 279 ; then \
echo "@c -- US Letter detected by makefile." >> texinfo.cnf ;\
else \
echo "@c -- US Letter not detected by makefile; selecting A4." >> texinfo.cnf ;\
echo "@afourpaper" >> texinfo.cnf ;\
fi
ne.pdf: ne.texinfo version.texinfo texinfo.cnf
pdftex ne.texinfo
html/index.html: ne.texinfo version.texinfo
-rm -fr html
makeinfo --html -o html ne.texinfo
clean:
rm -f ne.txt ne.info* ne.ps ne.pdf ne.aux ne.cms ne.cps ne.fn ne.log ne.tp ne.cm ne.cp ne.dvi ne.ky ne.pg ne.toc ne.vr
rm -rf html/
ne-3.3.4/doc/ne.1 0000664 0000000 0000000 00000004220 14751164310 0013354 0 ustar 00root root 0000000 0000000 .TH NE 1 "by Sebastiano Vigna and Todd M. Lewis" "ne" \" -*- nroff -*-
.SH NAME
ne \- A nice editor
.SH SYNOPSIS
.B ne [options] files
.TP
Options:
[\-\-help]
[\-\-]
[+[N[,M]]]
[\-\-binary]
[\-\-read-only]
[\-\-utf8]
[\-\-no\-utf8]
[\-\-ansi]
[\-\-no\-ansi]
[\-\-no\-config]
[\-\-no\-syntax]
[\-\-prefs ext]
[\-\-keys key\-configuration\-file]
[\-\-menus menu\-configuration\-file]
[\-\-macro macro\-file]
.SH DESCRIPTION
\fBne\fR is a free text editor that runs on (hopefully almost) any UN*X
machine. \fBne\fR is easy to use for the beginner, but powerful and fully
configurable for the wizard, and most sparing in its resource usage.
This documentation is incomplete. The Texinfo/info/HTML/PDF documentation is the
authoritative source (try \fBinfo ne\fR or \fBinfo \-f ne\fR).
.SS OPTIONS
.TP
.I "--help"
Prints a help message.
.TP
.I "--"
Next token is a filename.
May appear more than once.
.TP
.I "+[N[,M]]"
Moves to the last or N-th line, first or M-th column of the next named file.
May appear more than once.
.TP
.TP
.I "--binary"
Load the next named file in binary mode.
May appear more than once.
.TP
.I "--read-only"
Load the next file in read-only mode.
May appear more than once.
.TP
.I "--utf8"
Use UTF-8 I/O.
.TP
.I "--no-utf8"
Do not use UTF-8 I/O.
.TP
.I "--ansi"
Use the built-in ANSI sequences.
.TP
.I "--no-ansi"
Never use the built-in ANSI sequences.
.TP
.I "--no-config"
Skip reading the menu and keyboard configuration files.
.TP
.I "--no-syntax"
Disable syntax-highlighting support.
.TP
.I "--prefs ext"
Set autoprefs for the provided extension before loading the first file.
.TP
.I "--keys key-configuration-file"
Use the specified keyboard configuration file.
.TP
.I "--menus menu-configuration-file"
Use the specified menu configuration file.
.TP
.I "--macro macro-file"
Execute the given macro after startup.
.SS USAGE
Start \fBne\fR, then use escape, escape-escape or F1 to access the menus.
.SS BUGS
Please send bug reports to Sebastiano Vigna or Todd Lewis .
.SS AUTHORS
\fBne\fR was originally written by Sebastiano Vigna. Todd M. Lewis added
several new features. Daniele Filaretti helped with syntax highlighting.
ne-3.3.4/doc/ne.texinfo 0000664 0000000 0000000 00000723005 14751164310 0014701 0 ustar 00root root 0000000 0000000 \input texinfo @c -*-texinfo-*-
@setfilename ne.info
@settitle @code{ne}'s manual
@dircategory Text creation and manipulation
@direntry
* ne: (ne). The nice editor
@end direntry
@defindex cm
@include version.texinfo
@ignore
This file, besides being a normal Texinfo file, allows generation of
on-line help and other crucial parts of the C source code for ne. The
program info2src.pl (a *very* dirty hack) does this. See the Makefile
to see how this is done.
Conventions:
@file for files, directories, etc. (but extensions, etc. use @samp);
@code for command line stuff, internal commands, environment variables;
@samp for menu/menu item names, and anything left that's literal.
@end ignore
@ifinfo
This file documents ne @value{VERSION}, a free text editor for @sc{un*x}.
Copyright (C) 1993-1998 Sebastiano Vigna.
Copyright (C) 1999-@value{RELEASE_YEAR} Todd M. Lewis and Sebastiano Vigna.
Permission is granted to make and distribute verbatim copies of this
manual provided the copyright notice and this permission notice are
preserved on all copies.
@ignore
Permission is granted to process this file through TeX and print the
results, provided the printed document carries copying permission
notice identical to this one except for the removal of this paragraph
(this paragraph not being relevant to the printed manual).
@end ignore
Permission is granted to copy and distribute modified versions of this
manual under the conditions for verbatim copying, provided that the entire
resulting derived work is distributed under the terms of a permission
notice identical to this one.
Permission is granted to copy and distribute translations of this manual
into another language, under the above conditions for modified versions,
except that this permission notice may be stated in a translation approved
by the Free Software Foundation.
@end ifinfo
@setchapternewpage odd
@finalout
@titlepage
@title ne
@subtitle A nice editor
@subtitle Version @value{VERSION}
@author by Sebastiano Vigna and Todd M. Lewis
@page
@vskip 0pt plus 1filll
Copyright @copyright{} 1993-1998 Sebastiano Vigna@*
Copyright @copyright{} 1999-@value{RELEASE_YEAR} Todd M. Lewis and Sebastiano Vigna
Permission is granted to make and distribute verbatim copies of
this manual provided the copyright notice and this permission notice
are preserved on all copies.
Permission is granted to copy and distribute modified versions of this
manual under the conditions for verbatim copying, provided that the entire
resulting derived work is distributed under the terms of a permission
notice identical to this one.
Permission is granted to copy and distribute translations of this manual
into another language, under the above conditions for modified versions,
except that this permission notice may be stated in a translation approved
by the Free Software Foundation.
@end titlepage
@contents
@ifnottex
@node Top
@top
This file describes @code{ne} version @value{VERSION}, a free text editor that runs
(we hope) on almost any @sc{un*x} machine. @code{ne} is easy to use for
the beginner, but powerful and fully configurable for the wizard, and
most sparing in its resource use.
@noindent Copyright (C) 1993-1998 Sebastiano Vigna@*
@noindent Copyright (C) 1999-@value{RELEASE_YEAR} Todd M. Lewis and Sebastiano Vigna
@end ifnottex
@menu
* Introduction::
* Basics::
* Reference::
* Commands::
* Configuration::
* Hints and Tricks::
* Motivations and Design::
* The Encoding Mess::
* History::
* Portability Problems::
* Acknowledgments::
* Concept Index::
* Command Index::
@end menu
@node Introduction
@chapter Introduction
@cindex LITHP
@cindex Features
@cindex vi
@cindex POSIX
@cindex terminfo
@cindex termcap
@code{ne} is a full screen text editor for @sc{un*x} (or, more
precisely, for @sc{posix}: @pxref{Motivations and Design}). I came to
the decision to write such an editor after getting completely sick of
@code{vi}, both from a feature and user interface point of view. I
needed an editor that I could use through a @code{telnet} connection or
a phone line and that wouldn't fire off a full-blown
@sc{lithp}@footnote{This otherwise unremarkable language is
distinguished by the absence of an @samp{s} in its character set; users
must substitute @samp{th}. @sc{lithp} is said to be useful in
protheththing lithtth.} operating system just to do some editing.
A concise overview of the main features follows:
@itemize @bullet{}
@item
three user interfaces: control keystrokes, command line, and menus;
keystrokes and menus are completely configurable;
@item
syntax highlighting;
@item
full support for UTF-8 files, including multiple-column characters;
@item
64-bit file/line length;
@item
simple scripting language where scripts can be generated @i{via} an
idiotproof record/play method;
@item
unlimited undo/redo capability (can be disabled with a command);
@item
automatic preferences system based on the extension of the file name being
edited or regex content matching;
@item
automatic completion of prefixes using words in your documents as dictionary;
@item
a file requester with completion features for easy file retrieval;
@item
extended regular expression search and replace @`a la @code{emacs} and
@code{vi};
@item
a very compact memory model---you can
easily load and modify very large files, even if they do not fit your core memory;
@item
editing of binary files.
@end itemize
@node Basics
@chapter Basics
@noindent @i{Simple things should be simple. Complex things should be
possible.} (Alan Kay)
@code{ne}'s user interface is essentially a compromise between the limits of
character driven terminals and the power of GUIs. While @emph{real} editing is
done without ever touching a mouse, it is also true that editing should be
doable without ever touching a manual. These two conflicting goals can be
accommodated easily in a single program if we can offer a series of interfaces
that allow for differentiated use.
In other words, it is unlikely that an @code{ne} wizard will ever have to
activate a menu, but to become an expert user you just have to use
the menus enough to learn by heart the most important keystrokes. A good
manual is always invaluable when one comes to configuration and esoteric
features, but few users will ever need to change @code{ne}'s menus or
key bindings.
Another important thing is that powerful features should always be
accessible, at least in part, to every user. The average user should be
able to record his actions, replay them, and save them in a humanly
readable format for further use and editing.
In the following sections we shall take a quick tour of @code{ne}'s features.
@menu
* Terminology::
* Starting::
* Loading and Saving::
* Editing::
* Basic Preferences::
* Basic Macros::
* More Advanced Features::
@end menu
@node Terminology
@section Terminology
@cindex File
@cindex Document
@cindex Buffer
In this section we explain and contrast some of the terms
@code{ne} uses. Understanding these distinctions will go a long way
towards making the rest of this manual make sense.
A @dfn{file} is a group of bytes stored on disk. This may seem rather
obvious, but the important distinction here is that @code{ne} does not
edit files; it edits @dfn{documents}.
A @dfn{document} is what @code{ne} calls one of the ``text thingies''
that you can edit. It is a sequence of lines of text in the computer's
memory---not on disk.@footnote{Actually, it can be in a region of the disk
used to simulate a larger memory. @code{ne} will switch to such a simulation whenever
the computer's memory is not sufficient for editing a file. This means, in particular,
that out-of-memory errors can be caused by insufficient disk space, too.}
Documents can be created, edited, saved in
files, loaded from files, discarded, @i{et cetera}. When a
document is loaded from or saved to a file, it remains
associated with that file by name until the document is
either closed or saved to a different file. Interactions between
documents and files are handled by the commands under the
@samp{File} menu. The @samp{Documents} menu commands only deal with
documents. @xref{Menus}.
Internally, @code{ne} holds its documents' text in @dfn{buffers}. A
@dfn{buffer} is a chunk of memory in which @code{ne} holds text.
In addition to each document's text, buffers hold any loaded
or recorded macros, undo records, a copy of your last deleted line,
a copy of all your previous responses to long input, and several
other things.
@node Starting
@section Starting
@cindex Keyboard usage
@cindex Menu usage
@cindex Shortcuts
@cindex Status bar
@cindex Menu bar
@cindex Control key
@cindex Meta key
@cindex Command line
To start @code{ne}, just type @samp{ne} and press @key{Return}. If you
want to edit some specific file(s), you can put their name(s) on the command
line just after the command name, as for any @sc{un*x} command. The
screen of your terminal will be cleared (or filled with text loaded
from the first file you specified). You can also pipe the result of a
command directly into @code{ne}: it will be loaded and opened as the first
document. See @ref{Arguments} for other command line options.
Writing text is pretty straightforward: if your terminal is properly
configured, every key will (should) do what you expect. Alphabetic
characters insert text, cursor keys move the cursor, and so on. You can
use the @key{Delete} and @key{Backspace} key to perform corrections. If
your keyboard has an @key{Insert} key, you can use it to @dfn{toggle}
(switch from on to off, or vice versa) insert mode. In general,
@code{ne} tries to squeeze everything it can from your
keyboard. Function keys and special movement keys should work
flawlessly if your terminal is properly configured. If not, complain to
your system administrator. If that doesn't help, see @ref{Key Bindings}.
At the bottom of the screen, you will see a line containing some numbers and
letters. This is called the @dfn{status bar} because it reports to you part of
the internal state of the editor. At startup, the status bar has the following form:
@example
L: 1 C: 1 12% ia----pvu-t------@@A*
@end example
@noindent (the numbers could be different, and a file name could be shown as
last item instead of @samp{}). You probably already guessed
that the numbers after @samp{L:} and @samp{C:} are your cursor's line and column
numbers, respectively, whereas the percentage indicates approximately
your position in the file. The small letters represent user flags that
you can turn on and off. In particular, @samp{i} tells you that insert
mode is on, while @samp{p} tells that the automatic preferences system
is activated. The @samp{*} means this document has not been saved. For a
thorough explanation of the meaning of the flags on the status bar, see
@ref{The Status Bar}.
Once you are accustomed to cursor movement and line editing, it is time to
press @key{f1} (the first function key), or in case your keyboard does not
have such a key, @key{Escape}. Immediately, the @dfn{menu bar} will appear,
and the first menu will be drawn. (If you find yourself waiting for the menu
to appear, you can press @key{Escape} twice in a row.) You can now move
around menus and menu items by pressing the cursor keys. Moreover, a lower
case alphabetic key will move to the next item in the current menu whose name starts with that
letter, and an upper case alphabetic key will move to the next menu whose
name starts with that letter.
Moving around the menus should give you an idea of the capabilities of
@code{ne}. If you want to save your work, you should use the @samp{Save As...}
item from the @samp{File} menu. Menus are fully discussed in @ref{Menus}.
When you want to exit from the menu system, press @key{f1} (or @key{Escape})
again. If instead you prefer to choose a command and execute it, move
to the respective menu item and press @key{Return}.
At the end of several menu items you will find strange symbols like
@kbd{^A} or @key{f1}. They represent @dfn{shortcuts} for the respective
menu items. In other words, instead of activating, selecting and
executing a menu item, which can take seconds, you can simply press a
couple of keys. The symbol @samp{^} in front of a character denotes the
shortcut produced by the @key{Control} key plus that character (we
assume here that you are perfectly aware of the usage of the
@key{Control} key: it is just as if you had to type a capital letter
with @key{Shift}). The descriptions of the form @key{f}@var{n} represent
instead function keys. Finally, the symbol @samp{[} in front of a
character denotes the shortcut produced by @key{Control} plus @key{Meta}
(a.k.a. @key{Alt}) plus that character, @emph{or} @key{Meta} plus that
character, depending on your terminal emulator---you must check for
yourself. Moreover, these last bindings may not work with some
terminals, in which case you can replace them with a sequence: just
press the @key{Escape} key followed by the letter. A few menu items are
bound to two control sequences (just in case one does not work, or it is
impractical).
Note that under certain conditions (for instance, while using
@code{ne} through a @code{telnet} connection) some of the shortcuts
might not work because they are trapped by the operating system for
other purposes (@pxref{Hints and Tricks}).
Finally, we have the third and last interface to @code{ne}'s features: the
@dfn{command line}. If you press @kbd{@key{Control}-K}, or @key{Escape} followed by
@samp{:} (a la @code{vi}), you will be requested to enter a command
to execute. Just press @key{Return} for the time being (or, if you are
really interested in this topic, @pxref{The Command Line}).
In the sections that follow, when explaining how to use a command we
shall usually describe the corresponding menu item. The related shortcut
and command can be found on the menu item itself, and in @ref{Menus}.
@node Loading and Saving
@section Loading and Saving
@cindex Loading a file
@cindex Quitting
@cindex Exiting
@cindex Closing a document
@cindex Opening a file
@cindex Saving a file
@cindex Writing a file
@cindex File requester
The first thing to learn about an editor is how to exit. @code{ne} has a
@code{CloseDoc} command that can be activated by pressing @kbd{@key{Control}-Q},
by choosing the @samp{Close} item of the @samp{Document} menu, or by activating
the command line with @kbd{@key{Control}-K}, writing @samp{cd} and pressing
@key{Return}. Its effect is to close the current document without saving any
modifications. (You will be requested to confirm your choice in case the
current document has been modified since the last save.)
There is also a @code{Quit} command, which closes all the documents
without saving any modifications, and a @code{Save&Exit} (@kbd{@key{Meta}-X})
command, which saves the modified documents before quitting.
This choice of shortcuts could surprise you. Wouldn't @samp{Quit} be a
much better candidate for @kbd{@key{Control}-Q}? Well, experience shows that
the most common operation is closing a document rather than quitting the
editor. If there is just one document, the two operations coincide (this is
typical, for instance, when you use @code{ne} for writing electronic mail), and
if there are many documents, it is far more common to close a single document
than all the existing documents.
If you want to load a file, you may use the @code{Open} command, which can
be activated by pressing @kbd{@key{Control}-O}, by choosing the @samp{Open...}
item of the @samp{File} menu, or by typing it on the command line (as in the
previous case). You will be prompted with a list of files and directories in
the current working directory. (You can tell the directory names because they
end with a slash; they will also appear in a bold face if your terminal
allows it.) You can select any of the file names by using the
cursor keys, or any other movement key. Pressing an alphabetic key will
move the cursor to the first entry after the cursor that starts with the
given letter. When the cursor is positioned over the file you want to
open, press @key{Return}, and the file will be opened. If instead
you move to a directory name, pressing @key{Return} will display the
contents of that directory.
You can also escape with @key{f1}, @key{Escape} or
@key{Escape}-@key{Escape} and manually type the file name on the command
line (or escape again, and abort the @code{Open} operation). If you escape
with @key{Tab} instead, the file or directory under the cursor will be
copied to the input line, where you can modify it manually. @code{ne} also
has file name completion features activated by @key{Tab} (@pxref{The Input
Line}).
When you want to save a file, just use the command @code{Save}
(@kbd{@key{Control}-S}). It will use the current document name or will
ask you for one if the current document has no name. @code{SaveAs}, on the
other hand, will always ask for a new name before saving the file.
@code{SaveAll} will save all modified documents. If the file you are
saving a document to has changed since you last loaded or saved it,
perhaps because another user updated it while you were editing,
@code{ne} will warn you before overwriting the file.
If @code{ne} is interrupted by an external signal (for instance, if your
terminal crashes), it will try to save your work in some emergency files.
These files will have names similar to your current files, but they will
have a pound sign @samp{#} prefixed to their names.
@xref{Emergency Save}.
@node Editing
@section Editing
@cindex Deleting characters
@cindex Deleting lines
@cindex Undeleting lines
@cindex Block operations
@cindex Clip usage
@cindex Multiple documents
An editor is presumably used for editing text. If you decide not to edit text, you
probably don't want to use @code{ne}, because that's all it does---it edits text.
It does not play @code{Tetris}. It does not evaluate recursive
functions. It does not solve your love problems. It just allows you to
edit text.
The design of @code{ne} makes editing extremely natural and straightforward.
There is nothing special you have to do to start editing once you've
started @code{ne}. Just start typing, and the text you type shows up in
your document.
@code{ne} provides two ways of deleting characters: the @key{Backspace} key
(or @kbd{@key{Control}-H}, if you have no such key) and the @key{Delete}
key. In the former case you delete the character to the left of the
cursor, while in the latter case you delete the character just under the
cursor. This is in contrast with many @sc{un*x} editors, which for
unknown reasons decide to limit your ways of destroying
things---something notoriously much funnier than creating. (See
@ref{DeleteChar} and @ref{Backspace}.)
If you want to delete a line, you can use the @code{DeleteLine} command, or
@kbd{@key{Control}-Y}. A very nice feature of @code{ne} is that each time a
nonempty line is deleted, it is stored in a temporary buffer from which it can
be undeleted via the @code{UndelLine} command or @kbd{@key{Control}-U}. (See
@ref{DeleteLine} and @ref{UndelLine}.)
If you want to copy, cut, paste, shift or erase a block of text, you have to
set a mark. This is done via the @code{Mark} command, activated by
choosing the @samp{Mark Block} item of the @samp{Edit} menu, or by
pressing @kbd{@key{Control}-B} (think ``block''). This command
sets the mark at the current cursor position. Whenever the mark is set, the text
between the mark and the cursor can be cut, copied or erased. Note that
by using @kbd{@key{Control}-@@} you can set a @emph{vertical} mark
instead, which allows you to mark rectangles of text. Whenever a mark
has been set, either an @samp{M} appears on the command line or a
@samp{V} appears if the mark is vertical. If you forget where the mark
is currently, you can use the @samp{Goto Mark} menu item of the
@samp{Search} menu to move the cursor to it.
The block of text you cut or copy is saved in a @dfn{clip}, which you can
@samp{Paste} somewhere else in your document, or save it
to a file with the @samp{Save Clip...} menu
item of the @samp{Edit} menu. You can also load a file directly into a clip with
@samp{Open Clip...}, and @samp{Paste} it anywhere. All such operations act on the
@dfn{current clip}, which is by default the clip 0. You can change the current
clip number with the @code{ClipNumber} command. @xref{ClipNumber}.
One of the most noteworthy features of @code{ne} is its @dfn{unlimited
undo/redo} capability. Each editing action is recorded, and can be played
back and forth as much as you like. Undo and redo are bound to the function
keys @key{f5} and @key{f6}.
Another interesting feature of @code{ne} is its ability to load an
unlimited number of documents. If you activate the @code{NewDoc} command
(using the @samp{Document} menu or the command line), a new, empty
document will be created. You can switch between your documents
with @key{f2} and @key{f3}, which are bound to the
@code{PrevDoc} and @code{NextDoc} commands. If you have a lot of
documents, the @samp{Select...} menu item (@key{f4}) prompts you with
the list of names of currently loaded documents and allows you to
choose directly which to edit. In that list, names of documents with
unsaved changes will be bold. You can also change their relative order
in that list with the @key{f2} and @key{f3} keys.
@node Basic Preferences
@section Basic Preferences
@cindex Preferences
@cindex Flags
@cindex Insert mode
@cindex Automatic preferences
@cindex Virtual Extensions
@code{ne} has a number of @dfn{flags} that specify alternative
behaviors, the most prototypical example being the @dfn{insert} flag,
which specifies whether the text you type is inserted into the existing text
or replaces it. You can toggle this flag with the @samp{Insert} menu
item of the @samp{Prefs} menu, or with the @key{Insert} key of your
keyboard. (@dfn{Toggle} means to change the value of a flag from true to
false, or from false to true; @pxref{Insert}.)
Another important flag is the @dfn{free form} flag, which specifies
whether the cursor can be moved beyond the right end of each line of
text or only to existing text (a la @code{vi}). Programmers usually
prefer non free form editing; text writers seem to prefer free form. See
@ref{FreeForm} for some elaboration. The free form flag can be set with
the @samp{Free Form} menu item of the @samp{Prefs} menu.
At this point, we suggest you explore by trial and error the other
flags of the @samp{Prefs} menu, or try the @code{Flags} command (@pxref{Flags}),
which explains all the flags and the commands that operate on
them. We prefer spending a few words discussing @dfn{automatic
preferences} or @dfn{autoprefs}, and @dfn{default preferences} or @dfn{defprefs}.
Having many flags ensures a high degree of flexibility, but it can turn
editing into a nightmare if you have to turn on and off dozens of flags
for each different kind of file you edit. @code{ne}'s solution is to
load your default preferences whenever @code{ne} is run before loading
any file, then additionally set your stated preferences automatically
for each @dfn{file type} as files are loaded. A file's type is determined
by the @dfn{extension} of its file name, that is, the last group of letters
after the last dot. For instance, the extension of @file{ne.texinfo} is
@samp{texinfo}, the extension of @file{source.c} is @samp{c}, and the
extension of @file{my.txt} is @samp{txt}.
Thus, when you select the @samp{Save Def Prefs} menu item or use the
@code{SaveDefPrefs} command, a special preferences file named
@file{.default#ap} is saved. In addition to other preferences, this file also
includes a small set of preferences which are global to @code{ne} rather than
specific to particular document types. These preferences are: @code{FastGUI},
@code{RequestOrder}, @code{StatusBar} and @code{VerboseMacros};
@pxref{FastGUI}, @xref{RequestOrder}, @xref{StatusBar}, and
@xref{VerboseMacros}. These extra preferences are not saved by the
@code{SaveAutoPrefs} command.
By contrast, whenever you select the @samp{Save AutoPrefs} menu item, @code{ne} saves
the flags of your current document to be used when
you load other files with the same extension.
These @dfn{autoprefs} are saved in a file in your @file{~/.ne}
directory. This file has the same name as the extension of the current
document with @samp{#ap} appended to it. It contains all the commands
necessary to recreate your current document's flag settings. Whenever
you open a file with this file name extension, @code{ne} will
automagically recreate your preferred flag settings for that file type.
(There is a flag that inhibits the process; @pxref{AutoPrefs}.)
Similar to preference flags, the current syntax definition is specific to the current
document type, so it also is saved in autoprefs files by the
@code{SaveAutoPrefs} command or @samp{Save AutoPrefs} menu; it is not
saved in the @file{.default#ap} file.
Note that a preferences file---whether @file{.default#ap} or an AutoPrefs file---
is just a macro (as described in the following section). Thus, it can be edited manually if necessary.
Some files have no extension, but the file type can be discerned by simple
examination. Consider for example a file named @file{example} which contains XML.
You may reasonably expect it to be treated as an @file{.xml} file rather than
a generic file. For the purposes of applying automatic preferences and
syntax definitions, @code{ne} provides a mechanism for overriding a wrong or
missing extension with a @dfn{virtual extension} based on a document's
contents. You do this by creating a @file{~/.ne/.extensions} file which is
fully described in the @ref{Virtual Extensions} section.
@node Basic Macros
@section Basic Macros
@cindex Macro definition
@cindex Recording a macro
@cindex Saving a macro
@cindex Interrupting a macro
@cindex Interrupt character
@cindex Caching a macro
@cindex Unloading macros
@cindex Executing a macro
@cindex Comments in a macro
Very often, the programmer or the text writer has to repeat some complex
editing action over a series of similar blocks of text. This is where
@dfn{macros} come in.
A @dfn{macro} is a stored sequence of commands. Any sequence of commands
you find yourself repeating is an excellent candidate for being made
into a macro. You could create a macro by editing a document that only
contains valid @code{ne} commands and saving it, but by far the easiest way to create
a macro is to have @code{ne} record your actions. @code{ne} allows you
to record macros and then play them (execute the commands they contain)
many times. You can save recorded macros as files for future use, edit them, or bind
them to any key. You could even reconfigure each key of your keyboard to
play a complex macro if you wanted to.
@code{ne} can have any number of named macros loaded at the same time.
In addition, each document has one unnamed macro in its @dfn{current macro} buffer.
Named macros are loaded from---and get the name of---files, while each document's
unnamed current macro buffer is where your recorded macro is held before you
save it, play it, or record over it.
Recording a macro is very simple. The keystroke @kbd{@key{Control}-T} starts
and stops recording a macro. When you start recording a macro, @code{ne}
starts recording all your actions (with a few exceptions). You can see that you
are recording a macro if an @samp{R} appears on the status bar. When you stop
recording (again using @kbd{@key{Control}-T}), @code{ne} throws away the
current document's current macro and replaces it with the one you just finished
recording. You can then play the macro with the @samp{Play Once} item of the
@samp{Macros} menu or with the @key{f9} key. If you want to repeat the action
many times, the @code{Play} command allows you to specify a number of times to
repeat the macro. You can always interrupt the macro's execution with
@kbd{@key{Control}-\}.
These commands are omitted from recorded macros:
@example
Escape Help Play
Exec KeyCode Quit
Flags Macro Record
@end example
A recorded macro has no name. It's just an anonymous sequence of
commands associated with your current document. It will go away when you
record another macro, close the document, or exit @code{ne}. If you want to save your
recorded macro for future use, you can give it a name by saving it with
the @samp{Save Macro...} menu item or the @code{SaveMacro} command.
The macro is saved as a regular text file in your current directory by default or
whatever directory you specify when prompted for the macro's name. If
you save it in your @file{~/.ne} directory then it will be easy to
access it later from any other directory. The @samp{Open Macro...} menu item
and the @code{OpenMacro} command load a macro from a file into the
current document's macro buffer just as if you had just @code{Record}ed it.
The current setting of your @code{VerboseMacros} flag determines whether
long or abbreviated command names are used when saving a macro. For your
convenience, @code{SaveMacro} will also convert consecutive
@code{InsertChar} commands into single---usually much more readable---
@code{InsertString} commands, but only if all the inserted characters are
simple printable characters, and only if the macro contains no @code{Undo}
commands or macro invocations later in the macro.
Any macro can be loaded from a file and played with the @samp{Play Macro...}
menu item or the @code{Macro} command. (This won't modify the recorded
anonymous macro that may be in the @dfn{current macro} buffer;
@code{OpenMacro} does that.) Useful macros can be permanently bound to a
keystroke as explained in @ref{Key Bindings}. Moreover, whenever a
command line's first non-blank character is an ASCII letter but does
not specify one of @code{ne}'s built in commands, it is
assumed to specify the name of a macro to execute. Thus, you can execute
macros just by typing their file names at the command line. If the file
containing your macro is somewhere besides your current directory or
your @file{~/.ne} directory, and you have not already loaded it, then
use the @code{Macro} command on the command line followed by the path
to the macro. That's only necessary the first time you use such a macro.
From then on, you can use the macro's base name on the command line without
the preceding @code{Macro} command.
If the first attempt to open a macro fails, @code{ne} checks for a macro
with the given name in your @file{~/.ne} directory. This allows you
to program simple extensions to @code{ne}'s command set. For instance, all
automatic preferences macros---which are just specially named macros
that contain only commands to set preferences flags---can be executed
just by typing their names. For example, if you have an automatic
preference for the @samp{txt} extension, you can set
@code{ne}'s flags exactly as if you had loaded a file ending with
@samp{.txt} by typing the command @code{txt#ap}.
In general, it is a good idea to save frequently used macros in
@file{~/.ne} so that you can invoke them by name without specifying
a path regardless of your current directory. On the other hand, if you
have a macro that is customized for one document or a set of documents
that you store in one directory, then you might want to save the
macro in that directory instead. If you do, then you would want to
@code{cd} to that directory before you start @code{ne} so that you can
access that macro without specifying a path.
If your macro has the same name as one of @code{ne}'s built-in commands,
you can only access it with the @code{Macro @var{name}} command.
Built-in command names are always searched before the @code{ne} command
interpreter looks for macros.
The system administrator may make some macros available from
the @file{macros} subdirectory of @code{ne}'s global directory. @xref{Arguments}.
Since loading a macro each time it is invoked would be a rather slow and
expensive process, once a named macro has been executed it is cached internally.
Subsequent invocations of the named macro will use the cached version.
@noindent @strong{Warning:} while path and file names are case sensitive
when initially loading macros, loaded macro names are @emph{not} case
sensitive or path sensitive. @code{ne} only caches the file name of an
already loaded macro, not the path, and it uses a case insensitive
comparison when resolving command and macro names. As such, if you invoke @file{~/foobar/MyMacro}, @code{ne}
remembers it with the case-insensitive name @file{mymacro}; a subsequent
call for @file{/usr/MYMACRO} will instead find and use the cached version
of @file{~/foobar/MyMacro}. You can clear all macros from the cache by using the
@code{UnloadMacros} command. @xref{UnloadMacros}.
The behaviour of macros may vary with different preferences. If the user
changes the @code{AutoIndent} and @code{WordWrap} flags, for example, new lines and new
text may not appear the way they would have when a macro was
recorded. A good general purpose macro avoids such problems by using the
@code{PushPrefs} command first to preserve the current preferences.
The macro then sets any preferences that could affect its behaviour. Once
that is taken care of it can get on with the actual work for which it
were intended. Finally, it will use the @code{PopPrefs} command to restore
the original preferences. Note that if a macro stops before it
restores the preferences (either by the user pressing
@kbd{@key{Control}-\} or by a command failing) then dealing with the
changed preferences falls to the user.
Any changes made to a document by a macro are recorded just as if you had
entered the text and commands yourself. Therefore you can use the @code{Undo} command
to roll back those changes one at a time. This can be useful especially when
developing macros, but you may want to be able to undo all the changes made
by a macro with a single @code{Undo} command. The @code{AtomicUndo} command
makes this possible. If you add @code{AtomicUndo +} at the start of your
macro and @code{AtomicUndo -} at the end, then the @code{Undo} and
@code{Redo} commands will handle all changes made by your macro atomically,
i.e., as if they had been made by a single command, even if your macro
calls other macros which could themselves contain matching @code{AtomicUndo +}
and @code{AtomicUndo -} commands. @xref{AtomicUndo}.
Leading spaces in macros are ignored, so you can indent your macros to make
them more readable. Any line in a macro that starts with a non-blank, non-alphabetical character
is considered a comment, so you can add comments to a macro by starting
a line with @samp{#}. Recorded macros sometimes have comments added to them
indicating calls to other macros.
Macros can operate across multiple documents, by using the @code{NextDoc} and
@code{PrevDoc} commands for example. When you stop recording, the unnamed
macro is associated with the current document, replacing that document's
prior unnamed macro.
You can cancel in-progress macro recording---and thus avoid wiping out a
document's existing unnamed macro---with the @code{Record 0}
command, or by selecting @code{Record Cancel} from the @code{Macro} menu.
You can append additional recorded commands to your document's current
macro with the @code{Record 1} command, or by selecting @code{Record Append}
from the @code{Macro} menu.
Finally, you can insert the current document's unnamed macro into your
in-progress macro recording as many times as you like either by pressing the
@key{f9} key or entering the @code{Play 1} command. Although the unnamed macro
is distroyed and replaced when you stop recording your new macro, until then
it's available for use like any other.
@node More Advanced Features
@section More Advanced Features
@cindex UTF-8 support
@cindex Bookmarks
@cindex Automatic Completion
@cindex Automatic Bracket Matching
@cindex MS-DOS files
@cindex File requester
@cindex Binary files
@cindex Executing @sc{uni*x} commands
@subsection UTF-8 support
@code{ne} can load and
manipulate UTF-8 files transparently, in particular on systems that
provide UTF-8 I/O. @xref{UTF-8 Support}.
@subsection Bookmarks
It often happens that you have to browse through a file, switching
frequently between a small number of positions. In this case, you can use
@dfn{bookmarks}. There are up to ten bookmarks per document, each
designated by a single digit, with the default being @samp{0}. You can set
them with the @code{SetBookmark} command, and you can return to any set
bookmark with the @code{GotoBookmark} command. Another pair of bookmarks
designated by @samp{<} and @samp{>} mark the start and end points of the
last block of text you pasted into your current document. So for example
@code{GotoBookmark >} will take you to the end of the most recently pasted
text. Also, @code{ne} sets an
automatic bookmark (designated by @samp{-}) at your current position in a
document whenever you use the @code{GotoBookmark} command. You can use
a @code{GotoBookmark -} command to return to the location of the previous
@code{GotoBookmark} command. Doing so will reset the automatic bookmark,
so that subsequent @code{GotoBookmark -} commands will switch between
those two locations. The special parameters @samp{+1} and @samp{-1}
indicate the next or previous set bookmark in conjunction with
@code{GotoBookmark} and @code{UnsetBookmark}, but reference the next or
previous unset bookmark when used with @code{SetBookmark}. A sequence of
@code{GotoBookmark +1} commands lets you easily cycle through all your set
bookmarks. Finally, the special parameter @samp{?} causes @code{SetBookmark}
and @code{GotoBookmark} to prompt you for a bookmark designation. This prompt
includes an indication of which bookmarks are already set for the current
document. @xref{SetBookmark}, @ref{GotoBookmark}, and
@ref{UnsetBookmark}. Note that in the default configuration no key binding
is assigned to these commands. If you use them frequently, you may want to
change the key bindings. @xref{Key Bindings}.
@subsection Automatic Completion
The @code{AutoComplete} command helps you extend a given prefix with matching
words from your open documents. You can specify the @code{AutoComplete} command
and prefix on the command line, or you can enter the prefix directly into your
document and activate the @code{AutoComplete} command. With the cursor at the
right end of your prefix, activate the @code{AutoComplete} command by entering
either the @key{Escape}-@key{Tab} or the @key{Escape}-@key{I} key sequence, or
the @kbd{@key{Control}-@key{META}-@key{I}} key combination, or by selecting
@code{AutoComplete} from the @code{Extras} menu.
If the prefix can be extended
unambiguously, the extension will be immediately inserted into your
document (this is the case, for instance, if only one word matches the
prefix), and a message will tell you whether the extension is an actual word
or just the longest possible extension (for instance, if you expand
@samp{fo} and your document contains @samp{foobar} and @samp{foofoo} then the
partial match will be @samp{foo}). Otherwise, @code{ne} presents you with
a list of all matching words: choose the one you want and press
@key{Return}, to select it; otherwise, press @key{f1}, @key{Escape} or
@key{Escape}-@key{Escape} to cancel the completion operation.
The current
state of the @code{CaseSearch} flag determines whether the prefix match is
case sensitive. Any matching words which only exist in other open
documents but not the current one are displayed in bold with an asterisk;
think of that as a warning that if you select one of these bold words you
will introduce a new word into your current document. Plain words already
exist somewhere in your current document. @xref{AutoComplete}, and
@ref{CaseSearch}.
@subsection Automatic Bracket Matching
Unless you tell it not to (with the @code{AutoMatchBracket} command),
@code{ne} will highlight any recognized bracket that matches the
bracket your cursor is on if that matching bracket is currently visible on
your screen. Recognized bracket pairs are @samp{@{@}}, @samp{()}, @samp{[]},
@samp{<>}, and @samp{`'}. @xref{AutoMatchBracket}.
@subsection MS-DOS files
@code{ne} will detect automagically the presence of MS-DOS line terminators
(CR/LFs) and set the CR/LF flag. When the file will be saved, the
terminators will be restored correctly. You can change this behaviour
using the @code{PreserveCR} and @code{CRLF} commands. @xref{PreserveCR},
and @ref{CRLF}.
@subsection Binary files
@code{ne} allows a simplified form of @dfn{binary editing}. If the
binary flag is set, only NULLs are considered newlines when loading or
saving. Thus, binary files can be safely loaded, modified and saved.
Inserting a new line or joining two lines has the effect of inserting or
deleting a NULL. Be careful not to mismatch the state of the binary flag
when loading and saving the same file.
@subsection File requester
The @code{NoFileReq} command deactivates the file requester. It is
intended for ``tough guys'' who always remember the names of their files and can
type them at the speed of light (maybe with the help of the completer,
which is activated by the @key{Tab} key; @pxref{The Input Line}).
@subsection Executing @sc{un*x} commands
There are three ways to execute @sc{un*x} commands from within @code{ne}.
The @code{System} command can run any @sc{un*x} command; you
will get back into @code{ne} as soon as the command execution terminates.
@xref{System}. The @code{Through} (@kbd{@key{Meta}-T}) command (which can be found in the
@samp{Edit} menu), however, is much more powerful; it cuts the current
block, passes it as standard input to any @sc{un*x} command, and pastes the
command's output at the current cursor position. This provides a neat
way to pass a part of your document through one of @sc{un*x}'s many
@dfn{filter commands} (commands that read from standard input and write
to standard output, e.g., @code{sort}). @xref{Through}. Finally, you can use the
@code{Suspend} (@kbd{@key{Control}-Z}) command to temporarily stop @code{ne} and
return to your command shell. @xref{Suspend}.
@subsection Advanced key bindings
@code{ne} allows you to associate any keystroke with any command, both
built-in commands (with or without parameters) and macros. These associations
are referred to as @dfn{key bindings}, which you define in your
@code{~/.ne/.keys} file. The @code{KeyCode} command allows you to see the key
code @code{ne} sees in response to any key or key combination on your
keyboard. It also shows the command string currently bound to that key
code. This is described in @ref{Key Bindings}.
The following chapters provide an exhaustive list of the remaining features of @code{ne}.
@xref{Reference}.
@node Reference
@chapter Reference
In this chapter we shall methodically overview each part of @code{ne}. It
is required reading for becoming an expert user because some commands
and features are not available through menus.
@menu
* Arguments::
* The Status Bar::
* The Input Line::
* The Command Line::
* The Requester::
* Syntax Highlighting::
* Menus::
* Regular Expressions::
* Automatic Preferences::
* Emergency Save::
* UTF-8 Support::
@end menu
@node Arguments
@section Arguments
@cindex Arguments
@cindex Global Directory
@cindex Startup macro
@cindex Skipping configuration files
@cindex Setting configuration file names
The main arguments you can give to @code{ne} are the names of files you
want to edit. They will be loaded into separate documents. If you
specify @code{--help} or @code{-h} anywhere on the command line, a simple help text
describing @code{ne}'s arguments will be printed.
The @code{+@var{N}} option causes @code{ne} to advance to the @var{N}th
line of the next document loaded. This option is fairly common among
editors and text display programs like @code{vi} and @code{less}. The
@var{N} itself is optional. Without it, a bare @code{+} on the command
line causes @code{ne} to advance to the last line of the first
document. You can specify a line and column as @code{+N,M}. Any
non-digit can be used to separate the @var{N} from the @var{M}. As it
only affects the next document loaded, it can appear multiple times on
the command line.
The @code{--binary} option causes @code{ne} to load the next document
in binary mode. Binary mode treats the normal line termination
characters as any other character and only breaks lines on NULL
characters. Like @code{+N,M}, @code{--binary} only affects the next
document loaded, and it can appear multiple times on the command line.
See @ref{Binary}.
The @code{--read-only}/@code{--readonly}/@code{--ro} option causes @code{ne} to load the next named file
into a read-only document. You can't modify a read-only
document without first taking special action such as turning off the read-only
flag. You can still @code{Save} (@ref{Save}) a read-only document to a
file if the file's permissions allow it, but @code{ne} will prompt you
before attempting to save a document marked read-only.
The @code{--read-only} option only affects the next document loaded,
so it can appear multiple times on the command line. A document's
read-only flag is automatically set when a file is loaded if the
corresponding file is not writable (as determined by the @code{access()}
system call) regardless of whether the @code{--read-only} option is used. See
@ref{ReadOnly}.
The @code{--no-config}/@code{--noconfig} option skips the reading of the key
bindings and menu configuration files (@pxref{Configuration}). This is
essential if you are experimenting with a new configuration and you make
mistakes in it.
The @code{--prefs @var{extension}} option makes @code{ne} load a specified
set of automatic preferences, that is, those associated with the provided
extension, instead of the default ones, before loading the first file.
It can be useful, for instance, when piping a file into @code{ne} or when
reading from named pipes, as in those cases there is no file extension
from which @code{ne} can guess the correct preferences. Note that preferences
are cloned from the current document when a new document is created, so if
you open a number of files without extension this option will propagate
to all of them.
The @code{--macro @var{filename}} option specifies the name of a
macro that will be started just after all documents have been loaded. A
typical macro would move the cursor to a certain line.
The @code{--keys @var{filename}} option and the @code{--menus
@var{filename}} option specify a name different from the default one
(@file{.keys} and @file{.menus}, respectively) for the key bindings and
the menu configuration files.
For key bindings, @code{ne} loads system wide files first, then those in your
@file{~/.ne} directory, and finally those in the current directory. Key
bindings loaded later may override those loaded previously. In each location,
@file{~/.ne} will first look for a file named @file{.keys} (or whatever base
name you specify with the @code{--keys} option), then a file with the same name
followed by a dash and the value of your @code{TERM} environment variable.
This is how you can create key bindings specifically for different terminals
on a single system.
Menu configuration, unlike key bindings, is not cumulative; @code{ne} looks
first in your @file{~/.ne} directory possibly followed by the system wide
global directory for a menu configuration file and loads only the first one it
finds, if any.
The @code{--ansi} and the @code{--no-ansi}/@code{--noansi} options manage
@code{ne}'s built-in ANSI sequences. Usually @code{ne} tries to retrieve
from your system some information that is necessary to handle your
terminal. If for some reason this is impossible, you can ask @code{ne}
to use a built-in set of sequences that will work on many terminals
using the @code{--ansi} option (to be true, @code{ne} can be even
compiled so that it uses directly the built-in set, but you need not
know this). If you want to be sure (usually for debugging purposes) that
@code{ne} is not using the built-in set, you can specify
@code{--no-ansi}.
The @code{--no-syntax} option disables @code{ne}'s normal syntax
highlighting capability. For most editing situations, this would be
unnecessary, but for extremely large files it may be helpful. Syntax
highlighting incurs small memory usage and processor overhead penalties
for each line of text. The @code{--no-syntax} option eliminates that
overhead. Note that files longer than ten million bytes will have
syntax highlighting disabled by default, but it is possible to re-enable it.
@xref{Syntax Highlighting}.
The @code{--utf8} and @code{--no-utf8} options can be used to
force or inhibit UTF-8 I/O, overriding the choice imposed by the system
locale. Note, however, that in general it is more advisable to set the
@code{LANG} environment variable to a locale supporting UTF-8 (you can
usually see the locale list with @code{locale -a}). @xref{UTF-8 Support}.
If you need to open a file whose name starts with @samp{--}, you can put
@samp{--} before the filename, which will skip command recognition for
the next word.
You can use I/O redirection to pipe the output of other commands into your
first document. For example,
@example
ls -l | ne file1.txt --read-only file2.txt
@end example
will open three documents: an unnamed document containing the output of
the @code{ls -l} command, the contents of @file{file1.txt}, and the contents
of @file{file2.txt} with the read-only flag set.
It's possible to apply the @code{--binary}, @code{--read-only}, and
@code{+N,M} options to the piped unnamed document by referencing it as a
single @code{-}. Only the first such file name will reference the piped
document (even if it isn't the first file name on the command line).
Subsequent dashes will be considered normal file names. If you want the first
dash to be treated like a normal file instead of a reference to the piped
document, prefix the dash with @samp{--}. Consider these two command lines:
@example
ls -l | ne --read-only +3,8 - file1.txt -
ls -l | ne file1.txt -- - --read-only +3,8 -
ls -l | ne --binary file1.txt --read-only -- -
@end example
All three of these commands open @code{ne} with three documents: the output
of the @code{ls -l} command will be in the first unnamed document, the
contents of @file{file1.txt} will be in the second document, while the third
document will contain the contents of the file @file{-} (or an empty document
with that name if there is no such file). The first and second commands do
exactly the same thing: the unnamed first document is marked read-only and
the cursor is positioned on line 3 column 8, while the other two document are
opened normally. In the case of the third command, @file{file1.txt} is opened
in binary mode, the document named @file{-} is marked read-only, while the
first, unnamed, document---which is not referenced on the command line---with
the output from @code{ls -l} is opened normally.
Finally, @code{ne} has a @dfn{global directory} where the system
administrator can store macros, default preferences, and syntax
definitions for all users of the system. The location of this directory
is defined when @code{ne} is built, but you can override it by creating
and exporting the @code{NE_GLOBAL_DIR} environment variable prior to
invoking @code{ne}. If you load no files when you start @code{ne}, or
if you invoke the @code{About} command, it will display a splash
screen. The last line on that screen shows the global directory
@code{ne} is using, if it exists, or an error message otherwise.
@node The Status Bar
@section The Status Bar
@cindex Status bar
@cindex Line and column numbers
@cindex Fast GUI
The last line of the screen, the @dfn{status bar}, is reserved by
@code{ne} for displaying some information about its internal state.
Note that on most terminals it is physically impossible to write a
character on the last column of the last line, so we are not
stealing precious editing space.
The status bar looks more or less like this:
@example
L: 31 C: 25 12% iabcwfpvurt!MRPC@@8* 20 /foo/bar
@end example
The numbers after @samp{L:} and @samp{C:} are the line and column of the
cursor position. The first line and the first column are both number
1. Then, @code{ne} shows the percentage of lines before the current line
(it will be 0% on the first line, and 100% on the last line).
Following that are a sequence of letters or
dashes. These indicate the status of a series of flags which we shall
look at later.
The hexadecimal digits following the flags give the code for the
character at the cursor, and are displayed optionally (@pxref{HexCode}).
If your cursor is at or beyond the right end of the current line, the
code disappears.
The file name appearing after the character code is the file name of
the current document. The left end of very long file names may be
truncated to keep the right end visible. Of course,
@code{ne} is keeping track internally of the complete file name. It
is used by the @code{Save} command and as the default input for the
@code{SaveAs} command. @xref{Save}, and @ref{SaveAs}.
The displayed line and column numbers, the percentage indicator and the
character code change when the cursor moves. This fact can really slow
down cursor movement if you are using @code{ne} through a slow
connection. If you find this to be a problem, it is a good idea to turn
off the status bar using either the @samp{Status Bar} menu item of the
@samp{Prefs} menu or the @code{StatusBar}
command. @xref{StatusBar}. Alternatively you can turn on the fast GUI
mode using either the @samp{Fast GUI} menu item of the @samp{Prefs} menu
or the @code{FastGUI} command (@pxref{FastGUI}). In fast GUI mode the
location of the mark is not highlighted, and
status bar is not draw in reverse, so some additional optimization can
be done when refreshing it.
The letters after the line and column number represent the status of the flags
associated with the current document. Flags that are off display a
@samp{-} instead of a letter. Each flag also has an
associated command. The @code{Flags} command describes them all when
you don't have this manual handy. Here's the list in detail:
@table @samp
@item i
appears if the insert flag is true. @xref{Insert}.
@item a
appears if the auto indent flag is true. @xref{AutoIndent}.
@item b
appears if the back search flag is true. @xref{SearchBack}.
@item c
appears if the case sensitive search flag is true. @xref{CaseSearch}.
@item w
appears if the word wrap flag is true. @xref{WordWrap}.
@item f
appears if the free form flag is true. @xref{FreeForm}.
@item p
appears if the automatic preferences flag is true. @xref{AutoPrefs}.
@item v
appears if the verbose macros flag is true. @xref{VerboseMacros}.
@item u
appears if the undo flag is true. @xref{DoUndo}.
@item r
appears if the read only flag is true. @xref{ReadOnly}.
@item t or T
appears as @samp{t} if the tabs flag is true, @samp{T} if
the shifttabs flag is also true. @xref{Tabs}, @ref{ShiftTabs}.
@item d
appears if the deltabs flag is true. @xref{DelTabs}.
@item B or !
appears if the binary flag is true. @xref{Binary}.
@item !
appears in place of @samp{B} when not in binary mode
and the last line of the document is not empty (i.e. the last line of the
saved file would not be terminated).
@item M or V
appears if you are currently marking a block. @xref{Mark}.
@item V
can appear in place of @samp{M} if you are currently marking a vertical
block. @xref{MarkVert}.
@item R
appears if you are currently recording a macro. @xref{Record}.
@item P
appears if the PreserveCR flag is true. @xref{PreserveCR}.
@item C
appears if the CRLF flag is true. @xref{CRLF}.
@item @@
appears if UTF-8 I/O is enabled. @xref{UTF8IO}.
@item A/8/U
denotes the current document encoding---US-ASCII, 8-bit or UTF-8. @xref{UTF8}.
@item *
appears if the document has been modified since the last save, or if
the @code{Modified} command was issued to set this flag. @xref{Modified}.
This @samp{-} or @samp{*} may be underlined, which indicates the
corresponding file's modification time has changed since the current
document was loaded from or saved to that file.
@end table
Note that sometimes @code{ne} needs to communicate some message to you. The
message is usually written over the status bar, where it stays
until you do something. Any action such as moving the cursor or inserting a
character will restore the normal status bar.
@node The Input Line
@section The Input Line
@cindex Input line
@cindex Escaping an input
@cindex Immediate input
@cindex Long input
@cindex File name completion
The bottom line of the screen is usually occupied by the status bar
(@pxref{The Status Bar}). However, whenever @code{ne} prompts you for a
command or file name or asks you to confirm some action, the bottom line
becomes the @dfn{input line}. You can see this because a @dfn{prompt} is
displayed at the start of the line, suggesting what kind of input is
required. (Prompts always ends with a colon, so it is easy to
distinguish them from @dfn{error messages}, which overwrite the status
bar from time to time.)
@code{ne} uses the input line in two essentially different ways:
@dfn{immediate} input and @dfn{long} input. You can easily distinguish
between these two modes because in immediate input mode the cursor is
not on the input line, while for long input mode it is.
Immediate input is used whenever @code{ne} needs you to specify a simple
choice that can be expressed by one character (for example, @samp{y} or
@samp{n}). When you type the character, @code{ne} will immediately
accept and use your input. Most immediate inputs display a character
just after the prompt. This character is the default response, which is
used if you just press the @key{Return} key. Note that immediate input
is not case sensitive. Moreover, if a yes/no choice is requested,
@emph{anything} other than @samp{y} will be considered a negative
response.
Long input is used when a whole string is required. You can enter and
edit your response to long inputs like a line of text in a
document. Most key bindings related to line editing work on the command
line exactly as they do in a document. This is true even of custom key
bindings. Just edit as you are used to. Moreover, the you can paste the
first line of the current clip using the keystroke that is bound to the
@code{Paste} command, usually @kbd{@key{Control}-V}. If your long input
is longer than the screen width, the input line scrolls to accommodate
your text so you can input very long lines even on small
monitors. (There is a limit of 2048 characters.)
The default response to a long input is the response you gave to the
previous long input. Your @emph{first action} when presented with a long
input will either erase the default response or allow you to edit it. If
the first thing you type is a printing character, the default response
will be erased. Anything else (cursor movement for example) will allow
you to edit it further.
Long input also lets you access your previous long input responses with
the up and down cursor commands (or with wider movement commands, such
as start/end of file, page up/down, etc.). Once you find a previous
input you like, you can edit it further. Long input history is not
document specific, so you can recall any of your inputs regardless of
which document was active when you entered it. Furthermore, @code{ne}
saves the most recent long inputs in @file{~/.ne/.history} when you end
your @code{ne} session and loads them again when you begin another
@code{ne} session.
Invoking the @code{Find} command, usually bound to
@kbd{@key{Control}-F}, brings up a requester showing your prior inputs.
You can close the requester with the @key{Escape} key, replace your
input line with a highlighted prior entry with the @key{Enter} key, or
insert that prior entry into your input line with the @key{Tab} key.
When asked to input a number, you can choose between decimal, octal and
hexadecimal notation in the standard way: a number starting with
@samp{0} is considered in octal, a number starting with @samp{0x} is
considered in hexadecimal, and in all other cases decimal base is
assumed.
Whenever a file name is requested, you can type a partial file name and
@dfn{complete} it with the @key{Tab} key. @code{ne} will scan the
current directory (or the directory that you partially specified) and
search for the files matching your partial suggestion. The longest
prefix common to all such files will be copied to the input line
(@code{ne} will beep if no completion exists). It's easier done than
said---just try. If you press @key{Tab} again, you will be brought into
the file requester: only the files and directories matching your partial
specification will appear, and as usual you will be able to navigate and
select a file or escape. @xref{The Requester}. Note that @code{ne}
considers the @emph{last word} on the input line the partial file name
to complete, no matter where the cursor is currently (you must use quotes
if the name contains spaces, even if it is the only item on the input line).
Complete long input with the @key{Return} key. You can cancel a long
input using @key{f1}, @key{Escape}, @key{Escape}-@key{Escape} or any key
that is bound to the @code{Escape} command. The effect will vary depending
on what your were requested to input, but the execution of the command
requiring the input will stop.
@node The Command Line
@section The Command Line
@cindex Command line
The command line is a typical (topical) way of controlling an editor on
character driven systems. It has some advantages over menus in terms of
access speed, but it is not desirable from a user interface point of
view. @code{ne} has a command line that should be used whenever strange
features have to be accessed, or whenever you want to use a command that
you are familiar with and that is not bound to any key.
You have two ways to access the command line: by activating the menu and
typing a colon (@samp{:}) or by typing @kbd{@key{Control}-K} (or any key
that is bound to the @code{Exec} command; @pxref{Exec}). The first
method will work regardless of any key binding configuration if you
activate the menus with the @key{Escape} key since that key cannot be
reconfigured. Of course, there is also a menu entry that does the same
job.
Once you activate the command line, the status bar will turn into an input
line (@pxref{The Input Line}) with a @samp{Command:} prompt waiting for you
to do a long input. In other words, you can now type any command (possibly
with arguments), and when you press @key{Return}, the command will be
executed.
If the command you specify does not appear in @code{ne}'s internal tables,
it is considered to be the name of a macro. @xref{Basic Macros}, for details.
@node The Requester
@section The Requester
@cindex Requester
@cindex File requester
@cindex Interrupting directory scanning
@cindex Help requester
In various situations, @code{ne} needs to ask you to choose one string
from several (where ``several'' can mean a lot).
For this kind of event, the @dfn{requester} is issued. The requester
displays the strings in as many columns as possible and lets you move
with the cursor from one string to another. The strings can fill many
screens, which are handled as consecutive pages. Most navigation keys
work exactly as in normal editing. This is true even of custom key
bindings. Thus, for instance, you can page up and down through the
list with @kbd{@key{Control}-P} and @kbd{@key{Control}-N} (in the standard keyboard
configuration).
A special feature is bound to printing characters: the requester
progressively advances to entries that match the characters you
type without regard to case. You can use @key{Backspace} to
incrementally undo your matched characters. This progressive matching
works in two modes which you can switch between on the fly with either
the @key{Insert} or @key{Delete} key. In the default mode, the cursor
indicating your current selection simply advances to the next matching
entry (if there is one). In the other mode, all entries which don't
match the characters you've entered are removed from the list so you
only see the matching entries. The @key{Backspace} key incrementally
returns them to your list as your match becomes less specific. You can
switch between the two modes as often as you wish while searching for
your desired entry. This lets you quickly navigate large lists to get
to the entries you really want.
One example of a requester is the list of commands appearing when you use
the @code{Help} command. Another is the list of document words matching
a prefix given to the @code{AutoComplete} command. A third example is
the file requester that @code{ne} issues whenever a file operation is
going to take place. In this case, pressing @key{Return} while on a
directory name will enter that directory and refresh the requester with
that directory's entries. Note also that, should the requester take too long to
appear, you can interrupt the directory scanning with
@kbd{@key{Control}-\}. However, the listing will likely be incomplete.
Yet another example of a requester is the list of documents you
currently have open. This requester is displayed when you use the
@code{Select...} entry from the @code{Documents} menu, or invoke the
@code{SelectDoc} command with the @key{f4} key. Documents with unsaved
changes will be bold (if your terminal supports bold) and marked with
an asterisk.
These documents are generally listed in the order they
were opened. However, in this requester you can reorder these documents
by using the keys bound to the @code{NextDoc} and @code{PrevDoc}
commands, usually @key{f2} and @key{f3}. Any document reordering and
selection will only take effect if you exit the requester with the
@key{Return} key.
You can also save named documents and close unmodified documents without
leaving the @code{SelectDoc} requester by using the key bound to the
@code{Save} and @code{CloseDoc} commands respectively, usually
@kbd{@key{Control}-S} and @kbd{@key{Control}-Q}. You can't close the last
document this way because it would cause @code{ne} to exit.
Regardless of the type of requester, you can confirm your selection
with @key{Return} just as with the input line (@pxref{The Input Line}), or
you can escape the requester without making a selection with @key{f1}
or the @key{Escape} key (or whatever has been bound to the
@code{Escape} command).
Moreover, if you are selecting a file name through the requester there
is a third possibility: by escaping with the @key{Tab} key, the file or
directory name that the cursor is currently on will be copied to the
input line. This allows you to choose an existing name with @key{Tab}
and modify the name on the input line before hitting @key{Return}.
Note that there are two items that always appear at the top of a file
requester: @file{./} and @file{../}. The first one represents the
current directory and can be used to force a reread of the directory.
The second one represents the parent directory and can be used to move
up by one directory level.
The path to file names and directories selected through the requester
will be relative to the current directory, i.e. the directory you were
in when you invoked @code{ne}. The exception is when you've entered a
path on the command line that starts with a @key{/}, then hit @key{Tab}
to invoke the requester. In that case the path eventually returned by the
requester will be an absolute path. (Note that you can change the
current document's name from relative to absolute or absolute to
relative with the @code{NameConvert} command either on the command line
or from the @code{Extras} menu.)
All requesters present their selections by default in ``row major order,''
which means the second string is on the same row as the first but to
its right, at the top of the second column, and so on across each row
before filling in the next row down. If you prefer your lists displayed in
``column major order''---the first, second, and third strings are in
the same column and each column is filled before starting on the next
column to the right---then use the @code{RequestOrder} command to
switch that preference. The setting can be stored in your default
preferences the next time you save them. See @ref{Preferences Commands}.
@node Syntax Highlighting
@section Syntax Highlighting
@cindex Syntax Highlighting
Syntax highlighting is particularly useful for programming language
text or other types of documents which have a strictly defined syntax.
Colors indicate different syntactic categories of text according to
the syntax definition in use.
Syntax definitions are stored in separate files. @code{ne} comes with a
suite of syntax definitions for many popular programming languages and
other common text file types.
When you load a file, @code{ne} selects the appropriate syntax
definition as determined by the filename extension in much the same way
autoprefs are loaded. (See @ref{Virtual Extensions} for ways to override
a file's extension based on file contents.)
It also contains a built-in table of common
filename extensions that share the same syntax definitions. For
example, both @samp{cbl}, and @samp{cob} files use the @samp{cobol}
definition. See the @ref{Syntax} command for the complete list of
built-in extension mappings.
If there is no matching syntax definition for the filename extension,
or if the document you are editing has no filename yet, or you just want
to try a different syntax definition, you can load and use the
syntax definition of your choice with the @code{Syntax} command. It
takes the syntax name as a parameter. For example, the name ``@code{c}''
works for C syntax files with extensions @samp{.c}, @samp{.h},
@samp{c++}, etc. @code{ne} searches for the specified syntax definition
file in the @samp{syntax} subdirectory of your @file{~/.ne} directory
first. If not found there, @code{ne} then looks in the @samp{syntax}
subdirectory of @code{ne}'s global directory for the syntax definition
file. @xref{Arguments}.
With no parameter, the @code{Syntax} command prompts you for a syntax
to load, the offered default being the currently loaded syntax if there is one.
Use the @key{Tab} key at that prompt to get a list of available syntax
recognizers.
One syntax definition you may find useful for any type of text file is
called simply @samp{tabs}. It highlights the @sc{tab}s in your text so
you can distinguish them from regular spaces.
You can create your own syntax definitions and store them in your
@file{~/.ne/syntax} directory (actually, modifying the colors of an
existing definition is much easier; @pxref{Hints and Tricks}). A complete
explanation of syntax specifications is beyond the scope of this document,
but the existing definition files should prove to be useful examples. In
particular, the @file{syntax/c.jsf} file contains some particularly
helpful comments. Syntax definition files have a @samp{.jsf} extension. Do
not include that extension when using the @code{Syntax} command.
Your own syntax recognizers will be preferred over the global recognizers.
If you use the @key{Tab} key at the syntax prompt to display the requester
of extant recognizers, yours will be marked with an asterisk and bold if
your terminal supports that.
Syntax highlighting does incur a slight penalty in memory used per line of
text, and it also consumes some CPU resources. For small to medium sized
files you'll probably never notice. But for extremely large files---on
the order of the size of your system's RAM---the difference could be
significant. If you invoke @code{ne} with the @code{--no-syntax}
parameter, @code{ne} will disable the syntax highlighting mechanism
entirely, freeing up the memory and CPU otherwise consumed. (Note that
if you are that tight on memory, you may need to disable the undo
buffer as well. @xref{DoUndo}.) On the other hand, @code{ne} will silently
disable syntax highlighting on files longer than ten million bytes, but
you can force it using the @code{Syntax} command.
Note that there is a basic difference between these two cases: when you
use the @code{--no-syntax} parameter, the additional memory is not
allocated at all, and syntax highlighting cannot be enabled without
restarting @code{ne}. On the contrary, the automatic disabling for long
files keeps only @code{ne} from computing the actual highlighting, and it
can be overridden as explained above.
@code{ne} uses code from another editor---the GPL-licensed
@code{joe}---for its syntax highlighting capabilities. Because of this fact, the
syntax definition files are identical, even to the @samp{.jsf}
extension, which is an acronym for ``Joe's Syntax File''. It's possible
that if both @code{joe} and @code{ne} are installed on your system that
they share the same syntax file directory.
@node Menus
@section Menus
@cindex Menus
@code{ne}'s menus are extremely straightforward. The suggested way of
learning their use is by trial and error, with a peek here and there
at this manual when some doubts arise.
You activate menus with the @key{f1} key, or in case your keyboard does
not have such a key, @key{Escape}, @key{Escape}-@key{Escape} or any key
that is bound to the @code{Escape} command. Move around the menus pressing
with the cursor keys, the @key{Page Up} and @key{Page Down} keys (which move to the first or
last menu item in a menu), and the @key{Home} and @key{End} keys (which move to the first or
last menus). You can also move around menus and menu items by
pressing the alphabetic keys; a lower case letter will move to the first
item in the current menu whose name starts with the given letter; an upper
case letter will move to the first menu whose name starts with the given
letter.
If you've activated the menus and you want to switch immediately to the command line,
press the @key{:} key. The menus will clear and you'll find yourself on the command
line. @xref{The Command Line}.
Each menu item of @code{ne}'s standard menu corresponds to a
single command. In explaining what each menu item allows you to
do, we shall simply refer you to the section that explains the command
relative to the menu item.
If you plan to change @code{ne}'s menu (@pxref{Changing Menus}), you should
take a look at the file @file{default.menus} that comes with @code{ne}'s
distribution. It contains a complete menu configuration that clones the standard
one.
@menu
* File::
* Documents::
* Edit::
* Search::
* Macros::
* Extras::
* Navigation::
* Prefs::
@end menu
@node File
@subsection File
The File menu contains standard items that allow loading and saving
files. Quitting @code{ne} (which doesn't save changes) or exiting
@code{ne} (which does save changes) is also possible.
@table @samp
@item Open@dots{}
@xref{Open}.
@item Open New@dots{}
@xref{OpenNew}.
@item Save
@xref{Save}.
@item Save As@dots{}
@xref{SaveAs}.
@item Save All
@xref{SaveAll}.
@item Quit Now
@xref{Quit}.
@item Save&Exit
@xref{Exit}.
@item About
@xref{About}.
@end table
@node Documents
@subsection Documents
The Documents menu contains commands that create new documents, destroy
them, and browse through them.
@table @samp
@item New
@xref{NewDoc}.
@item Clear
@xref{Clear}.
@item Close
@xref{CloseDoc}.
@item Next
@xref{NextDoc}.
@item Prev
@xref{PrevDoc}.
@item Select@dots{}
@xref{SelectDoc}.
@end table
@node Edit
@subsection Edit
The Edit menu contains commands related to cutting and pasting text.
@table @samp
@item Mark Block
@xref{Mark}.
@item Cut
@xref{Cut}.
@item Copy
@xref{Copy}.
@item Paste
@xref{Paste}.
@item Erase
@xref{Erase}.
@item Through
@xref{Through}.
@item Delete Line
@xref{DeleteLine}.
@item Delete EOL
@xref{DeleteEOL}.
@item Mark Vert
@xref{MarkVert}.
@item Paste Vert
@xref{PasteVert}.
@item Open Clip@dots{}
@xref{OpenClip}.
@item Save Clip@dots{}
@xref{SaveClip}.
@end table
@node Search
@subsection Search
The Search menu contains commands related to searching for specific
contents or locations within a document.
@table @samp
@item Find@dots{}
@xref{Find}.
@item Find RegExp@dots{}
@xref{FindRegExp}.
@item Replace@dots{}
@xref{Replace}.
@item Replace Once@dots{}
@xref{ReplaceOnce}.
@item Replace All@dots{}
@xref{ReplaceAll}.
@item Repeat Last
@xref{RepeatLast}.
@item Goto Line@dots{}
@xref{GotoLine}.
@item Goto Col@dots{}
@xref{GotoColumn}.
@item Goto Mark@dots{}
@xref{GotoMark}.
@item Match Bracket
@xref{MatchBracket}.
@item Set Bookmark
@xref{SetBookmark}.
@item Unset Bookmark
@xref{UnsetBookmark}.
@item Goto Bookmark
@xref{GotoBookmark}.
@end table
@node Macros
@subsection Macros
The Macros menu contains commands related to creating and using macros.
@table @samp
@item Record
@xref{Record}.
@item Stop
@xref{Record}.
@item Replace@dots{}
@xref{Replace}.
@item Play Once
@itemx Play Many@dots{}
@xref{Play}.
@item Play Macro@dots{}
@xref{Macro}.
@item Open Macro@dots{}
@xref{OpenMacro}.
@item Save Macro@dots{}
@xref{SaveMacro}.
@end table
@node Extras
@subsection Extras
This menu contains a few special items that don't fit in obvious ways
into other menus.
@table @samp
@item Exec@dots{}
@xref{Exec}.
@item Suspend
@xref{Suspend}.
@item Help@dots{}
@xref{Help}.
@item Refresh
@xref{Refresh}.
@item Undo
@xref{Undo}.
@item Redo
@xref{Redo}.
@item Undel Line
@xref{UndelLine}.
@item Center
@xref{Center}.
@item Shift Right
@itemx Shift Left
@xref{Shift}.
@item Paragraph
@xref{Paragraph}.
@item Adjust View
@itemx Center View
@xref{AdjustView}.
@item ToUpper
@xref{ToUpper}.
@item ToLower
@xref{ToLower}.
@item Capitalize
@xref{Capitalize}.
@end table
@node Navigation
@subsection Navigation
The Navigation menu contains commands related moving around in a
document.
@table @samp
@item Move Left
@xref{MoveLeft}.
@item Move Right
@xref{MoveRight}.
@item Line Up
@xref{LineUp}.
@item Line Down
@xref{LineDown}.
@item Prev Page
@xref{PrevPage}.
@item Next Page
@xref{NextPage}.
@item Page Up
@xref{PageUp}.
@item Page Down
@xref{PageDown}.
@item Start Of File
@xref{MoveSOF}.
@item End Of File
@xref{MoveEOF}.
@item Start Of Line
@xref{MoveSOL}.
@item End Of Line
@xref{MoveEOL}.
@item Top Of Screen
@xref{MoveTOS}.
@item Bottom Of Screen
@xref{MoveBOS}.
@item Incr Up
@xref{MoveIncUp}.
@item Incr Down
@xref{MoveIncDown}.
@item Prev Word
@xref{PrevWord}.
@item Next Word
@xref{NextWord}.
@end table
@node Prefs
@subsection Prefs
The Prefs menu contains commands related to setting, storing, and using
your preferred document flags.
@table @samp
@item Tab Size@dots{}
@xref{TabSize}.
@item Tabs as Spaces
@xref{Tabs}.
@item Insert/Over
@xref{Insert}.
@item Free Form
@xref{FreeForm}.
@item Status Bar
@xref{StatusBar}.
@item Hex Code
@xref{HexCode}.
@item Fast GUI
@xref{FastGUI}.
@item Word Wrap
@xref{WordWrap}.
@item Right Margin
@xref{RightMargin}.
@item Auto Indent
@xref{AutoIndent}.
@item Request Order
@xref{RequestOrder}.
@item Preserve CR
@xref{PreserveCR}.
@item Save CR/LF
@xref{CRLF}.
@item Load Prefs@dots{}
@xref{LoadPrefs}.
@item Save Prefs@dots{}
@xref{SavePrefs}.
@item Load AutoPrefs
@xref{LoadAutoPrefs}.
@item Save AutoPrefs
@xref{SaveAutoPrefs}.
@item Save Def Prefs
@xref{SaveDefPrefs}.
@end table
@node Regular Expressions
@section Regular Expressions
@cindex Regular Expressions
Regular expressions are a powerful way of specifying complex search and
replace operations. @code{ne} supports the full regular expression
syntax on US-ASCII and 8-bit documents, but has to impose a restriction on
character sets when searching in UTF-8 text. @xref{UTF-8 Support}.
@subsection Syntax
The following section is taken (with minor modifications) from the GNU regular
expression library documentation and is Copyright @copyright{} Free Software
Foundation.
A regular expression describes a set of strings. The simplest case is one
that describes a particular string; for example, the string @samp{foo} when
regarded as a regular expression matches @samp{foo} and nothing else.
Nontrivial regular expressions use certain special constructs so that they
can match more than one string. For example, the regular expression
@samp{foo|bar} matches either the string @samp{foo} or the string
@samp{bar}; the regular expression @samp{c[ad]*r} matches any of the strings
@samp{cr}, @samp{car}, @samp{cdr}, @samp{caar}, @samp{cadddar} and all other
such strings with any number of @samp{a}'s and @samp{d}'s.
Regular expressions have a syntax in which a few characters are special
constructs and the rest are @dfn{ordinary}. An ordinary character is a
simple regular expression which matches that character and nothing else. The
special characters are @samp{$}, @samp{^}, @samp{.}, @samp{*}, @samp{+},
@samp{?}, @samp{[}, @samp{]} , @samp{(}, @samp{)} and @samp{\}. Any other
character appearing in a regular expression is ordinary, unless a @samp{\}
precedes it.
For example, @samp{f} is not a special character, so it is ordinary,
and therefore @samp{f} is a regular expression that matches the string @samp{f}
and no other string. (It does @emph{not} match the string @samp{ff}.) Likewise,
@samp{o} is a regular expression that matches only @samp{o}.
Any two regular expressions @var{a} and @var{b} can be concatenated.
The result is a regular expression that matches a string if @var{a}
matches some amount of the beginning of that string and @var{b}
matches the rest of the string.
As a simple example, we can concatenate the regular expressions
@samp{f} and @samp{o} to get the regular expression @samp{fo},
which matches only the string @samp{fo}. Still trivial.
Note: special characters are treated as ordinary ones if they are in
contexts where their special meanings make no sense. For example,
@samp{*foo} treats @samp{*} as ordinary since there is no preceding
expression on which the @samp{*} can act. It is poor practice to depend on
this behaviour; better to quote the special character anyway, regardless of
where is appears.
The following are the characters and character sequences that have special
meaning within regular expressions. Any character not mentioned here is not
special; it stands for exactly itself for the purposes of searching and
matching.
@table @samp
@item .
is a special character that matches anything except a newline. Using
concatenation, we can make regular expressions like @samp{a.b}, which matches
any three-character string which begins with @samp{a} and ends with
@samp{b}.
@item *
is not a construct by itself; it is a suffix, which means the preceding
regular expression is to be repeated as many times as possible. In
@samp{fo*}, the @samp{*} applies to the @samp{o}, so @samp{fo*} matches
@samp{f} followed by any number of @samp{o}'s.
The case of zero @samp{o}'s is allowed: @samp{fo*} does match
@samp{f}.
@samp{*} always applies to the @emph{smallest} possible preceding
expression. Thus, @samp{fo*} has a repeating @samp{o}, not a repeating
@samp{fo}.
@item +
@samp{+} is like @samp{*} except that at least one match for the preceding
pattern is required for @samp{+}. Thus, @samp{c[ad]+r} does not match
@samp{cr} but does match anything else that @samp{c[ad]*r} would match.
@item ?
@samp{?} is like @samp{*} except that it allows either zero or one match for
the preceding pattern. Thus, @samp{c[ad]?r} matches @samp{cr} or @samp{car}
or @samp{cdr}, and nothing else.
@item [ @dots{} ]
@samp{[} begins a @dfn{character set}, which is terminated by a @samp{]}.
In the simplest case, the characters between the two form the set.
Thus, @samp{[ad]} matches either @samp{a} or @samp{d},
and @samp{[ad]*} matches any string of @samp{a}'s and @samp{d}'s
(including the empty string), from which it follows that
@samp{c[ad]*r} matches @samp{car}, @i{et cetera}.
Character ranges can also be included in a character set, by writing two
characters with a @samp{-} between them. Thus, @samp{[a-z]} matches any
lower-case letter. Ranges may be intermixed freely with individual
characters, as in @samp{[a-z$%.]}, which matches any lower case letter or
@samp{$}, @samp{%} or period.
Note that the usual special characters are not special any more inside a
character set. A completely different set of special characters exists
inside character sets: @samp{]}, @samp{-} and @samp{^}. As @samp{\} is
not special inside character sets, you cannot use the shortcuts
@samp{\s} or @samp{\w} there.
To include a @samp{]} in a character set, you must make it
the first character. For example, @samp{[]a]} matches @samp{]} or @samp{a}.
To include a @samp{-}, you must use it in a context where it cannot possibly
indicate a range: that is, as the first character, or immediately
after a range.
Note that when searching in UTF-8 text, a character set may contain
US-ASCII characters only.
@item [^ @dots{} ]
@samp{[^} begins a @dfn{complement character set}, which matches any
character except the ones specified. Thus, @samp{[^a-z0-9A-Z]} matches
all characters @emph{except} letters and digits. Also in this case, when
searching in UTF-8 text a complemented character set may contain US-ASCII
characters only.
@samp{^} is not special in a character set unless it is the first character.
The character following the @samp{^} is treated as if it were first (it may
be a @samp{-} or a @samp{]}).
@item ^
is a special character that matches the empty string -- but only if at the
beginning of a line in the text being matched. Otherwise it fails to match
anything. Thus, @samp{^foo} matches a @samp{foo} that occurs at the
beginning of a line.
@item $
is similar to @samp{^} but matches only at the end of a line. Thus,
@samp{xx*$} matches a string of one or more @samp{x}'s at the end of a
line.
@item \
has two functions: it quotes the above special characters (including
@samp{\}), and it introduces additional special constructs.
Because @samp{\} quotes special characters, @samp{\$} is a regular
expression that matches only @samp{$}, and @samp{\[} is a regular
expression that matches only @samp{[}, and so on.
For the most part, @samp{\} followed by any character matches only that
character. However, there are several exceptions: characters which, when
preceded by @samp{\}, are special constructs. Such characters are always
ordinary when encountered on their own.
@item |
specifies an alternative. Two regular expressions @var{a} and @var{b} with
@samp{|} in between form an expression that matches anything that either
@var{a} or @var{b} will match.
Thus, @samp{foo|bar} matches either @samp{foo} or @samp{bar} but no other
string.
@samp{|} applies to the largest possible surrounding expressions. Only a
surrounding @samp{( @dots{} )} grouping can limit the grouping power of
@samp{|}.
@item ( @dots{} )
is a grouping construct that serves three purposes:
@enumerate
@item
To enclose a set of @samp{|} alternatives for other operations.
Thus, @samp{(foo|bar)x} matches either @samp{foox} or @samp{barx}.
@item
To enclose a complicated expression for the postfix @samp{*} to operate on.
Thus, @samp{ba(na)*} matches @samp{bananana} @i{et cetera}, with any (zero or
more) number of @samp{na}'s.
@item
To mark a matched substring for future reference.
@end enumerate
This last application is not a consequence of the idea of a parenthetical
grouping; it is a separate feature that happens to be assigned as a second
meaning to the same @samp{( @dots{} )} construct because there is no
conflict in practice between the two meanings. Here is an explanation of
this feature:
@item \@var{digit}
After the end of a @samp{( @dots{} )} construct, the matcher remembers the
beginning and end of the text matched by that construct. Then, later on in
the regular expression, you can use @samp{\} followed by @var{digit} to mean
``match the same text matched the @var{digit}'th time by the @samp{(
@dots{} )} construct.'' The @samp{( @dots{} )} constructs are numbered
in order of commencement in the regexp.
The strings matching the first nine @samp{( @dots{} )} constructs appearing
in a regular expression are assigned numbers 1 through 9 in order of their
beginnings.
@samp{\1} through @samp{\9} may be used to refer to the text matched by
the corresponding @samp{( @dots{} )} construct.
For example, @samp{(.+)\1} matches any non empty string that is composed of
two identical halves. The @samp{(.+)} matches the first half, which may be
anything non empty, but the @samp{\1} that follows must match the same exact
text.
@item \b
matches the empty string, but only if it is at the beginning or
end of a word. Thus, @samp{\bfoo\b} matches any occurrence of
@samp{foo} as a separate word. @samp{\bball(s|)\b} matches
@samp{ball} or @samp{balls} as a separate word.
@item \B
matches the empty string, provided it is @emph{not} at the beginning or end
of a word.
@item \<
matches the empty string, but only if it is at the beginning
of a word.
@item \>
matches the empty string, but only if it is at the end of a word.
@item \s
matches any white-space character in US-ASCII. These are @key{tab},
@kbd{@key{Control}-J} (line feed),
@kbd{@key{Control}-k} (vertical tab),
@kbd{@key{Control}-L} (form feed),
@kbd{@key{Control}-M} (carriage return), and space.
@item \w
matches any word-constituent character. These are US-ASCII letters,
numbers and the underscore, independently of the document encoding.
@item \W
matches any character that is not a word-constituent.
@end table
@subsection Replacing regular expressions
Also the replacement string has some special feature when doing a regular
expression search and replace. Exactly as during the search, @samp{\} followed
by @var{digit} stands for ``the text matched the @var{digit}'th time by the
@samp{( @dots{} )} construct in the search expression''. Moreover, @samp{\0}
represent the whole string matched by the regular expression. Thus, for
instance, the replace string @samp{\0\0} has the effect of doubling any string
matched.
Another example: if you search for @samp{(a+)(b+)}, replacing with
@samp{\2x\1}, you will match any string composed by a series of @samp{a}'s
followed by a series of @samp{b}'s, and you will replace it with the
string obtained by moving the @samp{a} in front of the @samp{b}'s, adding
moreover @samp{x} in between. For instance, @samp{aaaab} will be matched and
replaced by @samp{bxaaaa}.
Note that the backslash character can escape itself. Thus, to put a
backslash in the replacement string, you have to use @samp{\\}.
@node Automatic Preferences
@section Automatic Preferences
@cindex Automatic preferences
Automatic preferences let you set up a custom configuration that is
automatically used whenever you open a file with a given extension. For
instance, you may prefer a @sc{tab} size of three when editing C sources, but
eight could be more palatable when writing electronic mail.
The use of autoprefs is definitely straightforward. You simply use the
@samp{Save AutoPrefs} menu item (or the @code{SaveAutoPrefs} command;
@pxref{SaveAutoPrefs}) when the current document has the given extension and
the current configuration suits your tastes. The internal state of a series of
options will be recorded as a macro containing commands that reproduce the
current configuration. The macro is then saved in the @file{~/.ne}
directory (which is created if necessary) with the name given by the
extension, postfixed with @samp{#ap}. Thus, the C sources automatic
preferences file will be named @samp{c#ap}, the one for @TeX{} files @samp{tex#ap},
and so on.
Macros are generated with short or long command names depending on the
status of the verbose macros flag. @xref{VerboseMacros}.
Automatic preferences files are loaded and executed whenever a file with a known
extension is opened. Note that you can manually edit such files, and even
insert commands, but any command that does something other than setting a flag
will be rejected, and an error message will be issued.
@node Emergency Save
@section Emergency Save
@cindex Emergency Save
When @code{ne} is interrupted by an abnormal event (for instance, the
crash of your terminal), it will try to save all unsaved
documents in its current directory. Named documents will have their
names prefixed with a @samp{#}. Unnamed documents will be given names
made up of hexadecimal numbers obtained by some addresses in memory
that will make them unique.
@node UTF-8 Support
@section UTF-8 Support
@cindex UTF-8 Support
@code{ne} can manipulate UTF-8 files and supports
UTF-8 when communicating with the user. At startup, @code{ne} fetches
the system locale description, and checks whether it contains the string
@samp{utf8} or @samp{utf-8}. In this case, it starts communicating with
the user using UTF-8. This behaviour can be modified either using a
suitable command line option (see @pxref{Arguments}), or using
@ref{UTF8IO}. This makes it possible to display and read from the
keyboard a wide range of characters.
Independently of the input/output encoding, @code{ne} keeps track of the
encoding of each document. @code{ne} does not try to select a particular
coding on a document, unless it is forced to do so, for instance because a
certain character is inserted. Once a document has a definite encoding,
however, it keeps it forever.
More precisely, every document may be in one of three @emph{encoding
modes}: US-ASCII, when it is entirely composed of US-ASCII characters;
8-bit, if it contains also other characters, but it is not UTF-8
encoded; and finally, UTF-8, if it is UTF-8-encoded.
The behaviour of @code{ne} in US-ASCII and 8-bit mode is similar to
previous versions: each byte in the document is considered a separate
character.
There are, however, two important differences: first, if I/O is not
UTF-8 encoded, @emph{any} encoding of the ISO-8859 family will work
flawlessly, as @code{ne} merely reads bytes from the keyboard and
displays bytes on the screen. On the contrary, in the case of UTF-8
input/output @code{ne} must take a decision as to which encoding is used
for non-UTF-8 documents, and presently this is hardwired to ISO-8859-1.
Second, 8-bit documents use localized casing and
character type functions. This means that case-insensitive searches or
case foldings will work with, say, Cyrillic characters, provided that
your locale is set correctly.
In UTF-8 mode, instead, @code{ne} interprets the bytes in the document in
a different way---several bytes may encode a single character. The whole
process is completely transparent to the user, but if you really want to
look at the document content, you can switch to 8-bit mode (see
@pxref{UTF8}).
For most operations, UTF-8 support should be transparent. However, in
some cases, in particular when mixing documents with different encodings,
@code{ne} will refuse to perform certain operations because of
incompatible encodings.
The main limitation of UTF-8 documents is that when searching for a
regular expression in a UTF-8 text, character sets may only contain
US-ASCII characters (see @pxref{Regular Expressions}). You can, of
course, partially emulate a full UTF-8 character set implementation
specifying the possible alternatives using @samp{|} (but you have no
ranges).
@node Commands
@chapter Commands
@cindex Commands
Everything @code{ne} can do is specified through a command. Commands can
be manually typed on the command line, bound to a key, to a menu item,
or grouped into macros for easier manipulation. If you want to fully
exploit the power of @code{ne}, you will be faced sooner or later with
using commands directly.
@menu
* General Guidelines::
* File Commands::
* Document Commands::
* Clip Commands::
* Search Commands::
* Macros Commands::
* Undo Commands::
* Formatting Commands::
* Preferences Commands::
* Navigation Commands::
* Editing Commands::
* Support Commands::
@end menu
@node General Guidelines
@section General Guidelines
@cindex Long names
@cindex Short names
@cindex Command arguments
@cindex Flags
@cindex Repeating actions
@cindex Escape conventions
@cindex Quoting conventions
Every command in @code{ne} has a long and a short name. Except in a very
few cases, the short name is given by two or three letters that are the
initials of the words that form the long name. For instance,
@code{SearchBack} has short name @code{SB}, @code{SaveDefPrefs} has the
short name @code{SDP}, and @code{AdjustView}'s short name is @code{AV}.
There are some exceptions however. The most frequently used commands
such as @code{Exit} have one-letter short names (@code{X}). Also some
commands use a different short name to avoid clashes with a more common
command's short name. For example, @code{StatusBar}'s short name is
@code{ST} rather than @code{SB} to avoid clashes with
@code{SearchBack}'s short name.
A command always has at most one argument. This is a chosen limitation
that allows @code{ne}'s parsing of commands and macros to be very fast.
Moreover, it nullifies nearly all problems related to delimiters, escape
characters, and the like. The unique argument can be a number, a string,
or a flag modifier. You can easily distinguish these three cases even
without this manual by looking at what the @code{Help} command says
about the given command. Note that when a command's argument is enclosed
in square brackets, it is optional.
Strings are general purpose arguments. Numbers are used to modify internal
parameters, such as the size of a @sc{tab}. A flag modifier is an optional
number that is interpreted as follows:
@itemize @bullet{}
@item
0 means clearing the flag;
@item
1 (or any positive number) means setting the flag;
@item
no number means toggling the flag.
@end itemize
Thus, @code{StatusBar 1} will activate that status bar, while @code{I} will
toggle insert/overstrike. This design choice is due to the fact that most of
the time during interactive editing you need to @emph{change} a
flag. For instance, you may be in insert mode and you want to overstrike, or
vice versa. Absolute settings (those with a number) are useful essentially
for macros. It is reasonable to use the fastest approach for the most
frequent interactive event. When a number or a string is required and the
argument is optional, most of the time you will be prompted to
type the argument on the command line.
As for the input line, for numeric arguments you can choose between
decimal, octal and hexadecimal notation in the standard way:
a number starting with @samp{0} is considered in octal, a number
starting with @samp{0x} is considered in hexadecimal, and in all other
cases decimal base is assumed.
When a number represents how many times @code{ne} should repeat an
action, it is always understood that the command will terminate when the
conditions for applying it are no longer true. For instance, the
@code{Paragraph} command accepts the number of paragraphs to format.
But if not enough paragraphs exists in the text, only the available ones
will be formatted.
This easily allows performing operations on an entire document by specifying
preposterously huge numbers as arguments. @code{ToUpper 200000000} will
make all the words in the document upper case. (At least, one would
hope so!) Note that this is much faster than recording a macro with
the command @code{ToUpper} in it and playing it many times because in
the former case the command has to be parsed just one time.
In any case, if a macro or a repeated operation takes too long, you can stop
it using the interrupt key (@kbd{@key{Control}-\}).
To handle situations such as an argument string starting with a
space, @code{ne} implements a simple mechanism whereby you can enclose
any string argument in double quotes. If the first non-blank character
after the command and last character of the command line are double
quotes, the quotes will be removed and whatever is left will be used as
the string argument. For example, the @code{Find} command to find a
space could be entered on the command line or in a macro as
@code{Find " "}. The only case needing special treatment is
when a string starts and ends with double quotes. The command
@code{Find ""quote""} would locate the next occurrence of the string
@samp{"quote"} (including the double quotes). However,
@code{Find onequote"} wouldn't require special treatment because the
command argument doesn't both start and end with a double quote.
@ignore
N O T E T O D O C U M E N T M A I N T A I N E R S
It is essential that, in the following sections, all cross references
explaining the text of a command point only to another command, unless
they are inside an @ifclear autohelp / @endif pair. This way, the text
can be automagically extracted for the on-line help with makeinfo.
@end ignore
@node File Commands
@section File Commands
These commands allow opening and saving files. They all act in the
context of the current document (i.e., the document displayed when the
command is issued).
@menu
* Open::
* OpenNew::
* Save::
* SaveAs::
* SaveAll::
@end menu
@node Open
@subsection Open
@cmindex Open
@noindent Syntax: @code{Open [@var{filename}]}@*
@noindent Abbreviation: @code{O}
@noindent replaces the contents of the current document with that
of the file specified by the @var{filename} string. (To load @var{filename}'s
content into a new document without changing the current document,
see @ref{OpenNew}.) The current document's macro, search, and replace
strings are preserved.
If the optional @var{filename} argument is not specified, the
file requester is opened, and you are prompted to select a file. (You
can inhibit the file requester opening by using the @code{NoFileReq}
command; see @ref{NoFileReq}.)
If you escape from the file requester, you can input the file name on the
command line, the default being the current document name, if available.
If the current document is marked as modified at the time the command is
issued, you have to confirm the action.
@node OpenNew
@subsection OpenNew
@cmindex OpenNew
@noindent Syntax: @code{OpenNew [@var{filename}]}@*
@noindent Abbreviation: @code{ON}
@noindent creates a new document and loads into it the contents of the
file specified by the optional @var{filename} string. This new document will
inherit the macro, search, and replace strings from the current
document.
If @var{filename} is unspecified, the file requester behaves the same as
for the @code{Open} command; see @ref{Open}.
@node Save
@subsection Save
@cmindex Save
@noindent Syntax: @code{Save}@*
@noindent Abbreviation: @code{S}
@noindent saves the current document using its default file name.
If the current document is unnamed, the file requester will open and you
will be prompted to select a file. (You can inhibit the file requester opening by
using the @code{NoFileReq} command; see @ref{NoFileReq}.)
If you escape from the file requester, you can input the file name on the
command line.
If the file has been modified since the current document was loaded or last
saved (perhaps by another user), @code{ne} will warn you before overwriting
the updated file. If the current document's read only flag is set, @code{ne}
will prompt you before attempting to save it.
@node SaveAs
@subsection SaveAs
@cmindex SaveAs
@noindent Syntax: @code{SaveAs [@var{filename}]}@*
@noindent Abbreviation: @code{SA}
@noindent saves the current document using the specified string as the file name.
If the optional @var{filename} argument is not specified, the
file requester will open and you will be prompted to select a file. (You
can inhibit the file requester opening by using the @code{NoFileReq}
command; see @ref{NoFileReq}.)
If you escape from the file requester, you can enter the file name on the
input line, the default being the current document name, if available.
If the file has been modified since the current document was loaded or last
saved (perhaps by another user), @code{ne} will warn you before overwriting
the updated file. If the current document's read only flag is set, @code{ne}
will prompt you before attempting to save it.
@node SaveAll
@subsection SaveAll
@cmindex SaveAll
@noindent Syntax: @code{SaveAll}@*
@noindent Abbreviation: @code{SL}
@noindent saves all modified documents. If any modified documents cannot be
saved, the action is suspended and an error message is issued. Note that
only named documents can be saved, so @code{SaveAll} will report an error
if you have any modified unnamed documents. Other reasons @code{SaveAll}
may fail include: if any of the modified documents' corresponding files
have been updated since they were loaded or last saved, or if any modified
documents' read only flags are set.
@node Document Commands
@section Document Commands
These commands allow manipulation of the circular list of documents in @code{ne}.
@menu
* Quit::
* Exit::
* NewDoc::
* Clear::
* CloseDoc::
* NextDoc::
* PrevDoc::
* SelectDoc::
@end menu
@node Quit
@subsection Quit
@cmindex Quit
@noindent Syntax: @code{Quit}@*
@noindent Abbreviation: @code{Q}
@noindent closes all documents and exits. If any documents are modified, you
have to confirm the action.
@node Exit
@subsection Exit
@cmindex Exit
@noindent Syntax: @code{Exit}@*
@noindent Abbreviation: @code{X}
@noindent saves all modified documents, closes them and exits. If
any documents cannot be saved, the action is suspended and an error message
is issued and no documents are closed. Note that only named documents can be
saved, so @code{Exit} will report an error if you have any modified unnamed
documents. Like @code{SaveAll}, @code{Exit} will not save a document if its
corresponding file has been modified since the document was loaded or last
saved, or if its read only flag is set, in which cases an error is reported
and no documents are closed.
@node NewDoc
@subsection NewDoc
@cmindex NewDoc
@noindent Syntax: @code{NewDoc}@*
@noindent Abbreviation: @code{N}
@noindent creates a new, empty, unnamed document that becomes the current document.
The position of the document in the document list is just after the current
document. The preferences of the new document are a copy of the
preferences of the current document.
@node Clear
@subsection Clear
@cmindex Clear
@noindent Syntax: @code{Clear}@*
@noindent Abbreviation: @code{CL}
@noindent destroys the contents of the current document and of its undo
buffer. Moreover, the document becomes unnamed. If your current document is
marked as modified, you have to confirm the action.
@node CloseDoc
@subsection CloseDoc
@cmindex CloseDoc
@noindent Syntax: @code{CloseDoc}@*
@noindent Abbreviation: @code{CD}
@noindent closes the current document. If the document was modified since it was
last saved, you have to confirm the action. If it is the only document, @code{ne}
exits. Otherwise @code{ne} will select the most recently active next or previous
document adjacent to the one you just closed as the new current document. See
@ref{NextDoc}, and @ref{PrevDoc}.
@node NextDoc
@subsection NextDoc
@cmindex NextDoc
@noindent Syntax: @code{NextDoc}@*
@noindent Abbreviation: @code{ND}
@noindent sets as current document the next document in the document list.
See @ref{PrevDoc} and @ref{SelectDoc}.
@node PrevDoc
@subsection PrevDoc
@cmindex PrevDoc
@noindent Syntax: @code{PrevDoc}@*
@noindent Abbreviation: @code{PD}
@noindent sets as current document the previous document in the document list.
See @ref{NextDoc} and @ref{SelectDoc}.
@node SelectDoc
@subsection SelectDoc
@cmindex SelectDoc
@noindent Syntax: @code{SelectDoc}@*
@noindent Abbreviation: @code{SD}
@noindent displays a requester containing the names of all the
documents in memory. Your cursor will be on your current document's
name, and documents with unsaved changes will be indicated with
asterisks (and bold if your terminal supports that). You select
whichever document you want to become the current document by moving
your cursor to its name and hitting @key{Return}.
While the list of documents is displayed, you can close unmodified
documents with your @code{CloseDoc} key (@kbd{@key{Control}-Q}), save
named documents with your @code{Save} key (@kbd{@key{Control}-S}), and
you can alter their relative order using your @code{NextDoc} and
@code{PrevDoc} keys (@key{f2} and @key{f3}), which have the effect of
moving the currently selected document forward or backward in the list,
respectively.
Using plain characters and the @key{BackSpace} navigates your displayed
document names based on their common prefixes. The text between the
beginning and the cursor within the currently selected document's name
is the current common prefix. The @key{Ins} key toggles between hiding
or displaying names not matching the current common prefix. This can be
especially useful if you have a large number of open documents.
If you escape from the requester, the requester goes away, you are
returned to your original current document (unless you closed it!),
and no reordering of documents takes place.
@code{SelectDoc} is especially useful if you have a large number of
documents open or if you want to quickly see which documents contain
unsaved changes. See @ref{NextDoc}, and @ref{PrevDoc}.
@node Clip Commands
@section Clip Commands
These commands control the clipping system. A @dfn{clip} is a snippet of
text separate from any document, which you can save to a file or insert
into a document. You can select text in a document and copy it to a
clip, optionally deleting it from your text. You can also load text
directly from a file into a clip. @code{ne} can have any number of
clips, which are distinguished by an integer. Most clip commands act on
the current clip, which can be selected with @code{ClipNumber}. Clips can
be copied and pasted in two ways---normally (as lines of text) or
vertically (as a rectangular block of characters).
Note that by using the @code{Through} command you can automatically pass a
(possibly vertical) block of text through any filter (such as @code{sort} under
@sc{un*x}).
@menu
* Mark::
* MarkVert::
* Copy::
* Cut::
* Paste::
* PasteVert::
* Erase::
* Shift::
* OpenClip::
* SaveClip::
* ClipNumber::
* Through::
@end menu
@node Mark
@subsection Mark
@cmindex Mark
@noindent Syntax: @code{Mark [0|1]}@*
@noindent Abbreviation: @code{M}
@noindent sets the mark at the current position or cancels the previous mark.
The mark and cursor together define the range of text over which clips
(@code{Cut}, @code{Copy}, @code{Erase}) and left and right shifts operate.
If you invoke @code{Mark} with no arguments, it will set the mark. If you
specify 0 or 1, the mark will be cancelled or set to the current position,
respectively. A capital @samp{M} appears on the status bar, if the mark is
active.
@node MarkVert
@subsection MarkVert
@cmindex MarkVert
@noindent Syntax: @code{MarkVert [0|1]}@*
@noindent Abbreviation: @code{MV}
@noindent is the same as @code{Mark}, but the region manipulated by the
cut/paste commands is the rectangle having as vertices the cursor and the mark.
If you invoke @code{MarkVert} with no arguments, it will set the mark. If you
specify 0 or 1, the mark will be cancelled or set to the current position,
respectively. Moreover, a capital @samp{V}, rather than a capital @samp{M},
will appear on the status bar.
For example, if you have the following text:
@example
aaaBbbccc
aaabbbccc
aaabbbCcc
@end example
@noindent and you set a vertical mark at @samp{B} then move the cursor to @samp{C},
you can cut or copy all of the @samp{B}s.
If you have made a vertical cut or copy, it's very likely you will want to
use @code{PasteVert} rather than the usual @code{Paste} to reinsert the text
in a rectangle. See @ref{PasteVert}.
@node Copy
@subsection Copy
@cmindex Copy
@noindent Syntax: @code{Copy [@var{n}]}@*
@noindent Abbreviation: @code{C}
@noindent copies the contents of the characters lying between the cursor
and the mark into the clip specified by the optional numeric argument, the
default clip being the current clip, which can be set with the
@code{ClipNumber} command; see @ref{ClipNumber}. If the current mark was
vertical, the rectangle of characters defined by the cursor and the mark is
copied instead.
@node Cut
@subsection Cut
@cmindex Cut
@noindent Syntax: @code{Cut [@var{n}]}@*
@noindent Abbreviation: @code{CU}
@noindent acts just like @code{Copy}, but also deletes the block being copied.
@node Paste
@subsection Paste
@cmindex Paste
@noindent Syntax: @code{Paste [@var{n}]}@*
@noindent Abbreviation: @code{P}
@noindent pastes the contents of specified clip into the current
document at the cursor position. If you don't specify the clip number,
the current clip is used; Specify which clip is current with @ref{ClipNumber}.
Every time you paste into a document, a pair of bookmarks
designated by @samp{<} and @samp{>} mark the start and end points of the
text you pasted. Subsequently you can move to either end of a pasted block
with the @code{GotoBookmark <} or @code{GotoBookmark >} commands.
@node PasteVert
@subsection PasteVert
@cmindex PasteVert
@noindent Syntax: @code{PasteVert [@var{n}]}@*
@noindent Abbreviation: @code{PV}
@noindent vertically pastes the contents of the specified clip, the default
being the current clip. Each line of the clip is inserted on consecutive
lines at the horizontal cursor position.
Every time you paste into a document, a pair of bookmarks
designated by @samp{<} and @samp{>} mark the start and end points of the
text you pasted. Subsequently you can move to either end of a pasted block
with the @code{GotoBookmark <} or @code{GotoBookmark >} commands.
@node Erase
@subsection Erase
@cmindex Erase
@noindent Syntax: @code{Erase}@*
@noindent Abbreviation: @code{E}
@noindent acts like @code{Cut}, but the block is just deleted and not
copied into any clip.
@node Shift
@subsection Shift
@cmindex Shift
@noindent Syntax: @code{Shift [<|>][n][t|s]}@*
@noindent Abbreviation: @code{SH}
@noindent shifts the text on lines between the mark and the cursor either
right (@samp{>}) or left (@samp{<}) by adding or removing
white space on each line. The adjustment size, specified as an unsigned
integer @samp{n}, is in units of the current tab size (@samp{t}) or spaces
(@samp{s}). The defaults are @samp{>}, @samp{1}, and @samp{t}. Adjustments
start at the left edge of a vertical mark, or column 1 otherwise. If the mark
is not currently set, only the current line is affected.
@code{Shift} will insert tab characters only if @samp{s} is not used, and both
of the document's @code{Tabs} and @code{ShiftTabs} flags are set---in which
case an upper case @samp{T} will appear in the status bar. If either of the
@code{Tabs} or @code{ShiftTabs} flags is unset (i.e there is no upper case
@samp{T} in the status bar) @code{Shift} will only insert spaces.
In the case of left shifts, if any indicated line has insufficient leading
white space for the requested adjustment to be made, then @code{Shift}
reports an error and makes no changes.
@node OpenClip
@subsection OpenClip
@cmindex OpenClip
@noindent Syntax: @code{OpenClip [@var{filename}]}@*
@noindent Abbreviation: @code{OC}
@noindent loads the given file name as the current clip, just as if you
cut or copied it from the current document; see @ref{Copy}.
If the optional @var{filename} argument is not specified, the
file requester will open and you will be prompted to select a file. (You
can inhibit the file requester opening by using the @code{NoFileReq}
command; see @ref{NoFileReq}.)
If you escape from the file requester, you can enter the file name on the
input line.
@node SaveClip
@subsection SaveClip
@cmindex SaveClip
@noindent Syntax: @code{SaveClip [@var{filename}]}@*
@noindent Abbreviation: @code{SC}
@noindent saves the current clip to the given file name.
If the optional @var{filename} argument is not specified, the
file requester will open and you will be prompted to select a file. (You
can inhibit the file requester opening by using the @code{NoFileReq}
command; see @ref{NoFileReq}.)
If you escape from the file requester, you can enter the file name on the
input line.
@node ClipNumber
@subsection ClipNumber
@cmindex ClipNumber
@noindent Syntax: @code{ClipNumber [@var{n}]}@*
@noindent Abbreviation: @code{CN}
@noindent sets the current clip number. This number is used by
@code{OpenClip} and @code{SaveClip}, and by @code{Copy}, @code{Cut} and
@code{Paste} if they are called without any argument. Its default value is
zero. @var{n} is limited only by the integer size of the machine @code{ne}
is running on.
If the optional argument @var{n} is not specified, you can enter it on the
input line, the default being the current clip number.
@node Through
@subsection Through
@cmindex Through
@noindent Syntax: @code{Through [@var{command}]}@*
@noindent Abbreviation: @code{T}
@noindent asks the shell to execute @var{command}, piping the current block in
the standard input, and replacing it with the output of the command. This
command is most useful with filters, such as @code{sort}. Its practical effect
is to pass the block through the specified filter.
Note that by selecting an empty block (or equivalently by having the mark
unset) you can use @code{Through} to insert the output of any @sc{un*x}
command in your file.
If the optional argument @var{command} is not specified, you can enter it on
the input line.
@node Search Commands
@section Search Commands
These commands control the search system. @code{ne} offers two complementary
searching techniques: a simple, fast exact matching search (optionally
ignoring case), and a very flexible and powerful, but slower, regular
expression search based on the GNU @code{regex} library (again,
optionally case insensitive).
@menu
* Find::
* FindRegExp::
* Replace::
* ReplaceOnce::
* ReplaceAll::
* RepeatLast::
* MatchBracket::
* AutoMatchBracket::
* SearchBack::
* CaseSearch::
* AutoComplete::
@end menu
@node Find
@subsection Find
@cmindex Find
@noindent Syntax: @code{Find [@var{pattern}]}@*
@noindent Abbreviation: @code{F}
@noindent searches for the given pattern. The cursor is positioned on the
first occurrence of the pattern, or an error message is given. The direction
and the case sensitivity of the search are established by the value of the
back search and case sensitive search flags. See @ref{SearchBack}, and
@ref{CaseSearch}.
If the optional argument @var{pattern} is not specified, you can enter it on
the input line, the default being the last pattern used.
@node FindRegExp
@subsection FindRegExp
@cmindex FindRegExp
@noindent Syntax: @code{FindRegExp [@var{pattern}]}@*
@noindent Abbreviation: @code{FX}
@noindent searches the current document for the given extended regular expression
@ifclear autohelp
(@pxref{Regular Expressions})
@end ifclear
. The cursor is positioned on the first string
matching the expression. The direction and the kind of search are established
by the value of the back search and case sensitive search flags. See
@ref{SearchBack}, and @ref{CaseSearch}.
If the optional argument @var{pattern} is not specified, you can enter it on
the input line, the default being the last pattern used.
@node Replace
@subsection Replace
@cmindex Replace
@noindent Syntax: @code{Replace [@var{string}]}@*
@noindent Abbreviation: @code{R}
@noindent moves to the first match of the most recent find string or regular
expression and prompts you for which action to perform. You can choose among:
@itemize @bullet
@item
replacing the string found with the given string and moving to the next match
(@samp{Yes});
@item
moving to the next match (@samp{No});
@item
replacing the string found with the given string, and stopping the search (@samp{Last});
@item
stopping the search immediately (@samp{Quit});
@item
replacing @emph{all} occurrences of the find string with the given string
(@samp{All});
@item
reversing the search direction (@samp{Backward} or @samp{Forward});
this choice will also modify the value of the back search flag.
See @ref{SearchBack}.
@end itemize
@code{Replace} is mainly useful for interactive editing. @code{ReplaceOnce},
@code{ReplaceAll} and @code{RepeatLast} are more suited to macros.
If no find string was ever specified, you can enter it on the input line.
If the optional argument @var{string} is not specified, you can enter it on
the input line, the default being the last string used. When the last search
was a regular expression search, there are some special
features you can use in the replace string
@ifclear autohelp
(@pxref{Regular Expressions})
@end ifclear
. See @ref{FindRegExp}.
Note that normally a search starts just one character after the cursor.
However, when @code{Replace} is invoked, the search starts at the character
just @emph{under} the cursor, so that you can safely @code{Find} a pattern
and @code{Replace} it without having to move back.
@noindent @strong{Warning:} when recording a macro with @ref{Record}, there
is no trace in the macro of your interaction with @code{ne} during the
replacement process. When the macro is played, you will again have to choose
which actions to perform. If you want to apply automatic replacement of
strings for a certain number of times, you should look at @ref{ReplaceOnce},
@ref{ReplaceAll}, and @ref{RepeatLast}.
@node ReplaceOnce
@subsection ReplaceOnce
@cmindex ReplaceOnce
@noindent Syntax: @code{ReplaceOnce [@var{string}]}@*
@noindent Abbreviation: @code{R1}
@noindent acts just like @code{Replace}, but without any interaction with you
(unless there is no find string). The first string matched by the last search
pattern, if it exists, is replaced by the given replacement string.
If the optional argument @var{string} is not specified, you can enter it on
the input line, the default being the last string used.
@node ReplaceAll
@subsection ReplaceAll
@cmindex ReplaceAll
@noindent Syntax: @code{ReplaceAll [@var{string}]}@*
@noindent Abbreviation: @code{RA}
@noindent is similar to @code{ReplaceOnce}, but replaces @emph{all}
occurrences of the last search pattern between the cursor position and the
end of the document (in the direction indicated by the @code{SearchBack} flag)
with the given replacement string.
If the optional argument @var{string} is not specified, you can enter it on
the input line, the default being the last string used.
Note that a single @code{Undo} will restore @emph{all} the occurrences of the search
pattern replaced by @code{ReplaceAll}. See @ref{Undo}.
@node RepeatLast
@subsection RepeatLast
@cmindex RepeatLast
@noindent Syntax: @code{RepeatLast [@var{times}] [F|Find|R|Replace]}@*
@noindent Abbreviation: @code{RL}
@noindent repeats for the given number of times the last find or replace operation
(with replace we mean here a single replace, even if the last @code{Replace}
operation ended with a global substitution). If you don't specify either
@var{Find} or @var{Replace}, it will repeat whichever one was last performed.
If any find or replace operation runs into the end of the document (in the direction
indicated by the @code{SearchBack} flag) and stops, then and only then will a
@code{RepeatLast} command ``wrap around'' to the other end of the document to
continue the find or replace operation.
@code{RepeatLast} is especially useful for researching a given number of times, or
replacing something a given number of times. The standard technique for
accomplishing this is:
@enumerate
@item
@code{Find} (or @code{FindRegExp}) the string you are interested in;
@item
if you want to repeat a replace operation, @code{ReplaceOnce} with
the replacement string you are interested in;
@item
now issue a @code{RepeatLast @var{n}-1} command, where @var{n} is the
number of occurrences you wanted to skip over, or replace.
@end enumerate
The important thing about this sequence of actions is that it will
work this way even in a macro. The @code{Replace} command cannot be used
in a macro unless you really want to interact with @code{ne} during the
macro execution. Avoiding interaction during macros is the primary
reason the commands @code{ReplaceAll} and @code{ReplaceOnce} are
provided.
@node MatchBracket
@subsection MatchBracket
@cmindex MatchBracket
@noindent Syntax: @code{MatchBracket}@*
@noindent Abbreviation: @code{MB}
@noindent moves the cursor to the bracket associated with the bracket the
cursor is on. If the cursor is not on a bracket, or there is no bracket
associated with the current one, an error message is issued. Recognized
brackets are @samp{@{@}}, @samp{()}, @samp{[]} @samp{<>}, and @samp{`'}.
See @ref{AutoMatchBracket}.
@node AutoMatchBracket
@subsection AutoMatchBracket
@cmindex AutoMatchBracket
@noindent Syntax: @code{AutoMatchBracket [0..15]}@*
@noindent Abbreviation: @code{AMB}
@noindent sets the auto match bracket mode. When the cursor is on a
recognized bracket (@samp{@{@}}, @samp{()}, @samp{[]}, @samp{<>}, or @samp{`'}) and
the associated matching bracket is on the screen, that matching bracket
will be indicated according to the mode. The mode is either zero for no
bracket matching, or the sum of 1 (altered foreground and background
brightness), 2 (inverse), 4 (bold), and 8 (underline). If no mode is specified,
@code{ne} prompts you for one. The default mode is 1. See @ref{MatchBracket}.
@node SearchBack
@subsection SearchBack
@cmindex SearchBack
@noindent Syntax: @code{SearchBack [0|1]}@*
@noindent Abbreviation: @code{SB}
@noindent sets the back search flag. When this flag is true, every search or
replacement command is performed backwards.
If you invoke @code{SearchBack} with no arguments, it will toggle the flag. If you
specify 0 or 1, the flag will be set to false or true, respectively. A lower
case @samp{b} will appear on the status bar if the flag is true.
Note that this flag also can be set through interactions with the
@code{Replace} command. See @ref{Replace}.
@node CaseSearch
@subsection CaseSearch
@cmindex CaseSearch
@noindent Syntax: @code{CaseSearch [0|1]}@*
@noindent Abbreviation: @code{CS}
@noindent sets the case sensitivity flag. When this flag is true, the
search commands distinguish between the upper and lower case letters. By
default the flag is false.
If you invoke @code{CaseSearch} with no arguments, it will toggle the flag. If you
specify 0 or 1, the flag will be set to false or true, respectively. A lower
case @samp{c} will appear on the status bar if the flag is true.
@node AutoComplete
@subsection AutoComplete
@cmindex AutoComplete
@noindent Syntax: @code{AutoComplete [@var{prefix}]}@*
@noindent Abbreviation: @code{AC}
@noindent attempts to extend the @var{prefix} using matching words from your
open documents, and inserts the extended text into your document. If the
@var{prefix} can be extended unambiguously, the matching text is immediately
inserted into your document. Otherwise, @code{ne} displays a
selection of all words in open documents that match @var{prefix}, and inserts
the word you select into the current document.
Matching words from the current document display normally; those which only
exist in other open documents are bold and with a trailing asterisk. If no
@var{prefix} is given on the command line, or if @code{AutoComplete} is
selected from the @code{Extras} menu or using a keyboard shortcut, the word
characters to the immediate left of the cursor in the current document are used
as the @var{prefix}. Note that if no word characters are to the left of the
cursor, or the @var{prefix} given on the command line is an empty string
(@code{""}), then all words in all your open documents are displayed. Prefix
matches may be case sensitive or not depending on the current document's
@code{CaseSearch} flag state. See @ref{CaseSearch}.
@node Macros Commands
@section Macros Commands
Macros are lists of commands. Any series of operations that has to be
performed frequently is a good candidate for being a macro.
Macros can be written manually: they are just ASCII files, each command
occupying a line (lines starting with @samp{#} are considered comments;
lines starting with other nonalphabetical characters are presently
ignored). But the real power of macros is that they be recorded during
the normal usage of @code{ne}. When the recording terminates, the
operations that have been recorded can be saved for later use. Note that
each document has its own current macro (the last macro that has been opened
or recorded).
@menu
* Record::
* Play::
* Macro::
* OpenMacro::
* SaveMacro::
* UnloadMacros::
@end menu
@node Record
@subsection Record
@cmindex Record
@noindent Syntax: @code{Record [0|1]}@*
@noindent Abbreviation: @code{REC}
@noindent starts, stops, cancels, or resumes macro recording. With no
arguments, @code{Record} starts recording your commands as a new macro
unless recording is already underway. In that case, macro recording is
stopped, and the newly recorded macro replaces the current document's
unnamed macro. The new macro can be played or saved via @ref{Play},
or @ref{SaveMacro}. The default key binding for @kbd{@key{Control}-T}
is @code{Record} with no arguments, and is by far the most common way
to use the @code{Record} command.
If you've started recording a macro and wish to cancel rather than wipe
out your existing macro, you can cancel the recording by using @code{Record 0}.
An error will be displayed if you aren't recording already, but it's harmless.
Sometimes you've got a macro either recorded or loaded from a file
(see @ref{OpenMacro}), but you'd like to record additional commands onto
the end of it. @code{Record 1} will do that. It will start recording onto
the end of a copy of your current document's macro. An error will be displayed
if you are already recording, but it's otherwise harmless.
@node Play
@subsection Play
@cmindex Play
@noindent Syntax: @code{Play [@var{times}]}@*
@noindent Abbreviation: @code{PL}
@noindent plays the current document's macro the given number of times. If the
optional argument @var{times} is not specified, you can enter it on the
input line.
A (possibly iterated) macro execution terminates as soon as its stream of
instructions is exhausted, or one of its commands returns an error. This means
that, for instance, you can perform some complex operation on all the lines
containing a certain pattern by recording a macro that searches for the pattern
and performs the operation, and then playing it a preposterously huge number of
times.
Execution of a macro can be interrupted by @kbd{@key{Control}-\}.
@node Macro
@subsection Macro
@cmindex Macro
@noindent Syntax: @code{Macro [@var{filename}]}@*
@noindent Abbreviation: @code{MA}
@noindent executes the given file name as a macro.
If the optional @var{filename} argument is not specified, the file
requester is opened, and you are prompted to select a file. (You can
inhibit the file requester opening by using the @code{NoFileReq}
command; see @ref{NoFileReq}.)
If you escape from the file requester, you can input the file name on the
command line.
Note that macros whose names do not conflict with a command can be called
without using @code{Macro}. Whenever @code{ne} is required to perform a command
it cannot find in its internal tables, it will look for a macro by that
name in the current directory. If this search also fails, @code{ne} looks in
@file{~/.ne} and finally in the @file{macros} subdirectory of @code{ne}'s
global directory (defined when @code{ne} was built, or in a place specified by
your @code{NE_GLOBAL_DIR} environment variable) for a macro file by that name.
@noindent @strong{Warning:} the first time a macro is executed it is cached
into a hash table and is kept @emph{forever} in memory unless the
@code{UnloadMacros} command is issued; see @ref{UnloadMacros}. The next time a
macro with the same file name is invoked, the cached list is searched for it
before accessing the file using a case insensitive string comparison. That is,
if you call @code{~/foobar/macro}, a subsequent call for @code{/usr/MACRO} or
even just @code{MaCrO} will use the cached version of @code{~/foobar/macro}.
Note that the cache table is global to @code{ne} and not specific to any single
document. This greatly improves efficiency when macros are used repeatedly.
@node OpenMacro
@subsection OpenMacro
@cmindex OpenMacro
@noindent Syntax: @code{OpenMacro [@var{filename}]}@*
@noindent Abbreviation: @code{OM}
@noindent loads the given file name as the current document's macro just as if you
@code{Record}ed it; see @ref{Record}.
If the optional @var{filename} argument is not specified, the
file requester is opened, and you are prompted to select a file. (You
can inhibit the file requester opening by using the @code{NoFileReq}
command; see @ref{NoFileReq}.)
If you escape from the file requester, you can input the file name on the
command line.
@node SaveMacro
@subsection SaveMacro
@cmindex SaveMacro
@noindent Syntax: @code{SaveMacro [@var{filename}]}@*
@noindent Abbreviation: @code{SM}
@noindent saves the current document's macro in a file with the given name.
If the optional @var{filename} argument is not specified, the
file requester is opened, and you are prompted to select a file. (You
can inhibit the file requester opening by using the @code{NoFileReq}
command; see @ref{NoFileReq}.)
If you escape from the file requester, you can input the file name on the
command line.
@code{SaveMacro} is of course most useful for saving macros you just
recorded. The macros can then be loaded as normal text files for
further editing, if necessary. Note that @code{SaveMacro} converts
@code{InsertChar} commands into a possibly smaller number of
@code{InsertString} commands. This makes macros easier to
read and edit. See @ref{InsertChar}, and @ref{InsertString}.
@node UnloadMacros
@subsection UnloadMacros
@cmindex UnloadMacros
@noindent Syntax: @code{UnloadMacros}@*
@noindent Abbreviation: @code{UM}
@noindent frees the macro cache list. After this command, the @code{Macro}
command will be forced to search for the file containing the macros it has to
play.
@code{UnloadMacros} is especially useful if you are experimenting with a macro
bound to some keystroke, and you are interactively modifying it and playing it.
@code{UnloadMacros} forces @code{ne} to look for the newer version available.
@node Undo Commands
@section Undo Commands
The following commands control the undo system.
@menu
* Undo::
* Redo::
* UndelLine::
* DoUndo::
* AtomicUndo::
@end menu
@node Undo
@subsection Undo
@cmindex Undo
@noindent Syntax: @code{Undo [@var{n}]}@*
@noindent Abbreviation: @code{U}
@noindent undoes the last @var{n} actions. If @var{n} is not specified, it is
assumed to be one. After you undo a number of actions, you can @code{Redo}
all or some of them; see @ref{Redo}. However, if you take any new
actions after having @code{Undo}ne some, you can no longer @code{Redo}
those @code{Undo}ne actions. See @ref{Redo}.
@node Redo
@subsection Redo
@cmindex Redo
@noindent Syntax: @code{Redo [@var{n}]}@*
@noindent Abbreviation: @code{RE}
@noindent redoes the last @var{n} actions undone by @code{Undo} (as long
as you don't take any actions that change the text between the
@code{Undo} and @code{Redo} commands). If @var{n} is not specified, it
is assumed to be one. You can only @code{Redo} actions that have been
@code{Undo}ne. See @ref{Undo}.
@node UndelLine
@subsection UndelLine
@cmindex UndelLine
@noindent Syntax: @code{UndelLine [@var{n}]}@*
@noindent Abbreviation: @code{UL}
@noindent inserts at the cursor position for @var{n} times the last non-empty line
that was deleted with the @code{DeleteLine} command. If @var{n} is not
specified, it is assumed to be one.
@code{UndelLine} is most useful in that it allows a very fast way of moving
one line around. Just delete it, and undelete it somewhere else. It is
also an easy way to replicate a line without getting involved with clips.
Note that @code{UndelLine} works independently of the status of the
undo flag. See @ref{DoUndo}.
@node DoUndo
@subsection DoUndo
@cmindex DoUndo
@noindent Syntax: @code{DoUndo [0|1]}@*
@noindent Abbreviation: @code{DU}
@noindent sets the flag that enables or disables the undo system. When you turn
the undo system off, all the recorded actions are discarded, and the undo
buffers are reset.
If you invoke @code{DoUndo} with no arguments, it will toggle the flag. If you
specify 0 or 1, the flag will be set to false or true, respectively. A lower
case @samp{u} will appear on the status bar if the flag is true. (The
@samp{U} will be upper case if the flag is true and the @code{AtomicUndo}
level is non-zero.)
The usefulness of this option relies in the fact that the undo system is a major
memory eater. If you plan to do massive editing (say, cutting and pasting
megabytes of text) it is a good idea to disable the undo system, both for
improving (doubling) performance and for using less (half) memory. Except for
this, on a virtual memory system we see no reason to not keep the undo flag
always true, and this is indeed the default.
@node AtomicUndo
@subsection AtomicUndo
@cmindex AtomicUndo
@noindent Syntax: @code{AtomicUndo [0|+|-]}@*
@noindent Abbreviation: @code{AU}
@noindent increases, decreases, sets or clears the @code{AtomicUndo} level.
The normal level is zero. All current document changes made while the
@code{AtomicUndo} level is above zero are treated as a single change by the
@code{Undo} and @code{Redo} commands. If no parameter is given, a level of 0
is set to 1; otherwise the current non-zero level is decremented. If 0 is
given, the level is reset to zero. Parameters of @samp{+} and @samp{-} respectively
increment and decrement the level, which in no case can be negative. If the
level is above zero, the @code{DoUndo} flag in the status bar, which is
normally a lower-case @samp{u}, becomes upper case @samp{U}.
Two other actions will reset the @code{AtomicUndo} level to zero: invoking the
@code{Undo} command, and disabling the undo system with the @code{DoUndo}
command. You cannot set a non-zero @code{AtomicUndo} level unless the undo
system is enabled.
Note: macros that you wish to undo and redo atomically---i.e., as if they
were single commands---should begin with @code{AtomicUndo +} and end with
@code{AtomicUndo -} so that they can call and/or be called by other macros.
@node Formatting Commands
@section Formatting Commands
The following commands allow simple formatting operations on the text. Note
that for @code{ne} a paragraph is delimited by an empty line or a line with
leading white space incongruous with that of preceding lines.
@menu
* Center::
* Paragraph::
* ToUpper::
* ToLower::
* Capitalize::
* RightMargin::
* WordWrap::
* AutoIndent::
@end menu
@node Center
@subsection Center
@cmindex Center
@noindent Syntax: @code{Center [@var{n}]}@*
@noindent Abbreviation: @code{CE}
@noindent centers @var{n} lines from the cursor position onwards. If @var{n}
is not specified, it is assumed to be one. The lines are centered with
spaces, relatively to the value of the right margin as set by the
@code{RightMargin} command. See @ref{RightMargin}.
@node Paragraph
@subsection Paragraph
@cmindex Paragraph
@noindent Syntax: @code{Paragraph [@var{n}]}@*
@noindent Abbreviation: @code{PA}
@noindent reformats @var{n} paragraphs from the cursor position onwards. If
@var{n} is not specified, it is assumed to be one. The paragraphs are
formatted relatively to the value of the right margin as set by the
@code{RightMargin} command. See @ref{RightMargin}.
@code{ne}'s notion of a paragraph includes the current non-blank line
(regardless of its leading white space) and all subsequent non-blank lines
that have identical (to each other's---not to the first line's) leading
white space. Therefore your paragraphs can have various first line
indentations and left margins.
@ignore
Note: the following was true, briefly, but was removed because it
sometimes broke "normal" text that contained certain characters.
I'm leaving this here in case someday we reimplement this feature.
@code{Paragraph} also takes into account
characters commonly used at the left edge of block comments (@samp{/},
@samp{*}, @samp{#}, spaces, and tabs) or quoted email (@samp{>}), and
attempts to preserve those on the left edge when possible.
@end ignore
After the @code{Paragraph} command completes, your cursor will be
positioned on the first non-blank character after the last reformatted
paragraph (or, if there is no such character, at the end of the document).
@code{Paragraph} does not insert ``smart'' spaces after full stops and colons,
nor does it do other ``smart'' things such as justification. If you need such
facilities, you should consider using a text formatter. @TeX{} for example is
usually an excellent choice.
@node ToUpper
@subsection ToUpper
@cmindex ToUpper
@noindent Syntax: @code{ToUpper [@var{n}]}@*
@noindent Abbreviation: @code{TU}
@noindent shifts to upper case the letters from the cursor position up to the
end of a word, and moves to the first letter of next word for @var{n} times.
The description of the command may seem a little bit cryptic. What is really
happening is that there are situations where you only want to upper case the
last part of a word. In this case, you just have to position the cursor in
the first character you want to upper case, and use @code{ToUpper} with no
argument.
If you apply @code{ToUpper} on the first character of a word, it will
just upper case @var{n} words.
@node ToLower
@subsection ToLower
@cmindex ToLower
@noindent Syntax: @code{ToLower [@var{n}]}@*
@noindent Abbreviation: @code{TL}
@noindent acts exactly like @code{ToUpper}, but lowers the case. See @ref{ToUpper}.
@node Capitalize
@subsection Capitalize
@cmindex Capitalize
@noindent Syntax: @code{Capitalize [@var{n}]}@*
@noindent Abbreviation: @code{CA}
@noindent acts exactly like @code{ToUpper}, but capitalizes, that is, makes the first
letter upper case and the other ones lower case. See @ref{ToUpper}.
@node RightMargin
@subsection RightMargin
@cmindex RightMargin
@noindent Syntax: @code{RightMargin [@var{n}]}@*
@noindent Abbreviation: @code{RM}
@noindent sets the right margin for all formatting operations, and for @code{WordWrap}.
See @ref{WordWrap}.
If the optional argument @var{n} is not specified, you can enter it on the
input line, the default being the current value of the right margin.
A value of zero for @var{n} will force @code{ne} to use (what it thinks it is)
the current screen width as right margin.
@node WordWrap
@subsection WordWrap
@cmindex WordWrap
@noindent Syntax: @code{WordWrap [0|1]}@*
@noindent Abbreviation: @code{WW}
@noindent sets the word wrap flag. When this flag is true, @code{ne} will
automatically break lines of text when you attempt to insert characters beyond
the right margin. See @ref{RightMargin}.
@ignore
See ignored section in "paragraph" discussion.
@code{ne} will attempt to preserve
certain invariant characters at the left edge of paragraphs typically used to
indicate comments in source code or quoted passages in email text. See @ref{Paragraph}.
@end ignore
If you invoke @code{WordWrap} with no arguments, it will toggle the flag. If you
specify 0 or 1, the flag will be set to false or true, respectively. A lower
case @samp{w} will appear on the status bar if the flag is true.
@node AutoIndent
@subsection AutoIndent
@cmindex AutoIndent
@noindent Syntax: @code{AutoIndent [0|1]}@*
@noindent Abbreviation: @code{AI}
@noindent sets the auto indent flag. When this flag is true, @code{ne} will
automatically insert @sc{tab}s and spaces on a new line (created by an
@code{InsertLine} command, or by automatic word wrapping) in such a way to
replicate the initial spaces of the previous line. Most useful for indenting
programs.
If you invoke @code{AutoIndent} with no arguments, it will toggle the flag. If you
specify 0 or 1, the flag will be set to false or true, respectively. A lower
case @samp{a} will appear on the status bar if the flag is true.
@code{AutoIndent} features a nice interaction with @code{Undo}. Whenever a
new line is created, the insertion of spaces is recorded as a separate
action in the undo buffer (with respect to the line creation). If you are
not satisfied with the indentation, just give the @code{Undo} command and
the indentation will disappear (but the new line will remain in place, since
its creation has been recorded as a separate action). See @ref{Undo}.
@node Preferences Commands
@section Preferences Commands
These commands allow you to set your preferences, that is, the value of
a series of flags that modify the behaviour of @code{ne}. (Some of the
flag commands, like the command for the indent flag, appear in other
sections.) The status of the flags can be saved and restored later
either by writing them out to a file (saved as a macro that suitably
sets the flags) or by pushing them onto a ``preferences stack''. The
back search and the read only flags are not saved, because they do not
represent a preference, but rather a temporary state. The escape time
and the turbo parameter are global to @code{ne}, and are not
saved. However, you can add manually to a preferences file any
preferences command (such as @code{EscapeTime} or @code{Turbo});
usually, this will be done to the default preferences file
@file{~/.ne/.default#ap}.
Note that there is an automatic preferences system, which automagically loads a
preferences file related to the extension of the file name. Automatic
preferences files are kept in your @file{~/.ne} directory. They
are named as an extension postfixed with @samp{#ap}. Each time you open
a file whose name has an extension for which there is an automatic
preferences file, the latter is executed. Opening a file which has no
extension causes the prefs from @file{~/.ne/.default#ap} to be loaded.
If you want to inhibit this process, you can clear the automatic
preferences flag. See @ref{AutoPrefs}.
@menu
* Flags::
* AutoPrefs::
* Binary::
* Insert::
* FastGUI::
* FreeForm::
* NoFileReq::
* RequestOrder::
* StatusBar::
* HexCode::
* ReadOnly::
* EscapeTime::
* TabSize::
* Tabs::
* DelTabs::
* ShiftTabs::
* Turbo::
* VerboseMacros::
* PreserveCR::
* CRLF::
* BracketedPaste::
* VisualBell::
* PushPrefs::
* PopPrefs::
* LoadPrefs::
* SavePrefs::
* LoadAutoPrefs::
* SaveAutoPrefs::
* SaveDefPrefs::
* Modified::
* Syntax::
* UTF8::
* UTF8Auto::
* UTF8IO::
@end menu
@node Flags
@subsection Flags
@cmindex Flags
@noindent Syntax: @code{Flags}@*
@noindent Abbreviation: @code{FLAG}
@noindent displays a list of all the status flags for ne and their
associated commands. It is not recorded when recording a macro.
@example
FLAG COMMAND ABBR DESCRIPTION
i Insert I inserts new characters (vs. replacing)
a AutoIndent AI aligns cursor under previous line after
b SearchBack SB searches search backward rather than forward
c CaseSearch CS searches are case sensitive
w WordWrap WW breaks long lines as you type
f FreeForm FF allows cursor to move beyond the end of lines
p AutoPrefs AP use automatic preferences based on file extension
v VerboseMacros VM record macros using use long command names
u DoUndo DU record edits for later undoing
r ReadOnly RO changes are not allowed/saves are prompted
t/T Tabs TAB TAB key inserts TABs instead of spaces
T ShiftTabs SHT Shift may insert TABs (only if 't' is also set)
d DelTabs DT BS and DEL may remove tabs worth of space
B/! Binary B affects file loading/saving
M Mark M mark set for line-oriented block operations
V MarkVert MV like mark, but block is rectangle
R Record REC actions are being recorded in a macro
P PreserveCR PCR affects how chars are loaded from files
C CRLF CRLF use CR/LF as line terminator
@@ UTF8IO U8IO I/O (keyboard and terminal) are UTF-8 encoded
A/8/U UTF8 U8 the document encoding (ASCII, 8-bit or UTF-8)
*/_ Modified MOD document has been modified since last saved
_ (none) file's modtime changed since doc was loaded/saved
@end example
The @code{RequestOrder} and @code{AutoMatchBracket} flags' states are not indicated on the status bar.
See @ref{RequestOrder} and @ref{AutoMatchBracket} respectively.
A @samp{!} indicates the last line is not terminated.
@node AutoPrefs
@subsection AutoPrefs
@cmindex AutoPrefs
@noindent Syntax: @code{AutoPrefs [0|1]}@*
@noindent Abbreviation: @code{AP}
@noindent sets the automatic preferences flag. If this flag is true, each
time an @code{Open} command is executed and a file is loaded, @code{ne} will
look for an automatic preferences file in your @file{~/.ne} directory.
The preferences file name is given by the extension of the file loaded,
postfixed with @samp{#ap}. Thus, for instance, C sources have an associated
@file{c#ap} file.
@ifclear autohelp
@xref{Automatic Preferences}.
@end ifclear
If you invoke @code{AutoPrefs} with no arguments, it will toggle the flag. If you
specify 0 or 1, the flag will be set to false or true, respectively. A lower
case @samp{p} will appear on the status bar if the flag is true.
@node Binary
@subsection Binary
@cmindex Binary
@cindex Binary files
@noindent Syntax: @code{Binary [0|1]}@*
@noindent Abbreviation: @code{B}
@noindent sets the binary flag. When this flag is true, loading and saving a
document is performed in a different way. On loading, only nulls are
considered newlines; on saving, nulls are saved instead of newlines. This
allows you to edit a binary file, fix some text in it, and save it without
modifying anything else. Normally, line feeds, carriage returns and nulls are
considered newlines, so that what you load will have all nulls and carriage
returns substituted by newlines when saved.
Note that since usually binary files contain a great number of nulls, and every
null will be considered a line terminator, the memory necessary for loading a
binary file can be several times bigger than the length of the file
itself. Thus, binary editing within @code{ne} should be considered not a
normal activity, but rather an exceptional one.
If you invoke @code{Binary} with no arguments, it will toggle the flag. If you
specify 0 or 1, the flag will be set to false or true, respectively. An upper
case @samp{B} will appear on the status bar if the flag is true. If false,
a @samp{-} or @samp{!} will indicate whether the last line of the document is
empty, which will determine whether the resulting file will have a normal line
termination when the document is saved.
@node Insert
@subsection Insert
@cmindex Insert
@noindent Syntax: @code{Insert [0|1]}@*
@noindent Abbreviation: @code{I}
@noindent sets the insert flag. If this flag is true, the text you type is
inserted, otherwise it overwrites the existing characters. This also
governs the behaviour of the @code{InsertChar} and @code{InsertString}
commands.
If you invoke @code{Insert} with no arguments, it will toggle the flag. If you
specify 0 or 1, the flag will be set to false or true, respectively. A lower
case @samp{i} will appear on the status bar if the flag is true.
@node FastGUI
@subsection FastGUI
@cmindex FastGUI
@noindent Syntax: @code{FastGUI [0|1]}@*
@noindent Abbreviation: @code{FG}
@noindent sets the fast graphical user interface flag. When this flag is
true, @code{ne} tries to print as little as possible while displaying
menus and the status bar. In particular, menu items are highlighted by
the cursor only, the status bar is not highlighted (which allows
printing it with fewer characters), the current position of the mark is not
highlighted, and the hexadecimal code for the character under the cursor is not
displayed. This option is only (but very) useful if you are using @code{ne}
through a slow connection.
If you invoke @code{FastGUI} with no arguments, it will toggle the flag. If you
specify 0 or 1, the flag will be set to false or true, respectively.
The @code{FastGUI} setting is saved in your @file{~/.ne/.default#ap} file
when you use the @code{SaveDefPrefs} command or the @samp{Save Def Prefs} menu.
It is not saved by the @code{SaveAutoPrefs} command.
@node FreeForm
@subsection FreeForm
@cmindex FreeForm
@noindent Syntax: @code{FreeForm [0|1]}@*
@noindent Abbreviation: @code{FF}
@noindent sets the free form flag. When this flag is true, you can move with
the cursor anywhere on the screen, even where there is no text present (however,
you cannot move inside the space expansion of a @sc{tab} character).
If you invoke @code{FreeForm} with no arguments, it will toggle the flag. If you
specify 0 or 1, the flag will be set to false or true, respectively. A lower
case @samp{f} will appear on the status bar if the flag is true.
The issue free-form-versus-non-free-form is a major religious war that
has engaged users from day one. The due of the implementor is to allow
both choices, and to set as default the correct one (in his humble
opinion). In this case, non-free-form.
@node NoFileReq
@subsection NoFileReq
@cmindex NoFileReq
@noindent Syntax: @code{NoFileReq [0|1]}@*
@noindent Abbreviation: @code{NFR}
@noindent sets the file requester flag. When this flag is true,
the file requester is never opened, under any circumstances.
If you invoke @code{NoFileReq} with no arguments, it will toggle the flag. If you
specify 0 or 1, the flag will be set to false or true, respectively.
@node RequestOrder
@subsection RequestOrder
@cmindex RequestOrder
@noindent Syntax: @code{RequestOrder [0|1]}@*
@noindent Abbreviation: @code{RQO}
@noindent sets the request order flag. When this flag is true,
the requester displays entries in column order. Otherwise entries
are displayed by rows.
If you invoke @code{RequestOrder} with no arguments, it will toggle the flag. If you
specify 0 or 1, the flag will be set to false or true, respectively.
The @code{RequestOrder} setting is saved in your @file{~/.ne/.default#ap} file
when you use the @code{SaveDefPrefs} command or the @samp{Save Def Prefs} menu.
It is not saved by the @code{SaveAutoPrefs} command.
@node StatusBar
@subsection StatusBar
@cmindex StatusBar
@noindent Syntax: @code{StatusBar [0|1]}@*
@noindent Abbreviation: @code{ST}
@noindent sets the status bar flag. When this flag is true, the status bar is
displayed at the bottom of the screen. There are only two reasons to
turn off the status bar we are aware of:
@itemize @bullet{}
@item
if you are using @code{ne} through a slow connection, updating the
line/column indicator can really slow down editing;
@item
scrolling caused by cursor movement on terminals that do not allow to set a
scrolling region can produce annoying flashes at the bottom of the screen.
@end itemize
If you invoke @code{StatusBar} with no arguments, it will toggle the flag. If you
specify 0 or 1, the flag will be set to false or true, respectively.
The @code{StatusBar} setting is saved in your @file{~/.ne/.default#ap} file
when you use the @code{SaveDefPrefs} command or the @samp{Save Def Prefs} menu.
It is not saved by the @code{SaveAutoPrefs} command.
@node HexCode
@subsection HexCode
@cmindex HexCode
@noindent Syntax: @code{HexCode [0|1]}@*
@noindent Abbreviation: @code{HC}
@noindent sets the hex code flag. When this flag is true, the
hexadecimal code of the character currently under the cursor is
displayed on the status bar.
@node ReadOnly
@subsection ReadOnly
@cmindex ReadOnly
@noindent Syntax: @code{ReadOnly [0|1]}@*
@noindent Abbreviation: @code{RO}
@noindent sets the read only flag. When this flag is true, no editing can be
performed on the document (any such attempt produces an error message). Saving
read only documents is inhibited as well; you must affirmatively answer a prompt
to save a document with the read only flag set. This
flag is automatically set whenever you open a file that you cannot write to.
See @ref{Open}.
If you invoke @code{ReadOnly} with no arguments, it will toggle the flag. If you
specify 0 or 1, the flag will be set to false or true, respectively. A lower
case @samp{r} will appear on the status bar if the flag is true.
@node EscapeTime
@subsection EscapeTime
@cmindex EscapeTime
@noindent Syntax: @code{EscapeTime [@var{n}]}@*
@noindent Abbreviation: @code{ET}
@noindent sets the escape time. The @key{Escape} key is recognized as such
after @var{n} tenths of second.
@ifclear autohelp
(@pxref{Motivations and Design}.)
@end ifclear
Along slow connections, it can happen that the default value of 10 is
too low: in this case, escape sequences (e.g., those of the arrow keys)
could be erroneously broken into an escape and some spurious characters.
Rising the escape time usually solves this problem. Allowed values range
from 0 to 255, with 0 disabling escape time, which means that a
single @key{Escape} will not be recognized as a distinct command. Note
that you can accelerate the recognition of the @key{Escape} key by hitting
it twice in a row.
Note that the escape time is global to @code{ne}, and it is not saved. However,
you can add an @code{EscapeTime} command manually to a preferences file.
@node TabSize
@subsection TabSize
@cmindex TabSize
@noindent Syntax: @code{TabSize [@var{size}]}@*
@noindent Abbreviation: @code{TS}
@noindent sets the number of spaces @code{ne} will use when expanding a
@sc{tab} character.
If the optional argument @var{size} is not specified, you can enter it on the
input line, the default being the current @sc{tab} size. Allowed values are
strictly between 0 and half the width of the screen.
@node Tabs
@subsection Tabs
@cmindex Tabs
@noindent Syntax: @code{Tabs [0|1]}@*
@noindent Abbreviation: @code{TAB}
@noindent sets the @code{Tabs} flag. When this flag is set, @key{Tab} key and
the @code{InsertTab} command will insert literal @var{TAB} characters.
Otherwise it will insert enough spaces to have the same visual effect.
In normal editing, the @key{Tab} key invokes the command
"@code{InsertTab} 1". Unlike most others, the @key{Tab} key cannot
be mapped to other commands. Thus the @code{Tabs} flag provides the
only customization @code{ne} offers for the @key{Tab} key.
If set, either a lower case @samp{t} or upper case @samp{T} will appear
in the status bar depending on the state of the @code{ShiftTabs} flag.
(The @code{ShiftTabs} flag is irrelevant if the @code{Tabs} flag is off.)
See @ref{ShiftTabs}.
@node DelTabs
@subsection DelTabs
@cmindex DelTabs
@noindent Syntax: @code{DelTabs [0|1]}@*
@noindent Abbreviation: @code{DT}
@noindent sets the @code{DelTabs} flag. When this flag is set, a @samp{d}
will appear on the status bar, and the @key{BackSpace} and @key{Del} keys will
remove a tab's worth of @var{space} characters if a @var{TAB} character could
have occupied the same whitespace in the current line as the removed spaces.
This is the deletion counterpart to the @code{Tabs} flag. See @ref{Tabs}.
@node ShiftTabs
@subsection ShiftTabs
@cmindex ShiftTabs
@noindent Syntax: @code{ShiftTabs [0|1]}@*
@noindent Abbreviation: @code{SHT}
@noindent sets the @code{ShiftTabs} flag. @code{ShiftTabs} has an effect only
when the @code{Tabs} flag is set, in which case an upper case @samp{T}
appears in the status bar. When this flag and the @code{Tabs} flag are
both set, left and right @code{Shift} commands may use tab characters to
adjust leading white space. Otherwise only spaces are used. See @ref{Shift}.
@node Turbo
@subsection Turbo
@cmindex Turbo
@noindent Syntax: @code{Turbo [@var{steps}]}@*
@noindent Abbreviation: @code{TUR}
@noindent sets the turbo parameter. Iterated actions and
global replaces will update at most @var{steps} lines of the screen (or
at most twice the number of visible rows if @var{steps} is zero); then,
update will be delayed to the end of the action.
This feature is most useful when massive operations (such as replacing
thousands of occurrences of a pattern) have to be performed. After having
updated @var{steps} lines, @code{ne} can proceed at maximum speed, because
no visual update has to be performed.
The value of the turbo parameter has to be adapted to the kind of
terminal you are using. Very high values can be good on high-speed
terminals, since the time required for the visual updates is very small,
and it is always safer to look at what the editor is really doing. On
slow terminals, however, small values ensure that operations such as
paragraph formatting will not take too long.
You have to be careful about setting the turbo parameter too low. @code{ne}
keeps track internally of the part of the screen that needs refresh in a
very rough way. This means that a value of less than, say, 8 will force it
to do a lot of unnecessary refresh.
The default value of this parameter is zero, which means twice the number
of lines of the screen; for several reasons this does seem to be a good
value.
@node VerboseMacros
@subsection VerboseMacros
@cmindex VerboseMacros
@noindent Syntax: @code{VerboseMacros [0|1]}@*
@noindent Abbreviation: @code{VM}
@noindent sets the verbose macros flag. When this flag is true, all macros
generated by recording or by automatic preferences saving will contain
full names, instead of short names. This is highly desirable if you are
going to edit the macro manually, but it can slow down command parsing.
If you invoke @code{VerboseMacros} with no arguments, it will toggle the flag. If you
specify 0 or 1, the flag will be set to false or true, respectively. A lower
case @samp{v} will appear on the status bar if the flag is true.
The only reason to use this flag is when recording a macro that will be
played a great number of times. Automatic preferences files are too short to
be an issue with respect to execution timing.
The @code{VerboseMacros} setting is saved in your @file{~/.ne/.default#ap} file
when you use the @code{SaveDefPrefs} command or the @samp{Save Def Prefs} menu.
It is not saved by the @code{SaveAutoPrefs} command.
@node PreserveCR
@subsection PreserveCR
@cmindex PreserveCR
@noindent Syntax: @code{PreserveCR [0|1]}@*
@noindent Abbreviation: @code{PCR}
@noindent sets the preserve carriage returns flag. When a file is
loaded into a document for which this flag is false, both CR (carriage
return) and NL (new line) characters are treated as line terminators.
If the flag is true, CR characters do not act as line terminators but
are instead preserved in the document. This flag has no effect except
when loading a file into a document.
If you invoke @code{PreserveCR} with no arguments, it will toggle the flag.
If you specify 0 or 1, the flag will be set to false or true,
respectively. An upper case @samp{P} will appear on the status bar if the
flag is true.
@node CRLF
@subsection CRLF
@cmindex CRLF
@noindent Syntax: @code{CRLF [0|1]}@*
@noindent Abbreviation: @code{CRLF}
@noindent sets the CR/LF flag. When a file is
saved from a document for which this flag is true, both a CR (carriage
return) and a NL (new line) character are output as line terminators.
This flag has no effect except when saving a file.
This flag is automatically set if you load a file that has at least one
CR/LF sequence in it.
If you invoke @code{CRLF} with no arguments, it will toggle the flag.
If you specify 0 or 1, the flag will be set to false or true,
respectively. An upper case @samp{C} will appear on the status bar if the
flag is true.
@node BracketedPaste
@subsection BracketedPaste
@cmindex BracketedPaste
@noindent Syntax: @code{BracketedPaste [0|1|@var{macro_name_before} @var{macro_name_after}]}@*
@noindent Abbreviation: @code{BP}
@noindent ``Bracketed paste'' is an informal though widely adopted protocol between
terminal mode programs (like @code{ne}) and the terminal emulators through
which they present their user interfaces. When @code{BracketedPaste} is
enabled, blocks of text pasted from a system clipboard are ``bracketed'' --
that is, they are immediately preceded and followed -- by special character
sequences which distinguish such blocks from text you actually typed. Bracketed
paste text is usually already indented, tabbed, and line wrapped. The
@code{BracketedPaste} command lets you manage factors that change input
handling while inserting bracketed paste text into your document.
@code{BracketedPaste 0} disables bracketed paste support entirely;
@code{BracketedPaste 1} enables bracketed paste support, temporarily
disables the @code{AutoIndent} flag, and increases the @code{AtomicUndo} level
during bracketed pastes; when given a pair of macro names, bracketed paste
support is enabled and the indicated macros will be run immediately before and
after processing bracketed paste text. The macros need not already exist. (See
the @code{Macro} help page, especially the warning at the bottom.)
@code{BracketedPaste} with no argument will prompt you to enter a new value
with the prompt's default being the current value.
The default value is @samp{1} -- enabled with default behaviour. This is an
automatic preference (see @ref{AutoPrefs}) and so will be saved by both
@code{SaveAutoPrefs} and @code{SaveDefPrefs}.
The flags @code{AutoIndent}, @code{Tabs}, @code{ShiftTabs}, and @code{WordWrap}
all affect handling of pasted text. The examples below show
contents of before and after macros which would have effects similar to
@samp{BracketedPaste 1}.
@example
# Invoked Before Bracketed Paste # Invoked After Bracketed Paste
PushPrefs PopPrefs
AtomicUndo + AtomicUndo -
AutoIndent 0 Refresh
Refresh
@end example
@node VisualBell
@subsection VisualBell
@cmindex VisualBell
@noindent Syntax: @code{VisualBell [0|1]}@*
@noindent Abbreviation: @code{VB}
@noindent sets the visual bell flag. When this flag is true, the terminal will
flash (if possible) instead of beeping.
If you invoke @code{VisualBell} with no arguments, it will toggle the flag. If
you specify 0 or 1, the flag will be set to false or true, respectively.
@node PushPrefs
@subsection PushPrefs
@cmindex PushPrefs
@noindent Syntax: @code{PushPrefs [@var{n}]}@*
@noindent Abbreviation: @code{PUSHP}
@noindent pushes @var{n} copies of the user preferences onto a stack. If
not specified, @var{n} defaults to one. Use the @code{PopPrefs} command
to pop preferences off the stack and restore the values. See @ref{PopPrefs}. Note that the
preferences stack is global, not document-specific, so you could
@code{PushPrefs} one document's preferences, switch documents, then
@code{PopPrefs} those preferences, thereby altering the preferences for
the second document. The maximum preferences stack depth is 32.
@code{PushPrefs} and @code{PopPrefs} are useful in macros that require
certain preferences to work properly. A macro can @code{PushPrefs},
change any preferences necessary, do its work, then @code{PopPrefs} to
restore the users previous preferences settings.
@noindent @code{PushPrefs} saves the following values on the preferences stack:
@example
AutoIndent DelTabs NoFileReq StatusBar VisualBell
AutoPrefs DoUndo PreserveCR ShiftTabs WordWrap
Binary FreeForm ReadOnly Tabs
CaseSearch HexCode RightMargin TabSize
ClipNumber Insert SearchBack UTF8Auto
@end example
@node PopPrefs
@subsection PopPrefs
@cmindex PopPrefs
@noindent Syntax: @code{PopPrefs [@var{n}]}@*
@noindent Abbreviation: @code{POPP}
@noindent pops @var{n} sets of preferences from the preferences stack (where they were
placed previously by @code{PushPrefs}) and applies those preferences to the
current document. See @ref{PushPrefs}. If not specified, @var{n} defaults to one. Note that the
preferences stack is global, not document specific. Therefore you could
@code{PushPrefs} one document's preferences, switch documents, then @code{PopPrefs}
those settings altering the preferences for the second document. The
maximum preferences stack depth is 32.
@code{PushPrefs} and @code{PopPrefs} are useful in macros that require
certain preferences to work properly. A macro can @code{PushPrefs},
change any preferences necessary, do its work, then @code{PopPrefs} to
restore the users previous preferences settings.
@noindent PopPrefs restores the following values from the preferences stack:
@example
AutoIndent DelTabs NoFileReq StatusBar VisualBell
AutoPrefs DoUndo PreserveCR ShiftTabs WordWrap
Binary FreeForm ReadOnly Tabs
CaseSearch HexCode RightMargin TabSize
ClipNumber Insert SearchBack UTF8Auto
@end example
@node LoadPrefs
@subsection LoadPrefs
@cmindex LoadPrefs
@noindent Syntax: @code{LoadPrefs [@var{filename}]}@*
@noindent Abbreviation: @code{LP}
@noindent loads the given preference file, and sets the current preferences
accordingly.
If the optional @var{filename} argument is not specified, the file
requester is opened, and you are prompted to select a file. (You can
inhibit the file requester opening by using the @code{NoFileReq}
command; see @ref{NoFileReq}.) If you escape from the file requester, you
can input the file name on the command line.
Note that a preferences file is just a macro containing only option
modifiers. You can manually edit a preferences file for special
purposes, such as filtering out specific settings.
@ifclear autohelp
@xref{Hints and Tricks}.
@end ifclear
@node SavePrefs
@subsection SavePrefs
@cmindex SavePrefs
@noindent Syntax: @code{SavePrefs [@var{filename}]}@*
@noindent Abbreviation: @code{SP}
@noindent saves the current preferences to the given file.
If the optional @var{filename} argument is not specified, the file
requester is opened, and you are prompted to select a file. (You can
inhibit the file requester opening by using the @code{NoFileReq}
command; see @ref{NoFileReq}.) If you escape from the file requester, you
can input the file name on the command line.
@node LoadAutoPrefs
@subsection LoadAutoPrefs
@cmindex LoadAutoPrefs
@noindent Syntax: @code{LoadAutoPrefs}@*
@noindent Abbreviation: @code{LAP}
@noindent loads the preferences file in @file{~/.ne} associated
with the current document's file name extension. If the current file
name has no extension, the default preferences are loaded.
See @ref{AutoPrefs}.
@node SaveAutoPrefs
@subsection SaveAutoPrefs
@cmindex SaveAutoPrefs
@noindent Syntax: @code{SaveAutoPrefs}@*
@noindent Abbreviation: @code{SAP}
@noindent saves the current preferences to the file in @file{~/.ne}
associated with the current document's file name extension. If the
current file name has no extension, an error message is issued.
See @ref{AutoPrefs}.
@node SaveDefPrefs
@subsection SaveDefPrefs
@cmindex SaveDefPrefs
@noindent Syntax: @code{SaveDefPrefs}@*
@noindent Abbreviation: @code{SDP}
@noindent saves the current preferences to the @file{~/.ne/.default#ap} file.
This file is always loaded by @code{ne} at startup.
@node Modified
@subsection Modified
@cmindex Modified
@noindent Syntax: @code{Modified [0|1]}@*
@noindent Abbreviation: @code{MOD}
@noindent sets the modified flag. This flag is set automatically
whenever a document is modified, and is used to determine which documents
need to be saved by @code{SaveAll} or when @code{ne} exits. Normally you would not alter
this flag, but when a document is inadvertently modified and you don't
want the changes saved, @code{Modified} provides a way to make
@code{ne} consider the document unchanged.
If you invoke @code{Modified} with no arguments, it will toggle the flag.
If you specify 0 or 1, the flag will be set to false or true,
respectively. An asterisk (@samp{*}) will appear on the status bar when
this flag is set. If your terminal supports underlining, it will be underlined
if the corresponding file's modification time has changed since the document
was loaded or saved (perhaps by another user).
@node Syntax
@subsection Syntax
@cmindex Syntax
@noindent Syntax: @code{Syntax [@var{name}|*]}@*
@noindent Abbreviation: @code{SY}
@noindent loads the syntax with the given name, and colors the
current document accordingly.
If the optional @var{name} argument is not specified, you are
prompted for one. The current one, if set, is suggested as the
default. Use the @key{Tab} key for a requester of the extant
syntax recognizers. The special @var{name} * turns off syntax highlighting for
the current document. Otherwise, @var{name} must match a syntax
definition either in your @file{~/.ne/syntax} directory or in a
directory named @samp{syntax} inside @code{ne}'s global directory.
Additionally, @code{ne} has a table mapping common suffixes to
syntax names. If there is no syntax with a given name, @code{ne}
will try to remap the name using the following table (the string
before the colon is the name of the syntax file):
@ignore
N O T E T O D O C U M E N T M A I N T A I N E R S
The following "example" section is read by info2src.pl to build ne's
extension map. You can break a line if you end it with a "," but
otherwise stick to the .jsf file base name followed by ":" followed by
a list of common extensions. Don't get fancy with this format.
@end ignore
@example
ada: adb, ads
asm: s
c: c++, cc, cpp, cxx, h, h++, hpp, l, lex, y, yacc
cobol: cbl, cob
csh: tcsh
diff: patch
fortran: f, F, for, f90, F90
html: htm
java: js
lisp: el, lsp
mason: mas
ocaml: ml, mli
pascal: p, pas
perl: pl, pm
ps: eps
puppet: pp
python: py, sage
rexx: rex
ruby: rb
sh: bash, bash_login, bash_logout, bash_profile, bashrc, ksh,
profile, rc
skill: il
tex: latex, dtx, sty
texinfo: texi, txi
troff: 1
verilog: v, vh, vhd
xml: xsd
yaml: yml
@end example
@node UTF8
@subsection UTF8
@cmindex UTF8
@noindent Syntax: @code{UTF8 [0|1]}@*
@noindent Abbreviation: @code{U8}
@noindent sets the UTF-8 flag. When this flag is
true, @code{ne} considers the current document as UTF-8 coded. Note that
this flag is set automatically upon file loading (if possible) if you
required automatic detection. See @ref{UTF8Auto}.
If you invoke @code{UTF8} with no arguments, it will toggle the flag.
If you specify 0 or 1, the flag will be set to false or true,
respectively. When you try to set this flag, the document will be checked
for UTF-8 compliance, and you will get an error message in case of
failure. When you try to reset it, the document is set to ASCII or
8-bit, depending on its content. A @samp{U} will appear on the
status bar if the flag is true. Alternatively, an @samp{A} or an
@samp{8} will be displayed to denote whether the document is composed
exclusively by US-ASCII characters, or also by other 8-bit characters
(whose encoding is likely to be part of the ISO-8859 family). Note that
each time this command modifies the document encoding, it also resets the
undo buffer.
@node UTF8Auto
@subsection UTF8Auto
@cmindex UTF8Auto
@noindent Syntax: @code{UTF8Auto [0|1]}@*
@noindent Abbreviation: @code{U8A}
@noindent sets the UTF-8 automatic-detection flag. When this flag is
true, @code{ne} will try to guess whether a file just loaded is UTF-8
encoded. Moreover, when a non US-ASCII character is inserted in a pure
US-ASCII document, ne will automatically switch to UTF-8. See @ref{UTF8}. The
flag is true by default if @code{ne} detects UTF-8 I/O at
startup. See @ref{UTF8IO}.
If you invoke @code{UTF8Auto} with no arguments, it will toggle the flag.
If you specify 0 or 1, the flag will be set to false or true,
respectively.
@node UTF8IO
@subsection UTF8IO
@cmindex UTF8IO
@noindent Syntax: @code{UTF8IO [0|1]}@*
@noindent Abbreviation: @code{U8IO}
@noindent sets the UTF-8 input/output flag. This flag is set automatically
depending on your locale setting, and is used to determine whether
communication with the user (keyboard and terminal) should be UTF-8
encoded. Normally you would not alter this flag, but sometimes @code{ne}
may make the wrong guess (e.g., when you are remotely connected).
If you invoke @code{UTF8IO} with no arguments, it will toggle the flag.
If you specify 0 or 1, the flag will be set to false or true,
respectively. An @samp{@@} will appear on the status bar if the flag is
true.
@node Navigation Commands
@section Navigation Commands
These commands allow you to move through a document. Besides the standard
commands that allow you to move by lines, pages, @i{et cetera}, @code{ne}
has bookmarks that let you mark a position in a file so to move
to the same position later.
@menu
* MoveLeft::
* MoveRight::
* LineUp::
* LineDown::
* GotoLine::
* GotoColumn::
* GotoMark::
* PrevPage::
* NextPage::
* PageUp::
* PageDown::
* PrevWord::
* NextWord::
* MoveEOL::
* MoveSOL::
* MoveTOS::
* MoveBOS::
* MoveEOF::
* MoveSOF::
* MoveEOW::
* MoveIncUp::
* MoveIncDown::
* AdjustView::
* ToggleSEOF::
* ToggleSEOL::
* SetBookmark::
* GotoBookmark::
* UnsetBookmark::
@end menu
@node MoveLeft
@subsection MoveLeft
@cmindex MoveLeft
@noindent Syntax: @code{MoveLeft [@var{n}]}@*
@noindent Abbreviation: @code{ML}
@noindent moves the cursor to the left by one character @var{n} times. If
the optional @var{n} argument is not specified, it is assumed to be one.
@node MoveRight
@subsection MoveRight
@cmindex MoveRight
@noindent Syntax: @code{MoveRight [@var{n}]}@*
@noindent Abbreviation: @code{MR}
@noindent moves the cursor to the right by one character @var{n} times. If
the optional @var{n} argument is not specified, it is assumed to be one.
@node LineUp
@subsection LineUp
@cmindex LineUp
@noindent Syntax: @code{LineUp [@var{n}]}@*
@noindent Abbreviation: @code{LU}
@noindent moves the cursor up by one line @var{n} times. If the optional
@var{n} argument is not specified, it is assumed to be one.
@node LineDown
@subsection LineDown
@cmindex LineDown
@noindent Syntax: @code{LineDown [@var{n}]}@*
@noindent Abbreviation: @code{LD}
@noindent moves the cursor down by one line @var{n} times. If the optional
@var{n} argument is not specified, it is assumed to be one.
@node GotoLine
@subsection GotoLine
@cmindex GotoLine
@noindent Syntax: @code{GotoLine [@var{line}]}@*
@noindent Abbreviation: @code{GL}
@noindent moves the cursor to the @var{line}th line of the file. If @var{line}
is zero or greater than the number of lines in the file, the cursor is
moved to the last line.
If the optional argument @var{line} is not specified, you can enter it on
the input line; the default input response is the current line number.
@node GotoColumn
@subsection GotoColumn
@cmindex GotoColumn
@noindent Syntax: @code{GotoColumn [@var{column}]}@*
@noindent Abbreviation: @code{GC}
@noindent moves the cursor to the @var{column}th column of the file.
If the optional argument @var{line} is not specified, you can enter it on
the input line; the default input response is the current column number.
@node GotoMark
@subsection GotoMark
@cmindex GotoMark
@noindent Syntax: @code{GotoMark}@*
@noindent Abbreviation: @code{GM}
@noindent moves the cursor to the current mark, if it exists. See @ref{Mark}.
@code{GotoMark} is mainly useful if you forgot where you started marking.
If you want to record positions in a file and jump to them later, you
may want to use bookmarks instead. See @ref{SetBookmark}.
@node PrevPage
@subsection PrevPage
@cmindex PrevPage
@noindent Syntax: @code{PrevPage [@var{n}]}@*
@noindent Abbreviation: @code{PP}
@noindent moves the cursor @var{n} pages backward, if the cursor is on the
first line of the screen; otherwise moves the cursor to the first line of
the screen, and moves by @var{n}-1 pages. If the optional @var{n} argument
is not specified, it is assumed to be one.
@node NextPage
@subsection NextPage
@cmindex NextPage
@noindent Syntax: @code{NextPage [@var{n}]}@*
@noindent Abbreviation: @code{NP}
@noindent moves the cursor @var{n} pages forward, if the cursor is on the
last line of the screen; otherwise moves the cursor to the last line of the
screen, and moves by @var{n}-1 pages. If the optional @var{n} argument is
not specified, it is assumed to be one.
@node PageUp
@subsection PageUp
@cmindex PageUp
@noindent Syntax: @code{PageUp [@var{n}]}@*
@noindent Abbreviation: @code{PUP}
@noindent pages the screen backward by @var{n} screens.
If @var{n} is not specified, it is assumed to be one.
@node PageDown
@subsection PageDown
@cmindex PageDown
@noindent Syntax: @code{PageDown [@var{n}]}@*
@noindent Abbreviation: @code{PDN}
@noindent pages the screen forward by @var{n} screens. If @var{n} is not specified, it is
assumed to be one.
@node PrevWord
@subsection PrevWord
@cmindex PrevWord
@noindent Syntax: @code{PrevWord [@var{n}][<|>]}@*
@noindent Abbreviation: @code{PW}
@noindent moves the cursor to the left to the @var{n}th word beginning or ending.
If the optional @var{n} argument is not specified, one is used.
The optional @samp{<} or @samp{>} determines whether to move to
the word beginning or ending, respectively. If unspecified, the
left end (@samp{<}) is used.
@node NextWord
@subsection NextWord
@cmindex NextWord
@noindent Syntax: @code{NextWord [@var{n}][<|>]}@*
@noindent Abbreviation: @code{NW}
@noindent moves the cursor to the right to the @var{n}th word beginning or ending.
If the optional @var{n} argument is not specified, one is used.
The optional @samp{<} or @samp{>} determines whether to move to
the word beginning or ending, respectively. If unspecified, the
left end (@samp{<}) is used.
@node MoveEOL
@subsection MoveEOL
@cmindex MoveEOL
@noindent Syntax: @code{MoveEOL}@*
@noindent Abbreviation: @code{EOL}
@noindent moves the cursor to the end of the current line (@code{EOL} = end of
line).
@node MoveSOL
@subsection MoveSOL
@cmindex MoveSOL
@noindent Syntax: @code{MoveSOL}@*
@noindent Abbreviation: @code{SOL}
@noindent moves the cursor to the start of the current line (@code{SOL} = start of
line).
@node MoveTOS
@subsection MoveTOS
@cmindex MoveTOS
@noindent Syntax: @code{MoveTOS}@*
@noindent Abbreviation: @code{TOS}
@noindent moves the cursor to the top line of the screen (@code{TOS} = top of
screen).
@node MoveBOS
@subsection MoveBOS
@cmindex MoveBOS
@noindent Syntax: @code{MoveBOS}@*
@noindent Abbreviation: @code{BOS}
@noindent moves the cursor to the lowest line currently visible (@code{BOS} = bottom of
screen).
@node MoveEOF
@subsection MoveEOF
@cmindex MoveEOF
@noindent Syntax: @code{MoveEOF}@*
@noindent Abbreviation: @code{EOF}
@noindent moves the cursor to the end of the document (@code{EOF} = end of
file).
@node MoveSOF
@subsection MoveSOF
@cmindex MoveSOF
@noindent Syntax: @code{MoveSOF}@*
@noindent Abbreviation: @code{SOF}
@noindent moves the cursor to the start of the document (@code{SOF} = start
of file).
@node MoveEOW
@subsection MoveEOW
@cmindex MoveEOW
@noindent Syntax: @code{MoveEOW [<|>]}@*
@noindent Abbreviation: @code{EOW}
@noindent moves the cursor to the end of the current word, that end being
the right end unless you include the optional parameter @samp{<}. If the
cursor is not currently in a word, or if it's already at the indicated
end, it retains its current location.
@code{MoveEOW} is extremely useful in macros, because it allows you to copy
precisely the word the cursor is on.
@ifclear autohelp
@xref{Hints and Tricks}.
@end ifclear
@node MoveIncUp
@subsection MoveIncUp
@cmindex MoveIncUp
@noindent Syntax: @code{MoveIncUp}@*
@noindent Abbreviation: @code{MIU}
@noindent moves the cursor incrementally towards the beginning of the
document. More precisely, if the cursor is not on the start of the line
it lies on, then it is moved to the start of that line. Otherwise, if it
is on the first line of the screen, then it is moved to the start of the
document; otherwise, it is moved to the first line of the screen.
@node MoveIncDown
@subsection MoveIncDown
@cmindex MoveIncDown
@noindent Syntax: @code{MoveIncDown}@*
@noindent Abbreviation: @code{MID}
@noindent moves the cursor incrementally towards the end of the
document. More precisely, if the cursor is not on the end of the line
it lies on, then it is moved to the end of that line. Otherwise, if it is
on the last line of the screen, then it is moved to the end of the
document; otherwise, it is moved to the last line of the screen.
@node AdjustView
@subsection AdjustView
@cmindex AdjustView
@noindent Syntax: @code{AdjustView [T|M|B|L|C|R] [@var{n}]}@*
@noindent Abbreviation: @code{AV}
@noindent shifts the view (text visible in the terminal window)
horizontally or vertically without changing the cursor's position in
the document. View adjustments are constrained by the current @sc{tab}
size and the length and width of the current document. If called with
no arguments @samp{T} is assumed.
@samp{T}, @samp{M}, and @samp{B} cause vertical shifts so that the
current line becomes the top, middle, or bottom-most visible line
respectively.
@samp{L}, @samp{C}, and @samp{R} cause horizontal shifts, making the
current column the left-most, center, or right-most visible positions.
A optional number @var{n} immediately after @samp{T}, @samp{B},
@samp{L}, or @samp{R} indicate the number or rows or columns to shift
the view toward the top, bottom, left, or right of the window.
Horizontal and vertical adjustment specifications may be combined,
so that for example @samp{AdjustView TL} shifts the view so
that the current position becomes the top left-most character on
screen (within the limits of the current @sc{tab} size). Likewise,
@samp{AdjustView B3R5} shifts the view three lines toward the bottom and
five columns (excepting @sc{tab} size) toward the right.
@node ToggleSEOF
@subsection ToggleSEOF
@cmindex ToggleSEOF
@noindent Syntax: @code{ToggleSEOF}@*
@noindent Abbreviation: @code{TSEOF}
@noindent moves the cursor to the start of document, if it is not already
there; otherwise, moves it to the end of the document.
This kind of toggling command is very useful in
order to gain some keystrokes on systems with very few keys. See also
@ref{ToggleSEOL}, @ref{MoveSOF}, and @ref{MoveEOF}.
@node ToggleSEOL
@subsection ToggleSEOL
@cmindex ToggleSEOL
@noindent Syntax: @code{ToggleSEOL}@*
@noindent Abbreviation: @code{TSEOL}
@noindent moves the cursor to the start of the current line, if it is not
already there; otherwise, moves it to the end of the current line.
This kind of toggling command is very useful in
order to gain some keystrokes on systems with very few keys. See also
@ref{ToggleSEOF}, @ref{MoveSOL}, and @ref{MoveEOL}.
@node SetBookmark
@subsection SetBookmark
@cmindex SetBookmark
@noindent Syntax: @code{SetBookmark [@var{n}|@var{+1}|@var{-1}|@var{<}|@var{>}|@var{-}|@var{?}]}@*
@noindent Abbreviation: @code{SBM}
@noindent sets a document bookmark to the current cursor position.
Each document has 10 available bookmarks designated @samp{0} to @samp{9},
the most recently pasted block's start and end designated @samp{<} and @samp{>},
plus the automatic bookmark designated by @samp{-}. If no option is given,
@samp{0} is assumed. Values of @var{n} from @samp{0} to @samp{9} set the
@var{n}th bookmark, while @samp{+1} and @samp{-1} indicate respectively
the next and previous available unset bookmarks. You can also set the
pasted block's start and end which will be reset on the next @code{Paste}
or @code{PasteVert} command, or the @samp{-} automatic bookmark, but it will
be reset automatically to the current position whenever a @code{GotoBookmark}
command is issued.
The @samp{?} option will cause @code{SetBookmark} to prompt you
for a bookmark designation. The promp will include an indication of which
bookmarks are currently set. You may find this useful in macros, or to bind
a key to @samp{SetBookmark ?}.
@node GotoBookmark
@subsection GotoBookmark
@cmindex GotoBookmark
@noindent Syntax: @code{GotoBookmark [@var{n}|@var{+1}|@var{-1}|@var{<}|@var{>}|@var{-}]|@var{?}]}@*
@noindent Abbreviation: @code{GBM}
@noindent moves the cursor to the designated bookmark if that bookmark is
set; see @ref{SetBookmark}. Each document has 10 available bookmarks
designated @samp{0} to @samp{9}, either end of the most recently pasted
text block designated @samp{<} and @samp{>}, plus the automatic bookmark designated by
@samp{-}. If no option is given, @samp{0} is assumed. The options @samp{+1}
and @samp{-1} indicate respectively the next and previous set bookmarks,
so that repeated @code{GotoBookmark +1} commands will cycle through all
currently set numbered bookmarks.
When successful, the automatic bookmark @samp{-}
is set to the position in the document from which the command was issued,
so that @code{GotoBookmark -} returns you to the location from which you
last issued a successful @code{GotoBookmark} command. Subsequent repeated
@code{GotoBookmark -} commands will toggle you between the two locations.
The @samp{?} option will cause @code{GotoBookmark} to prompt you
for a bookmark designation. The promp will include an indication of which
bookmarks are currently set. You may find this useful in macros, or to bind
a key to @samp{GotoBookmark ?}.
@node UnsetBookmark
@subsection UnsetBookmark
@cmindex UnsetBookmark
@noindent Syntax: @code{UnsetBookmark [@var{n}|@var{+1}|@var{-1}|@var{<}|@var{>}|@var{-}|@var{*}]}@*
@noindent Abbreviation: @code{UBM}
@noindent unsets either the @var{n}th bookmark (0 through 9), the next (@var{+1}) or
previous (@var{-1}) set bookmarks, the bookmarks designating the
start (@var{<}) or end (@var{>}) of the most recently pasted text
block, the automatic (@var{-}) bookmark, or
all (@var{*}) bookmarks, making it as if they had never been set; see
@ref{SetBookmark}. If no option is specified, @var{n} is assumed to be
zero.
While you can unset the automatic bookmark @samp{-}, it will be
reset automatically to the current position whenever a @code{GotoBookmark}
command is issued.
@node Editing Commands
@section Editing Commands
These commands allow modifying a document directly.
@menu
* InsertChar::
* InsertString::
* InsertTab::
* DeleteChar::
* DeletePrevWord::
* DeleteNextWord::
* Backspace::
* InsertLine::
* DeleteLine::
* DeleteEOL::
@end menu
@node InsertChar
@subsection InsertChar
@cmindex InsertChar
@noindent Syntax: @code{InsertChar [@var{code}]}@*
@noindent Abbreviation: @code{IC}
@noindent inserts a character whose @sc{ascii} code is @var{code} at the
current cursor position. @var{code} can be either decimal, hexadecimal if
preceded by @samp{0x}, or octal if preceded by @samp{0}. In any case,
@var{code} must be different from 0. All the currently active preferences
options (insert, word wrapping, auto indent, @i{et cetera}) are applied.
If the optional argument @var{code} is not specified, you can enter it on
the input line, the default being the last inserted character.
Note that inserting a line feed (10) is completely different from inserting
a line with @code{InsertLine}. @code{InsertChar 10} puts the control char
@kbd{@key{Control}-J} in the text at the current cursor position.
See @ref{InsertLine}.
Note also that @code{SaveMacro} converts @code{InsertChar} commands into
a possibly smaller number of @code{InsertString} commands.
This makes macros easier to read and edit. See @ref{SaveMacro}.
@node InsertString
@subsection InsertString
@cmindex InsertString
@noindent Syntax: @code{InsertString [@var{text}]}@*
@noindent Abbreviation: @code{IS}
@noindent inserts @var{text} at the current cursor position. If
the optional argument @var{text} is omitted, you will be prompted for it
on the command line. All the currently active preferences options
(insert, word wrapping, auto indent, @i{et cetera}) are applied.
Note that @code{SaveMacro} converts @code{InsertChar} commands into a
possibly smaller number of @code{InsertString} commands.
This makes macros easier to read and edit. See @ref{SaveMacro}.
@node InsertTab
@subsection InsertTab
@cmindex InsertTab
@noindent Syntax: @code{InsertTab [@var{n}]}@*
@noindent Abbreviation: @code{IT}
@noindent inserts either @var{n} literal @var{TAB} characters or one or more
spaces sufficient to advance the current cursor position @var{n}
tab stops depending on the @code{Tabs} flag. See @ref{Tabs}, @ref{TabSize}.
@node DeleteChar
@subsection DeleteChar
@cmindex DeleteChar
@noindent Syntax: @code{DeleteChar [@var{n}]}@*
@noindent Abbreviation: @code{DC}
@noindent deletes @var{n} characters from the text. If the optional @var{n}
argument is not specified, it is assumed to be one. Deleting a character
when the cursor is just after the last char on a line will join a line with
the following one; in other words, the carriage return between the two lines
will be deleted. Note that if the cursor is past the end of the current line,
no action will be performed.
@node DeletePrevWord
@subsection DeletePrevWord
@cmindex DeletePrevWord
@noindent Syntax: @code{DeletePrevWord [@var{n}]}@*
@noindent Abbreviation: @code{DPW}
@noindent deletes text from the current position through the first character of the
@var{n}'th previous start-of-word. If the optional @var{n} argument is not specified,
it is assumed to be one (in which case, if the cursor is in the middle of a
word the effect is just to delete to the start of that word).
@node DeleteNextWord
@subsection DeleteNextWord
@cmindex DeleteNextWord
@noindent Syntax: @code{DeleteNextWord [@var{n}]}@*
@noindent Abbreviation: @code{DNW}
@noindent deletes text from the current position to the @var{n}'th next end-of-word
If the optional @var{n} argument is not specified, it is assumed to be
one (in which case, if the cursor is in the middle of a word the effect is
just to delete to the end of that word).
@node Backspace
@subsection Backspace
@cmindex Backspace
@noindent Syntax: @code{Backspace [@var{n}]}@*
@noindent Abbreviation: @code{BS}
@noindent acts like @code{DeleteChar}, but moves the cursor to the left
before deleting each character.
@node InsertLine
@subsection InsertLine
@cmindex InsertLine
@noindent Syntax: @code{InsertLine [@var{n}]}@*
@noindent Abbreviation: @code{IL}
@noindent inserts @var{n} lines at the current cursor position, breaking the
current line. If the optional @var{n} argument is not specified, it is
assumed to be one.
@node DeleteLine
@subsection DeleteLine
@cmindex DeleteLine
@noindent Syntax: @code{DeleteLine [@var{n}]}@*
@noindent Abbreviation: @code{DL}
@noindent deletes @var{n} lines starting from the current cursor
position, putting the last one in the temporary buffer, from which it
can be undeleted. See @ref{UndelLine}. If the optional @var{n} argument is
not specified, it is assumed to be one. Note that this action is in no
way inverse with respect to @code{InsertLine}.
@node DeleteEOL
@subsection DeleteEOL
@cmindex DeleteEOL
@noindent Syntax: @code{DeleteEOL}@*
@noindent Abbreviation: @code{DE}
@noindent deletes all characters from the current cursor position to the end
of the line.
@code{DeleteEOL} could be easily implemented with a macro, but it is such a
common, basic editing feature that it seemed worth a separate implementation.
@node Support Commands
@section Support Commands
These commands perform miscellaneous useful actions. In particular, they
provide access to the shell and a way to assign the functionality of
@key{Escape} to another key.
@menu
* About::
* Alert::
* Beep::
* Exec::
* Flash::
* Help::
* NOP::
* Refresh::
* Suspend::
* System::
* Escape::
* KeyCode::
* NameConvert::
@end menu
@node About
@subsection About
@cmindex About
@noindent Syntax: @code{About}@*
@noindent Abbreviation: @code{About}
@noindent displays the copyright splash screen and places a simple
information line containing the version and build date of @code{ne}
on the status bar. Press any key to dismiss this screen.
@node Alert
@subsection Alert
@cmindex Alert
@noindent Syntax: @code{Alert}@*
@noindent Abbreviation: @code{AL}
@noindent beeps or flashes, depending on the value of the visual bell flag.
@node Beep
@subsection Beep
@cmindex Beep
@noindent Syntax: @code{Beep}@*
@noindent Abbreviation: @code{BE}
@noindent beeps. If your terminal cannot beep, it flashes. If it cannot
flash, nothing happens (but you have a very bad terminal).
@node Exec
@subsection Exec
@cmindex Exec
@noindent Syntax: @code{Exec}@*
@noindent Abbreviation: @code{EX}
@noindent prompts the user on the input line, asking for a command, and
executes it. It is never registered while recording a macro (though the command
you type is).
@code{Exec} is mainly useful for key bindings, menu configurations, and in
manually programmed macros.
Note that if the command you specify does not appear in @code{ne}'s internal
tables, it is considered to be a macro name. See @ref{Macro}.
@node Flash
@subsection Flash
@cmindex Flash
@noindent Syntax: @code{Flash}@*
@noindent Abbreviation: @code{FL}
@noindent acts as @code{Beep}, but interchanging the words ``beep'' and ``flash''.
Same comments apply. See @ref{Beep}.
@node Help
@subsection Help
@cmindex Help
@noindent Syntax: @code{Help [@var{name}]}@*
@noindent Abbreviation: @code{H}
@noindent displays some help about the command @var{name} (both the short and
the long versions of the command names are accepted). If no argument is
given, a list of all existing commands in long form is displayed,
allowing you to choose one. You can browse the help text with the
standard navigation keys. If you press @key{Return}, the command list
will be displayed again. If you press @key{f1} or @key{Escape}, you will
return to normal editing.
Invocations of the @code{Help} command are never registered while
recording macros so that you can safely access the help system while
recording. See @ref{Record}.
@node NOP
@subsection NOP
@cmindex NOP
@noindent Syntax: @code{NOP}@*
@noindent Abbreviation: @code{NOP}
@noindent does nothing. Mainly useful for inhibiting standard key bindings.
@node Refresh
@subsection Refresh
@cmindex Refresh
@noindent Syntax: @code{Refresh}@*
@noindent Abbreviation: @code{REF}
@noindent refreshes the display. @code{Refresh} is very important, and should
preferably be bound to the @kbd{@key{Control}-L} sequence, for historical
reasons. It can always happen that a noisy phone line or a quirk in the
terminal corrupts the display. This command restores it from scratch.
@code{Refresh} has the side effect of checking to see if your window
size has changed, and will modify the display to take that into
account.
@node Suspend
@subsection Suspend
@cmindex Suspend
@noindent Syntax: @code{Suspend}@*
@noindent Abbreviation: @code{SU}
@noindent suspends @code{ne} and returns you to a shell prompt; usually,
the shell command @code{fg} is used to resume @code{ne}.
@node System
@subsection System
@cmindex System
@noindent Syntax: @code{System [@var{command}]}@*
@noindent Abbreviation: @code{SYS}
@noindent asks the shell to execute @var{command}. The terminal is
temporarily reset to the state it was in before @code{ne}'s activation, and
@var{command} is started. When the execution is finished, control
returns to @code{ne}.
If the optional argument @var{command} is not specified, you can enter it on
the input line.
@ignore
Before starting the execution, @code{ne} defines a series of environment
variables which can thus be used on the command line. The variables allow
to refer to parts of the internal state of @code{ne}.
@end ignore
@node Escape
@subsection Escape
@cmindex Escape
@noindent Syntax: @code{Escape}@*
@noindent Abbreviation: @code{ESC}
@noindent toggles the menus on and off, or escapes from the input line. This
command is mainly useful for reprogramming the menu activator, and it is
never registered while recording a macro. See @ref{Record}.
@node KeyCode
@subsection KeyCode
@cmindex KeyCode
@noindent Syntax: @code{KeyCode [@var{k}]}@*
@noindent Abbreviation: @code{KC}
@noindent prompts you to press a key, and reports on the status bar
the key code @code{ne} associates with that key, the
command currently associated with that key code, as well as the
input class for that key code. Input class codes are: ALPHA, COMMAND,
RETURN, TAB, IGNORE, and INVALID. This can be useful while
configuring your @file{~/.ne/.keys} file.
If the optional integer @var{k} (between 0 and 511) is given, @code{KeyCode}
uses @var{k} as the key code and displays the information described above.
@var{k} can be either decimal, hexadecimal if preceded by '0x', or octal if
preceded by '0'.
@node NameConvert
@subsection NameConvert
@cmindex NameConvert
@noindent Syntax: @code{NameConvert [0|1]}@*
@noindent Abbreviation: @code{NC}
@noindent converts the current document's name between relative and
absolute path names. With no parameter it switches the current name
from relative to absolute, or absolute to relative. With @code{1} it
converts the relative path to absolute or leaves the absolute path
unchanged. With @code{0} it converts the absolute path to relative or
leaves the relative path unchanged.
@node Configuration
@chapter Configuration
@cindex Meta key
In this chapter we shall see how the menus and the key bindings of @code{ne} can
be completely configured. Note that menu and key configuration is parsed at startup
time, and cannot be changed during the execution of the program. This is a
chosen limitation.
We will also see how to override incorrect or missing file name extensions by
comparing the contents of documents to patterns to determine @emph{virtual extensions}.
@ignore
It should also be remarked that the standard configuration of @code{ne} does
not contain key bindings relative to the @key{Meta} key. This choice was forced
by the fact that the behaviour of this key is unpredictable on most systems. If
your @key{Meta} key does what it should (i.e., it rises the high bit of any
character), you can configure about thirty new shortcuts---the
@kbd{@key{Control}-@key{Meta}-@var{letter}} combinations---that will produce
ASCII characters between 128 and 159, and will be parsed as shortcuts by
@code{ne}.
@end ignore
@menu
* Key Bindings::
* Changing Menus::
* Virtual Extensions::
@end menu
@node Key Bindings
@section Key Bindings
@cindex Key bindings
@cindex Configuring the keyboard
@code{ne} allows you to associate any keystroke with any command. These
associations are referred to as key bindings. You define your key
bindings in a (possibly UTF-8) file named @file{.keys} in your
@file{~/.ne} directory. You could additionally create a @file{.keys} file in
your current directory, any your system administrator might also place a
@file{.keys} file in your system's global directory. These will always be
loaded each time you start @code{ne}, but you can also create key binding
files with the same base name (@file{.keys}) followed by a dash and some
string. Such a file will only be loaded when the string matches the value of
your @code{TERM} environment variable. For example, you might have files
named @file{~/.ne/.keys}, @file{~/.ne/.keys-xterm}, and
@file{~/.ne/.keys-vt102}. The first file's key bindings will always be
loaded, while at most one of the latter two files will be loaded, depending
on your @code{TERM} environment variable.
You can change your key binding files' default base name from @file{.keys} by
passing an argument to the @code{--keys} option when you start @code{ne}, and
you can prevent any configuration from being loaded with the
@code{--no-config} option (@pxref{Arguments}).
The format of key binding files is simple: each line starting with the @samp{KEY}
sequence of capital characters is considered the description of a key binding.
Each line starting with @samp{SEQ} binds a character sequence to a key.
All other lines are considered comments. The format of a key binding
description is
@example
KEY @var{hexcode} @var{command}
@end example
The @var{hexcode} value is the @sc{ascii} code of the keystroke. (For
special keys such as @key{Insert} or function keys, you should take a
look at the file @file{default.keys} that comes with @code{ne}'s
distribution: it contains a complete, commented definition of
@code{ne}'s standard bindings that you can modify with a trial-and-error
approach.) The easiest way to see the code @code{ne} uses for a given
key is by using the @ref{KeyCode} command. It prompts you to press a
key, then reports the code for that key on the status bar. It also
displays the command bound to that key if there is one.
You can write just the hexadecimal digits, nothing else is
necessary (but a prefixing @samp{0x} is tolerated). For instance,
@example
KEY 1 MoveSOL
@end example
@noindent binds to @kbd{@key{Control}-A} the action of moving to the start
of a line, while
@example
KEY 101 LineUp
@end example
@noindent binds to the ``cursor-up'' key the action of moving the cursor
one line up.
@var{command} can be any @code{ne} command, including @code{Escape}
(which allows reconfiguring the menu activator) and @code{Macro}, which
allows binding complex sequences of actions to a single keystroke. The binding
of a macro is very fast because on the first call the macro is cached in memory.
@xref{Macro}.
Note that you cannot @emph{ever} redefine @key{Return} or @key{Escape}. This
is a basic issue---however brain damaged is the current configuration, you will
always be able to exploit fully the menus and the command line.
Besides the ``standard'' combinations (e.g.,
@kbd{@key{Control}-@var{letter}}), it possible to program combinations
based on the @key{Meta} key (a.k.a. @key{Alt}). The situation in this
case is a bit more involved, because depending on the terminal emulator you are
using, the effect of the @key{Meta} key can be widely different. For
instance, @code{xterm} raises the eighth bit of a character, so, for
instance,
@example
KEY 81 MoveSOF
@end example
@noindent binds @kbd{@key{Control}-@key{Meta}-a} to the action of moving to the
start of the document. However, @code{gnome-terminal} will emit the
character of ASCII code 1 prefixed with ESC instead (``@code{\x1b\x01}''). To handle this case,
@code{ne} provides codes from 180 on for @emph{simulated @key{Meta}
sequences}: for instance,
@example
KEY 181 MoveSOF
@end example
@noindent binds the abovementioned sequence to the same action as
before. In general, the code 180+@var{x} corresponds to the sequence
ESC followed by the ASCII character of code @var{x}. Note that some of
these sequences may be disabled, if they conflict with existing sequences
of your terminal (for instance, ESC followed by @samp{O} is always
disabled because it prefixes several built-in keyboard sequences).
As a final note, we remark that typing @kbd{@key{Meta}-a} on
@code{gnome-terminal} will produce an ESC followed by @samp{a} (``@code{\x1ba}''). Since it
is obviously easier to press just @key{Meta} rather than @key{Meta} and
@key{Control} at the same time, it is a good idea to associate the same
sequence also to this combination, using
@example
KEY 1E1 MoveSOF
@end example
@noindent Moreover, this setting provides the user with a second choice: one can
press @key{Escape} followed by a letter instead of using modifiers.
This is the approach used by default in @code{ne}: this way,
@key{Control} with @key{Meta} plus a letter should always work, and
@key{Meta} should work sometimes (of course, if you're sure to use
always the same kind of emulator you can bind more features). Again, the
best place to look is the @file{default.keys} file.
As stated above, each line starting with @samp{SEQ} binds a character sequence
to a key code. The format for a @samp{SEQ} binding is
@example
SEQ "@var{sequence}" @var{hexcode}
@end example
@noindent where "@var{sequence}" is a double-quoted string of characters
(which can include escaped hexadecimals) followed by a hexadecimal key code
as described above for @samp{KEY} definitions.
You should rarely need this, as properly configured systems already do
this for most keys. However, some key combinations (@key{Control} in
conjunction with cursor keys for example) are usually not defined. If you
know the character sequence your system generates for such a combination, you may use
@samp{SEQ} to bind that sequence to a particular key code if that sequence
isn't already defined on your system. For example,
@key{Control}-``cursor-left'' may generate the sequence
@code{\x1b[1;5D}. The following lines bind that sequence to the @key{f10}
key code @samp{14A}, then bind that key code to the @samp{HELP} command.
@example
SEQ "\x1b[1;5D" 14A
KEY 14A HELP
@end example
Sequences are inherently terminal- or terminal emulator-specific, so
their utility will vary depending on how many emulators you use. At least
they give you the possibility to use keys or key combinations that aren't
covered by @code{curses}.
The key binding file is parsed at startup. If something does not work,
@code{ne} exits displaying an error message. If you want @code{ne} to skip
parsing the key binding file (for instance, to correct the broken
file), just give @code{ne} the @code{--no-config} argument. @xref{Arguments}.
@node Changing Menus
@section Changing Menus
@cindex Configuring the menus
@code{ne} allows you to change the contents of its menus. To
accomplish this task, you have to create a file named @file{.menus} in your
home directory, or in @file{~/.ne}. You can change the default name
(possibly specifying a complete path) using the @code{--menus} argument
(@pxref{Arguments}).
Each line of a menu configuration file not starting with the @samp{MENU} or
@samp{ITEM} keywords is considered a comment. You should describe the menus
as in the following example:
@example
MENU "File"
ITEM "Open... ^O" Open
ITEM "Close " Close
ITEM "DoIt " Macro DoIt
@end example
In other words: a line of this form
@example
MENU "@var{title}"
@end example
will start the definition of a new menu, having the given title. Each line
of the form
@example
ITEM "@var{text}" @var{command}
@end example
will then define a menu item, and associate the given command to it.
Any number of menus can be accommodated, but you should consider that many
terminals are 80 columns wide. There is also a minor restriction on the
items---their width has to be constant throughout each menu (but different
menus can have different widths). Note that the text of an item, as the name of
a menu, is between quotes. Whatever follows the last quote is considered the
command associated to the menu.
@noindent @strong{Warning:} the description of key bindings in menus
(@samp{^O} in the previous example) is very important for the beginner;
there is no relation inside @code{ne} about what you say in the menu and how
you configure the key bindings (@pxref{Key Bindings}). Please do not say
things in the menus that are not true in the key binding file.
The menu configuration file is parsed at startup. If something does not
work, @code{ne} exits displaying an error message. If you want @code{ne} to
skip the menu configuration phase (for instance, to correct the
broken file), just give @code{ne} the @code{--no-config} argument.
@xref{Arguments}.
@node Virtual Extensions
@section Virtual Extensions
@cindex Virtual Extensions
@cindex Extension by Content
When a document is loaded or saved under a different name, @code{ne} may
examine the contents of the document to determine whether to ignore the
corresponding file's actual or missing extension and use instead a @dfn{virtual
extension}. This affects which AutoPrefs and Syntax settings @code{ne}
applies to the document.
The document's contents are matched against the regular expressions (see
@pxref{Regular Expressions}) you include in your @file{~/.ne/.extensions}
file. (There may also be an @file{extensions} file (no leading @samp{.})
in the global directory.) If @code{ne} finds a match it will act as if the
document's name had the corresponding extension.
Note that by default @code{ne} does not override a file's
given extension. However, you can specify any number of extensions that
you would like to allow to be overridden by including lines in your
@file{~/.ne/extensions} containing only a dot followed by a single extension or
shell ``glob pattern''. In particular, ``@code{.*}'' would
allow overriding all extensions.
@noindent Here's an example @file{~/.ne/.extensions} file:
@example
# The following patterns match some common command interpreters.
# They must match on the first line.
sh 1 ^#!\s*/.*\b(bash|sh|ksh|zsh)\s*
csh 1 ^#!\s*/.*\b(csh|tcsh)\s*
pl 1 ^#!\s*/.*\bperl\b
py 1 ^#!\s*/.*\bpython[0-9]*\s*
rb 1 ^#!\s*/.*\bruby\s*
xml 1 ^<\?xml
# These must match in the first 30 and 1000 lines, respectively.
yaml 30 ^---$
ini 1000i ^\[\s*(\w|[.-])+\s*\]\s*$
# You must list the existing extensions you wish to override, one
# per line. Shell glob patterns are allowed. Note that ".*" would
# allow overriding any extension. (Think before you do that!)
.conf
.tx[0-9]
@end example
@noindent The only lines which matter consist of a space-delimited set of
@verbatim
extension number regular_expression
@end verbatim
@noindent or a single @samp{.} followed by a glob pattern. Anything else is treated as a
comment. The number must be a positive integer indicating the maximum line number of the
document in which the corresponding regular expression must match. The exception is zero,
which allows a match on any line in the document. (Actually, ne restricts the examined
portion of the document to the first 1,000,000 bytes.) If the number has a lower-case
@samp{i} suffix (see the @file{ini} example above), the corresponding regular expression
is not case sensitive. Trailing spaces are not included as part of the regular_expression.
Only the last instance of any extension specification is considered. This allows you to
override any specifications from the global @file{extensions} file. If you really need
two different patterns, join them into one by concatenating them with a @samp{|} like so:
@verbatim
foo 1000i (pattern_A)|(pattern_B)
@end verbatim
@node Hints and Tricks
@chapter Hints and Tricks
@cindex Escape usage
@cindex Turbo adjustment
@cindex Shortcuts not working
@cindex Meta key
@cindex Changing colors
@cindex Large files
@table @emph
@item Use @key{f1} or @key{Escape}-@key{Escape}, not @key{Escape}.
Due to the limitations of the techniques used when communicating with a
terminal, it is not possible to ``decide'' that the user pressed the
@key{Escape} key for about a second after the actual key press
(@pxref{EscapeTime}). This means that you will experience annoying delays when
using menus. If you have no @key{f1} key, use @key{Escape}-@key{Escape}, or
redefine a keystroke assigning the command @code{Escape}, and you will be
able to use that keystroke instead of @key{Escape}. Unfortunately,
some GUI-based terminals (most notably, @code{gnome-terminal}) use @key{f1}
for their own purposes; in that case, you can assign the @code{Escape}
command to another key (@pxref{Configuration}).
@item Check for the presence of a @key{Meta} key.
If your system has a standard @key{Meta} or @key{Alt} key, there is a
good chance that you have several other shortcuts. If the built-in
@key{Meta} bindings do not work, you must discover which is the effect
of the @key{Meta} in your terminal emulator. Indeed, it is possible in
theory to configure about 150 shortcuts. @xref{Configuration}. In any
case, prefixing a key with @key{Escape} has the same effect as holding
down @key{Meta}, so with the standard key bindings
you can, for instance, advance by word with @kbd{@key{Escape}} followed by @kbd{F}.
@item When editing very large files, please use the @code{--no-syntax} option.
Even if @code{ne} will switch transparently to memory-mapped disk files, syntax
highlighting requires a great deal of additional memory.
@item Mac users should turn on ``Delete sends CTRL-H'' in the @command{Terminal} settings.
If you are a Mac user, you need to check the ``Delete sends CTRL-H'' option
in the @samp{Advanced} tab of the @command{Terminal} application settings.
@item @code{ne} does tilde expansion.
When you have to specify a file name, you can always start with @file{~/} in
order to specify your home directory, or @file{~@var{user}/} to
specify the home directory of another user.
@item It is easy to correct bad colors.
Sometimes, due to different opinions about the best default foreground and
background colors, some of the color choices in a syntax file might be
unreadable (for instance, @samp{dim white} on a terminal with a white
background). Just copy the guilty syntax specification file to the
@file{~/.ne/syntax} directory, and change the color names at the start of
the file.
@item Use the @samp{tabs} syntax to distinguish @sc{tab}s from @sc{space}s.
When you're struggling to clean up a mix of @sc{tab}s and @sc{space}s,
temporarily switching to the @samp{tabs} syntax may help. The command
@command{Syntax tabs} makes @sc{tab} characters show up in a different
background color from @sc{space}s. Once you've gotten your white space
issues straightened out, you can switch back to the syntax appropriate
for your current file type.
@item @code{ne} does interactive filename completion.
When you have to specify a file name as last element of a long input,
you can invoke the completer using @key{Tab}. If you hit it twice in a
row, you will enter the file requester, where you can navigate and
escape back to the command line, either with @key{f1}, which will let
you edit again your previous input, or with @key{Tab}, which will copy
your current selection over your previous file name. In other words, you
can freely alternate completion, editing and browsing.
@item Disable the status bar for slow connections.
@code{ne} tries to emit as few characters as possible when updating the
screen. However, for each key you type it is likely that the status
bar has to be updated. If your connection is very slow, you can
disable the status bar to get a quicker response (@pxref{StatusBar}).
@item The @key{Escape} delay when activating menus can be avoided.
If you press after @key{Escape} any key that does not produce the second
character of an escape sequence, @code{ne} will immediately recognize the
@key{Escape} key code as such. Since non-alphabetical keys have no effect while
browsing through the menus, if you're forced to use @key{Escape} as menu
activator you can press, for instance, @samp{,} just after it to speed
up the menu activation (note that @samp{:} would not work, because it would
activate the command line). Alternatively, you can just type @key{Escape}
twice in a row.
@item Use turbo mode for lengthy operations.
Turbo mode (@pxref{Turbo}) allows performing very complex operations
without updating the screen until the operations are complete. This can
be a major plus if you are editing very long files, or if your terminal
is slow. If the default value (0, which means twice the number of
visible rows) does not give you the best results, experiment other
values.
@item Regular expressions are powerful, and slow.
Regular expressions must be studied very carefully. If you spend a lot
of time doing editing, it is definitely reasonable to study even their
most esoteric features. Very complex editing actions can be performed by
a single find/replace using the @code{\@var{n}} convention. But remember
always that regular expressions are much slower than a normal search: in
particular, if you use them on a UTF-8 text, @code{ne} has to transform
them into an equivalent (but more complex) expression that cannot match
partially a UTF-8 sequence, and this expansion makes the search even
slower.
@item Use the correct movement commands in a macro.
Many boring, repetitive editing actions can be performed in a breeze
by recording them the first time. Remember, however, that while recording
a complex macro you should always use a cursor movement that will apply
in a different context. For instance, if you are copying a word, you cannot
move with cursor keys, because that word at another application of the
macro could be of a different length. Rather, use the next/previous word
keys and the @code{MoveEOW} command, which guarantee a correct behaviour in
all situations.
@item Some preferences can be preserved even with automatic preferences.
When you save an autoprefs file, the file simply contains a macro that,
when executed, produces the current configuration. However, you could want,
for instance, to never change the insert/overwrite state. In this case, just
edit the autoprefs files with @code{ne} and delete the line containing the
command setting the insert flag. When the autoprefs are loaded later, the
insert flag will be left untouched. This trick is particularly useful with
the @code{StatusBar} and @code{FastGUI} commands.
@item If some keystrokes do not work, check for system-specific features.
Sometimes it can happen that a keystroke does not work---for instance,
@kbd{@key{Control}-O} does not open a file. This usually is due to the kernel
tracking that key for its purposes. For instance, along a @code{telnet}
connection with xon/xoff flow control, @kbd{@key{Control}-S} and
@kbd{@key{Control}-Q} would block and release the output
instead of saving and quitting.
In these cases, if you do not need the system
feature you should check how to disable it: for instance, some @sc{bsd}-like
systems feature a delayed suspend signal that is not in the @sc{posix}
standard, and thus cannot be disabled by @code{ne}. On @sc{hp-ux}, the command
@code{stty dsusp ^-} would disable the signal, and would let the control
sequence previously assigned to it to run up to @code{ne}.
@end table
@node Motivations and Design
@chapter Motivations and Design
@cindex Mode
@cindex curses
@cindex POSIX
@cindex terminfo
@cindex termcap
@cindex Magic cookie terminals
@cindex Resource usage
@cindex Interrupt character
In this chapter I will try to outline the rationale behind @code{ne}'s
design choices. Moreover, some present, voluntary limitations of the current
implementation will be described. The intended audience of such a
description is the programmer wanting to hack up @code{ne}'s sources, or the
informed user wanting to deepen his knowledge of the limitations.
The design goal of @code{ne} was to write an editor that is easy to use
at first sight, powerful, and completely configurable. Making @code{ne}
run on any terminal that @code{vi} could handle was also a basic issue,
because there is no use getting accustomed to a new tool if you cannot
use it when you really need it. Finally, using resources sparingly was
considered essential.
@code{ne} has no concept of @emph{mode}. All shortcuts are defined by a
single key, possibly with a modifier (such as @key{Control} or @key{Meta}).
Modality is in my opinion a Bad Thing unless it has a very clear visual
feedback. As an example, menus are a form of modality. After entering the
menus, the alphabetic keys and the navigation keys have a different meaning.
But the modality is clearly reflected by a change in the user
interface. The same can be said about the input line, because it is always
preceded by a (possibly highlighted) prompt ending with a colon.
@code{ne} has no sophisticated visual updating system similar to, for
instance, the one of @code{curses}. All updating is done while
manipulating the text, and only if the turbo flag is set can some iterated
operations delay the update. (In this case, @code{ne} keeps track in a
very rough way of the part of the screen that changed.) Moreover, the
output is not preempted by additional input coming in, so that along a
slow connection the output could not keep up with the input. However,
along reasonably fast connections, the responsiveness of the editor is
greatly enhanced by the direct update. And since we update the screen in
parallel with the internal representation, we can exploit our knowledge to
output a very small number of characters per modification. As it is
typical in @code{ne}, when such design tradeoffs arise, preference is
given to the solution that is effective on a good part of the existing
hardware and will be very effective on most future hardware.
@code{ne} uses a particular scheme for handling text. There is a doubly
linked list of line descriptors that contain pointers to each line of text.
The lines themselves are kept in a list of pools, which is expanded and
reduced dynamically. The interesting thing is that for each pool @code{ne}
keeps track just of the first and of the last character used. A character is
free iff it contains a null, so there is no need for a list of free chunks.
The point is that the free characters lying between that first and the last
used characters (the @dfn{lost} characters) can only be allocated
@emph{locally}: whenever a line has to grow in length, @code{ne} first
checks if there are enough free characters around it. Otherwise, it remaps
the line elsewhere. Since editing is essentially a local activity, the
number of such lost characters remains very low. And the manipulation of a
line is extremely fast and independent of the size of the file, which can be
very huge. A mathematical analysis of the space/time tradeoff is rather
difficult, but empirical evidence suggests that the idea works.
@code{ne} takes the @sc{posix} standard as the basis for @sc{un*x}
compatibility. The fact that this standard has been designed by a worldwide
recognized and impartial organization such as @sc{ieee} makes it in my
opinion the most interesting effort in its league. No attempt is made to
support ten thousand different versions and releases by using conditional
compilation. Very few assumptions are made about the behaviour of the system
calls. This has obvious advantages in terms of code testing, maintenance, and
reliability. For the same reasons, the availability of an @sc{ansi} C
(C99) compiler is assumed.
If the system has a @code{terminfo} database and the related functions
(which are usually contained in the @code{curses} library), @code{ne} will
use them. The need for a terminal capability database is clear, and the
choice of @code{terminfo} (with respect to @code{termcap}) is compulsory
if you want to support a series of features (such as more than ten
function keys) that @code{termcap} lacks. If @code{terminfo} is not
available, @code{ne} can use a @code{termcap} database, or, as a last
resort, a built-in set of ANSI control sequences. Some details about
this can be found in @ref{Portability Problems}.
@code{ne} does not allow redefinition of the @key{Escape}, @key{Tab} or
@key{Return} keys, nor of the interrupt character
@kbd{@key{Control}-\}. This decision has been made mainly for two
reasons. First of all, it is necessary to keep a user from transforming
@code{ne}'s bindings to such a point that another unaware user cannot
work with it. These two keys and the alphabetic keys allow activating
any command without any further knowledge of the key bindings, so it
seems to me this is a good choice. As a second point, the @key{Escape}
key usage should generally be avoided. The reason is that most escape
sequences that are produced by special keys start with the escape
character. When @key{Escape} is pressed, @code{ne} has to wait for one
second (this timing can be changed with the @code{EscapeTime} command),
just to be sure that it did not receive the first character of an escape
sequence. This makes the response of the key very slow, unless it is
immediately followed by another key such as @samp{:}, or by @key{Escape},
again. @xref{Hints and Tricks}.
Note that, as has been stated several times, the custom key bindings also work
when doing a long input, navigating through the menus or browsing the
requester. However, this is only partially true. To keep the code size
and complexity down, in these cases @code{ne} recognizes only direct bindings to
commands, and discards the arguments. Thus, for instance, if a key is bound to
the command line @code{LineUp 2}, it will act like @code{LineUp}, while a
binding to @code{Macro MoveItUp} would produce no result. Of course full
binding capability is available while writing text. (This limitation will
probably be lifted in a future version: presently it does not seem to limit
seriously the configurability of @code{ne}.)
@code{ne} has some restrictions in its terminal handling. It does not support
highlighting on terminals that use a magic cookie. Supporting such terminals
correctly is a royal pain, and I did not have any means of testing the code anyway.
Moreover, they are rather obsolete. Another lack of support is for the
capability strings that specify a file to print or a program to launch in
order to initialize the terminal.
The macro capabilities of @code{ne} are rather limited. For instance, you
cannot give an argument to a macro: macros are simply sequences of commands that can be played
back automatically. This makes them very useful for everyday use in a
learn/play context, but rather inflexible for extending the capabilities of the
editor.
@code{ne} has been written with sparing resource use as a basic goal.
Every possible effort has been made to reduce the use of @sc{cpu} time and
memory, the number of system calls, and the number of characters output to
the terminal. For instance, command parsing is done through hash
techniques, and the escape sequence analysis uses the order structure of
strings for minimizing the number of comparisons. The optimal cursor
motion functions were directly copied from @code{emacs}. The update of
files using syntax highlighting is as lazy as possible: modifications
cause just the update of the current line, and the rest of the screen is
updated only when you move away. The search algorithm is a simplified
version of the Boyer-Moore algorithm that provides high performance with a
minimal setup time. An effort has been taken to move to the text segment
all data that do not change during the program execution. When the status
bar is switched off, additional optimizations reduce the cursor movement
to a minimum.
A word should be said about lists. Clearly, handling the text as a
single block with an insertion gap (a la @code{emacs}) allows you to
gain some memory. However, the management of the text as a linked list
requires much less @sc{cpu} time, and the tradeoff seems to be
particularly favorable on virtual memory systems, where moving the
insertion gap can require a lot of accesses to different pages.
@node The Encoding Mess
@chapter The Encoding Mess
@cindex UTF-8
@cindex ISO-8859 family
@cindex ISO-8859-1
@code{ne} supports UTF-8. It can use UTF-8 for its
input/output, and it can also interpret one or more documents as containing
UTF-8 encoded text, acting accordingly. Note that the document content is
actual UTF-8 text---@code{ne} does not use wide characters. As a
positive side-effect, @code{ne} can support fully the ISO-10646
standard, but nonetheless non-UTF-8 texts occupy exactly one byte per
character.
More precisely, @emph{any} piece of text in @code{ne} is classified as
US-ASCII, 8-bit or UTF-8. A US-ASCII text contains only US-ASCII
characters. An 8-bit text sports a one-to-one correspondence between
characters and bytes, whereas an UTF-8 text is interpreted in UTF-8. Of
course, this raises a difficult question: @emph{when} should a document be
classified as UTF-8?
Character encodings are a mess. There is nothing we can do to change
this fact, as character encodings are @emph{metadata that modify data
semantics}. The same file may represent different texts of different
lengths when interpreted with different encodings. Thus, there is no safe
way of guessing the encoding of a file.
@code{ne} stays on the safe side: it will never try to convert a file
from an encoding to another one. It can, however, interpret data
contained in a document depending on an encoding: in other words,
encodings are truly treated as metadata. You can switch off UTF-8
at any time, and see the same document as a standard 8-bit file.
Moreover, @code{ne} uses a @emph{lazy} approach to the problem: first of
all, unless the UTF-8 automatic detection flag is set
(@pxref{UTF8Auto}), no attempt is ever made to consider a file as UTF-8
encoded. Every file, clip, command line, etc., is firstly scanned for
non-US-ASCII characters: if it is entirely made of US-ASCII characters,
it is classified as US-ASCII. An US-ASCII piece of text is compatible
with anything else---it may be pasted in any document, or, if it is a
document, it may accept any form of text. Documents classified as US-ASCII
are distinguished by an @samp{A} on the status bar.
As soon as a user action forces a choice of encoding (e.g., an accented
character is typed, or an UTF-8-encoded clip is pasted), @code{ne} fixes
the mode to 8-bit or UTF-8 (when there is a choice, this depends on
the value of the @ref{UTF8Auto} flag). Of course, in some cases this may
be impossible, and in that case an error will be reported.
All this happens behind the scenes, and it is designed so that in 99% of
the cases there is no need to think of encodings. In any case, should
@code{ne}'s behaviour not match your needs, you can always change at run
time the level of UTF-8 support.
@node History
@chapter History
@cindex TurboText
@cindex Amiga
The main inspiration for this work came from Martin Taillefer's
@code{TurboText} for the Amiga, which is the best editor I ever saw on
any computer.
The first versions of @code{ne} were created on an Amiga 3000T, using
the port of the @code{curses} library by Simon John Raybould. After
switching to the lower-level @code{terminfo} library, the development
continued under @sc{un*x}. Finally, I ported @code{terminfo} to the
Amiga, thus making it possible to develop on that platform again. For
@code{ne} 1.0, an effort has been made to provide a @code{terminfo}
emulation using GNU's @code{termcap}.
The development eventually moved to Linux.
Todd Lewis got involved with @code{ne} when the University of North
Carolina's Chapel Hill campus migrated its central research computers
from @sc{mvs} to @sc{unix} in 1995. The readily available @sc{unix} editors
had serious weaknesses in their user interfaces, especially from the
standpoint of @sc{mvs} users who were not too excited about having to move
their projects to another platform while learning an entirely new suite
of tools. @code{ne} offered an easily understood interface with enough
capabilities to keep these new @sc{unix} users productive. Todd installed and
has maintained @code{ne} at UNC since then, making several improvements to the
code to meet his users' needs. In early 1999 his code base and
mine were merged to become version 1.17.
Support for syntax highlighting was added in 2009 with code and
techniques heavily borrowed from the GNU-licensed editor @code{joe},
which was written by Joseph H. Allen. Much of the work to incorporate
this code into @code{ne} was undertaken by Daniele Filaretti, an
undergraduate student working under the direction of Sebastiano at the
Universit@`a degli Studi di Milano.
@node Portability Problems
@chapter Portability Problems
@cindex Portability
@cindex POSIX
@cindex terminfo
@cindex termcap
@cindex Printable characters
This chapter is devoted to the description of the (hopefully very few) problems
that could arise when porting @code{ne} to other flavors of @sc{un*x}.
The fact that only @sc{posix} calls have been used (@pxref{Motivations
and Design}) should guarantee that on @sc{posix}-compliant systems a
recompilation should suffice. Unfortunately, @code{terminfo} has not
been standardized by @sc{ieee}, so that different calls could be
available. The necessary calls are @code{setupterm()}, @code{tparm()}
and @code{tputs()}. The other @code{terminfo} functions are never used.
If @code{terminfo} is not available, the source files @file{info2cap.c}
and @file{info2cap.h} map @code{terminfo} calls on @code{termcap}
calls. The complete GNU @code{termcap} sources are distributed with
@code{ne}, so no library at all is needed to use them. You just have to
compile using one of the options explained in the @file{makefile} and in
the @file{README}. Should you need comprehensive information on GNU
@code{termcap}, you can find the distribution files on any @code{ftp}
site that distributes the GNU archives. I should note that the GNU
@code{termcap} manual is definitely the best manual ever written about
terminal databases.
There are, however, some details that are not specified by @sc{posix}, or are
specified with insufficient precision. The places of the source where such
details come to the light are evidenced by the @samp{PORTABILITY PROBLEM}
string, which is followed by a complete explanation of the problem.
For instance, there is no standard way of printing extended @sc{ascii}
characters (i.e., characters whose code is smaller than 32 or greater than
126). On many system, these characters have to be filtered and replaced with
something printable: the default behaviour is to add 64 to all characters under
32 (so that control characters will translate to the respective letter) and to print
them in reverse video; moreover, all characters between 127 and 160 are visualized
as a reversed question mark (this works particularly well with ISO Latin
1, but Windows users might not like it). This behavior can be easily changed
by modifying the @code{out()} function in @file{term.c}.
Note that it is certainly possible that some system features not standardized
by @sc{posix} interfere with @code{ne}'s use of the I/O stream. Such problems
should be dealt with locally by using the system facilities rather than by
horribly @code{#ifdef}'ing the source code. An example is given in @ref{Hints
and Tricks}.
@node Acknowledgments
@chapter Acknowledgments
A lot of people contributed to this project. Part of the code comes from
@code{emacs} and @code{joe}. Many people, in particular at the silab (the Milan
University Computer Science Department Laboratory), helped in beta testing the
first versions. Daniele Filaretti worked at the integration of syntax-highlighting
code from @code{joe}. John Gabriele suggested several new features and relentlessly tested them.
Comments, complaints, desiderata are welcome.
@example
Sebastiano Vigna
Via California 22
I-20144 Milano MI
Italia
sebastiano.vigna@@unimi.it
Todd M. Lewis
418 Arlington Cir.
Sanford, NC 27330-7600
USA
utoddl@@gmail.com
ne home page: @uref{http://ne.di.unimi.it/}
Discuss ne at @uref{http://groups.google.com/group/niceeditor/}
Github repo: @uref{https://github.com/vigna/ne/}
@end example
@page
@page
@node Concept Index
@unnumbered Concept Index
@printindex cp
@page
@node Command Index
@unnumbered Command Index
@printindex cm
@bye
ne-3.3.4/doc/texinfo.cnf.in 0000664 0000000 0000000 00000000351 14751164310 0015442 0 ustar 00root root 0000000 0000000 @c Most of the world uses A4 paper, but texinfo defaults to
@c US Letter (8.5in x 11in) paper. If we can determine from
@c the locale that we should use 11in (279mm) paper, we'll take
@c the default. Otherwise we'll assume A4 paper.
ne-3.3.4/doc/texinfo.tex 0000775 0000000 0000000 00001005554 14751164310 0015105 0 ustar 00root root 0000000 0000000 % 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{2007-01-02.19}
%
% Copyright (C) 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995,
% 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
% 2007 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 2, 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 texinfo.tex file; see the file COPYING. If not, write
% to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
% Boston, MA 02110-1301, USA.
%
% 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 has been our intent since Texinfo was invented.)
%
% Please try the latest version of texinfo.tex before submitting bug
% reports; you can get the latest version from:
% http://www.gnu.org/software/texinfo/ (the Texinfo home page), or
% ftp://tug.org/tex/texinfo.tex
% (and all CTAN mirrors, see http://www.ctan.org).
% 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 http://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}
\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\ptexrbrace=\}
\let\ptexslash=\/
\let\ptexstar=\*
\let\ptext=\t
% 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\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
% Since the category of space is not known, we have to be careful.
\chardef\spacecat = 10
\def\spaceisspace{\catcode`\ =\spacecat}
% sometimes characters are active, so we need control sequences.
\chardef\colonChar = `\:
\chardef\commaChar = `\,
\chardef\dashChar = `\-
\chardef\dotChar = `\.
\chardef\exclamChar= `\!
\chardef\lquoteChar= `\`
\chardef\questChar = `\?
\chardef\rquoteChar= `\'
\chardef\semiChar = `\;
\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
}
% Margin to add to right of even pages, to left of odd pages.
\newdimen\bindingoffset
\newdimen\normaloffset
\newdimen\pagewidth \newdimen\pageheight
% 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}
% @| inserts a changebar to the left of the current line. It should
% surround any changed text. This approach does *not* work if the
% change spans more than two lines of output. To handle that, we would
% have adopt a much more difficult approach (putting marks into the main
% vertical list for the beginning and end of each change).
%
\def\|{%
% \vadjust can only be used in horizontal mode.
\leavevmode
%
% Append this vertical mode material after the current line in the output.
\vadjust{%
% We want to insert a rule with the height and depth of the current
% leading; that is exactly what \strutbox is supposed to record.
\vskip-\baselineskip
%
% \vadjust-items are inserted at the left edge of the type. So
% the \llap here moves out into the left-hand margin.
\llap{%
%
% For a thicker or thinner bar, change the `1pt'.
\vrule height\baselineskip width1pt
%
% This is the space between the bar and the text.
\hskip 12pt
}%
}%
}
% 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\undefined\else % etex gives us more logging
\tracingscantokens1
\tracingifs1
\tracinggroups1
\tracingnesting2
\tracingassigns1
\fi
\tracingcommands3 % 3 gives us more in etex
\errorcontextlines16
}%
% 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}
% For @cropmarks command.
% Do @cropmarks to get crop marks.
%
\newif\ifcropmarks
\let\cropmarks = \cropmarkstrue
%
% Dimensions to add cropmarks at corners.
% Added by P. A. MacKay, 12 Nov. 1986
%
\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines
\newdimen\cornerlong \cornerlong=1pc
\newdimen\cornerthick \cornerthick=.3pt
\newdimen\topandbottommargin \topandbottommargin=.75in
% Main output routine.
\chardef\PAGE = 255
\output = {\onepageout{\pagecontents\PAGE}}
\newbox\headlinebox
\newbox\footlinebox
% \onepageout takes a vbox as an argument. Note that \pagecontents
% does insertions, but you have to call it yourself.
\def\onepageout#1{%
\ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi
%
\ifodd\pageno \advance\hoffset by \bindingoffset
\else \advance\hoffset by -\bindingoffset\fi
%
% Do this outside of the \shipout so @code etc. will be expanded in
% the headline as they should be, not taken literally (outputting ''code).
\setbox\headlinebox = \vbox{\let\hsize=\pagewidth \makeheadline}%
\setbox\footlinebox = \vbox{\let\hsize=\pagewidth \makefootline}%
%
{%
% 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.
%
\indexdummies % don't expand commands in the output.
\normalturnoffactive % \ in index entries must not stay \, e.g., if
% the page break happens to be in the middle of an example.
% We don't want .vr (or whatever) entries like this:
% \entry{{\tt \indexbackslash }acronym}{32}{\code {\acronym}}
% "\acronym" won't work when it's read back in;
% it needs to be
% {\code {{\tt \backslashcurfont }acronym}
\shipout\vbox{%
% Do this early so pdf references go to the beginning of the page.
\ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi
%
\ifcropmarks \vbox to \outervsize\bgroup
\hsize = \outerhsize
\vskip-\topandbottommargin
\vtop to0pt{%
\line{\ewtop\hfil\ewtop}%
\nointerlineskip
\line{%
\vbox{\moveleft\cornerthick\nstop}%
\hfill
\vbox{\moveright\cornerthick\nstop}%
}%
\vss}%
\vskip\topandbottommargin
\line\bgroup
\hfil % center the page within the outer (page) hsize.
\ifodd\pageno\hskip\bindingoffset\fi
\vbox\bgroup
\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
%
\ifcropmarks
\egroup % end of \vbox\bgroup
\hfil\egroup % end of (centering) \line\bgroup
\vskip\topandbottommargin plus1fill minus1fill
\boxmaxdepth = \cornerthick
\vbox to0pt{\vss
\line{%
\vbox{\moveleft\cornerthick\nsbot}%
\hfill
\vbox{\moveright\cornerthick\nsbot}%
}%
\nointerlineskip
\line{\ewbot\hfil\ewbot}%
}%
\egroup % \vbox from first cropmarks clause
\fi
}% end of \shipout\vbox
}% end of group with \indexdummies
\advancepageno
\ifnum\outputpenalty>-20000 \else\dosupereject\fi
}
\newinsert\margin \dimen\margin=\maxdimen
\def\pagebody#1{\vbox to\pageheight{\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 \unvbox#1
\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi
\ifr@ggedbottom \kern-\dimen@ \vfil \fi}
}
% Here are the rules for the cropmarks. Note that they are
% offset so that the space between them is truly \outerhsize or \outervsize
% (P. A. MacKay, 12 November, 1986)
%
\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong}
\def\nstop{\vbox
{\hrule height\cornerthick depth\cornerlong width\cornerthick}}
\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong}
\def\nsbot{\vbox
{\hrule height\cornerlong depth\cornerthick width\cornerthick}}
% 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.
%
\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.
\def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm}
\def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm}
% Each occurence 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.
% (Similarily, 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\foo{...}
% is roughly equivalent to
% \def\foo{\parsearg\Xfoo}
% \def\Xfoo#1{...}
%
% Actually, I use \csname\string\foo\endcsname, ie. \\foo, as it is my
% favourite TeX trick. --kasal, 16nov03
\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 enviroments; they don't open a group. (The
% implementation of @end takes care not to call \endgroup in this
% special case.)
% At runtime, 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
}
% Evironment mismatch, #1 expected:
\def\badenverr{%
\errhelp = \EMsimple
\errmessage{This command can appear only \inenvironment\temp,
not \inenvironment\thisenv}%
}
\def\inenvironment#1{%
\ifx#1\empty
out 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, but... --kasal, 06nov03
\expandafter\checkenv\csname#1\endcsname
\csname E#1\endcsname
\endgroup
\fi
}
\newhelp\EMsimple{Press RETURN to continue.}
%% Simple single-character @ commands
% @@ prints an @
% Kludge this until the fonts are right (grr).
\def\@{{\tt\char64}}
% This is turned off because it was never documented
% and you can use @w{...} around a quote to suppress ligatures.
%% Define @` and @' to be the same as ` and '
%% but suppressing ligatures.
%\def\`{{`}}
%\def\'{{'}}
% Used to generate quoted braces.
\def\mylbrace {{\tt\char123}}
\def\myrbrace {{\tt\char125}}
\let\{=\mylbrace
\let\}=\myrbrace
\begingroup
% Definitions to produce \{ and \} commands for indices,
% and @{ and @} for the aux/toc files.
\catcode`\{ = \other \catcode`\} = \other
\catcode`\[ = 1 \catcode`\] = 2
\catcode`\! = 0 \catcode`\\ = \other
!gdef!lbracecmd[\{]%
!gdef!rbracecmd[\}]%
!gdef!lbraceatcmd[@{]%
!gdef!rbraceatcmd[@}]%
!endgroup
% @comma{} to avoid , parsing problems.
\let\comma = ,
% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent
% Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H.
\let\, = \c
\let\dotaccent = \.
\def\ringaccent#1{{\accent23 #1}}
\let\tieaccent = \t
\let\ubaraccent = \b
\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{\selectfonts\lllsize \underbar{a}}}
\def\ordm{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \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 \ptexi
\else\ifx\temp\jmacro \j
\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{\selectfonts\lllsize A}\vss}}%
\kern-.15em
\TeX
}
% 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\*{\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.
% \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 = \pageheight \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\pageheight
\page
\fi
\fi
\box\groupbox
\prevdepth = \dimen1
\checkinserts
}
%
% 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
% Old definition--didn't work.
%\parseargdef\need{\par %
%% This method tries to make TeX break the page naturally
%% if the depth of the box does not fit.
%{\baselineskip=0pt%
%\vtop to #1\mil{\vfil}\kern -#1\mil\nobreak
%\prevdepth=-1000pt
%}}
\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'.
%
\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 insert text of that file as input.
%
\def\include{\parseargusing\filenamecatcodes\includezzz}
\def\includezzz#1{%
\pushthisfilestack
\def\thisfile{#1}%
{%
\makevalueexpandable
\def\temp{\input #1 }%
\expandafter
}\temp
\popthisfilestack
}
\def\filenamecatcodes{%
\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\next\centerH
\else
\let\next\centerV
\fi
\next{\hfil \ignorespaces#1\unskip \hfil}%
}
\def\centerH#1{%
{%
\hfil\break
\advance\hsize by -\leftskip
\advance\hsize by -\rightskip
\line{#1}%
\break
}%
}
\def\centerV#1{\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\comment{\begingroup \catcode`\^^M=\other%
\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other%
\commentxxx}
{\catcode`\^^M=\other \gdef\commentxxx#1^^M{\endgroup}}
\let\c=\comment
% @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 = {}%
}
% @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 \ character.
% FYI, plain.tex uses \\ as a temporary control sequence (why?), but
% this is not advertised and we don't care. Texinfo does not
% otherwise define @\.
%
% The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\.
\def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi}
%
\def\math{%
\tex
\mathunderscore
\let\\ = \mathbackslash
\mathactive
$\finishmath
}
\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
\gdef\mathactive{%
\let^ = \ptexhat
\let< = \ptexless
\let> = \ptexgtr
\let+ = \ptexplus
}
}
% @bullet and @minus need the same treatment as @math, just above.
\def\bullet{$\ptexbullet$}
\def\minus{$-$}
% @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
}
% @comma{} is so commas can be inserted into text without messing up
% Texinfo's parsing.
%
\let\comma = ,
% @refill is a no-op.
\let\refill=\relax
% 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 (before @setfilename).
%
\newif\iflinks \linkstrue % by default we want the aux files.
\let\novalidate = \linksfalse
% @setfilename is done at the beginning of every texinfo file.
% So open here the files we need to have open while reading the input.
% This makes it possible to make a .fmt file for texinfo.
\def\setfilename{%
\fixbackslash % Turn off hack to swallow `\input texinfo'.
\iflinks
\tryauxfile
% Open the new aux file. TeX will close it automatically at exit.
\immediate\openout\auxfile=\jobname.aux
\fi % \openindices needs to do some work in any case.
\openindices
\let\setfilename=\comment % Ignore extra @setfilename cmds.
%
% If texinfo.cnf is present on the system, read it.
% Useful for site-wide @afourpaper, etc.
\openin 1 texinfo.cnf
\ifeof 1 \else \input texinfo.cnf \fi
\closein 1
%
\comment % Ignore the actual filename.
}
% Called from \setfilename.
%
\def\openindices{%
\newindex{cp}%
\newcodeindex{fn}%
\newcodeindex{vr}%
\newcodeindex{tp}%
\newcodeindex{ky}%
\newcodeindex{pg}%
}
% @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
\newcount\countA
\newif\ifpdf
\newif\ifpdfmakepagedest
% 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 \undefined,
% borrowed from ifpdf.sty.
\ifx\pdfoutput\undefined
\else
\ifx\pdfoutput\relax
\else
\ifcase\pdfoutput
\else
\pdftrue
\fi
\fi
\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.
% 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).
% double active backslashes.
%
{\catcode`\@=0 \catcode`\\=\active
@gdef@activebackslashdouble{%
@catcode`@\=@active
@let\=@doublebackslash}
}
% To handle parens, we must adopt a different approach, since parens are
% not active characters. hyperref.dtx (which has the same problem as
% us) handles it with this amazing macro to replace tokens, with minor
% changes for Texinfo. It is included here under the GPL by permission
% from the author, Heiko Oberdiek.
%
% #1 is the tokens to replace.
% #2 is the replacement.
% #3 is the control sequence with the string.
%
\def\HyPsdSubst#1#2#3{%
\def\HyPsdReplace##1#1##2\END{%
##1%
\ifx\\##2\\%
\else
#2%
\HyReturnAfterFi{%
\HyPsdReplace##2\END
}%
\fi
}%
\xdef#3{\expandafter\HyPsdReplace#3#1\END}%
}
\long\def\HyReturnAfterFi#1\fi{\fi#1}
% #1 is a control sequence in which to do the replacements.
\def\backslashparens#1{%
\xdef#1{#1}% redefine it as its expansion; the definition is simply
% \lastnode when called from \setref -> \pdfmkdest.
\HyPsdSubst{(}{\realbackslash(}{#1}%
\HyPsdSubst{)}{\realbackslash)}{#1}%
}
\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
\input pdfcolor
\pdfcatalog{/PageMode /UseOutlines}
%
% #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto).
\def\dopdfimage#1#2#3{%
\def\imagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}%
\def\imageheight{#3}\setbox2 = \hbox{\ignorespaces #3}%
%
% pdftex (and the PDF format) support .png, .jpg, .pdf (among
% others). Let's try in that order.
\let\pdfimgext=\empty
\begingroup
\openin 1 #1.png \ifeof 1
\openin 1 #1.jpg \ifeof 1
\openin 1 #1.jpeg \ifeof 1
\openin 1 #1.JPG \ifeof 1
\openin 1 #1.pdf \ifeof 1
\errhelp = \nopdfimagehelp
\errmessage{Could not find image file #1 for pdf}%
\else \gdef\pdfimgext{pdf}%
\fi
\else \gdef\pdfimgext{JPG}%
\fi
\else \gdef\pdfimgext{jpeg}%
\fi
\else \gdef\pdfimgext{jpg}%
\fi
\else \gdef\pdfimgext{png}%
\fi
\closein 1
\endgroup
%
% without \immediate, 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 \imagewidth \fi
\ifdim \wd2 >0pt height \imageheight \fi
\ifnum\pdftexversion<13
#1.\pdfimgext
\else
{#1.\pdfimgext}%
\fi
\ifnum\pdftexversion < 14 \else
\pdfrefximage \pdflastximage
\fi}
%
\def\pdfmkdest#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
\turnoffactive
\activebackslashdouble
\def\pdfdestname{#1}%
\backslashparens\pdfdestname
\pdfdest name{\pdfdestname} xyz%
}}
%
% used to mark target names; must be expandable.
\def\pdfmkpgn#1{#1}
%
\let\linkcolor = \Blue % was Cyan, but that seems light?
\def\endlink{\Black\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.
\def\pdfoutlinedest{#3}%
\ifx\pdfoutlinedest\empty
\def\pdfoutlinedest{#4}%
\else
% Doubled backslashes in the name.
{\activebackslashdouble \xdef\pdfoutlinedest{#3}%
\backslashparens\pdfoutlinedest}%
\fi
%
% Also double the backslashes in the display string.
{\activebackslashdouble \xdef\pdfoutlinetext{#1}%
\backslashparens\pdfoutlinetext}%
%
\pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{\pdfoutlinetext}%
}
%
\def\pdfmakeoutlines{%
\begingroup
% Thanh's hack / proper braces in bookmarks
\edef\mylbrace{\iftrue \string{\else}\fi}\let\{=\mylbrace
\edef\myrbrace{\iffalse{\else\string}\fi}\let\}=\myrbrace
%
% Read toc silently, to get counts of subentries for \pdfoutline.
\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.
%
% xx to do this right, we have to translate 8-bit characters to
% their "best" equivalent, based on the @documentencoding. Right
% now, I guess we'll just let the pdf reader have its way.
\indexnofonts
\setupdatafile
\catcode`\\=\active \otherbackslash
\input \jobname.toc
\endgroup
}
%
\def\skipspaces#1{\def\PP{#1}\def\D{|}%
\ifx\PP\D\let\nextsp\relax
\else\let\nextsp\skipspaces
\ifx\p\space\else\addtokens{\filename}{\PP}%
\advance\filenamelength by 1
\fi
\fi
\nextsp}
\def\getfilename#1{\filenamelength=0\expandafter\skipspaces#1|\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
\leavevmode\Red
\startlink attr{/Border [0 0 0]}%
user{/Subtype /Link /A << /S /URI /URI (#1) >>}%
\endgroup}
\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}}
\linkcolor #1\endlink}
\def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st}
\else
\let\pdfmkdest = \gobble
\let\pdfurl = \gobble
\let\endlink = \relax
\let\linkcolor = \relax
\let\pdfmakeoutlines = \relax
\fi % \ifx\pdfoutput
\message{fonts,}
% 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 ten#1\endcsname % change the current font
}
% Select #1 fonts with the current style.
%
\def\selectfonts#1{\csname #1fonts\endcsname \csname\curfontstyle\endcsname}
\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}}
\let\li = \sf % Sometimes we call it \li, not \sf.
% We don't need math for this font style.
\def\ttsl{\setfontstyle{ttsl}}
% Default leading.
\newdimen\textleading \textleading = 13.2pt
% 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}
%
\def\setleading#1{%
\normalbaselineskip = #1\relax
\normallineskip = \lineskipfactor\normalbaselineskip
\normalbaselines
\setbox\strutbox =\hbox{%
\vrule width0pt height\strutheightpercent\baselineskip
depth \strutdepthpercent \baselineskip
}%
}
% Set the font macro #1 to the font named #2, adding on the
% specified font prefix (normally `cm').
% #3 is the font's design size, #4 is a scale factor
\def\setfont#1#2#3#4{\dimen255=#3pt\divide\dimen255by1000
\multiply\dimen255by#4%
\global\font#1=\fontprefix#27t at \dimen255}
% Use cm as the default font prefix.
% To specify the font prefix, you must define \fontprefix
% before you read in texinfo.tex.
\ifx\fontprefix\undefined
\def\fontprefix{p}
\fi
% Support font families that don't use the same naming scheme as CM.
\def\rmshape{tmr}
\def\rmbshape{tmb} %where the normal face is bold
\def\bfshape{tmb}
\def\bxshape{tmb}
\def\ttshape{crr}
\def\ttbshape{crb}
\def\ttslshape{crro}
\def\itshape{tmri}
\def\itbshape{tmbi}
\def\slshape{tmro}
\def\slbshape{tmbo}
\def\sfshape{hvr}
\def\sfbshape{hvb}
\def\scshape{tmrc}
\def\scbshape{tmbc}
% Definitions for a main text size of 11pt. This is the default in
% Texinfo.
%
\def\definetextfontsizexi{
% Text fonts (11.2pt, magstep1).
\def\textnominalsize{11pt}
\edef\mainmagstep{\magstephalf}
\setfont\textrm\rmshape{10}{\mainmagstep}
\setfont\texttt\ttshape{9}{\mainmagstep}
\setfont\textbf\bfshape{10}{\mainmagstep}
\setfont\textit\itshape{10}{\mainmagstep}
\setfont\textsl\slshape{10}{\mainmagstep}
\setfont\textsf\sfshape{10}{\mainmagstep}
\setfont\textsc\scshape{10}{\mainmagstep}
\setfont\textttsl\ttslshape{9}{\mainmagstep}
\font\texti=cmmi10 scaled \mainmagstep
\font\textsy=cmsy10 scaled \mainmagstep
% A few fonts for @defun names and args.
\setfont\defbf\bfshape{10}{\magstep1}
\setfont\deftt\ttshape{9}{\magstep1}
\setfont\defttsl\ttslshape{9}{\magstep1}
\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf}
% Fonts for indices, footnotes, small examples (9pt).
\def\smallnominalsize{9pt}
\setfont\smallrm\rmshape{9}{1000}
\setfont\smalltt\ttshape{8.1}{1000}
\setfont\smallbf\bfshape{10}{900}
\setfont\smallit\itshape{9}{1000}
\setfont\smallsl\slshape{9}{1000}
\setfont\smallsf\sfshape{9}{1000}
\setfont\smallsc\scshape{10}{900}
\setfont\smallttsl\ttslshape{8.1}{1000}
\font\smalli=cmmi9
\font\smallsy=cmsy9
% Fonts for small examples (8pt).
\def\smallernominalsize{8pt}
\setfont\smallerrm\rmshape{8}{1000}
\setfont\smallertt\ttshape{7.2}{1000}
\setfont\smallerbf\bfshape{10}{800}
\setfont\smallerit\itshape{8}{1000}
\setfont\smallersl\slshape{8}{1000}
\setfont\smallersf\sfshape{8}{1000}
\setfont\smallersc\scshape{10}{800}
\setfont\smallerttsl\ttslshape{7.2}{1000}
\font\smalleri=cmmi8
\font\smallersy=cmsy8
% Fonts for title page (20.4pt):
\def\titlenominalsize{20pt}
\setfont\titlerm\rmbshape{12}{\magstep3}
\setfont\titleit\itbshape{10}{\magstep4}
\setfont\titlesl\slbshape{10}{\magstep4}
\setfont\titlett\ttbshape{9}{\magstep4}
\setfont\titlettsl\ttslshape{9}{\magstep4}
\setfont\titlesf\sfbshape{17}{\magstep1}
\let\titlebf=\titlerm
\setfont\titlesc\scbshape{10}{\magstep4}
\font\titlei=cmmi12 scaled \magstep3
\font\titlesy=cmsy10 scaled \magstep4
\def\authorrm{\secrm}
\def\authortt{\sectt}
% Chapter (and unnumbered) fonts (17.28pt).
\def\chapnominalsize{17pt}
\setfont\chaprm\rmbshape{12}{\magstep2}
\setfont\chapit\itbshape{10}{\magstep3}
\setfont\chapsl\slbshape{10}{\magstep3}
\setfont\chaptt\ttbshape{9}{\magstep3}
\setfont\chapttsl\ttslshape{9}{\magstep3}
\setfont\chapsf\sfbshape{17}{1000}
\let\chapbf=\chaprm
\setfont\chapsc\scbshape{10}{\magstep3}
\font\chapi=cmmi12 scaled \magstep2
\font\chapsy=cmsy10 scaled \magstep3
% Section fonts (14.4pt).
\def\secnominalsize{14pt}
\setfont\secrm\rmbshape{12}{\magstep1}
\setfont\secit\itbshape{10}{\magstep2}
\setfont\secsl\slbshape{10}{\magstep2}
\setfont\sectt\ttbshape{9}{\magstep2}
\setfont\secttsl\ttslshape{9}{\magstep2}
\setfont\secsf\sfbshape{12}{\magstep1}
\let\secbf\secrm
\setfont\secsc\scbshape{10}{\magstep2}
\font\seci=cmmi12 scaled \magstep1
\font\secsy=cmsy10 scaled \magstep2
% Subsection fonts (13.15pt).
\def\ssecnominalsize{13pt}
\setfont\ssecrm\rmbshape{12}{\magstephalf}
\setfont\ssecit\itbshape{10}{1315}
\setfont\ssecsl\slbshape{10}{1315}
\setfont\ssectt\ttbshape{9}{1315}
\setfont\ssecttsl\ttslshape{9}{1315}
\setfont\ssecsf\sfbshape{12}{\magstephalf}
\let\ssecbf\ssecrm
\setfont\ssecsc\scbshape{10}{1315}
\font\sseci=cmmi12 scaled \magstephalf
\font\ssecsy=cmsy10 scaled 1315
% Reduced fonts for @acro in text (10pt).
\def\reducednominalsize{10pt}
\setfont\reducedrm\rmshape{10}{1000}
\setfont\reducedtt\ttshape{9}{1000}
\setfont\reducedbf\bfshape{10}{1000}
\setfont\reducedit\itshape{10}{1000}
\setfont\reducedsl\slshape{10}{1000}
\setfont\reducedsf\sfshape{10}{1000}
\setfont\reducedsc\scshape{10}{1000}
\setfont\reducedttsl\ttslshape{9}{1000}
\font\reducedi=cmmi10
\font\reducedsy=cmsy10
% reset the current fonts
\textfonts
\rm
} % end of 11pt text font size definitions
% 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}
\setfont\texttt\ttshape{9}{\mainmagstep}
\setfont\textbf\bfshape{10}{\mainmagstep}
\setfont\textit\itshape{10}{\mainmagstep}
\setfont\textsl\slshape{10}{\mainmagstep}
\setfont\textsf\sfshape{10}{\mainmagstep}
\setfont\textsc\scshape{10}{\mainmagstep}
\setfont\textttsl\ttslshape{9}{\mainmagstep}
\font\texti=cmmi10 scaled \mainmagstep
\font\textsy=cmsy10 scaled \mainmagstep
% A few fonts for @defun names and args.
\setfont\defbf\bfshape{10}{\magstephalf}
\setfont\deftt\ttshape{9}{\magstephalf}
\setfont\defttsl\ttslshape{9}{\magstephalf}
\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf}
% Fonts for indices, footnotes, small examples (9pt).
\def\smallnominalsize{9pt}
\setfont\smallrm\rmshape{9}{1000}
\setfont\smalltt\ttshape{9}{900}
\setfont\smallbf\bfshape{10}{900}
\setfont\smallit\itshape{9}{1000}
\setfont\smallsl\slshape{9}{1000}
\setfont\smallsf\sfshape{9}{1000}
\setfont\smallsc\scshape{10}{900}
\setfont\smallttsl\ttslshape{9}{900}
\font\smalli=cmmi9
\font\smallsy=cmsy9
% Fonts for small examples (8pt).
\def\smallernominalsize{8pt}
\setfont\smallerrm\rmshape{8}{1000}
\setfont\smallertt\ttshape{9}{800}
\setfont\smallerbf\bfshape{10}{800}
\setfont\smallerit\itshape{8}{1000}
\setfont\smallersl\slshape{8}{1000}
\setfont\smallersf\sfshape{8}{1000}
\setfont\smallersc\scshape{10}{800}
\setfont\smallerttsl\ttslshape{9}{800}
\font\smalleri=cmmi8
\font\smallersy=cmsy8
% Fonts for title page (20.4pt):
\def\titlenominalsize{20pt}
\setfont\titlerm\rmbshape{12}{\magstep3}
\setfont\titleit\itbshape{10}{\magstep4}
\setfont\titlesl\slbshape{10}{\magstep4}
\setfont\titlett\ttbshape{9}{\magstep4}
\setfont\titlettsl\ttslshape{9}{\magstep4}
\setfont\titlesf\sfbshape{17}{\magstep1}
\let\titlebf=\titlerm
\setfont\titlesc\scbshape{10}{\magstep4}
\font\titlei=cmmi12 scaled \magstep3
\font\titlesy=cmsy10 scaled \magstep4
\def\authorrm{\secrm}
\def\authortt{\sectt}
% Chapter fonts (14.4pt).
\def\chapnominalsize{14pt}
\setfont\chaprm\rmbshape{12}{\magstep1}
\setfont\chapit\itbshape{10}{\magstep2}
\setfont\chapsl\slbshape{10}{\magstep2}
\setfont\chaptt\ttbshape{9}{\magstep2}
\setfont\chapttsl\ttslshape{9}{\magstep2}
\setfont\chapsf\sfbshape{12}{\magstep1}
\let\chapbf\chaprm
\setfont\chapsc\scbshape{10}{\magstep2}
\font\chapi=cmmi12 scaled \magstep1
\font\chapsy=cmsy10 scaled \magstep2
% Section fonts (12pt).
\def\secnominalsize{12pt}
\setfont\secrm\rmbshape{12}{1000}
\setfont\secit\itbshape{10}{\magstep1}
\setfont\secsl\slbshape{10}{\magstep1}
\setfont\sectt\ttbshape{9}{\magstep1}
\setfont\secttsl\ttslshape{9}{\magstep1}
\setfont\secsf\sfbshape{12}{1000}
\let\secbf\secrm
\setfont\secsc\scbshape{10}{\magstep1}
\font\seci=cmmi12
\font\secsy=cmsy10 scaled \magstep1
% Subsection fonts (10pt).
\def\ssecnominalsize{10pt}
\setfont\ssecrm\rmbshape{10}{1000}
\setfont\ssecit\itbshape{10}{1000}
\setfont\ssecsl\slbshape{10}{1000}
\setfont\ssectt\ttbshape{9}{1000}
\setfont\ssecttsl\ttslshape{9}{1000}
\setfont\ssecsf\sfbshape{10}{1000}
\let\ssecbf\ssecrm
\setfont\ssecsc\scbshape{10}{1000}
\font\sseci=cmmi10
\font\ssecsy=cmsy10
% Reduced fonts for @acro in text (9pt).
\def\reducednominalsize{9pt}
\setfont\reducedrm\rmshape{9}{1000}
\setfont\reducedtt\ttshape{9}{900}
\setfont\reducedbf\bfshape{10}{900}
\setfont\reducedit\itshape{9}{1000}
\setfont\reducedsl\slshape{9}{1000}
\setfont\reducedsf\sfshape{9}{1000}
\setfont\reducedsc\scshape{10}{900}
\setfont\reducedttsl\ttslshape{9}{900}
\font\reducedi=cmmi9
\font\reducedsy=cmsy9
% reduce space between paragraphs
\divide\parskip by 2
% reset the current fonts
\textfonts
\rm
} % end of 10pt text font size definitions
% We provide the user-level command
% @fonttextsize 10
% (or 11) to redefine the text font size. pt is assumed.
%
\def\xword{10}
\def\xiword{11}
%
\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
}
% In order for the font changes to affect most math symbols and letters,
% we have to define the \textfont of the standard families. Since
% texinfo doesn't allow for producing subscripts and superscripts except
% in the main text, we don't bother to reset \scriptfont and
% \scriptscriptfont (which would also require loading a lot more fonts).
%
\def\resetmathfonts{%
\textfont0=\tenrm \textfont1=\teni \textfont2=\tensy
\textfont\itfam=\tenit \textfont\slfam=\tensl \textfont\bffam=\tenbf
\textfont\ttfam=\tentt \textfont\sffam=\tensf
}
% The font-changing commands redefine the meanings of \tenSTYLE, 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
% \tenSTYLE to set the current font.
%
% Each font-changing command also sets the names \lsize (one size lower)
% and \lllsize (three sizes lower). These relative commands are used in
% the LaTeX logo and acronyms.
%
% This all needs generalizing, badly.
%
\def\textfonts{%
\let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl
\let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc
\let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy
\let\tenttsl=\textttsl
\def\curfontsize{text}%
\def\lsize{reduced}\def\lllsize{smaller}%
\resetmathfonts \setleading{\textleading}}
\def\titlefonts{%
\let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl
\let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc
\let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy
\let\tenttsl=\titlettsl
\def\curfontsize{title}%
\def\lsize{chap}\def\lllsize{subsec}%
\resetmathfonts \setleading{25pt}}
\def\titlefont#1{{\titlefonts\rm #1}}
\def\chapfonts{%
\let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl
\let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc
\let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy
\let\tenttsl=\chapttsl
\def\curfontsize{chap}%
\def\lsize{sec}\def\lllsize{text}%
\resetmathfonts \setleading{19pt}}
\def\secfonts{%
\let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl
\let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc
\let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy
\let\tenttsl=\secttsl
\def\curfontsize{sec}%
\def\lsize{subsec}\def\lllsize{reduced}%
\resetmathfonts \setleading{16pt}}
\def\subsecfonts{%
\let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl
\let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc
\let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy
\let\tenttsl=\ssecttsl
\def\curfontsize{ssec}%
\def\lsize{text}\def\lllsize{small}%
\resetmathfonts \setleading{15pt}}
\let\subsubsecfonts = \subsecfonts
\def\reducedfonts{%
\let\tenrm=\reducedrm \let\tenit=\reducedit \let\tensl=\reducedsl
\let\tenbf=\reducedbf \let\tentt=\reducedtt \let\reducedcaps=\reducedsc
\let\tensf=\reducedsf \let\teni=\reducedi \let\tensy=\reducedsy
\let\tenttsl=\reducedttsl
\def\curfontsize{reduced}%
\def\lsize{small}\def\lllsize{smaller}%
\resetmathfonts \setleading{10.5pt}}
\def\smallfonts{%
\let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl
\let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc
\let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy
\let\tenttsl=\smallttsl
\def\curfontsize{small}%
\def\lsize{smaller}\def\lllsize{smaller}%
\resetmathfonts \setleading{10.5pt}}
\def\smallerfonts{%
\let\tenrm=\smallerrm \let\tenit=\smallerit \let\tensl=\smallersl
\let\tenbf=\smallerbf \let\tentt=\smallertt \let\smallcaps=\smallersc
\let\tensf=\smallersf \let\teni=\smalleri \let\tensy=\smallersy
\let\tenttsl=\smallerttsl
\def\curfontsize{smaller}%
\def\lsize{smaller}\def\lllsize{smaller}%
\resetmathfonts \setleading{9.5pt}}
% 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
%
% I wish the USA used A4 paper.
% --karl, 24jan03.
% Set up the default fonts, so we can use them for creating boxes.
%
\definetextfontsizexi
% Define these so they can be easily changed for other fonts.
\def\angleleft{$\langle$}
\def\angleright{$\rangle$}
% Count depth in font-changes, for error checks
\newcount\fontdepth \fontdepth=0
% Fonts for short table of contents.
\setfont\shortcontrm\rmshape{12}{1000}
\setfont\shortcontbf\bfshape{10}{\magstep1} % no cmb12
\setfont\shortcontsl\slshape{12}{1000}
\setfont\shortconttt\ttshape{12}{900}
%% Add scribe-like font environments, plus @l for inline lisp (usually sans
%% serif) and @ii for TeX italic
% \smartitalic{ARG} outputs arg in italics, followed by an italic correction
% unless the following character is such as not to need one.
\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else
\ptexslash\fi\fi\fi}
\def\smartslanted#1{{\ifusingtt\ttsl\sl #1}\futurelet\next\smartitalicx}
\def\smartitalic#1{{\ifusingtt\ttsl\it #1}\futurelet\next\smartitalicx}
% like \smartslanted except unconditionally uses \ttsl.
% @var is set to this for defun arguments.
\def\ttslanted#1{{\ttsl #1}\futurelet\next\smartitalicx}
% like \smartslanted except unconditionally use \sl. We never want
% ttsl for book titles, do we?
\def\cite#1{{\sl #1}\futurelet\next\smartitalicx}
\let\i=\smartitalic
\let\slanted=\smartslanted
\let\var=\smartslanted
\let\dfn=\smartslanted
\let\emph=\smartitalic
% @b, explicit bold.
\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\dotChar =\@m \sfcode\questChar=\@m \sfcode\exclamChar=\@m
\sfcode\colonChar=\@m \sfcode\semiChar =\@m \sfcode\commaChar =\@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
\def\t#1{%
{\tt \rawbackslash \plainfrenchspacing #1}%
\null
}
\def\samp#1{`\tclose{#1}'\null}
\setfont\keyrm\rmshape{8}{1000}
\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}}}}
\def\key #1{{\nohyphenation \uppercase{#1}}\null}
% The old definition, with no lozenge:
%\def\key #1{{\ttsl \nohyphenation \uppercase{#1}}\null}
\def\ctrl #1{{\tt \rawbackslash \hat}#1}
% @file, @option are the same as @samp.
\let\file=\samp
\let\option=\samp
% @code is a modification of @t,
% which makes spaces the same size as normal in the surrounding text.
\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
%
\rawbackslash
\plainfrenchspacing
#1%
}%
\null
}
% We *must* turn on hyphenation at `-' and `_' in @code.
% 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\def\code{\begingroup
\catcode\rquoteChar=\active \catcode\lquoteChar=\active
\let'\codequoteright \let`\codequoteleft
%
\catcode\dashChar=\active \catcode\underChar=\active
\ifallowcodebreaks
\let-\codedash
\let_\codeunder
\else
\let-\realdash
\let_\realunder
\fi
\codex
}
}
\def\realdash{-}
\def\codedash{-\discretionary{}{}{}}
\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{}{}{}}%
{\_}%
}
\def\codex #1{\tclose{#1}\endgroup}
% An additional complication: the above will allow breaks after, e.g.,
% each of the four underscores in __typeof__. This is undesirable in
% some manuals, especially if they don't have long identifiers in
% general. @allowcodebreaks provides a way to control this.
%
\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'}%
\fi\fi
}
% @kbd is like @code, except that if the argument is just one @key command,
% then @kbd has no effect.
% @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 option `\txiarg'}%
\fi\fi\fi
}
\def\worddistinct{distinct}
\def\wordexample{example}
\def\wordcode{code}
% Default is `distinct.'
\kbdinputstyle distinct
\def\xkey{\key}
\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}%
\ifx\one\xkey\ifx\threex\three \key{#2}%
\else{\tclose{\kbdfont\look}}\fi
\else{\tclose{\kbdfont\look}}\fi}
% For @indicateurl, @env, @command quotes seem unnecessary, so use \code.
\let\indicateurl=\code
\let\env=\code
\let\command=\code
% @uref (abbreviation for `urlref') 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. Perhaps eventually put in
% a hypertex \special here.
%
\def\uref#1{\douref #1,,,\finish}
\def\douref#1,#2,#3,#4\finish{\begingroup
\unsepspaces
\pdfurl{#1}%
\setbox0 = \hbox{\ignorespaces #3}%
\ifdim\wd0 > 0pt
\unhbox0 % third arg given, show only that
\else
\setbox0 = \hbox{\ignorespaces #2}%
\ifdim\wd0 > 0pt
\ifpdf
\unhbox0 % PDF: 2nd arg given, show only it
\else
\unhbox0\ (\code{#1})% DVI: 2nd arg given, show both it and url
\fi
\else
\code{#1}% only url given, so show it
\fi
\fi
\endlink
\endgroup}
% @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}
\ifpdf
\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
% 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 }
% 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}
\def\kbd#1{\def\look{#1}\expandafter\kbdfoo\look??\par}
% @l was never documented to mean ``switch to the Lisp font'',
% and it is not used as such in any manual I can find. We need it for
% Polish suppressed-l. --karl, 22sep96.
%\def\l#1{{\li #1}\null}
% 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
% @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{%
{\selectfonts\lsize #1}%
\def\temp{#2}%
\ifx\temp\empty \else
\space ({\unsepspaces \ignorespaces \temp \unskip})%
\fi
}
% @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
}
% @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
}
% @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{\selectfonts\lllsize 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\undefined
\def\Orb{\mathhexbox20D}
\fi
\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
% Do an implicit @contents or @shortcontents after @end titlepage if the
% user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage.
%
\newif\ifsetcontentsaftertitlepage
\let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue
\newif\ifsetshortcontentsaftertitlepage
\let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue
\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
%
% If they want short, they certainly want long too.
\ifsetshortcontentsaftertitlepage
\shortcontents
\contents
\global\let\shortcontents = \relax
\global\let\contents = \relax
\fi
%
\ifsetcontentsaftertitlepage
\contents
\global\let\contents = \relax
\global\let\shortcontents = \relax
\fi
}
\def\finishtitlepage{%
\vskip4pt \hrule height 2pt width \hsize
\vskip\titlepagebottomglue
\finishedtitlepagetrue
}
%%% Macros to be used within @titlepage:
\let\subtitlerm=\tenrm
\def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}
\def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines
\let\tt=\authortt}
\parseargdef\title{%
\checkenv\titlepage
\leftline{\titlefonts\rm #1}
% 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
{\authorfont \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 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\pageheight by -12pt
\global\advance\vsize by -12pt
}
\parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}}
% @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.
\def\headings #1 {\csname HEADINGS#1\endcsname}
\def\HEADINGSoff{%
\global\evenheadline={\hfil} \global\evenfootline={\hfil}
\global\oddheadline={\hfil} \global\oddfootline={\hfil}}
\HEADINGSoff
% 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{\thischapter\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{\thischapter\hfil\folio}}
\global\oddheadline={\line{\thischapter\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{\thischapter\hfil\folio}}
\global\let\contentsalignmacro = \chapoddpage
}
\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex}
\def\HEADINGSsinglex{%
\global\evenfootline={\hfil}
\global\oddfootline={\hfil}
\global\evenheadline={\line{\thischapter\hfil\folio}}
\global\oddheadline={\line{\thischapter\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\undefined
\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
\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
\def\itemcontents{#1}%
% @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}%
\vadjust{\penalty 1200}}% not good to break after first line of item.
\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. Note that \everycr resets \everytab.
\def\headitem{\checkenv\multitable \crcr \global\everytab={\bf}\the\everytab}%
%
% 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 encounter the problem it was intended to solve again.
% --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={}%
\global\colcount=0 % Reset the column counter.
% Check for saved footnotes, etc.
\checkinserts
% Keeps underfull box messages off when table breaks over pages.
%\filbreak
% Maybe so, but it also creates really weird page breaks when the
% table breaks over pages. Wouldn't \vfil be better? Wait until the
% problem manifests itself, so it can be fixed for real --karl.
}%
}%
%
\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-\realdash \let_\normalunderscore
}
}
% We have this subroutine so that we can handle at least some @value's
% properly in indexes (we call \makevalueexpandable in \indexdummies).
% The command has to be fully expandable (if the variable is set), since
% the result winds up in the index file. This means that if the
% variable's value contains other Texinfo commands, it's almost certain
% it will fail (although perhaps we could fix that with sufficient work
% to do a one-level expansion on the result, instead of complete).
%
\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
}
% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined
% with @set.
%
% To get special treatment of `@end ifset,' call \makeond and the 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 ifclear reads 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}}
% @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 foo.
% It automatically defines \fooindex such that
% \fooindex ...rest of line... puts an entry in the index foo.
% It also defines \fooindfile to be the number of the output channel for
% the file that accumulates this index. The file's extension is foo.
% The name of an index should be no more than 2 characters long
% for the sake of vms.
%
\def\newindex#1{%
\iflinks
\expandafter\newwrite \csname#1indfile\endcsname
\openout \csname#1indfile\endcsname \jobname.#1 % Open the file
\fi
\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{%
\iflinks
\expandafter\newwrite \csname#1indfile\endcsname
\openout \csname#1indfile\endcsname \jobname.#1
\fi
\expandafter\xdef\csname#1index\endcsname{%
\noexpand\docodeindex{#1}}%
}
% @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{%
% Only do \closeout if we haven't already done it, else we'll end up
% closing the target index.
\expandafter \ifx\csname donesynindex#2\endcsname \undefined
% The \closeout helps reduce unnecessary open files; the limit on the
% Acorn RISC OS is a mere 16 files.
\expandafter\closeout\csname#2indfile\endcsname
\expandafter\let\csname\donesynindex#2\endcsname = 1
\fi
% 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 \fooindex macros.
% Argument #1 is generated by the calling \fooindex macro,
% and it is "foo", the name of the index.
% \doindex just uses \parsearg; it calls \doind for the actual work.
% This is because \doind is more useful to call from other macros.
% There is also \dosubind {index}{topic}{subtopic}
% which makes an entry in a two-level index such as the operation index.
\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer}
\def\singleindexer #1{\doind{\indexname}{#1}}
% like the previous two, but they put @code around the argument.
\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer}
\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}}
% Take care of Texinfo commands that can appear in an index entry.
% Since there are some commands we want to expand, and others we don't,
% we have to laboriously prevent expansion for those that we don't.
%
\def\indexdummies{%
\escapechar = `\\ % use backslash in output files.
\def\@{@}% change to @@ when we switch to @ as escape char in index files.
\def\ {\realbackslash\space }%
%
% Need these in case \tex is in effect and \{ is a \delimiter again.
% But can't use \lbracecmd and \rbracecmd because texindex assumes
% braces and backslashes are used only as delimiters.
\let\{ = \mylbrace
\let\} = \myrbrace
%
% I don't entirely understand this, but when an index entry is
% generated from a macro call, the \endinput which \scanmacro inserts
% causes processing to be prematurely terminated. This is,
% apparently, because \indexsorttmp is fully expanded, and \endinput
% is an expandable command. The redefinition below makes \endinput
% disappear altogether for that purpose -- although logging shows that
% processing continues to some further point. On the other hand, it
% seems \endinput does not hurt in the printed index arg, since that
% is still getting written without apparent harm.
%
% Sample source (mac-idx3.tex, reported by Graham Percival to
% help-texinfo, 22may06):
% @macro funindex {WORD}
% @findex xyz
% @end macro
% ...
% @funindex commtest
%
% The above is not enough to reproduce the bug, but it gives the flavor.
%
% Sample whatsit resulting:
% .@write3{\entry{xyz}{@folio }{@code {xyz@endinput }}}
%
% So:
\let\endinput = \empty
%
% Do the redefinitions.
\commondummies
}
% For the aux and toc files, @ is the escape character. So we want to
% redefine everything using @ as the escape character (instead of
% \realbackslash, still used for index files). When everything uses @,
% this will be simpler.
%
\def\atdummies{%
\def\@{@@}%
\def\ {@ }%
\let\{ = \lbraceatcmd
\let\} = \rbraceatcmd
%
% Do the redefinitions.
\commondummies
\otherbackslash
}
% Called from \indexdummies and \atdummies.
%
\def\commondummies{%
%
% \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.
%
% For control letters, we have \definedummyletter, which omits the
% space.
%
% 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).
%
\def\definedummyword ##1{\def##1{\string##1\space}}%
\def\definedummyletter##1{\def##1{\string##1}}%
\let\definedummyaccent\definedummyletter
%
\commondummiesnofonts
%
\definedummyletter\_%
%
% Non-English letters.
\definedummyword\AA
\definedummyword\AE
\definedummyword\L
\definedummyword\OE
\definedummyword\O
\definedummyword\aa
\definedummyword\ae
\definedummyword\l
\definedummyword\oe
\definedummyword\o
\definedummyword\ss
\definedummyword\exclamdown
\definedummyword\questiondown
\definedummyword\ordf
\definedummyword\ordm
%
% 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\bullet
\definedummyword\comma
\definedummyword\copyright
\definedummyword\registeredsymbol
\definedummyword\dots
\definedummyword\enddots
\definedummyword\equiv
\definedummyword\error
\definedummyword\euro
\definedummyword\expansion
\definedummyword\minus
\definedummyword\pounds
\definedummyword\point
\definedummyword\print
\definedummyword\result
\definedummyword\textdegree
%
% We want to disable all macros so that they are not expanded by \write.
\macrolist
%
\normalturnoffactive
%
% Handle some cases of @value -- where it does not contain any
% (non-fully-expandable) commands.
\makevalueexpandable
}
% \commondummiesnofonts: common to \commondummies and \indexnofonts.
%
\def\commondummiesnofonts{%
% Control letters and accents.
\definedummyletter\!%
\definedummyaccent\"%
\definedummyaccent\'%
\definedummyletter\*%
\definedummyaccent\,%
\definedummyletter\.%
\definedummyletter\/%
\definedummyletter\:%
\definedummyaccent\=%
\definedummyletter\?%
\definedummyaccent\^%
\definedummyaccent\`%
\definedummyaccent\~%
\definedummyword\u
\definedummyword\v
\definedummyword\H
\definedummyword\dotaccent
\definedummyword\ringaccent
\definedummyword\tieaccent
\definedummyword\ubaraccent
\definedummyword\udotaccent
\definedummyword\dotless
%
% Texinfo font commands.
\definedummyword\b
\definedummyword\i
\definedummyword\r
\definedummyword\sc
\definedummyword\t
%
% Commands that take arguments.
\definedummyword\acronym
\definedummyword\cite
\definedummyword\code
\definedummyword\command
\definedummyword\dfn
\definedummyword\emph
\definedummyword\env
\definedummyword\file
\definedummyword\kbd
\definedummyword\key
\definedummyword\math
\definedummyword\option
\definedummyword\pxref
\definedummyword\ref
\definedummyword\samp
\definedummyword\strong
\definedummyword\tie
\definedummyword\uref
\definedummyword\url
\definedummyword\var
\definedummyword\verb
\definedummyword\w
\definedummyword\xref
}
% \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\definedummyaccent##1{\let##1\asis}%
% We can just ignore other control letters.
\def\definedummyletter##1{\let##1\empty}%
% Hopefully, all control words can become @asis.
\let\definedummyword\definedummyaccent
%
\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\@{@}%
% how to handle braces?
\def\_{\normalunderscore}%
%
% Non-English letters.
\def\AA{AA}%
\def\AE{AE}%
\def\L{L}%
\def\OE{OE}%
\def\O{O}%
\def\aa{aa}%
\def\ae{ae}%
\def\l{l}%
\def\oe{oe}%
\def\o{o}%
\def\ss{ss}%
\def\exclamdown{!}%
\def\questiondown{?}%
\def\ordf{a}%
\def\ordm{o}%
%
\def\LaTeX{LaTeX}%
\def\TeX{TeX}%
%
% Assorted special characters.
% (The following {} will end up in the sort string, but that's ok.)
\def\bullet{bullet}%
\def\comma{,}%
\def\copyright{copyright}%
\def\registeredsymbol{R}%
\def\dots{...}%
\def\enddots{...}%
\def\equiv{==}%
\def\error{error}%
\def\euro{euro}%
\def\expansion{==>}%
\def\minus{-}%
\def\pounds{pounds}%
\def\point{.}%
\def\print{-|}%
\def\result{=>}%
\def\textdegree{degrees}%
%
% 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\indexbackslash=0 %overridden during \printindex.
\let\SETmarginindex=\relax % put index entries in margin (undocumented)?
% Most index entries go through here, but \dosubind is the general case.
% #1 is the index name, #2 is the entry text.
\def\doind#1#2{\dosubind{#1}{#2}{}}
% Workhorse for all \fooindexes.
% #1 is name of index, #2 is stuff to put there, #3 is subentry --
% empty if called from \doind, as we usually are (the main exception
% is with most defuns, which call us directly).
%
\def\dosubind#1#2#3{%
\iflinks
{%
% Store the main index entry text (including the third arg).
\toks0 = {#2}%
% If third arg is present, precede it with a space.
\def\thirdarg{#3}%
\ifx\thirdarg\empty \else
\toks0 = \expandafter{\the\toks0 \space #3}%
\fi
%
\edef\writeto{\csname#1indfile\endcsname}%
%
\ifvmode
\dosubindsanitize
\else
\dosubindwrite
\fi
}%
\fi
}
% Write the entry in \toks0 to the index file:
%
\def\dosubindwrite{%
% Put the index entry in the margin if desired.
\ifx\SETmarginindex\relax\else
\insert\margin{\hbox{\vrule height8pt depth3pt width0pt \the\toks0}}%
\fi
%
% Remember, we are within a group.
\indexdummies % Must do this here, since \bf, etc expand at this stage
\def\backslashcurfont{\indexbackslash}% \indexbackslash isn't defined now
% so it will be output as is; and it will print as backslash.
%
% Process the index entry with all font commands turned off, to
% get the string to sort by.
{\indexnofonts
\edef\temp{\the\toks0}% need full expansion
\xdef\indexsorttmp{\temp}%
}%
%
% 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{\indexsorttmp}{\noexpand\folio}{\the\toks0}}%
}%
\temp
}
% Take care of unwanted page breaks:
%
% 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 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}
%
% ..., ready, GO:
%
\def\dosubindsanitize{%
% \lastskip and \lastpenalty cannot both be nonzero simultaneously.
\skip0 = \lastskip
\edef\lastskipmacro{\the\lastskip}%
\count255 = \lastpenalty
%
% If \lastskip is nonzero, that means the last item was a
% skip. And since a skip is discardable, that means this
% -\skip0 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-\skip0
\fi
%
\dosubindwrite
%
\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\count255>9999 \penalty\count255 \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\skip0
\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}
% for the beginning of a topic that is used with subtopics
% \secondary {subtopic}{pagelist}
% for each 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}
\def\cindexsub {\begingroup\obeylines\cindexsub}
{\obeylines %
\gdef\cindexsub "#1" #2^^M{\endgroup %
\dosubind{cp}{#2}{#1}}}
% 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
\everypar = {}% don't want the \kern\-parindent from indentation suppression.
%
% See if the index file exists and is nonempty.
% Change catcode of @ here so that if the index file contains
% \initial {@}
% as its first line, TeX doesn't complain about mismatched braces
% (because it thinks @} is a control sequence).
\catcode`\@ = 11
\openin 1 \jobname.#1s
\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
\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 \temp
\ifeof 1
\putwordIndexIsEmpty
\else
% Index files are almost Texinfo source, but we use \ as the escape
% character. It would be better to use @, but that's too big a change
% to make right now.
\def\indexbackslash{\backslashcurfont}%
\catcode`\\ = 0
\escapechar = `\\
\begindoublecolumns
\input \jobname.#1s
\enddoublecolumns
\fi
\fi
\closein 1
\endgroup}
% These macros are used by the sorted index file itself.
% Change them to control the appearance of the index.
\def\initial#1{{%
% Some minor font changes for the special characters.
\let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt
%
% Remove any glue we may have, we'll be inserting our own.
\removelastskip
%
% We like breaks before the index initials, so insert a bonus.
\nobreak
\vskip 0pt plus 3\baselineskip
\penalty 0
\vskip 0pt plus -3\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 .5\baselineskip
\leftline{\secbf #1}%
% Do our best not to break after the initial.
\nobreak
\vskip .33\baselineskip plus .1\baselineskip
}}
% \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.
%
% A straightforward implementation would start like this:
% \def\entry#1#2{...
% But this frozes the catcodes in the argument, and can cause problems to
% @code, which sets - active. This problem was fixed by a kludge---
% ``-'' was active throughout whole index, but this isn't really right.
%
% The right solution is to prevent \entry from swallowing the whole text.
% --kasal, 21nov03
\def\entry{%
\begingroup
%
% Start a new paragraph if necessary, so our assignments below can't
% affect previous text.
\par
%
% Do not fill out the last line with white space.
\parfillskip = 0in
%
% No extra space above this paragraph.
\parskip = 0in
%
% Do not prefer a separate line ending with a hyphen to fewer lines.
\finalhyphendemerits = 0
%
% \hangindent is only relevant when the entry text and page number
% don't both fit on one line. In that case, bob suggests starting the
% dots pretty far over on the line. Unfortunately, a large
% indentation looks wrong when the entry text itself is broken across
% lines. So we use a small indentation and put up with long leaders.
%
% \hangafter is reset to 1 (which is the value we want) at the start
% of each paragraph, so we need not do anything with that.
\hangindent = 2em
%
% When the entry text needs to be broken, just fill out the first line
% with blank space.
\rightskip = 0pt plus1fil
%
% A bit of stretch before each entry for the benefit of balancing
% columns.
\vskip 0pt plus1pt
%
% Swallow the left brace of the text (first parameter):
\afterassignment\doentry
\let\temp =
}
\def\doentry{%
\bgroup % Instead of the swallowed brace.
\noindent
\aftergroup\finishentry
% And now comes the text of the entry.
}
\def\finishentry#1{%
% #1 is the page number.
%
% The following is kludged to not output a line of dots in the index if
% there are no page numbers. The next person who breaks this will be
% cursed by a Unix daemon.
\def\tempa{{\rm }}%
\def\tempb{#1}%
\edef\tempc{\tempa}%
\edef\tempd{\tempb}%
\ifx\tempc\tempd
\ %
\else
%
% If we must, put the page number on a line of its own, and fill out
% this line with blank space. (The \hfil is overwhelmed with the
% fill leaders glue in \indexdotfill if the page number does fit.)
\hfil\penalty50
\null\nobreak\indexdotfill % Have leaders before the page number.
%
% The `\ ' here is removed by the implicit \unskip that TeX does as
% part of (the primitive) \par. Without it, a spurious underfull
% \hbox ensues.
\ifpdf
\pdfgettoks#1.%
\ \the\toksA
\else
\ #1%
\fi
\fi
\par
\endgroup
}
% Like plain.tex's \dotfill, except uses up at least 1 em.
\def\indexdotfill{\cleaders
\hbox{$\mathsurround=0pt \mkern1.5mu.\mkern1.5mu$}\hskip 1em plus 1fill}
\def\primary #1{\line{#1\hfil}}
\newskip\secondaryindent \secondaryindent=0.5cm
\def\secondary#1#2{{%
\parfillskip=0in
\parskip=0in
\hangindent=1in
\hangafter=1
\noindent\hskip\secondaryindent\hbox{#1}\indexdotfill
\ifpdf
\pdfgettoks#2.\ \the\toksA % The page number ends the paragraph.
\else
#2
\fi
\par
}}
% 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
\newbox\partialpage
\newdimen\doublecolumnhsize
\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns
% Grab any single-column material above us.
\output = {%
%
% Here is a possibility not foreseen in manmac: if we accumulate a
% whole lot of material, we might end up calling this \output
% routine twice in a row (see the doublecol-lose test, which is
% essentially a couple of indexes with @setchapternewpage off). In
% that case we just ship out what is in \partialpage with the normal
% output routine. Generally, \partialpage will be empty when this
% runs and this will be a no-op. See the indexspread.tex test case.
\ifvoid\partialpage \else
\onepageout{\pagecontents\partialpage}%
\fi
%
\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
%
% Double the \vsize as well. (We don't need a separate register here,
% since nobody clobbers \vsize.)
\vsize = 2\vsize
}
% The double-column output routine for all double-column pages except
% the last.
%
\def\doublecolumnout{%
\splittopskip=\topskip \splitmaxdepth=\maxdepth
% Get the available space for the double columns -- the normal
% (undoubled) page height minus any material left over from the
% previous page.
\dimen@ = \vsize
\divide\dimen@ by 2
\advance\dimen@ by -\ht\partialpage
%
% box0 will be the left-hand column, box2 the right.
\setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@
\onepageout\pagesofar
\unvbox255
\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\pagewidth{\box0\hfil\box2}%
}
%
% All done with double columns.
\def\enddoublecolumns{%
\output = {%
% Split the last of the double-column material. Leave it on the
% current page, no automatic page break.
\balancecolumns
%
% If we end up splitting too much material for the current page,
% though, there will be another page break right after this \output
% invocation ends. Having called \balancecolumns once, we do not
% want to call it again. Therefore, reset \output to its normal
% definition right away. (We hope \balancecolumns will never be
% called on to balance too much material, but if it is, this makes
% the output somewhat more palatable.)
\global\output = {\onepageout{\pagecontents\PAGE}}%
}%
\eject
\endgroup % started in \begindoublecolumns
%
% \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 (after the
% \endgroup where \vsize got restored).
\pagegoal = \vsize
}
%
% Called at the end of the double column material.
\def\balancecolumns{%
\setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120.
\dimen@ = \ht0
\advance\dimen@ by \topskip
\advance\dimen@ by-\baselineskip
\divide\dimen@ by 2 % target to split to
%debug\message{final 2-column material height=\the\ht0, target=\the\dimen@.}%
\splittopskip = \topskip
% Loop until we get a decent breakpoint.
{%
\vbadness = 10000
\loop
\global\setbox3 = \copy0
\global\setbox1 = \vsplit3 to \dimen@
\ifdim\ht3>\dimen@
\global\advance\dimen@ by 1pt
\repeat
}%
%debug\message{split to \the\dimen@, column heights: \the\ht1, \the\ht3.}%
\setbox0=\vbox to\dimen@{\unvbox1}%
\setbox2=\vbox to\dimen@{\unvbox3}%
%
\pagesofar
}
\catcode`\@ = \other
\message{sectioning,}
% Chapters, sections, etc.
% \unnumberedno is an oxymoron, of course. 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 this as the name of the chapter.
% page headings and footings can use it. @section does likewise.
% However, they are not reliable, because we don't use marks.
\def\thischapter{}
\def\thissection{}
\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}
\let\up=\raisesections % original BFox name
% @lowersections: treat @chapter as section, @section as subsection, etc.
\def\lowersections{\global\advance\secbase by 1}
\let\down=\lowersections % original BFox name
% we only have subsub.
\chardef\maxseclevel = 3
%
% A numbered section within an unnumbered changes to unnumbered too.
% To achive this, remember the "biggest" unnum. sec. we are currently in:
\chardef\unmlevel = \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 < \unmlevel
\chardef\unmlevel = \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 > \unmlevel
\def\headtype{U}%
\else
\chardef\unmlevel = 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
%
\message{\putwordChapter\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 apphead0 calls appendixzzz
\def\appendixzzz#1{%
\global\secno=0 \global\subsecno=0 \global\subsubsecno=0
\global\advance\appendixno by 1
\gdef\chaplevelprefix{\appendixletter.}%
\resetallfloatnos
%
\def\appendixnum{\putwordAppendix\space \appendixletter}%
\message{\appendixnum}%
%
\chapmacro{#1}{Yappendix}{\appendixletter}%
%
\global\let\section = \appendixsec
\global\let\subsection = \appendixsubsec
\global\let\subsubsection = \appendixsubsubsec
}
\outer\parseargdef\unnumbered{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz
\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{%
% Well, we could do the following in a group, but that would break
% an assumption that \chapmacro is called at the outermost level.
% Thus we are safer this way: --kasal, 24feb04
\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}%
}
\outer\parseargdef\appendixsection{\apphead1{#1}} % normally calls appendixsectionzzz
\def\appendixsectionzzz#1{%
\global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1
\sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}%
}
\let\appendixsec\appendixsection
\outer\parseargdef\unnumberedsec{\unnmhead1{#1}} % normally calls unnumberedseczzz
\def\unnumberedseczzz#1{%
\global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1
\sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}%
}
% Subsections.
\outer\parseargdef\numberedsubsec{\numhead2{#1}} % normally calls numberedsubseczzz
\def\numberedsubseczzz#1{%
\global\subsubsecno=0 \global\advance\subsecno by 1
\sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}%
}
\outer\parseargdef\appendixsubsec{\apphead2{#1}} % normally calls appendixsubseczzz
\def\appendixsubseczzz#1{%
\global\subsubsecno=0 \global\advance\subsecno by 1
\sectionheading{#1}{subsec}{Yappendix}%
{\appendixletter.\the\secno.\the\subsecno}%
}
\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} %normally calls unnumberedsubseczzz
\def\unnumberedsubseczzz#1{%
\global\subsubsecno=0 \global\advance\subsecno by 1
\sectionheading{#1}{subsec}{Ynothing}%
{\the\unnumberedno.\the\secno.\the\subsecno}%
}
% Subsubsections.
\outer\parseargdef\numberedsubsubsec{\numhead3{#1}} % normally numberedsubsubseczzz
\def\numberedsubsubseczzz#1{%
\global\advance\subsubsecno by 1
\sectionheading{#1}{subsubsec}{Ynumbered}%
{\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}%
}
\outer\parseargdef\appendixsubsubsec{\apphead3{#1}} % normally appendixsubsubseczzz
\def\appendixsubsubseczzz#1{%
\global\advance\subsubsecno by 1
\sectionheading{#1}{subsubsec}{Yappendix}%
{\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}%
}
\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} %normally unnumberedsubsubseczzz
\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
% NOTE on use of \vbox for chapter headings, section headings, and such:
% 1) We use \vbox rather than the earlier \line to permit
% overlong headings to fold.
% 2) \hyphenpenalty is set to 10000 because hyphenation in a
% heading is obnoxious; this forbids it.
% 3) Likewise, headings look best if no \parindent is used, and
% if justification is not attempted. Hence \raggedright.
\def\majorheading{%
{\advance\chapheadingskip by 10pt \chapbreak }%
\parsearg\chapheadingzzz
}
\def\chapheading{\chapbreak \parsearg\chapheadingzzz}
\def\chapheadingzzz#1{%
{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
\parindent=0pt\raggedright
\rm #1\hfill}}%
\bigskip \par\penalty 200\relax
\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}
%%% Define plain chapter starts, and page on/off switching for it
% Parameter controlling skip before chapter headings (if needed)
\newskip\chapheadingskip
\def\chapbreak{\dobreak \chapheadingskip {-4000}}
\def\chappager{\par\vfill\supereject}
\def\chapoddpage{\chappager \ifodd\pageno \else \hbox to 0pt{} \chappager\fi}
\def\setchapternewpage #1 {\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
% Chapter opening.
%
% #1 is the text, #2 is the section type (Ynumbered, Ynothing,
% Yappendix, Yomitfromtoc), #3 the chapter number.
%
% To test against our argument.
\def\Ynothingkeyword{Ynothing}
\def\Yomitfromtockeyword{Yomitfromtoc}
\def\Yappendixkeyword{Yappendix}
%
\def\chapmacro#1#2#3{%
\pchapsepmacro
{%
\chapfonts \rm
%
% Have to define \thissection 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\thissection{#1}%
\gdef\thischaptername{#1}%
%
% Only insert the separating space if we have a chapter/appendix
% number, and don't print the unnumbered ``number''.
\def\temptype{#2}%
\ifx\temptype\Ynothingkeyword
\setbox0 = \hbox{}%
\def\toctype{unnchap}%
\gdef\thischapternum{}%
\gdef\thischapter{#1}%
\else\ifx\temptype\Yomitfromtockeyword
\setbox0 = \hbox{}% contents like unnumbered, but no toc entry
\def\toctype{omit}%
\gdef\thischapternum{}%
\gdef\thischapter{}%
\else\ifx\temptype\Yappendixkeyword
\setbox0 = \hbox{\putwordAppendix{} #3\enspace}%
\def\toctype{app}%
\xdef\thischapternum{\appendixletter}%
% We don't substitute the actual chapter name into \thischapter
% because we don't want its macros evaluated now. And we don't
% use \thissection because that changes with each section.
%
\xdef\thischapter{\putwordAppendix{} \appendixletter:
\noexpand\thischaptername}%
\else
\setbox0 = \hbox{#3\enspace}%
\def\toctype{numchap}%
\xdef\thischapternum{\the\chapno}%
\xdef\thischapter{\putwordChapter{} \the\chapno:
\noexpand\thischaptername}%
\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.
\vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright
\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
}
% I don't think this chapter style is supported any more, so I'm not
% updating it with the new noderef stuff. We'll see. --karl, 11aug03.
%
\def\setchapterstyle #1 {\csname CHAPF#1\endcsname}
%
\def\unnchfopen #1{%
\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
\parindent=0pt\raggedright
\rm #1\hfill}}\bigskip \par\nobreak
}
\def\chfopen #1#2{\chapoddpage {\chapfonts
\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}%
\par\penalty 5000 %
}
\def\centerchfopen #1{%
\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
\parindent=0pt
\hfill {\rm #1}\hfill}}\bigskip \par\nobreak
}
\def\CHAPFopen{%
\global\let\chapmacro=\chfopen
\global\let\centerchapmacro=\centerchfopen}
% 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, #2 is the section level (sec/subsec/subsubsec), #3 is
% the section type for xrefs (Ynumbered, Ynothing, Yappendix), #4 is the
% section number.
%
\def\sectionheading#1#2#3#4{%
{%
% Switch to the right set of fonts.
\csname #2fonts\endcsname \rm
%
% Insert space above the heading.
\csname #2headingbreak\endcsname
%
% Only insert the space after the number if we have a section number.
\def\sectionlevel{#2}%
\def\temptype{#3}%
%
\ifx\temptype\Ynothingkeyword
\setbox0 = \hbox{}%
\def\toctype{unn}%
\gdef\thissection{#1}%
\else\ifx\temptype\Yomitfromtockeyword
% for @headings -- no section number, don't include in toc,
% and don't redefine \thissection.
\setbox0 = \hbox{}%
\def\toctype{omit}%
\let\sectionlevel=\empty
\else\ifx\temptype\Yappendixkeyword
\setbox0 = \hbox{#4\enspace}%
\def\toctype{app}%
\gdef\thissection{#1}%
\else
\setbox0 = \hbox{#4\enspace}%
\def\toctype{num}%
\gdef\thissection{#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 \raggedright
\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.)
\vskip-\parskip
%
% This is purely so the last item on the list is a known \penalty >
% 10000. This is so \startdefun can avoid allowing breakpoints after
% section headings. Otherwise, it would insert a valid breakpoint between:
%
% @section sec-whatever
% @deffn def-whatever
\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'.
\ifpdf \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 \jobname.toc
}
\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.
\def\thischapter{}%
\chapmacro{#1}{Yomitfromtoc}{}%
%
\savepageno = \pageno
\begingroup % Set up to handle contents files properly.
\raggedbottom % Worry more about breakpoints than the bottom.
\advance\hsize by -\contentsrightmargin % Don't use the full line length.
%
% Roman numerals for page numbers.
\ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi
}
% Normal (long) toc.
\def\contents{%
\startcontents{\putwordTOC}%
\openin 1 \jobname.toc
\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\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 \jobname.toc
\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, ...
% 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}\labelspace#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
\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.
% @point{}, @result{}, @expansion{}, @print{}, @equiv{}.
%
% Since these characters are used in examples, it should be an even number of
% \tt widths. Each \tt character is 1en, so two makes it 1em.
%
\def\point{$\star$}
\def\result{\leavevmode\raise.15ex\hbox to 1em{\hfil$\Rightarrow$\hfil}}
\def\expansion{\leavevmode\raise.1ex\hbox to 1em{\hfil$\mapsto$\hfil}}
\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}}
\def\equiv{\leavevmode\lower.1ex\hbox to 1em{\hfil$\ptexequiv$\hfil}}
% The @error{} command.
% Adapted from the TeXbook's \boxit.
%
\newbox\errorbox
%
{\tentt \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 error\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}
% @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 tex @ character.
\envdef\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
\escapechar=`\\
%
\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\*=\ptexstar
\let\t=\ptext
\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
% 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
}}
\let\afterenvbreak = \aboveenvbreak
% \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.
\font\circle=lcircle10
\newdimen\circthick
\newdimen\cartouter\newdimen\cartinner
\newskip\normbskip\newskip\normpskip\newskip\normlskip
\circthick=\fontdimen8\circle
%
\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
\envdef\cartouche{%
\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
% Flag to tell @lisp, etc., not to narrow margin.
\let\nonarrowing = t%
\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
\checkinserts
}
% This macro is called at the beginning of all the @example variants,
% inside a group.
\def\nonfillstart{%
\aboveenvbreak
\hfuzz = 12pt % 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
\parindent = 0pt
\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
}
% 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 by one command:
\def\makedispenv #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 synonyms:
\def\maketwodispenvs #1#2#3{
\makedispenv{#1}{#3}
\makedispenv{#2}{#3}
}
% @lisp: indented, narrowed, typewriter font; @example: same as @lisp.
%
% @smallexample and @smalllisp: use smaller fonts.
% Originally contributed by Pavel@xerox.
%
\maketwodispenvs {lisp}{example}{%
\nonfillstart
\tt\quoteexpand
\let\kbdfont = \kbdexamplefont % Allow @kbd to do something special.
\gobble % eat return
}
% @display/@smalldisplay: same as @lisp except keep current font.
%
\makedispenv {display}{%
\nonfillstart
\gobble
}
% @format/@smallformat: same as @display except don't narrow margins.
%
\makedispenv{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
\gobble
}
\let\Eflushright = \afterenvbreak
% @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.
%
\envdef\quotation{%
{\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
\advance\rightskip by \lispnarrowing
\exdentamount = \lispnarrowing
\else
\let\nonarrowing = \relax
\fi
\parsearg\quotationlabel
}
% We have retained a nonzero parskip for the environment, since we're
% doing normal filling.
%
\def\Equotation{%
\par
\ifx\quotationauthor\undefined\else
% indent a bit.
\leftline{\kern 2\leftskip \sl ---\quotationauthor}%
\fi
{\parskip=0pt \afterenvbreak}%
}
% 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
}
% 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\"%
}
%
% [Knuth] p. 380
\def\uncatcodespecials{%
\def\do##1{\catcode`##1=\other}\dospecials}
%
% [Knuth] pp. 380,381,391
% Disable Spanish ligatures ?` and !` of \tt font
\begingroup
\catcode`\`=\active\gdef`{\relax\lq}
\endgroup
%
% 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}%
\catcode`\`=\active
\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
%
\def\starttabbox{\setbox0=\hbox\bgroup}
% Allow an option to not replace quotes with a regular directed right
% quote/apostrophe (char 0x27), but instead use 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{%
\expandafter\ifx\csname SETcodequoteundirected\endcsname\relax
'%
\else
\char'15
\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{%
\expandafter\ifx\csname SETcodequotebacktick\endcsname\relax
`%
\else
\char'22
\fi
}
%
\begingroup
\catcode`\^^I=\active
\gdef\tabexpand{%
\catcode`\^^I=\active
\def^^I{\leavevmode\egroup
\dimen0=\wd0 % the width so far, or since the previous tab
\divide\dimen0 by\tabw
\multiply\dimen0 by\tabw % compute previous multiple of \tabw
\advance\dimen0 by\tabw % advance to next multiple of \tabw
\wd0=\dimen0 \box0 \starttabbox
}%
}
\catcode`\'=\active
\gdef\rquoteexpand{\catcode\rquoteChar=\active \def'{\codequoteright}}%
%
\catcode`\`=\active
\gdef\lquoteexpand{\catcode\lquoteChar=\active \def`{\codequoteleft}}%
%
\gdef\quoteexpand{\rquoteexpand \lquoteexpand}%
\endgroup
% start the verbatim environment.
\def\setupverbatim{%
\let\nonarrowing = t%
\nonfillstart
% Easiest (and conventionally used) font for verbatim
\tt
\def\par{\leavevmode\egroup\box0\endgraf}%
\catcode`\`=\active
\tabexpand
\quoteexpand
% 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
\input #1
\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 very 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
% Start the processing of @deffn:
\def\startdefun{%
\ifnum\lastpenalty<10000
\medbreak
\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 \defargscommonending, 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.
%
\ifnum\lastpenalty=10002 \penalty2000 \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 \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
\endgraf
\nobreak\vskip -\parskip
\penalty 10002 % 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 remainnig 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
%
% Define \deffn and \deffnx, without parameters.
% \deffnheader has to be defined explicitly.
%
\def\domakedefun#1#2#3{%
\envdef#1{%
\startdefun
\parseargusing\activeparens{\printdefunline#3}%
}%
\def#2{\dodefunx#1}%
\def#3%
}
%%% 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{%
% Remember that \dosubind{fn}{foo}{} is equivalent to \doind{fn}{foo}.
\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}%
\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 {} }
%%% Type:
% @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{%
% Get the values of \leftskip and \rightskip as they were outside the @def...
\advance\leftskip by -\defbodyindent
%
% How we'll format the type 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.
% 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
% The continuations:
\dimen2=\hsize \advance\dimen2 by -\defargsindent
% (plain.tex says that \dimen1 should be used only as global.)
\parshape 2 0in \dimen0 \defargsindent \dimen2
%
% Put the type name to 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}% return value type
\ifx\temp\empty\else \tclose{\temp} \fi
#3% output function name
}%
{\rm\enskip}% hskip 0.5 em of \tenrm
%
\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. Let's try @var for that.
\let\var=\ttslanted
#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}
}
\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
}
\def\badparencount{%
\errmessage{Unbalanced parentheses in @def}%
\global\parencount=0
}
\def\badbrackcount{%
\errmessage{Unbalanced square braces 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\undefined
\newwrite\macscribble
\def\scantokens#1{%
\toks0={#1}%
\immediate\openout\macscribble=\jobname.tmp
\immediate\write\macscribble{\the\toks0}%
\immediate\closeout\macscribble
\input \jobname.tmp
}
\fi
\def\scanmacro#1{%
\begingroup
\newlinechar`\^^M
\let\xeatspaces\eatspaces
% Undo catcode changes of \startcontents and \doprintindex
% When called from @insertcopying or (short)caption, we need active
% backslash to get it printed correctly. Previously, we had
% \catcode`\\=\other instead. We'll see whether a problem appears
% with macro expansion. --kasal, 19aug04
\catcode`\@=0 \catcode`\\=\active \escapechar=`\@
% ... and \example
\spaceisspace
%
% Append \endinput to make sure that TeX does not see the ending newline.
% I've verified that it is necessary both for e-TeX and for ordinary TeX
% --kasal, 29nov03
\scantokens{#1\endinput}%
\endgroup
}
\def\scanexp#1{%
\edef\temp{\noexpand\scanmacro{#1}}%
\temp
}
\newcount\paramno % Count of parameters
\newtoks\macname % Macro name
\newif\ifrecursive % Is it recursive?
% List of all defined macros in the form
% \definedummyword\macro1\definedummyword\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\definedummyword#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 \.
% 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{%
\catcode`\"=\other
\catcode`\+=\other
\catcode`\<=\other
\catcode`\>=\other
\catcode`\@=\other
\catcode`\^=\other
\catcode`\_=\other
\catcode`\|=\other
\catcode`\~=\other
}
\def\scanargctxt{%
\scanctxt
\catcode`\\=\other
\catcode`\^^M=\other
}
\def\macrobodyctxt{%
\scanctxt
\catcode`\{=\other
\catcode`\}=\other
\catcode`\^^M=\other
\usembodybackslash
}
\def\macroargctxt{%
\scanctxt
\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\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%
\else
\expandafter\parsemargdef \argl;%
\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\definedummyword\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\definedummyword \noexpand#1%
\fi
}
% This makes use of the obscure 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.
\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}}
% Parse the optional {params} list. Set up \paramno and \paramlist
% so \defmacro knows what to do. Define \macarg.blah for each blah
% in the params list, to be ##N where N is the position in that list.
% That gets used by \mbodybackslash (above).
% We need to get `macro parameter char #' into several definitions.
% The technique used is stolen from LaTeX: let \hash be something
% unexpandable, insert that wherever you need a #, and then redefine
% it to # just before using the token list produced.
%
% The same technique is used to protect \eatspaces till just before
% the macro is used.
\def\parsemargdef#1;{\paramno=0\def\paramlist{}%
\let\hash\relax\let\xeatspaces\relax\parsemargdefxxx#1,;,}
\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}
% These two commands read recursive and nonrecursive macro bodies.
% (They're different since rec and nonrec macros end differently.)
\long\def\parsemacbody#1@end macro%
{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}%
\long\def\parsermacbody#1@end rmacro%
{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}%
% This defines the macro itself. There are six cases: recursive and
% nonrecursive macros of zero, one, and many arguments.
% Much magic with \expandafter here.
% \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
\ifrecursive
\ifcase\paramno
% 0
\expandafter\xdef\csname\the\macname\endcsname{%
\noexpand\scanmacro{\temp}}%
\or % 1
\expandafter\xdef\csname\the\macname\endcsname{%
\bgroup\noexpand\macroargctxt
\noexpand\braceorline
\expandafter\noexpand\csname\the\macname xxx\endcsname}%
\expandafter\xdef\csname\the\macname xxx\endcsname##1{%
\egroup\noexpand\scanmacro{\temp}}%
\else % many
\expandafter\xdef\csname\the\macname\endcsname{%
\bgroup\noexpand\macroargctxt
\noexpand\csname\the\macname xx\endcsname}%
\expandafter\xdef\csname\the\macname xx\endcsname##1{%
\expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}%
\expandafter\expandafter
\expandafter\xdef
\expandafter\expandafter
\csname\the\macname xxx\endcsname
\paramlist{\egroup\noexpand\scanmacro{\temp}}%
\fi
\else
\ifcase\paramno
% 0
\expandafter\xdef\csname\the\macname\endcsname{%
\noexpand\norecurse{\the\macname}%
\noexpand\scanmacro{\temp}\egroup}%
\or % 1
\expandafter\xdef\csname\the\macname\endcsname{%
\bgroup\noexpand\macroargctxt
\noexpand\braceorline
\expandafter\noexpand\csname\the\macname xxx\endcsname}%
\expandafter\xdef\csname\the\macname xxx\endcsname##1{%
\egroup
\noexpand\norecurse{\the\macname}%
\noexpand\scanmacro{\temp}\egroup}%
\else % many
\expandafter\xdef\csname\the\macname\endcsname{%
\bgroup\noexpand\macroargctxt
\expandafter\noexpand\csname\the\macname xx\endcsname}%
\expandafter\xdef\csname\the\macname xx\endcsname##1{%
\expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}%
\expandafter\expandafter
\expandafter\xdef
\expandafter\expandafter
\csname\the\macname xxx\endcsname
\paramlist{%
\egroup
\noexpand\norecurse{\the\macname}%
\noexpand\scanmacro{\temp}\egroup}%
\fi
\fi}
\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}}
% \braceorline decides whether the next nonwhitespace character is a
% {. If so it reads up to the closing }, if not, it reads the whole
% line. Whatever was read is then fed to the next control sequence
% as an argument (by \parsebrace or \parsearg)
\def\braceorline#1{\let\macnamexxx=#1\futurelet\nchar\braceorlinexxx}
\def\braceorlinexxx{%
\ifx\nchar\bgroup\else
\expandafter\parsearg
\fi \macnamexxx}
% @alias.
% We need some trickery to remove the optional spaces around the equal
% sign. Just 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}}
\let\nwnode=\node
\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 \thissection,
% 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
{%
\atdummies % preserve commands, but don't expand them
\edef\writexrdef##1##2{%
\write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef
##1}{##2}}% these are parameters of \writexrdef
}%
\toks0 = \expandafter{\thissection}%
\immediate \writexrdef{title}{\the\toks0 }%
\immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc.
\writexrdef{pg}{\folio}% will be written later, during \shipout
}%
\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#1{\putwordsee{} \xrefX[#1,,,,,,,]}
\def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]}
\def\ref#1{\xrefX[#1,,,,,,,]}
\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup
\unsepspaces
\def\printedmanual{\ignorespaces #5}%
\def\printedrefname{\ignorespaces #3}%
\setbox1=\hbox{\printedmanual\unskip}%
\setbox0=\hbox{\printedrefname\unskip}%
\ifdim \wd0 = 0pt
% No printed node name was explicitly given.
\expandafter\ifx\csname SETxref-automatic-section-title\endcsname\relax
% Use the node name inside the square brackets.
\def\printedrefname{\ignorespaces #1}%
\else
% Use the actual chapter/section title appear inside
% the square brackets. Use the real section title if we have it.
\ifdim \wd1 > 0pt
% It is in another manual, so we don't have it.
\def\printedrefname{\ignorespaces #1}%
\else
\ifhavexrefs
% We 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
\leavevmode
\getfilename{#4}%
{\indexnofonts
\turnoffactive
% See comments at \activebackslashdouble.
{\activebackslashdouble \xdef\pdfxrefdest{#1}%
\backslashparens\pdfxrefdest}%
%
\ifnum\filenamelength>0
\startlink attr{/Border [0 0 0]}%
goto file{\the\filename.pdf} name{\pdfxrefdest}%
\else
\startlink attr{/Border [0 0 0]}%
goto name{\pdfmkpgn{\pdfxrefdest}}%
\fi
}%
\linkcolor
\fi
%
% Float references are printed completely differently: "Figure 1.2"
% instead of "[somenode], p.3". We distinguish them by the
% LABEL-title being set to a magic string.
{%
% Have to otherify everything special to allow the \csname to
% include an _ in the xref name, etc.
\indexnofonts
\turnoffactive
\expandafter\global\expandafter\let\expandafter\Xthisreftitle
\csname XR#1-title\endcsname
}%
\iffloat\Xthisreftitle
% If the user specified the print name (third arg) to the ref,
% print it instead of our usual "Figure 1.2".
\ifdim\wd0 = 0pt
\refx{#1-snt}{}%
\else
\printedrefname
\fi
%
% if the user also gave the printed manual name (fifth arg), append
% "in MANUALNAME".
\ifdim \wd1 > 0pt
\space \putwordin{} \cite{\printedmanual}%
\fi
\else
% node/anchor (non-float) references.
%
% If we use \unhbox0 and \unhbox1 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 \wd1 > 0pt
\putwordsection{} ``\printedrefname'' \putwordin{} \cite{\printedmanual}%
\else
% _ (for example) has to be the character _ for the purposes of the
% control sequence corresponding to the node, but it has to expand
% into the usual \leavevmode...\vrule stuff for purposes of
% printing. So we \turnoffactive for the \refx-snt, back on for the
% printing, back off for the \refx-pg.
{\turnoffactive
% 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 a macro 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}{}%
\fi
\fi
\endlink
\endgroup}
% 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
}
% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME.
% If its value is nonempty, SUFFIX is output afterward.
%
\def\refx#1#2{%
{%
\indexnofonts
\otherbackslash
\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
\message{\linenumber Undefined cross reference `#1'.}%
\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. Usually it's
% just a \def (we prepend XR to the control sequence name to avoid
% collisions). But if this is a float type, we have more work to do.
%
\def\xrdef#1#2{%
{% The node name might contain 8-bit characters, which in our current
% implementation are changed to commands like @'e. Don't let these
% mess up the control sequence name.
\indexnofonts
\turnoffactive
\xdef\safexrefname{#1}%
}%
%
\expandafter\gdef\csname XR\safexrefname\endcsname{#2}% remember this xref
%
% 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
}
% 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
% It was suggested to set the catcode of ^ to 7, which would allow ^^e4 etc.
% in xref tags, i.e., node names. But since ^^e4 notation isn't
% supported in the main text, it doesn't seem desirable. Furthermore,
% that is not enough: for node names that actually contain a ^
% character, we would end up writing a line like this: 'xrdef {'hat
% b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first
% argument, and \hat is not an expandable control sequence. It could
% all be worked out, but why? Either we support ^^ or we don't.
%
% The other change necessary for this was to define \auxhat:
% \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter
% and then to call \auxhat in \setq.
%
\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
%
% This is to support \ in node names and titles, since the \
% characters end up in a \csname. It's easier than
% leaving it active and making its active definition an actual \
% character. What I don't understand is why it works in the *value*
% of the xrdef. Seems like it should be a catcode12 \, and that
% should not typeset properly. But it works, so I'm moving on for
% now. --karl, 15jan04.
\catcode`\\=\other
%
% Make the characters 128-255 be printing characters.
{%
\count1=128
\def\loop{%
\catcode\count1=\other
\advance\count1 by 1
\ifnum \count1<256 \loop \fi
}%
}%
%
% @ 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{%
\let\indent=\ptexindent
\let\noindent=\ptexnoindent
\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
% 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=\pagewidth
\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
\futurelet\next\fo@t
}
}%end \catcode `\@=11
% 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.
% Similarily, 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 ftp://tug.org/tex/epsf.tex.}
%
\def\image#1{%
\ifx\epsfbox\undefined
\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 this 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
% If the image is by itself, center it.
\ifvmode
\imagevmodetrue
\nobreak\bigskip
% 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
\line\bgroup
\fi
%
% Output the image.
\ifpdf
\dopdfimage{#1}{#2}{#3}%
\else
% \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}%
\fi
%
\ifimagevmode \egroup \bigbreak \fi % space after the image
\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 \thissection 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\thissection{\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.
{%
\atdummies
%
% since we read the caption text in the macro world, where ^^M
% is turned into a normal character, we have to scan it back, so
% we don't write the literal three characters "^^M" into the aux file.
\scanexp{%
\xdef\noexpand\gtemp{%
\ifx\thisshortcaption\empty
\thiscaption
\else
\thisshortcaption
\fi
}%
}%
\immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident
\ifx\gtemp\empty \else : \gtemp \fi}}%
}%
\fi
\egroup % end of \vtop
%
% place the captured inserts
%
% BEWARE: when the floats start floating, we have to issue warning
% whenever an insert appears inside a float which could possibly
% float. --kasal, 26may04
%
\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
% \thissection 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,}
% @documentlanguage is usually given very early, just after
% @setfilename. If done too late, it may not override everything
% properly. Single argument is the language abbreviation.
% It would be nice if we could set up a hyphenation file here.
%
\parseargdef\documentlanguage{%
\tex % read txi-??.tex file in plain TeX.
% Read the file if it exists.
\openin 1 txi-#1.tex
\ifeof 1
\errhelp = \nolanghelp
\errmessage{Cannot read language file txi-#1.tex}%
\else
\input txi-#1.tex
\fi
\closein 1
\endgroup
}
\newhelp\nolanghelp{The given language definition file cannot be found or
is empty. Maybe you need to install it? In the current directory
should work if nowhere else does.}
% 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
\advance\count255 by 1
\repeat
}
% @documentencoding sets the definition of non-ASCII characters
% according to the specified encoding.
%
\parseargdef\documentencoding{%
% 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
\setnonasciicharscatcode\active
\lattwochardefs
%
\else \ifx \declaredencoding \latone
\setnonasciicharscatcode\active
\latonechardefs
%
\else \ifx \declaredencoding \latnine
\setnonasciicharscatcode\active
\latninechardefs
%
\else \ifx \declaredencoding \utfeight
\setnonasciicharscatcode\active
\utfeightchardefs
%
\else
\message{Unknown document encoding #1, ignoring.}%
%
\fi % utfeight
\fi % latnine
\fi % latone
\fi % lattwo
\fi % ascii
}
% A message to be logged when using a character that isn't available
% the default font encoding (OT1).
%
\def\missingcharmsg#1{\message{Character missing in OT1 encoding: #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
%
% Latin1 (ISO-8859-1) character definitions.
\def\latonechardefs{%
\gdef^^a0{~}
\gdef^^a1{\exclamdown}
\gdef^^a2{\missingcharmsg{CENT SIGN}}
\gdef^^a3{{\pounds}}
\gdef^^a4{\missingcharmsg{CURRENCY SIGN}}
\gdef^^a5{\missingcharmsg{YEN SIGN}}
\gdef^^a6{\missingcharmsg{BROKEN BAR}}
\gdef^^a7{\S}
\gdef^^a8{\"{}}
\gdef^^a9{\copyright}
\gdef^^aa{\ordf}
\gdef^^ab{\missingcharmsg{LEFT-POINTING DOUBLE ANGLE QUOTATION MARK}}
\gdef^^ac{$\lnot$}
\gdef^^ad{\-}
\gdef^^ae{\registeredsymbol}
\gdef^^af{\={}}
%
\gdef^^b0{\textdegree}
\gdef^^b1{$\pm$}
\gdef^^b2{$^2$}
\gdef^^b3{$^3$}
\gdef^^b4{\'{}}
\gdef^^b5{$\mu$}
\gdef^^b6{\P}
%
\gdef^^b7{$^.$}
\gdef^^b8{\cedilla\ }
\gdef^^b9{$^1$}
\gdef^^ba{\ordm}
%
\gdef^^bb{\missingcharmsg{RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK}}
\gdef^^bc{$1\over4$}
\gdef^^bd{$1\over2$}
\gdef^^be{$3\over4$}
\gdef^^bf{\questiondown}
%
\gdef^^c0{\`A}
\gdef^^c1{\'A}
\gdef^^c2{\^A}
\gdef^^c3{\~A}
\gdef^^c4{\"A}
\gdef^^c5{\ringaccent A}
\gdef^^c6{\AE}
\gdef^^c7{\cedilla C}
\gdef^^c8{\`E}
\gdef^^c9{\'E}
\gdef^^ca{\^E}
\gdef^^cb{\"E}
\gdef^^cc{\`I}
\gdef^^cd{\'I}
\gdef^^ce{\^I}
\gdef^^cf{\"I}
%
\gdef^^d0{\missingcharmsg{LATIN CAPITAL LETTER ETH}}
\gdef^^d1{\~N}
\gdef^^d2{\`O}
\gdef^^d3{\'O}
\gdef^^d4{\^O}
\gdef^^d5{\~O}
\gdef^^d6{\"O}
\gdef^^d7{$\times$}
\gdef^^d8{\O}
\gdef^^d9{\`U}
\gdef^^da{\'U}
\gdef^^db{\^U}
\gdef^^dc{\"U}
\gdef^^dd{\'Y}
\gdef^^de{\missingcharmsg{LATIN CAPITAL LETTER THORN}}
\gdef^^df{\ss}
%
\gdef^^e0{\`a}
\gdef^^e1{\'a}
\gdef^^e2{\^a}
\gdef^^e3{\~a}
\gdef^^e4{\"a}
\gdef^^e5{\ringaccent a}
\gdef^^e6{\ae}
\gdef^^e7{\cedilla c}
\gdef^^e8{\`e}
\gdef^^e9{\'e}
\gdef^^ea{\^e}
\gdef^^eb{\"e}
\gdef^^ec{\`{\dotless i}}
\gdef^^ed{\'{\dotless i}}
\gdef^^ee{\^{\dotless i}}
\gdef^^ef{\"{\dotless i}}
%
\gdef^^f0{\missingcharmsg{LATIN SMALL LETTER ETH}}
\gdef^^f1{\~n}
\gdef^^f2{\`o}
\gdef^^f3{\'o}
\gdef^^f4{\^o}
\gdef^^f5{\~o}
\gdef^^f6{\"o}
\gdef^^f7{$\div$}
\gdef^^f8{\o}
\gdef^^f9{\`u}
\gdef^^fa{\'u}
\gdef^^fb{\^u}
\gdef^^fc{\"u}
\gdef^^fd{\'y}
\gdef^^fe{\missingcharmsg{LATIN SMALL LETTER THORN}}
\gdef^^ff{\"y}
}
% Latin9 (ISO-8859-15) encoding character definitions.
\def\latninechardefs{%
% Encoding is almost identical to Latin1.
\latonechardefs
%
\gdef^^a4{\euro}
\gdef^^a6{\v S}
\gdef^^a8{\v s}
\gdef^^b4{\v Z}
\gdef^^b8{\v z}
\gdef^^bc{\OE}
\gdef^^bd{\oe}
\gdef^^be{\"Y}
}
% Latin2 (ISO-8859-2) character definitions.
\def\lattwochardefs{%
\gdef^^a0{~}
\gdef^^a1{\missingcharmsg{LATIN CAPITAL LETTER A WITH OGONEK}}
\gdef^^a2{\u{}}
\gdef^^a3{\L}
\gdef^^a4{\missingcharmsg{CURRENCY SIGN}}
\gdef^^a5{\v L}
\gdef^^a6{\'S}
\gdef^^a7{\S}
\gdef^^a8{\"{}}
\gdef^^a9{\v S}
\gdef^^aa{\cedilla S}
\gdef^^ab{\v T}
\gdef^^ac{\'Z}
\gdef^^ad{\-}
\gdef^^ae{\v Z}
\gdef^^af{\dotaccent Z}
%
\gdef^^b0{\textdegree}
\gdef^^b1{\missingcharmsg{LATIN SMALL LETTER A WITH OGONEK}}
\gdef^^b2{\missingcharmsg{OGONEK}}
\gdef^^b3{\l}
\gdef^^b4{\'{}}
\gdef^^b5{\v l}
\gdef^^b6{\'s}
\gdef^^b7{\v{}}
\gdef^^b8{\cedilla\ }
\gdef^^b9{\v s}
\gdef^^ba{\cedilla s}
\gdef^^bb{\v t}
\gdef^^bc{\'z}
\gdef^^bd{\H{}}
\gdef^^be{\v z}
\gdef^^bf{\dotaccent z}
%
\gdef^^c0{\'R}
\gdef^^c1{\'A}
\gdef^^c2{\^A}
\gdef^^c3{\u A}
\gdef^^c4{\"A}
\gdef^^c5{\'L}
\gdef^^c6{\'C}
\gdef^^c7{\cedilla C}
\gdef^^c8{\v C}
\gdef^^c9{\'E}
\gdef^^ca{\missingcharmsg{LATIN CAPITAL LETTER E WITH OGONEK}}
\gdef^^cb{\"E}
\gdef^^cc{\v E}
\gdef^^cd{\'I}
\gdef^^ce{\^I}
\gdef^^cf{\v D}
%
\gdef^^d0{\missingcharmsg{LATIN CAPITAL LETTER D WITH STROKE}}
\gdef^^d1{\'N}
\gdef^^d2{\v N}
\gdef^^d3{\'O}
\gdef^^d4{\^O}
\gdef^^d5{\H O}
\gdef^^d6{\"O}
\gdef^^d7{$\times$}
\gdef^^d8{\v R}
\gdef^^d9{\ringaccent U}
\gdef^^da{\'U}
\gdef^^db{\H U}
\gdef^^dc{\"U}
\gdef^^dd{\'Y}
\gdef^^de{\cedilla T}
\gdef^^df{\ss}
%
\gdef^^e0{\'r}
\gdef^^e1{\'a}
\gdef^^e2{\^a}
\gdef^^e3{\u a}
\gdef^^e4{\"a}
\gdef^^e5{\'l}
\gdef^^e6{\'c}
\gdef^^e7{\cedilla c}
\gdef^^e8{\v c}
\gdef^^e9{\'e}
\gdef^^ea{\missingcharmsg{LATIN SMALL LETTER E WITH OGONEK}}
\gdef^^eb{\"e}
\gdef^^ec{\v e}
\gdef^^ed{\'\i}
\gdef^^ee{\^\i}
\gdef^^ef{\v d}
%
\gdef^^f0{\missingcharmsg{LATIN SMALL LETTER D WITH STROKE}}
\gdef^^f1{\'n}
\gdef^^f2{\v n}
\gdef^^f3{\'o}
\gdef^^f4{\^o}
\gdef^^f5{\H o}
\gdef^^f6{\"o}
\gdef^^f7{$\div$}
\gdef^^f8{\v r}
\gdef^^f9{\ringaccent u}
\gdef^^fa{\'u}
\gdef^^fb{\H u}
\gdef^^fc{\"u}
\gdef^^fd{\'y}
\gdef^^fe{\cedilla t}
\gdef^^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
}
\begingroup
\catcode`\~13
\catcode`\"12
\def\UTFviiiLoop{%
\global\catcode\countUTFx\active
\uccode`\~\countUTFx
\uppercase\expandafter{\UTFviiiTmp}%
\advance\countUTFx by 1
\ifnum\countUTFx < \countUTFy
\expandafter\UTFviiiLoop
\fi}
\countUTFx = "C2
\countUTFy = "E0
\def\UTFviiiTmp{%
\xdef~{\noexpand\UTFviiiTwoOctets\string~}}
\UTFviiiLoop
\countUTFx = "E0
\countUTFy = "F0
\def\UTFviiiTmp{%
\xdef~{\noexpand\UTFviiiThreeOctets\string~}}
\UTFviiiLoop
\countUTFx = "F0
\countUTFy = "F4
\def\UTFviiiTmp{%
\xdef~{\noexpand\UTFviiiFourOctets\string~}}
\UTFviiiLoop
\endgroup
\begingroup
\catcode`\"=12
\catcode`\<=12
\catcode`\.=12
\catcode`\,=12
\catcode`\;=12
\catcode`\!=12
\catcode`\~=13
\gdef\DeclareUnicodeCharacter#1#2{%
\countUTFz = "#1\relax
\wlog{\space\space defining Unicode char U+#1 (decimal \the\countUTFz)}%
\begingroup
\parseXMLCharref
\def\UTFviiiTwoOctets##1##2{%
\csname u8:##1\string ##2\endcsname}%
\def\UTFviiiThreeOctets##1##2##3{%
\csname u8:##1\string ##2\string ##3\endcsname}%
\def\UTFviiiFourOctets##1##2##3##4{%
\csname u8:##1\string ##2\string ##3\string ##4\endcsname}%
\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter
\gdef\UTFviiiTmp{#2}%
\endgroup}
\gdef\parseXMLCharref{%
\ifnum\countUTFz < "A0\relax
\errhelp = \EMsimple
\errmessage{Cannot define Unicode char value < 00A0}%
\else\ifnum\countUTFz < "800\relax
\parseUTFviiiA,%
\parseUTFviiiB C\UTFviiiTwoOctets.,%
\else\ifnum\countUTFz < "10000\relax
\parseUTFviiiA;%
\parseUTFviiiA,%
\parseUTFviiiB E\UTFviiiThreeOctets.{,;}%
\else
\parseUTFviiiA;%
\parseUTFviiiA,%
\parseUTFviiiA!%
\parseUTFviiiB F\UTFviiiFourOctets.{!,;}%
\fi\fi\fi
}
\gdef\parseUTFviiiA#1{%
\countUTFx = \countUTFz
\divide\countUTFz by 64
\countUTFy = \countUTFz
\multiply\countUTFz by 64
\advance\countUTFx by -\countUTFz
\advance\countUTFx by 128
\uccode `#1\countUTFx
\countUTFz = \countUTFy}
\gdef\parseUTFviiiB#1#2#3#4{%
\advance\countUTFz by "#10\relax
\uccode `#3\countUTFz
\uppercase{\gdef\UTFviiiTmp{#2#3#4}}}
\endgroup
\def\utfeightchardefs{%
\DeclareUnicodeCharacter{00A0}{\tie}
\DeclareUnicodeCharacter{00A1}{\exclamdown}
\DeclareUnicodeCharacter{00A3}{\pounds}
\DeclareUnicodeCharacter{00A8}{\"{ }}
\DeclareUnicodeCharacter{00A9}{\copyright}
\DeclareUnicodeCharacter{00AA}{\ordf}
\DeclareUnicodeCharacter{00AD}{\-}
\DeclareUnicodeCharacter{00AE}{\registeredsymbol}
\DeclareUnicodeCharacter{00AF}{\={ }}
\DeclareUnicodeCharacter{00B0}{\ringaccent{ }}
\DeclareUnicodeCharacter{00B4}{\'{ }}
\DeclareUnicodeCharacter{00B8}{\cedilla{ }}
\DeclareUnicodeCharacter{00BA}{\ordm}
\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{00D1}{\~N}
\DeclareUnicodeCharacter{00D2}{\`O}
\DeclareUnicodeCharacter{00D3}{\'O}
\DeclareUnicodeCharacter{00D4}{\^O}
\DeclareUnicodeCharacter{00D5}{\~O}
\DeclareUnicodeCharacter{00D6}{\"O}
\DeclareUnicodeCharacter{00D8}{\O}
\DeclareUnicodeCharacter{00D9}{\`U}
\DeclareUnicodeCharacter{00DA}{\'U}
\DeclareUnicodeCharacter{00DB}{\^U}
\DeclareUnicodeCharacter{00DC}{\"U}
\DeclareUnicodeCharacter{00DD}{\'Y}
\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{00F1}{\~n}
\DeclareUnicodeCharacter{00F2}{\`o}
\DeclareUnicodeCharacter{00F3}{\'o}
\DeclareUnicodeCharacter{00F4}{\^o}
\DeclareUnicodeCharacter{00F5}{\~o}
\DeclareUnicodeCharacter{00F6}{\"o}
\DeclareUnicodeCharacter{00F8}{\o}
\DeclareUnicodeCharacter{00F9}{\`u}
\DeclareUnicodeCharacter{00FA}{\'u}
\DeclareUnicodeCharacter{00FB}{\^u}
\DeclareUnicodeCharacter{00FC}{\"u}
\DeclareUnicodeCharacter{00FD}{\'y}
\DeclareUnicodeCharacter{00FF}{\"y}
\DeclareUnicodeCharacter{0100}{\=A}
\DeclareUnicodeCharacter{0101}{\=a}
\DeclareUnicodeCharacter{0102}{\u{A}}
\DeclareUnicodeCharacter{0103}{\u{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{0112}{\=E}
\DeclareUnicodeCharacter{0113}{\=e}
\DeclareUnicodeCharacter{0114}{\u{E}}
\DeclareUnicodeCharacter{0115}{\u{e}}
\DeclareUnicodeCharacter{0116}{\dotaccent{E}}
\DeclareUnicodeCharacter{0117}{\dotaccent{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{0124}{\^H}
\DeclareUnicodeCharacter{0125}{\^h}
\DeclareUnicodeCharacter{0128}{\~I}
\DeclareUnicodeCharacter{0129}{\~{\dotless{i}}}
\DeclareUnicodeCharacter{012A}{\=I}
\DeclareUnicodeCharacter{012B}{\={\dotless{i}}}
\DeclareUnicodeCharacter{012C}{\u{I}}
\DeclareUnicodeCharacter{012D}{\u{\dotless{i}}}
\DeclareUnicodeCharacter{0130}{\dotaccent{I}}
\DeclareUnicodeCharacter{0131}{\dotless{i}}
\DeclareUnicodeCharacter{0132}{IJ}
\DeclareUnicodeCharacter{0133}{ij}
\DeclareUnicodeCharacter{0134}{\^J}
\DeclareUnicodeCharacter{0135}{\^{\dotless{j}}}
\DeclareUnicodeCharacter{0139}{\'L}
\DeclareUnicodeCharacter{013A}{\'l}
\DeclareUnicodeCharacter{0141}{\L}
\DeclareUnicodeCharacter{0142}{\l}
\DeclareUnicodeCharacter{0143}{\'N}
\DeclareUnicodeCharacter{0144}{\'n}
\DeclareUnicodeCharacter{0147}{\v{N}}
\DeclareUnicodeCharacter{0148}{\v{n}}
\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{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{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{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{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{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}
\DeclareUnicodeCharacter{2013}{--}
\DeclareUnicodeCharacter{2014}{---}
\DeclareUnicodeCharacter{2022}{\bullet}
\DeclareUnicodeCharacter{2026}{\dots}
\DeclareUnicodeCharacter{20AC}{\euro}
\DeclareUnicodeCharacter{2192}{\expansion}
\DeclareUnicodeCharacter{21D2}{\result}
\DeclareUnicodeCharacter{2212}{\minus}
\DeclareUnicodeCharacter{2217}{\point}
\DeclareUnicodeCharacter{2261}{\equiv}
}% end of \utfeightchardefs
% US-ASCII character definitions.
\def\asciichardefs{% nothing need be done
\relax
}
% Make non-ASCII characters printable again for compatibility with
% existing Texinfo documents that may use them, even without declaring a
% document encoding.
%
\setnonasciicharscatcode \other
\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 so finicky about underfull hboxes, either.
\hbadness = 2000
% Following George Bush, just 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
\pageheight = \vsize
%
\hsize = #2\relax
\outerhsize = \hsize
\advance\outerhsize by 0.5in
\pagewidth = \hsize
%
\normaloffset = #4\relax
\bindingoffset = #5\relax
%
\ifpdf
\pdfpageheight #7\relax
\pdfpagewidth #8\relax
\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{46\baselineskip}{6in}%
{\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}%
{\voffset}{.25in}%
{\bindingoffset}{16pt}%
{9.25in}{7in}%
%
\lispnarrowing = 0.3in
\tolerance = 700
\hfuzz = 1pt
\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
\hfuzz = 1pt
\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{51\baselineskip}{160mm}
{\voffset}{\hoffset}%
{\bindingoffset}{44pt}%
{297mm}{210mm}%
%
\tolerance = 700
\hfuzz = 1pt
\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
\hfuzz = 1.2pt
\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
\advance\dimen0 by \voffset
%
\dimen2 = \hsize
\advance\dimen2 by \normaloffset
%
\internalpagesizes{#1}{\hsize}%
{\voffset}{\normaloffset}%
{\bindingoffset}{44pt}%
{\dimen0}{\dimen2}%
}}
% Set default to letter.
%
\letterpaper
\message{and turning on texinfo input format.}
% Define macros to output various characters with catcode for normal text.
\catcode`\"=\other
\catcode`\~=\other
\catcode`\^=\other
\catcode`\_=\other
\catcode`\|=\other
\catcode`\<=\other
\catcode`\>=\other
\catcode`\+=\other
\catcode`\$=\other
\def\normaldoublequote{"}
\def\normaltilde{~}
\def\normalcaret{^}
\def\normalunderscore{_}
\def\normalverticalbar{|}
\def\normalless{<}
\def\normalgreater{>}
\def\normalplus{+}
\def\normaldollar{$}%$ font-lock fix
% 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}
% Turn off all special characters except @
% (and those which the user can use as if they were ordinary).
% 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~{{\tt\char126}}
\chardef\hat=`\^
\catcode`\^=\active
\def^{{\tt \hat}}
\catcode`\_=\active
\def_{\ifusingtt\normalunderscore\_}
\let\realunder=_
% Subroutine for the previous macro.
\def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em }
\catcode`\|=\active
\def|{{\tt\char124}}
\chardef \less=`\<
\catcode`\<=\active
\def<{{\tt \less}}
\chardef \gtr=`\>
\catcode`\>=\active
\def>{{\tt \gtr}}
\catcode`\+=\active
\def+{{\tt \char 43}}
\catcode`\$=\active
\def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix
% 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 \everyjob (or @setfilename) turn them on.
% \otherifyactive is called near the end of this file.
\def\otherifyactive{\catcode`+=\other \catcode`\_=\other}
% 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=`\\
\global\let\rawbackslashxx=\backslashcurfont % let existing .??s files work
% \realbackslash is an actual character `\' with catcode other, and
% \doublebackslash is two of them (for the pdf outlines).
{\catcode`\\=\other @gdef@realbackslash{\} @gdef@doublebackslash{\\}}
% In texinfo, backslash is an active character; it prints the backslash
% in fixed width font.
\catcode`\\=\active
@def@normalbackslash{{@tt@backslashcurfont}}
% On startup, @fixbackslash assigns:
% @let \ = @normalbackslash
% \rawbackslash defines an active \ to do \backslashcurfont.
% \otherbackslash defines an active \ to be a literal `\' character with
% catcode other.
@gdef@rawbackslash{@let\=@backslashcurfont}
@gdef@otherbackslash{@let\=@realbackslash}
% Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of
% the literal character `\'.
%
@def@normalturnoffactive{%
@let\=@normalbackslash
@let"=@normaldoublequote
@let~=@normaltilde
@let^=@normalcaret
@let_=@normalunderscore
@let|=@normalverticalbar
@let<=@normalless
@let>=@normalgreater
@let+=@normalplus
@let$=@normaldollar %$ font-lock fix
@unsepspaces
}
% Make _ and + \other characters, temporarily.
% This is canceled by @fixbackslash.
@otherifyactive
% 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.
%
@gdef@eatinput input texinfo{@fixbackslash}
@global@let\ = @eatinput
% On the other hand, perhaps the file did not have a `\input texinfo'. Then
% the first `\' in the file would cause an error. This macro tries to fix
% that, assuming it is called before the first `\' could plausibly occur.
% Also turn back on active characters that might appear in the input
% file name, in case not using a pre-dumped format.
%
@gdef@fixbackslash{%
@ifx\@eatinput @let\ = @normalbackslash @fi
@catcode`+=@active
@catcode`@_=@active
}
% Say @foo, not \foo, in error messages.
@escapechar = `@@
% These look ok in all fonts, so just make them not special.
@catcode`@& = @other
@catcode`@# = @other
@catcode`@% = @other
@c Local variables:
@c eval: (add-hook 'write-file-hooks 'time-stamp)
@c page-delimiter: "^\\\\message"
@c time-stamp-start: "def\\\\texinfoversion{"
@c time-stamp-format: "%:y-%02m-%02d.%02H"
@c time-stamp-end: "}"
@c End:
@c vim:sw=2:
@ignore
arch-tag: e1b36e32-c96e-4135-a41a-0b2efa2ea115
@end ignore
ne-3.3.4/extensions 0000664 0000000 0000000 00000002364 14751164310 0014254 0 ustar 00root root 0000000 0000000 # Comments and blank lines are allowed, or any line that doesn't match:
# ^\s*(\w+)\s*([0-9]*)\s*(.+)
# ^\s*(\w+)\s+([0-9]+i?)\s+(.+[^ \t])\s*$
# Canonical format: [i]
# Space delimited; when matches the first lines, .jsf applies.
# "i" suffix on makes search case-insensitive.
# These patterns match some common command interpreters in the first line:
csh 1 ^#!\s*/.*(csh|tcsh)\b\s*
sh 1 ^#!\s*/.*sh\b\s*
pl 1 ^#!\s*/.*perl\b\s*
py 1 ^#!\s*/.*python[0-9]*\b\s*
rb 1 ^#!\s*/.*ruby\b\s*
xml 1 ^\s*<\?xml\b
# These must match in the first 30 and 20 lines respectively to work.
yaml 30 ^---$
ini 20 ^\[\s*\w+\s*\]$
# By default ne does not override a file's given extension. However,
# you can specify any number of extensions that you would like to
# allow to be overridden by including lines in your ~/.ne/extensions
# containing only a dot followed by a single extension or shell "glob
# pattern". (Basically if "ls *.pat" matches, then put ".pat" on a
# line by itself in your ~/.ne/.extensions file. Note that ".*" would
# allow overriding all extensions.
#
# Don't put such extensions in this global file, as then individual
# users cannot choose whether to allow overriding such files.
ne-3.3.4/macros/ 0000775 0000000 0000000 00000000000 14751164310 0013411 5 ustar 00root root 0000000 0000000 ne-3.3.4/macros/DeleteSOL 0000664 0000000 0000000 00000001624 14751164310 0015117 0 ustar 00root root 0000000 0000000 # This example macro implements a "Delete to Start-of-line"
# command which you can map to a key of your choice. If I
# chose to use the Alt-U key, for example, then first I use
# the KeyCode command to find the ne key number for that key
# combination. On my system, it's 1f5, which is currently
# mapped to the "U" command shortcut for "UNDO". To map it to
# this DeleteSOL macro instead, I would put these lines:
#
# # Let Alt-U map to the DeleteSOL macro
# KEY 1f5 DeleteSOL
#
# in my ~/.ne/.keys file. The first line is just a comment.
#
# Note that while the case of the "KEY" keyword and keycode
# itself don't matter (you could spell it "Key 1F5" or "key
# 1f5" or even "kEY 1F5"), and the case of built-in ne
# commands don't matter either, the case of macro names does
# matter, as these must match file names.
AtomicUndo +
PushPrefs
AutoIndent 0
InsertLine
LineUp
DeleteLine
PopPrefs
AtomicUndo -
ne-3.3.4/macros/aspell 0000664 0000000 0000000 00000001116 14751164310 0014613 0 ustar 00root root 0000000 0000000 # This example macro works with your system's aspell
# command to spell-check the selected block of text.
# The AtomicUndo commands at the beginning and end make
# it possible to undo/redo the entire operation in one
# go. Otherwise the Cut and Paste would each require
# their own undo/redo. Indentation is just for clarity;
# it doesn't affect operation.
AtomicUndo +
Cut
SaveClip ~/.ne/ne-aspell.txt
System aspell --check ~/.ne/ne-aspell.txt
OpenClip ~/.ne/ne-aspell.txt
System rm -f ~/.ne/ne-aspell.txt ~/.ne/ne-aspell.txt.bak
Paste
GotoBookmark >
AtomicUndo -
ne-3.3.4/makefile 0000664 0000000 0000000 00000011135 14751164310 0013626 0 ustar 00root root 0000000 0000000 # Makefile for ne's distribution archive.
.PHONY: install
VERSION=3.3.4
# If you change this prefix, you can invoke "make build install" and ne will
# be compiled and installed under the $(PREFIX) hierarchy. You can even use
# "make install PREFIX=$HOME/" to install ne locally into the directory
# .
#
# Note that the build target uses ne's default compilation options. More
# options can be passed to the build process by running directly make in
# the "src" directory.
PREFIX?=/usr/local
PROGRAM = ne
STRIP?=strip
ifeq ($(OS), Windows_NT)
OS := Windows
else
OS := $(shell uname -s)
endif
build: docs
( cd src; $(MAKE) clean; $(MAKE) NE_GLOBAL_DIR=$(PREFIX)/share/ne; $(STRIP) ne )
docs:
( cd doc; $(MAKE) )
alldocs: docs
( cd doc; $(MAKE) pdf )
version:
./version.pl VERSION=$(VERSION)
source: version alldocs
( cd src; $(MAKE) clean; $(MAKE) )
-rm -f ne-$(VERSION)
ln -s . ne-$(VERSION)
tar cvf ne-$(VERSION).tar ne-$(VERSION)/version.pl ne-$(VERSION)/makefile ne-$(VERSION)/COPYING ne-$(VERSION)/INSTALL.md ne-$(VERSION)/README.md ne-$(VERSION)/NEWS ne-$(VERSION)/CHANGES \
ne-$(VERSION)/src/*.[hc] ne-$(VERSION)/src/*.c.in ne-$(VERSION)/src/*.pl \
ne-$(VERSION)/extensions \
ne-$(VERSION)/macros/* \
ne-$(VERSION)/syntax/*.jsf \
ne-$(VERSION)/src/makefile ne-$(VERSION)/src/ne.texinfo ne-$(VERSION)/doc/ne.1 \
ne-$(VERSION)/doc/makefile ne-$(VERSION)/doc/ne.texinfo ne-$(VERSION)/doc/texinfo.cnf.in ne-$(VERSION)/doc/ne.info* ne-$(VERSION)/doc/version.* \
ne-$(VERSION)/doc/html/*.html \
ne-$(VERSION)/doc/ne.pdf ne-$(VERSION)/doc/ne.txt ne-$(VERSION)/doc/default*
-rm -f ne-*.tar.gz
gzip ne-$(VERSION).tar
-rm -f ne-$(VERSION)
install:
mkdir -p $(DESTDIR)$(PREFIX)/bin
mkdir -p $(DESTDIR)$(PREFIX)/share/ne/syntax
mkdir -p $(DESTDIR)$(PREFIX)/share/ne/macros
mkdir -p $(DESTDIR)$(PREFIX)/share/man/man1
mkdir -p $(DESTDIR)$(PREFIX)/share/doc/ne
mkdir -p $(DESTDIR)$(PREFIX)/share/info
cp -pf src/ne$(CMDSUFFIX) $(DESTDIR)$(PREFIX)/bin
cp -p extensions $(DESTDIR)$(PREFIX)/share/ne
cp -p syntax/*.jsf $(DESTDIR)$(PREFIX)/share/ne/syntax
cp -p macros/* $(DESTDIR)$(PREFIX)/share/ne/macros
cp -p doc/ne.1 $(DESTDIR)$(PREFIX)/share/man/man1
cp -pR doc/html doc/ne.txt doc/default.* README.md COPYING NEWS CHANGES $(DESTDIR)$(PREFIX)/share/doc/ne
if [ -f doc/ne.pdf ]; then cp -p doc/ne.pdf $(DESTDIR)$(PREFIX)/share/doc/ne ; fi
cp -p doc/ne.info.gz $(DESTDIR)$(PREFIX)/share/info
-install-info --dir-file=$(DESTDIR)$(PREFIX)/share/info/dir $(DESTDIR)$(PREFIX)/share/info/ne.info.gz
directory = $(DESTDIR)$(PREFIX)/share/ne
dir_target = $(directory)-$(wildcard $(directory))
dir_present = $(directory)-$(directory)
dir_absent = $(directory)-
uninstall: | $(dir_target)
$(dir_present):
-rm -fr $(DESTDIR)$(PREFIX)/share/ne
-rm -fr $(DESTDIR)$(PREFIX)/doc/ne
-rm -fr $(DESTDIR)$(PREFIX)/bin/ne
-rm -fr $(DESTDIR)$(PREFIX)/bin/ne.exe
-rm -f $(DESTDIR)$(PREFIX)/share/man/man1/ne.1
-install-info --delete --dir-file=$(DESTDIR)$(PREFIX)/share/info/dir $(DESTDIR)$(PREFIX)/share/info/ne.info.gz
-rm -f $(DESTDIR)$(PREFIX)/share/info/ne.info.gz
@echo "ne uninstalled."
$(dir_absent):
@echo "Cannot uninstall: folder $(directory) does not exist; please check the DESTDIR (\"$(DESTDIR)\") and PREFIX (\"$(PREFIX)\") make variables."
# Creates cygwin package on Windows
cygwin: version
ifneq ($(OS), Windows)
$(error This target can only be run under Windows)
endif
( cd src; $(MAKE) clean; $(MAKE) NE_GLOBAL_DIR=/usr/share/ne NE_TERMCAP=1 NE_ANSI=1 OPTS=-U__STRICT_ANSI__ )
$(MAKE) install PREFIX=/usr CMDSUFFIX=.exe
tar zcvf ne-cygwin-ansi-$(VERSION)-$(shell uname -m).tar.gz /usr/share/ne /usr/bin/ne.exe /usr/share/doc/ne /usr/share/info/ne.info.gz /usr/share/man/man1/ne.1
( cd src; $(MAKE) clean; $(MAKE) NE_GLOBAL_DIR=/usr/share/ne OPTS=-U__STRICT_ANSI__ )
$(MAKE) install PREFIX=/usr CMDSUFFIX=.exe
tar zcvf ne-cygwin-$(VERSION)-$(shell uname -m).tar.gz /usr/share/ne /usr/bin/ne.exe /usr/share/doc/ne /usr/share/info/ne.info.gz /usr/share/man/man1/ne.1
# Creates Mac OS X .dmg
macosx: version alldocs
ifneq ($(OS), Darwin)
$(error This target can only be run under Mac OS X)
endif
( cd src; $(MAKE) clean; $(MAKE) NE_GLOBAL_DIR=/usr/local/share/ne; strip ne )
-rm -fr /tmp/package-ne-$(VERSION)
$(MAKE) install DESTDIR=/tmp/package-ne-$(VERSION)
pkgbuild --root /tmp/package-ne-$(VERSION) --install-location "/" --version $(VERSION) --identifier ne-$(VERSION) ne-$(VERSION).pkg
-rm -f ne-$(VERSION).dmg
hdiutil create -fs HFS+ -srcfolder ne-$(VERSION).pkg -volname ne-$(VERSION) ne-$(VERSION).dmg
clean:
-rm -f ne-*.tar*
really-clean: clean
(cd src; $(MAKE) clean)
(cd doc; $(MAKE) clean)
ne-3.3.4/ne.spec 0000664 0000000 0000000 00000006256 14751164310 0013414 0 ustar 00root root 0000000 0000000 %define _hardened_build 1
Summary: ne, the nice editor
Name: ne
Version: 3.3.4
Release: 1%{?dist}
License: GPL-3.0-or-later
Source0: https://ne.di.unimi.it/ne-%{version}.tar.gz
URL: https://ne.di.unimi.it/
Requires: ncurses
BuildRequires: gcc
BuildRequires: ncurses-devel
BuildRequires: make
BuildRequires: bash
BuildRequires: perl
BuildRequires: texinfo
BuildRequires: sed
%description
ne is a free (GPL'd) text editor based on the POSIX standard that runs (we
hope) on almost every UN*X machine. ne is easy to use for the beginner, but
powerful and fully configurable for the wizard, and most sparing in its
resource usage.
%prep
%setup -q
%build
cd src
%make_build NE_GLOBAL_DIR=%{_datadir}/ne LIBS=-lncurses OPTS="%{optflags} -fno-strict-aliasing -Wno-parentheses"
%install
mkdir -p $RPM_BUILD_ROOT%{_bindir}
mkdir -p $RPM_BUILD_ROOT%{_datadir}/ne/syntax
mkdir -p $RPM_BUILD_ROOT%{_datadir}/ne/macros
mkdir -p $RPM_BUILD_ROOT%{_infodir}
mkdir -p $RPM_BUILD_ROOT%{_mandir}/man1
install -p -m 755 ./src/ne $RPM_BUILD_ROOT%{_bindir}/ne
install -p -m 644 ./extensions $RPM_BUILD_ROOT%{_datadir}/ne/extensions
install -p -m 644 ./syntax/*.jsf $RPM_BUILD_ROOT%{_datadir}/ne/syntax
install -p -m 644 ./macros/* $RPM_BUILD_ROOT%{_datadir}/ne/macros
install -p -m 644 ./doc/ne.1 $RPM_BUILD_ROOT%{_mandir}/man1
install -p -m 644 ./doc/ne.info* $RPM_BUILD_ROOT%{_infodir}
rm INSTALL.md
mv doc/html .
%files
%{_bindir}/ne
%{_datadir}/ne/
%{_mandir}/man1/ne.1*
%doc %{_infodir}/ne.info*
%doc ./README.md
%doc ./NEWS
%doc ./CHANGES
%package doc
Summary: Documentation for ne, the nice editor
BuildArch: noarch
%description doc
Documentation for ne, the nice editor.
%files doc
%license ./COPYING
%doc html
%doc ./doc/ne.texinfo
%doc ./doc/ne.pdf
%doc ./doc/ne.txt
%doc ./doc/default.*
%changelog
* Thu Feb 6 2025 Sebastiano Vigna - 3.3.4-1
- First release
* Fri Jan 17 2025 Fedora Release Engineering - 3.3.3-6
- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild
* Thu Jul 25 2024 Miroslav Suchý - 3.3.3-5
- convert license to SPDX
* Thu Jul 18 2024 Fedora Release Engineering - 3.3.3-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild
* Thu Jan 25 2024 Fedora Release Engineering - 3.3.3-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
* Sun Jan 21 2024 Fedora Release Engineering - 3.3.3-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
* Thu Oct 19 2023 Sebastiano Vigna - 3.3.3-1
- First release
* Thu Jul 20 2023 Fedora Release Engineering - 3.3.2-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild
* Thu Jan 19 2023 Fedora Release Engineering - 3.3.2-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild
* Tue Oct 25 2022 Sebastiano Vigna - 3.3.2-2
- Hardened build
* Tue Sep 13 2022 Sebastiano Vigna - 3.3.2-1
- First release
* Tue May 18 2021 Sebastiano Vigna - 3.3.1-1
- First release
ne-3.3.4/src/ 0000775 0000000 0000000 00000000000 14751164310 0012714 5 ustar 00root root 0000000 0000000 ne-3.3.4/src/.gitignore 0000664 0000000 0000000 00000000131 14751164310 0014677 0 ustar 00root root 0000000 0000000 enums.h
ext.c
hash.c
hash.h
help.c
help.h
names.c
names.h
ne
version.h
*.gcda
*.gcno
*.o
ne-3.3.4/src/TODO 0000664 0000000 0000000 00000001036 14751164310 0013404 0 ustar 00root root 0000000 0000000 0) Garbage collection. Under some extreme circumstances
in which the number of lost characters is
intolerably high, ne should allocate a new block of
memory and move contiguously the text into it.
1) Fast large copying. When one pastes a *very large*
clip, we should create a pool entry just for that
copy and copy contiguously in it, instead of
allocating line by line.
2) Check that update_partial_line() is always given the best start column.
3) Rewrite SHIFT to use direct buffer access rather than cursor movements.
ne-3.3.4/src/actions.c 0000664 0000000 0000000 00000172356 14751164310 0014536 0 ustar 00root root 0000000 0000000 /* Main command processing loop.
Copyright (C) 1993-1998 Sebastiano Vigna
Copyright (C) 1999-2025 Todd M. Lewis and Sebastiano Vigna
This file is part of ne, the nice editor.
This library 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 library 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 . */
#include "ne.h"
#include "support.h"
#include "version.h"
#include
/* ne's temporary file name template for the THROUGH command. */
#define NE_TMP ".ne-tmp.XXXXXX"
/* Turns an unspecified integer argument (-1) to 1. This
is what most commands require. */
#define NORMALIZE(x) { x = (x)<0 ? 1 : (x); }
/* The length of the static message buffer. It must be larger by a factor
of about three of the maximum screen width as UTF-8 encoded characters
might take several characters per screen position. */
#define MAX_MESSAGE_SIZE (1024)
/* Here, given a mask represent a user flag and an integer i, we do as follows:
i < 0 : toggle flag;
i = 0 : clear flag;
i > 0 : set flag;
*/
#define SET_USER_FLAG(b,i,x) {\
if ((i)<0) (b)->x = !(b)->x;\
else (b)->x = ((i) != 0);\
}
#define SET_GLOBAL_FLAG(c,f) {\
if ((c)<0) (f) = !(f);\
else (f) = ((c) != 0);\
}
/* Converts a non-positive result from request_number() to OK if the
function was aborted or not-a-number error if an invalid number was read. */
#define NUMERIC_ERROR(c) ((c) == ABORT ? OK : NOT_A_NUMBER)
/* Do we want RepealLast to wrap on the next invocation? Upon NOT_FOUND from
search/replace functions, set this to 2. do_action() reduces toward 0.*/
static int perform_wrap;
#ifdef LOGACTIONS
/* Wrap do_action() and various request* functions to facilitate action logging. */
int do_action_wrapped(buffer *b, action a, int64_t c, char *p);
static int da_depth = 0;
static FILE *da_log;
static buffer *da_prev_b;
bool request_response_wrapper(const buffer *b, const char *prompt, bool default_value) {
bool resp = request_response(b, prompt, default_value);
if (da_log) {
fprintf(da_log," %p request_response: %s\n", b, resp ? "true" : "false");
}
return resp;
}
#define request_response request_response_wrapper
char request_char_wrapper(const buffer *b, const char *prompt, const char default_value) {
char resp = request_char(b, prompt, default_value);
if (da_log) {
fprintf(da_log," %p request_char: '%c'\n", b, resp);
}
return resp;
}
#define request_char request_char_wrapper
int64_t request_number_wrapper(const buffer *b, const char *prompt, int64_t default_value) {
int64_t resp = request_number(b, prompt, default_value);
if (da_log) {
fprintf(da_log," %p request_number: %ld\n", b, resp);
}
return resp;
}
#define request_number request_number_wrapper
char *request_string_wrapper(const buffer *b, const char *prompt, const char *default_string, bool accept_null_string, int completion_type, bool prefer_utf8) {
char *resp = request_string(b, prompt, default_string, accept_null_string, completion_type, prefer_utf8);
if (da_log) {
fprintf(da_log," %p request_string: '%s'\n", b, resp ? resp : "");
}
return resp;
}
#define request_string request_string_wrapper
char *request_wrapper(const buffer *b, const char *prompt, const char *default_string, bool alpha_allowed, int completion_type, bool prefer_utf8) {
char * resp = request(b, prompt, default_string, alpha_allowed, completion_type, prefer_utf8);
if (da_log) {
fprintf(da_log," %p request: '%s'\n", b, resp ? resp : "");
}
return resp;
}
#define request request_wrapper
char *request_file_wrapper(const buffer *b, const char *prompt, const char *default_name) {
char * resp = request_file(b, prompt, default_name);
if (da_log) {
fprintf(da_log," %p request: '%s'\n", b, resp ? resp : "");
}
return resp;
}
#define request_file request_file_wrapper
int do_action(buffer *b, action a, int64_t c, char *p) {
if (!da_log) da_log = fopen("/tmp/ne-actions.log","a");
if (da_log) {
if (b != da_prev_b && b->filename) {
fprintf(da_log, "%p: %s\n", b, b->filename);
da_prev_b = b;
}
fprintf(da_log,"%p%2d %ld,%ld(%ld) %s %ld '%s'\n",
b,
da_depth++,
b->cur_line,
b->cur_pos,
b->cur_char,
command_names[a],
c, p ? p : "");
fflush(da_log);
}
int rc = do_action_wrapped(b, a, c, p);
da_depth--;
return rc;
}
/* End of the insertions to achieve logging of do_action(). */
#endif
/* This is the dispatcher of all actions that have some effect on the text.
The arguments are:
b: the buffer to act on,
a: an action to be executed,
c: a possible integer parameter, and
p: a possible string parameter.
-1 and NULL are, respectively, reserved values
meaning "no argument". For most operations, the integer argument is the
number of repetitions. When an on/off choice is required, nonzero means on,
zero means off, no argument means toggle.
If there is a string argument (i.e. p != NULL), it is assumed that the
action will consume p -- it ends up being free()d or stored
somewhere. Though efficient, this has lead to some memory leaks (can you
find them?). */
#ifdef LOGACTIONS
int do_action_wrapped(buffer *b, action a, int64_t c, char *p) {
#else
int do_action(buffer *b, action a, int64_t c, char *p) {
#endif
static char msg[MAX_MESSAGE_SIZE];
line_desc *next_ld;
HIGHLIGHT_STATE next_line_state;
int error = OK;
char_stream *recording;
int64_t col;
char *q;
assert(b->cur_pos >= 0);
assert_buffer(b);
assert_buffer_content(b);
assert(b->encoding != ENC_UTF8 || b->cur_pos >= b->cur_line_desc->line_len || utf8len(b->cur_line_desc->line[b->cur_pos]) > 0);
assert(b != cur_buffer || b->cur_x < ne_columns);
assert(b != cur_buffer || b->cur_y < ne_lines - 1);
#ifndef NDEBUG
if (b->syn && b->attr_len != -1) {
HIGHLIGHT_STATE next_state = parse(b->syn, b->cur_line_desc, b->cur_line_desc->highlight_state, b->encoding == ENC_UTF8);
assert(attr_len == b->attr_len);
assert(attr_len == 0 || memcmp(attr_buf, b->attr_buf, attr_len) == 0);
assert(memcmp(&next_state, &b->next_state, sizeof next_state) == 0);
}
#endif
stop = false;
if (recording_macro) record_action(recording_macro, a, c, p, verbose_macros);
if (perform_wrap > 0) perform_wrap--;
if (cur_buffer->visible_mark.shown) highlight_mark(cur_buffer, false);
switch(a) {
case EXIT_A:
if (save_all_modified_buffers()) {
print_error(CANT_SAVE_EXIT_SUSPENDED);
return ERROR;
}
else {
close_history();
unset_interactive_mode();
exit(0);
}
return OK;
case SAVEALL_A:
if (save_all_modified_buffers()) {
print_error(CANT_SAVE_ALL);
return ERROR;
}
else print_message(info_msg[MODIFIED_SAVED]);
return OK;
case PUSHPREFS_A:
NORMALIZE(c);
for (int64_t i = 0; i < c && !(error = push_prefs(b)) && !stop; i++);
return stop ? STOPPED : error ;
case POPPREFS_A:
NORMALIZE(c);
for (int64_t i = 0; i < c && !(error = pop_prefs(b)) && !stop; i++);
return stop ? STOPPED : error ;
case QUIT_A:
if (modified_buffers() && !request_response(b, info_msg[SOME_DOCUMENTS_ARE_NOT_SAVED], false)) return ERROR;
close_history();
unset_interactive_mode();
exit(0);
case LINEUP_A:
NORMALIZE(c);
for(int64_t i = 0; i < c && !(error = line_up(b)) && !stop; i++);
return stop ? STOPPED : error;
case LINEDOWN_A:
NORMALIZE(c);
for(int64_t i = 0; i < c && !(error = line_down(b)) && !stop; i++);
return stop ? STOPPED : error;
case PREVPAGE_A:
NORMALIZE(c);
for(int64_t i = 0; i < c && !(error = prev_page(b)) && !stop; i++);
return stop ? STOPPED : error;
case NEXTPAGE_A:
NORMALIZE(c);
for(int64_t i = 0; i < c && !(error = next_page(b)) && !stop; i++);
return stop ? STOPPED : error;
case MOVELEFT_A:
NORMALIZE(c);
for(int64_t i = 0; i < c && !(error = char_left(b)) && !stop; i++);
return stop ? STOPPED : error;
case MOVERIGHT_A:
NORMALIZE(c);
for(int64_t i = 0; i < c && !(error = char_right(b)) && !stop; i++);
return stop ? STOPPED : error;
case MOVESOL_A:
move_to_sol(b);
return OK;
case MOVEEOL_A:
move_to_eol(b);
return OK;
case MOVESOF_A:
move_to_sof(b);
return OK;
case MOVEEOF_A:
delay_update();
move_to_bof(b);
move_to_eol(b);
return OK;
case PAGEUP_A:
NORMALIZE(c);
for(int64_t i = 0; i < c && !(error = page_up(b)) && !stop; i++);
return stop ? STOPPED : error;
case PAGEDOWN_A:
NORMALIZE(c);
for(int64_t i = 0; i < c && !(error = page_down(b)) && !stop; i++);
return stop ? STOPPED : error;
case MOVETOS_A:
error = move_tos(b);
return error;
case MOVEBOS_A:
error = move_bos(b);
return error;
case ADJUSTVIEW_A:
NORMALIZE(c);
error = adjust_view(b, p);
if (p) free(p);
return error;
case TOGGLESEOF_A:
toggle_sof_eof(b);
return OK;
case TOGGLESEOL_A:
toggle_sol_eol(b);
return OK;
case NEXTWORD_A:
case PREVWORD_A: {
/* parse_word_parm(char *p, char *pat_in, int64_t *match) */
struct {
int64_t c;
int64_t left;
int64_t right;
} params = {-1, 0, 0};
error = parse_word_parm(p, "#<>", (int64_t *)¶ms) || (params.left && params.right);
if (!error) {
if (!(params.left || params.right)) params.left = 1;
NORMALIZE(params.c);
for(int64_t i = 0; i < params.c && !(error = search_word(b, a==NEXTWORD_A ? 1 : -1, params.left!=0)) && !stop; i++);
}
if (p) free(p);
return stop ? STOPPED : error;
}
case DELETENEXTWORD_A:
case DELETEPREVWORD_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
recording = recording_macro;
recording_macro = NULL;
NORMALIZE(c);
int marking_t = b->marking;
int mark_is_vertical_t = b->mark_is_vertical;
b->bookmark[WORDWRAP_BOOKMARK].pos = b->block_start_pos;
b->bookmark[WORDWRAP_BOOKMARK].line = b->block_start_line;
b->bookmark_mask |= (1 << WORDWRAP_BOOKMARK);
start_undo_chain(b);
b->marking = 1;
b->mark_is_vertical = 0;
b->block_start_line = b->cur_line;
b->block_start_pos = b->cur_pos;
if (a == DELETEPREVWORD_A) {
/* This ugly insertion and deletion of a single character ensures
that the cursor ends up here after an undo. */
insert_one_char(b, b->cur_line_desc, b->cur_line, b->cur_pos, ' ');
delete_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos, 1);
for(int64_t i = 0; i < c && !(error = search_word(b, -1, true)) && !stop; i++);
} else if (c > 0) {
move_to_eow(b);
if (b->block_start_line != b->cur_line || b->block_start_pos != b->cur_pos) c--;
for(int64_t i = 0; i < c && !(error = search_word(b, 1, true)) && !stop; i++);
if (c) move_to_eow(b);
}
const bool line_changed = b->block_start_line != b->cur_line;
if (line_changed || b->block_start_pos != b->cur_pos) { /* we moved */
b->attr_len = -1;
error |= erase_block(b, false);
end_undo_chain(b);
if (line_changed) {
if (b->syn) update_syntax_states_delay(b, b->cur_line_desc, NULL);
update_window_lines(b, b->cur_line_desc, b->cur_y, ne_lines - 2, false);
}
else {
update_line(b, b->cur_line_desc, b->cur_y, 0, false);
if (b->syn) need_attr_update = true;
}
} else {
end_undo_chain(b);
if (a == DELETEPREVWORD_A) error |= undo(b);
}
b->bookmark_mask &= ~(1 << WORDWRAP_BOOKMARK);
b->block_start_pos = b->bookmark[WORDWRAP_BOOKMARK].pos;
b->block_start_line = b->bookmark[WORDWRAP_BOOKMARK].line;
b->marking = marking_t;
b->mark_is_vertical = mark_is_vertical_t;
recording_macro = recording;
return stop ? STOPPED : error;
case MOVEEOW_A:
if (!p || (p && !strcmp(p,">"))) move_to_eow(b);
else if (p && !strcmp(p,"<")) move_to_sow(b);
else if (p) {
free(p);
return SYNTAX_ERROR;
}
if (p) free(p);
return OK;
case MOVEINCUP_A:
move_inc_up(b);
return OK;
case MOVEINCDOWN_A:
move_inc_down(b);
return OK;
case BRACKETEDPASTE_A:
if (!bracketed_paste) {
print_message("Bracketed Paste support is globally disabled.");
free(p);
return OK;
}
if (!p || (p && p[0] == '?')) {
free(p);
snprintf(msg, MAX_MESSAGE_SIZE, "BracketedPaste: 0|1|macro_before macro_after");
p = request_string(b, msg, cur_bracketed_paste_value(b), true, COMPLETE_NONE, b->encoding == ENC_UTF8 || b->encoding == ENC_ASCII && b->opt.utf8auto);
}
if (p) {
if ((*p == '0' || *p == '1') && *(p+1) == '\0') {
c = *p - '0';
if (b->bpaste_support == c) {
free(p);
return OK;
}
b->bpaste_support = c;
if (c == 0) turn_off_bracketed_paste();
if (c == 1) turn_on_bracketed_paste();
free(b->bpaste_macro_before);
free(b->bpaste_macro_after);
b->bpaste_macro_before = NULL;
b->bpaste_macro_after = NULL;
}
else {
char *q = p;
char *m1, *m2;
while (*q && *q == ' ') q++; /* skip leading spaces */
m1 = q; /* start of 1st macro name */
while (*q && *q != ' ') q++; /* keep non-spaces */
if (*q == ' ') {
*q = '\0'; /* mark end of 1st macro name */
q++;
}
while (*q && *q == ' ') q++; /* skip middle spaces */
m2 = q; /* start of 2nd macro name */
while (*q && *q != ' ') q++; /* keep non-spaces */
*q = '\0'; /* mark end of 2nd macro name */
if (strlen(m1) && strlen(m2)) {
free(b->bpaste_macro_before);
b->bpaste_macro_before = str_dup(m1);
free(b->bpaste_macro_after);
b->bpaste_macro_after = str_dup(m2);
b->bpaste_support = 2;
} else {
free(p);
return INVALID_BRACKETED_PASTE_DESIGNATION;
}
}
free(p);
}
return OK;
case UNSETBOOKMARK_A:
if (p && p[0] == '*' && !p[1]) { /* Special parm "*" for UNSETBOOKMARK_A */
b->bookmark_mask = b->cur_bookmark = 0;
print_message(info_msg[ALL_BOOKMARKS_CLEARED]);
free(p);
return OK;
} /* Intentionally fall through to regular BOOKMARK parm parsing. */
case SETBOOKMARK_A:
case GOTOBOOKMARK_A:
{
bool relative = false;
/* Value for *p c becomes
------------ --------------------------
"" 0
"?" promp
"-" AUTO_BOOKMARK
"0".."9" 0..9
"+1" prev
"-1" next
"<" PASTE_START_BOOKMARK
">" PASTE_END_BOOKMARK
Anything else is out of range. */
if (p) {
if (p[0] == '?') {
free(p);
snprintf(msg, MAX_MESSAGE_SIZE, "Cur Bookmarks: [%s] %s (0-9, -1, +1, '<', '>', or '-')", cur_bookmarks_string(b), a == SETBOOKMARK_A ? "SetBookmark" : "GotoBookmark");
p = request_string(b, msg, NULL, true, COMPLETE_NONE, b->encoding == ENC_UTF8 || b->encoding == ENC_ASCII && b->opt.utf8auto);
if (!p) {
return INVALID_BOOKMARK_DESIGNATION;
}
}
}
if (p) {
if ((p[0]=='+' || p[0]=='-') && p[1]=='1') {
if (b->cur_bookmark < 0 || b->cur_bookmark > MAX_USER_BOOKMARK) b->cur_bookmark = 0;
int i;
for(i = 0; i <= MAX_USER_BOOKMARK; i++) {
b->cur_bookmark = (b->cur_bookmark + MAX_USER_BOOKMARK + 1 + (p[0] == '+' ? 1 : -1)) % (MAX_USER_BOOKMARK + 1);
if ((a == SETBOOKMARK_A ? ~b->bookmark_mask : b->bookmark_mask) & (1 << b->cur_bookmark)) {
c = b->cur_bookmark;
relative = true;
break;
}
}
if (i==MAX_USER_BOOKMARK + 1) {
free(p);
switch (a) {
case SETBOOKMARK_A:
return NO_UNSET_BOOKMARKS_TO_SET;
case GOTOBOOKMARK_A:
return NO_SET_BOOKMARKS_TO_GOTO;
default:
return NO_SET_BOOKMARKS_TO_UNSET;
}
}
} else if (p[0]) {
if (!p[1]) {
if (*p == '-') c = AUTO_BOOKMARK;
else if (*p == '<') c = PASTE_START_BOOKMARK;
else if (*p == '>') c = PASTE_END_BOOKMARK;
else c = *p - '0';
}
else c = -1;
}
else c = 0;
free(p);
if (c < 0 || c >= NUM_BOOKMARKS) return INVALID_BOOKMARK_DESIGNATION;
}
else
c = 0;
switch(a) {
case SETBOOKMARK_A:
b->bookmark[c].pos = b->cur_pos;
b->bookmark[c].line = b->cur_line;
b->bookmark[c].cur_y = b->cur_y;
b->bookmark_mask |= (1 << c);
b->cur_bookmark = c;
snprintf(msg, MAX_MESSAGE_SIZE, "Bookmark %c set", "0123456789-<>w"[c]);
print_message(msg);
break;
case UNSETBOOKMARK_A:
if (! (b->bookmark_mask & (1 << c)))
return BOOKMARK_NOT_SET;
b->bookmark_mask &= ~(1 << c);
snprintf(msg, MAX_MESSAGE_SIZE, "Bookmark %c unset", "0123456789-<>w"[c]);
print_message(msg);
break;
case GOTOBOOKMARK_A:
if (! (b->bookmark_mask & (1 << c))) return BOOKMARK_NOT_SET;
else {
const int64_t prev_line = b->cur_line;
const int64_t prev_pos = b->cur_pos;
const int cur_y = b->cur_y;
b->cur_bookmark = c;
int avshift;
delay_update();
goto_line_pos(b, b->bookmark[c].line, b->bookmark[c].pos);
if (avshift = b->cur_y - b->bookmark[c].cur_y) {
snprintf(msg, MAX_MESSAGE_SIZE, "%c%d", avshift > 0 ? 'T' :'B', avshift > 0 ? avshift : -avshift);
adjust_view(b, msg);
}
b->bookmark[AUTO_BOOKMARK].line = prev_line;
b->bookmark[AUTO_BOOKMARK].pos = prev_pos;
b->bookmark[AUTO_BOOKMARK].cur_y = cur_y;
b->bookmark_mask |= 1<w"[c]);
print_message(msg);
}
}
default: ; /* Can't happen */
}
return OK;
}
case GOTOLINE_A:
if (c < 0 && (c = request_number(b, "Line", b->cur_line + 1)) < 0) return NUMERIC_ERROR(c);
if (c == 0 || c > b->num_lines) c = b->num_lines;
goto_line(b, --c);
return OK;
case GOTOCOLUMN_A:
if (c < 0 && (c = request_number(b, "Column", b->cur_x + b->win_x + 1)) < 0) return NUMERIC_ERROR(c);
goto_column(b, c ? --c : 0);
return OK;
case INSERTSTRING_A:
/* Since we are going to call another action, we do not want to record this insertion twice. */
recording = recording_macro;
recording_macro = NULL;
error = ERROR;
if (p || (p = request_string(b, "String", NULL, false, COMPLETE_NONE, b->encoding == ENC_UTF8 || b->encoding == ENC_ASCII && b->opt.utf8auto))) {
encoding_type encoding = detect_encoding(p, strlen(p));
error = OK;
start_undo_chain(b);
/* We cannot rely on encoding promotion done by INSERTCHAR_A, because it could work
just for part of the string if UTF-8 auto-detection is not enabled. */
if (b->encoding == ENC_ASCII || encoding == ENC_ASCII || (b->encoding == encoding)) {
if (b->encoding == ENC_ASCII) b->encoding = encoding;
for(int64_t pos = 0; p[pos] && error == OK; pos = next_pos(p, pos, encoding)) error = do_action(b, INSERTCHAR_A, get_char(&p[pos], encoding), NULL);
}
else error = INVALID_STRING;
end_undo_chain(b);
free(p);
}
recording_macro = recording;
return error;
case TABS_A:
SET_USER_FLAG(b, c, opt.tabs);
return OK;
case DELTABS_A:
SET_USER_FLAG(b, c, opt.del_tabs);
return OK;
case SHIFTTABS_A:
SET_USER_FLAG(b, c, opt.shift_tabs);
return OK;
case AUTOMATCHBRACKET_A:
if (c < 0 && (c = request_number(b, "Match mode (sum of 0:none, 1:brightness, 2:inverse, 4:bold, 8:underline)", b->opt.automatch)) < 0 || c > 15) return ((c) == ABORT ? OK : INVALID_MATCH_MODE);
b->opt.automatch = c;
return OK;
case INSERTTAB_A:
recording = recording_macro;
recording_macro = NULL;
NORMALIZE(c);
start_undo_chain(b);
if (b->opt.tabs) {
while (c-- > 0) {
error = do_action(b, INSERTCHAR_A, '\t', NULL);
}
}
else {
while (c-- > 0) {
do {
error = do_action(b, INSERTCHAR_A, ' ', NULL);
} while (b->opt.tab_size && (b->win_x + b->cur_x) % b->opt.tab_size);
}
}
end_undo_chain(b);
recording_macro = recording;
return error;
case INSERTCHAR_A: {
static int last_inserted_char = ' ';
int deleted_char, old_char;
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
if ((c < 0 || c > MAX_UTF_8) && ((c = request_number(b, "Char Code", last_inserted_char)) < 0 || c > MAX_UTF_8)) return NUMERIC_ERROR(c);
if (c == 0) return CANT_INSERT_0;
if (b->encoding == ENC_ASCII) {
if (c > 0xFF) b->encoding = ENC_UTF8;
else if (c > 0x7F) b->encoding = b->opt.utf8auto ? ENC_UTF8 : ENC_8_BIT;
}
if (c > 0xFF && b->encoding == ENC_8_BIT) return INVALID_CHARACTER;
last_inserted_char = c;
old_char = b->cur_pos < b->cur_line_desc->line_len ? get_char(&b->cur_line_desc->line[b->cur_pos], b->encoding) : 0;
ensure_attributes(b);
start_undo_chain(b);
if (deleted_char = !b->opt.insert && b->cur_pos < b->cur_line_desc->line_len) delete_one_char(b, b->cur_line_desc, b->cur_line, b->cur_pos);
if (b->cur_pos > b->cur_line_desc->line_len) {
/* We insert spaces to reach the insertion position. */
insert_spaces(b, b->cur_line_desc, b->cur_line, b->cur_line_desc->line_len, b->cur_pos - b->cur_line_desc->line_len);
if (b->syn) update_line(b, b->cur_line_desc, b->cur_y, 0, true);
}
insert_one_char(b, b->cur_line_desc, b->cur_line, b->cur_pos, c);
need_attr_update = true;
/* At this point the line has been modified: note that if we are in overwrite mode and write a character
at or beyond the length of the current line, we are actually doing an insertion. */
if (!deleted_char) update_inserted_char(b, c, b->cur_line_desc, b->cur_pos, b->cur_char, b->cur_y, b->cur_x);
else update_overwritten_char(b, old_char, c, b->cur_line_desc, b->cur_pos, b->cur_char, b->cur_y, b->cur_x);
char_right(b);
/* Note the use of ne_columns-1. This avoids a double horizontal scrolling each time a
word wrap happens with b->opt.right_margin = 0. */
error = b->opt.word_wrap && b->win_x + b->cur_x >= (b->opt.right_margin ? b->opt.right_margin : ne_columns - 1) ? word_wrap(b) : ERROR;
if (error == ERROR) {
assert_buffer_content(b);
/* No word wrap. */
if (b->syn) update_line(b, b->cur_line_desc, b->cur_y, 0, true);
assert_buffer_content(b);
}
else {
/* Fixes in case of word wrapping. */
const bool wont_scroll = b->win_x == 0;
int64_t a = 0;
update_line(b, b->cur_line_desc, b->cur_y, calc_width(b->cur_line_desc, b->cur_line_desc->line_len, b->opt.tab_size, b->encoding) - b->win_x, false);
need_attr_update = false;
/* Poke the correct state into the next line. */
if (b->syn) ((line_desc *)b->cur_line_desc->ld_node.next)->highlight_state = b->next_state;
if (b->opt.auto_indent) a = auto_indent_line(b, b->cur_line + 1, (line_desc *)b->cur_line_desc->ld_node.next, INT_MAX);
move_to_sol(b);
line_down(b);
goto_pos(b, error + a);
if (wont_scroll) {
if (b->cur_line == b->num_lines - 1) update_line(b, b->cur_line_desc, b->cur_y, 0, false);
else scroll_window(b, b->cur_line_desc, b->cur_y, 1);
}
need_attr_update = true;
assert_buffer_content(b);
}
end_undo_chain(b);
return OK;
}
case BACKSPACE_A:
case DELETECHAR_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
NORMALIZE(c);
start_undo_chain(b);
for(int64_t i = 0; i < c && !stop; i++) {
if (a == BACKSPACE_A) {
if (b->cur_pos == 0) {
if (b->cur_line == 0) {
/* Start of buffer. We just return an error. */
end_undo_chain(b);
return ERROR;
}
/* We turn a backspace at the start of a line into a delete
at the end of the previous line. */
char_left(b);
}
else {
if (b->opt.del_tabs && (b->win_x + b->cur_x) % b->opt.tab_size == 0
&& (b->cur_pos > b->cur_line_desc->line_len || b->cur_line_desc->line[b->cur_pos - 1] == ' ')) {
/* We are deleting one or more spaces from a tabbing position. We go left until the
previous tabbing, or when spaces end. */
int64_t back = 1;
while((b->win_x + b->cur_x - back) % b->opt.tab_size != 0
&& (b->cur_pos - back > b->cur_line_desc->line_len || b->cur_line_desc->line[b->cur_pos - back - 1] == ' ')) back++;
goto_pos(b, b->cur_pos - back);
}
else char_left(b);
/* If we are not over text, we are in free form mode; the backspace
is turned into moving to the left. */
if (b->cur_pos >= b->cur_line_desc->line_len) continue;
}
}
/* From here, we just implement a delete. */
if (b->opt.del_tabs && b->cur_pos < b->cur_line_desc->line_len && b->cur_line_desc->line[b->cur_pos] == ' ' &&
((b->win_x + b->cur_x) % b->opt.tab_size == 0 || b->cur_line_desc->line[b->cur_pos - 1] != ' ')) {
col = 0;
do col++; while((b->win_x + b->cur_x + col) % b->opt.tab_size != 0
&& b->cur_pos + col < b->cur_line_desc->line_len
&& b->cur_line_desc->line[b->cur_pos + col] == ' ');
/* We are positioned at the start of the block of col spaces. If there is at most
one character to delete, we can just go on. Otherwise, we replace the block with a
TAB, doing some magick to keep everything in sync. */
if (col > 1 && (b->win_x + b->cur_x + col) % b->opt.tab_size == 0) {
if (b->syn) {
ensure_attributes(b);
memmove(b->attr_buf + b->cur_char + 1, b->attr_buf + b->cur_char + col, b->attr_len - (b->cur_char + col));
b->attr_buf[b->cur_char] = -1;
b->attr_len -= (col - 1);
}
delete_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos, col);
insert_one_char(b, b->cur_line_desc, b->cur_line, b->cur_pos, '\t');
}
}
if (b->cur_pos > b->cur_line_desc->line_len) {
col = b->win_x + b->cur_x;
/* We are not over text; we must be in FreeForm mode.
We're deleting past the end of the line, so if we aren't on the last line
we need to pad this line with space up to col, then fall through to the
delete_one_char() below. */
if (b->cur_line_desc->ld_node.next->next == NULL) continue;
if (b->cur_line_desc->line_len == 0) {
auto_indent_line(b, b->cur_line, b->cur_line_desc, col);
resync_pos(b);
}
/* We need spaces if the line was not empty, or if we were sitting in the middle of a TAB. */
insert_spaces(b, b->cur_line_desc, b->cur_line, b->cur_line_desc->line_len, col - calc_width(b->cur_line_desc, b->cur_line_desc->line_len, b->opt.tab_size, b->encoding));
if (b->syn) store_attributes(b, b->cur_line_desc);
}
ensure_attributes(b);
if (b->cur_pos < b->cur_line_desc->line_len) {
/* Deletion inside a line. */
const int old_char = b->encoding == ENC_UTF8 ? utf8char(&b->cur_line_desc->line[b->cur_pos]) : b->cur_line_desc->line[b->cur_pos];
const uint32_t old_attr = b->syn ? b->attr_buf[b->cur_char] : 0;
if (b->syn) {
/* Invalidate attrs beyond the right window edge. */
const int64_t right_char = calc_char_len(b->cur_line_desc, calc_pos(b->cur_line_desc, b->win_x + ne_columns, b->opt.tab_size, b->encoding), b->encoding);
if (right_char < b->attr_len) b->attr_len = right_char;
}
delete_one_char(b, b->cur_line_desc, b->cur_line, b->cur_pos);
update_deleted_char(b, old_char, old_attr, b->cur_line_desc, b->cur_pos, b->cur_char, b->cur_y, b->cur_x);
if (b->syn) update_line(b, b->cur_line_desc, b->cur_y, 0, true);
}
else {
/* Here we handle the case in which two lines are joined. Note that if the first line is empty,
it is just deleted by delete_one_char(), so we must store its initial state and restore
it after the deletion. */
if (b->syn && b->cur_pos == 0) next_line_state = b->cur_line_desc->highlight_state;
delete_one_char(b, b->cur_line_desc, b->cur_line, b->cur_pos);
if (b->syn && b->cur_pos == 0) b->cur_line_desc->highlight_state = next_line_state;
update_line(b, b->cur_line_desc, b->cur_y, b->cur_x, true);
if (b->cur_y < ne_lines - 2) scroll_window(b, (line_desc *)b->cur_line_desc->ld_node.next, b->cur_y + 1, -1);
}
}
need_attr_update = true;
end_undo_chain(b);
return error ? error : stop ? STOPPED : 0;
case INSERTLINE_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
NORMALIZE(c);
/* There SHOULD be a start_undo_chain(b) here, but in practice we've become used to using
a single Undo after an InsertLine to remove the autoindent space. Therefore, this sillyness.
If we had a real DeleteSOL command, we wouldn't need the Undo to undo InsertLine in two steps.
There could also be extant macros out there that expect Undo to revert autoindent and
InsertLine in two steps. So, again, this sillyness with excessive start_ and end_undo_chain. */
for(int64_t i = 0; i < c && !stop; i++) {
start_undo_chain(b);
if (b->win_x == 0) ensure_attributes(b);
if (insert_one_line(b, b->cur_line_desc, b->cur_line, b->cur_pos > b->cur_line_desc->line_len ? b->cur_line_desc->line_len : b->cur_pos) == OK) {
end_undo_chain(b);
if (b->win_x) {
int64_t a = -1;
/* If b->win_x is nonzero, the move_to_sol() call will
refresh the entire video, so we shouldn't do anything. However, we
must poke into the next line initial state the correct state. */
if (b->syn) {
b->attr_len = -1;
ensure_attributes(b);
((line_desc *)b->cur_line_desc->ld_node.next)->highlight_state = b->next_state;
}
if (b->opt.auto_indent) {
start_undo_chain(b);
a = auto_indent_line(b, b->cur_line + 1, (line_desc *)b->cur_line_desc->ld_node.next, INT_MAX);
end_undo_chain(b);
}
move_to_sol(b);
line_down(b);
if (a != -1) goto_pos(b, a);
}
else {
int64_t a = -1;
update_line(b, b->cur_line_desc, b->cur_y, b->cur_x, false);
/* We need to avoid updates until we fix the next line. */
need_attr_update = false;
/* We poke into the next line initial state the correct state. */
if (b->syn) {
b->attr_len = -1;
ensure_attributes(b);
((line_desc *)b->cur_line_desc->ld_node.next)->highlight_state = b->next_state;
}
if (b->opt.auto_indent) {
start_undo_chain(b);
a = auto_indent_line(b, b->cur_line + 1, (line_desc *)b->cur_line_desc->ld_node.next, INT_MAX);
end_undo_chain(b);
}
move_to_sol(b);
line_down(b);
if (a != -1) goto_pos(b, a);
if (b->cur_line == b->num_lines - 1) update_line(b, b->cur_line_desc, b->cur_y, 0, false);
else scroll_window(b, b->cur_line_desc, b->cur_y, 1);
}
need_attr_update = true;
} else end_undo_chain(b);
}
return stop ? STOPPED : 0;
case DELETELINE_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
NORMALIZE(c);
col = b->win_x + b->cur_x;
b->cur_pos = -1;
start_undo_chain(b);
for(int64_t i = 0; i < c && !stop; i++) {
if (error = delete_one_line(b, b->cur_line_desc, b->cur_line)) break;
scroll_window(b, b->cur_line_desc, b->cur_y, -1);
}
end_undo_chain(b);
if (b->syn) {
b->attr_len = -1;
update_line(b, b->cur_line_desc, b->cur_y, 0, false);
need_attr_update = true;
}
goto_column(b, col);
return stop ? STOPPED : error;
case UNDELLINE_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
NORMALIZE(c);
next_ld = (line_desc *)b->cur_line_desc->ld_node.next;
start_undo_chain(b);
for(int64_t i = 0; i < c && !stop; i++) {
/* This is a bit tricky. First of all, if we are undeleting for
the first time and the local attribute buffer is not valid
we fill it. */
if (i == 0) ensure_attributes(b);
if (error = undelete_line(b)) break;
if (i == 0) {
if (b->syn) {
/* Now the only valid part of the local attribute buffer is before b->cur_char. */
if (b->cur_char < b->attr_len) b->attr_len = b->cur_char;
update_line(b, b->cur_line_desc, b->cur_y, b->cur_x, false);
next_line_state = b->next_state;
}
else update_line(b, b->cur_line_desc, b->cur_y, b->cur_x, false);
}
/* For each undeletion, we must poke into the next line its correct initial state. */
if (b->syn) ((line_desc *)b->cur_line_desc->ld_node.next)->highlight_state = next_line_state;
/* We actually scroll down the remaining lines, if necessary. */
if (b->cur_y < ne_lines - 2) scroll_window(b, (line_desc *)b->cur_line_desc->ld_node.next, b->cur_y + 1, 1);
}
if (b->syn) {
/* Finally, we force the update of the initial states of all following lines up to next_ld. */
need_attr_update = true;
update_syntax_states(b, b->cur_y, b->cur_line_desc, next_ld);
}
end_undo_chain(b);
return stop ? STOPPED : error;
case DELETEEOL_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
ensure_attributes(b);
delete_to_eol(b, b->cur_line_desc, b->cur_line, b->cur_pos);
update_line(b, b->cur_line_desc, b->cur_y, b->cur_x, false);
need_attr_update = true;
return OK;
case SAVE_A:
p = str_dup(b->filename);
case SAVEAS_A:
if (b->opt.read_only && !request_response(b, info_msg[SAVE_READ_ONLY_DOCUMENT],false)) {
free(p);
return DOCUMENT_NOT_SAVED;
}
if (p || (q = p = request_file(b, "Filename", b->filename))) {
print_info(SAVING);
if (buffer_file_modified(b, p) && !request_response(b, info_msg[a == SAVE_A ? FILE_HAS_BEEN_MODIFIED : FILE_ALREADY_EXISTS], false)) {
free(p);
return DOCUMENT_NOT_SAVED;
}
c = b->opt.read_only;
SET_USER_FLAG(b, 0, opt.read_only);
error = save_buffer_to_file(b, p);
SET_USER_FLAG(b, c, opt.read_only);
if (!print_error(error)) {
const bool load_syntax = b->filename == NULL || ! same_str(extension(p), extension(b->filename));
change_filename(b, p);
if (load_syntax) {
b->syn = NULL; /* So that autoprefs will load the right syntax. */
load_auto_prefs(b, NULL); /* Will get extension from the name, or virtual extension. */
reset_syntax_states(b);
reset_window();
}
print_info(SAVED);
}
else {
free(p);
return ERROR;
}
}
b->undo.last_save_step = b->undo.cur_step;
return OK;
case KEYCODE_A:
if (c >= NUM_KEYS) c = -1;
if (c < 0) {
print_message(info_msg[PRESS_A_KEY]);
do c = get_key_code(); while(c == INVALID_CHAR || c > 0xFF || CHAR_CLASS(c) == IGNORE);
}
col = (c < 0) ? -c-1 : c;
snprintf(msg, MAX_MESSAGE_SIZE, "Key Code: 0x%02x, Input Class: %s, Assigned Command: %s", (int)col, input_class_names[CHAR_CLASS(c)],
(key_binding[col] && key_binding[col][0]) ? key_binding[col] : "(none)" );
print_message(msg);
return OK;
case CLEAR_A:
if ((b->is_modified) && !request_response(b, info_msg[THIS_DOCUMENT_NOT_SAVED], false)) return ERROR;
clear_buffer(b);
reset_window();
return OK;
case OPEN_A:
if ((b->is_modified) && !request_response(b, info_msg[THIS_DOCUMENT_NOT_SAVED], false)) return ERROR;
case OPENNEW_A:
if (p || (p = request_file(b, "Filename", b->filename))) {
static bool dprompt = false; /* Set to true if we ever respond 'yes' to the prompt. */
bool empty = is_buffer_empty(b);
if (b = new_buffer()) reset_window();
else {
if (p) free(p);
return OUT_OF_MEMORY;
}
buffer *dup = get_buffer_named(p);
/* 'c' -- flag meaning "Don't prompt if we've ever responded 'yes'." */
if (!dup || dup == b || (dprompt && !c) || (dprompt = request_response(b, info_msg[SAME_NAME], false))) {
error = load_file_in_buffer(b, p);
if (error == FILE_DOES_NOT_EXIST && a == OPEN_A && (empty || request_response(b, info_msg[NO_SUCH_FILE_EXISTS], false)))
error = OK;
if (! error || (a == OPENNEW_A && error == FILE_DOES_NOT_EXIST)) { /* Keep the new buffer, or delete it? */
change_filename(b, p);
p = NULL;
b->syn = NULL; /* So that autoprefs will load the right syntax. */
if (b->opt.auto_prefs) {
if (b->allocated_chars - b->free_chars <= MAX_SYNTAX_SIZE) {
if (load_auto_prefs(b, NULL) == HAS_NO_EXTENSION) load_auto_prefs(b, DEF_PREFS_NAME);
reset_syntax_states(b);
}
else if (error == OK) error = FILE_TOO_LARGE_SYNTAX_HIGHLIGHTING_DISABLED;
}
buffer * old_buffer = (buffer *)cur_buffer->b_node.prev;
/* preserve cur_macro, find_string, and replace_string */
free_char_stream(cur_buffer->cur_macro);
cur_buffer->cur_macro = dup_stream(old_buffer->cur_macro);
cur_buffer->find_string = str_dup(old_buffer->find_string);
cur_buffer->replace_string = str_dup(old_buffer->replace_string);
cur_buffer->last_was_replace = old_buffer->last_was_replace;
cur_buffer->last_was_regexp = old_buffer->last_was_regexp;
if (a == OPEN_A) {
do_action(cur_buffer, PREVDOC_A, 1, NULL);
delete_buffer();
}
} else {
delete_buffer();
do_action(cur_buffer, PREVDOC_A, 1, NULL);
}
} else {
delete_buffer();
do_action(cur_buffer, PREVDOC_A, 1, NULL);
}
print_error(error);
reset_window();
if (p) free(p);
return error;
}
return ERROR;
case ABOUT_A:
about();
return OK;
case REFRESH_A:
clear_entire_screen();
ttysize();
keep_cursor_on_screen(cur_buffer);
reset_window();
if (executing_macro) {
refresh_window(b);
draw_status_bar();
}
cur_buffer->act = ++buffer_actuations;
return OK;
case FIND_A:
case FINDREGEXP_A:
if (p || (p = request_string(b, a == FIND_A ? "Find" : "Find RegExp", b->find_string, false, COMPLETE_NONE, b->encoding == ENC_UTF8 || b->encoding == ENC_ASCII && b->opt.utf8auto))) {
const encoding_type encoding = detect_encoding(p, strlen(p));
if (encoding != ENC_ASCII && b->encoding != ENC_ASCII && encoding != b->encoding) {
free(p);
return INCOMPATIBLE_SEARCH_STRING_ENCODING;
}
free(b->find_string);
b->find_string = p;
b->find_string_changed = 1;
print_error(error = (a == FIND_A ? find : find_regexp)(b, NULL, false, false));
if (error == NOT_FOUND) perform_wrap = 2;
b->last_was_replace = 0;
b->last_was_regexp = (a == FINDREGEXP_A);
}
return error ? ERROR : 0;
case REPLACE_A:
case REPLACEONCE_A:
case REPLACEALL_A:
if (b->opt.read_only) {
free(p);
return DOCUMENT_IS_READ_ONLY;
}
if ((q = b->find_string) || (q = request_string(b, b->last_was_regexp ? "Find RegExp" : "Find", NULL, false, COMPLETE_NONE, b->encoding == ENC_UTF8 || b->encoding == ENC_ASCII && b->opt.utf8auto))) {
const encoding_type search_encoding = detect_encoding(q, strlen(q));
if (search_encoding != ENC_ASCII && b->encoding != ENC_ASCII && search_encoding != b->encoding) {
free(p);
free(q);
return INCOMPATIBLE_SEARCH_STRING_ENCODING;
}
if (q != b->find_string) {
free(b->find_string);
b->find_string = q;
b->find_string_changed = 1;
}
if (p || (p = request_string(b, b->last_was_regexp ? "Replace RegExp" : "Replace", b->replace_string, true, COMPLETE_NONE, b->encoding == ENC_UTF8 || b->encoding == ENC_ASCII && b->opt.utf8auto))) {
const encoding_type replace_encoding = detect_encoding(p, strlen(p));
bool first_search = true;
int64_t num_replace = 0;
if (replace_encoding != ENC_ASCII && b->encoding != ENC_ASCII && replace_encoding != b->encoding ||
search_encoding != ENC_ASCII && replace_encoding != ENC_ASCII && search_encoding != replace_encoding) {
free(p);
return INCOMPATIBLE_REPLACE_STRING_ENCODING;
}
c = 0;
b->last_was_replace = 1;
free(b->replace_string);
b->replace_string = p;
if (a == REPLACEALL_A) start_undo_chain(b);
while(!stop &&
!(error = (b->last_was_regexp ? find_regexp : find)(b, NULL, !first_search && a != REPLACEALL_A && c != 'A' && c != 'Y', false))) {
if (c != 'A' && a != REPLACEALL_A && a != REPLACEONCE_A) {
refresh_window(b);
c = request_char(b, b->opt.search_back ? "Replace (Yes/No/Last/All/Quit/Forward)" : "Replace (Yes/No/Last/All/Quit/Backward)", 'n');
if (c == 'Q') break;
if (c == 'A') start_undo_chain(b);
}
if (c == 'A' || c == 'Y' || c == 'L' || a == REPLACEONCE_A || a == REPLACEALL_A) {
/* We delay buffer encoding promotion until it is really necessary. */
if (b->encoding == ENC_ASCII) b->encoding = replace_encoding;
const int64_t cur_char = b->cur_char;
const int cur_x = b->cur_x;
if (b->last_was_regexp) error = replace_regexp(b, p);
else error = replace(b, strlen(b->find_string), p);
if (!error) {
if (cur_char < b->attr_len) b->attr_len = cur_char;
update_line(b, b->cur_line_desc, b->cur_y, cur_x, false);
if (b->syn) {
need_attr_update = true;
update_syntax_states(b, b->cur_y, b->cur_line_desc, NULL);
}
num_replace++;
if (b->opt.search_back) error = char_left(b) ? NOT_FOUND : OK;
else if (last_replace_empty_match) error = char_right(cur_buffer) ? NOT_FOUND : OK;
if (error) break;
}
if (print_error(error)) {
if (a == REPLACEALL_A || c == 'A') end_undo_chain(b);
return ERROR;
}
}
if (c == 'B' && !(b->opt.search_back) || c == 'F' && (b->opt.search_back)) {
b->opt.search_back = !b->opt.search_back;
b->find_string_changed = 1;
}
if (a == REPLACEONCE_A || c == 'L') break;
first_search = false;
}
if (a == REPLACEALL_A || c == 'A') end_undo_chain(b);
if (num_replace) {
snprintf(msg, MAX_MESSAGE_SIZE, "%" PRId64 " replacement%s made.%s", num_replace, num_replace > 1 ? "s" : "", error == NOT_FOUND ? strchr(error_msg[NOT_FOUND], '(')-1 :"");
print_message(msg);
}
if (stop) error = STOPPED;
if (error == STOPPED) reset_window();
if (error == NOT_FOUND) perform_wrap = 2;
if (error && ((c != 'A' && a != REPLACEALL_A || first_search) || error != NOT_FOUND)) {
print_error(error);
return ERROR;
}
return OK;
}
}
return ERROR;
case REPEATLAST_A:
if (p) {
char *q;
c = strtoll(p, &q, 0);
if (p == q) c = -1;
while (*q == ' ') q++;
if (*q) {
if ( !strcasecmp("f",q) || !strcasecmp("find",q)) b->last_was_replace = false;
else if (!strcasecmp("r",q) || !strcasecmp("replace",q)) b->last_was_replace = true;
else {
free(p);
return SYNTAX_ERROR;
}
}
free(p);
}
if (b->opt.read_only && b->last_was_replace) return DOCUMENT_IS_READ_ONLY;
if (!b->find_string) return NO_SEARCH_STRING;
if ((b->last_was_replace) && !b->replace_string) return NO_REPLACE_STRING;
const encoding_type search_encoding = detect_encoding(b->find_string, strlen(b->find_string));
if (search_encoding != ENC_ASCII && b->encoding != ENC_ASCII && search_encoding != b->encoding) return INCOMPATIBLE_SEARCH_STRING_ENCODING;
if (b->last_was_replace) {
const encoding_type replace_encoding = detect_encoding(b->replace_string, strlen(b->replace_string));
if (replace_encoding != ENC_ASCII && b->encoding != ENC_ASCII && replace_encoding != b->encoding ||
search_encoding != ENC_ASCII && replace_encoding != ENC_ASCII && search_encoding != replace_encoding)
return INCOMPATIBLE_REPLACE_STRING_ENCODING;
}
NORMALIZE(c);
error = OK;
int64_t num_replace = 0;
start_undo_chain(b);
for (int64_t i = 0; i < c && ! stop && ! (error = (b->last_was_regexp ? find_regexp : find)(b, NULL, !b->last_was_replace||((b->cur_pos + b->cur_line == 0)&&b->opt.search_back), perform_wrap > 0)); i++)
if (b->last_was_replace) {
const int64_t cur_char = b->cur_char;
const int cur_x = b->cur_x;
if (b->last_was_regexp) error = replace_regexp(b, b->replace_string);
else error = replace(b, strlen(b->find_string), b->replace_string);
if (! error) {
if (cur_char < b->attr_len) b->attr_len = cur_char;
update_line(b, b->cur_line_desc, b->cur_y, cur_x, false);
if (b->syn) {
need_attr_update = true;
update_syntax_states(b, b->cur_y, b->cur_line_desc, NULL);
}
num_replace++;
if (b->opt.search_back) error = char_left(b) ? NOT_FOUND : OK;
else if (last_replace_empty_match) error = char_right(cur_buffer) ? NOT_FOUND : OK;
}
if (error) break;
}
end_undo_chain(b);
if (num_replace) {
snprintf(msg, MAX_MESSAGE_SIZE, "%" PRId64 " replacement%s made.%s", num_replace, num_replace > 1 ? "s" : "", error == NOT_FOUND ? strchr(error_msg[NOT_FOUND], '(')-1 :"");
print_message(msg);
}
if (stop) error = STOPPED;
if (error == STOPPED) reset_window();
if (error == NOT_FOUND) perform_wrap = 2;
return num_replace && error ? ERROR : error;
case MATCHBRACKET_A:
return print_error(match_bracket(b)) ? ERROR : 0;
case ALERT_A:
alert();
return OK;
case BEEP_A:
ring_bell();
return OK;
case FLASH_A:
do_flash();
return OK;
case ESCAPETIME_A:
if (c < 0 && (c = request_number(b, "Timeout (1/10s)", -1))<0) return NUMERIC_ERROR(c);
if (c < 256) {
set_escape_time(c);
return OK;
}
else return ESCAPE_TIME_OUT_OF_RANGE;
case TABSIZE_A:
if (c < 0 && (c = request_number(b, "TAB Size", b->opt.tab_size))<=0) return NUMERIC_ERROR(c);
if (c < ne_columns / 2) {
const int64_t pos = b->cur_pos;
move_to_sol(b);
b->opt.tab_size = c;
goto_pos(b, pos);
reset_window();
return OK;
}
return TAB_SIZE_OUT_OF_RANGE;
case TURBO_A:
if ((int)c < 0 && (int)(c = request_number(b, "Turbo Threshold", turbo)) < 0) return NUMERIC_ERROR(c);
turbo = c;
return OK;
case CLIPNUMBER_A:
if ((int)c < 0 && (int)(c = request_number(b, "Clip Number", b->opt.cur_clip)) < 0) return NUMERIC_ERROR(c);
b->opt.cur_clip = c;
return OK;
case RIGHTMARGIN_A:
if ((int)c < 0 && (int)(c = request_number(b, "Right Margin", b->opt.right_margin)) < 0) return NUMERIC_ERROR(c);
b->opt.right_margin = c;
return OK;
case FREEFORM_A:
SET_USER_FLAG(b, c, opt.free_form);
return OK;
case PRESERVECR_A:
SET_USER_FLAG(b, c, opt.preserve_cr);
return OK;
case CRLF_A:
SET_USER_FLAG(b, c, is_CRLF);
return OK;
case VISUALBELL_A:
SET_USER_FLAG(b, c, opt.visual_bell);
return OK;
case STATUSBAR_A:
SET_GLOBAL_FLAG(c, status_bar);
reset_status_bar();
return OK;
case HEXCODE_A:
SET_USER_FLAG(b, c, opt.hex_code);
reset_status_bar();
return OK;
case FASTGUI_A:
SET_GLOBAL_FLAG(c, fast_gui);
reset_status_bar();
return OK;
case INSERT_A:
SET_USER_FLAG(b, c, opt.insert);
return OK;
case WORDWRAP_A:
SET_USER_FLAG(b, c, opt.word_wrap);
return OK;
case AUTOINDENT_A:
SET_USER_FLAG(b, c, opt.auto_indent);
return OK;
case VERBOSEMACROS_A:
SET_GLOBAL_FLAG(c, verbose_macros);
return OK;
case AUTOPREFS_A:
SET_USER_FLAG(b, c, opt.auto_prefs);
return OK;
case BINARY_A:
SET_USER_FLAG(b, c, opt.binary);
return OK;
case NOFILEREQ_A:
SET_USER_FLAG(b, c, opt.no_file_req);
return OK;
case REQUESTORDER_A:
SET_GLOBAL_FLAG(c, req_order);
return OK;
case UTF8AUTO_A:
SET_USER_FLAG(b, c, opt.utf8auto);
return OK;
case UTF8_A: {
const encoding_type old_encoding = b->encoding, encoding = detect_buffer_encoding(b);
if (c < 0 && b->encoding != ENC_UTF8 || c > 0) {
if (encoding == ENC_ASCII || encoding == ENC_UTF8) b->encoding = ENC_UTF8;
else return BUFFER_IS_NOT_UTF8;
}
else b->encoding = encoding == ENC_ASCII ? ENC_ASCII : ENC_8_BIT;
if (old_encoding != b->encoding) {
reset_syntax_states(b);
reset_undo_buffer(&b->undo);
}
b->attr_len = -1;
need_attr_update = false;
move_to_sol(b);
reset_window();
return OK;
}
case MODIFIED_A:
SET_USER_FLAG(b, c, is_modified);
return OK;
case UTF8IO_A:
if (c < 0) io_utf8 = ! io_utf8;
else io_utf8 = c != 0;
reset_window();
return OK;
case DOUNDO_A:
SET_USER_FLAG(b, c, opt.do_undo);
if (!(b->opt.do_undo)) {
reset_undo_buffer(&b->undo);
b->atomic_undo = 0;
}
return OK;
case READONLY_A:
SET_USER_FLAG(b, c, opt.read_only);
return OK;
case CASESEARCH_A:
SET_USER_FLAG(b, c, opt.case_search);
b->find_string_changed = 1;
return OK;
case SEARCHBACK_A:
SET_USER_FLAG(b, c, opt.search_back);
b->find_string_changed = 1;
return OK;
case ATOMICUNDO_A:
if (b->opt.do_undo) {
/* set c to the desired b->link_undos */
if (!p) {
c = b->link_undos ? b->link_undos - 1 : 1;
} else if (p[0]=='0') {
c = 0;
} else if (p[0]=='-') {
c = b->link_undos ? b->link_undos - 1 : 0;
} else if (p[0]=='+' || p[0]=='1') { /* Kindly allow undocumented "AtomicUndo 1" also. */
c = b->link_undos + 1;
} else {
free(p);
return INVALID_LEVEL;
}
while(c > b->link_undos) start_undo_chain(b);
while(c < b->link_undos) end_undo_chain(b);
b->atomic_undo = (c > 0) ? 1 : 0;
snprintf(msg, MAX_MESSAGE_SIZE, "AtomicUndo level: %" PRId64, c);
print_message(msg);
if (p) free(p);
return OK;
} else {
if (p) free(p);
return UNDO_NOT_ENABLED;
}
case RECORD_A:
if (recording_macro) {
if (c < 0) { /* normal completion */
free_char_stream(b->cur_macro);
b->cur_macro = recording_macro;
recording_macro = NULL;
print_message(info_msg[MACRO_RECORDING_COMPLETED]);
} else if (c == 0) { /* cancel recording */
free_char_stream(recording_macro);
recording_macro = NULL;
print_message(info_msg[MACRO_RECORDING_CANCELLED]);
} else {
print_message(info_msg[INVALID_ARGUMENT_WHILE_RECORDING]);
return ERROR;
}
} else if (c == 1) { /* resume recording */
if (recording_macro = dup_stream(b->cur_macro))
print_message(info_msg[MACRO_RECORD_APPENDING_STARTED]);
} else if (c < 0) { /* start recording */
recording_macro = alloc_char_stream(0);
print_message(info_msg[STARTING_MACRO_RECORDING]);
} else {
print_message(info_msg[INVALID_ARGUMENT_WHILE_RECORDING]);
return ERROR;
}
return OK;
case PLAY_A:
if (c < 0 && (c = request_number(b, "Times", 1))<=0) return NUMERIC_ERROR(c);
if (recording_macro) add_to_stream(recording_macro, "# include macro ", 26);
error = play_macro(b->cur_macro, c);
if (recording_macro) add_to_stream(recording_macro, "# conclude macro ", 27);
return print_error(error) ? ERROR : 0;
case SAVEMACRO_A:
if (p || (p = request_file(b, "Macro Name", NULL))) {
print_info(SAVING);
optimize_macro(b->cur_macro, verbose_macros);
if ((error = print_error(save_stream(b->cur_macro, p, b->is_CRLF, false))) == OK) print_info(SAVED);
free(p);
return error ? ERROR : 0;
}
return ERROR;
case OPENMACRO_A:
if (p || (p = request_file(b, "Macro Name", NULL))) {
char_stream *cs;
cs = load_stream(b->cur_macro, p, false, false);
if (cs) b->cur_macro = cs;
free(p);
return cs ? 0 : ERROR;
}
return ERROR;
case MACRO_A:
if (p || (p = request_file(b, "Macro Name", NULL))) {
error = print_error(execute_macro(b, p));
free(p);
return error ? ERROR : 0;
}
return ERROR;
case UNLOADMACROS_A:
unload_macros();
return OK;
case NEWDOC_A:
new_buffer();
reset_window();
return OK;
case CLOSEDOC_A:
if ((b->is_modified) && !request_response(b, info_msg[THIS_DOCUMENT_NOT_SAVED], false)) return ERROR;
if (!delete_buffer()) {
close_history();
unset_interactive_mode();
exit(0);
}
cur_buffer->act = ++buffer_actuations;
keep_cursor_on_screen(cur_buffer);
reset_window();
/* We used to return ERROR after a buffer has been deleted to prevent
the calling routines (and macros) attempting to work on an nonexistent buffer,
but that should no longer be an issue. */
return OK;
case NEXTDOC_A: /* Was NEXT_BUFFER: */
if (b->b_node.next->next) cur_buffer = (buffer *)b->b_node.next;
else cur_buffer = (buffer *)buffers.head;
cur_buffer->act = ++buffer_actuations;
keep_cursor_on_screen(cur_buffer);
reset_window();
need_attr_update = false;
b->attr_len = -1;
return OK;
case PREVDOC_A:
if (b->b_node.prev->prev) cur_buffer = (buffer *)b->b_node.prev;
else cur_buffer = (buffer *)buffers.tail_pred;
cur_buffer->act = ++buffer_actuations;
keep_cursor_on_screen(cur_buffer);
reset_window();
need_attr_update = false;
b->attr_len = -1;
return OK;
case SELECTDOC_A: ;
const int n = request_document();
if (n < 0 || !(b = get_nth_buffer(n))) return ERROR;
cur_buffer = b;
cur_buffer->act = ++buffer_actuations;
keep_cursor_on_screen(cur_buffer);
reset_window();
need_attr_update = false;
b->attr_len = -1;
return OK;
case MARK_A:
case MARKVERT_A:
if (c < 0) c = 1;
SET_USER_FLAG(b, c, marking);
if (!b->marking) return(OK);
print_message(info_msg[a == MARK_A ? BLOCK_START_MARKED : VERTICAL_BLOCK_START_MARKED]);
b->mark_is_vertical = (a == MARKVERT_A);
b->block_start_line = b->cur_line;
b->block_start_pos = b->cur_pos;
return OK;
case CUT_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
case COPY_A:
if (!(error = print_error((b->mark_is_vertical ? copy_vert_to_clip : copy_to_clip)(b, (int)c < 0 ? b->opt.cur_clip : c, a == CUT_A)))) {
b->marking = 0;
update_window_lines(b, b->cur_line_desc, b->cur_y, ne_lines - 2, false);
}
return error ? ERROR : 0;
case ERASE_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
if (!(error = print_error((b->mark_is_vertical ? erase_vert_block : erase_block)(b, true)))) {
b->marking = 0;
update_window_lines(b, b->cur_line_desc, b->cur_y, ne_lines - 2, false);
}
return OK;
case PASTE_A:
case PASTEVERT_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
if (!(error = print_error((a == PASTE_A ? paste_to_buffer : paste_vert_to_buffer)(b, (int)c < 0 ? b->opt.cur_clip : c)))) update_window_lines(b, b->cur_line_desc, b->cur_y, ne_lines - 2, false);
assert_buffer_content(b);
return error ? ERROR : 0;
case GOTOMARK_A:
if (b->marking) {
delay_update();
goto_line_pos(b, b->block_start_line, b->block_start_pos);
return OK;
}
print_error(MARK_BLOCK_FIRST);
return ERROR;
case OPENCLIP_A:
if (p || (p = request_file(b, "Clip Name", NULL))) {
error = print_error(load_clip(b->opt.cur_clip, p, b->opt.preserve_cr, b->opt.binary));
free(p);
return error ? ERROR : 0;
}
return ERROR;
case SAVECLIP_A:
if (p || (p = request_file(b, "Clip Name", NULL))) {
print_info(SAVING);
if ((error = print_error(save_clip(b->opt.cur_clip, p, b->is_CRLF, b->opt.binary))) == OK) print_info(SAVED);
free(p);
return error ? ERROR : 0;
}
return ERROR;
case EXEC_A:
if (p || (p = request_string(b, "Command", b->command_line, false, COMPLETE_FILE, b->encoding == ENC_UTF8 || b->encoding == ENC_ASCII && b->opt.utf8auto))) {
free(b->command_line);
b->command_line = p;
return print_error(execute_command_line(b, p)) ? ERROR : 0;
}
return ERROR;
case NAMECONVERT_A:
q = NULL;
if (b->filename && (p = ne_getcwd(CUR_DIR_MAX_SIZE))) {
if (b->filename[0] == '/' && (c < 1))
q = relative_file_path(b->filename, p);
else if (b->filename[0] != '/' && (c != 0))
q = absolute_file_path(b->filename, p);
if (q) {
change_filename(b, q);
reset_status_bar();
}
free(p);
}
return OK;
case SYSTEM_A:
if (p || (p = request_string(b, "Shell command", NULL, false, COMPLETE_FILE, b->encoding == ENC_UTF8 || b->encoding == ENC_ASCII && b->opt.utf8auto))) {
unset_interactive_mode();
if (system(p)) error = EXTERNAL_COMMAND_ERROR;
set_interactive_mode();
free(p);
ttysize();
keep_cursor_on_screen(cur_buffer);
reset_window();
return print_error(error) ? ERROR : OK;
}
return ERROR;
case THROUGH_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
if (!b->marking) b->mark_is_vertical = 0;
if (p || (p = request_string(b, "Filter", NULL, false, COMPLETE_FILE, b->encoding == ENC_UTF8 || b->encoding == ENC_ASCII && b->opt.utf8auto))) {
int fin = -1, fout = -1;
char tmpnam1[strlen(P_tmpdir)+strlen(NE_TMP)+2], tmpnam2[strlen(P_tmpdir)+strlen(NE_TMP)+2], *command;
strcat(strcat(strcpy(tmpnam1, P_tmpdir), "/"), NE_TMP);
strcat(strcat(strcpy(tmpnam2, P_tmpdir), "/"), NE_TMP);
if ((fin = mkstemp(tmpnam1)) != -1) close(fin);
if ((fout = mkstemp(tmpnam2)) != -1) close(fout);
if (fin != -1 && fout != -1) {
realloc_clip_desc(get_nth_clip(INT_MAX), INT_MAX, 0);
if (!b->marking || !(error = (b->mark_is_vertical ? copy_vert_to_clip : copy_to_clip)(b, INT_MAX, false))) {
if (!(error = save_clip(INT_MAX, tmpnam1, b->is_CRLF, b->opt.binary))) {
if (command = malloc(strlen(p) + strlen(tmpnam1) + strlen(tmpnam2) + 16)) {
strcat(strcat(strcat(strcat(strcat(strcpy(command, "( "), p), " ) <"), tmpnam1), " >"), tmpnam2);
unset_interactive_mode();
if (system(command)) error = EXTERNAL_COMMAND_ERROR;
set_interactive_mode();
if (!error) {
if (!(error = load_clip(INT_MAX, tmpnam2, b->opt.preserve_cr, b->opt.binary))) {
start_undo_chain(b);
if (b->marking) (b->mark_is_vertical ? erase_vert_block : erase_block)(b, true);
error = (b->mark_is_vertical ? paste_vert_to_buffer : paste_to_buffer)(b, INT_MAX);
end_undo_chain(b);
b->marking = 0;
realloc_clip_desc(get_nth_clip(INT_MAX), INT_MAX, 0);
}
}
free(command);
}
else error = OUT_OF_MEMORY;
}
}
}
else error = CANT_OPEN_TEMPORARY_FILE;
remove(tmpnam1);
remove(tmpnam2);
ttysize();
keep_cursor_on_screen(cur_buffer);
reset_window();
free(p);
return print_error(error) ? ERROR : OK;
}
return ERROR;
case TOUPPER_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
NORMALIZE(c);
start_undo_chain(b);
for(int64_t i = 0; i < c && !(error = to_upper(b)) && !stop; i++);
end_undo_chain(b);
if (stop) error = STOPPED;
return print_error(error) ? ERROR : 0;
case TOLOWER_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
NORMALIZE(c);
start_undo_chain(b);
for(int64_t i = 0; i < c && !(error = to_lower(b)) && !stop; i++);
end_undo_chain(b);
if (stop) error = STOPPED;
return print_error(error) ? ERROR : 0;
case CAPITALIZE_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
NORMALIZE(c);
start_undo_chain(b);
for(int64_t i = 0; i < c && !(error = capitalize(b)) && !stop; i++);
end_undo_chain(b);
if (stop) error = STOPPED;
return print_error(error) ? ERROR : 0;
case CENTER_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
NORMALIZE(c);
start_undo_chain(b);
for(int64_t i = 0; i < c && !(error = center(b)) && !stop; i++) {
need_attr_update = true;
b->attr_len = -1;
update_line(b, b->cur_line_desc, b->cur_y, 0, false);
move_to_sol(b);
if (line_down(b) != OK) break;
}
end_undo_chain(b);
if (stop) error = STOPPED;
return print_error(error) ? ERROR : 0;
case PARAGRAPH_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
NORMALIZE(c);
start_undo_chain(b);
for(int64_t i = 0; i < c && !(error = paragraph(b, i == 0)) && !stop; i++);
end_undo_chain(b);
if (stop) error = STOPPED;
if (error == STOPPED) reset_window();
assert(b->cur_pos >= 0);
return print_error(error) ? ERROR : 0;
case SHIFT_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
error = shift(b, p, &msg[0], MAX_MESSAGE_SIZE);
if (stop) error = STOPPED;
if (p) free(p);
return print_error(error) ? ERROR : 0;
case LOADPREFS_A:
if (p || (p = request_file(b, "Prefs Name", NULL))) {
error = print_error(load_prefs(b, p));
free(p);
return error ? ERROR : OK;
}
return ERROR;
case SAVEPREFS_A:
if (p || (p = request_file(b, "Prefs Name", NULL))) {
error = print_error(save_prefs(b, p));
free(p);
return error ? ERROR : OK;
}
return ERROR;
case LOADAUTOPREFS_A:
return print_error(load_auto_prefs(b, NULL)) ? ERROR : OK;
case SAVEAUTOPREFS_A:
return print_error(save_auto_prefs(b, NULL)) ? ERROR : OK;
case SAVEDEFPREFS_A:
return print_error(save_auto_prefs(b, DEF_PREFS_NAME)) ? ERROR : OK;
case SYNTAX_A:
if (!do_syntax) return SYNTAX_NOT_ENABLED;
if (p || (p = request_string(b, "Syntax", b->syn ? (const char *)b->syn->name : NULL, true, COMPLETE_SYNTAX, b->encoding == ENC_UTF8 || b->encoding == ENC_ASCII && b->opt.utf8auto))) {
if (!strcmp(p, "*")) b->syn = NULL;
else error = print_error(load_syntax_by_name(b, p));
reset_window();
free(p);
return error ? ERROR : OK;
}
return ERROR;
case ESCAPE_A:
handle_menus();
return OK;
case UNDO_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
if (!b->opt.do_undo) return UNDO_NOT_ENABLED;
NORMALIZE(c);
delay_update();
if (b->atomic_undo) {
b->atomic_undo = 0;
while (b->link_undos) end_undo_chain(b);
print_message(info_msg[ATOMIC_UNDO_LEVEL_0]);
}
for(int64_t i = 0; i < c && !(error = undo(b)) && !stop; i++);
if (stop) error = STOPPED;
b->is_modified = b->undo.cur_step != b->undo.last_save_step;
update_window(b);
return print_error(error) ? ERROR : 0;
case REDO_A:
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
if (!b->opt.do_undo) return UNDO_NOT_ENABLED;
NORMALIZE(c);
delay_update();
for(int64_t i = 0; i < c && !(error = redo(b)) && !stop; i++);
if (stop) error = STOPPED;
b->is_modified = b->undo.cur_step != b->undo.last_save_step;
update_window(b);
return print_error(error) ? ERROR : 0;
case FLAGS_A:
help("FLAGS");
reset_window();
return OK;
case HELP_A:
help(p);
if (p) free(p);
reset_window();
return OK;
case SUSPEND_A:
stop_ne();
reset_window();
keep_cursor_on_screen(cur_buffer);
return OK;
case AUTOCOMPLETE_A:
/* Since we are going to call other actions (INSERTSTRING_A and DELETEPREVWORD_A),
we do not want to record this insertion twice. Also, we are counting on
INSERTSTRING_A to handle character encoding issues. */
recording = recording_macro;
int64_t pos = b->cur_pos;
if (!p) { /* no prefix given; find one left of the cursor. */
if (context_prefix(b, &p, &pos)) return OUT_OF_MEMORY;
}
snprintf(msg, MAX_MESSAGE_SIZE, "AutoComplete: prefix \"%s\"", p);
int e;
if (p = autocomplete(p, msg, true, &e)) {
recording_macro = NULL;
start_undo_chain(b);
if (pos >= b->cur_pos || (error = do_action(b, DELETEPREVWORD_A, 1, NULL)) == OK) error = do_action(b, INSERTSTRING_A, 0, p);
end_undo_chain(b);
recording_macro = recording;
print_message(info_msg[e]);
}
else if (stop) error = STOPPED;
else if (e == AUTOCOMPLETE_NO_MATCH) print_message(info_msg[AUTOCOMPLETE_NO_MATCH]);
return print_error(error) ? ERROR : 0;
default:
if (p) free(p);
return OK;
}
}
ne-3.3.4/src/ansi.c 0000664 0000000 0000000 00000005126 14751164310 0014016 0 ustar 00root root 0000000 0000000 /* Hardwired ANSI terminal control sequences.
Copyright (C) 2001-2025 Todd M. Lewis and Sebastiano Vigna
This file is part of ne, the nice editor.
This library 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 library 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 . */
#include "ne.h"
#ifdef NE_TERMCAP
#include "info2cap.h"
#endif
/* Pokes into the terminfo capability strings the ANSI definitions.
This is good for situation in which no database is available.
The keyboard is already fixed in keys.c, because key_may_set() will
poke into the keyboard capabilities all ANSI keyboard sequences.
*/
void setup_ansi_term(void) {
#ifdef NE_TERMCAP
ne_cursor_address = "\x1b[%i%d;%dH";
ne_set_background = "\x1b[4%dm";
ne_set_foreground = "\x1b[3%dm";
#else
/* This call to tgetent() is necessary due to changes to ncurses that
made tgoto() stop working if setupterm()/tgetent() are not invoked
first. Unfortunately, this means that if this call does not succeed
ANSI mode will not work. */
tgetent(NULL, "vt100");
ne_cursor_address = "\x1b[%i%p1%d;%p2%dH";
ne_set_background = "\x1b[4%p1%dm";
ne_set_foreground = "\x1b[3%p1%dm";
#endif
ne_enter_bold_mode = "\x1b[1m";
ne_enter_underline_mode = "\x1b[4m";
ne_enter_blink_mode = "\x1b[5m";
ne_lines = 25;
ne_columns = 80;
ne_carriage_return = "\xd";
ne_cursor_home = "\x1b[H";
ne_cursor_right = "\x1b[C";
ne_cursor_down = "\x1b[B";
ne_cursor_left = "\x1b[D";
ne_cursor_up = "\x1b[A";
ne_auto_right_margin = 1;
ne_eat_newline_glitch = 0;
ne_clr_eos = "\x1b[J";
ne_clear_screen = "\x1b[H\x1b[J";
ne_bell = "\x7";
ne_scroll_forward = "\xa";
ne_enter_standout_mode = "\x1b[7m";
ne_exit_standout_mode = ne_exit_attribute_mode = "\x1b[m";
ne_magic_cookie_glitch = -1;
ne_move_standout_mode = 0;
ne_insert_line = "\x1b[L";
ne_delete_line = "\x1b[M";
ne_delete_character = "\x1b[P";
ne_move_insert_mode = 1;
ne_exit_alt_charset_mode = "\x1b[10m";
ne_tilde_glitch = 0;
ne_memory_below = 0;
ne_has_meta_key = 0;
ne_clr_eol = "\x1b[K";
ne_transparent_underline = 0;
ne_no_color_video = 3;
ansi_color_ok = true;
}
ne-3.3.4/src/ansi.h 0000664 0000000 0000000 00000001464 14751164310 0014024 0 ustar 00root root 0000000 0000000 /* Hardwired ANSI terminal control sequences (prototypes).
Copyright (C) 2001-2025 Todd M. Lewis and Sebastiano Vigna
This file is part of ne, the nice editor.
This library 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 library 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 . */
void setup_ansi_term(void);
ne-3.3.4/src/autocomp.c 0000664 0000000 0000000 00000014342 14751164310 0014713 0 ustar 00root root 0000000 0000000 /* AutoComplete
Copyright (C) 2010-2025 Todd M. Lewis and Sebastiano Vigna
This file is part of ne, the nice editor.
This library 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 library 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 . */
#include "ne.h"
#include "support.h"
#define EXTERNAL_FLAG_CHAR '*'
#define MAX_AUTOCOMPLETE_SCAN (1000000)
static req_list rl;
/* Keeps track of how many strings we have scanned. At MAX_AUTOCOMPLETE_SCAN we return. */
static int count_scanned;
static void add_string(const char * const s, const int len, const int ext) {
char *buf = strntmp(s, len);
if (len < 1) return;
req_list_add(&rl, buf, ext);
}
static void search_buff(const buffer *b, char * p, const int encoding, const bool case_search, const int ext) {
assert(p);
const int p_len = strlen(p);
const int (*cmp)(const char *, const char *, size_t) = (const int (*)(const char *, const char *, size_t))(case_search ? strncmp : strncasecmp);
for(line_desc *ld = (line_desc *)b->line_desc_list.head, *next; next = (line_desc *)ld->ld_node.next; ld = next) {
int64_t l = 0, r = 0;
do {
/* find left edge of word */
while (l < ld->line_len - p_len && !ne_isword(get_char(&ld->line[l], b->encoding), b->encoding)) l = next_pos(ld->line, l, b->encoding);
if (l < ld->line_len - p_len ) {
int ch;
/* find right edge of word */
r = next_pos(ld->line, l, b->encoding);
/* accept "'" as a word character if it is followed by another word character, so that
words like "don't" are not broken into "don" and "t". */
while (r < ld->line_len
&& ( ne_isword(ch=get_char(&ld->line[r], b->encoding), b->encoding)
|| ( r+1 < ld->line_len && ch == '\'' && ne_isword(get_char(&ld->line[r+1], b->encoding), b->encoding))
)
) r = next_pos(ld->line, r, b->encoding);
if ((b != cur_buffer || ld != b->cur_line_desc || b->cur_pos < l || r < b->cur_pos)
&& r - l > p_len && (b->encoding == encoding || is_ascii(&ld->line[l], r - l))
&& !cmp(p, &ld->line[l], p_len))
add_string(&ld->line[l], r - l, ext);
l = r;
count_scanned++;
}
assert(l <= ld->line_len);
if (stop || count_scanned >= MAX_AUTOCOMPLETE_SCAN) {
add_string(NULL, -1, 0);
return;
}
} while (l < ld->line_len - p_len);
}
add_string(NULL, -1, 0);
}
/* Returns a completion for the (non-NULL) prefix p, showing suffixes from
all buffers if ext is true. Note that p is free()'d by this function,
and that, in turn, the returned string must be free()'d by the caller
if it is non-NULL (a returned NULL means that no completion is available).
If there is more than one completion, this function will invoke request_strings()
(and subsequently reset_window()) after displaying req_msg. In any case, error
will contain a value out of those in the enum info that start with AUTOCOMPLETE_. */
char *autocomplete(char *p, char *req_msg, const int ext, int * const error) {
assert(p);
int max_len = 0, min_len = INT_MAX, prefix_len = strlen(p);
static int ac_prune = true;
req_list_init(&rl, (cur_buffer->opt.case_search ? strcmp : strdictcmp), false, false, EXTERNAL_FLAG_CHAR);
rl.prune = ac_prune;
count_scanned = 0;
search_buff(cur_buffer, p, cur_buffer->encoding, cur_buffer->opt.case_search, false);
if (stop) {
req_list_free(&rl);
free(p);
return NULL;
}
if (ext) {
buffer *b = (buffer *)buffers.head;
while (b->b_node.next) {
if (b != cur_buffer) {
search_buff(b, p, cur_buffer->encoding, cur_buffer->opt.case_search, true);
if (stop) {
req_list_free(&rl);
free(p);
return NULL;
}
}
b = (buffer *)b->b_node.next;
}
}
for(int i = 0; i < rl.cur_entries; i++) {
const int l = strlen(rl.entries[i]);
if (max_len < l) max_len = l;
if (min_len > l) min_len = l;
}
/* We compact the table into a vector of char pointers. */
req_list_finalize(&rl);
free(p);
p = NULL;
#ifdef NE_TEST
/* During tests, we always output the middle entry. */
if (rl.cur_entries) {
if (rl.entries[rl.cur_entries/2][strlen(rl.entries[rl.cur_entries/2]) - 1] == EXTERNAL_FLAG_CHAR) rl.entries[rl.cur_entries/2][strlen(rl.entries[rl.cur_entries/2]) - 1] = 0;
p = str_dup(rl.entries[rl.cur_entries/2]);
}
*error = AUTOCOMPLETE_COMPLETED;
req_list_free(&rl);
return p;
#endif
if (rl.cur_entries > 0) {
qsort(rl.entries, rl.cur_entries, sizeof(char *), strdictcmpp);
/* Find maximum common prefix. */
int m = strlen(rl.entries[0]);
if (m && rl.entries[0][m-1] == EXTERNAL_FLAG_CHAR) m--;
encoding_type enc_0 = detect_encoding(rl.entries[0], m);
for (int i = 1; m && i < rl.cur_entries; i++) {
encoding_type enc_i = detect_encoding(rl.entries[i], strlen(rl.entries[i]));
int mi = max_prefix(rl.entries[0], enc_0, rl.entries[i], enc_i);
if (mi < m) m = mi;
}
/* If we can output more characters than the prefix len, we do so without
starting the requester. */
if (m > prefix_len) {
p = malloc(m + 1);
strncpy(p, rl.entries[0], m);
p[m] = 0;
*error = min_len == m ? AUTOCOMPLETE_COMPLETED : AUTOCOMPLETE_PARTIAL;
}
else {
if (req_msg) print_message(req_msg);
int result = request_strings(&rl, 0);
ac_prune = rl.prune;
if (result != ERROR) {
result = result >= 0 ? result : -result - 2;
/* Delete EXTERNAL_FLAG_CHAR at the end of the strings if necessary. */
if (rl.entries[result][strlen(rl.entries[result]) - 1] == EXTERNAL_FLAG_CHAR) rl.entries[result][strlen(rl.entries[result]) - 1] = 0;
p = str_dup(rl.entries[result]);
*error = AUTOCOMPLETE_COMPLETED;
}
else *error = AUTOCOMPLETE_CANCELLED;
reset_window();
}
}
else *error = AUTOCOMPLETE_NO_MATCH;
req_list_free(&rl);
D(fprintf(stderr, "autocomp returning '%s', entries: %d\n", p, rl.cur_entries);)
return p;
}
ne-3.3.4/src/buffer.c 0000664 0000000 0000000 00000142362 14751164310 0014341 0 ustar 00root root 0000000 0000000 /* Buffer handling functions, including allocation, deallocation, and I/O.
Copyright (C) 1993-1998 Sebastiano Vigna
Copyright (C) 1999-2025 Todd M. Lewis and Sebastiano Vigna
This file is part of ne, the nice editor.
This library 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 library 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 . */
#include "ne.h"
#include "support.h"
#include
/* The standard pool allocation dimension. */
#define STD_POOL_SIZE (16 * 1024)
/* The standard line descriptor pool allocation dimension (in lines). */
#define STD_LINE_DESC_POOL_SIZE (512)
/* The starting size when reading a non-seekable file. */
#define START_SIZE (8 * 1024)
/* The number of lines by which we increment the first line descriptor
pool dimension, with respect to the number of lines of the given file. */
#define STANDARD_LINE_INCREMENT (256)
/* The size of the space array. Batch printing of spaces happens in blocks of
this size. */
#define MAX_STACK_SPACES (256)
/* The length of the block used in order to optimize saves. */
#define SAVE_BLOCK_LEN (16 * 1024 - 1)
/* The length of a half of the circular buffer used for memory mapping. */
#define CIRC_BUFFER_SIZE (8 * 1024)
/* The number of line descriptors in the buffer used for memory mapping. */
#define LD_BUFFER_COUNT (256)
/* Detects (heuristically) the encoding of a buffer. */
encoding_type detect_buffer_encoding(const buffer * const b) {
line_desc *ld = (line_desc *)b->line_desc_list.head, *next;
encoding_type encoding = ENC_ASCII, e;
while(next = (line_desc *)ld->ld_node.next) {
e = detect_encoding(ld->line, ld->line_len);
if (e != ENC_ASCII) {
if (encoding == ENC_ASCII) encoding = e;
if (e == ENC_8_BIT) encoding = ENC_8_BIT;
}
ld = next;
}
return encoding;
}
/* These functions allocate and deallocate character pools. The size of the
pool is the number of characters, and it is forced to be at least
STD_POOL_SIZE. force is passed to alloc_or_mmap(). */
char_pool *alloc_char_pool(int64_t size, const int fd_or_zero, int force) {
if (size < STD_POOL_SIZE && ! fd_or_zero) size = STD_POOL_SIZE;
char_pool * const cp = calloc(1, sizeof(char_pool));
if (cp) {
if (cp->pool = alloc_or_mmap(size, fd_or_zero, &force)) {
cp->mapped = force;
cp->size = size;
return cp;
}
free(cp);
}
return NULL;
}
char_pool *alloc_char_pool_from_memory(char * const pool, const int64_t size) {
char_pool * const cp = calloc(1, sizeof(char_pool));
if (cp) {
cp->pool = pool;
cp->size = size;
return cp;
}
return NULL;
}
void free_char_pool(char_pool * const cp) {
if (cp == NULL) return;
if (cp->mapped) munmap(cp->pool, cp->size);
else free(cp->pool);
free(cp);
}
/* Given a pointer in a character pool and a buffer, this function returns the
respective pool. It can return NULL if the pointer wasn't in any pool, but
this condition denotes a severe malfunctioning. */
char_pool *get_char_pool(buffer * const b, char * const p) {
for(char_pool *cp = (char_pool *)b->char_pool_list.head; cp->cp_node.next;) {
assert_char_pool(cp);
if (p >= cp->pool && p < cp->pool + cp->size) return cp;
cp = (char_pool *)cp->cp_node.next;
}
assert(false);
return NULL;
}
/* These functions allocate and deallocate line-descriptor pools. The size of
the pool is the number of lines, and is forced to be at least
STD_LINE_DESC_POOL_SIZE. force is passed to alloc_or_mmap(). */
line_desc_pool *alloc_line_desc_pool(int64_t pool_size, int force) {
if (pool_size < STD_LINE_DESC_POOL_SIZE) pool_size = STD_LINE_DESC_POOL_SIZE;
line_desc_pool * const ldp = calloc(1, sizeof(line_desc_pool));
if (ldp) {
if (ldp->pool = alloc_or_mmap(pool_size * (do_syntax ? sizeof(line_desc) : sizeof(no_syntax_line_desc)), 0, &force)) {
ldp->mapped = force;
ldp->size = pool_size;
new_list(&ldp->free_list);
for(int64_t i = 0; i < pool_size; i++)
if (do_syntax) add_tail(&ldp->free_list, &((line_desc *)ldp->pool)[i].ld_node);
else add_tail(&ldp->free_list, &((no_syntax_line_desc *)ldp->pool)[i].ld_node);
return ldp;
}
free(ldp);
}
return NULL;
}
/* This function creates a line-descriptor pool using a given region of memory.
which must be able to hold pool_size element of the right type (depending on
do_syntax). All items in the pool are considered to be allocated. */
line_desc_pool *alloc_line_desc_pool_from_memory(void *pool, int64_t pool_size) {
line_desc_pool * const ldp = calloc(1, sizeof(line_desc_pool));
if (ldp) {
new_list(&ldp->free_list);
ldp->pool = pool;
ldp->size = ldp->allocated_items = pool_size;
return ldp;
}
return NULL;
}
void free_line_desc_pool(line_desc_pool * const ldp) {
if (ldp == NULL) return;
assert_line_desc_pool(ldp);
if (ldp->mapped) munmap(ldp->pool, ldp->size * (do_syntax ? sizeof(line_desc) : sizeof(no_syntax_line_desc)));
else free(ldp->pool);
free(ldp);
}
/* These functions allocate and deallocate a buffer. Note that on allocation
we have to initialize the list pointers, and on dellocation we have to free
all the lists. Moreover, on allocation a buffer pointer can be passed so
that the new buffer can inherit various user flags. */
buffer *alloc_buffer(const buffer * const cur_b) {
buffer *b;
if (b = calloc(1, sizeof(buffer))) {
new_list(&b->line_desc_pool_list);
new_list(&b->line_desc_list);
new_list(&b->char_pool_list);
b->act = ++buffer_actuations;
b->cur_macro = alloc_char_stream(0);
b->bpaste_support = bracketed_paste ? 1 : 0;
b->opt.tab_size = 8;
b->opt.insert =
b->opt.tabs =
b->opt.shift_tabs =
b->opt.automatch =
b->opt.do_undo =
b->opt.auto_prefs = 1;
b->opt.utf8auto = io_utf8;
b->attr_len = -1;
if (cur_b) {
b->opt.cur_clip = cur_b->opt.cur_clip;
b->opt.tab_size = cur_b->opt.tab_size;
b->opt.tabs = cur_b->opt.tabs;
b->opt.del_tabs = cur_b->opt.del_tabs;
b->opt.shift_tabs = cur_b->opt.shift_tabs;
b->opt.automatch = cur_b->opt.automatch;
b->opt.right_margin = cur_b->opt.right_margin;
b->opt.free_form = cur_b->opt.free_form;
b->opt.hex_code = cur_b->opt.hex_code;
b->opt.word_wrap = cur_b->opt.word_wrap;
b->opt.auto_indent = cur_b->opt.auto_indent;
b->opt.preserve_cr = cur_b->opt.preserve_cr;
b->opt.do_undo = cur_b->opt.do_undo;
b->opt.auto_prefs = cur_b->opt.auto_prefs;
b->opt.no_file_req = cur_b->opt.no_file_req;
b->opt.case_search = cur_b->opt.case_search;
b->opt.binary = cur_b->opt.binary;
b->opt.utf8auto = cur_b->opt.utf8auto;
b->opt.visual_bell = cur_b->opt.visual_bell;
b->bpaste_support = cur_b->bpaste_support;
if (b->bpaste_support == 2) {
b->bpaste_macro_before = str_dup(cur_b->bpaste_macro_before);
b->bpaste_macro_after = str_dup(cur_b->bpaste_macro_after);
}
}
/* This leaves out only opt.read_only and opt.search_back, which are
implicitly set to 0 by the calloc(). */
return b;
}
return NULL;
}
/* This function is useful when resetting a buffer, but not really
destroying it. Since it modifies some lists, it cannot be interrupted
from a signal. Note that the search, replace and command_line strings are
not cleared. */
void free_buffer_contents(buffer * const b) {
if (!b) return;
block_signals();
free_list(&b->line_desc_pool_list, free_line_desc_pool);
free_list(&b->char_pool_list, free_char_pool);
new_list(&b->line_desc_list);
b->cur_line_desc = b->top_line_desc = NULL;
b->allocated_chars = b->free_chars = 0;
b->num_lines = 0;
b->is_CRLF = false;
b->encoding = ENC_ASCII;
b->bookmark_mask = 0;
b->mtime = 0;
free_char_stream(b->last_deleted);
b->last_deleted = NULL;
free(b->filename);
b->filename = NULL;
reset_undo_buffer(&b->undo);
b->is_modified = b->marking = b->x_wanted = 0;
release_signals();
}
/* Removes all data in a buffer, but leaves in the current macro, the search,
replace and command_line strings, and an empty line. */
void clear_buffer(buffer * const b) {
if (!b) return;
block_signals();
free_buffer_contents(b);
line_desc * const ld = alloc_line_desc(b);
add_head(&b->line_desc_list, &ld->ld_node);
if (do_syntax) {
ld->highlight_state.state = 0;
ld->highlight_state.stack = NULL;
ld->highlight_state.saved_s[0] = 0;
}
b->num_lines = 1;
reset_position_to_sof(b);
assert_buffer(b);
release_signals();
}
/* Frees all the data associated to a buffer. */
void free_buffer(buffer * const b) {
if (b == NULL) return;
assert_buffer(b);
free_buffer_contents(b);
free_char_stream(b->cur_macro);
free(b->find_string);
free(b->replace_string);
free(b->command_line);
free(b->bpaste_macro_before);
free(b->bpaste_macro_after);
if (b->attr_buf) free(b->attr_buf);
free(b);
}
/* Computes how many characters have been "lost" in a buffer, that is, how many
free characters lie inside the first and last used characters of the
character pools. This characters can only be allocated by
alloc_chars_around(). */
int64_t calc_lost_chars(const buffer * const b) {
int64_t n = 0;
for(char_pool *cp = (char_pool *)b->char_pool_list.head; cp->cp_node.next; cp = (char_pool *)cp->cp_node.next)
n += cp->size - (cp->last_used - cp->first_used + 1);
return b->free_chars - n;
}
/* Returns the nth buffer in the global buffer list, or NULL if less than n
buffers are available. */
buffer *get_nth_buffer(int n) {
for(buffer *b = (buffer *)buffers.head; b->b_node.next; b = (buffer *)b->b_node.next)
if (!n--) return b;
return NULL;
}
/* Returns the first buffer with a name matching *p.
The buffers' names and *p are converted to their fully qualified form
for the comparisons.
This is a departure from earlier behavior, which only considered the
file_part() of either name. */
buffer *get_buffer_named(const char *p) {
char *bname=NULL, *pname=NULL, *cwd=NULL;
buffer *b;
int rc = 1;
if (!p) return NULL;
cwd = ne_getcwd(CUR_DIR_MAX_SIZE);
if (!cwd) return NULL;
if ((pname = absolute_file_path(p, cwd))) {
for(b = (buffer *)buffers.head; b->b_node.next; b = (buffer *)b->b_node.next) {
if (b->filename && (bname = absolute_file_path(b->filename, cwd))) {
if (!(rc = strcmp(bname, pname))) break;
}
free(bname);
bname = NULL;
}
}
free(pname);
free(bname);
free(cwd);
if (!rc) return b;
return NULL;
}
/* Returns true if the given pointer references an extant buffer. */
bool is_buffer(const buffer * const maybe_buf) {
for(buffer *b = (buffer *)buffers.head; b->b_node.next; b = (buffer *)b->b_node.next)
if (maybe_buf == b) return true;
return false;
}
/* Returns true if the given buffer is empty. */
bool is_buffer_empty(const buffer * const b) {
if (b) {
if (b->line_desc_list.head->next == NULL ||
b->line_desc_list.head->next->next == NULL && ((line_desc *)b->line_desc_list.head)->line_len == 0) return true;
}
return false;
}
/* Returns true if any of the buffers has been modified since the last save. */
int modified_buffers(void) {
for(buffer *b = (buffer *)buffers.head; b->b_node.next; b = (buffer *)b->b_node.next)
if (b->is_modified) return true;
return false;
}
/* Saves all buffers which have been modified since the last save. Returns an
error if a save is unsuccessful, a file on-disk was modified since last
loaded or saved, or if a buffer has no name. */
int save_all_modified_buffers(void) {
int rc = 0;
for(buffer *b = (buffer *)buffers.head; b->b_node.next; b = (buffer *)b->b_node.next)
if (b->is_modified) {
if (buffer_file_modified(b, NULL)) rc = ERROR;
else if (save_buffer_to_file(b, NULL)) rc = ERROR;
}
return rc;
}
/* Now we have the much more sophisticated allocation functions which create
small elements such as lines and line descriptors. All the operations
are in the context of a given buffer. Most of these functions are
protected internally against being interrupted by signals, since
auto_save could die miserably because of the inconsistent state of a
list. */
/* Allocates a line descriptor from the pools available in the given buffer. A
new pool is allocated and linked if necessary. New line descriptors are
created with an invalid syntax state, so they will always force an update. */
line_desc *alloc_line_desc(buffer * const b) {
block_signals();
line_desc_pool *ldp;
for(ldp = (line_desc_pool *)b->line_desc_pool_list.head; ldp->ldp_node.next; ldp = (line_desc_pool *)ldp->ldp_node.next) {
assert_line_desc_pool(ldp);
if (ldp->free_list.head->next) {
line_desc * const ld = (line_desc *)ldp->free_list.head;
rem(&ld->ld_node);
if (!ldp->free_list.head->next) {
rem(&ldp->ldp_node);
add_tail(&b->line_desc_pool_list, &ldp->ldp_node);
}
ldp->allocated_items++;
ld->line = NULL;
ld->line_len = 0;
if (do_syntax) ld->highlight_state.state = -1;
release_signals();
return ld;
}
}
/* No chances, all pools are full. Let's allocate a new one,
using the standard pool size, and let's put it at the start
of the list, so that it is always scanned first. */
if (ldp = alloc_line_desc_pool(0, -1)) {
add_head(&b->line_desc_pool_list, &ldp->ldp_node);
line_desc * const ld = (line_desc *)ldp->free_list.head;
rem(&ld->ld_node);
ldp->allocated_items = 1;
if (do_syntax) ld->highlight_state.state = -1;
release_signals();
return ld;
}
release_signals();
return NULL;
}
/* Frees a line descriptor, (and the line descriptor pool containing it, should
it become empty). */
void free_line_desc(buffer * const b, line_desc * const ld) {
/* We scan the pool list in order to find where the given
line descriptor lives. */
line_desc_pool *ldp;
for(ldp = (line_desc_pool *)b->line_desc_pool_list.head; ldp->ldp_node.next; ldp = (line_desc_pool *)ldp->ldp_node.next) {
assert_line_desc_pool(ldp);
if (do_syntax && ld >= (line_desc *)ldp->pool && ld < (line_desc *)ldp->pool + ldp->size
|| !do_syntax && (no_syntax_line_desc *)ld >= (no_syntax_line_desc *)ldp->pool
&& (no_syntax_line_desc *)ld < (no_syntax_line_desc *)ldp->pool + ldp->size) break;
}
assert(ldp->ldp_node.next != NULL);
block_signals();
add_head(&ldp->free_list, &ld->ld_node);
if (--ldp->allocated_items == 0) {
rem(&ldp->ldp_node);
free_line_desc_pool(ldp);
}
release_signals();
}
/* Allocates len characters from the character pools of the
given buffer. If necessary, a new pool is allocated. */
char *alloc_chars(buffer * const b, const int64_t len) {
if (!len || !b) return NULL;
assert_buffer(b);
block_signals();
char_pool *cp;
for(cp = (char_pool *)b->char_pool_list.head; cp->cp_node.next; cp = (char_pool *)cp->cp_node.next) {
assert_char_pool(cp);
/* We try to allocate before the first used character,
or after the last used character. If we succeed with a
pool which is not the head of the list, we move it to
the head in order to optimize the next try. */
if (cp->first_used >= len) {
cp->first_used -= len;
b->free_chars -= len;
if (cp != (char_pool *)b->char_pool_list.head) {
rem(&cp->cp_node);
add_head(&b->char_pool_list, &cp->cp_node);
}
release_signals();
return cp->pool + cp->first_used;
}
else if (cp->size - cp->last_used > len) {
cp->last_used += len;
b->free_chars -= len;
if (cp != (char_pool *)b->char_pool_list.head) {
rem(&cp->cp_node);
add_head(&b->char_pool_list, &cp->cp_node);
}
release_signals();
return cp->pool + cp->last_used - len + 1;
}
}
/* If no free space has been found, we allocate a new pool which is guaranteed
to contain at least len characters. The pool is added to the head of the list. */
if (cp = alloc_char_pool(len, 0, -1)) {
add_head(&b->char_pool_list, &cp->cp_node);
cp->last_used = len - 1;
b->allocated_chars += cp->size;
b->free_chars += cp->size - len;
release_signals();
return cp->pool;
}
release_signals();
return NULL;
}
/* This function is very important, since it embeds all the philosophy behind
ne's character pool management. It performs an allocation *locally*, that
is, it tries to see if there are enough free characters around the line
pointed to by a line descriptor by looking at non-nullness of surrounding
characters (if a character is set to 0, it is free). First the characters
after the line are checked, then the characters before (this can be reversed
via the check_first_before flag). The number of characters available *after*
the line is returned, or ERROR if the allocation failed. The caller can
recover the characters available before the line since he knows the length
of the allocation. Note that it is *only* through this function that the
"lost" characters can be allocated, but being editing a local activity, this
is what happens usually. */
int64_t alloc_chars_around(buffer * const b, line_desc * const ld, const int64_t n, const bool check_first_before) {
assert(ld->line != NULL);
char_pool *cp = get_char_pool(b, ld->line);
assert_char_pool(cp);
block_signals();
char *before = ld->line - 1;
char *after = ld->line + ld->line_len;
if (check_first_before) {
while(before >= cp->pool && !*before && (ld->line - 1) - before < n)
before--;
while(after < cp->pool + cp->size && !*after && (after - (ld->line + ld->line_len)) + ((ld->line - 1) - before)pool + cp->size && !*after && after - (ld->line + ld->line_len)= cp->pool && !*before && (after - (ld->line + ld->line_len)) + ((ld->line - 1) - before)line - 1) - before) + (after - (ld->line + ld->line_len)) <= n);
assert(((ld->line - 1) - before) + (after - (ld->line + ld->line_len)) >= 0);
if (((ld->line - 1) - before) + (after - (ld->line + ld->line_len)) == n) {
if (cp->pool + cp->first_used == ld->line) cp->first_used = (before + 1) - cp->pool;
if (cp->pool + cp->last_used == ld->line + ld->line_len - 1) cp->last_used = (after - 1) - cp->pool;
b->free_chars -= n;
release_signals();
return after - (ld->line + ld->line_len);
}
release_signals();
return ERROR;
}
/* Frees a block of len characters pointed to by p. If the char pool containing
the block becomes completely free, it is removed from the list. */
void free_chars(buffer *const b, char *const p, const int64_t len) {
if (!b || !p || !len) return;
char_pool *cp = get_char_pool(b, p);
assert_char_pool(cp);
assert(*p);
assert(p[len - 1]);
block_signals();
memset(p, 0, len);
b->free_chars += len;
if (p == &cp->pool[cp->first_used]) while(cp->first_used <= cp->last_used && !cp->pool[cp->first_used]) cp->first_used++;
if (p + len - 1 == &cp->pool[cp->last_used]) while(!cp->pool[cp->last_used] && cp->first_used <= cp->last_used) cp->last_used--;
if (cp->last_used < cp->first_used) {
rem(&cp->cp_node);
b->allocated_chars -= cp->size;
b->free_chars -= cp->size;
free_char_pool(cp);
release_signals();
return;
}
assert_char_pool(cp);
release_signals();
}
/* The following functions represent the only legal way of modifying a
buffer. They are all based on insert_stream and delete_stream (except for
the I/O functions). A stream is a sequence of NULL-terminated strings. The
semantics associated is that each string is a separate line terminated by a
line feed, *except for the last one*. Thus, a NULL-terminated string is a
line with no linefeed. All the functions accept a position specified via a
line descriptor and a position (which is the offset to be applied to the
line pointer of the line descriptor). Also the line number is usually
supplied, since it is necessary for recording the operation in the undo
buffer. */
/* Inserts a line at the current position. The effect is obtained by inserting
a stream containing one NULL. */
int insert_one_line(buffer * const b, line_desc * const ld, const int64_t line, const int64_t pos) {
return insert_stream(b, ld, line, pos, "", 1);
}
/* Deletes a whole line, putting it in the temporary line buffer used by the
UndelLine command. */
int delete_one_line(buffer * const b, line_desc * const ld, const int64_t line) {
assert_line_desc(ld, b->encoding);
assert_buffer(b);
block_signals();
if (ld->line_len && (b->last_deleted = reset_stream(b->last_deleted))) add_to_stream(b->last_deleted, ld->line, ld->line_len);
/* We delete a line by delete_stream()ing its length plus one. However, if
we are on the last line of text, there is no terminating line feed. */
const int error = delete_stream(b, ld, line, 0, ld->line_len + (ld->ld_node.next->next ? 1 : 0));
release_signals();
return error;
}
/* Undeletes the last deleted line, using the last_deleted stream. */
int undelete_line(buffer * const b) {
line_desc * const ld = b->cur_line_desc;
if (!b->last_deleted) return ERROR;
start_undo_chain(b);
if (b->cur_pos > ld->line_len)
insert_spaces(b, ld, b->cur_line, ld->line_len, b->win_x + b->cur_x - calc_width(ld, ld->line_len, b->opt.tab_size, b->encoding));
insert_one_line(b, ld, b->cur_line, b->cur_pos);
insert_stream(b, ld, b->cur_line, b->cur_pos, b->last_deleted->stream, b->last_deleted->len);
end_undo_chain(b);
return OK;
}
/* Deletes a line up to its end. */
void delete_to_eol(buffer * const b, line_desc * const ld, const int64_t line, const int64_t pos) {
if (!ld || pos >= ld->line_len) return;
delete_stream(b, ld, line, pos, ld->line_len - pos);
}
/* Inserts a stream in a line at a given position. The position has to be
smaller or equal to the line length. Since the stream can contain many
lines, this function can be used for manipulating all insertions. It also
records the inverse operation in the undo buffer if b->opt.do_undo is
true. */
int insert_stream(buffer * const b, line_desc * ld, int64_t line, int64_t pos, const char * const stream, const int64_t stream_len) {
assert(pos >= 0);
assert(stream_len >= 0);
if (!b || !ld || !stream || stream_len < 1 || pos > ld->line_len) return ERROR;
assert_line_desc(ld, b->encoding);
assert_buffer(b);
block_signals();
if (b->opt.do_undo && !(b->undoing || b->redoing)) {
const int error = add_undo_step(b, line, pos, -stream_len);
if (error) {
release_signals();
return error;
}
}
const char *s = stream;
while(s - stream < stream_len) {
int64_t const len = strnlen_ne(s, stream_len - (s - stream));
if (len) {
/* First case; there is no character allocated on this line. We
have to freshly allocate the line. */
if (!ld->line) {
if (ld->line = alloc_chars(b, len)) {
memcpy(ld->line, s, len);
ld->line_len = len;
}
else {
release_signals();
return OUT_OF_MEMORY_DISK_FULL;
}
}
/* Second case. There are not enough characters around ld->line. Note
that the value of the check_first_before parameter depends on
the position at which the insertion will be done, and it is chosen
in such a way to minimize the number of characters to move. */
else {
const int64_t result = alloc_chars_around(b, ld, len, pos < ld->line_len / 2);
if (result < 0) {
char * const p = alloc_chars(b, ld->line_len + len);
if (p) {
memcpy(p, ld->line, pos);
memcpy(&p[pos], s, len);
memcpy(&p[pos + len], ld->line + pos, ld->line_len - pos);
free_chars(b, ld->line, ld->line_len);
ld->line = p;
ld->line_len += len;
}
else {
release_signals();
return OUT_OF_MEMORY_DISK_FULL;
}
}
else { /* Third case. There are enough free characters around ld->line. */
if (len - result) memmove(ld->line - (len - result), ld->line, pos);
if (result) memmove(ld->line + pos + result, ld->line + pos, ld->line_len - pos);
memcpy(ld->line - (len - result) + pos, s, len);
ld->line -= (len - result);
ld->line_len += len;
}
}
b->is_modified = 1;
/* We just inserted len chars at (line,pos); adjust bookmarks and mark accordingly. */
if (b->marking && b->block_start_line == line && b->block_start_pos > pos) b->block_start_pos += len;
for (int i = 0, mask = b->bookmark_mask; mask; i++, mask >>= 1)
if ((mask & 1) && b->bookmark[i].line == line && b->bookmark[i].pos > pos) b->bookmark[i].pos += len;
}
/* If the string we have inserted has a NULL at the end, we create a new
line under the current one and set ld to point to it. */
if (len + (s - stream) < stream_len) {
line_desc *new_ld;
if (new_ld = alloc_line_desc(b)) {
add(&new_ld->ld_node, &ld->ld_node);
b->num_lines++;
if (pos + len < ld->line_len) {
new_ld->line_len = ld->line_len - pos - len;
new_ld->line = &ld->line[pos + len];
ld->line_len = pos + len;
if (pos + len == 0) ld->line = NULL;
}
b->is_modified = 1;
ld = new_ld;
/* We just inserted a line break at (line,pos);
adjust the buffer bookmarks and mark accordingly. */
if (b->marking) {
if (b->block_start_line == line && b->block_start_pos > pos) {
b->block_start_pos -= pos + len;
b->block_start_line++;
}
else if (b->block_start_line > line) b->block_start_line++;
}
for (int i = 0, mask=b->bookmark_mask; mask; i++, mask >>= 1) {
if (mask & 1) {
if (b->bookmark[i].line == line && b->bookmark[i].pos > pos) {
b->bookmark[i].pos -= pos + len;
b->bookmark[i].line++;
}
else if (b->bookmark[i].line > line) b->bookmark[i].line++;
}
}
pos = 0;
line++;
}
else {
release_signals();
return OUT_OF_MEMORY_DISK_FULL;
}
}
s += len + 1;
}
release_signals();
return OK;
}
/* Inserts a single ISO 10646 character (it creates, if necessary, a suitable
temporary stream). The character must be compatible with the current buffer
encoding. */
int insert_one_char(buffer * const b, line_desc * const ld, const int64_t line, const int64_t pos, const int c) {
static char t[8];
assert(b->encoding == ENC_8_BIT || b->encoding == ENC_UTF8 || c <= 127);
assert(b->encoding == ENC_UTF8 || c <= 255);
assert(c != 0);
if (b->encoding == ENC_UTF8) t[utf8str(c, t)] = 0;
else t[0] = c, t[1] = 0;
return insert_stream(b, ld, line, pos, t, strlen(t));
}
/* Inserts a number of spaces. */
int insert_spaces(buffer * const b, line_desc * const ld, const int64_t line, const int64_t pos, int64_t n) {
static char spaces[MAX_STACK_SPACES];
int result = OK, i;
if (!spaces[0]) memset(spaces, ' ', sizeof spaces);
while(result == OK && n > 0) {
i = min(n, MAX_STACK_SPACES);
result = insert_stream(b, ld, line, pos, spaces, i);
n -= i;
}
assert(result != OK || n == 0);
return result;
}
/* Deletes a stream of len bytes, that is, deletes len bytes from the given
position, counting line feeds as a byte. The operation is recorded in the
undo buffer. */
int delete_stream(buffer * const b, line_desc * const ld, const int64_t line, const int64_t pos, int64_t len) {
assert_buffer(b);
assert_line_desc(ld, b->encoding);
/* If we are in no man's land, we return. */
if (!b || !ld || !len || pos > ld->line_len || pos == ld->line_len && !ld->ld_node.next->next) return ERROR;
block_signals();
if (b->opt.do_undo && !(b->undoing || b->redoing)) {
const int error = add_undo_step(b, line, pos, len);
if (error) {
release_signals();
return error;
}
}
while(len) {
/* First case: we are just on the end of a line. We join the current
line with the following one (if it's there of course). If, however,
the current line is empty, we rather remove it. The only difference
is in the resulting syntax state. */
if (pos == ld->line_len) {
line_desc *next_ld = (line_desc *)ld->ld_node.next;
/* There's nothing more to do--we are at the end of the file. */
if (next_ld->ld_node.next == NULL) break;
/* We're about to join line+1 to line; adjust mark and bookmarks accordingly. */
if (b->marking) {
if (b->block_start_line == line+1) {
b->block_start_line--;
b->block_start_pos += ld->line_len;
}
else if (b->block_start_line > line) b->block_start_line--;
}
for (int i = 0, mask = b->bookmark_mask; mask; i++, mask >>= 1) {
if (mask & 1) {
if (b->bookmark[i].line == line+1) {
b->bookmark[i].line--;
b->bookmark[i].pos += ld->line_len;
}
else if (b->bookmark[i].line > line) b->bookmark[i].line--;
}
}
/* If one of the lines is empty, or their contents are adjacent,
we either do nothing or simply set a pointer. */
if (!ld->line || !next_ld->line || ld->line + ld->line_len == next_ld->line) {
if (!ld->line) ld->line = next_ld->line;
}
else {
int64_t n, m;
if ((n = alloc_chars_around(b, ld, next_ld->line_len, false))<0 && (m = alloc_chars_around(b, next_ld, ld->line_len, true))<0) {
/* We try to allocate characters around one line or the other
one; if we fail, we allocate enough space for both lines elsewhere. */
char * const p = alloc_chars(b, ld->line_len + next_ld->line_len);
if (p) {
memcpy(p, ld->line, ld->line_len);
memcpy(p + ld->line_len, next_ld->line, next_ld->line_len);
free_chars(b, ld->line, ld->line_len);
free_chars(b, next_ld->line, next_ld->line_len);
ld->line = p;
}
else {
release_signals();
if (b->opt.do_undo && !(b->undoing || b->redoing)) fix_last_undo_step(b, -len);
return OUT_OF_MEMORY_DISK_FULL;
}
}
/* In case one of the alloc_chars_around succeeds, we have just to
move the lines in the right place. */
else if (n >= 0) {
if (n < next_ld->line_len) memmove(ld->line + (n - next_ld->line_len), ld->line, ld->line_len);
ld->line += (n - next_ld->line_len);
memcpy(ld->line + ld->line_len, next_ld->line, next_ld->line_len);
free_chars(b, next_ld->line, next_ld->line_len);
}
else {
if (m) memmove(next_ld->line + m, next_ld->line, next_ld->line_len);
next_ld->line += m;
memcpy(next_ld->line - ld->line_len, ld->line, ld->line_len);
free_chars(b, ld->line, ld->line_len);
ld->line = next_ld->line - ld->line_len;
}
}
ld->line_len += next_ld->line_len;
b->num_lines--;
rem(&next_ld->ld_node);
free_line_desc(b, next_ld);
len--;
if (!b->redoing) {
if (b->undoing) add_to_stream(&b->undo.redo, "", 1);
else if (b->opt.do_undo) add_to_undo_stream(&b->undo, "", 1);
}
}
/* Second case: we are inside a line. We delete len bytes or, if
there are less then len bytes to delete, we delete up to the end
of the line. In the latter case, we simply set the line length and
free the corresponding bytes. Otherwise, the number of bytes to
move is minimized. */
else {
int64_t n = len > ld->line_len - pos ? ld->line_len - pos : len;
/* We're about to erase n chars at (line,pos); adjust mark and bookmarks accordingly. */
if (b->marking)
if (b->block_start_line == line)
if (b->block_start_pos >= pos)
if (b->block_start_pos < pos + n)
b->block_start_pos = pos;
else
b->block_start_pos -= n;
for (int i = 0, mask = b->bookmark_mask; mask; i++, mask>>=1) {
if (mask & 1) {
if (b->bookmark[i].line == line)
if (b->bookmark[i].pos >= pos)
if (b->bookmark[i].pos < pos + n) b->bookmark[i].pos = pos;
else b->bookmark[i].pos -= n;
}
}
if (!b->redoing) {
if (b->undoing) add_to_stream(&b->undo.redo, &ld->line[pos], n);
else if (b->opt.do_undo) add_to_undo_stream(&b->undo, &ld->line[pos], n);
}
if (n == ld->line_len - pos) free_chars(b, &ld->line[pos], n);
else {
if (pos < ld->line_len / 2) {
memmove(ld->line + n, ld->line, pos);
free_chars(b, ld->line, n);
ld->line += n;
}
else {
memmove(ld->line + pos, ld->line + pos + n, ld->line_len - pos - n);
free_chars(b, &ld->line[ld->line_len - n], n);
}
}
if (!(ld->line_len -= n)) ld->line = NULL;
len -= n;
assert_line_desc(ld, b->encoding);
}
b->is_modified = 1;
}
if (b->opt.do_undo && !(b->undoing || b->redoing)) fix_last_undo_step(b, -len);
release_signals();
return OK;
}
/* Deletes a single character. */
int delete_one_char(buffer * const b, line_desc * const ld, const int64_t line, const int64_t pos) {
return delete_stream(b, ld, line, pos, b->encoding == ENC_UTF8 && pos < ld->line_len ? utf8len(ld->line[pos]) : 1);
}
/* Returns the line descriptor for line n of buffer b, or NULL if n is out of range.
We assume that cur_line and cur_line_desc are coherent, and try to use the
faster way (i.e., relative or absolute). */
line_desc *nth_line_desc(const buffer *b, const int64_t n) {
if (n < 0 || n >= b->num_lines) return NULL;
line_desc *ld;
const int64_t best_absolute_cost = min(n, b->num_lines - 1 - n);
const int64_t relative_cost = b->cur_line < n ? n - b->cur_line : b->cur_line - n;
if (best_absolute_cost < relative_cost) {
if (n < b->num_lines / 2) {
ld = (line_desc *)b->line_desc_list.head;
for(int64_t i = 0; i < n; i++) ld = (line_desc *)ld->ld_node.next;
}
else {
ld = (line_desc *)b->line_desc_list.tail_pred;
for(int64_t i = 0; i < b->num_lines - 1 - n; i++) ld = (line_desc *)ld->ld_node.prev;
}
}
else {
ld = (line_desc *)b->cur_line_desc;
if (n < b->cur_line) for(int64_t i = 0; i < b->cur_line - n; i++) ld = (line_desc *)ld->ld_node.prev;
else for(int64_t i = 0; i < n - b->cur_line; i++) ld = (line_desc *)ld->ld_node.next;
}
return ld;
}
/* Changes the buffer file name to the given string, which must have been
obtained through malloc(). */
void change_filename(buffer * const b, char * const name) {
assert(name != NULL);
if (b->filename) free(b->filename);
b->filename = name;
}
/* Here we load a file into a given buffer. The buffer lists are deallocated
first. If there is not write access to the file, the read-only flag is set.
Note that we consider line feeds 0x0A's, 0x0D's and 0x00's (the last being
made necessary by the way the pools are handled), unless the binary flag is
set, in which case we consider only the 0x00's. */
int load_file_in_buffer(buffer * const b, const char *name) {
if (!b) return ERROR;
assert_buffer(b);
name = tilde_expand(name);
if (is_directory(name)) return FILE_IS_DIRECTORY;
if (is_migrated(name)) return FILE_IS_MIGRATED;
const int fd = open(name, READ_FLAGS);
if (fd >= 0) {
const int result = load_fd_in_buffer(b, fd);
close(fd);
b->mtime = file_mod_time(name);
if (!result) b->opt.read_only = (access(name, W_OK) != 0);
return result;
}
return errno == ENOENT ? FILE_DOES_NOT_EXIST : CANT_OPEN_FILE;
}
/* Support function for load_fd_in_buffer(). */
static int create_mmap_files(buffer * const b, const int fd, const int char_fd, const int ld_fd, const size_t len, const int line_desc_size, char * const terminators) {
/* A circular buffer whose first and second half are alternately written and reloaded. */
char buffer[CIRC_BUFFER_SIZE * 2];
/* A buffer for line descriptors. */
char ld_buffer[LD_BUFFER_COUNT * line_desc_size];
line_desc *ld_buffer_syn = (line_desc *)ld_buffer;
no_syntax_line_desc *ld_buffer_no_syn = (no_syntax_line_desc *)ld_buffer;
int ld_count = 0;
size_t remaining = len;
size_t to_do = min(remaining, sizeof buffer);
ssize_t result = read(fd, buffer, to_do);
if (result < to_do) return IO_ERROR;
remaining -= to_do;
int i = 0;
int64_t curr_pos = 0, start_of_line = 0, end_of_line = 0;
while(curr_pos < len) {
/* Here we replicate the logic of load_fd_in_buffer(). The circularity of the buffer
makes it possible to check the current character and the following one. */
if (!b->opt.binary && (buffer[i] == terminators[0] || buffer[i] == terminators[1]) || !buffer[i]) {
end_of_line = curr_pos;
if (curr_pos < len - 1 && buffer[i] == '\r' && buffer[i + 1 & sizeof buffer - 1] == '\n') {
b->is_CRLF = true;
buffer[i] = 0;
curr_pos++;
b->free_chars++;
i = i + 1 & sizeof buffer - 1;
if ((i & sizeof buffer / 2 - 1) == 0) { // Buffer flip-flop
char * const p = buffer + (i ^ sizeof buffer / 2);
if (write(char_fd, p, sizeof buffer / 2) < sizeof buffer / 2) return OUT_OF_MEMORY_DISK_FULL;
to_do = min(remaining, sizeof buffer / 2);
ssize_t result = read(fd, p, to_do);
if (result < to_do) return IO_ERROR;
remaining -= to_do;
}
}
b->num_lines++;
/* Line descriptors are created with an offset from the start
of fd, rather than an absolute pointer in memory. They will be
fixed afterwards. */
if (do_syntax) {
ld_buffer_syn[ld_count].line = (char *)start_of_line;
ld_buffer_syn[ld_count].line_len = end_of_line - start_of_line;
}
else {
ld_buffer_no_syn[ld_count].line = (char *)start_of_line;
ld_buffer_no_syn[ld_count].line_len = end_of_line - start_of_line;
}
if (++ld_count == LD_BUFFER_COUNT) {
if (write(ld_fd, ld_buffer, LD_BUFFER_COUNT * line_desc_size) < LD_BUFFER_COUNT * line_desc_size) return OUT_OF_MEMORY_DISK_FULL;
ld_count = 0;
}
b->free_chars++;
buffer[i] = 0;
start_of_line = curr_pos + 1;
}
curr_pos++;
i = i + 1 & sizeof buffer - 1;
if ((i & sizeof buffer / 2 - 1) == 0) { // Buffer flip-flop
char * const p = buffer + (i ^ sizeof buffer / 2);
if (write(char_fd, p, sizeof buffer / 2) < sizeof buffer / 2) return OUT_OF_MEMORY_DISK_FULL;
to_do = min(remaining, sizeof buffer / 2);
ssize_t result = read(fd, p, to_do);
if (result < to_do) return IO_ERROR;
remaining -= to_do;
}
}
if ((i & sizeof buffer / 2 - 1) != 0
&& write(char_fd, buffer + (i & sizeof buffer / 2), i & sizeof buffer / 2 - 1) < (i & sizeof buffer / 2 - 1)) return OUT_OF_MEMORY_DISK_FULL;
b->num_lines++;
if (do_syntax) {
ld_buffer_syn[ld_count].line = (char *)start_of_line;
ld_buffer_syn[ld_count].line_len = curr_pos - start_of_line;
}
else {
ld_buffer_no_syn[ld_count].line = (char *)start_of_line;
ld_buffer_no_syn[ld_count].line_len = curr_pos - start_of_line;
}
ld_count++;
if (write(ld_fd, ld_buffer, ld_count * line_desc_size) < ld_count * line_desc_size) return OUT_OF_MEMORY_DISK_FULL;
return OK;
}
int load_fd_mmap(buffer * const b, const int fd, const size_t len, char * const terminators, char_pool **cp, line_desc_pool **ldp) {
char template[16];
const int char_fd = mkstemp(strcpy(template, ".ne-mmap-XXXXXX"));
unlink(template);
const int ld_fd = mkstemp(strcpy(template, ".ne-mmap-XXXXXX"));
unlink(template);
const int line_desc_size = do_syntax ? sizeof(line_desc) : sizeof(no_syntax_line_desc);
char * char_p = MAP_FAILED, * ld_p = MAP_FAILED;
if (char_fd != -1 && ld_fd != -1) {
const int error = create_mmap_files(b, fd, char_fd, ld_fd, len, line_desc_size, terminators);
if (error) {
close(char_fd);
close(ld_fd);
return error;
}
char_p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, char_fd, 0);
ld_p = mmap(NULL, b->num_lines * line_desc_size, PROT_READ | PROT_WRITE, MAP_SHARED, ld_fd, 0);
if (char_p != MAP_FAILED && ld_p != MAP_FAILED
&& (*cp = alloc_char_pool_from_memory(char_p, len)) && (*ldp = alloc_line_desc_pool_from_memory(ld_p, b->num_lines))) {
(*cp)->mapped = true;
(*ldp)->mapped = true;
b->allocated_chars = len;
/* We replace the offsets from the start of the file with actual memory
pointers, while adding the line descriptors to the buffer list. */
if (do_syntax) {
for(line_desc *ld = (line_desc *)ld_p, *ld_end = ld + b->num_lines; ld < ld_end; ld++) {
ld->line = ld->line_len ? char_p + (int64_t)ld->line : NULL;
add_tail(&b->line_desc_list, &ld->ld_node);
}
}
else {
for(no_syntax_line_desc *ld = (no_syntax_line_desc *)ld_p, *ld_end = ld + b->num_lines; ld < ld_end; ld++) {
ld->line = ld->line_len ? char_p + (int64_t)ld->line : NULL;
add_tail(&b->line_desc_list, &ld->ld_node);
}
}
return OK;
}
else {
close(char_fd);
close(ld_fd);
int error = IO_ERROR;
if (char_p != MAP_FAILED) munmap(char_p, len);
if (ld_p != MAP_FAILED) munmap(ld_p, b->num_lines * line_desc_size);
if (char_p != MAP_FAILED && ld_p != MAP_FAILED) error = OUT_OF_MEMORY;
if (*cp) free(*cp);
if (*ldp) free(*ldp);
return error;
}
}
else {
if (char_fd != -1) close(char_fd);
if (ld_fd != -1) close(ld_fd);
return IO_ERROR;
}
}
/* This function, together with insert_stream and delete_stream, is the only
way of modifying the contents of a buffer. While loading a file could have
passed through insert_stream, it would have been intolerably slow for large
files. The flexible pool structure of ne makes it possible loading the
file with a single read in a big pool. */
int load_fd_in_buffer(buffer *b, int fd) {
char terminators[] = { 0x0d, 0x0a };
if (b->opt.preserve_cr) terminators[0] = 0;
off_t len = lseek(fd, 0, SEEK_END);
if (len == 0) {
clear_buffer(b);
b->encoding = ENC_ASCII;
if (b->opt.do_undo) b->undo.last_save_step = 0;
return OK;
}
/* In the following code, we create a character pool and a line descriptor
pool either by loading into memory, or by memory mapping. We are expected
to compute the number of lines. */
char_pool *cp = NULL;
line_desc_pool *ldp = NULL;
if (len > 0) { /* Seekable */
if (lseek(fd, 0, SEEK_SET) < 0) return IO_ERROR;
block_signals();
free_buffer_contents(b);
cp = alloc_char_pool(len, fd, 0);
if (! cp) { // mmap()
const int error = load_fd_mmap(b, fd, len, terminators, &cp, &ldp);
if (error) {
clear_buffer(b);
release_signals();
return error;
}
}
}
else { /* Not seekable */
block_signals();
free_buffer_contents(b);
int64_t curr_size = START_SIZE;
len = 0;
char *pool = calloc(curr_size, 1);
for(;;) {
const int64_t res = read_safely(fd, pool + len, curr_size - len);
if (res < 0) {
free(pool);
clear_buffer(b);
release_signals();
return IO_ERROR;
}
len += res;
if (len < curr_size) break;
char * const new_pool = realloc(pool, curr_size *= 2);
if (new_pool == NULL) {
free(pool);
clear_buffer(b);
release_signals();
return OUT_OF_MEMORY;
}
pool = new_pool;
memset(pool + len, 0, curr_size - len);
}
cp = alloc_char_pool_from_memory(pool, curr_size);
if (!cp) {
free(pool);
clear_buffer(b);
release_signals();
return OUT_OF_MEMORY;
}
}
if (! ldp) { // Not mmap()'s
b->allocated_chars = cp->size;
b->free_chars = cp->size - len;
char *p = cp->pool;
/* This is the first pass on the data we just read. We count the number
of lines. If we meet a CR/LF sequence and we did not ask for binary
files, we decide the file is of CR/LF type. Note that this cannot happen
if preserve_cr is set. */
for(int64_t i = b->num_lines = 0; i < len; i++, p++)
if (!b->opt.binary && (*p == terminators[0] || *p == terminators[1]) || !*p) {
if (i < len - 1 && p[0] == '\r' && p[1] == '\n') {
b->is_CRLF = true;
p++, i++;
b->free_chars++;
}
b->num_lines++;
b->free_chars++;
}
b->num_lines++;
ldp = alloc_line_desc_pool(b->num_lines + STANDARD_LINE_INCREMENT, -1);
if (ldp) {
char *p = cp->pool, * const end = p + len;
/* This is the second pass. Here we find the actual lines, and set to
NUL the line terminators if necessary, following the same rationale of
the first pass (this is important, as b->free_chars has been computed
on the first pass). */
for(int64_t i = 0; i < b->num_lines; i++) {
line_desc *ld = do_syntax ? &((line_desc *)ldp->pool)[i] : (line_desc *)&((no_syntax_line_desc *)ldp->pool)[i];
rem(&ld->ld_node);
add_tail(&b->line_desc_list, &ld->ld_node);
char *q = p;
while(q < end && (b->opt.binary || *q != terminators[0] && *q != terminators[1]) && *q) q++;
ld->line_len = q - p;
ld->line = q - p ? p : NULL;
if (q < end) {
if (q - cp->pool < len - 1 && q[0] == '\r' && q[1] == '\n') *q++ = 0;
*q++ = 0;
}
p = q;
}
ldp->allocated_items = b->num_lines;
}
else {
free_char_pool(cp);
clear_buffer(b);
release_signals();
return OUT_OF_MEMORY_DISK_FULL;
}
}
/* Now, if UTF-8 auto-detection is enabled, we try to guess whether this
buffer is in UTF-8. */
const encoding_type encoding = detect_encoding(cp->pool, len);
if (encoding == ENC_ASCII) b->encoding = ENC_ASCII;
else {
if (b->opt.utf8auto && encoding == ENC_UTF8) b->encoding = ENC_UTF8;
else b->encoding = ENC_8_BIT;
}
/* We set correctly the offsets of the first and last character used. If no
character is used (i.e., we have a file of line feeds), the char pool is
freed. */
if (b->free_chars < b->allocated_chars) {
cp->last_used = len;
while(!cp->pool[cp->first_used]) cp->first_used++;
while(!cp->pool[--cp->last_used]);
add_head(&b->char_pool_list, &cp->cp_node);
assert_char_pool(cp);
}
else free_char_pool(cp);
add_head(&b->line_desc_pool_list, &ldp->ldp_node);
reset_position_to_sof(b);
if (b->opt.do_undo) b->undo.last_save_step = 0;
release_signals();
return OK;
}
/* Recomputes initial states for all lines in a buffer. */
void reset_syntax_states(buffer *b) {
if (b->syn) {
HIGHLIGHT_STATE next_line_state = { 0, 0, "" };
for(line_desc *ld = (line_desc *)b->line_desc_list.head; ld->ld_node.next; ld = (line_desc *)ld->ld_node.next) {
ld->highlight_state = next_line_state;
next_line_state = parse(b->syn, ld, next_line_state, b->encoding == ENC_UTF8);
}
b->attr_len = -1;
}
}
/* Ensures that the attribute buffer of this buffer is large enough. */
void ensure_attr_buf(buffer * const b, const int64_t capacity) {
if (capacity == 0) return;
/* attr_buf already exists? */
if (!b->attr_buf) {
b->attr_size = capacity;
b->attr_buf = malloc(b->attr_size * sizeof *b->attr_buf);
}
else if (capacity > b->attr_size) {
b->attr_size = capacity;
b->attr_buf = realloc(b->attr_buf, b->attr_size * sizeof *b->attr_buf);
}
}
/* Here we save a buffer to a given file. If no file is specified, the
buffer filename field is used. The is_modified flag is set to 0,
and the mtime is updated. */
int save_buffer_to_file(buffer *b, const char *name) {
line_desc *ld = (line_desc *)b->line_desc_list.head;
if (!b) return ERROR;
assert_buffer(b);
if (b->opt.read_only) return DOCUMENT_IS_READ_ONLY;
if (name == NULL) name = b->filename;
if (!name) return ERROR;
name = tilde_expand(name);
if (is_directory(name)) return FILE_IS_DIRECTORY;
if (is_migrated(name)) return FILE_IS_MIGRATED;
block_signals();
int error = OK;
const int fd = open(name, WRITE_FLAGS, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (fd >= 0) {
/* If we can allocate SAVE_BLOCK_LEN bytes, we will use
them as a buffer for our saves. */
char * const p = malloc(SAVE_BLOCK_LEN + 1);
if (p) {
/* used keeps track of the number of bytes used in the buffer. l, len
specify the pointer to the block of characters to save, and
its length. In case of very long lines, or buffer border crossing,
they could point in the middle of a line descriptor. */
int64_t used = 0, len;
char *l;
while(ld->ld_node.next) {
l = ld->line;
len = ld->line_len;
while(len > 0) {
if (SAVE_BLOCK_LEN - used > len) {
memcpy(p + used, l, len);
used += len;
len = 0;
}
else {
memcpy(p + used, l, SAVE_BLOCK_LEN - used);
len -= SAVE_BLOCK_LEN - used;
l += SAVE_BLOCK_LEN - used;
used = 0;
if (write(fd, p, SAVE_BLOCK_LEN) < SAVE_BLOCK_LEN) {
error = CANNOT_SAVE_DISK_FULL;
break;
}
}
}
if (error) break;
ld = (line_desc *)ld->ld_node.next;
/* Note that the two previous blocks never leave used == SAVE_BLOCK_LEN.
Thus, we can always assume there are two free bytes at p+used. */
if (ld->ld_node.next) {
if (b->opt.binary) p[used++] = 0;
else {
if (b->is_CRLF) p[used++] = '\r';
p[used++] = '\n';
}
}
if (used >= SAVE_BLOCK_LEN) {
if (write(fd, p, used) < used) {
error = IO_ERROR;
break;
}
else used = 0;
}
}
if (!error && used && write(fd, p, used) < used) error = IO_ERROR;
free(p);
}
else {
/* If the buffer is not available, just save line by line. */
while(ld->ld_node.next) {
if (ld->line) {
if (write(fd, ld->line, ld->line_len) < ld->line_len) {
error = IO_ERROR;
break;
}
}
ld = (line_desc *)ld->ld_node.next;
if (ld->ld_node.next) {
if (!b->opt.binary && b->is_CRLF && write(fd, "\r", 1) < 1) {
error = IO_ERROR;
break;
}
if (write(fd, b->opt.binary ? "\0" : "\n", 1) < 1) {
error = IO_ERROR;
break;
}
}
}
}
if (close(fd)) error = IO_ERROR;
if (error == OK) b->is_modified = 0;
b->mtime = file_mod_time(name);
}
else error = CANT_OPEN_FILE;
release_signals();
return error;
}
/* Autosaves a given buffer. If the buffer has a name, a '#' is prefixed to
it. If the buffer has no name, a fake name is generated using the PID of ne
and the pointer to the buffer structure. This ensures uniqueness. Autosave
never writes on the original file, also because it can be called during an
emergency exit caused by a signal. */
void auto_save(buffer *b) {
if (b->is_modified) {
char *p;
if (b->filename) {
if (p = malloc(strlen(file_part(b->filename)) + 2)) {
strcpy(p, "#");
strcat(p, file_part(b->filename));
}
}
else if (p = malloc(MAX_INT_LEN * 2)) sprintf(p, "%p.%x", b, getpid());
save_buffer_to_file(b, p);
free(p);
}
}
ne-3.3.4/src/clips.c 0000664 0000000 0000000 00000046457 14751164310 0014212 0 ustar 00root root 0000000 0000000 /* Clip handling functions.
Copyright (C) 1993-1998 Sebastiano Vigna
Copyright (C) 1999-2025 Todd M. Lewis and Sebastiano Vigna
This file is part of ne, the nice editor.
This library 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 library 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 . */
#include "ne.h"
#include "support.h"
/* A clip is a numbered node in the global clip list. The contents of the clip
are handled through the stream functions contained in streams.c.
At creation time, a clip is marked with an encoding. Clips, of course, may
be pasted only in buffers with a compatible encoding.
Note that pasting a clip in an ASCII buffer may change its encoding.
*/
/* Allocates a clip descriptor. */
clip_desc *alloc_clip_desc(int n, int64_t size) {
assert(n >= 0);
assert(size >= 0);
clip_desc * const cd = calloc(1, sizeof(clip_desc));
if (cd) {
cd->n = n;
if (cd->cs = alloc_char_stream(size)) return cd;
free(cd);
}
return NULL;
}
/* Reallocates a clip descriptor of the given size. If cd is NULL, this is
equivalent to calling alloc_clip_desc. */
clip_desc *realloc_clip_desc(clip_desc *cd, int n, int64_t size) {
assert(n >= 0);
assert(size >= 0);
if (!cd) return alloc_clip_desc(n, size);
assert_clip_desc(cd);
if (cd->n != n) return NULL;
char_stream * const cs = realloc_char_stream(cd->cs, size);
if (cs) {
cd->cs = cs;
return cd;
}
return NULL;
}
/* Frees a clip descriptor. */
void free_clip_desc(clip_desc *cd) {
if (!cd) return;
assert_clip_desc(cd);
free_char_stream(cd->cs);
free(cd);
}
/* Scans the global clip list, searching for a specific numbered clip. Returns
NULL on failure. */
clip_desc *get_nth_clip(int n) {
for(clip_desc *cd = (clip_desc *)clips.head; cd->cd_node.next; cd = (clip_desc *)cd->cd_node.next) {
assert_clip_desc(cd);
if (cd->n == n) return cd;
}
return NULL;
}
/* Copies the characters between the cursor and the block marker of the given
buffer to the nth clip. If the cut flag is true, the characters are also
removed from the text. The code scans the text two times: the first time in
order to determine the exact length of the text, the second time in order to
actually copy it. */
int copy_to_clip(buffer *b, int n, bool cut) {
if (!b->marking) return MARK_BLOCK_FIRST;
if (b->block_start_line >= b->num_lines) return MARK_OUT_OF_BUFFER;
/* If the mark and the cursor are on the same line and on the same position
(or both beyond the line length), we can't copy anything. */
line_desc *ld = b->cur_line_desc;
clip_desc *cd = get_nth_clip(n);
int64_t y = b->cur_line;
if (y == b->block_start_line &&
(b->cur_pos == b->block_start_pos ||
b->cur_pos >= ld->line_len && b->block_start_pos >= ld->line_len)) {
clip_desc * const new_cd = realloc_clip_desc(cd, n, 0);
if (!new_cd) return OUT_OF_MEMORY;
if (!cd) add_head(&clips, &new_cd->cd_node);
return OK;
}
/* We have two different loops for direct or inverse copying. Making this
conditional code would be cumbersome, awkward, and definitely inefficient. */
bool chaining;
char *p = NULL;
if (y > b->block_start_line || y == b->block_start_line && b->cur_pos > b->block_start_pos) {
/* mark before/above cursor */
int64_t clip_len = 0;
chaining = false;
for(int pass = 0; pass < 2; pass++) {
ld = b->cur_line_desc;
for(int64_t i = y; i >= b->block_start_line; i--) {
int64_t start_pos = 0;
if (i == b->block_start_line) {
if (!pass && cut && ld->line_len < b->block_start_pos) {
if (!chaining) {
chaining = true;
start_undo_chain(b);
}
const int64_t bsp = b->block_start_pos; /* because the mark will move when we insert_spaces()! */
insert_spaces(b, ld, i, ld->line_len, b->block_start_pos - ld->line_len);
b->block_start_pos = bsp;
}
start_pos = min(ld->line_len, b->block_start_pos);
}
const int64_t end_pos = i == y ? min(ld->line_len, b->cur_pos) : ld->line_len;
const int64_t len = end_pos - start_pos;
if (pass) {
assert(!(len != 0 && ld->line == NULL));
if (i != y) *--p = 0;
p -= len;
if (ld->line) memcpy(p, ld->line + start_pos, len);
}
else clip_len += len + (i != y);
ld = (line_desc *)ld->ld_node.prev;
}
if (pass) {
cd->cs->len = clip_len;
set_stream_encoding(cd->cs, b->encoding);
assert_clip_desc(cd);
if (cut) {
goto_line_pos(b, b->block_start_line, b->block_start_pos);
delete_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos, clip_len);
update_syntax_states_delay(b, b->cur_line_desc, NULL);
}
if (chaining) end_undo_chain(b);
return OK;
}
clip_desc * const new_cd = realloc_clip_desc(cd, n, clip_len);
if (!new_cd) {
if (chaining) end_undo_chain(b);
return OUT_OF_MEMORY;
}
if (!cd) add_head(&clips, &new_cd->cd_node);
cd = new_cd;
p = cd->cs->stream + clip_len;
}
}
else {
/* mark after cursor */
int64_t clip_len = 0;
chaining = false;
for(int pass = 0; pass < 2; pass++) {
ld = b->cur_line_desc;
for(int64_t i = y; i <= b->block_start_line; i++) {
int64_t start_pos = 0;
if (i == y) {
if (!pass && cut && b->cur_pos > ld->line_len) {
if (!chaining) {
chaining = true;
start_undo_chain(b);
}
insert_spaces(b, ld, i, ld->line_len, b->cur_pos - ld->line_len);
}
start_pos = b->cur_pos > ld->line_len ? ld->line_len : b->cur_pos;
}
const int64_t end_pos = i == b->block_start_line ? min(b->block_start_pos, ld->line_len) : ld->line_len;
const int64_t len = end_pos - start_pos;
if (pass) {
assert(!(len != 0 && ld->line == NULL));
if (ld->line) memcpy(p, ld->line + start_pos, len);
p += len;
if (i != b->block_start_line) *(p++) = 0;
}
else clip_len += len + (i != y);
ld = (line_desc *)ld->ld_node.next;
}
if (pass) {
cd->cs->len = clip_len;
set_stream_encoding(cd->cs, b->encoding);
assert_clip_desc(cd);
if (cut) delete_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos, clip_len);
update_syntax_states_delay(b, b->cur_line_desc, NULL);
if (chaining) end_undo_chain(b);
return OK;
}
clip_desc * const new_cd = realloc_clip_desc(cd, n, clip_len);
if (!new_cd) {
if (chaining) end_undo_chain(b);
return OUT_OF_MEMORY;
}
if (!cd) add_head(&clips, &new_cd->cd_node);
cd = new_cd;
p = cd->cs->stream;
}
}
if (chaining) end_undo_chain(b);
return OK;
}
/* Simply erases a block, without putting it in a clip. Calls
update_syntax_states_delay() if update is true. */
int erase_block(buffer *b, const bool update) {
if (!b->marking) return MARK_BLOCK_FIRST;
if (b->block_start_line >= b->num_lines) return MARK_OUT_OF_BUFFER;
int64_t y = b->cur_line, erase_len = 0;
line_desc *ld = b->cur_line_desc;
if (y == b->block_start_line &&
(b->cur_pos == b->block_start_pos ||
b->cur_pos >= ld->line_len && b->block_start_pos >= ld->line_len))
return OK;
bool chaining = false;
if (y > b->block_start_line || y == b->block_start_line && b->cur_pos > b->block_start_pos) {
for(int64_t i = y; i >= b->block_start_line; i--) {
int64_t start_pos = 0;
if (i == b->block_start_line) {
if (ld->line_len < b->block_start_pos) {
if (!chaining) {
chaining = true;
start_undo_chain(b);
}
const int64_t bsp = b->block_start_pos; /* because the mark will move when we insert_spaces()! */
insert_spaces(b, ld, i, ld->line_len, b->block_start_pos - ld->line_len);
b->block_start_pos = bsp;
}
start_pos = min(ld->line_len, b->block_start_pos);
}
const int64_t end_pos = i == y ? min(ld->line_len, b->cur_pos) : ld->line_len;
const int64_t len = end_pos - start_pos;
erase_len += len + 1;
ld = (line_desc *)ld->ld_node.prev;
}
goto_line_pos(b, b->block_start_line, b->block_start_pos);
}
else {
for(int64_t i = y; i <= b->block_start_line; i++) {
int64_t start_pos = 0;
if (i == y) {
if (b->cur_pos > ld->line_len) {
if (!chaining) {
chaining = true;
start_undo_chain(b);
}
insert_spaces(b, ld, i, ld->line_len, b->cur_pos - ld->line_len);
}
start_pos = b->cur_pos > ld->line_len ? ld->line_len : b->cur_pos;
}
const int64_t end_pos = i == b->block_start_line ? min(b->block_start_pos, ld->line_len) : ld->line_len;
const int64_t len = end_pos - start_pos;
erase_len += len + 1;
ld = (line_desc *)ld->ld_node.next;
}
}
delete_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos, erase_len - 1);
if (chaining) end_undo_chain(b);
if (update) update_syntax_states_delay(b, b->cur_line_desc, NULL);
return OK;
}
/* Pastes a clip into a buffer. Since clips are streams, the operation is
definitely straightforward. */
int paste_to_buffer(buffer *b, int n) {
clip_desc * const cd = get_nth_clip(n);
if (!cd) return CLIP_DOESNT_EXIST;
if (!cd->cs->len) return OK;
if (cd->cs->encoding == ENC_ASCII || b->encoding == ENC_ASCII || cd->cs->encoding == b->encoding) {
line_desc * const ld = b->cur_line_desc, * const end_ld = (line_desc *)b->cur_line_desc->ld_node.next;
if (b->encoding == ENC_ASCII) b->encoding = cd->cs->encoding;
b->bookmark[PASTE_START_BOOKMARK].pos = b->cur_pos;
b->bookmark[PASTE_START_BOOKMARK].line = b->cur_line;
b->bookmark[PASTE_START_BOOKMARK].cur_y = b->cur_y;
b->bookmark_mask |= (1 << PASTE_START_BOOKMARK);
b->bookmark[PASTE_END_BOOKMARK].pos = next_pos(b->cur_line_desc->line, b->cur_pos, b->encoding);
b->bookmark[PASTE_END_BOOKMARK].line = b->cur_line;
b->bookmark_mask |= (1 << PASTE_END_BOOKMARK);
start_undo_chain(b);
if (b->cur_pos > ld->line_len)
insert_spaces(b, ld, b->cur_line, ld->line_len, b->win_x + b->cur_x - calc_width(ld, ld->line_len, b->opt.tab_size, b->encoding));
insert_stream(b, ld, b->cur_line, b->cur_pos, cd->cs->stream, cd->cs->len);
end_undo_chain(b);
b->bookmark[PASTE_END_BOOKMARK].pos = prev_pos(nth_line_desc(b, b->bookmark[PASTE_END_BOOKMARK].line)->line, b->bookmark[PASTE_END_BOOKMARK].pos, b->encoding);
b->bookmark[PASTE_END_BOOKMARK].cur_y = min(ne_lines - 2, b->bookmark[PASTE_START_BOOKMARK].cur_y + b->bookmark[PASTE_END_BOOKMARK].line - b->bookmark[PASTE_START_BOOKMARK].line);
assert(ld == b->cur_line_desc);
update_syntax_states_delay(b, ld, end_ld);
return OK;
}
return INCOMPATIBLE_CLIP_ENCODING;
}
/* Works like copy_to_clip(), but the region to copy is the rectangle defined
by the cursor and the marker. Same comments apply. Note that in case of a
cut we use start_undo_chain() in order to make the various deletions a
single undo operation. */
int copy_vert_to_clip(buffer *b, int n, bool cut) {
if (!b->marking) return MARK_BLOCK_FIRST;
if (b->block_start_line >= b->num_lines) return MARK_OUT_OF_BUFFER;
int64_t y = b->cur_line;
line_desc *ld = b->cur_line_desc;
clip_desc *cd = get_nth_clip(n);
if (b->cur_pos == b->block_start_pos ||
y == b->block_start_line && b->cur_pos >= ld->line_len &&
b->block_start_pos >= ld->line_len) {
clip_desc * const new_cd = realloc_clip_desc(cd, n, 0);
if (!new_cd) return OUT_OF_MEMORY;
set_stream_encoding(new_cd->cs, ENC_ASCII);
if (!cd) add_head(&clips, &new_cd->cd_node);
return OK;
}
int64_t start_x = calc_width(nth_line_desc(b, b->block_start_line), b->block_start_pos, b->opt.tab_size, b->encoding);
int64_t end_x = b->win_x + b->cur_x;
if (end_x < start_x) {
const uint64_t t = start_x;
start_x = end_x;
end_x = t;
}
const int64_t cur_pos = b->cur_pos;
if (cut) {
b->cur_pos = -1;
start_undo_chain(b);
}
char *p = NULL;
if (y > b->block_start_line) {
int64_t clip_len = 0;
for(int pass = 0; pass < 2; pass++) {
ld = b->cur_line_desc;
for(int64_t i = y; i >= b->block_start_line; i--) {
const int64_t start_pos = calc_pos(ld, start_x, b->opt.tab_size, b->encoding);
const int64_t len = calc_pos(ld, end_x, b->opt.tab_size, b->encoding) - start_pos;
if (pass) {
*--p = 0;
p -= len;
if (len) memcpy(p, ld->line + start_pos, len);
if (cut) delete_stream(b, ld, i, start_pos, len);
}
else clip_len += len + 1;
ld = (line_desc *)ld->ld_node.prev;
}
if (pass) {
cd->cs->len = clip_len;
set_stream_encoding(cd->cs, b->encoding);
assert_clip_desc(cd);
if (cut) {
update_syntax_states_delay(b, (line_desc *)ld->ld_node.next, b->cur_line_desc);
goto_line_pos(b, min(b->block_start_line, b->cur_line), min(b->block_start_pos, cur_pos));
end_undo_chain(b);
}
return OK;
}
clip_desc * const new_cd = realloc_clip_desc(cd, n, clip_len);
if (!new_cd) return OUT_OF_MEMORY;
if (!cd) add_head(&clips, &new_cd->cd_node);
cd = new_cd;
p = cd->cs->stream + clip_len;
}
}
else {
int64_t clip_len = 0;
for(int pass = 0; pass < 2; pass++) {
ld = b->cur_line_desc;
for(int64_t i = y; i <= b->block_start_line; i++) {
const int64_t start_pos = calc_pos(ld, start_x, b->opt.tab_size, b->encoding);
const int64_t len = calc_pos(ld, end_x, b->opt.tab_size, b->encoding) - start_pos;
if (pass) {
if (len) memcpy(p, ld->line + start_pos, len);
p += len;
*(p++) = 0;
if (cut) delete_stream(b, ld, i, start_pos, len);
}
else clip_len += len + 1;
ld = (line_desc *)ld->ld_node.next;
}
if (pass) {
cd->cs->len = clip_len;
set_stream_encoding(cd->cs, b->encoding);
assert_clip_desc(cd);
if (cut) {
update_syntax_states_delay(b, b->cur_line_desc, (line_desc *)ld->ld_node.prev);
goto_line_pos(b, min(b->block_start_line, b->cur_line), min(b->block_start_pos, cur_pos));
end_undo_chain(b);
}
return OK;
}
clip_desc * const new_cd = realloc_clip_desc(cd, n, clip_len);
if (!new_cd) return OUT_OF_MEMORY;
if (!cd) add_head(&clips, &new_cd->cd_node);
cd = new_cd;
p = cd->cs->stream;
}
}
if (cut) end_undo_chain(b);
return OK;
}
/* Simply erases a vertical block, without putting it in a clip. Calls
update_syntax_states_delay() if update is true. */
int erase_vert_block(buffer *b, const bool update) {
if (!b->marking) return MARK_BLOCK_FIRST;
if (b->block_start_line >= b->num_lines) return MARK_OUT_OF_BUFFER;
int64_t y = b->cur_line;
line_desc *ld = b->cur_line_desc;
if (b->cur_pos == b->block_start_pos ||
y == b->block_start_line && b->cur_pos >= ld->line_len &&
b->block_start_pos >= ld->line_len)
return OK;
const int64_t cur_pos = b->cur_pos;
b->cur_pos = -1;
int64_t start_x = calc_width(nth_line_desc(b, b->block_start_line), b->block_start_pos, b->opt.tab_size, b->encoding);
int64_t end_x = b->win_x + b->cur_x;
if (end_x < start_x) {
const uint64_t t = start_x;
start_x = end_x;
end_x = t;
}
start_undo_chain(b);
if (y > b->block_start_line) {
for(int64_t i = y; i >= b->block_start_line; i--) {
const int64_t start_pos = calc_pos(ld, start_x, b->opt.tab_size, b->encoding);
const int64_t len = calc_pos(ld, end_x, b->opt.tab_size, b->encoding) - start_pos;
delete_stream(b, ld, i, start_pos, len);
ld = (line_desc *)ld->ld_node.prev;
}
if (update) update_syntax_states_delay(b, (line_desc *)ld->ld_node.next, b->cur_line_desc);
}
else {
for(int64_t i = y; i <= b->block_start_line; i++) {
const int64_t start_pos = calc_pos(ld, start_x, b->opt.tab_size, b->encoding);
const int64_t len = calc_pos(ld, end_x, b->opt.tab_size, b->encoding)-start_pos;
delete_stream(b, ld, i, start_pos, len);
ld = (line_desc *)ld->ld_node.next;
}
if (update) update_syntax_states_delay(b, b->cur_line_desc, (line_desc *)ld->ld_node.prev);
}
end_undo_chain(b);
goto_line_pos(b, min(b->block_start_line, b->cur_line), min(b->block_start_pos, cur_pos));
return OK;
}
/* Performs a vertical paste. It has to be done via an insert_stream() for each
string of the clip. Again, the undo linking feature makes all these
operations a single undo step. */
int paste_vert_to_buffer(buffer *b, int n) {
line_desc * ld = b->cur_line_desc;
clip_desc * const cd = get_nth_clip(n);
if (!cd) return CLIP_DOESNT_EXIST;
if (!cd->cs->len) return OK;
if (cd->cs->encoding != ENC_ASCII && b->encoding != ENC_ASCII && cd->cs->encoding != b->encoding) return INCOMPATIBLE_CLIP_ENCODING;
if (b->encoding == ENC_ASCII) b->encoding = cd->cs->encoding;
char * const stream = cd->cs->stream;
char *p = stream;
const int64_t stream_len = cd->cs->len;
const uint64_t x = b->cur_x + b->win_x;
int64_t line = b->cur_line;
start_undo_chain(b);
b->bookmark[PASTE_START_BOOKMARK].pos = b->cur_pos;
b->bookmark[PASTE_START_BOOKMARK].line = b->cur_line;
b->bookmark[PASTE_START_BOOKMARK].cur_y = b->cur_y;
b->bookmark_mask |= (1 << PASTE_START_BOOKMARK);
while(p - stream < stream_len) {
if (!ld->ld_node.next) {
insert_one_line(b, (line_desc *)ld->ld_node.prev, line - 1, ((line_desc *)ld->ld_node.prev)->line_len);
ld = (line_desc *)ld->ld_node.prev;
}
const int64_t len = strnlen_ne(p, stream_len - (p - stream));
if (len) {
uint64_t pos, n;
for(n = pos = 0; pos < ld->line_len && n < x; pos = next_pos(ld->line, pos, b->encoding)) {
if (ld->line[pos] == '\t') n += b->opt.tab_size - n % b->opt.tab_size;
else n += get_char_width(&ld->line[pos], b->encoding);
}
b->bookmark[PASTE_END_BOOKMARK].line = line;
b->bookmark_mask |= (1 << PASTE_END_BOOKMARK);
if (pos == ld->line_len && n < x) {
/* We miss x - n characters after the end of the line. */
insert_spaces(b, ld, line, ld->line_len, x - n);
b->bookmark[PASTE_END_BOOKMARK].pos = next_pos(ld->line, ld->line_len, b->encoding);
insert_stream(b, ld, line, ld->line_len, p, len);
}
else {
b->bookmark[PASTE_END_BOOKMARK].pos = next_pos(ld->line, pos, b->encoding);
insert_stream(b, ld, line, pos, p, len);
}
}
p += len + 1;
ld = (line_desc *)ld->ld_node.next;
line++;
}
b->bookmark[PASTE_END_BOOKMARK].pos = prev_pos(((line_desc *)ld->ld_node.prev)->line, b->bookmark[PASTE_END_BOOKMARK].pos, b->encoding);
b->bookmark[PASTE_END_BOOKMARK].cur_y = min(ne_lines - 2, b->bookmark[PASTE_START_BOOKMARK].cur_y + b->bookmark[PASTE_END_BOOKMARK].line - b->bookmark[PASTE_START_BOOKMARK].line);
end_undo_chain(b);
update_syntax_states_delay(b, b->cur_line_desc, ld);
return OK;
}
/* Loads a clip. It is just a load_stream, plus an insertion in the clip
list. If preserve_cr is true, CRs are preserved. */
int load_clip(int n, const char *name, const bool preserve_cr, const bool binary) {
clip_desc *cd = get_nth_clip(n);
if (!cd) {
if (!(cd = alloc_clip_desc(n, 0))) return OUT_OF_MEMORY;
add_head(&clips, &cd->cd_node);
}
const int error = load_stream(cd->cs, name, preserve_cr, binary) ? OK : CANT_OPEN_FILE;
if (error == OK) set_stream_encoding(cd->cs, ENC_ASCII);
return error;
}
/* Saves a clip to a file. If CRLF is true, the clip is saved with CR/LF pairs
as line terminators. */
int save_clip(int n, const char *name, const bool CRLF, const bool binary) {
clip_desc * const cd = get_nth_clip(n);
if (!cd) return CLIP_DOESNT_EXIST;
return save_stream(cd->cs, name, CRLF, binary);
}
ne-3.3.4/src/cm.c 0000664 0000000 0000000 00000022223 14751164310 0013460 0 ustar 00root root 0000000 0000000 /* Optimal cursor motion functions.
Based primarily on public domain code written by Chris Torek.
Originally part of GNU Emacs. Vastly edited and modified for use within ne.
Copyright (C) 1985, 1995 Free Software Foundation, Inc.
Copyright (C) 1993-1998 Sebastiano Vigna
Copyright (C) 1999-2025 Todd M. Lewis and Sebastiano Vigna
This file is part of ne, the nice editor.
This library 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 library 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 . */
#include
#include
#ifndef NE_TERMCAP
#include
#include
#else
#include "info2cap.h"
#endif
#include "cm.h"
#define BIG 9999
int cost; /* sums up costs */
/* This function is used in place of putchar() in tputs() so can we can
computed the padded length of a capability string. Note that they should
be putchar()-like, so we have to care about the returned value. */
int evalcost (int c) {
cost++;
return c;
}
/* This function is used in tputs(). */
int cmputc (int c) {
return putchar(c & 0x7f);
}
/* Terminals with magicwrap (xn) don't all behave identically. The VT100
leaves the cursor in the last column but will wrap before printing the next
character. I hear that the Concept terminal does the wrap immediately but
ignores the next newline it sees. And some terminals just have buggy
firmware, and think that the cursor is still in limbo if we use direct
cursor addressing from the phantom column. The only guaranteed safe thing
to do is to emit a CRLF immediately after we reach the last column; this
takes us to a known state. */
void cmcheckmagic () {
if (curX == ScreenCols) {
assert(MagicWrap && curY < ScreenRows - 1);
putchar ('\r');
putchar ('\n');
curX = 0;
curY++;
}
}
/* (Re)Initialises the cost factors, given the output speed of the terminal in
the variable ospeed. (Note: this holds B300, B9600, etc -- ie stuff out of
.) */
void cmcostinit () {
char *p;
#define COST(x,e) (x ? (cost = 0, tputs (x, 1, e), cost) : BIG)
#define CMCOST(x,e) ((x == 0) ? BIG : (p = tgoto(x, 0, 0), COST(p, e)))
Wcm.cc_up = COST (Wcm.cm_up, evalcost);
Wcm.cc_down = COST (Wcm.cm_down, evalcost);
Wcm.cc_left = COST (Wcm.cm_left, evalcost);
Wcm.cc_right = COST (Wcm.cm_right, evalcost);
Wcm.cc_home = COST (Wcm.cm_home, evalcost);
Wcm.cc_cr = COST (Wcm.cm_cr, evalcost);
Wcm.cc_ll = COST (Wcm.cm_ll, evalcost);
Wcm.cc_tab = Wcm.cm_tabwidth ? COST (Wcm.cm_tab, evalcost) : BIG;
/* These last three are actually minimum costs. When (if) they are
candidates for the least-cost motion, the real cost is computed. (Note
that "0" is the assumed to generate the minimum cost. While this is not
necessarily true, I have yet to see a terminal for which is not; all the
terminals that have variable-cost cursor motion seem to take straight
numeric values. --ACT) */
Wcm.cc_abs = CMCOST (Wcm.cm_abs, evalcost);
Wcm.cc_habs = CMCOST (Wcm.cm_habs, evalcost);
Wcm.cc_vabs = CMCOST (Wcm.cm_vabs, evalcost);
#undef CMCOST
#undef COST
}
/* Calculates the cost to move from (srcy, srcx) to (dsty, dstx) using up and
down, and left and right, motions, and tabs. If doit is set actually perform
the motion. */
static int calccost (int srcy, int srcx, int dsty, int dstx, int doit) {
register int deltay, deltax, c, totalcost;
int ntabs, n2tabs, tabx, tab2x, tabcost;
register char *p;
/* If have just wrapped on a terminal with xn, don't believe the cursor
position: give up here and force use of absolute positioning. */
if (curX == Wcm.cm_cols) goto fail;
totalcost = 0;
if ((deltay = dsty - srcy) == 0) goto x;
if (deltay < 0) p = Wcm.cm_up, c = Wcm.cc_up, deltay = -deltay;
else p = Wcm.cm_down, c = Wcm.cc_down;
if (c == BIG) { /* caint get thar from here */
if (doit) printf ("OOPS");
return c;
}
totalcost = c * deltay;
if (doit) while (deltay-- != 0) tputs (p, 1, cmputc);
x:
if ((deltax = dstx - srcx) == 0) goto done;
if (deltax < 0) {
p = Wcm.cm_left, c = Wcm.cc_left, deltax = -deltax;
goto dodelta; /* skip all the tab junk */
}
/* Tabs (the toughie) */
if (Wcm.cc_tab >= BIG || !Wcm.cm_usetabs) goto olddelta; /* forget it! */
/* ntabs is # tabs towards but not past dstx; n2tabs is one more (ie past
dstx), but this is only valid if that is not past the right edge of the
screen. We can check that at the same time as we figure out where we
would be if we use the tabs (which we will put into tabx (for ntabs) and
tab2x (for n2tabs)). */
ntabs = (deltax + srcx % Wcm.cm_tabwidth) / Wcm.cm_tabwidth;
n2tabs = ntabs + 1;
tabx = (srcx / Wcm.cm_tabwidth + ntabs) * Wcm.cm_tabwidth;
tab2x = tabx + Wcm.cm_tabwidth;
if (tab2x >= Wcm.cm_cols) n2tabs = 0; /* too far (past edge) */
/* Now set tabcost to the cost for using ntabs, and c to the cost for using
n2tabs, then pick the minimum. */
/* cost for ntabs - cost for right motion */
tabcost = ntabs ? ntabs * Wcm.cc_tab + (dstx - tabx) * Wcm.cc_right : BIG;
/* cost for n2tabs - cost for left motion */
c = n2tabs ? n2tabs * Wcm.cc_tab + (tab2x - dstx) * Wcm.cc_left : BIG;
if (c < tabcost) /* then cheaper to overshoot & back up */
ntabs = n2tabs, tabcost = c, tabx = tab2x;
if (tabcost >= BIG) /* caint use tabs */
goto newdelta;
/* See if tabcost is less than just moving right */
if (tabcost < (deltax * Wcm.cc_right)) {
totalcost += tabcost; /* use the tabs */
if (doit) while (ntabs-- != 0) tputs (Wcm.cm_tab, 1, cmputc);
srcx = tabx;
}
/* Now might as well just recompute the delta. */
newdelta:
if ((deltax = dstx - srcx) == 0) goto done;
olddelta:
if (deltax > 0) p = Wcm.cm_right, c = Wcm.cc_right;
else p = Wcm.cm_left, c = Wcm.cc_left, deltax = -deltax;
dodelta:
if (c == BIG) { /* caint get thar from here */
fail:
if (doit)
printf ("OOPS");
return BIG;
}
totalcost += c * deltax;
if (doit) while (deltax-- != 0) tputs (p, 1, cmputc);
done:
return totalcost;
}
#define USEREL 0
#define USEHOME 1
#define USELL 2
#define USECR 3
void cmgoto (int row, int col) {
int homecost, crcost, llcost, relcost, directcost;
int use = USEREL;
char *p, *dcm;
/* First the degenerate case */
if (row == curY && col == curX) return; /* already there */
if (curY >= 0 && curX >= 0) {
/* We may have quick ways to go to the upper-left, bottom-left,
start-of-line, or start-of-next-line. Or it might be best to start
where we are. Examine the options, and pick the cheapest. */
relcost = calccost (curY, curX, row, col, 0);
use = USEREL;
if ((homecost = Wcm.cc_home) < BIG) homecost += calccost (0, 0, row, col, 0);
if (homecost < relcost) relcost = homecost, use = USEHOME;
if ((llcost = Wcm.cc_ll) < BIG) llcost += calccost (Wcm.cm_rows - 1, 0, row, col, 0);
if (llcost < relcost) relcost = llcost, use = USELL;
if ((crcost = Wcm.cc_cr) < BIG) {
if (Wcm.cm_autolf)
if (curY + 1 >= Wcm.cm_rows) crcost = BIG;
else crcost += calccost (curY + 1, 0, row, col, 0);
else crcost += calccost (curY, 0, row, col, 0);
}
if (crcost < relcost) relcost = crcost, use = USECR;
directcost = Wcm.cc_abs, dcm = Wcm.cm_abs;
if (row == curY && Wcm.cc_habs < BIG) directcost = Wcm.cc_habs, dcm = Wcm.cm_habs;
else if (col == curX && Wcm.cc_vabs < BIG)
directcost = Wcm.cc_vabs, dcm = Wcm.cm_vabs;
}
else {
directcost = 0, relcost = 100000;
dcm = Wcm.cm_abs;
}
/* In the following comparison, the = in <= is because when the costs are
the same, it looks nicer (I think) to move directly there. */
if (directcost <= relcost) {
/* compute REAL direct cost */
cost = 0;
p = dcm == Wcm.cm_habs ? tgoto (dcm, row, col) : tgoto (dcm, col, row);
tputs (p, 1, evalcost);
if (cost <= relcost) { /* really is cheaper */
tputs (p, 1, cmputc);
curY = row, curX = col;
return;
}
}
switch (use) {
case USEHOME:
tputs (Wcm.cm_home, 1, cmputc);
curY = 0, curX = 0;
break;
case USELL:
tputs (Wcm.cm_ll, 1, cmputc);
curY = Wcm.cm_rows - 1, curX = 0;
break;
case USECR:
tputs (Wcm.cm_cr, 1, cmputc);
if (Wcm.cm_autolf) curY++;
curX = 0;
break;
}
(void) calccost (curY, curX, row, col, 1);
curY = row, curX = col;
}
/* Clears out all terminal info. Used before copying into it the info on the
actual terminal. */
void Wcm_clear () {
memset(&Wcm, 0, sizeof Wcm);
}
/* Initialises stuff. Returns 0 if can do CM, -1 if cannot, -2 if size not
specified. */
int Wcm_init () {
if (Wcm.cm_abs) return 0;
/* Require up and left, and, if no absolute, down and right */
if (!Wcm.cm_up || !Wcm.cm_left) return - 1;
if (!Wcm.cm_abs && (!Wcm.cm_down || !Wcm.cm_right)) return - 1;
/* Check that we know the size of the screen.... */
if (Wcm.cm_rows <= 0 || Wcm.cm_cols <= 0) return - 2;
return 0;
}
ne-3.3.4/src/cm.h 0000664 0000000 0000000 00000010322 14751164310 0013462 0 ustar 00root root 0000000 0000000 /* Optimal cursor motion definitions.
Based primarily on public domain code written by Chris Torek.
Originally part of GNU Emacs. Vastly edited and modified for use within ne.
Copyright (C) 1985, 1989 Free Software Foundation, Inc.
Copyright (C) 1993-1998 Sebastiano Vigna
Copyright (C) 1999-2025 Todd M. Lewis and Sebastiano Vigna
This file is part of ne, the nice editor.
This library 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 library 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 . */
/* This structure holds everything needed to do cursor motion. */
extern struct cm {
/* Cursor position. -1 in *both* variables means the cursor
position is unknown, in order to force absolute cursor motion. */
int cm_curY, /* current row */
cm_curX; /* current column */
/* Capabilities from terminfo */
char *cm_up, /* up (up) */
*cm_down, /* down (do) */
*cm_left, /* left (bs) */
*cm_right, /* right (nd) */
*cm_home, /* home (ho) */
*cm_cr, /* carriage return (cr) */
*cm_ll, /* last line (ll) */
*cm_tab, /* tab (ta) */
*cm_backtab, /* backtab (bt) */
*cm_abs, /* absolute (cm) */
*cm_habs, /* horizontal absolute (ch) */
*cm_vabs, /* vertical absolute (cv) */
*cm_multiup, /* multiple up (UP) */
*cm_multidown, /* multiple down (DO) */
*cm_multileft, /* multiple left (LE) */
*cm_multiright; /* multiple right (RI) */
int cm_cols, /* Number of cols on screen (co) */
cm_rows, /* Number of rows on screen (li) */
cm_tabwidth; /* tab width (it) */
unsigned int
cm_autowrap:1, /* autowrap flag (am) */
cm_magicwrap:1, /* vt100s: cursor stays in last col but
will wrap if next char is printing (xn) */
cm_usetabs:1, /* if set, use tabs */
cm_autolf:1, /* \r performs a \r\n (rn) */
cm_losewrap:1; /* if reach right margin, forget cursor location */
/* Costs */
int cc_up, /* cost for up */
cc_down, /* etc */
cc_left,
cc_right,
cc_home,
cc_cr,
cc_ll,
cc_tab,
cc_backtab,
cc_abs, /* abs costs are actually min costs */
cc_habs,
cc_vabs;
} Wcm;
/* Shorthands */
#define curY Wcm.cm_curY
#define curX Wcm.cm_curX
#define Up Wcm.cm_up
#define Down Wcm.cm_down
#define Left Wcm.cm_left
#define Right Wcm.cm_right
#define Tab Wcm.cm_tab
#define BackTab Wcm.cm_backtab
#define TabWidth Wcm.cm_tabwidth
#define CR Wcm.cm_cr
#define Home Wcm.cm_home
#define LastLine Wcm.cm_ll
#define AbsPosition Wcm.cm_abs
#define ColPosition Wcm.cm_habs
#define RowPosition Wcm.cm_vabs
#define MultiUp Wcm.cm_multiup
#define MultiDown Wcm.cm_multidown
#define MultiLeft Wcm.cm_multileft
#define MultiRight Wcm.cm_multiright
#define AutoWrap Wcm.cm_autowrap
#define MagicWrap Wcm.cm_magicwrap
#define UseTabs Wcm.cm_usetabs
#define ScreenRows Wcm.cm_rows
#define ScreenCols Wcm.cm_cols
#define UpCost Wcm.cc_up
#define DownCost Wcm.cc_down
#define LeftCost Wcm.cc_left
#define RightCost Wcm.cc_right
#define HomeCost Wcm.cc_home
#define CRCost Wcm.cc_cr
#define LastLineCost Wcm.cc_ll
#define TabCost Wcm.cc_tab
#define BackTabCost Wcm.cc_backtab
#define AbsPositionCost Wcm.cc_abs
#define ColPositionCost Wcm.cc_habs
#define RowPositionCost Wcm.cc_vabs
#define MultiUpCost Wcm.cc_multiup
#define MultiDownCost Wcm.cc_multidown
#define MultiLeftCost Wcm.cc_multileft
#define MultiRightCost Wcm.cc_multiright
#define cmat(row,col) (curY = (row), curX = (col))
#define cmplus(n) \
{ \
if ((curX += (n)) >= ScreenCols && !MagicWrap) \
{ \
if (Wcm.cm_losewrap) losecursor (); \
else if (AutoWrap) curX = 0, curY++; \
else curX--; \
} \
}
#define losecursor() (curX = -1, curY = -1)
int cmputc(int);
int Wcm_init (void);
void cmcostinit (void);
void cmgoto (int row, int col);
#include "debug.h"
ne-3.3.4/src/command.c 0000664 0000000 0000000 00000106112 14751164310 0014477 0 ustar 00root root 0000000 0000000 /* Command table manipulation functions and vectors.
Copyright (C) 1993-1998 Sebastiano Vigna
Copyright (C) 1999-2025 Todd M. Lewis and Sebastiano Vigna
This file is part of ne, the nice editor.
This library 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 library 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 . */
#include "ne.h"
#include "support.h"
#include "help.h"
#include "hash.h"
#undef TABSIZE
/* The standard macro descriptor allocation dimension. */
#define STD_MACRO_DESC_SIZE 1024
/* This structure represents a command. It includes a long and a short name,
a NULL-terminated vector of help strings (of specified length) and some flags
which are related to the syntax and the semantics of the arguments. */
typedef struct {
const char *name, *short_name;
const char * const *help;
int help_len;
int flags;
} command;
#define NO_ARGS (1<<1) /* This command must be called without argument. */
#define ARG_IS_STRING (1<<2) /* The argument is a string (default is a number). */
#define IS_OPTION (1<<3) /* The command controls an option,
and can be played while exec_only_options is true. */
#define DO_NOT_RECORD (1<<4) /* Never record this command. */
#define EMPTY_STRING_OK (1<<5) /* This command can accept an empty string ("") as an argument. */
/* These macros makes the following vector more readable. */
#define HELP_LEN(x) (sizeof(x ## _HELP) / sizeof(char *) - 1)
#define NAHL(x) x ## _NAME, x ##_ABBREV, x ## _HELP, HELP_LEN(x)
/* This is the command vector. Note that the command names come from names.h,
and the help names come from help.h. This must be kept sorted. */
static const command commands[ACTION_COUNT] = {
{ NAHL(ABOUT ), NO_ARGS },
{ NAHL(ADJUSTVIEW ), ARG_IS_STRING },
{ NAHL(ALERT ), NO_ARGS },
{ NAHL(ATOMICUNDO ), ARG_IS_STRING | EMPTY_STRING_OK },
{ NAHL(AUTOCOMPLETE ), ARG_IS_STRING | EMPTY_STRING_OK },
{ NAHL(AUTOINDENT ), IS_OPTION },
{ NAHL(AUTOMATCHBRACKET), IS_OPTION },
{ NAHL(AUTOPREFS ), IS_OPTION },
{ NAHL(BACKSPACE ),0 },
{ NAHL(BEEP ), NO_ARGS },
{ NAHL(BINARY ), IS_OPTION },
{ NAHL(BRACKETEDPASTE), ARG_IS_STRING | IS_OPTION | EMPTY_STRING_OK },
{ NAHL(CAPITALIZE ),0 },
{ NAHL(CASESEARCH ), IS_OPTION },
{ NAHL(CENTER ),0 },
{ NAHL(CLEAR ), NO_ARGS },
{ NAHL(CLIPNUMBER ), IS_OPTION },
{ NAHL(CLOSEDOC ), NO_ARGS },
{ NAHL(COPY ),0 },
{ NAHL(CRLF ), IS_OPTION },
{ NAHL(CUT ),0 },
{ NAHL(DELETECHAR ),0 },
{ NAHL(DELETEEOL ), NO_ARGS },
{ NAHL(DELETELINE ),0 },
{ NAHL(DELETENEXTWORD),0 },
{ NAHL(DELETEPREVWORD),0 },
{ NAHL(DELTABS ), IS_OPTION },
{ NAHL(DOUNDO ), IS_OPTION },
{ NAHL(ERASE ),0 },
{ NAHL(ESCAPE ), DO_NOT_RECORD },
{ NAHL(ESCAPETIME ), IS_OPTION },
{ NAHL(EXEC ), ARG_IS_STRING | DO_NOT_RECORD },
{ NAHL(EXIT ), NO_ARGS },
{ NAHL(FASTGUI ), IS_OPTION },
{ NAHL(FIND ), ARG_IS_STRING },
{ NAHL(FINDREGEXP ), ARG_IS_STRING },
{ NAHL(FLAGS ), NO_ARGS | DO_NOT_RECORD },
{ NAHL(FLASH ), NO_ARGS },
{ NAHL(FREEFORM ), IS_OPTION },
{ NAHL(GOTOBOOKMARK ), ARG_IS_STRING | EMPTY_STRING_OK },
{ NAHL(GOTOCOLUMN ),0 },
{ NAHL(GOTOLINE ),0 },
{ NAHL(GOTOMARK ), NO_ARGS },
{ NAHL(HELP ), ARG_IS_STRING | DO_NOT_RECORD },
{ NAHL(HEXCODE ), IS_OPTION },
{ NAHL(INSERT ), IS_OPTION },
{ NAHL(INSERTCHAR ),0 },
{ NAHL(INSERTLINE ),0 },
{ NAHL(INSERTSTRING ), ARG_IS_STRING },
{ NAHL(INSERTTAB ),0 },
{ NAHL(KEYCODE ), DO_NOT_RECORD },
{ NAHL(LINEDOWN ),0 },
{ NAHL(LINEUP ),0 },
{ NAHL(LOADAUTOPREFS ), NO_ARGS },
{ NAHL(LOADPREFS ), ARG_IS_STRING },
{ NAHL(MACRO ), ARG_IS_STRING | DO_NOT_RECORD },
{ NAHL(MARK ), IS_OPTION },
{ NAHL(MARKVERT ), IS_OPTION },
{ NAHL(MATCHBRACKET ), NO_ARGS },
{ NAHL(MODIFIED ), IS_OPTION },
{ NAHL(MOVEBOS ), NO_ARGS },
{ NAHL(MOVEEOF ), NO_ARGS },
{ NAHL(MOVEEOL ), NO_ARGS },
{ NAHL(MOVEEOW ), ARG_IS_STRING | EMPTY_STRING_OK },
{ NAHL(MOVEINCDOWN ), NO_ARGS },
{ NAHL(MOVEINCUP ), NO_ARGS },
{ NAHL(MOVELEFT ),0 },
{ NAHL(MOVERIGHT ),0 },
{ NAHL(MOVESOF ), NO_ARGS },
{ NAHL(MOVESOL ), NO_ARGS },
{ NAHL(MOVETOS ), NO_ARGS },
{ NAHL(NAMECONVERT ), IS_OPTION },
{ NAHL(NEWDOC ), NO_ARGS },
{ NAHL(NEXTDOC ), NO_ARGS },
{ NAHL(NEXTPAGE ),0 },
{ NAHL(NEXTWORD ), ARG_IS_STRING | EMPTY_STRING_OK },
{ NAHL(NOFILEREQ ), IS_OPTION },
{ NAHL(NOP ), NO_ARGS },
{ NAHL(OPEN ), ARG_IS_STRING },
{ NAHL(OPENCLIP ), ARG_IS_STRING },
{ NAHL(OPENMACRO ), ARG_IS_STRING },
{ NAHL(OPENNEW ), ARG_IS_STRING },
{ NAHL(PAGEDOWN ),0 },
{ NAHL(PAGEUP ),0 },
{ NAHL(PARAGRAPH ),0 },
{ NAHL(PASTE ),0 },
{ NAHL(PASTEVERT ),0 },
{ NAHL(PLAY ), DO_NOT_RECORD },
{ NAHL(POPPREFS ),0 },
{ NAHL(PRESERVECR ), IS_OPTION },
{ NAHL(PREVDOC ), NO_ARGS },
{ NAHL(PREVPAGE ),0 },
{ NAHL(PREVWORD ), ARG_IS_STRING | EMPTY_STRING_OK },
{ NAHL(PUSHPREFS ), IS_OPTION },
{ NAHL(QUIT ), DO_NOT_RECORD },
{ NAHL(READONLY ), IS_OPTION },
{ NAHL(RECORD ), IS_OPTION | DO_NOT_RECORD },
{ NAHL(REDO ),0 },
{ NAHL(REFRESH ), NO_ARGS },
{ NAHL(REPEATLAST ), ARG_IS_STRING | EMPTY_STRING_OK },
{ NAHL(REPLACE ), ARG_IS_STRING | EMPTY_STRING_OK },
{ NAHL(REPLACEALL ), ARG_IS_STRING | EMPTY_STRING_OK },
{ NAHL(REPLACEONCE ), ARG_IS_STRING | EMPTY_STRING_OK },
{ NAHL(REQUESTORDER ), IS_OPTION },
{ NAHL(RIGHTMARGIN ), IS_OPTION },
{ NAHL(SAVE ), NO_ARGS },
{ NAHL(SAVEALL ), NO_ARGS },
{ NAHL(SAVEAS ), ARG_IS_STRING },
{ NAHL(SAVEAUTOPREFS ), NO_ARGS },
{ NAHL(SAVECLIP ), ARG_IS_STRING },
{ NAHL(SAVEDEFPREFS ), NO_ARGS },
{ NAHL(SAVEMACRO ), ARG_IS_STRING },
{ NAHL(SAVEPREFS ), ARG_IS_STRING },
{ NAHL(SEARCHBACK ), IS_OPTION },
{ NAHL(SELECTDOC ),0 },
{ NAHL(SETBOOKMARK ), ARG_IS_STRING | EMPTY_STRING_OK },
{ NAHL(SHIFT ), ARG_IS_STRING | EMPTY_STRING_OK },
{ NAHL(SHIFTTABS ), IS_OPTION },
{ NAHL(STATUSBAR ), IS_OPTION },
{ NAHL(SUSPEND ), NO_ARGS },
{ NAHL(SYNTAX ), ARG_IS_STRING | IS_OPTION },
{ NAHL(SYSTEM ), ARG_IS_STRING },
{ NAHL(TABS ), IS_OPTION },
{ NAHL(TABSIZE ), IS_OPTION },
{ NAHL(THROUGH ), ARG_IS_STRING },
{ NAHL(TOGGLESEOF ), NO_ARGS },
{ NAHL(TOGGLESEOL ), NO_ARGS },
{ NAHL(TOLOWER ),0 },
{ NAHL(TOUPPER ),0 },
{ NAHL(TURBO ), IS_OPTION },
{ NAHL(UNDELLINE ),0 },
{ NAHL(UNDO ),0 },
{ NAHL(UNLOADMACROS ), NO_ARGS },
{ NAHL(UNSETBOOKMARK ), ARG_IS_STRING | EMPTY_STRING_OK },
{ NAHL(UTF8 ), IS_OPTION },
{ NAHL(UTF8AUTO ), IS_OPTION },
{ NAHL(UTF8IO ), IS_OPTION },
{ NAHL(VERBOSEMACROS ), IS_OPTION },
{ NAHL(VISUALBELL ), IS_OPTION },
{ NAHL(WORDWRAP ), IS_OPTION },
};
/* Checks whether the command line m starts with the command c. Return 0 on
success, non-zero on failure. */
int cmdcmp(const char *c, const char *m) {
assert(c != NULL);
assert(m != NULL);
while (*c && ascii_up_case[*(unsigned char *)c] == ascii_up_case[*(unsigned char *)m]) {
c++;
m++;
}
return *c || *m && !isasciispace(*m) ;
}
/* This table *can* have conflicts, so that we can keep its size much
smaller. */
static macro_desc *macro_hash_table[MACRO_HASH_TABLE_SIZE];
/* This is the command name hashing function. We consider only the 5 least
significant bits because they are the bits which distinguish characters,
independently of their case. We are not interested in strings which contain
non-alphabetical characters, because they will certainly generate an error
(the only exception notably being "R1"). We should subtract 1 to s[i], but
this doesn't seem to produce any improvement. hash_macro() act as hash(),
but uses MACRO_HASH_TABLE_SIZE for its modulo. */
static int hash_cmd(const char * const s, int len) {
int h = -1;
while(len-- != 0) h = (h * 31 + ascii_up_case[(unsigned char)s[len]]) % HASH_TABLE_SIZE;
return (h + HASH_TABLE_SIZE) % HASH_TABLE_SIZE;
}
static int hash_macro(const char * const s, int len) {
int h = -1;
while(len-- != 0) h = (h * 31 + ascii_up_case[(unsigned char)s[len]]) % MACRO_HASH_TABLE_SIZE;
return (h + MACRO_HASH_TABLE_SIZE) % MACRO_HASH_TABLE_SIZE;
}
/* Parses a command line. This function has an interface which is slightly
varied with respect to the other functions of ne. In case of a parsing
error, an error index *with sign inverted* is passed back. In case parsing
succeeds, an (greater or equal to zero) action is returned, and the
numerical or string argument is passed in the variables pointed to by
num_arg or string_arg, respectively, if they are non-NULL. Otherwise, the
argument is not passed back. The string argument is free()able. -1 and NULL
denote the lack of an optional numerical or string argument, respectively.
NOP is returned on a NOP command, or on a comment line (any line whose first
non-space character is a non alphabetic character). Note that the various
syntax flags are used here. */
int parse_command_line(const char * command_line, int64_t * const num_arg, char ** const string_arg, const bool exec_only_options) {
if (num_arg) *num_arg = -1;
if (string_arg) *string_arg = NULL;
if (!command_line || !*command_line) return NOP_A;
while(isasciispace(*command_line)) command_line++;
const char *p = command_line;
D(fprintf(stderr,"parse_command_line[%d]: command_line=\"%s\"\n", __LINE__, p);)
if (!isalpha((unsigned char)*p)) { /* Comment, treated as NOP. */
const int len = strlen(p);
if (!(*string_arg = malloc(len + 1))) return -OUT_OF_MEMORY;
memcpy(*string_arg, p, len);
(*string_arg)[len] = 0;
D(fprintf(stderr,"parse_command_line[%d]: returning NOP_A\n", __LINE__);)
return NOP_A;
}
while(*p && !isasciispace(*p)) p++;
const int h = hash_cmd(command_line, p - command_line);
action a;
if ((a = hash_table[h]) && !cmdcmp(commands[--a].name, command_line)
|| (a = short_hash_table[h]) && !cmdcmp(commands[--a].short_name, command_line)) {
while(isasciispace(*p)) p++;
if (!(*p && (commands[a].flags & NO_ARGS))) {
if (!*p || (commands[a].flags & ARG_IS_STRING) || isxdigit((unsigned char)*p) || *p == 'x' || *p =='X') {
if ((commands[a].flags & IS_OPTION) || !exec_only_options) {
if (*p) {
if ((commands[a].flags & ARG_IS_STRING) && string_arg) {
int len = strlen(p);
if (len > 1 && *p == '"' && p[len - 1] == '"') {
p++;
len -= 2;
}
if (len == 0 && !(commands[a].flags & EMPTY_STRING_OK)) return -STRING_IS_EMPTY;
if (!(*string_arg = malloc(len + 1))) return -OUT_OF_MEMORY;
memcpy(*string_arg, p, len);
(*string_arg)[len] = 0;
}
else if (num_arg) {
char *q;
*num_arg = strtoll(p, &q, 0);
if (*q && !isasciispace(*q)) return -NOT_A_NUMBER;
}
}
return a;
}
D(fprintf(stderr, "parse_command[%d] error: Can execute only options \"%s\"\n", __LINE__, command_line);)
return -CAN_EXECUTE_ONLY_OPTIONS;
}
D(fprintf(stderr, "parse_command[%d] error: Has numeric argument \"%s\"\n", __LINE__, command_line);)
return -HAS_NUMERIC_ARGUMENT;
}
D(fprintf(stderr, "parse_command[%d] error: Has no argument \"%s\"\n", __LINE__, command_line);)
return -HAS_NO_ARGUMENT;
}
D(fprintf(stderr, "parse_command[%d] error: No such command \"%s\"\n", __LINE__, command_line);)
return -NO_SUCH_COMMAND;
}
/* Parses and executes a command line. Standard error codes are returned. If
the search for a standard command fails, we try to execute a macro in ~/.ne
with the same name. */
int execute_command_line(buffer *b, const char *command_line) {
encoding_type encoding = detect_encoding(command_line, strlen(command_line));
if (b->encoding != ENC_ASCII && encoding != ENC_ASCII && b->encoding != encoding) return INCOMPATIBLE_COMMAND_ENCODING;
int64_t n;
int a;
char *p;
if ((a = parse_command_line(command_line, &n, &p, b->exec_only_options)) >= 0) return do_action(b, a, n, p);
a = -a;
if ((a == NO_SUCH_COMMAND) && (a = execute_macro(b, command_line)) == CANT_OPEN_MACRO) a = NO_SUCH_COMMAND;
return a;
}
/* Allocates a macro descriptor. It does not allocate the internal character
stream, which has to be allocated and stuffed in separately. */
macro_desc *alloc_macro_desc(void) {
return calloc(1, sizeof(macro_desc));
}
/* Frees a macro descriptor. */
void free_macro_desc(macro_desc *md) {
if (!md) return;
assert_macro_desc(md);
free(md->name);
free_char_stream(md->cs);
free(md);
}
/* Here we record an action in a character stream. The action name is expanded
in a short or long name, depending on the value of the verbose parameter. A
numerical or string argument are expanded and copied, too. If the command
should not be recorded (for instance, ESCAPE_A) we return. */
void record_action(char_stream *cs, action a, int64_t c, const char *p, bool verbose) {
if (commands[a].flags & DO_NOT_RECORD) return;
char t[MAX_INT_LEN + 2];
/* NOP_A is special; it may actually be a comment.
Blank lines and real NOPs are recorded as blank lines. */
if (a == NOP_A) {
if (p && *p) add_to_stream(cs, p, strlen(p) + 1);
else add_to_stream(cs, "", 1);
return;
}
if (verbose) add_to_stream(cs, commands[a].name, strlen(commands[a].name));
else add_to_stream(cs, commands[a].short_name, strlen(commands[a].short_name));
if (c >= 0) {
sprintf(t, " %" PRId64, c);
add_to_stream(cs, t, strlen(t));
}
else if (p) {
add_to_stream(cs, " ", 1);
if (!*p || isasciispace(*p)) add_to_stream(cs, "\"", 1);
add_to_stream(cs, p, strlen(p));
if (!*p || isasciispace(*p)) add_to_stream(cs, "\"", 1);
}
add_to_stream(cs, "", 1);
}
/* A support function for optimize_macro(). It examines a string to see if it
is a valid "InsertChar ##" command. If it is, then insertchar_val() returns
the character code, otherwise it returns 0. */
static int insertchar_val(const char *p) {
if ( !p || !*p) return 0;
while(isasciispace(*p)) p++;
const char * const cmd = p;
if (!isalpha((unsigned char)*p)) return 0;
while(*p && !isasciispace(*p)) p++;
int h = hash_cmd(cmd, p - cmd);
action a;
if (((a = hash_table[h]) && !cmdcmp(commands[--a].name, cmd)
|| (a = short_hash_table[h]) && !cmdcmp(commands[--a].short_name, cmd)) && a == INSERTCHAR_A) {
while(isasciispace(*p)) p++;
h = strtol(p, (char **)&cmd, 0);
return *cmd || h < 0 ? 0 : h;
}
return 0;
}
/* Optimizing macros is not safe if there are any subsequent undo commands or
calls to other macros (which may themselves contain undo commands). This function
looks through a stream for undo or non-built in commands, and returns false
if any are found; returns true otherwise. */
bool vet_optimize_macro_stream(char_stream * const cs, int64_t pos) {
int64_t n;
int a;
char *p;
while (pos < cs->len ) {
if ((a = parse_command_line(&cs->stream[pos], &n, &p, 0)) >= 0) {
if (p) free(p);
if (a == UNDO_A) return false; /* optimization is not safe */
} else {
a = -a;
if (a == NO_SUCH_COMMAND) return false; /* possibly a macro invocation */
}
pos += strlen(&cs->stream[pos]) + 1;
}
return true;
}
/* Looks through the macro stream for consecutive runs of InsertChar commands
and replaces them with appropriate InsertString commands. This makes macros
much easier to read if and when they have to be edited. Note that if the
character inserted by InsertChar is not an ASCII character, then we should
leave it as an InsertChar command to maximize portability of the macros. */
void optimize_macro(char_stream *cs, bool verbose) {
if (!cs || !cs->len) return;
int building = 0;
bool safe_to_optimize = false;
for (int64_t pos = 0; pos < cs->len; pos += strlen(&cs->stream[pos]) + 1) {
char * const cmd = &cs->stream[pos];
const int chr = insertchar_val(cmd);
if (chr < 0x80 && isprint(chr) && (safe_to_optimize = vet_optimize_macro_stream(cs, pos))) {
delete_from_stream(cs, pos, strlen(cmd) + 1);
const char two[2] = { chr };
if (building) {
building++;
insert_in_stream(cs, two, building, 1);
}
else {
const char * const insert = verbose ? INSERTSTRING_NAME : INSERTSTRING_ABBREV;
const int64_t len = strlen(insert);
insert_in_stream(cs, "\"", pos, 2); /* Closing quote */
insert_in_stream(cs, two, pos, 1); /* The character itself */
insert_in_stream(cs, " \"", pos, 2); /* space and opening quote */
insert_in_stream(cs, insert, pos, len); /* The command itself */
building = pos + len + 2; /* This is where the char is now */
}
}
else building = 0;
}
}
/* Plays a character stream c times, considering each line as a command line. It
polls the global stop variable in order to check for the user interrupting.
Note that the macro is duplicated before the first execution: this is absolutely
necessary, for otherwise a call to CloseDoc, Record or UnloadMacros could
free() the block of memory which we are executing. */
int play_macro(char_stream *cs, int64_t c) {
if (!cs) return ERROR;
/* If len is 0 or 1, the character stream does not contain anything. */
const int64_t len = cs->len;
if (len < 2 || c < 1) return OK;
char * const stream = malloc(len);
if (!stream) return OUT_OF_MEMORY;
static int call_depth = 0;
if (++call_depth > 32) {
--call_depth;
return MAX_MACRO_DEPTH_EXCEEDED;
}
executing_macro = true;
memcpy(stream, cs->stream, len);
stop = false;
int error = OK;
while(!stop && !error && c--) {
char * p = stream;
while(!stop && p - stream < len) {
#ifdef NE_TEST
fprintf(stderr, "%s\n", p); /* During tests, we output to stderr the current command. */
#endif
if (error = execute_command_line(cur_buffer, p))
#ifndef NE_TEST
break /* During tests, we never interrupt a macro. */
#endif
;
#ifdef NE_TEST
refresh_window(cur_buffer);
draw_status_bar();
#endif
p += strlen(p) + 1;
}
}
free(stream);
if (--call_depth == 0) executing_macro = false;
return stop ? STOPPED : error;
}
/* Loads a macro, and puts it in the global macro hash table. file_part is
applied to the name argument before storing it and hashing it. Note that if
the macro can't be opened, we retry prefixing its name with the preferences
directory name (~/.ne/). Thus, for instance, all autopreferences file whose
name does not conflict with internal commands can be executed transparently
just by typing their name. */
macro_desc *load_macro(const char *name) {
assert(name != NULL);
macro_desc * const md = alloc_macro_desc();
if (!md) return NULL;
char_stream * cs = load_stream(md->cs, name, false, false);
D(fprintf(stderr,"load_macro[%d]: name=%s, cs=%lx\n", __LINE__, name, cs);)
char *macro_dir, *prefs_dir;
if (!cs && (prefs_dir = exists_prefs_dir()) && (macro_dir = malloc(strlen(prefs_dir) + 2 + strlen(name)))) {
strcat(strcpy(macro_dir, prefs_dir), name);
cs = load_stream(md->cs, macro_dir, false, false);
D(fprintf(stderr,"load_macro[%d]: name=%s, cs=%lx\n", __LINE__, macro_dir, cs);)
free(macro_dir);
}
if (!cs && (prefs_dir = exists_gprefs_dir()) && (macro_dir = malloc(strlen(prefs_dir) + 2 + strlen(name) + 7))) {
strcat(strcat(strcpy(macro_dir, prefs_dir), "macros/"), name);
cs = load_stream(md->cs, macro_dir, false, false);
D(fprintf(stderr,"load_macro[%d]: name=%s, cs=%lx\n", __LINE__, macro_dir, cs);)
free(macro_dir);
}
if (cs) {
/* the last line may not be null-terminated, so... */
add_to_stream(cs, "", 1);
md->cs = cs;
md->name = str_dup(file_part(name));
const int h = hash_macro(md->name, strlen(md->name));
macro_desc **m = ¯o_hash_table[h];
while(*m) m = &((*m)->next);
*m = md;
return md;
}
free_macro_desc(md);
return NULL;
}
/* Executes a named macro. If the macro is not in the global
macro list, it is loaded. A standard error code is returned. */
int execute_macro(buffer *b, const char *name) {
const char * const p = file_part(name);
int h = hash_macro(p, strlen(p));
D(fprintf(stderr,"execute_macro[%d]: searching macro_hash_table for file_part of name=%s\n", __LINE__, name);)
macro_desc *md;
for(md = macro_hash_table[h]; md && cmdcmp(md->name, p); md = md->next );
if (!md) {
md = load_macro(name);
D(fprintf(stderr,"execute_macro[%d]: load_macro(\"%s\") returned md=%lx\n", __LINE__, name, md);)
}
assert_macro_desc(md);
int rc = CANT_OPEN_MACRO;
if (md) {
if (recording_macro) {
add_to_stream(recording_macro, "# include macro ", 16);
add_to_stream(recording_macro, md->name, strlen(md->name)+1);
}
rc = play_macro(md->cs, 1);
if (recording_macro) {
add_to_stream(recording_macro, "# conclude macro ", 17);
add_to_stream(recording_macro, md->name, strlen(md->name)+1);
}
}
return rc;
}
/* Clears up the macro table. */
void unload_macros(void) {
for(int i = 0; i < MACRO_HASH_TABLE_SIZE; i++) {
macro_desc *m = macro_hash_table[i];
macro_hash_table[i] = NULL;
while(m) {
macro_desc * const n = m->next;
free_macro_desc(m);
m = n;
}
}
}
/* Find first n key strokes that currently map to commands[i].name or commands[i].short_name.
Returns either NULL or a char string that must be freed by the caller. */
char *find_key_strokes(int c, int n) {
char *str=NULL, *p;
for(int i = 0; i < NUM_KEYS && n; i++) {
if (key_binding[i]) {
if (((!strncasecmp(commands[c].short_name, key_binding[i], strlen(commands[c].short_name))) &&
((!key_binding[i][strlen(commands[c].short_name)] ) ||
(key_binding[i][strlen(commands[c].short_name)] == ' ')
)
) ||
((!strncasecmp(commands[c].name, key_binding[i], strlen(commands[c].name))) &&
((!key_binding[i][strlen(commands[c].name)] ) ||
(key_binding[i][strlen(commands[c].name)] == ' ')
)
)
) {
n--;
if (!str) {
if (!(str = malloc(1))) return NULL;
str[0] = '\0';
}
if (p = realloc(str, strlen(str) + strlen(key_stroke[i]) + 2)) {
str = strcat(strcat(p, *p ? " " : ""), key_stroke[i]);
} else {
free(str);
return NULL;
}
}
}
}
return str;
}
char *bound_keys_string(int c) {
char *key_strokes = find_key_strokes(c, 9);
char *str=NULL;
if (key_strokes) {
if ((str = malloc(strlen(key_strokes) + 16)))
strcat(strcpy(str, "Bound keys(s): "), key_strokes);
free(key_strokes);
}
return str;
}
/* This function helps. The help text relative to the command name pointed to
by p is displayed (p can also contain arguments). The string *p is not
free'd by help(). If p is NULL, the alphabetically ordered list of commands
is displayed with the string requester. The help finishes when the user
escapes.
WARNING: help() assumes a lot about how request_strings() and 'req_list's
work rather than using the support functions to build its req_list. Any
changes here or in request.c need to be thoroughly checked in both places. */
void help(char *p) {
bool request_order_orig = req_order;
req_list rl = { .ignore_tab=true, .help_quits=true };
D(fprintf(stderr, "Help Called with parm %p.\n", p);)
int r = 0, width;
do {
print_message(info_msg[HELP_KEYS]);
rl.cur_entries = ACTION_COUNT;
rl.alloc_entries = 0;
rl.entries = (char **)command_names;
rl.lengths = realloc(rl.lengths, sizeof(int) * rl.cur_entries);
width = 0;
for (int i=0,w; i width) width = w;
for (int i=0; i= 0) {
D(fprintf(stderr, "Help check #2: p=%p, r=%d\n", p, r);)
if (p) {
for(r = 0; r < strlen(p); r++) if (isasciispace((unsigned char)p[r])) break;
r = hash_cmd(p, r);
D(fprintf(stderr, "Help check #3: p=%p, *p=%s, r=%d\n", p, p, r);)
action a;
if ((a = hash_table[r]) && !cmdcmp(commands[--a].name, p)
|| (a = short_hash_table[r]) && !cmdcmp(commands[--a].short_name, p)) r = a;
else r = -1;
D(fprintf(stderr, "Help check #4: r=%d\n", r);)
p = NULL;
}
else {
D(fprintf(stderr, "Gonna parse_command_line(\"%s\",NULL,NULL,false);\n", command_names[r]);)
r = parse_command_line(command_names[r], NULL, NULL, false);
D(fprintf(stderr, "...and got r=%d\n", r);)
}
if (r < 0) {
r = 0;
continue;
}
assert(r >= 0 && r < ACTION_COUNT);
print_message(info_msg[HELP_COMMAND_KEYS]);
char *key_strokes, **tmphelp;
if ((key_strokes = bound_keys_string(r)) && (tmphelp = calloc(commands[r].help_len+1, sizeof(char *)))) {
tmphelp[0] = (char *)commands[r].help[0];
tmphelp[1] = (char *)commands[r].help[1];
tmphelp[2] = key_strokes;
memcpy(&tmphelp[3], &commands[r].help[2], sizeof(char *) * (commands[r].help_len-2));
rl.cur_entries = commands[r].help_len+1;
rl.alloc_entries = 0;
rl.entries = tmphelp;
rl.lengths = realloc(rl.lengths, sizeof(int) * rl.cur_entries);
width = 0;
for (int i=0,w; i width) width = w;
for (int i=0; i width) width = w;
for (int i=0; i= 0);
free(rl.lengths);
draw_status_bar();
}
/* Parse string parameters for NextWord, PrevWord, AdjustView, etc. */
int parse_word_parm(char *p, char *pat_in, int64_t *match) {
int i, len = strlen(pat_in);
char *pat = strntmp(pat_in, len);
if (p) {
while (*p) {
if (isasciispace(*p)) p++;
else if (isdigit((unsigned char)*p)) {
for (i=0; i