summaryrefslogtreecommitdiff
path: root/support/texdoc
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/texdoc
Initial commit
Diffstat (limited to 'support/texdoc')
-rw-r--r--support/texdoc/COPYING674
-rw-r--r--support/texdoc/NEWS251
-rw-r--r--support/texdoc/README.md117
-rw-r--r--support/texdoc/doc/texdoc.1177
-rw-r--r--support/texdoc/doc/texdoc.pdfbin0 -> 76508 bytes
-rw-r--r--support/texdoc/doc/texdoc.tex867
-rwxr-xr-xsupport/texdoc/script/texdoc.tlu17
-rw-r--r--support/texdoc/script/texdoclib-alias.tlu127
-rw-r--r--support/texdoc/script/texdoclib-cli.tlu219
-rw-r--r--support/texdoc/script/texdoclib-config.tlu559
-rw-r--r--support/texdoc/script/texdoclib-const.tlu158
-rw-r--r--support/texdoc/script/texdoclib-score.tlu293
-rw-r--r--support/texdoc/script/texdoclib-search.tlu771
-rw-r--r--support/texdoc/script/texdoclib-util.tlu127
-rw-r--r--support/texdoc/script/texdoclib-view.tlu246
-rw-r--r--support/texdoc/script/texdoclib.tlu37
-rw-r--r--support/texdoc/texdoc.cnf507
17 files changed, 5147 insertions, 0 deletions
diff --git a/support/texdoc/COPYING b/support/texdoc/COPYING
new file mode 100644
index 0000000000..94a9ed024d
--- /dev/null
+++ b/support/texdoc/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/support/texdoc/NEWS b/support/texdoc/NEWS
new file mode 100644
index 0000000000..430c925d24
--- /dev/null
+++ b/support/texdoc/NEWS
@@ -0,0 +1,251 @@
+NEWS for texdoc
+===============
+(This file public domain.)
+
+Version 3.1 (TeX Live 2019)
+===========================
+- New option -c: changing configure temporally
+- New config item `texlive_tlpdb` to specify a path for texlive.tlpdb
+- Fixed an issue that texdoc fails to open gzipped file in GNOME/XFCE
+- Added prefixes to script files not to contaminate kpse search space
+- Many other small improvements and bug fixes
+
+Version 3.0 (TeX Live 2018)
+===========================
+This version has two big features:
+- Fuzzy search
+- New option parser
+
+And also includes a lot of small improvements:
+- Get rid of Data.tlpdb.lua
+- Documentation update
+- Bug fix around environment variables
+- Other small improvements including score adjustments
+- Sources moved to https://github.com/TeX-Live/texdoc
+
+Version 2.0171
+--------------
+- In list mode (-l), just RET views first document in list.
+- --help and --version override other specified command-line actions.
+
+Version 2.017 (TeX Live 2017)
+============================
+- Restore user's LC_CTYPE value if possible (with this year's luatex).
+- Generic system environment variables, such as BROWSER, accept
+ a colon-separated list of programs.
+- Sources moved to https://puszcza.gnu.org.ua/projects/texdoc.
+
+Version 0.85
+------------
+Compatibility with LuaTeX 0.74+ (Lua 5.2).
+
+Version 0.84 (TeX Live 2012)
+============================
+Fix -f output when TEXMFHOME contains a list of paths. Bug sort-of reported
+by Matthew Leingang, see http://tex.stackexchange.com/questions/42920/
+
+Version 0.83
+------------
+Minor adjustments in the scoring system.
+
+Version 0.82
+------------
+Fixes to the default viewer/destkop environment detection:
+- try gvfs-open with Gnome
+- always try xdg-open first (usually more up-to-date)
+Thanks to Axel E. Retif for informing me that newer versions of Ubuntu don't
+ship gnome-open by default any more.
+
+Version 0.81 (TeX Live 2011)
+============================
+Allow configuration files to be found in all TEXMF trees.
+The default one should now be installed in TEXMFDIST (in upstream TeX Live) or
+in a tree search after TEXMFLOCAL (in downstream distros).
+
+Delay default viewer detection, see:
+http://tug.org/pipermail/tex-live/2011-May/029150.html
+
+Drop long-deprecated things:
+- --search (-> --showall), --verbosity-level and old syntax of -v
+- alias_switch, -a, -A, --alias, --noalias
+- 'regex' mode,
+
+Various internal cleanups and rearrangements.
+
+Version 0.80
+============
+New 'lang' configuration option (defaults on system locale): now <name>-de.pdf
+wins over <name>.pdf if German is preferred. Patch kindly provided by Philipp
+Stephani. Also works with aliases (eg koma-de = scrguide wins over koma =
+scrguien).
+
+Document the previously undocumented options from 0.72-tl2010.
+
+Accept htm as a valid extension, synonym for html. Emit less warnings about
+viewers.
+
+Version 0.72 as in TeX Live 2010
+--------------------------------
+New (undocumented) settings (bad)basenme_list, parallel to (bad)ext_list.
+
+New (undocumented) setting item suffix_list for known documentation suffixes,
+previously hardcoded.
+
+New (undocumented) option --just-view for use by front-end programs.
+
+Version 0.72
+------------
+Fix stupid bug introduced in 0.70: texdoc would crash if called without
+arguments. (Reported by Maxime Chupin.)
+
+Version 0.71
+------------
+Fix bug introduced in 0.70: some results were duplicated on windows. (Reported
+by Uwe Siart.)
+
+Version 0.70
+============
+Use information from the TeX Live package database (some of it coming from the
+CTAN catalogue) to improve search results. Display some of this information.
+
+Show the current settings in --help message.
+
+Misc. internal changes & fixes.
+
+Deprecate -a, --alias, -A, --noalias and alias_switch.
+
+Version 0.66
+------------
+Turn the -v=N, --verbosity=N command-line option into a pair of related options
+-v, --verbose and -q, --quiet (more standard, Karl's suggestion). Also remove
+uselessly verbose debug items so --debug is usable again.
+
+Rework help text (Karl's suggestion).
+
+Issue a warning about DISPLAY not being set on generic Unix before trying to
+start a viewer.
+
+Small internal clean-ups.
+
+Version 0.65
+------------
+Make it easier to change the list of configuration files used (eg for
+downstream distributions).
+
+Version 0.64
+------------
+Fix a crash when DISPLAY is unset on Unix by querying xprop more carefully.
+(Bug introduced in 0.61.)
+
+Version 0.63
+------------
+Fix a bug on windows which prevented from viewing sty files.
+
+Version 0.62
+------------
+Support for zipped documentation is now an official option.
+Please read the warnings before using it!
+
+Version 0.61
+------------
+Detect desktop environments (KDE4, KDE, GNOME, XFCE) for viewer selection.
+
+Version 0.60
+============
+Changed the search/alias/score logic. See section 2 of the manual for details,
+but here is a primer:
+- alias now /adds/ patterns to be search, it is no more a substitution
+- new scoring system, partially configurable, with improved heuristics
+
+The beginning of the manual has been completely rewritten.
+
+Bugs have been fixed as usual, and probably some others introduced...
+
+Version 0.50
+============
+- New option for machine-readable output.
+- Bugfixes (esp. for zipped file support (Unix only)).
+- Hopefully more sensible default viewer selection.
+
+Version 0.49
+------------
+Major code rewrite. No big user-visible change, except a few bugs fixed and new
+debugging levels.
+
+Version 0.47 (TeX Live 2009)
+----------------------------
+Adapt for man pages named *.man1.pdf instead of *.pdf.
+(Temporary hack, done better since version 0.60.)
+
+Warning: zip support probably broken!
+
+Version 0.46
+------------
+Avoid using lfs.currentdir() and lfs.chdir() which are broken on Solaris 10
+in LuaTeX 0.40
+
+Version 0.45
+------------
+Fix possible line ending problems on windows with certain versions of LuaTeX
+
+Version 0.44
+------------
+- fixing viewer problems on Windows
+- expanding the list of default viewers on generic Unix
+
+
+Version 0.43
+------------
+Improve support for zipped documentation and comments about it in the default
+configuration file. (Thank Norbert Preining for his help.)
+
+Various fixes, aliases updates as usual.
+
+Version 0.42
+------------
+Improvements to default txt viewer on Windows (thanks to Phil Taylor).
+
+New debugging levels; new names for debugging options.
+
+New configuration file allowing to run the development version more easily.
+
+Changes to the way files are displayed. Results are numbered even when -I is
+used.
+
+Documentation update. Alias additions and fixes as usual. Various bugfixes.
+
+License switched to GPL.
+
+Version 0.4 (TeX Live 2008)
+===========================
+Use our own configuration files, not texmf.cnf. Rework the way viewers are
+handled, and add a few configuration options.
+
+Add an 'alias' feature. Filter results in a more restrictive way. Add new search
+modes 'mixed' and 'regex'. Turn result lists into menus.
+
+Add a user manual in pdf.
+
+Development passed from Frank Küster to Manuel Pégourié-Gonnard.
+
+Version 0.3
+-----------
+Various changes including:
+- added changelog
+- better OS detection for default viewer settings
+- removed some debugging code
+- -s now works in dirs without ls-R, too
+
+Version 0.2
+-----------
+Various changes including:
+- implemented reading of configuration from texmf.cnf
+- fixed "-s" option
+
+Version 0.1
+-----------
+Initial public release of the texlua version by Frank Küster.
+
+Earlier versions
+----------------
+Shell script by Thomas Esser maintained as part of teTeX.
diff --git a/support/texdoc/README.md b/support/texdoc/README.md
new file mode 100644
index 0000000000..f0da3fc234
--- /dev/null
+++ b/support/texdoc/README.md
@@ -0,0 +1,117 @@
+# Texdoc v3.1 (2019-03-28)
+
+[![Build Status](https://travis-ci.org/TeX-Live/texdoc.svg?branch=master)](https://travis-ci.org/TeX-Live/texdoc)
+[![Build status](https://ci.appveyor.com/api/projects/status/uq28ms7eba7ns6d3/branch/master?svg=true)](https://ci.appveyor.com/project/wtsnjp/texdoc/branch/master)
+
+Texdoc is a command line program to find and view documentation in TeX Live.
+
+## General Information
+
+* Website: <https://www.tug.org/texdoc/>
+* Repository: <https://github.com/TeX-Live/texdoc/>
+* Mailing list: <texdoc@tug.org>
+
+## Using Texdoc
+
+Texdoc is part of the TeX Live distribution. Generally, you don't have to install it yourself.
+
+If you want to try/test the develop version, please see below.
+
+## How to Install the Develop Version
+
+### Using Bundler
+
+The easiest way to install the develop version of Texdoc is using [Bundler](https://bundler.io/) and [Rake](https://github.com/ruby/rake):
+
+```
+$ bundle install
+$ rake install
+```
+
+If you want to uninstall the develop version (to use the TeX Live version), just try:
+
+```
+$ rake uninstall
+```
+
+### Installing manually
+
+Here, `$TEXDOC` means the path to the texdoc dev sources you cloned (or downloaded), and `$TEXMFHOME` denotes your personal TEXMF tree (you can get its location with `kpsewhich --var-value TEXMFHOME`).
+
+* **The command.** Symlink or copy `$TEXDOC/script` as `$TEXMFHOME/scripts/texdoc`.
+* **The configuration file.** Symlink or copy `$TEXDOC/texdoc.cnf` as `$TEXMFHOME/texdoc/texdoc-dist.cnf` (notice the `-dist` part). It will completely override the other configuration files except `$TEXMFHOME/texdoc/texdoc.cnf` which you can still use for you personal settings.
+
+You can make sure that the right files are used by running `texdoc --files`: the first line contains the full path to the version of used `texdoclib.tlu`, and the rest lists the configuration files in use.
+
+## Files in This Repository
+
+- `doc/` contains the documentation,
+- `script/` contains the source code,
+- `spec/` is a directory for testing scripts, and
+- `texdoc.cnf` is the default configuration file.
+
+## Building and Testing
+
+Many building tasks are defined as [Rake](https://github.com/ruby/rake) tasks:
+
+### Generating all documentation
+
+Following will generate both the PDF and the manpage in `doc/` directory.
+
+```
+$ rake doc
+```
+
+### Generating a pre-hashed cache file
+
+Following will generate a pre-hashed cache file `script/Data.tlpdb.lua`. This task have to be done under a TeX Live setup with tlpdb.
+
+```
+$ rake gen_datafile
+```
+
+### Running tests
+
+Following will run all tests in `spec/` directory.
+
+```
+$ rake test
+```
+
+Alternatively, you can give spec names with the `--list` (`-l`) option for this task. E.g., following will run only `spec/action/help_spec.rb` and `spec/mode/list_spec.rb`:
+
+```
+$ rake test -- -l action/help,mode/list
+```
+
+### Showing all available tasks
+
+Following will show all available tasks with a short description.
+
+```
+$ rake -T
+```
+
+In addition to that, for options available tasks (e.g., `rake test` and `rake run_texdoc`) you can get options information with `-h` option for each task:
+
+```
+$ rake test -- -h
+```
+
+## Further Information
+
+More specific information (e.g., TODO) can be found in the Wiki of our GitHub repository. Please visit:
+
+* <https://github.com/TeX-Live/texdoc/wiki>
+
+## Copyright and License
+
+Copyright 2008 Manuel Pégourié-Gonnard, Takuto Asakura, and the TeX Live Team.
+
+This package is distributed under the terms of the GNU General Public License as published by the Free Software Foundation, either [version 3](./COPYING) of the License, or (at your option) any later version.
+
+Previous work (texdoc program) in the public domain:
+
+* Contributions from Reinhard Kotucha (2008).
+* First texlua versions by Frank Küster (2007).
+* Original shell script by Thomas Esser, David Aspinall, and Simon Wilkinson.
diff --git a/support/texdoc/doc/texdoc.1 b/support/texdoc/doc/texdoc.1
new file mode 100644
index 0000000000..cf072bd5fc
--- /dev/null
+++ b/support/texdoc/doc/texdoc.1
@@ -0,0 +1,177 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "TEXDOC" "1" "March 2019" "Texdoc 3.1" "Texdoc manual"
+.
+.SH "NAME"
+\fBtexdoc\fR \- find & view documentation in TeX Live
+.
+.SH "SYNOPSIS"
+\fBtexdoc\fR [OPTION\.\.\.] NAME\.\.\.
+.
+.br
+\fBtexdoc\fR [OPTION\.\.\.] ACTION
+.
+.SH "DESCRIPTION"
+Try to find appropriate TeX documentation for the specified NAME(s)\. Alternatively, perform the given ACTION and exit\.
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-w\fR, \fB\-\-view\fR
+Use view mode: start a viewer\. \fB(default)\fR
+.
+.TP
+\fB\-m\fR, \fB\-\-mixed\fR
+Use mixed mode (view or list)\.
+.
+.TP
+\fB\-l\fR, \fB\-\-list\fR
+Use list mode: show a list of results\.
+.
+.TP
+\fB\-s\fR, \fB\-\-showall\fR
+Use showall mode: show also "bad" results\.
+.
+.TP
+\fB\-i\fR, \fB\-\-interact\fR
+Use interactive menus\. \fB(default)\fR
+.
+.TP
+\fB\-I\fR, \fB\-\-nointeract\fR
+Use plain lists, no interaction required\.
+.
+.TP
+\fB\-M\fR, \fB\-\-machine\fR
+Machine\-readable output for lists (implies \-I)\.
+.
+.TP
+\fB\-q\fR, \fB\-\-quiet\fR
+Suppress warnings and most error messages\.
+.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Print additional information (e\.g\., viewer command)\.
+.
+.TP
+\fB\-D\fR, \fB\-\-debug\fR
+Activate all debug output (equal to "\-\-debug=all")\.
+.
+.TP
+\fB\-d\fR LIST, \fB\-\-debug\fR=LIST
+Activate debug output restricted to the items specified in LIST\.
+.
+.br
+Available items: \fBconfig\fR, \fBfiles\fR, \fBsearch\fR, \fBscore\fR, \fBtexdocs\fR, \fBtlpdb\fR, \fBversion\fR, \fBview\fR, and \fBall\fR to activate all of these\.
+.
+.TP
+\fB\-c\fR NAME=VALUE
+Set configuration item NAME to VALUE\.
+.
+.SH "ACTIONS"
+.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Print this help message\.
+.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Print the version number\.
+.
+.TP
+\fB\-f\fR, \fB\-\-files\fR
+Print the list of configuration files used\.
+.
+.TP
+\fB\-\-just\-view\fR FILE
+Display FILE, given with full path (no searching)\.
+.
+.SH "ENVIRONMENT"
+.
+.TP
+\fBBROWSER\fR, \fBBROWSER_texdoc\fR
+Set the command to be used for HTML documents\.
+.
+.TP
+\fBDVIVIEWER\fR, \fBDVIVIEWER_texdoc\fR
+Set the command to be used for DVI documents\.
+.
+.TP
+\fBMDVIEWER\fR, \fBMDVIEWER_texdoc\fR
+Set the command to be used for Markdown documents\.
+.
+.TP
+\fBPAGER\fR, \fBPAGER_texdoc\fR
+Set the command to be used for text documents\.
+.
+.TP
+\fBPDFVIEWER\fR, \fBPDFVIEWER_texdoc\fR
+Set the command to be used for PDF documents\.
+.
+.TP
+\fBPSVIEWER\fR, \fBPSVIEWER_texdoc\fR
+Set the command to be used for PS documents\.
+.
+.TP
+\fBLANG\fR, \fBLC_ALL\fR and so on
+Set the locale (which will influence on the search results)\.
+.
+.TP
+\fBTEXDOCS\fR
+In addition to the documents included in the TeX Live database, Texdoc also searches documentation under TEXMF trees specified by the kpathsea variable \fBTEXDOCS\fR\.
+.
+.SH "FILES"
+\fB<texmf>/texdoc/texdoc\.cnf\fR, see output of the \fB\-\-files\fR option\.
+.
+.SH "EXIT STATUS"
+The \fBtexdoc\fR command exists with one of the following values:
+.
+.TP
+0
+Success\.
+.
+.TP
+1
+Internal error\.
+.
+.TP
+2
+Usage error\.
+.
+.TP
+3
+No documentation found\.
+.
+.SH "REPORTING BUGS"
+Report bugs to \fItexdoc@tug\.org\fR\.
+.
+.br
+Texdoc home page: \fIhttp://tug\.org/texdoc/\fR
+.
+.br
+Source: \fIhttps://github\.com/TeX\-Live/texdoc\fR
+.
+.SH "COPYRIGHT"
+Copyright 2018 Manuel Pe\'gourie\'\-Gonnard, Takuto Asakura, Karl Berry, and Norbert Preining\.
+.
+.br
+License: GNU GPL version 3 or later \fIhttp://gnu\.org/licenses/gpl\.html\fR\.
+.
+.br
+This is free software: you are free to change and redistribute it\.
+.
+.SH "SEE ALSO"
+The full documentation is maintained as a PDF manual\. The command
+.
+.IP "" 4
+.
+.nf
+
+texdoc texdoc
+.
+.fi
+.
+.IP "" 0
+.
+.P
+should give you access to the complete manual\.
diff --git a/support/texdoc/doc/texdoc.pdf b/support/texdoc/doc/texdoc.pdf
new file mode 100644
index 0000000000..d897017714
--- /dev/null
+++ b/support/texdoc/doc/texdoc.pdf
Binary files differ
diff --git a/support/texdoc/doc/texdoc.tex b/support/texdoc/doc/texdoc.tex
new file mode 100644
index 0000000000..37b4460259
--- /dev/null
+++ b/support/texdoc/doc/texdoc.tex
@@ -0,0 +1,867 @@
+%#!xelatex
+% Texdoc user manual
+% Copyright 2008 Manuel Pégourié-Gonnard and Takuto Asakura
+% distributed under the terms of GPL v3 or later
+
+\setlength\overfullrule{5pt}
+
+\documentclass[a4paper,oneside]{scrartcl}
+\usepackage{fontspec}
+\usepackage{xunicode}
+
+\defaultfontfeatures{Ligatures=TeX}
+\setmainfont{DejaVuSerif}
+\setsansfont{DejaVuSans}
+\setmonofont{DejaVuSansMono}
+\renewcommand\familydefault{\sfdefault} \normalfont
+\newcommand\mylangle{$\langle$}
+\newcommand\myrangle{$\rangle$}
+
+\usepackage{xargs,xspace,fancyvrb,xcolor,pifont,calc,ifmtarg,mathstyle}
+
+\usepackage[sf,bf]{titlesec}
+\titlelabel{\makebox[0pt][r]{\thetitle\kern1pc}}
+\titleformat{\subsubsection}[runin]{\itshape}
+ {\makebox[0pt][r]{\thetitle\kern1pc}}
+ {0pt}{}[\maybedot\space --- \kern0pt]
+\titlespacing{\subsubsection}{0pt}{0.5\baselineskip}{0pt}
+
+\usepackage{enumitem}
+\newlength\lssep \setlength\lssep{\smallskipamount}
+\setlist{noitemsep,topsep=\lssep,partopsep=\lssep}
+
+\usepackage[british]{babel}
+\usepackage[bookmarks=true]{hyperref}
+\usepackage{bookmark}
+\hypersetup{%
+ bookmarksnumbered=true, bookmarksopen=true, bookmarksopenlevel=2,
+ pdftitle={Texdoc: find and view documentation in TeX Live},
+ pdfauthor={Manuel Pégourié-Gonnard, Takuto Asakura},
+ pdfsubject={Texdoc's user manual},
+ pdfkeywords={Texdoc, TeX Live, manual, documentation},
+}
+
+\usepackage[yyyymmdd]{datetime}
+\renewcommand{\dateseparator}{-}
+
+\newcommand\texlive{\TeX~Live\xspace}
+
+\setlength\parindent{\baselineskip}
+
+\lastlinefit=500 % e-TeX powered
+
+\definecolor{links}{named}{violet}
+\definecolor{special}{rgb}{0,0.5,0}
+\definecolor{code}{rgb}{0,0,0.6}
+\hypersetup{colorlinks=true, linkcolor=links, urlcolor=links, citecolor=links}
+
+\newcommand\cofont{% code
+ \color{code}\normalfont\ttfamily}
+\newcommand\meta[1]{% meta elements
+ {\normalfont\color{special}\mylangle\textit{#1}\myrangle}}
+
+% take care of non-breakable spaces
+\catcode`\ 10\relax
+
+\fvset{%
+ formatcom=\cofont,
+ defineactive=\makeallfancy,
+ codes=\fancyactives,
+}
+\newcommand\fancyactives{\catcode`\«\active}
+\newcommand\makeallfancy{\makefancyog}
+\bgroup
+ \catcode`\«\active
+ \global\def\makefancyog{\def«##1»{\meta{##1}}}
+\egroup
+
+\newif\ifframed
+\newlength\dec
+\setlength\dec{\heightof{\cofont{texdoc \meta{name}}}}
+
+\makeatletter
+\newenvironment{commandes}[3]{%
+ \def\thecmd{\noexpand#1}%
+ \def\bmtext{#2}%
+ \def\thelabel{#3}%
+ \SaveVerbatim[samepage, gobble=2]{verbmat}%
+}{%
+ \endSaveVerbatim
+ \xdef\sectioncmd{\noexpand\nodotthistime
+ \thecmd[\bmtext]{%
+ \ifframed
+ \unexpanded{\normalsize\normalfont
+ \fbox{\raisebox{\dec}{\BUseVerbatim[baseline=t]{verbmat}}}}%
+ \else
+ \unexpanded{\normalsize\normalfont
+ \BUseVerbatim{verbmat}}%
+ \fi
+ \noexpand\label{\thelabel}}}%
+ \aftergroup\sectioncmd
+}
+\makeatother
+
+\newcommand\maybedot{.}
+\newcommand\nodotthistime{\renewcommand\maybedot{\global\def\maybedot{.}}}
+
+\newenvironment{cmdsubsec}[2]
+ {\framedtrue \commandes\subsection{#1}{#2}}
+ {\endcommandes}
+
+\newenvironment{cmdsubsub}[2]
+ {\framedfalse \commandes\subsubsection{#1}{#2}}
+ {\endcommandes}
+
+\makeatletter
+% code in hors-text
+\newenvironment{htcode}
+ {\SaveVerbatim[samepage, gobble=2]{verbmat}}
+ {
+ \endSaveVerbatim
+ \par\medskip\noindent\hspace*{\parindent}%
+ \BUseVerbatim{verbmat}%
+ \par\medskip\@endpetrue
+ }
+\makeatother
+\DefineShortVerb{\|}
+
+\setkomafont{title}{}
+\setkomafont{subtitle}{\Large}
+\deffootnote[1.5em]{1.5em}{1em}{\textsuperscript{\thefootnotemark}\thinspace}
+
+\newcommand\tdml{\href{http://lists.tug.org/texdoc}{texdoc mailing list}\xspace}
+
+\title{Texdoc}
+\subtitle{Find \& view documentation in \texlive\\
+ \href{https://tug.org/texdoc/}{https://tug.org/texdoc/}}
+\author{Manuel Pégourié-Gonnard\and Takuto Asakura}
+\date{v3.1\quad \today}
+
+\begin{document}
+\VerbatimFootnotes
+
+\maketitle
+
+\section{Quick guide}
+
+\subsection{Basics}
+
+Texdoc is a command line tool which find and view documentation in \texlive.
+If you type
+%
+\begin{quote}
+|texdoc «name»|
+\end{quote}
+%
+in your command line, the documentation of the |«name»| package will pop up. Of
+course, you have to replace |«name»| with the actual name of the package. To
+look up the documentation of more than one package at once, just use multiple
+|«name»|s as arguments.
+
+\subsection{Modes}\label{ss-modes}
+
+Texdoc has different modes that determine how results will be handled. In the
+default, ``view'' mode, it opens the first (supposedly the best) result with a
+suitable viewer. It is rather handy when you know what you want to read, and
+want to access it quickly. On the other hand, there may be other relevant
+documents for the given |«name»|, which are ignored in view mode.
+
+The so-called ``list mode'' makes Texdoc list all relevant documentation and
+ask you which one you want to view. It is useful when there another interesting
+sources of information besides the package's main documentation.
+
+There is also a ``mixed'' mode, intended to combine the best of view mode and
+list mode: if there is only one relevant result, then Texdoc opens it in a
+viewer, else it offers you a menu.
+
+By default, Texdoc hides the results it considers less relevant (unless it
+finds no relevant result at all). In ``showall'' mode, it always shows all
+results.
+
+To select the mode on the command-line, use |texdoc «option» «name»| with one
+of the following options: |-w| or |--view| for view mode, |-m| or |--mixed|
+for mixed mode, |-l| or |--list| for list mode, |-s| or |--showall| for
+showall mode.
+
+If you always (or mostly) use the same mode, you don't want to keep typing the
+same option. The next section describes how to customize Texdoc using
+configurations files.
+
+\subsection{Configuration files}\label{ss-quick-file}
+
+Use |texdoc --files| to know where to put your personal configuration file;
+you'll need to create this file (a possibly some directories) the first time.
+(If you want to know the full list of possible configuration files,
+see~\ref{ss-prec}.)
+
+In order to select you favorite mode, just insert a line |mode = «mode»| in
+this file, where |«mode»| is one of |view|, |mixed|, |list| or |showall|. To
+set your favorite language, use |lang = «2-letter code»|, though it is usually
+detected automatically.
+
+The configuration file can be used to tweak Texdoc in many ways, the most
+useful of which is probably the selection of the viewers for various types of
+documents, explained in the next section.
+
+\subsection{Viewers}\label{ss-viewer}
+
+Texdoc's mechanism for choosing a viewer varies according to your platform.
+On Windows, macOS, or Unix with KDE, GNOME, or XFCE, it uses your file
+associations like when you double-click files in the Explorer, the Finder or
+your default file manager (except for the text viewer, which is always a
+pager). Otherwise, it tries to find a viewer in the path from a list of
+``known'' viewers.
+
+You may want to select a different viewer for some kind of file. This is
+achieved by setting the various |viewer_«ext»| configuration options, where
+|«ext»| is the extension corresponding to the file type. For example, if you
+want to set xpdf as your default PDF viewer, and run it in the background,
+insert the line |viewer_pdf = xpdf %s &| in your configuration file. Here,
+|%s| stands for the name of the file to view.
+
+\subsection{You can stop reading now}
+
+Following parts explain Texdoc's mechanisms for finding the best results and
+how to customize them. We have been trying hard to optimize the default
+configuration values so that you normally don't need to fiddle with that, but
+you may be curious or have special needs.
+
+The final part is a full reference including a few points omitted in the
+present and next parts.
+
+\clearpage
+
+\section{File search, aliases, score}
+
+\subsection{An overview of how Texdoc works}
+
+When you type |texdoc «keyword»|, Texdoc first makes a list of files, from two
+sources:
+\begin{enumerate}
+ \item In the trees containing documentation (given by the
+ \href{https://www.tug.org/kpathsea/} {kpathsea} variable |TEXDOCS|), it
+ selects all files containing |«keyword»| in their name (including the
+ directory name);
+ \item In the \texlive Database, it looks for packages named
+ |«keyword»| or containing a file |«keyword».«ext»| where |«ext»| may be
+ |sty| or |cls|, and selects all the documentation files from this package.
+\end{enumerate}
+Files are filtered by extension: only files with known extensions may be
+selected. In case Texdoc cannot find any documentation here, fuzzy search will
+find the closest package name to the |«keyword»| and reselect the files (see
+\ref{ss-fuzzy}).
+
+The selected files are then score according to some simple heuristics. For
+example, a file named |«keyword».pdf|, is good, |«keyword»-«lang».pdf| will
+score higher if your favorite language |«lang»| is detected or configured,
+|«keyword»-doc| will be preferred over |«keyword»whatever|, files in a
+directory named exactly |«keyword»| get a bonus, etc.
+
+Score may also be adjusted base on file extensions or known names (or
+subwords): for example, by default, |Makefile|s get a very bad score since
+they are almost never documentation.\footnote{They often end up in the doc
+ tree, since the source of documentation is often in the same directory as
+ the documentation itself in \texlive. Other source files are discriminated
+ by extension.}
+
+Finally, depending on the mode, the file with the highest score is opened in a
+viewer, or the list of results is shown. Usually, only results with a positive
+score are displayed, except in showall mode. Results with very bad scores
+(-100 and below) are never displayed.
+
+\medskip
+
+This model for searching and scoring is quite efficient, but is unfortunately
+not perfect: Texdoc may sometimes need a hint, either to find a relevant file
+or, more likely, to recognize which of the files found is the most relevant.
+
+For example, assume you are looking for the documentation of the shortvrb
+{\LaTeX} package. Texdoc will find |shortvrb.sty| in the |latex| \texlive
+package, but since this package contains a lot of documentation files, none of
+which contains the string |shortvrb|, it will sort them basically at random.
+
+Here comes the notion of \emph{alias}: in the default configuration file,
+|shortvrb| is aliased to |base/doc|, so that when you type |texdoc shortvrb|,
+Texdoc knows it has to look primarily for |base/doc|. Note that Texdoc will
+also look for the original name, and that a name can be aliased to more than
+one new name.
+
+\medskip
+
+We will soon see how you can configure this, but let's start with a few
+definitions about how a file can match keyword (all matching is
+case-insensitive):
+\begin{enumerate}
+ \item The keyword is a substring of the file name.
+ \item The keyword is a ``subword'' of the file name; words are defined as
+ sequences of alphanumeric characters delimited by punctuation characters
+ (there is no space in file names in \texlive) and a subword is a
+ substring both ends of which are a word boundary.
+ \item The keyword matches ``exactly'' the file name: that is, the file
+ name is the keyword, possibly plus an extension.
+\end{enumerate}
+
+\subsection{Alias directives}\label{ss-alias}
+
+\begin{htcode}
+ alias «original keyword» = «name»
+ alias(«score») «original keyword» = «name»
+\end{htcode}
+
+You can define your own aliases in Texdoc's configuration files
+(see~\ref{ss-quick-file} or \ref{ss-prec}). For example,
+insert\footnote{Actually, you don't need to do this, the default configuration
+ file already includes this directive.}
+\begin{htcode}
+ alias shortvrb = base/doc
+\end{htcode}
+in order to alias |shortvrb| to |base/doc|. Precisely, it means that files in
+the doc trees matching exactly |base/doc| will be added to the result list
+when you look for |shortvrb|, and get a score of 10 (default score for alias
+results). This is greater than the results of heuristic scoring: it means that
+results found via aliases will always rank before results associated to the
+original keyword.
+
+If you want the results associated to a particular alias to have a custom
+score instead of the default 10, you can use the optional argument to the
+alias directive. This can be useful if you associate many aliases to
+a keyword and want one of them to show up first.
+
+Additionally, starting from with v0.80, aliases for |«keyword»-«lang»|, where
+|«lang»| is your preferred language's 2-letter code (as detected or
+configured, see the |lang| option) are also used for |«keyword»| and get a
+|+1| score upgrade.
+
+You can have a look at the configuration file provided (the last shown by
+|texdoc -f|) for examples. If you feel one of the aliases you defined locally
+should be added to the default configuration, please share it on the \tdml.
+
+Aliases are additive: if you define your own aliases for a keyword in your
+configuration file, and there are also aliases for the same keyword in the
+default configuration, they will add up. To prevent the default aliases
+from being applied for a particular keyword, include |stopalias «keyword»| in
+your personal configuration file. It will preserve the aliases defined before
+this directive (if any) but prevent all further aliasing on this keyword.
+
+\textit{Remark.} Aliasing is case-insensitive, and doesn't cascade:
+only aliases associated to the original keyword are used.
+
+\textbf{Warning.} Results found from aliases always have the score defined by
+the |alias| directive (10 by default), regardless of the adjustments described
+in the next subsections.
+
+\subsection{Score directives}\label{ss-score}
+
+\begin{htcode}
+ adjscore «pattern» = «score adjustment»
+ adjscore(«keyword») «pattern» = «score adjustment»
+\end{htcode}
+
+It is possible to adjust the score of results containing some pattern as a
+subword, either globally (for the result of all searches) or only when
+searching with a particular keyword. This is done in a configuration file
+(\ref{ss-quick-file} or \ref{ss-prec}) using the |adjscore| directive. Here are
+a few examples from the default configuration file.
+
+\begin{htcode}
+ adjscore /Makefile = -1000
+ adjscore /tex-virtual-academy-pl/ = -50
+ adjscore(tex) texdoc = -10
+\end{htcode}
+
+All files named |Makefile| (and also files named |Makefile-foo| if there are
+any) are ``killed'' : by adjusting their score with such a large negative
+value, their final score will most probably be less than -100, so they will
+never be displayed. Files from the |tex-virtual-academy-pl| directory, on the
+other hand, are not killed but just get a malus, since they are a common
+source of ``fake'' matches which hide better results (even for the lucky ones
+who can read polish).
+
+The third directive gives a malus for results containing |texdoc| only if the
+search keyword is |tex|. Otherwise, such results would get a high score
+because the heuristic scoring would think |texdoc| is the name of \TeX's
+documentation. The value -10 is enough to ensure that those results will have
+a negative score, so will not be displayed unless ``showall'' mode is active.
+
+\textbf{Warning}: Values of scores (like the default score for aliases, the
+range of heuristic scoring, etc.) may change in a future version of Texdoc.
+So, don't be surprised if you need to adapt your scoring directives after a
+future update of Texdoc. This warning will hopefully disappear at some point.
+
+\subsection{File extensions and names}\label{ss-ext}
+
+The allowed file extensions are defined by the configuration item |ext_list|
+(default: pdf, html, htm, txt, md, ps, dvi, no extension). You can configure it
+with a line |ext_list = «your, list»| in a configuration file. Be aware
+that it will completely override the default list, not add to it. An empty
+string in the list means files without extension (no dot in the name), while a
+star means any extension.
+
+For scoring purposes, there is also a |badext_list| parameter: files whose
+extension is ``bad'' according to this list will get a lesser score (currently
+0).
+
+Unfortunately, sometimes what follows a dot in a file name is not a ``real''
+extension. This often happens with readme files, for example |readme.fr| or
+|readme.texlive|. So, in addition to his list of known extensions, Texdoc has
+a list of known basenames, by default just |readme|.
+
+The corresponding settings are |basename_list| and |badbasename_list|; both
+are similar to |ext_list| and |badext_list|. So, a file will be selected if
+either its extension or its base name is known, and get a lesser score if
+either is known to be ``bad.''
+
+\subsection{Variants}\label{ss-variants}
+
+The documentation for a given package is often found in a file named like
+|«package»-doc|. To handle this properly, Texdoc gives a special score files
+named |«package»«suffix»| where |«suffix»| is one element of the list given by
+the configuration setting |suffix_list|.
+
+To customise this list, add a line with |suffix_list = «your, list»| in a
+configuration file. Be warned, it will replace the default list, no expand
+it. You'll find the default list in the shipped configuration file; feel free
+to suggest additions on the \tdml (with a real-life example).
+
+\subsection{Fuzzy search}\label{ss-fuzzy}
+
+When the normal search can't find any document in {\texlive}, Texdoc will
+execute fuzzy search without user-interactions. The fuzzy search finds the
+closest name of package in {\texlive}\footnote{Note that the feature searches
+only package names at this point. Other objects such as aliases cannot be found
+by the fuzzy search.} to the input |«keyword»|. The results of fuzzy search are
+shown by as an informational message (you can see that with option |-v|).
+
+The default allowance of Levenshtein distance is 5. You can change this default
+value by specifying |fuzzy_level| in your |texdoc.cnf| (see
+\ref{cf-fuzzy_level}). Results of fuzzy search could be irreproducible if
+multiple strings have the same Levenshtein distance.
+
+\clearpage
+
+\section{Full reference}
+
+\subsection{Precedence of configuration sources}\label{ss-prec}
+
+Values for a particular setting can come from several sources. The sources are
+treated in the following order and the first value found is always used:
+\begin{enumerate}
+ \item Command-line options.
+ \item Environment variables ending with |_texdoc|.
+ \item Other environment variables.
+ \item Values from configuration files (see below).
+ \item Hard-coded defaults that may depend on the current machine.
+\end{enumerate}
+
+The configuration files are found in the directories |TEXMF/texdoc|, where
+|TEXMF| is the kpathsea variable, in the order given by this variable. Inside
+each directory, three files are recognized, in this order:
+\begin{enumerate}
+ \item |texdoc-«platform».cnf| where |«platform»| is the name of the current
+ platform (defined as the name of the directories where the \texlive
+ binaries are located, for example |x86-64-linux|). This may be useful when
+ an installation is shared across machines with different architectures
+ needing different settings, for example for viewers. Their use is not
+ recommended in any other situation.
+ \item |texdoc.cnf| is the recommended file for normal use.
+ \item |texdoc-dist.cnf| is useful for installing a newer version of texdoc
+ (including its default configuration file) in your home while retaining
+ the use of the previous file for your personal setting; see
+ \href{https://github.com/TeX-Live/texdoc}{our GitHub repository} for
+ instructions on running the development version.
+\end{enumerate}
+
+\subsection{Command-line options}\label{ss-cl}
+
+All command-line options (except the first four below) correspond to
+configuration item that can be set in the configuration files: we refer
+the reader to the corresponding section for the meaning of this configuration
+item.
+
+\begin{cmdsubsub}{-h, --help}{cl-h}
+ -h, --help
+\end{cmdsubsub}
+
+Shows a quick help message (namely a list of command-line options) and exit
+successfully.
+
+\begin{cmdsubsub}{-V, --version}{cl-V}
+ -V, --version
+\end{cmdsubsub}
+
+Shows the current version of the program and exit successfully.
+
+\begin{cmdsubsub}{-f, --files}{cl-f}
+ -f, --files
+\end{cmdsubsub}
+
+Shows the list of configuration files for the current installation and
+platform, with their status and exit successfully. Normally, only ``active''
+and ``disabled'' files are shown (see~\ref{cf-lastfile_switch}). To show ``not
+found'' files as well, you can use |--verbose|.
+
+\begin{cmdsubsub}{--just-view}{cl-just-view}
+ --just-view «file»
+\end{cmdsubsub}
+
+Open «file» in the usual viewer. The file should be given with full path,
+absolutely no searching is done. This option is not really meant for users,
+but rather intended to be used from another program, like a GUI front-end to
+Texdoc.
+
+\begin{cmdsubsub}{-w, -l, -m, -s, --view, --list, --mixed, --showall}{cl-mode}
+ -w, --view, -l, --list, -m, --mixed, -s, --showall
+\end{cmdsubsub}
+
+Set |mode| to the given value, see~\ref{cf-mode}.
+
+\begin{cmdsubsub}{-i, -I, --interact, --nointeract}{cl-i}
+ -i, --interact, -I, --nointeract
+\end{cmdsubsub}
+
+Set |interact_switch| to true (or false), see~\ref{cf-interact_switch}.
+
+\begin{cmdsubsub}{-M, --machine}{cl-M}
+ -M, --machine
+\end{cmdsubsub}
+
+Set |machine_switch| to true, see~\ref{cf-machine_switch}.
+
+\begin{cmdsubsub}{-q, --quiet}{cl-q}
+ -q, --quiet
+\end{cmdsubsub}
+
+Set |verbosity_level| to minimum, see~\ref{cf-verbosity_level}.
+
+\begin{cmdsubsub}{-v, --verbose}{cl-v}
+ -v, --verbose
+\end{cmdsubsub}
+
+Set |verbosity_level| to maximum, see~\ref{cf-verbosity_level}.
+
+\begin{cmdsubsub}{-d, -D, --debug}{cl-d}
+ -d «list», --debug=«list», -D, --debug
+\end{cmdsubsub}
+
+Set |debug_list|, see~\ref{cf-debug_list}. If you specify |-D| or |--debug|
+without specifying a list, activates all available debug items.
+
+\begin{cmdsubsub}{-c}{cl-c}
+ -c «name»=«value»
+\end{cmdsubsub}
+
+Set configuration item |«name»| to |«value»|. This is a general way to access
+any configuration items listed in \ref{ss-conf} from command line.
+
+\subsection{Environment variables}\label{ss-envvar}
+
+They all correspond to some |viewer_«ext»| setting, and the reader is referred
+to~\ref{cf-viewer_*} for details.\footnote{Old names of environment variables,
+namely |TEXDOCVIEW_{html,dvi,md,txt,pdf,ps}| and
+|TEXDOC_VIEWER_{HTML,DVI,MD,TXT,PDF,PS}|, are deprecated but still work.} You can
+append |_texdoc| to every name in the first column: this wins over every other
+name.
+
+\begin{center}
+\begin{tabular}{ll}
+Environment variables & Configuration items \\
+|BROWSER| & |viewer_html| \\
+|DVIVIEWER| & |viewer_dvi| \\
+|MDVIEWER| & |viewer_md| \\
+|PAGER| & |viewer_txt| \\
+|PDFVIEWER| & |viewer_pdf| \\
+|PSVIEWER| & |viewer_ps| \\
+\end{tabular}
+\end{center}
+
+Also, on Unix systems, locale-related variables such as |LANG| and |LC_ALL|
+are used for the default value of |lang|.
+
+\subsection{Configuration items}\label{ss-conf}
+
+\subsubsection{Structure of configuration files}\label{sss-sonf-struct}
+
+Configuration files are line-oriented text files. Comments begin with a |#|
+and run to the end of line. Lines containing only space are ignored. Space at
+the beginning or end of a line, as well as around an |=| sign, is ignored.
+Apart from comments and empty lines, each line must be of one of the following
+forms.
+
+\begin{htcode}
+ «configuration item» = «value»
+ alias «original keyword» = «name»
+ alias(«score») «original keyword» = «name»
+ stopalias «original keyword»
+ adjscore «pattern» = «score adjustment»
+ adjscore(«keyword») «pattern» = «score adjustment»
+\end{htcode}
+
+We will concentrate on the |«configuration item»| part here, since other
+directives have already been presented (\ref{ss-alias} and \ref{ss-score}).
+
+In the above, |«value»| never needs to be quoted: quotes would be interpreted
+as part of the value, not as quotation marks (this also holds for the other
+directives).
+
+Lines which do not obey these rules raise a warning, as well as unrecognised
+values of |«configuration item»|. The |«value»| can be an arbitrary string,
+except when the name of the |«configuration item»| ends with:
+\begin{enumerate}
+ \item |_list|, then |«value»| is a coma-separated list of strings. Space
+ around commas is ignored. Two consecutive comas or a coma at the beginning
+ or end of the list means the empty string at the corresponding place.
+ \item |_switch|, then |«value»| must be either |true| or |false|
+ (lowercase).
+ \item |_level|, then |«value»| is an integer.
+\end{enumerate}
+In these cases, an improper |«value»| will raise a warning too.
+
+\begin{cmdsubsub}{mode}{cf-mode}
+ mode = «view, list, mixed, showall»
+\end{cmdsubsub}
+Set the mode to the given value. Default is |view|. The various modes
+have been presented in~\ref{ss-modes}.
+
+\begin{cmdsubsub}{interact}{cf-interact_switch}
+ interact_switch = «true, false»
+\end{cmdsubsub}
+
+Turn on or off interaction. Default is on. Turning interaction off prevents
+Texdoc from asking you to choose a file to view when there are multiple
+choices, so it just prints the list of files found.
+
+\begin{cmdsubsub}{suffix_list}{cf-suffix_list}
+ suffix_list = «list»
+\end{cmdsubsub}
+
+Set the list of known suffixes to |«list»| (see~\ref{ss-variants}). Default is
+the empty list, but see the shipped configuration file for more.
+
+\begin{cmdsubsub}{ext_list}{cf-ext_list}
+ ext_list = «list»
+\end{cmdsubsub}
+
+Set the list of recognised extensions to |«list»|. The default value is:
+%
+\begin{htcode}
+ pdf, html, htm, txt, md, dvi, ps,
+\end{htcode}
+%
+This list is used to filter and sort the results that have the same
+score (with the default value: pdf first, etc). Two special values are
+recognised:
+\begin{itemize}
+ \item \emph{The empty element}. This means files without extensions, or more
+ precisely without a dot in their name. This is meant for files like
+ |README|, etc. The file is assumed to be plain text for viewing purpose.
+ \item |*| means any extension. Of course if it is present in the list, it
+ can be the only element!
+\end{itemize}
+
+There is a very special case: if the searched |«name»| has |.sty| extension,
+Texdoc enters a special search mode for |.sty| files (not located in the same
+place as real documentation files) for this |«name»|, independently of the
+current value of |ext_list| and |mode|. In an ideal world, this wouldn't be
+necessary since every sty file would have a proper documentation in pdf, html
+or plain text, but\dots
+
+For each |«ext»| in |ext_list| there should be a corresponding |viewer_«ext»|
+value set. Defaults are defined corresponding to the default |ext_list|, but
+you can add values if you want. For example, if you want Texdoc to be able
+to find man pages and display them with the |man| command, you can use
+\begin{htcode}
+ ext_list = pdf, html, htm, 1, 5, txt, md, dvi, ps,
+ viewer_1 = man
+ viewer_5 = man
+\end{htcode}
+
+As a special case, if the extension is |sty|, then the |txt| viewer is used;
+similarly, if it is |htm| the |html| viewer is used. Otherwise, the |txt|
+viewer is used and a warning is issued.
+
+\begin{cmdsubsub}{badext_list}{cf-badext_list}
+ badext_list = «list»
+\end{cmdsubsub}
+
+Set the list of ``bad'' extensions to |«list»|. Default is ``|txt,|''. Files
+with those extensions get a malus of |1| on their heuristic score if it was
+previously positive.
+
+\begin{cmdsubsub}{basename_list}{cf-basename_list}
+ basename_list = «list»
+\end{cmdsubsub}
+
+Set the list of ``known'' base names to |«list»|. Default is ``|readme|''.
+Files with those base names are selected regardless of their extension. If the
+extension is unknown, the text viewer will be used to view the file.
+
+\begin{cmdsubsub}{badbasename_list}{cf-badbasename_list}
+ badbasename_list = «list»
+\end{cmdsubsub}
+
+Set the list of ``bad'' base names to |«list»|. Default is ``|readme|''. Files
+with those names get a malus of |1| on their heuristic score if it was
+previously positive.
+
+\begin{cmdsubsub}{viewer_*}{cf-viewer_*}
+ viewer_«ext» = «cmd»
+\end{cmdsubsub}
+
+Set the viewer command for files with extension |«ext»| to |«cmd»|. For files
+without extension, |viewer_txt| is used, and there's no |viewer_| variable.
+In |«cmd»|, |%s| can be used as a placeholder for the file name, which is
+otherwise inserted at the end of the command. The command can be an arbitrary
+shell construct.
+
+\begin{cmdsubsub}{lang}{cf-lang}
+ lang = «2-letter code»
+\end{cmdsubsub}
+
+Set you preferred language. Defaults to your system's locale.
+
+\begin{cmdsubsub}{verbosity_level}{cf-verbosity_level}
+ verbosity_level = «n»
+\end{cmdsubsub}
+
+Set the verbosity level to |«n»| (default: 2). At level~3, errors, warnings and
+informational messages will be printed on stderr; 2 means only errors and
+warnings, 1 only errors and 0 nothing except internal errors (obviously not
+recommended).
+
+\begin{cmdsubsub}{debug_list}{cf-debug_list}
+ debug_list = «list»
+\end{cmdsubsub}
+
+Set the list of activated debug items (default: none). Available debug items
+are |config|, |files|, |search|, |score|, |texdocs|, |tlpdb|, |version|,
+|view|, and |all| to activate all of these. Implies |--verbose|. Debug
+information are printed on standard error.
+
+\begin{cmdsubsub}{max_line}{cf-max_lines}
+ max_lines = «number»
+\end{cmdsubsub}
+
+Set the maximum number of results to be printed without confirmation in list,
+mixed or showall mode (default: 20). This setting has no effect if interaction
+is disabled.
+
+\begin{cmdsubsub}{machine_switch}{cf-machine_switch}
+ machine_switch = «true, false»
+\end{cmdsubsub}
+
+Turn on or off machine-readable output (default: off). With this option
+active, the value of |interact_switch| is forced to |false|, and each line of
+output is
+\begin{htcode}
+ «argument»\t«score»\t«filename»
+\end{htcode}
+where |«argument»| is the name of the argument to which the results correspond
+(mainly useful if there were many arguments), |\t| is the tab (ASCII \#9)
+character, and the other entries are pretty self-explanatory. Nothing else is
+printed on stdout, except if an internal error occurs (in which case exit code
+will be 1). In the future, more tab-separated fields may be added at the end
+of the line, but the first 3 fields will remain unchanged.
+
+Currently, there are two additional fields: a two-letter language code, and an
+unstructured description, both taken from the CTAN catalogue (via the \texlive
+database). These fields may be empty, and they are not guaranteed to keep the
+same meaning in future versions of Texdoc.
+
+\begin{cmdsubsub}{zipext_list}{cf-zipext_list}
+ zipext_list = «list»
+\end{cmdsubsub}
+
+List of supported extensions for zipped files (default: empty). Allows
+compressed files with names like |foobar.«zip»|, with |«zip»| in the given
+|«list»|, to be found and unzipped before the viewer is started (the
+temporary file will be destroyed right after).
+
+\textbf{Warning.} Support for zipped documentation is not meant to work on
+windows, a Unix shell is assumed! If you add anything to this list, please make
+sure that you also set a corresponding |unzip=«ext»| value for each |«ext»| in
+the list. At the same time, make sure you are using blocking (i.e., not
+returning immediately) viewers.
+
+\textit{Remark.} \texlive doesn't ship compressed documentation files, so
+this option is mainly useful with re-packaged version of \texlive that do,
+for example in Linux distributions.
+
+\begin{cmdsubsub}{unzip_*}{cf-unzip_star}
+ unzip_«zipext» = «command»
+\end{cmdsubsub}
+
+The unzipping command for compressed files with extension |«zipext»| (default:
+none). Define one for each item in |zipext_list|. The command must print
+the result on stdout, like |gzip -d -c| does.
+
+\begin{cmdsubsub}{rm_file, rm_dir}{cf-rm_star}
+ rm_file = «command»
+ rm_dir = «command»
+\end{cmdsubsub}
+
+Commands for removing files (resp. directories) on your system (defaults:
+|rm -f| and |rmdir|). Only useful for zipped documents (see |zipext_list|).
+
+\begin{cmdsubsub}{lastfile_switch}{cf-lastfile_switch}
+ lastfile_switch = «true, false»
+\end{cmdsubsub}
+
+If set to true, prevents Texdoc from reading any other configuration file after
+this one (they will be reported as ``disabled'' by |texdoc -f|). Mainly useful
+for installing a newer version of Texdoc in your home and preventing the
+default configuration file from older versions to be used (see the
+\href{https://github.com/TeX-Live/texdoc}{README} for instructions on how to do
+so).
+
+\begin{cmdsubsub}{fuzzy_level}{cf-fuzzy_level}
+ fuzzy_level = «n»
+\end{cmdsubsub}
+
+Set the allowance of Levenshtein distance to |«n»| for the fuzzy search
+(default: 5). At level 0, the fuzzy search feature is disabled.
+
+\subsection{Exit codes}\label{ss-exit}
+
+The current exit codes are:
+\begin{enumerate}[start=0]
+ \item Success.
+ \item Internal error.
+ \item Usage error.
+ \item No documentation found.
+\end{enumerate}
+
+\section{Licence}\label{s-licence}
+
+The current version of Texdoc program and its documentation are copyright 2008
+Manuel Pégourié-Gonnard, Takuto Asakura, and the {\texlive} Team.
+
+They are 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
+\emph{without any warranty}; without even the implied warranty of
+\emph{merchantability} or \emph{fitness for a particular purpose}. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see \url{http://www.gnu.org/licenses/}.
+
+\bigskip
+
+Previous work (Texdoc program) in the public domain:
+\begin{itemize}
+ \item Contributions from Reinhard Kotucha (2008).
+ \item First texlua versions by Frank Küster (2007).
+ \item Original shell script by Thomas Esser, David Aspinall, and Simon
+ Wilkinson.
+\end{itemize}
+
+\bigskip
+\begin{center}\Large\bfseries
+ Happy {\TeX}ing!
+\end{center}
+
+\end{document}
+% vim: ambiwidth=single spell:
diff --git a/support/texdoc/script/texdoc.tlu b/support/texdoc/script/texdoc.tlu
new file mode 100755
index 0000000000..b859868236
--- /dev/null
+++ b/support/texdoc/script/texdoc.tlu
@@ -0,0 +1,17 @@
+#!/usr/bin/env texlua
+
+-- texdoc.tlu: the main program of texdoc
+--
+-- The TeX Live Team, GPLv3, see texdoclib.tlu for details
+
+-- Note: we keep this file small as much as possible so that make it easier
+-- to install a new version of texdoc in TEXMFHOME.
+
+-- setup the kpse library and load texdoclib
+kpse.set_program_name(arg[-1], 'texdoc')
+local texdoc = require('texdoclib')
+
+-- execute
+texdoc.cli.exec()
+
+-- vim: ft=lua:
diff --git a/support/texdoc/script/texdoclib-alias.tlu b/support/texdoc/script/texdoclib-alias.tlu
new file mode 100644
index 0000000000..16c375b3ba
--- /dev/null
+++ b/support/texdoc/script/texdoclib-alias.tlu
@@ -0,0 +1,127 @@
+-- texdoclib-alias.tlu: alias handling for texdoc
+--
+-- The TeX Live Team, GPLv3, see texdoclib.tlu for details
+
+--[[ structure of the alias table
+
+alias = {
+ name1 = {<true or nill> stop, <aliasentry> aliasentry1, ...},
+ ...
+}
+stop == true means further alias directives should be ignored
+
+aliasentry = {
+ name = <string> pattern to be matched,
+ score = <number or nil> associated score,
+ original = <true or nil> is this the original keyword?,
+ locale = <true or nil> is this entry found via config.lang?
+}
+score == nil means to use the default score (defined in texdoclib-score.tlu)
+
+--]]
+
+-- dependencies
+local texdoc = {
+ config = require('texdoclib-config'),
+}
+
+-- shortcuts
+local M = {}
+
+-- alias is local to this file
+local alias = {}
+
+-- turn a name into a suitable alias entry
+-- if score is 'false', this is the original name
+local function make_alias(pat, score)
+ local al = {}
+ al.name = pat
+ if score == false then
+ al.original = true
+ else
+ al.score = score -- may be nil
+ end
+ return al
+end
+
+-- add an alias value for a key
+local function add_alias(key, value, score)
+ local k = string.lower(key)
+ alias[k] = alias[k] or {make_alias(key, false)}
+ if alias[k].stop then return end
+ table.insert(alias[k], make_alias(value, score))
+end
+
+-- prevent a key from being further aliased
+local function stop_alias(key)
+ local k = string.lower(key)
+ alias[k] = alias[k] or {}
+ alias[k].stop = true
+end
+
+-- get patterns for a name
+function M.get_patterns(name, no_alias)
+ local n = string.lower(name)
+
+ -- get normal aliases
+ local res
+ if alias[n] and not no_alias then
+ res = alias[n]
+ else
+ res = {make_alias(name, false)}
+ end
+
+ -- check for language-specific aliases
+ local config_lang = texdoc.config.get_value('lang')
+ local lang = config_lang and alias[n .. '-' .. config_lang]
+ if lang then
+ for _, entry in ipairs(lang) do
+ if not entry.original then
+ table.insert(res, {
+ name = entry.name,
+ score = entry.score,
+ locale = true,
+ })
+ end
+ end
+ end
+
+ return res
+end
+
+-- interpret a confline as an alias setting or return false
+function M.confline_to_alias(line, file, pos)
+ -- alias directive without score
+ local key, val = string.match(line, '^alias%s+([%w%p]+)%s*=%s*(.+)')
+ if key and val then
+ add_alias(key, val)
+ return true
+ end
+ -- alias directive with score
+ local score, key, val = string.match(line,
+ '^alias%(([%d+-.]+)%)%s+([%w%p]+)%s*=%s*(.+)')
+ if score then score = tonumber(score) end
+ if key and val and score then
+ add_alias(key, val, score)
+ return true
+ end
+ -- stopalias directive
+ local key = string.match(line, '^stopalias%s+(.+)')
+ if key then
+ stop_alias(key)
+ return true
+ end
+ return false
+end
+
+-- iterator over the list of keys in the alias table
+-- Note: currently this function is not used (for debug)
+function M.aliased_names()
+ return function(_, cur)
+ return (next(alias, cur))
+ end
+end
+
+return M
+
+-- vim: ft=lua:
diff --git a/support/texdoc/script/texdoclib-cli.tlu b/support/texdoc/script/texdoclib-cli.tlu
new file mode 100644
index 0000000000..3de786ef2c
--- /dev/null
+++ b/support/texdoc/script/texdoclib-cli.tlu
@@ -0,0 +1,219 @@
+-- texdoclib-cli.tlu: command line interfaces for texdoc
+--
+-- The TeX Live Team, GPLv3, see texdoclib.tlu for details
+
+-- dependencies
+local texdoc = {
+ const = require('texdoclib-const'),
+ util = require('texdoclib-util'),
+ config = require('texdoclib-config'),
+ search = require('texdoclib-search'),
+ view = require('texdoclib-view'),
+}
+
+-- shortcuts
+local M = {}
+local C = texdoc.const
+local err_print = texdoc.util.err_print
+
+-------------------------- parsing options --------------------------
+
+-- modified Alternative GetOpt
+-- cf. http://lua-users.org/wiki/AlternativeGetOpt
+local function getopt(arg, options)
+ local tmp
+ local tab = {}
+ local saved_arg = {table.unpack(arg)}
+
+ for k, v in ipairs(saved_arg) do
+ if string.sub(v, 1, 2) == '--' then
+ table.remove(arg, 1)
+ local x = string.find(v, '=', 1, true)
+ if x then
+ table.insert(tab, {string.sub(v, 3, x-1), string.sub(v, x+1)})
+ else
+ table.insert(tab, {string.sub(v, 3), true})
+ end
+ elseif string.sub(v, 1, 1) == '-' then
+ table.remove(arg, 1)
+ local y = 2
+ local l = string.len(v)
+ local jopt
+ while (y <= l) do
+ jopt = string.sub(v, y, y)
+ if string.find(options, jopt, 1, true) then
+ if y < l then
+ tmp = string.sub(v, y+1)
+ y = l
+ else
+ table.remove(arg, 1)
+ tmp = saved_arg[k + 1]
+ end
+ if string.match(tmp, '^%-') then
+ table.insert(tab, {jopt, false})
+ else
+ table.insert(tab, {jopt, tmp})
+ end
+ else
+ table.insert(tab, {jopt, true})
+ end
+ y = y + 1
+ end
+ end
+ end
+
+ return tab
+end
+
+local function parse_options()
+ local curr_arg
+ local action = true
+ local cl_config = {}
+
+ local function insert_cl_config(key, val, opt_name)
+ table.insert(cl_config, {key, val, opt_name})
+ end
+
+ -- actual parsing
+ opts = getopt(arg, 'cd')
+
+ for _, tp in ipairs(opts) do
+ local k, v = tp[1], tp[2]
+ if #k == 1 then
+ curr_arg = '-' .. k
+ else
+ curr_arg = '--' .. k
+ end
+
+ -- action
+ if (curr_arg == '-h') or (curr_arg == '--help') then
+ action = 'help'
+ elseif (curr_arg == '-V') or (curr_arg == '--version') then
+ action = 'version'
+ elseif (curr_arg == '-f') or (curr_arg == '--files') then
+ action = 'files'
+ elseif curr_arg == '--just-view' then
+ action = 'view'
+
+ -- mode
+ elseif (curr_arg == '-w') or (curr_arg == '--view') then
+ insert_cl_config('mode', 'view', curr_arg)
+ elseif (curr_arg == '-m') or (curr_arg == '--mixed') then
+ insert_cl_config('mode', 'mixed', curr_arg)
+ elseif (curr_arg == '-l') or (curr_arg == '--list') then
+ insert_cl_config('mode', 'list', curr_arg)
+ elseif (curr_arg == '-s') or (curr_arg == '--showall') then
+ insert_cl_config('mode', 'showall', curr_arg)
+
+ -- interaction
+ elseif (curr_arg == '-I') or (curr_arg == '--nointeract') then
+ insert_cl_config('interact_switch', 'false', curr_arg)
+ elseif (curr_arg == '-i') or (curr_arg == '--interact') then
+ insert_cl_config('interact_switch', 'true', curr_arg)
+
+ -- output format
+ elseif (curr_arg == '-M') or (curr_arg == '--machine') then
+ insert_cl_config('machine_switch', 'true', curr_arg)
+
+ -- config
+ elseif curr_arg == '-c' then
+ local item, value = string.match(v, '^([%a%d_]+)%s*=%s*(.+)')
+ insert_cl_config(item, value, curr_arg)
+
+ -- debug
+ elseif (curr_arg == '-d') or (curr_arg == '--debug') then
+ if v == true then v = 'all' end
+ insert_cl_config('debug_list', v, curr_arg)
+ elseif curr_arg == '-D' then
+ insert_cl_config('debug_list', 'all', curr_arg)
+
+ -- verbosity
+ elseif (curr_arg == '-q') or (curr_arg == '--quiet') then
+ insert_cl_config('verbosity_level', C.min_verbosity, curr_arg)
+ elseif (curr_arg == '-v') or (curr_arg == '--verbose') then
+ insert_cl_config('verbosity_level', C.max_verbosity, curr_arg)
+
+ -- having trouble
+ else
+ err_print('error', 'unknown option: ' .. curr_arg)
+ err_print('error', C.error_msg)
+ return false
+ end
+ end
+
+ return action, cl_config
+end
+
+-------------------------- process execution --------------------------
+
+-- handling actions
+local function do_action(action)
+ if action == 'help' then
+ texdoc.util.print_usage()
+ os.exit(C.exit_ok)
+ elseif action == 'version' then
+ print(string.format(
+ '%s %s (%s)', C.progname, C.version, C.release_date) ..
+ '\n\n' .. C.copyright_msg)
+ os.exit(C.exit_ok)
+ elseif action == 'files' then
+ print(C.fullname .. ' ' .. C.version)
+ texdoc.config.show_config_files(true)
+ os.exit(C.exit_ok)
+ elseif action == 'view' then
+ if not arg[1] then
+ err_print('error', 'Missing file operand to --just-view.')
+ err_print('error', C.error_msg)
+ os.exit(C.exit_usage)
+ end
+ texdoc.view.view_file(arg[1])
+ os.exit(C.exit_ok)
+ end
+end
+
+-- the main loop
+local function do_texdoc()
+ texdoc.search.init_databases()
+
+ for _, docname in ipairs(arg) do
+ -- do we have more then one argument?
+ local multiarg = not not arg[2]
+ -- get results
+ local doclist = texdoc.search.get_doclist(docname)
+ -- deliver results to the user
+ texdoc.view.deliver_results(docname, doclist, multiarg)
+ end
+end
+
+-------------------------- the main function --------------------------
+
+function M.exec()
+ -- parsing command line options
+ local action, cl_config = parse_options()
+
+ if not action then
+ os.exit(C.exit_usage)
+ end
+
+ -- setup config and alias
+ texdoc.config.setup_config_and_alias(cl_config)
+
+ -- special action
+ do_action(action)
+
+ -- do we actually have arguments?
+ if not arg[1] then
+ err_print('error', 'No action specified.')
+ err_print('error', C.error_msg)
+ os.exit(C.exit_usage)
+ end
+
+ -- the main feature
+ do_texdoc()
+
+ os.exit(C.exit_ok)
+end
+
+return M
+
+-- vim: ft=lua:
diff --git a/support/texdoc/script/texdoclib-config.tlu b/support/texdoc/script/texdoclib-config.tlu
new file mode 100644
index 0000000000..471fef9fdc
--- /dev/null
+++ b/support/texdoc/script/texdoclib-config.tlu
@@ -0,0 +1,559 @@
+-- texdoclib-config.tlu: handling config of texdoc
+--
+-- The TeX Live Team, GPLv3, see texdoclib.tlu for details
+
+-- shortcuts
+local M = {}
+local C = require('texdoclib-const')
+
+-- config is local to this file
+local config = {}
+
+-------------------------- handling dependencies --------------------------
+
+-- in this file, dependencies should be required ondemand to prevent infinite
+-- recursion.
+
+local texdoc = {}
+
+-- import specified function from the submodule
+local function import_function(mod, func)
+ texdoc[mod] = texdoc[mod] or require('texdoclib-' .. mod)
+ return texdoc[mod][func]
+end
+
+-------------------------- hide the config table --------------------------
+
+-- config is read-only (but not "deep" read-only)
+local function set_read_only(table, name)
+ assert(next(table) == nil,
+ 'Internal error: ' .. name .. ' should be empty at this point.')
+ local ro = 'Internal error: attempt to update read-only table '
+ local real = {}
+ setmetatable(table, {
+ __index = real,
+ __newindex = function() error(ro .. name .. '.') end,
+ })
+ return function(k, v) real[k] = v end
+end
+local real_set_config = set_read_only(config, 'config')
+
+-- the accessor
+function M.get_value(key) return config[key] end
+
+------------------------- general config functions ------------------------
+
+-- interpreting 'context' in this section
+local function context_to_string(context)
+ if not context then return '(no context)' end
+ if context.src == 'cl' then
+ return 'from command line option "' .. context.name .. '"'
+ elseif context.src == 'env' then
+ return 'from environment variable "' .. context.name .. '"'
+ elseif context.src == 'loc' then
+ return 'from operating system locale'
+ elseif context.src == 'file' then
+ return 'in file "' .. context.file .. '" on line ' .. context.line
+ elseif context.src == 'def' then
+ return 'from built-in defaults'
+ else
+ return 'from unkown source (should not happen, please report)'
+ end
+end
+
+-- a helper function for warning messages in this section
+local function config_warn(key, value, context, unknown)
+ local err_print = import_function('util', 'err_print')
+ local begin = unknown
+ and string.format('Unknown option "%s"', key)
+ or string.format('Illegal value "%s" for option "%s"',
+ key, tostring(value))
+ texdoc.util.err_print('warning',
+ '%s %s. Skipping.', begin, context_to_string(context))
+end
+
+-- set a config parameter, but don't overwrite it if already set
+-- three special types: *_list (list), *_switch (boolean), *_level (number)
+function M.set_config_element(key, value, context)
+ local dbg_print = import_function('util', 'dbg_print')
+ local parse_error = false
+
+ local is_known = false -- is key a valid option?
+ local option
+ for _, option in ipairs(C.known_options) do
+ if string.match(key, '^' .. option .. '$') then
+ is_known = true
+ break
+ end
+ end
+
+ -- warn and exit if key is not a known option
+ if not is_known then config_warn(key, nil, context, true) return end
+
+ -- exit if key is already set (/!\ must test for nil, not false)
+ if not (config[key] == nil) then
+ if context.src ~= 'def' then
+ dbg_print('config', 'Ignoring "%s=%s" %s.',
+ key, tostring(value), context_to_string(context))
+ end
+ return nil
+ end
+
+ -- record the source of the setting
+ real_set_config(key .. '_src', context.src)
+
+ -- detect the type of the key
+ if string.match(key, '_list$') then
+ -- coma-separated list
+ local values
+ if value == '' then
+ values = {}
+ else
+ values = string.explode(value, ',')
+ end
+
+ local inverse = {}
+ for i, j in ipairs(values) do -- sanitize values...
+ j = string.gsub(j, '%s*$', '')
+ j = string.gsub(j, '^%s*', '')
+ values[i] = j
+ inverse[j] = i -- ... and build inverse mapping on the way
+ end
+
+ real_set_config(key, values)
+ real_set_config(key .. '_inv', inverse)
+ real_set_config(key .. '_max', #values)
+ elseif string.find(key, '_switch$') then
+ -- boolean
+ if value == 'true' then
+ real_set_config(key, true)
+ elseif value == 'false' then
+ real_set_config(key, false)
+ else
+ config_warn(key, value, context)
+ parse_error = true
+ end
+ elseif string.find(key, '_level$') then
+ -- integer
+ local val = tonumber(value)
+ if val then
+ real_set_config(key, val)
+ else
+ config_warn(key, value, context)
+ parse_error = true
+ end
+ else -- string
+ real_set_config(key, value)
+ end
+
+ -- special case: if we just set debug_list, print version info now
+ if key == 'debug_list' then
+ dbg_print('version', '%s v%s', C.fullname, C.version)
+ end
+
+ -- now tell what we have just done, for debugging
+ if not parse_error then
+ dbg_print('config', 'Setting "%s=%s" %s.',
+ key, tostring(value), context_to_string(context))
+ end
+end
+local set_config_element = M.set_config_element
+
+-- set a whole list, also without overwriting
+local function set_config_list(conf, context)
+ for key, value in pairs(conf) do
+ set_config_element(key, value, context)
+ end
+end
+
+------------------------ config from command line ------------------------
+
+-- set config from the command line
+-- Note: Make sure to set a default value in setup_config_from_defaults()
+-- if relevant.
+local function setup_config_from_cl(cl_config)
+ for _, e in ipairs(cl_config) do
+ set_config_element(e[1], e[2], {src='cl', name=e[3]})
+ end
+end
+
+------------------------- config from environment --------------------------
+
+-- set config from environment if available
+local function setup_config_from_env()
+ local function set_config_elt_from_vars(key, vars)
+ for _, var in ipairs(vars) do
+ local value = os.getenv(var)
+ if value then
+ set_config_element(key, value, {src='env', name=var})
+ end
+ end
+ end
+ set_config_elt_from_vars('viewer_pdf',
+ {'PDFVIEWER_texdoc', 'PDFVIEWER', 'TEXDOCVIEW_pdf', 'TEXDOC_VIEWER_PDF'})
+ set_config_elt_from_vars('viewer_ps',
+ {'PSVIEWER_texdoc', 'PSVIEWER', 'TEXDOCVIEW_ps', 'TEXDOC_VIEWER_PS'})
+ set_config_elt_from_vars('viewer_dvi',
+ {'DVIVIEWER_texdoc', 'DVIVIEWER', 'TEXDOCVIEW_dvi', 'TEXDOC_VIEWER_DVI'})
+ set_config_elt_from_vars('viewer_html',
+ {'BROWSER_texdoc', 'BROWSER', 'TEXDOCVIEW_html', 'TEXDOC_VIEWER_HTML'})
+ set_config_elt_from_vars('viewer_md',
+ {'MDVIEWER_texdoc', 'MDVIEWER', 'TEXDOCVIEW_md', 'TEXDOC_VIEWER_MD'})
+ set_config_elt_from_vars('viewer_txt',
+ {'PAGER_texdoc', 'PAGER', 'TEXDOCVIEW_txt', 'TEXDOC_VIEWER_TXT'})
+end
+
+---------------------- options and aliases from files ----------------------
+
+-- interpret a confline as a config setting or return false
+local function confline_to_config(line, file, pos)
+ local key, val = string.match(line, '^([%a%d_]+)%s*=%s*(.+)')
+ if key and val then
+ set_config_element(key, val, {src='file', file=file, line=pos})
+ return true
+ end
+ return false
+end
+
+-- set config and aliases from a particular config file assumed to exist
+local function read_config_file(configfile)
+ local err_print = import_function('util', 'err_print')
+ local confline_to_alias = import_function('alias', 'confline_to_alias')
+ local confline_to_score = import_function('score', 'confline_to_score')
+
+ local cnf = assert(io.open(configfile, 'r'))
+ local lineno = 0
+ while true do
+ local line=cnf:read('*line')
+ lineno = lineno + 1
+
+ if line == nil then break end -- EOF
+ line = string.gsub(line, '%s*#.*$', '') -- comments begin with #
+ line = string.gsub(line, '%s*$', '') -- remove trailing spaces
+ line = string.gsub(line, '^%s*', '') -- remove leading spaces
+
+ -- try to interpret the line
+ local ok = string.match(line, '^%s*$')
+ or confline_to_alias(line, configfile, lineno)
+ or confline_to_score(line, configfile, lineno)
+ or confline_to_config(line, configfile, lineno)
+
+ -- complain if it failed
+ if not ok then
+ err_print('warning',
+ 'syntax error in %s at line %d.', configfile, lineno)
+ end
+ end
+ cnf:close()
+end
+
+-- return the list of configuration files
+local function get_config_files()
+ -- get names
+ local platform = string.match(kpse.var_value('SELFAUTOLOC'), '.*/(.*)$')
+ local names = {
+ 'texdoc-' .. platform .. '.cnf',
+ 'texdoc.cnf',
+ 'texdoc-dist.cnf',
+ }
+
+ -- get dirs
+ local sep = (os.type == 'windows') and ';' or ':'
+ local texmf_texdoc = kpse.expand_path('$TEXMF/texdoc')
+ local dirs = texmf_texdoc:explode(sep)
+
+ -- merge them
+ local ret = {}
+ for _, dir in ipairs(dirs) do
+ for _, name in ipairs(names) do
+ local pathname = dir .. '/' .. name
+ table.insert(ret, pathname)
+ end
+ end
+ return ret
+end
+
+-- the config_files table is shared by the following two functions
+local setup_config_from_files
+
+do -- begin scope of config_files
+local config_files = {}
+
+-- set config/aliases from all config files
+setup_config_from_files = function()
+ local file_list = get_config_files()
+
+ for i, file in ipairs(file_list) do
+ -- determine the status of the config file
+ local status
+ if lfs.isfile(file) then
+ status = config.lastfile_switch and 'disabled' or 'active'
+ else
+ status = 'not found'
+ end
+
+ -- register it
+ config_files[i] = {
+ path = file,
+ status = status,
+ }
+
+ -- read only if active
+ if status == 'active' then
+ read_config_file(file)
+ end
+ end
+end
+
+-- now a special information function (see -f,--file option)
+function M.show_config_files(is_action)
+ local w32_path = import_function('util', 'w32_path')
+ local dbg_print = import_function('util', 'dbg_print')
+
+ -- controled print_func
+ local indent = is_action and ' ' or ''
+ local print_func = is_action and print
+ or function(s) dbg_print('files', s) end
+
+ -- show the list of configuration files
+ print_func('Configuration files are:')
+ for i, file in ipairs(config_files) do
+ -- if not verbose, do not show "not found" files for -f
+ if file.status ~= "not found"
+ or (not is_action or config.verbosity_level == 3) then
+ print_func(indent .. file.status .. '\t' .. w32_path(file.path))
+ end
+ end
+
+ -- show the recommendation (only for the "files" action)
+ if is_action then
+ print_func('Recommended file(s) for personal settings:')
+ local sep = (os.type == 'windows') and ';' or ':'
+ local texmfhomes = string.explode(kpse.var_value('TEXMFHOME'), sep)
+ for _, home in ipairs(texmfhomes) do
+ print_func(indent .. w32_path(home .. '/texdoc/texdoc.cnf'))
+ end
+ end
+end
+
+end -- end scope of config_files
+
+---------------------- config from locale settings -------------------------
+
+local function setup_config_from_locale()
+ local current = os.setlocale(nil, 'all')
+ os.setlocale('', 'all')
+ local native = os.setlocale(nil, 'time')
+ os.setlocale(current, 'all')
+ local lang = string.match(native, '^[a-z][a-z]')
+ if lang then
+ set_config_element('lang', lang, {src='loc'})
+ end
+end
+
+---------------------- options from built-in defaults ----------------------
+
+-- for default viewer on general Unix, we have a list; the following function is
+-- used to check in the path which program is available
+
+-- check if "name" is the name of a file in the path
+-- Warning: to be used only on Unix! (separators, and PATH irrelevant on win32)
+-- the value of PATH is cached
+local is_in_path
+do local path_list = string.explode(os.getenv('PATH'), ':')
+is_in_path = function(name)
+ for _, path in ipairs(path_list) do
+ if lfs.isfile(path .. '/' .. name) then return true end
+ end
+ return false
+end
+end
+
+-- returns a viewer specific to a desktop environment if relevant
+-- doesn't work on windows (uses io.popen)
+-- logic stolen from xdg-open (http://www.freedesktop.org/) and adapted
+local function desktop_environment_viewer()
+ local xdg_current_desktop = os.getenv('XDG_CURRENT_DESKTOP') or ''
+ if (os.getenv('KDE_SESSION_VERSION') or os.getenv('KDE_FULL_SESSION'))
+ or string.match(xdg_current_desktop, '.*KDE.*') then
+ if is_in_path('kde-open') then return '(kde-open %s) &' end
+ if is_in_path('kfmclient') then return '(kfmclient exec %s) &' end
+ end
+ if os.getenv('GNOME_DESKTOP_SESSION_ID')
+ or string.match(xdg_current_desktop, '.*GNOME.*') then -- gnome
+ if is_in_path('gvfs-open') then return '(gvfs-open %s) &' end
+ if is_in_path('gnome-open') then return '(gnome-open %s) &' end
+ end
+ if not is_in_path('xprop') then return end
+ local xprop_fh = io.popen('xprop -root _DT_SAVE_MODE 2>/dev/null')
+ local xprop_out = xprop_fh:read('*line')
+ xprop_fh:close()
+ if xprop_out and string.find(xprop_out, '= "xfce4"$') then -- xfce
+ return '(exo-open %s) &'
+ end
+end
+
+-- guess a viewer from a list:
+-- - xdg-open from freedesktop if available
+-- - try detecting desktop environments
+-- - or return the first element of "list" whose name is found in path
+-- - or nil
+-- caches results of desktop environment detection
+local guess_viewer
+do local de_viewer
+guess_viewer = function(cmds)
+ -- try the freedesktop method
+ if is_in_path('xdg-open') then
+ return '(xdg-open %s) &'
+ end
+ -- try desktop environment
+ if not de_viewer then de_viewer = desktop_environment_viewer() end
+ if de_viewer then return de_viewer end
+ -- or look along path
+ for _, cmd in ipairs(cmds) do
+ if is_in_path(cmd[1]) then return cmd[2] end
+ end
+end
+end
+
+-- set viewers from defaults (done only if necessary)
+function M.get_default_viewers()
+ local function set_config_ls(ls) set_config_list(ls, {src='def'}) end
+ if (os.type == 'windows') then
+ set_config_ls {
+ -- Use 'start' to get file associations.
+ -- We need to quote the filenames, but the first quoted argument
+ -- is considered as the title by start, so we provide a dummy title.
+ -- Also, since the command line parser removes quotes if there
+ -- is no space inside, the dummy title must contain spaces.
+ viewer_dvi = 'start "texdoc dvi viewer"',
+ viewer_html = 'start "texdoc html viewer"',
+ viewer_pdf = 'start "texdoc pdf viewer"',
+ viewer_ps = 'start "texdoc ps viewer"',
+ -- 'more' is always available.
+ -- However, we can't assume texdoc is called from a cmd.exe window
+ -- (it can be run from the start->run menu), hence we make sure
+ -- to open a new window if needed.
+ viewer_txt = 'start cmd /k more',
+ viewer_md = viewer_txt,
+ }
+ elseif (os.name == 'macosx') then
+ set_config_ls {
+ viewer_dvi = 'open',
+ viewer_html = 'open',
+ viewer_pdf = 'open',
+ viewer_ps = 'open',
+ viewer_txt = 'less',
+ viewer_md = viewer_txt,
+ }
+ else -- generic Unix
+ set_config_ls {
+ viewer_dvi = guess_viewer {
+ {'xdvi', '(xdvi %s) &'},
+ {'evince', '(evince %s) &'},
+ {'okular', '(okular %s) &'},
+ {'kdvi', '(kdvi %s) &'},
+ {'xgdvi', '(xgdvi %s) &'},
+ {'spawg', '(spawg %s) &'},
+ {'spawx11', '(spawx11 %s) &'},
+ {'tkdvi', '(tkdvi %s) &'},
+ {'dvilx', '(dvilx %s) &'},
+ {'advi', '(advi %s) &'},
+ {'xdvik-ja', '(xdvik-ja %s) &'},
+ {'see', '(see %s) &'}
+ },
+ viewer_html = guess_viewer {
+ {'firefox', '(firefox %s) &'},
+ {'seamonkey', '(seamonkey %s) &'},
+ {'mozilla', '(mozilla %s) &'},
+ {'konqueror', '(konqueror %s) &'},
+ {'epiphany', '(epiphany %s) &'},
+ {'opera', '(opera %s) &'},
+ {'w3m', 'w3m'},
+ {'links', 'links'},
+ {'lynx', 'lynx'},
+ {'see', 'see'}
+ },
+ viewer_pdf = guess_viewer {
+ {'xpdf', '(xpdf %s) &'},
+ {'evince', '(evince %s) &'},
+ {'okular', '(okular %s) &'},
+ {'kpdf', '(kpdf %s) &'},
+ {'acroread', '(xpdf %s) &'},
+ {'see', '(see %s) &'}
+ },
+ viewer_ps = guess_viewer {
+ {'gv', '(gv %s) &'},
+ {'evince', '(evince %s) &'},
+ {'okular', '(okular %s) &'},
+ {'kghostview', '(kghostview %s) &'},
+ {'see', '(see %s) &'}
+ },
+ viewer_txt = guess_viewer {
+ {'most', 'most'},
+ {'less', 'less'},
+ {'more', 'more'}
+ },
+ viewer_md = viewer_txt,
+ }
+ end
+end
+
+-- set some fall-back default values if no previous value is set
+local function setup_config_from_defaults()
+ local function set_config_ls(ls) set_config_list(ls, {src='def'}) end
+ local function set_config_elt(key, val)
+ set_config_element(key, val, {src='def'})
+ end
+ -- various, platform independent, stuff
+ set_config_ls {
+ mode = 'view',
+ interact_switch = 'true',
+ machine_switch = 'false',
+ ext_list = 'pdf, htm, html, txt, md, ps, dvi, ',
+ basename_list = 'readme, 00readme',
+ badext_list = 'txt, ',
+ badbasename_list = 'readme, 00readme',
+ suffix_list = '',
+ verbosity_level = C.def_verbosity,
+ debug_list = '',
+ max_lines = '20',
+ fuzzy_level = '5',
+ }
+ -- zip-related options
+ set_config_ls {
+ zipext_list = '',
+ rm_file = 'rm -f',
+ rm_dir = 'rmdir',
+ }
+end
+
+-------------------------- set all configuration ---------------------------
+
+-- populate the config and alias arrays
+function M.setup_config_and_alias(cl_config)
+
+ -- setup config from all sources
+ setup_config_from_cl(cl_config)
+ setup_config_from_env()
+ setup_config_from_files()
+ setup_config_from_locale()
+ setup_config_from_defaults()
+
+ -- machine mode implies no interaction
+ if config.machine_switch == true then
+ real_set_config('interact_switch', false)
+ end
+
+ -- debug implies verbose
+ if #config.debug_list > 0 then
+ real_set_config('verbosity_level', tonumber(C.max_verbosity))
+ end
+
+ -- we were waiting for config.debug_list to be known to do this
+ M.show_config_files()
+end
+
+return M
+
+-- vim: ft=lua:
diff --git a/support/texdoc/script/texdoclib-const.tlu b/support/texdoc/script/texdoclib-const.tlu
new file mode 100644
index 0000000000..5d34bed3f6
--- /dev/null
+++ b/support/texdoc/script/texdoclib-const.tlu
@@ -0,0 +1,158 @@
+-- texdoclib-const.tlu: global constants for texdoc
+--
+-- The TeX Live team, GPLv3, see texdoclib.tlu for details
+
+-- use an empty environment that will become texdoc_env.C (see EOF)
+local constants = {}
+local kpse = kpse
+local setfenv = setfenv
+local texdoc_env
+
+if setfenv then -- Lua <5.2
+ texdoc_env = getfenv()
+ setfenv(1, constants)
+else
+ texdoc_env = _ENV
+ _ENV = constants
+end
+
+-- BEGIN constants
+
+-- progname and version
+fullname = kpse.find_file('texdoc/texdoclib', 'lua')
+progname = 'Texdoc'
+version = '3.1'
+release_date = '2019-03-28'
+
+-- make sure to update setup_config_from_cl() accordingly
+-- and set a default value in setup_config_from_defaults() if relevant
+usage_msg = [[
+Usage: texdoc [OPTION...] NAME...
+ or: texdoc [OPTION...] ACTION
+
+Try to find appropriate TeX documentation for the specified NAME(s).
+Alternatively, perform the given ACTION and exit.
+
+Options:
+ -w, --view Use view mode: start a viewer. (default)
+ -m, --mixed Use mixed mode (view or list).
+ -l, --list Use list mode: show a list of results.
+ -s, --showall Use showall mode: show also "bad" results.
+
+ -i, --interact Use interactive menus. (default)
+ -I, --nointeract Use plain lists, no interaction required.
+ -M, --machine Machine-readable output for lists (implies -I).
+
+ -q, --quiet Suppress warnings and most error messages.
+ -v, --verbose Print additional information (e.g., viewer command).
+ -D, --debug Activate all debug output (equal to "--debug=all").
+ -d LIST, --debug=LIST
+ Activate debug output restricted to LIST.
+ -c NAME=VALUE Set configuration item NAME to VALUE.
+
+Actions:
+ -h, --help Print this help message.
+ -V, --version Print the version number.
+ -f, --files Print the list of configuration files used.
+ --just-view FILE Display FILE, given with full path (no searching).
+
+Full manual available via `texdoc texdoc'.
+
+Website: <https://tug.org/texdoc/>
+Repository: <https://github.com/TeX-Live/texdoc>
+Please email bugs to <texdoc@tug.org>.]]
+
+copyright_msg = [[
+Copyright 2008 Manuel Pégourié-Gonnard, Takuto Asakura, and the TeX Live Team.
+License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
+This is free software: you are free to change and redistribute it.]]
+
+actions_ph = 'Actions:\n'
+
+known_options = {
+ 'viewer_.*',
+ 'mode',
+ 'interact_switch',
+ 'machine_switch',
+ 'ext_list',
+ 'basename_list',
+ 'badext_list',
+ 'badbasename_list',
+ 'suffix_list',
+ 'verbosity_level',
+ 'debug_list',
+ 'lastfile_switch',
+ 'rm_dir',
+ 'rm_file',
+ 'unzip_.*',
+ 'zipext_list',
+ 'max_lines',
+ 'lang',
+ 'fuzzy_level',
+ 'texlive_tlpdb',
+}
+
+error_msg = [[
+Try `texdoc --help' for short help, `texdoc texdoc' for full manual.]]
+
+notfound_msg = [[
+Sorry, no documentation found for "PKGNAME".
+If you are unsure about the name, try full-text searching on CTAN.
+Search form: <https://www.ctan.org/search/>]]
+notfound_msg_ph = 'PKGNAME'
+
+-- exit codes
+exit_ok = 0
+exit_error = 1 -- apparently hard-coded in Lua
+exit_usage = 2
+exit_notfound = 3
+
+err_priority = {
+ error = 1,
+ warning = 2,
+ info = 3,
+}
+min_verbosity = '0' -- (nothing at all)
+max_verbosity = '3'
+def_verbosity = '2'
+
+known_debugs = {
+ config = {'files'},
+ files = {},
+ search = {},
+ score = {},
+ texdocs = {},
+ tlpdb = {},
+ version = {},
+ view = {},
+}
+
+-- various cache or non-cache files
+cache_name = 'texdoc/cache-tlpdb.lua' -- relative to TEXMFVAR
+data_tlpdb_name = 'texdoc/Data.tlpdb.lua'
+
+place_holder = '%%s' -- used for viewer commands
+
+-- END constants
+
+-- get our previous environment back
+if setfenv then
+ setfenv(1, texdoc_env)
+else
+ _ENV = texdoc_env
+end
+
+-- Make table C a read-only proxy to the local <constants>.
+-- Note this is not deep read-only: C.known_debugs is read-only, but
+-- C.known_debugs.version isn't, for instance.
+local C = {}
+setmetatable(C, {
+ __index = constants,
+ __newindew = function()
+ error('Internal error: attempt to modify a constant.')
+ end
+})
+
+return C
+
+-- vim: ft=lua:
diff --git a/support/texdoc/script/texdoclib-score.tlu b/support/texdoc/script/texdoclib-score.tlu
new file mode 100644
index 0000000000..81e1be2a33
--- /dev/null
+++ b/support/texdoc/script/texdoclib-score.tlu
@@ -0,0 +1,293 @@
+-- texdoclib-score.tlu: scoring functions for texdoc
+--
+-- The TeX Live Team, GPLv3, see texdoclib.tlu for details
+
+-- dependencies
+local texdoc = {
+ util = require('texdoclib-util'),
+ config = require('texdoclib-config'),
+}
+
+-- shortcuts
+local M = {}
+local dbg_print = texdoc.util.dbg_print
+
+-- shared variables
+local global_adjscore, spec_adjscore = {}, {}
+
+------------------------- configuration directives -------------------------
+
+-- set key in score table to val, without overriding
+local function set_score_table(tab, key, val)
+ local k = string.lower(key)
+ local v = tonumber(val)
+ if v then
+ if tab[k] == nil then tab[k] = v end
+ return true
+ end
+ return false
+end
+
+-- interpret a confline as a score directive or return false
+function M.confline_to_score(line, file, pos)
+ local keyw, pat, val
+ -- try global adjscore
+ pat, val = string.match(line, '^adjscore%s+([%w%p]+)%s*=%s*([%d+-.]+)')
+ if pat and val then
+ return set_score_table(global_adjscore, pat, val)
+ end
+ -- try keyword specific adjscore
+ keyw, pat, val = string.match(line,
+ '^adjscore%(([%w%p]+)%)%s+([%w%p]+)%s*=%s*([%d+-.]+)')
+ if keyw and pat and val then
+ keyw = string.lower(keyw)
+ spec_adjscore[keyw] = spec_adjscore[keyw] or {}
+ return set_score_table(spec_adjscore[keyw], pat, val)
+ end
+ return false
+end
+
+---------------------------- score computation -----------------------------
+
+-- says if pat is a "subword" of str
+local function is_subword(str, pat)
+ local function is_delim(str, i)
+ return not not string.find(string.sub(str, i, i), '%p')
+ end
+
+ local i, j = string.find(str, pat, 1, true)
+ return not not (i and j
+ and (i == 1 or is_delim(str, i) or is_delim(str, i-1))
+ and (j == #str or is_delim(str, j) or is_delim(str, j+1)))
+end
+
+-- says if a filename has a bad basename
+local function has_bad_basename(file)
+ file = file:gsub('.*/', '')
+ for _, b in ipairs(texdoc.config.get_value('badbasename_list')) do
+ if file:find('^' .. b .. '$') or file:find('^' .. b .. '%.') then
+ return true
+ end
+ end
+ return false
+end
+
+-- compute a heuristic score -10 <= s < 10
+local function heuristic_score(file, pat)
+ dbg_print('score', 'Start heuristic scoring with pattern: ' .. pat)
+ -- score management
+ local score = -10
+ local function upscore(s, reason, force)
+ if s > score or force then
+ score = s
+ dbg_print('score',
+ 'New heuristic score: %.1f. Reason: %s', s, reason)
+ end
+ end
+ local slash = not not string.find(pat, '/', 1, true)
+ -- look for exact or subword match
+ if M.is_exact_locale(file, pat) then
+ upscore(5, 'exact match with correct locale')
+ elseif M.is_exact(file, pat) then
+ upscore(4, 'exact match')
+ elseif is_subword(file, pat) then
+ upscore(1, 'subword match')
+ end
+ -- try derivatives unless pat contains a slash
+ if not slash then
+ for _, suffix in ipairs(texdoc.config.get_value('suffix_list')) do
+ local deriv = pat..suffix
+ if M.is_exact(file, deriv) then
+ upscore(3, 'exact match for derived pattern: ' .. deriv)
+ elseif is_subword(file, deriv) then
+ upscore(2, 'subword match for derived pattern: ' .. deriv)
+ end
+ end
+ end
+ -- if extension is bad, score becomes an epsilon
+ local ext = texdoc.config.get_value('ext_list')[M.ext_pos(file)]
+ if ext and texdoc.config.get_value('badext_list_inv')[ext] and score > 0 then
+ upscore(0.1, 'bad extension', true)
+ end
+ -- if basename is bad, score becomes an epsilon
+ if has_bad_basename(file) and score > 0 then
+ upscore(0.1, 'bad basename', true)
+ end
+ -- bonus for being in the right directory
+ if string.find('/' .. file, '/' .. pat .. '/', 1, true) and not slash then
+ upscore(score + 1.5, 'directory bonus')
+ end
+ -- done
+ dbg_print('score', 'Final heuristic score: %.1f', score)
+ return score
+end
+
+-- set the score of a docfile
+local function set_score(df, original_kw)
+ -- scoring is case-insensitive (patterns are already lowercased)
+ local name = string.lower(df.shortname)
+ dbg_print('score', '----------')
+ dbg_print('score', 'Start scoring ' .. df.realpath)
+ dbg_print('score', 'Name used: ' .. name)
+ -- get score from patterns
+ local score = -10
+ for _, pat in ipairs(df.matches) do
+ local s = -10
+ local p = string.lower(pat.name)
+ if pat.original then
+ s = df.tree > -1 and heuristic_score(name, p) or 1
+ elseif M.is_exact(name, p) then
+ local bonus, note = 0, ''
+ if pat.locale then
+ bonus, note = 5, ', (language-based)'
+ end
+ s = (pat.score or 10) + bonus -- default alias score is 10
+ dbg_print('score',
+ 'Matching alias "%s", score: %.1f%s', pat.name, s, note)
+ end
+ if s > score then score = s end
+ end
+ dbg_print('score', 'Max pattern score: %.1f', score)
+ -- get score from tlp associations
+ if score == -10 and df.tlptodoc then
+ score = -1
+ dbg_print('score',
+ 'New score: %.1f from package name association', score)
+ end
+ if score == -10 and df.runtodoc then
+ score = -5
+ dbg_print('score',
+ 'New score: %.1f from sty/cls association', score)
+ end
+ -- bonus for metadata
+ if df.details then
+ if string.find(string.lower(df.details), 'readme') then
+ score = score + 0.1
+ dbg_print('score', 'Catalogue "readme" bonus: +0.1')
+ else
+ score = score + 1.5
+ dbg_print('score', 'Catalogue details bonus: +1.5')
+ end
+ end
+ -- adjust from keyword-specific tables
+ if df.tree > -1 and spec_adjscore[original_kw] then
+ for pat, val in pairs(spec_adjscore[original_kw]) do
+ if val and is_subword('/' .. name, pat) then
+ score = score + val
+ dbg_print('score',
+ 'Adjust by %.1f from specific pattern "%s"', val, pat)
+ end
+ end
+ end
+ -- adjust from global tables
+ if df.tree > -1 then
+ for pat, val in pairs(global_adjscore) do
+ if val and is_subword('/' .. name, pat) then
+ if score > -10 or val < 0 then score = score + val end
+ dbg_print('score',
+ 'Adjust by %.1f from global pattern "%s"', val, pat)
+ end
+ end
+ end
+ dbg_print('score', 'Final score: %.1f', score)
+ df.score = score
+end
+
+-- set the scores for a doclist
+local function set_list_scores(list, original_kw)
+ for _, df in ipairs(list) do
+ set_score(df, original_kw)
+ end
+end
+
+-- says if file is an exact match for pat
+function M.is_exact(file, pat)
+ file = texdoc.util.parse_zip(file)
+ local slashes = string.gsub(pat, '[^/]+', '[^/]+')
+ basename = string.match(file, slashes .. '$')
+ if not basename then return nil end
+ if basename == pat then return true end
+ for _, ext in ipairs(texdoc.config.get_value('ext_list')) do
+ if ext ~= '' and ext ~= '*' and basename == pat .. '.' .. ext then
+ return true
+ end
+ end
+ return false
+end
+
+-- says if file is an exact match for pat and the current locale
+function M.is_exact_locale(file, pat)
+ if string.match(pat, '%-%l%l%l?$') then
+ -- don't match if the pattern appears to include a language code
+ return false
+ end
+ local lang = texdoc.config.get_value('lang')
+ if lang then
+ return M.is_exact(file, pat .. '-' .. lang)
+ or M.is_exact(file, lang .. '-' .. pat)
+ end
+ return false
+end
+
+-- compare two docfile's: (see texdoclib-search.tlu for structure)
+-- 1. by score
+-- 2. then by extensions (ordered as in ext_list),
+-- 3. then lexicographically by fullpath.
+-- 4. then by tree.
+-- return true if a is better than b
+local function docfile_order(a, b)
+ if a.score > b.score then return true
+ elseif a.score < b.score then return false
+ elseif a.ext_pos < b.ext_pos then return true
+ elseif a.ext_pos > b.ext_pos then return false
+ elseif a.realpath < b.realpath then return true
+ elseif a.realpath > b.realpath then return false
+ else return (a.tree > b.tree)
+ end
+end
+
+----------------------------- public functions -----------------------------
+
+-- returns the index of the most specific extension of file in ext_list,
+-- or config.ext_list_max + 1
+function M.ext_pos(file)
+ -- remove zipext if applicable
+ file = texdoc.util.parse_zip(file)
+ -- now find the extension
+ local p, e, pos, ext
+ for p, e in ipairs(texdoc.config.get_value('ext_list')) do
+ if (e == '*') and (ext == nil) then
+ pos, ext = p, e
+ elseif (e == '') and not string.find(file, '.', 1, true) then
+ pos, ext = p, e
+ elseif string.sub(file, -string.len(e)-1) == '.' .. e then
+ if (ext == nil) or (ext == '*')
+ or (string.len(e) > string.len(ext)) then
+ pos, ext = p, e
+ end
+ end
+ end
+ return pos or (texdoc.config.get_value('ext_list_max') + 1)
+end
+
+-- return the "quality" of docfile
+function M.docfile_quality(df)
+ if df.score > 0 then
+ return 'good'
+ elseif df.score > -100 then
+ return 'bad'
+ else
+ return 'killed'
+ end
+end
+
+-- sort a doclist
+function M.sort_doclist(dl, original_kw)
+ dl:stop()
+ set_list_scores(dl, original_kw)
+ table.sort(dl, docfile_order)
+end
+
+return M
+
+-- vim: ft=lua:
diff --git a/support/texdoc/script/texdoclib-search.tlu b/support/texdoc/script/texdoclib-search.tlu
new file mode 100644
index 0000000000..1a7270d0b7
--- /dev/null
+++ b/support/texdoc/script/texdoclib-search.tlu
@@ -0,0 +1,771 @@
+-- texdoclib-search.tlu: file searching functions for texdoc
+--
+-- The TeX Live Team, GPLv3, see texdoclib.tlu for details
+
+-- Warning: Some functions here assume that M.init_databases() has been called.
+
+-- dependencies
+local texdoc = {
+ const = require('texdoclib-const'),
+ util = require('texdoclib-util'),
+ alias = require('texdoclib-alias'),
+ score = require('texdoclib-score'),
+ config = require('texdoclib-config'),
+}
+
+-- shortcuts
+local M = {}
+local C = texdoc.const
+local err_print = texdoc.util.err_print
+local dbg_print = texdoc.util.dbg_print
+
+-- shared by all functions in this file
+local s_doclist -- the Doclist object to be populated by various functions
+local s_meta -- {[normname] = meta, ...} (populated by init_tlp_database)
+local vanilla -- is this a vanilla TL or a re-package one without tlpdb?
+
+---------------------------- utility functions -----------------------------
+
+-- find the TeX Live root
+local function get_tlroot()
+ local tlroot = kpse.var_value('TEXMFROOT')
+ get_tlroot = function() return tlroot end
+ return tlroot
+end
+
+-- says if file has a known extension according to ext_list
+-- (or known basename according to basename_list)
+local function check_ext(file)
+ file = string.lower(file)
+ -- remove zipext if applicable
+ file = texdoc.util.parse_zip(file)
+ -- then do the normal thing
+ for _, e in ipairs(texdoc.config.get_value('ext_list')) do
+ if e == '*' then
+ return true
+ elseif (e == '') then
+ if not string.find(file, '.', 1, true) then
+ return true
+ end
+ else
+ local dot_e = '.'..e
+ if string.sub(file, -string.len(dot_e)) == dot_e then
+ return true
+ end
+ end
+ end
+ -- is the basename good?
+ for _, b in ipairs(texdoc.config.get_value('basename_list')) do
+ if file:find('^' .. b .. '$') or file:find('^' .. b .. '%.') then
+ return true
+ end
+ end
+ return false
+end
+
+----------------------- docfile and doclist objects ------------------------
+
+--[[
+doclist = {
+ [1] = docfile1, [2] = docfiles2, ...,
+ inv = {realpath1 = index1, ...}
+}
+
+The inv subtable is such that for all i
+doclist.inv(doclist[i].realpath:lower()) == i
+Paths are lowercased in order to avoid duplicates on windows.
+--]]
+
+local Doclist = {}
+Doclist.__index = Doclist
+
+-- create a new list of docfiles
+function Doclist:new()
+ local dl = {inv = {}}
+ setmetatable(dl, self)
+ return dl
+end
+
+-- add a docfile to a list
+function Doclist:add(df)
+ -- if no realpath information, unable to add
+ -- (useful if vanilla == false)
+ if not df.realpath then return end
+
+ -- check the existence of the file
+ if not lfs.isfile(df.realpath) then
+ dbg_print('search', 'File %s not found. Skipping.', df.realpath)
+ return
+ end
+
+ -- add the docfile to the list
+ local index = self.inv[df.realpath:lower()]
+ if index then
+ self[index]:mergein(df)
+ else
+ dbg_print('search', 'File %s found.', df.realpath)
+
+ local newindex = #self + 1
+ self[newindex] = df
+ self.inv[df.realpath:lower()] = newindex
+ end
+end
+
+-- stops a doclist
+function Doclist:stop()
+ self.inv = nil
+end
+
+--[[
+docfile = {
+ -- name and tree are mandatory
+ name = filename (used for scoring only)
+ tree = code of the tree, see below
+ -- at least one of the following fields should exist
+ matches = {pattern1, pattern2, ...} or {}
+ runtodoc = true if there is a runfile -> docfile association
+ tlptodoc = true if there is a tlp name -> docfile association
+ -- those are virtual members, see below
+ realpath = full path
+ normname = nomrmalised (path removed up to the 'doc' component)
+ shortname = short name used for scoring
+ basename = basename
+ lang = language tag from the catalogue metadata
+ details = details tag from the catalogue metadata
+ quality = 'good', 'bad', or 'killed' depending on score
+ ext_pos = position of the extension in ext_list
+ -- set for elements of a list as a side effect of sort_doclist()
+ score = score
+}
+if tree > 1, this is the index of the tree in TEXDOCS
+if tree = 0, then name is relative to TLROOT
+tree = - 1 if and only if file is a sty file. Here name is absolute.
+--]]
+
+-- Docfile objects inherit members from Docfile
+-- and have virtual members implemented by get_<member>() methods
+-- for best cache performance, getters should not return nil
+local Docfile = {}
+function Docfile:__index(key)
+ if Docfile[key] then return Docfile[key] end
+ local getter = Docfile['get_' .. key]
+ if getter then
+ rawset(self, key, getter(self))
+ return rawget(self, key)
+ end
+end
+
+-- create a new docfile object using initialisation info
+-- required fields: name, tree
+function Docfile:new(info)
+ local df = {}
+ setmetatable(df, self)
+ for k, v in pairs(info) do
+ if k == 'pattern' then
+ df.matches = {info.pattern}
+ else
+ df[k] = v
+ end
+ end
+ return df
+end
+
+-- merge a second docfile object in, assuming it represents the same file
+function Docfile:mergein(df)
+ for k, v in pairs(df) do
+ if k == 'matches' then
+ for _, m in ipairs(df.matches or {}) do
+ table.insert(self.matches, m)
+ end
+ else
+ self[k] = v
+ end
+ end
+end
+
+-- return the full path to the file
+local texdocs_tree_to_path -- definition later
+function Docfile:get_realpath()
+ if self.tree > 0 then
+ return texdocs_tree_to_path(self.tree, self.name)
+ elseif self.tree == 0 then
+ if vanilla then
+ return get_tlroot() .. '/' .. self.name
+ else
+ return kpse.find_file(self.normname, 'TeX system documentation')
+ end
+ else
+ return self.name
+ end
+end
+
+-- normalise a name from the tlpdb (use for s_meta indexes)
+local function reloc_tlpdb_path(name)
+ return string.gsub(name, '^texmf[^/]*/doc/', '', 1)
+end
+
+-- return normalised name
+function Docfile:get_normname()
+ return (self.tree == 0) and reloc_tlpdb_path(self.name) or self.name
+end
+
+-- retrieve the lang from meta
+function Docfile:get_lang()
+ local meta = s_meta[self.normname]
+ return meta and (meta.lang or false) or false
+end
+
+-- retrieve the details from meta
+function Docfile:get_details()
+ local meta = s_meta[self.normname]
+ return meta and (meta.details or false) or false
+end
+
+-- return the short name used for scoring
+function Docfile:get_shortname()
+ if self.tree == -1 then return self.name end
+ -- remove first component of name if at least two directory levels
+ return string.match(self.normname, '^..-/(.+/.+)$') or self.normname
+end
+
+-- return the base name
+function Docfile:get_basename()
+ return string.gsub(self.name, '.*/', '', 1)
+end
+
+-- for interface consistency, matches should always be a table, never nil
+function Docfile:get_matches()
+ return {}
+end
+
+-- from texdoclib-score.tlu
+Docfile.get_quality = texdoc.score.docfile_quality
+
+-- from texdoclib-score.tlu
+function Docfile:get_ext_pos()
+ return texdoc.score.ext_pos(self.basename)
+end
+
+-------------------- select results from TEXDOCS trees ---------------------
+
+-- says if a file (with its path) matches a pattern
+local function matches(pattern, file)
+ if pattern.original then
+ return string.find(file:lower(), pattern.name:lower(), 1, true)
+ else
+ return texdoc.score.is_exact(file, pattern.name)
+ end
+end
+
+-- return a docfile object if file "matches", nil otherwise
+local function process_file(patlist, file, pathfile, code)
+ local docfile
+ local pattern
+ for _, pattern in ipairs(patlist) do
+ if matches(pattern, pathfile) then
+ local info = {
+ name = pathfile,
+ tree = code,
+ pattern = pattern,
+ }
+ if docfile then
+ docfile:mergein(Docfile:new(info))
+ else
+ docfile = Docfile:new(info)
+ end
+ end
+ end
+ return docfile
+end
+
+-- scan a database
+local function scan_db(patlist, code, lsr_db)
+ for file, basename in pairs(lsr_db) do
+ local df = process_file(patlist, basename, file, code)
+ if df then s_doclist:add(df) end
+ end
+end
+
+--------------------- manage TEXDOCS trees à la kpse ----------------------
+
+-- build a db from a ls-R file
+local function init_lsr_db(root, shift)
+ -- open the file
+ local lsr = assert(io.open(root .. '/ls-R', 'r'))
+ local _ = lsr:read('*line') -- throw away first line (comment)
+
+ -- scan it
+ local db = {}
+ local maybe_dir, isdoc = true, false
+ local current_dir
+ local l = #shift
+ while true do
+ local line = lsr:read('*line')
+ while line == '' do line, maybe_dir = lsr:read('*line'), true end
+ if line == nil then break end -- EOF
+ local dir_line = maybe_dir and string.match(line, '^%./(.*):$')
+ if dir_line then
+ maybe_dir = false -- next line may not be a dir
+ if string.sub(dir_line .. '/', 1, l) == shift then
+ isdoc = true
+ current_dir = string.sub(dir_line, l+1)
+ db[current_dir] = nil
+ elseif isdoc then
+ break -- we're exiting the ./doc (or shift) dir, so it's over
+ end
+ elseif isdoc then
+ local file = (current_dir == '') and line or current_dir .. '/' .. line
+ if check_ext(line) then db[file] = line end
+ end
+ end
+
+ lsr:close()
+
+ return db
+end
+
+-- build a db for a tree without ls-R index
+local function init_tree_db(base, recurse)
+ local db = {}
+ local function init_tree_db_rec(dir)
+ for file in lfs.dir(base .. '/' .. dir) do
+ if file ~= '.' and file ~= '..' then
+ local f = (dir == '') and file or dir .. '/' .. file
+ if lfs.isdir(base..'/'..f) then
+ if recurse then init_tree_db_rec(f) end
+ else
+ if check_ext(file) then db[f] = file end
+ end
+ end
+ end
+ end
+ init_tree_db_rec('')
+ return db
+end
+
+local init_texdocs_database, get_doclist_texdocs
+
+do -- begin scope of doc_roots
+local doc_roots
+
+--[[
+doc_roots[i] = {
+ path = initial path,
+ db = {[file1] = basename1, [file2] = basename2, ...},
+}
+--]]
+
+-- populate the doc_roots filename databases
+init_texdocs_database = function()
+ -- find a ls-R file in a parent directory and return it or nil
+ local function lsr_root(path)
+ if not lfs.isdir (path) then return end
+ local root, shift = path, ''
+ if string.sub(root, -1) == '/' then root = string.sub(root, 1, -2) end
+ while string.find(root, '/', 1, true) do
+ if lfs.isfile(root .. '/ls-R') then
+ return root, shift
+ end
+ local last_comp = string.match(root, '^.*/(.*)$')
+ -- /!\ cannot put last_comp in a regex: can contain special char
+ root = string.sub(root, 1, - (#last_comp + 2))
+ shift = last_comp .. '/' .. shift
+ end
+ end
+
+ doc_roots = {}
+ local sep = (os.type == 'windows') and ';' or ':'
+ local kpse_texdocs = kpse.expand_var("$TEXDOCS")
+
+ -- expand the path and turn it into a lua list
+ local raw_doc_roots = string.explode(kpse.expand_braces(kpse_texdocs), sep)
+ local max = #raw_doc_roots + 1
+
+ for j, dir in ipairs(raw_doc_roots) do
+ local i = max - j
+ local n
+ local path, db
+
+ -- get path, !! and // values
+ dir, n = string.gsub (dir, '//$', '')
+ local recursion_allowed = (n == 1)
+ local path, n = string.gsub (dir, '^!!', '')
+ local index_mandatory = (n == 1)
+ dbg_print('texdocs',
+ 'texdocs[%d] = %s (index_mandatory=%s, recursion_allowed=%s)',
+ i, path, tostring(index_mandatory), tostring(recursion_allowed))
+
+ -- decide if we should use a ls-R index, the filesystem, or do nothing
+ local root, shift = lsr_root(path)
+ if root and shift and recursion_allowed then
+ dbg_print('texdocs',
+ 'texdocs[%d] using index: %s (shift=%s)', i, root, shift)
+ db = init_lsr_db(root, shift)
+ elseif not index_mandatory and lfs.isdir(path) then
+ dbg_print('texdocs',
+ 'texdocs[%d] using filesystem search', i)
+ db = init_tree_db(path, recursion_allowed)
+ end
+
+ -- register this in docroots
+ doc_roots[i] = {path=path, db=db}
+ end
+end
+
+-- find docfiles in texdocs directories
+get_doclist_texdocs = function(patlist)
+ for code, dr in ipairs(doc_roots) do
+ if dr.db then scan_db(patlist, code, dr.db) end
+ end
+end
+
+-- return the real path from a texdocs tree number + relative path
+texdocs_tree_to_path = function (tree, rel)
+ return doc_roots[tree].path .. '/' .. rel
+end
+
+end -- end scope of doc_roots
+
+---------------------------- look for sty files ----------------------------
+
+-- add doclist entries for sty files in patlist
+local function get_doclist_sty(patlist)
+ for _, pat in ipairs(patlist) do
+ local file = kpse.find_file(pat.name)
+ if file then
+ local df = Docfile:new({
+ name = file,
+ tree = -1,
+ pattern = pat,
+ })
+ s_doclist:add(df)
+ end
+ end
+end
+
+-------------------------------- use tlpdb ---------------------------------
+
+-- tlpdb mean TeX Live Package DataBase and tlp means TeX Live Package
+
+-- return true if cache exists and is newer than original, false otherwise
+local function good_cache(cache, ori)
+ local cache_date = lfs.attributes(cache, 'modification')
+ if not cache_date then return false end
+ local ori_date = assert(lfs.attributes(ori, 'modification'))
+ return cache_date > ori_date
+end
+
+-- make sure a given directory exists, or return nil plus an error string
+local function mkdir_p(dir)
+ if lfs.isdir(dir) then return true end
+ local parent = texdoc.util.path_parent(dir)
+ if parent then
+ local ok, msg = mkdir_p(parent)
+ if not ok then return nil, msg end
+ end
+ return lfs.mkdir(dir)
+end
+
+local print_out_tlpinfo, get_doclist_tlpdb
+local get_tlpinfo_from_tlpdb, get_tlpinfo_from_cache, get_tlpinfo_from_dist
+
+do -- begin scope of tlpinfo tables
+local tlp_from_runfile -- {[runfile_basename] = {tlp1 = true, ...}, ...}
+local tlp_doclist -- {[tlp_name] = {relname1, relname2, ...}, ...}
+
+-- remove entries for tlp without any docfile
+local function remove_useless_tlp()
+ for tlp, doclist in pairs(tlp_doclist) do
+ if #doclist == 0 then tlp_doclist[tlp] = nil end
+ end
+ for runfile, tlp_set in pairs(tlp_from_runfile) do
+ for tlp in pairs(tlp_set) do
+ if not tlp_doclist[tlp] then
+ tlp_from_runfile[runfile][tlp] = nil
+ end
+ end
+ end
+end
+
+-- populate tlpinfo tables using the given texlive.tlpdb
+get_tlpinfo_from_tlpdb = function(filename)
+ s_meta, tlp_from_runfile, tlp_doclist = {}, {}, {}
+ local curr_tlp
+ local state = 'none'
+ for line in io.lines(filename) do
+ if state == 'none' and string.find(line, '^name ') then
+ -- begin a new package
+ curr_tlp = string.lower(string.sub(line, 6, -1))
+ tlp_doclist[curr_tlp] = {}
+ elseif state == 'docfiles' then
+ if not string.find(line, '^ ') then
+ state = 'none'
+ else
+ local file = string.match(line, '^ ([^ ]*)')
+ local meta = string.match(line, '^ [^ ]* (.+)')
+ local basename = string.match(file, '([^/]*)$')
+ if check_ext(basename) then
+ -- we've got a docfile here, add it
+ table.insert(tlp_doclist[curr_tlp], file)
+ if meta then
+ local details = string.match(meta, 'details="([^"]+)"')
+ local lang = string.match(meta, 'language="([^"]+)"')
+ s_meta[reloc_tlpdb_path(file)] = {
+ details = details,
+ lang = lang,
+ }
+ end
+ end
+ end
+ elseif state == 'runfiles' then
+ if not string.find(line, '^ ') then
+ state = 'none'
+ else
+ -- check for interesting runfiles
+ local e = string.sub(line, -4, -1)
+ if e == '.tex' or e == '.sty' or e == '.cls' then
+ local f = string.match(line, '.*/(.*)%.')
+ tlp_from_runfile[f] = tlp_from_runfile[f] or {}
+ tlp_from_runfile[f][curr_tlp] = true
+ end
+ end
+ end
+ -- update state
+ if string.find(line, '^docfiles ') then
+ state = 'docfiles'
+ elseif string.find(line, '^runfiles ') then
+ state = 'runfiles'
+ end
+ end
+ remove_useless_tlp()
+end
+
+-- print out data from tlpdb in dofile()-able form
+print_out_tlpinfo = function(filename)
+ local fh = assert(io.open(filename, 'w'))
+ local function printf(s, ...) fh:write(string.format(s, ...)) end
+
+ -- s_meta
+ printf('local s_meta = {\n')
+ for k, v in pairs(s_meta) do
+ printf(' [%q] = {', k)
+ for i, j in pairs(v) do printf('[%q] = %q, ', i, j) end
+ printf('},\n')
+ end
+ printf('}\n')
+
+ -- tlp_from_runfile
+ printf('local tlp_from_runfile = {\n')
+ for k, v in pairs(tlp_from_runfile) do
+ printf(' [%q] = {', k)
+ for f in pairs(v) do printf('[%q]=true,', f) end
+ printf('},\n')
+ end
+ printf('}\n')
+
+ -- tlp_doclist
+ printf('local tlp_doclist = {\n')
+ for k, v in pairs(tlp_doclist) do
+ printf(' [%q] = {\n', k)
+ for _, f in ipairs(v) do printf(' %q,\n', f) end
+ printf(' },\n')
+ end
+ printf('}\n')
+ printf('return s_meta, tlp_from_runfile, tlp_doclist\n')
+
+ fh:close()
+end
+
+-- get pre-hashed tlpdb info from a cache file
+get_tlpinfo_from_cache = function(filename)
+ s_meta, tlp_from_runfile, tlp_doclist = dofile(filename)
+end
+
+-- get pre-hashed tlpdb info from a pseudo-cache file
+get_tlpinfo_from_dist = function()
+ local f = kpse.find_file(C.data_tlpdb_name, 'texmfscripts')
+ if not f then
+ err_print('error', 'No texlive.tlpdb nor shipped tlpdb data found.')
+ os.exit(C.exit_usage)
+ end
+ dbg_print('tlpdb', 'Getting data from shipped tlpdb data file ' .. f)
+ s_meta, tlp_from_runfile, tlp_doclist = dofile(f)
+end
+
+-- get docfiles for pattern using specific tlpdb information
+get_doclist_tlpdb = function(pattern)
+ -- runfile to tlp to docfile
+ if tlp_from_runfile[pattern] then
+ for tlp in pairs(tlp_from_runfile[pattern]) do
+ for _, file in ipairs(tlp_doclist[tlp]) do
+ s_doclist:add(Docfile:new{
+ name = file,
+ tree = 0,
+ runtodoc = true,
+ })
+ end
+ end
+ end
+ -- tlp name to docfile
+ if tlp_doclist[pattern] then
+ for _, file in ipairs(tlp_doclist[pattern]) do
+ s_doclist:add(Docfile:new{
+ name = file,
+ tree = 0,
+ tlptodoc = true,
+ })
+ end
+ end
+end
+
+-- calculating Levenshtein distance by dynamic programming
+-- cf. http://lua-users.org/lists/lua-l/2008-01/msg00095.html
+function M.levenshtein(s, t)
+ local s, t = tostring(s), tostring(t)
+ if type(s) == 'string' and type(t) == 'string' then
+ local m, n, d = #s, #t, {}
+ for i = 0, m do d[i] = {[0] = i} end
+ for j = 1, n do d[0][j] = j end
+ for i = 1, m do
+ for j = 1, n do
+ local cost = s:sub(i,i) == t:sub(j,j) and 0 or 1
+ d[i][j] = math.min(
+ d[i-1][j] + 1, d[i][j-1] + 1, d[i-1][j-1] + cost
+ )
+ end
+ end
+ return d[m][n]
+ end
+end
+
+-- fuzzy search by using Levenshtein distance
+function M.fuzzy_search(pattern)
+ local tmp_d
+ local min = math.huge
+ local result = ''
+
+ for p in pairs(tlp_doclist) do
+ tmp_d = M.levenshtein(pattern, p)
+ if tmp_d < min then
+ min, result = tmp_d, p
+ end
+ end
+
+ return result, min
+end
+
+end -- end scope of tlpinfo table
+
+-- get tlpinfo tables initialised by whatever mean
+local function init_tlp_database()
+ local TEXMFVAR = kpse.var_value('TEXMFVAR')
+ local cache_file = TEXMFVAR .. '/' .. C.cache_name
+
+ -- set vanilla and detect texlive.tlpdb to use
+ local texlive_tlpdb = get_tlroot() .. '/tlpkg/texlive.tlpdb'
+ vanilla = lfs.isfile(texlive_tlpdb)
+ local tlpdb_found = vanilla
+
+ local custom_texlive_tlpdb = texdoc.config.get_value('texlive_tlpdb')
+ if custom_texlive_tlpdb then
+ if lfs.isfile(custom_texlive_tlpdb) then
+ texlive_tlpdb = custom_texlive_tlpdb
+ tlpdb_found = true
+ else
+ err_print('warning',
+ 'Specified texlive.tlpdb does not exist: ' ..
+ texdoc.util.w32_path(custom_texlive_tlpdb))
+ err_print('warning',
+ 'Fallback to use the texlive.tlpdb in the distribution.')
+ end
+ end
+
+ -- get tlpinfo
+ if tlpdb_found then
+ if good_cache(cache_file, texlive_tlpdb) then
+ dbg_print('tlpdb', 'Using cached data from ' .. cache_file)
+ get_tlpinfo_from_cache(cache_file)
+ else
+ dbg_print('tlpdb', 'Getting data from tlpdb file ' .. texlive_tlpdb)
+ get_tlpinfo_from_tlpdb(texlive_tlpdb)
+ dbg_print('tlpdb', 'Writing data in cache file ' .. cache_file)
+ local ok, msg = mkdir_p(texdoc.util.path_parent(cache_file))
+ if not ok then
+ err_print('warning',
+ 'Failed to create cache file in %s:', cache_file)
+ err_print('warning', msg)
+ else
+ print_out_tlpinfo(cache_file)
+ end
+ end
+ else
+ dbg_print('tlpdb', 'Using shipped tlpdb data.')
+ get_tlpinfo_from_dist()
+ end
+end
+
+------------------------------ main function -------------------------------
+
+-- initialise the various databases (must be called first)
+function M.init_databases()
+ init_texdocs_database()
+ init_tlp_database()
+end
+
+-- find docfiles according to pattern
+function M.get_doclist(pattern, no_alias)
+ -- separate sty patterns from the rest
+ local function normal_vs_sty(list)
+ local normal, sty = {}, {}
+ for _, p in ipairs(list) do
+ if string.match(string.lower(p.name), '%.([^/.]*)$') == 'sty' then
+ table.insert(sty, p)
+ else
+ table.insert(normal, p)
+ end
+ end
+ return normal, sty
+ end
+
+ local function doc_search(pattern, no_alias)
+ -- get patterns (inc. aliases)
+ local patterns = texdoc.alias.get_patterns(pattern, no_alias)
+ local normal, sty = normal_vs_sty(patterns)
+
+ -- get results; _texdocs search comes after _tlpdb search so that
+ -- files found by both will have the priority of the _texdocs tree.
+ -- (https://puszcza.gnu.org.ua/bugs/?369)
+ get_doclist_sty(sty)
+ get_doclist_tlpdb(pattern)
+ get_doclist_texdocs(normal)
+ end
+
+ -- initialise result list
+ s_doclist = Doclist:new()
+
+ -- 1. normal search with the input pattern
+ doc_search(pattern, no_alias)
+
+ -- 2. if no result, execute fuzzy search
+ local fuzzy_level = texdoc.config.get_value('fuzzy_level')
+ if not s_doclist[1] and fuzzy_level > 0 then
+ local f_res, f_lev = M.fuzzy_search(pattern)
+ if f_lev <= fuzzy_level then
+ err_print('info', 'Fuzzy search result: ' .. f_res)
+ dbg_print('search', 'Levenshtein distance: ' .. f_lev)
+ pattern = f_res
+ doc_search(pattern, no_alias)
+ else
+ dbg_print('search', 'Fuzzy search result: ' .. f_res)
+ dbg_print('search', 'Levenshtein distance: ' .. f_lev)
+ end
+ end
+
+ -- finally, sort results
+ texdoc.score.sort_doclist(s_doclist, pattern)
+ return s_doclist
+end
+
+return M
+
+-- vim: ft=lua:
diff --git a/support/texdoc/script/texdoclib-util.tlu b/support/texdoc/script/texdoclib-util.tlu
new file mode 100644
index 0000000000..d14a212a12
--- /dev/null
+++ b/support/texdoc/script/texdoclib-util.tlu
@@ -0,0 +1,127 @@
+-- texdoclib-util.tlu: utility functions for texdoc
+--
+-- The TeX Live Team, GPLv3, see texdoclib.tlu for details
+
+-- dependencies
+local texdoc = {
+ const = require('texdoclib-const'),
+ config = require('texdoclib-config'),
+}
+
+-- shortcuts
+local M = {}
+local C = texdoc.const
+
+-- change '/' to '\' on windows
+-- Note: Internal representation of files always use forward slashes.
+-- This function should be called only before displaying a path.
+if os.type == 'windows' then
+ function M.w32_path(path)
+ return (string.gsub(path, '/', '\\'))
+ end
+else
+ function M.w32_path(path)
+ return path
+ end
+end
+
+-- remove the last directory component of a path
+if os.type == 'windows' then
+ function M.path_parent(path)
+ return string.match(path, '^(.*)[\\/]')
+ end
+else
+ function M.path_parent(path)
+ return string.match(path, '^(.*)/')
+ end
+end
+
+-- generic log display function
+local function log(label, msg, ...)
+ io.stderr:write('texdoc ' .. label .. ': ' .. msg:format(...) .. '\n')
+end
+
+-- generic error display function (see the err_priority constant)
+function M.err_print(lvl, msg, ...)
+ -- be careful: maybe config item "verbosity_level" has not set yet
+ local verbosity_level = texdoc.config.get_value('verbosity_level')
+ or tonumber(C.def_verbosity)
+
+ -- print if the priority is higher than current verbosity level
+ if C.err_priority[lvl] <= verbosity_level then
+ log(lvl, msg, ...)
+ end
+end
+local err_print = M.err_print
+
+do -- begin scope of active_debugs
+local active_debugs
+
+-- set active_debugs
+local function set_active_debugs()
+ local debug_list = texdoc.config.get_value('debug_list')
+
+ if not debug_list then return end
+ active_debugs = {}
+
+ -- all debug options imply version info
+ if debug_list[1] then
+ active_debugs.version = true
+ else
+ return
+ end
+
+ -- if 'all' is the first keyword, just activate all categories
+ if debug_list[1] == 'all' then
+ for dbg in pairs(C.known_debugs) do
+ active_debugs[dbg] = true end
+ return
+ end
+
+ -- activate options from the list
+ for _, dbg in ipairs(debug_list) do
+ local deps = C.known_debugs[dbg]
+ if deps then
+ active_debugs[dbg] = true
+ for _, d in ipairs(deps) do active_debugs[d] = true end
+ else
+ err_print('warning', 'Unknown debug category "' .. dbg .. '".')
+ end
+ end
+end
+
+-- generic debug function
+function M.dbg_print(cat, msg, ...)
+ -- make sure active_debugs is set
+ if not active_debugs then set_active_debugs() end
+
+ -- print message it belongs to an active category
+ if active_debugs and active_debugs[cat] or cat == 'XXX' then
+ log('debug-' .. cat, msg, ...)
+ end
+end
+
+end -- end scope of active_debugs
+
+-- if file is base .. '.' .. zip with zip in zipext_list, return: base, zip
+-- otherwise, return: file, nil
+function M.parse_zip(file)
+ local zip
+ for _, zip in ipairs(texdoc.config.get_value('zipext_list')) do
+ local l = #zip + 1
+ if string.sub(file, -l, -1) == '.' .. zip then
+ return string.sub(file, 1, -l - 1), zip
+ end
+ end
+
+ return file, nil
+end
+
+-- print a usage message
+function M.print_usage()
+ print(C.usage_msg)
+end
+
+return M
+
+-- vim: ft=lua:
diff --git a/support/texdoc/script/texdoclib-view.tlu b/support/texdoc/script/texdoclib-view.tlu
new file mode 100644
index 0000000000..126ca57686
--- /dev/null
+++ b/support/texdoc/script/texdoclib-view.tlu
@@ -0,0 +1,246 @@
+-- texdoclib-view.tlu: view a document and/or display the list of results in texdoc
+--
+-- The TeX Live Team, GPLv3, see texdoclib.tlu for details
+
+-- dependencies
+local texdoc = {
+ const = require('texdoclib-const'),
+ util = require('texdoclib-util'),
+ config = require('texdoclib-config'),
+}
+
+-- shortcuts
+local M = {}
+local C = texdoc.const
+local err_print = texdoc.util.err_print
+local dbg_print = texdoc.util.dbg_print
+
+----------------------------- view a document -----------------------------
+
+-- view a document
+-- see texdoclib-search.tlu for the structure of the argument
+local function view_doc(docfile)
+ M.view_file(docfile.realpath)
+end
+
+-- view a file, if possible
+local function try_viewing(view_command, viewer_replacement)
+ if string.match (view_command, C.place_holder) then
+ view_command = string.gsub(
+ view_command, C.place_holder, viewer_replacement)
+ else
+ view_command = view_command .. ' ' .. viewer_replacement
+ end
+
+ -- try to catch problems with missing DISPLAY on Unix
+ if os.type == 'unix' and not (os.name == 'macosx')
+ and os.getenv('DISPLAY') == nil then
+ err_print('warning',
+ 'DISPLAY is not set; your viewer will likely have problems.')
+ err_print('warning',
+ 'Try --list to list results instead of displaying them.')
+ end
+ err_print('info', 'View command: ' .. view_command)
+
+ -- See long comment below this function for the LC_CTYPE story.
+ -- We only want to reset the environment if we have the value
+ -- to reset it to. In older versions of luatex, status.lc_* will be nil.
+ local env_lc_ctype = status.lc_ctype
+ local luatex_lc_ctype = os.setlocale(nil, 'ctype')
+ if (env_lc_ctype) then
+ err_print('info', 'Setting environment LC_CTYPE to: ' .. env_lc_ctype)
+ os.setenv('LC_CTYPE', env_lc_ctype)
+ end
+
+ -- the big casino: run the external command.
+ if os.execute(view_command) > 0 then
+ err_print('error', 'Failed to execute: ' .. view_command)
+ os.exit(C.exit_error)
+ end
+
+ -- reset back to "C" (should always be C and always happen, but in case...)
+ if (luatex_lc_ctype) then
+ os.setenv('LC_CTYPE', luatex_lc_ctype)
+ end
+end
+
+-- get viewer and viewer_replacement before calling try_viewing
+-- returns false of failure, true on success viewer_replacement is either:
+--
+-- 1. the filename, quoted with ""
+-- 2. the filename, quoted with "" and followed by some rm commands
+--
+-- The second case happens when the doc is zipped. In the case, this function
+-- unzips it in a tmpdir so that the viewer command can use the unzipped file.
+function M.view_file(filename)
+ local viewer, viewer_replacement
+
+ -- check if the file is zipped
+ local nozipname, zipext = texdoc.util.parse_zip(filename)
+
+ -- determine viewer_replacement
+ if zipext then
+ local unzip_cmd = texdoc.config.get_value('unzip_' .. zipext)
+
+ if not unzip_cmd then
+ err_print('error', 'No unzip command for ".%s" files.', zipext)
+ os.exit(C.exit_error)
+ end
+
+ local tmpdir = os.tmpdir('/tmp/texdoc.XXXXXX')
+ if not tmpdir then
+ err_print('error', 'Failed to create tempdir to unzip.')
+ os.exit(C.exit_error)
+ end
+
+ local basename = string.match(nozipname, '.*/(.*)$') or nozipname
+ local tmpfile = '"' .. tmpdir .. '/' .. basename .. '"'
+ local unzip = unzip_cmd .. ' "' .. filename .. '">' .. tmpfile
+ dbg_print('view', 'Unzip command: ' .. unzip)
+
+ if not os.execute(unzip) then
+ err_print('error', 'Failed to unzip ' .. filename)
+ os.remove(tmpfile)
+ os.remove(tmpdir)
+ os.exit(C.exit_error)
+ end
+
+ -- it is necessary to sleep a few secounds. Otherwise, the temporary
+ -- file could be removed before opening it.
+ viewer_replacement = tmpfile .. '; sleep 2; ' ..
+ texdoc.config.get_value('rm_file') .. ' ' ..tmpfile .. '; ' ..
+ texdoc.config.get_value('rm_dir') .. ' ' .. tmpdir
+ filename = nozipname
+ else
+ viewer_replacement = '"' .. texdoc.util.w32_path(filename) .. '"'
+ end
+
+ -- files without extension are assumed to be text
+ local viewext = (filename:match('.*%.([^/]*)$') or 'txt'):lower()
+
+ -- special case : sty files use txt viewer
+ -- FIXME: hardcoding such cases this way is not very clean
+ if viewext == 'sty' then viewext = 'txt' end
+ if viewext == 'texlive' then viewext = 'txt' end
+ if viewext == 'htm' then viewext = 'html' end
+
+ -- get a viewer from built-in defaults if not already set
+ if not texdoc.config.get_value('viewer_' .. viewext) then
+ texdoc.config.get_default_viewers()
+ end
+
+ -- still no viewers? use txt as a fallback
+ if not texdoc.config.get_value('viewer_' .. viewext) then
+ err_print('warning',
+ 'No viewer defined for ".%s" files, using "viewer_txt" instead.',
+ viewext)
+ viewext = 'txt'
+ end
+
+ -- finally, check validity of the viewer
+ viewer = texdoc.config.get_value('viewer_' .. viewext)
+ assert(viewer, 'Internal error: no viewer found.')
+ dbg_print('view', 'Using "viewer_%s" to open the file.', viewext )
+
+ return try_viewing(viewer, viewer_replacement)
+end
+
+-- Explanation of locale madness:
+-- LuaTeX resets LC_CTYPE, LC_COLLATE, LC_NUMERIC to "C". That is good for
+-- inside luatex, but when we run an external program, if the user's
+-- environment is something else, we want to switch back to it. As of
+-- TL 2017 LuaTeX, we can inspect the user's locale with status.lc_ctype, etc.
+--
+-- For texdoc purposes, what matters is LC_CTYPE (so we don't bother with
+-- the others). For example, with the less pager, when LC_CTYPE=C,
+-- non-ASCII bytes are displayed as "<xx>", where xx is the two hex
+-- digits for the byte.
+
+----------------------------- display results -----------------------------
+
+-- print a list of docfile objects (see texdoclib-search.tlu) as a menu
+-- if showall is false, stop as soon as a bad result is encountered
+local function print_menu(name, doclist, showall)
+ local max_lines = tonumber(texdoc.config.get_value('max_lines'))
+ if texdoc.config.get_value('interact_switch') and doclist[max_lines + 1] then
+ -- there may be too many lines, count them
+ local n = 0
+ for _, doc in pairs(doclist) do
+ if doc.quality == 'good' or
+ (showall and doc.quality ~= 'killed') then
+ n = n + 1
+ end
+ end
+
+ if n > max_lines then
+ io.write(n, ' results. Display them all? (y/N) ')
+ local ans = io.read('*line')
+ if not ((ans == 'y') or (ans == 'Y')
+ -- io.read had a bug wrt windows eol on some versions of texlua
+ or (ans == '\ry') or (ans == '\rY')) then
+ return
+ end
+ end
+ end
+ local i, doc, last_i
+ for i, doc in ipairs(doclist) do
+ if doc.quality == 'killed' then break end
+ if doc.quality ~= 'good' and not showall then break end
+ if texdoc.config.get_value('machine_switch') == true then
+ print(name, doc.score, texdoc.util.w32_path(doc.realpath),
+ doc.lang or '', doc.details or '')
+ else
+ last_i = i -- save for test below
+ print(string.format('%2d %s', i, texdoc.util.w32_path(doc.realpath)))
+ if doc.details or doc.lang then
+ local line = ' = '
+ if doc.lang then line = line .. '[' .. doc.lang .. '] ' end
+ if doc.details then line = line .. doc.details end
+ print(line)
+ end
+ end
+ end
+ if texdoc.config.get_value('interact_switch') then
+ io.write('Enter number of file to view, RET to view 1, anything else to skip: ')
+ local num_str = io.read('*line')
+ -- That returns the empty string on an empty line, nil on EOF.
+ -- We only want to default to viewing 1 on an empty line.
+ -- Use Lua's faked ternary operator for fun and brevity:
+ num = (num_str == '' and 1 or tonumber(num_str))
+ if num and doclist[num] and num <= last_i then
+ view_doc(doclist[num])
+ end
+ end
+end
+
+----------------------- deliver results based on mode ---------------------
+
+function M.deliver_results(name, doclist, many)
+ -- ensure that results were found or apologize
+ if not doclist[1] or doclist[1].quality == 'killed' then
+ local msg = string.gsub(C.notfound_msg, C.notfound_msg_ph, name)
+ io.stderr:write(msg .. '\n') -- get rid of gsub's 2nd value
+ os.exit(C.exit_notfound)
+ end
+ -- shall we show all of them or only the "good" ones?
+ local showall = (texdoc.config.get_value('mode') == 'showall')
+ if not showall and doclist[1].quality ~= 'good' then
+ showall = true
+ err_print('info', 'No good result found, showing all results.')
+ end
+ -- view result or show menu based on mode and number of results
+ if (texdoc.config.get_value('mode') == 'view')
+ or texdoc.config.get_value('mode') == 'mixed' and (not doclist[2]
+ or (doclist[2].quality ~= 'good' and not showall)) then
+ view_doc(doclist[1])
+ else
+ if many and not texdoc.config.get_value('machine_switch') then
+ print('*** Results for: ' .. name .. ' ***')
+ end
+ print_menu(name, doclist, showall)
+ end
+end
+
+return M
+
+-- vim: ft=lua:
diff --git a/support/texdoc/script/texdoclib.tlu b/support/texdoc/script/texdoclib.tlu
new file mode 100644
index 0000000000..fb85895d37
--- /dev/null
+++ b/support/texdoc/script/texdoclib.tlu
@@ -0,0 +1,37 @@
+-- texdoclib.tlu: the texdoc library
+
+--[[
+Copyright 2008 Manuel Pégourié-Gonnard, Takuto Asakura, and the TeX Live Team.
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <https://www.gnu.org/licenses/>.
+
+Previous work in the public domain:
+- Contributions from Reinhard Kotucha (2008).
+- First texlua versions by Frank Küster (2007).
+- Original shell script by Thomas Esser, David Aspinall, and Simon Wilkinson.
+--]]
+
+local texdoc = {
+ const = require('texdoclib-const'),
+ util = require('texdoclib-util'),
+ alias = require('texdoclib-alias'),
+ score = require('texdoclib-score'),
+ config = require('texdoclib-config'),
+ search = require('texdoclib-search'),
+ view = require('texdoclib-view'),
+ cli = require('texdoclib-cli'),
+}
+
+return texdoc
+
+-- vim: ft=lua:
diff --git a/support/texdoc/texdoc.cnf b/support/texdoc/texdoc.cnf
new file mode 100644
index 0000000000..5fcb2a5344
--- /dev/null
+++ b/support/texdoc/texdoc.cnf
@@ -0,0 +1,507 @@
+# original texdoc.cnf -- default runtime configuration for texdoc.
+# Public domain.
+#
+# DO NOT edit this file!
+# For your personal settings, run 'texdoc -f' and use the recommended file; you
+# may need to create it. DO NOT copy the present file, please create a new one
+# (you may want to copy/paste/edit portions of the present file however, or
+# just read it for inspiration).
+#
+# For system-wide configuration or other uses, you may want to use the other
+# files listed, see the manual (texdoc texdoc) for details. The files are read
+# in the order they are printed. If a value is set multiple times, first wins.
+#
+# In those files, you can set your preferred viewer for various formats,
+# some nice names (alias) for specific documentation, and your preferred default
+# values of texdoc's configuration settings.
+#
+# Everything after a # on a line is ignored.
+# Spaces at the beginning/end of a line, as well as empty lines, are ignored.
+#
+# For more details, see the texdoc manual (try 'texdoc texdoc').
+
+# General settings
+# ================
+
+## Viewers
+
+# Defaults depend on what is available on your system, as well as your desktop
+# environment. Here are a few examples.
+#
+# %s is optional and stands for the filename.
+#
+# viewer_pdf = xpdf # works
+# viewer_pdf = xpdf %s & # works even better
+
+# If you want to enable support for zipped documentation (see below),
+# you may want to adapt viewer_* so that it starts a subshell:
+#
+# viewer_pdf = (xpdf %s) &
+#
+# Otherwise, the & will have no effect since the viewing command is followed by
+# some cleanup-commands for temporary files. For the same reason, %s must be the
+# last thing on your command line: (xpdf %s -option) will not work.
+#
+# WARNING: for zip support your viewer needs to be blocking, i.e. it must not
+# return immediately: otherwise the temporary file will be deleted too early.
+
+## Zipped documentation support
+
+# WARNING: support for zipped documents works only on Unix. Make sure that the
+# values of the associated options are adapted to your system. Read the above
+# warning concerning viewers.
+
+# Built-in defaults (for reference):
+#
+# zipext_list = # the empty list
+# rm_file = rm -f
+# rm_dir = rmdir
+#
+# For each "ext" in zipext_list, you must define a "unzip_ext" unzipper.
+# The unzipped data must be printed on stdout. Example:
+#
+# zipext_list = gz
+# unzip_gz = gzip -d -c
+
+## User interaction
+
+# Built-in defaults (for reference):
+#
+# mode = view
+#
+# interact_switch = true
+# machine_switch = false
+#
+# verbosity_level = 2 # Print: 0: nothing, 1: errors, 2: warnings, 3: infos
+# debug_list = # the empty list
+
+## Documentation searching
+
+# Built-in defaults (for reference):
+#
+# ext_list = pdf, html, htm, txt, md, ps, dvi, # Note: empty string at end
+# badext_list = txt, # Note: empty string at end
+# basename_list = readme, 00readme
+# badbasename_list = readme, 00readme
+
+## Fuzzy search
+
+# Built-in defaults (for reference):
+#
+# fuzzy_level = 5
+
+# Known suffixes for documentation
+suffix_list = doc, -doc, _doc, .doc, /doc, manual, /manual, -manual, userguide, /user_guide, -guide, -user, -man, notes, -info, ref
+
+# Score adjustments
+# =================
+
+# Results are sorted by score (decreasing).
+# Only results with positive scores are displayed (unless mode is 'showall').
+# Results with score <= -100 are never displayed.
+#
+# The score of results containing a given pattern can be adjusted using the
+# 'adjscore' directive, either globally or only for specific queries. It is
+# also possible to set the score directly in alias directives, see the next
+# section.
+#
+# As a general rule, use 'adjscore' if you only want to adjust the score of
+# particular results, and 'alias' if you want to add new results (possibly
+# adjusting their score) for a particular query.
+#
+# To override one of the settings below, use the 'noadjscore' directive in
+# your personal configuration file, see the manual for details.
+
+## General adjustments
+
+# Makefile are never documentation, just as documents in src or source subdir
+# -1000 should be enough to kill them
+adjscore /Makefile = -1000
+adjscore /src/ = -1000
+adjscore /source/ = -1000
+
+# licence files aren't very likely to contain relevant documentation, but it
+# feels wrong to totally kill them
+adjscore copying = -10
+adjscore license = -10
+adjscore gpl = -10
+
+# tex-virtual-academy provides a lot of spurious matches
+adjscore /tex-virtual-academy-pl/ = -50
+
+# test and example files are not likely the best documentation
+adjscore example = -1
+adjscore examples = -1
+adjscore sample = -1
+adjscore samples = -1
+adjscore test = -3
+adjscore tests = -3
+adjscore /demo/ = -1
+adjscore /demos/ = -1
+adjscore /figures/ = -3
+adjscore /images/ = -3
+
+# readme files usually get negative scores because they have bad extensions,
+# but they're still slightly better than other results with negative scores
+adjscore readme = +0.1
+
+## Specific adjustments
+
+# 'texdoc' may look like "tex's documentation" but it isn't; 'tex-*' as well
+adjscore(tex) texdoc = -10
+adjscore(tex) tex-gyre = -10
+adjscore(tex) tex-ps = -10
+
+# avoid too many results to be shown for 'latex'
+# package names
+adjscore(latex) cjw-latex = -10
+adjscore(latex) cweb-latex = -10
+adjscore(latex) duerer-latex = -10
+adjscore(latex) guide-to-latex = -10 # only useful with the book
+adjscore(latex) latex-web-companion = -10
+adjscore(latex) ocr-latex = -10
+adjscore(latex) tufte-latex = -10
+# file names
+adjscore(latex) Content_LaTeX_Package_Demo = -10
+adjscore(latex) example_latex = -10
+adjscore(latex) test_latex = -10
+
+# beamer
+adjscore(beamer) beamer-tut-pt/tutorialbeamer = +10
+adjscore(beamer) beamer-FUBerlin = -3
+adjscore(beamer) beamer-tut-pt = -10
+adjscore(beamer) presentations = -10
+
+# misc
+adjscore(context) circuitikz = -10
+adjscore(context) /gnuplot/ = -3
+adjscore(context) context.man1 = +2
+adjscore(symbols) /staves/ = -5
+
+# catalogue info missing in the tlcontrib version of the package :-(
+adjscore(pgf) pgfmanual.pdf = +5
+
+# another catalogue info missing case
+adjscore(hyph-utf8) hyphenation.pdf = +5
+
+# let's not have context version found first
+adjscore(fixme) /third/ = -6
+
+# prioritize newer document
+adjscore(babel-german) ngermanb.pdf = +5
+
+# prioritize man page
+adjscore(findhyph) findhyph.man1 = +5
+
+# prioritize official latex-tools
+adjscore(layout) /tools/ = +1
+
+# Aliases
+# =======
+
+# Aliases allow to add results for a specific query, by making texdoc search
+# for additional keywords in the file name. Matches for the new keyword get a
+# very high score by default, but this can be adjusted.
+#
+# To override one of the settings below, use the 'noalias' directive in
+# your personal configuration file, see the manual for details.
+
+## Essential documentation
+
+alias live = texlive-en
+alias texlive = texlive-en
+alias tex-live = texlive-en
+
+## various stuff
+
+# a few useful general documents
+alias faq-en = newfaq
+alias faq = newfaq
+alias latex = latex-doc-ptr
+
+# various lshort translations
+adjscore(lshort) /lshort-english/ = +0.5 # original
+alias lshort-de = l2kurz # german
+alias lshort-ja = jlshort # japanese
+alias lshort-pl = lshort-polish/lshort2e # polish
+alias lshort-pt = pt-lshort # portuguese
+alias lshort-sk = slshorte # slovak
+alias lshort-es = lshort-spanish/lshort-a4 # spanish
+
+# *TeX engines reference manuals, man pages and related
+alias e-tex-ref = etex_man
+alias etex-ref = etex_man
+alias pdftex-ref = pdftex-a
+#alias xetex-ref = xetex-reference # OK
+alias xelatex = xetex-reference
+alias luatex-ref = luatexref-t
+
+# by default, <engine> = <engine>-ref (duplicated: no alias cascading)
+alias e-tex = etex_man
+alias etex = etex_man
+alias pdftex = pdftex-a
+#alias xetex = xetex-reference # OK
+alias xelatex = xetex-reference
+alias luatex = luatexref-t
+
+alias e-tex-man = etex.man1
+alias etex-man = etex.man1
+alias pdftex-man = pdftex.man1
+#alias xetex-man = xetex.man1 # missing
+alias luatex-man = luatex.man1
+
+alias luatex-pkg = oberdiek/luatex.pdf
+
+# latex/basic classes
+alias article = classes
+alias book = classes
+alias report = classes
+#alias ltxguide = ? # missing
+#alias minimal = ? # missing
+adjscore(minimal) europecv = -1000 # false positive
+
+# latex/basic packages, sorted by dtx
+alias doc = base/doc
+alias shortvrb = base/doc
+alias letter = base/letter
+alias slides = base/slides
+alias(20) fontenc = encguide
+alias(20) textcomp = encguide
+alias fix-cm = fixltx2e
+alias bezier = latex209
+alias fleqn = latex209
+alias leqno = latex209
+alias openbib = latex209
+alias t1enc = latex209
+alias makeidx = makeindx
+alias showidx = makeindx
+alias flafter = source2e
+alias fontenc = source2e
+alias textcomp = source2e
+alias tracefnt = source2e
+# TODO: try to avoid the huge amount of false positives for `doc' itself.
+
+# latex's required graphics bundle
+alias color-dev = color
+alias epsfig-dev = epsfig
+alias graphics-dev = graphics
+alias graphicx-dev = graphicx
+alias color = grfguide
+alias epsfig = grfguide
+alias graphics = grfguide
+alias graphicx = grfguide
+
+# latex's required psnfss2e bundle
+alias avant = psnfss2e
+alias bookman = psnfss2e
+alias chancery = psnfss2e
+alias charter = psnfss2e
+alias courier = psnfss2e
+alias helvet = psnfss2e
+alias mathpazo = psnfss2e
+alias mathpple = psnfss2e
+alias mathptm = psnfss2e
+alias mathptmx = psnfss2e
+alias newcent = psnfss2e
+alias palatino = psnfss2e
+alias times = psnfss2e
+alias utopia = psnfss2e
+
+# ams
+alias amsfonts = amsfndoc
+alias amslatex2 = technote
+alias amsmath = amsldoc
+alias amsrefs = amsrdoc
+alias amsthm = amsthdoc
+alias amsart = instr-l
+alias amsbook = instr-l
+alias amscls = instr-l
+alias amsproc = instr-l
+alias amsart-dev = amsclass
+alias amsbook-dev = amsclass
+alias amscls-dev = amsclass
+alias amsproc-dev = amsclass
+alias amsmath-dev = amsmath
+
+# koma-script
+alias koma = scrguien
+alias koma-script = scrguien
+alias scrartcl = scrguien
+alias scrbook = scrguien
+alias scrreprt = scrguien
+alias typearea = scrguien
+alias koma-de = scrguide
+alias koma-script-de = scrguide
+alias scrartcl-de = scrguide
+alias scrbook-de = scrguide
+alias scrreprt-de = scrguide
+alias typearea-de = scrguide
+
+# tugboat package and classes
+alias tugboat = ltubguid
+
+# caption
+alias caption = caption-eng
+alias caption-dev = caption.pdf
+
+# metapost by various names
+alias metapost = mpman
+alias(5) metapost = mpgraph
+alias(5) metapost = mpintro
+alias mpost = mpman
+alias(5) mpost = mpgraph
+alias(5) mpost = mpintro
+alias mp = mpman
+alias(5) mp = mpgraph
+alias(5) mp = mpintro
+
+# latex3 and friends
+alias latex3 = ltx3info
+alias(3) expl3 = interface3
+alias(3) expl3 = source3
+alias l3bootstrap = interface3
+alias l3names = interface3
+alias l3basics = interface3
+alias l3expan = interface3
+alias l3prg = interface3
+alias l3quark = interface3
+alias l3token = interface3
+alias l3int = interface3
+alias l3skip = interface3
+alias l3tl = interface3
+alias l3seq = interface3
+alias l3clist = interface3
+alias l3prop = interface3
+alias l3box = interface3
+alias l3coffins = interface3
+alias l3color = interface3
+alias l3msg = interface3
+alias l3keys = interface3
+alias l3file = interface3
+alias l3fp = interface3
+alias l3luatex = interface3
+
+# misc
+alias autofe = ucs
+alias afoot = arabtex-doc
+alias arabicfont = bezos
+alias bibtex = btxdoc
+alias changes = changes.english.pdf
+alias cm = cm/README
+alias(5) cmsuper = cm-super/FAQ
+alias cm-super = cm-super/README
+alias cmsuper = cm-super/README
+alias cyrillic = cyrillic/00readme.txt
+alias elsarticle = elsdoc
+alias(0.1) fontinst = fontinstallationguide
+alias hyperref = hyperref/manual.pdf
+alias hyperref-dev = hyperref.pdf
+alias iso = isoman
+alias lettrine = lettrine # to hide context/third/lettrine-doc.pdf
+alias nonfloat-de = nonfloat
+alias nonfloat = nonfloat-en
+alias xstring-fr = xstring_doc_fr
+alias arabic = arabi/user_guide
+alias 3parttable = threeparttable
+alias croatian = babel
+alias mls = montex
+alias vrbexin = verbasef
+alias dtk = dtk/doc/beispiel.pdf
+alias musixtex = musixdoc
+alias mychemistry = mychemistry_en
+alias mychemistry-de = mychemistry_de
+alias cjk = CJK.txt # FIXME: should be case insensitive
+alias ieee = IEEEtran_HOWTO
+alias layouts = layman
+alias plnfss = plnfss.txt # XXX
+alias teubner = teubner-doc
+alias xypic = xyguide
+alias kvoptions-patch = kvoptions # XXX?
+alias cweb = cwebman # XXX: is in 'cweb' dir, should win
+alias einfuehrung = einfuehrung/README # tl should get "readme" tag
+
+## a few easy patterns
+
+# egrep 'manual\.pdf\>'
+alias tree-dvips = tree-manual
+
+# egrep 'user\.pdf\>'
+alias cweb-latex = cweb-user
+
+# egrep 'doc\.pdf\>'
+alias barcode = eandoc.pdf
+alias enctex-cz = encdoc
+alias ngerman = gerdoc
+alias jurabib-de = jbgerdoc
+alias ofs-cz = ofsdoc
+alias ofs = ofsdoc-e
+alias pst-geo = pst-map3d-doc
+
+## aliases basically borrowed from texdoctk.dat with some adaptations
+alias akletter = akletter/lettereng
+alias arydshln = arydshln-man
+alias cv = curve/curve
+alias datenumber-de = datenumber/docgerman
+alias labels(1) = envlab/elguide
+alias expressg = expeg
+alias flcards = flashcards
+alias lehman = fontinstallationguide
+alias gnlogic = gn-logic14
+alias kluwer = kluwer/usrman
+alias lmfonts = lm-info
+alias mathenv = mdwtab
+alias montex = mlsquick
+alias mltex = mltex/mltex.txt
+alias natbib2 = natnotes
+alias uktugfaq = newfaq
+alias onrannual = onrannual/README
+alias otibet = otibet/unidoc
+alias pbdiagram = pb-manual
+alias tikz = pgfmanual
+alias apmgraph = pmgraph
+alias preprint = preprint/00readme.txt
+alias dutch = rapdoc
+alias startex = startex/base/guide
+alias tangles = t-angles/t-manual
+alias tex4ht = tex4ht/mn.html
+alias(60) akademia = tex-virtual-academy-pl/index.html
+alias treetex = tree_doc
+alias lineno = ulineno
+alias texguide = usrguide
+alias(5) xypic = xyrefer
+
+## packages from ctan/macros/latex/contrib/misc or with doc in .sty only
+
+# the score used is 4.1 because:
+# - it is less than 4.5, the default score of foobar/foobar.pdf for name foobar
+# - it is greater than the worse false positive here (.../VERSION for version)
+#
+# Anyway, this is a hopefully a temporary measure, since work is going on to
+# produce suitable documentation for all these packages (thanks to Philipp
+# Stephani and Robin Fairbairns mostly).
+
+alias(4.1) 3parttable = 3parttable.sty
+alias(4.1) bibcheck = bibcheck.sty
+alias(4.1) concrete = concrete.sty
+alias(4.1) linsys = linsys.sty
+alias(4.1) mitpress = mitpress.sty
+alias(4.1) nextpage = nextpage.sty
+alias(4.1) setspace = setspace.sty
+alias(4.1) statex = statex.sty
+alias(4.1) statex2 = statex2.sty
+alias(4.1) texilikecover = texilikecover.sty
+alias(4.1) thrmappendix = thrmappendix.sty
+alias(4.1) topcapt = topcapt.sty
+alias(4.1) vrbexin = vrbexin.sty
+
+# Final hack
+# ================
+#
+# The next line has no effect when this file is installed as the last
+# configuration file (default) but is useful when you install it as the "tricky"
+# configuration file (usually TEXMFHOME/texdoc/texdoc-dist.cnf) whose mere
+# existence is a hack to make it easier to run the development version, see
+# <https://github.com/TeX-Live/texdoc>.
+#
+# It is strongly recommended NOT to use it in any other circumstance.
+lastfile_switch = true