diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/texdoc |
Initial commit
Diffstat (limited to 'support/texdoc')
-rw-r--r-- | support/texdoc/COPYING | 674 | ||||
-rw-r--r-- | support/texdoc/NEWS | 251 | ||||
-rw-r--r-- | support/texdoc/README.md | 117 | ||||
-rw-r--r-- | support/texdoc/doc/texdoc.1 | 177 | ||||
-rw-r--r-- | support/texdoc/doc/texdoc.pdf | bin | 0 -> 76508 bytes | |||
-rw-r--r-- | support/texdoc/doc/texdoc.tex | 867 | ||||
-rwxr-xr-x | support/texdoc/script/texdoc.tlu | 17 | ||||
-rw-r--r-- | support/texdoc/script/texdoclib-alias.tlu | 127 | ||||
-rw-r--r-- | support/texdoc/script/texdoclib-cli.tlu | 219 | ||||
-rw-r--r-- | support/texdoc/script/texdoclib-config.tlu | 559 | ||||
-rw-r--r-- | support/texdoc/script/texdoclib-const.tlu | 158 | ||||
-rw-r--r-- | support/texdoc/script/texdoclib-score.tlu | 293 | ||||
-rw-r--r-- | support/texdoc/script/texdoclib-search.tlu | 771 | ||||
-rw-r--r-- | support/texdoc/script/texdoclib-util.tlu | 127 | ||||
-rw-r--r-- | support/texdoc/script/texdoclib-view.tlu | 246 | ||||
-rw-r--r-- | support/texdoc/script/texdoclib.tlu | 37 | ||||
-rw-r--r-- | support/texdoc/texdoc.cnf | 507 |
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 Binary files differnew file mode 100644 index 0000000000..d897017714 --- /dev/null +++ b/support/texdoc/doc/texdoc.pdf 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 |